From c9a714d75fdb32f502b6716d9fb7dacda3613d14 Mon Sep 17 00:00:00 2001 From: Vincent Riemer Date: Tue, 18 Jul 2023 15:33:17 -0700 Subject: [PATCH] Add basics of pointer event interception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Changelog: [Internal] - Add basics of pointer event interception This diff adds the scaffolding for intercepting Pointer Events (without actually doing anything with them yet). Most notably introduces the logic of determining which events are of the PointerEvent type and downcasting to the type so the interceptor can properly work with the typed properties. Originally my plan was to leverage `dynamic_cast` but sandcastle's signals let me know that there exists internal apps (particularly VR ones) which don't have RTTI enabled — so to avoid that usage I introduced an additional abstract method on EventPayload which lets subclasses identify themselves cheaply and then subsequently leverage `static_cast`. Differential Revision: https://internalfb.com/D47443773 fbshipit-source-id: deb804a229db7c57f0d36cd6b34fbf39bd550859 --- .../renderer/components/view/PointerEvent.cpp | 4 +++ .../renderer/components/view/PointerEvent.h | 1 + .../react/renderer/core/EventPayload.h | 9 +++++ .../react/renderer/core/EventPayloadType.h | 14 ++++++++ .../core/ValueFactoryEventPayload.cpp | 4 +++ .../renderer/core/ValueFactoryEventPayload.h | 1 + .../uimanager/PointerEventsProcessor.cpp | 23 ++++++++++++ .../uimanager/PointerEventsProcessor.h | 36 +++++++++++++++++++ .../renderer/uimanager/UIManagerBinding.cpp | 27 ++++++++++++++ .../renderer/uimanager/UIManagerBinding.h | 13 +++++++ 10 files changed, 132 insertions(+) create mode 100644 packages/react-native/ReactCommon/react/renderer/core/EventPayloadType.h create mode 100644 packages/react-native/ReactCommon/react/renderer/uimanager/PointerEventsProcessor.cpp create mode 100644 packages/react-native/ReactCommon/react/renderer/uimanager/PointerEventsProcessor.h diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/PointerEvent.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/PointerEvent.cpp index 8e9f0ae93eb180..d3b749ee840a6c 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/PointerEvent.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/PointerEvent.cpp @@ -44,6 +44,10 @@ jsi::Value PointerEvent::asJSIValue(jsi::Runtime &runtime) const { return object; } +EventPayloadType PointerEvent::getType() const { + return EventPayloadType::PointerEvent; +} + #if RN_DEBUG_STRING_CONVERTIBLE std::string getDebugName(PointerEvent const & /*pointerEvent*/) { diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/PointerEvent.h b/packages/react-native/ReactCommon/react/renderer/components/view/PointerEvent.h index 0dc4ce016c2e62..cf71de0281eaf2 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/PointerEvent.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/PointerEvent.h @@ -115,6 +115,7 @@ struct PointerEvent : public EventPayload { * EventPayload implementations */ jsi::Value asJSIValue(jsi::Runtime &runtime) const override; + EventPayloadType getType() const override; }; #if RN_DEBUG_STRING_CONVERTIBLE diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventPayload.h b/packages/react-native/ReactCommon/react/renderer/core/EventPayload.h index ef9f2bc8aad8ac..1d46c84f87bea3 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/EventPayload.h +++ b/packages/react-native/ReactCommon/react/renderer/core/EventPayload.h @@ -9,6 +9,8 @@ #include +#include + namespace facebook::react { /** @@ -24,6 +26,13 @@ struct EventPayload { EventPayload &operator=(EventPayload &&) = default; virtual jsi::Value asJSIValue(jsi::Runtime &runtime) const = 0; + + /** + * Reports the type of the event payload for efficient downcasting. + * When adding a new EventPayload be sure to add a new type of it + * in `EventPayloadType` and return it from its overriden `getType()` method. + */ + virtual EventPayloadType getType() const = 0; }; using SharedEventPayload = std::shared_ptr; diff --git a/packages/react-native/ReactCommon/react/renderer/core/EventPayloadType.h b/packages/react-native/ReactCommon/react/renderer/core/EventPayloadType.h new file mode 100644 index 00000000000000..35d1b6e56fef69 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/core/EventPayloadType.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +namespace facebook::react { + +enum class EventPayloadType { ValueFactory, PointerEvent }; + +} diff --git a/packages/react-native/ReactCommon/react/renderer/core/ValueFactoryEventPayload.cpp b/packages/react-native/ReactCommon/react/renderer/core/ValueFactoryEventPayload.cpp index fc5e81d3646f76..3d13654910d48d 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/ValueFactoryEventPayload.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/ValueFactoryEventPayload.cpp @@ -16,4 +16,8 @@ jsi::Value ValueFactoryEventPayload::asJSIValue(jsi::Runtime &runtime) const { return valueFactory_(runtime); } +EventPayloadType ValueFactoryEventPayload::getType() const { + return EventPayloadType::ValueFactory; +} + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/core/ValueFactoryEventPayload.h b/packages/react-native/ReactCommon/react/renderer/core/ValueFactoryEventPayload.h index 08486966dd5905..205929a8e020af 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/ValueFactoryEventPayload.h +++ b/packages/react-native/ReactCommon/react/renderer/core/ValueFactoryEventPayload.h @@ -16,6 +16,7 @@ class ValueFactoryEventPayload : public EventPayload { public: explicit ValueFactoryEventPayload(ValueFactory factory); jsi::Value asJSIValue(jsi::Runtime &runtime) const override; + EventPayloadType getType() const override; private: ValueFactory valueFactory_; diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/PointerEventsProcessor.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/PointerEventsProcessor.cpp new file mode 100644 index 00000000000000..04b409cffe7de2 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/PointerEventsProcessor.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "PointerEventsProcessor.h" + +namespace facebook::react { + +void PointerEventsProcessor::interceptPointerEvent( + jsi::Runtime &runtime, + EventTarget const *eventTarget, + std::string const &type, + ReactEventPriority priority, + PointerEvent const &event, + DispatchEvent const &eventDispatcher) { + // TODO: implement pointer capture redirection + eventDispatcher(runtime, eventTarget, type, priority, event); +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/PointerEventsProcessor.h b/packages/react-native/ReactCommon/react/renderer/uimanager/PointerEventsProcessor.h new file mode 100644 index 00000000000000..87953bd88bff40 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/PointerEventsProcessor.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace facebook::react { + +using DispatchEvent = std::function; + +class PointerEventsProcessor final { + public: + void interceptPointerEvent( + jsi::Runtime &runtime, + EventTarget const *eventTarget, + std::string const &type, + ReactEventPriority priority, + PointerEvent const &event, + DispatchEvent const &eventDispatcher); +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index e56132e02c226f..f44aef8ecedfe2 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -96,6 +97,32 @@ void UIManagerBinding::dispatchEvent( const EventPayload &eventPayload) const { SystraceSection s("UIManagerBinding::dispatchEvent", "type", type); + if (eventPayload.getType() == EventPayloadType::PointerEvent) { + auto pointerEvent = static_cast(eventPayload); + auto dispatchCallback = [this]( + jsi::Runtime &runtime, + EventTarget const *eventTarget, + std::string const &type, + ReactEventPriority priority, + const EventPayload &eventPayload) { + this->dispatchEventToJS( + runtime, eventTarget, type, priority, eventPayload); + }; + pointerEventsProcessor_.interceptPointerEvent( + runtime, eventTarget, type, priority, pointerEvent, dispatchCallback); + } else { + dispatchEventToJS(runtime, eventTarget, type, priority, eventPayload); + } +} + +void UIManagerBinding::dispatchEventToJS( + jsi::Runtime &runtime, + EventTarget const *eventTarget, + std::string const &type, + ReactEventPriority priority, + const EventPayload &eventPayload) const { + SystraceSection s("UIManagerBinding::dispatchEventToJS", "type", type); + auto payload = eventPayload.asJSIValue(runtime); // If a payload is null, the factory has decided to cancel the event diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.h index 7f5adc03f4c122..cd55d271385e05 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -71,8 +72,20 @@ class UIManagerBinding : public jsi::HostObject { UIManager &getUIManager(); private: + /* + * Internal method that sends the event to JS. Should only be called from + * UIManagerBinding::dispatchEvent. + */ + void dispatchEventToJS( + jsi::Runtime &runtime, + EventTarget const *eventTarget, + std::string const &type, + ReactEventPriority priority, + const EventPayload &payload) const; + std::shared_ptr uiManager_; std::unique_ptr eventHandler_; + mutable PointerEventsProcessor pointerEventsProcessor_; mutable ReactEventPriority currentEventPriority_; };