Skip to content

Commit

Permalink
Refactor conditional event emitting to the C++ layer (#38674)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #38674

Changelog: [Internal] - Refactor conditional pointer event emitting to the C++ layer

Some background: early on in the implementation of Pointer Events a concern was brought up that events related to hovering pointers could saturate the JS thread if they were fired all the time unconditionally, so as a mitigation we would check in native to see if listeners in the tree were listening for those events and only fire them if there were listeners.

Now since we're going to be moving some of the event derivation logic to the C++ layer we need to receive all the events — but recreate the conditional firing in the C++ layer so we can still avoid saturating the JS thread. That's what this diff does.

The only change I see being potentially contraversial is the fact that I needed a way to turn an `EventTarget` (the only information I receive regarding which node the event is firing on) to its cooresponding `ShadowNode` which I did in the method `GetShadowNodeFromEventTarget`. It essentially does the exact same thing the `getNodeFromInternalInstanceHandle` method in `ReactNativePublicCompat.js`, but in C++ against the JSI API. I don't know if there's a better way to do this but this was the best one I came up with that actually works.

Reviewed By: NickGerleman

Differential Revision: D47852371

fbshipit-source-id: be47c616274bb0a9bd3e226152fa1c4ce100e617
  • Loading branch information
vincentriemer committed Aug 10, 2023
1 parent bd68875 commit 38e3219
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 77 deletions.
60 changes: 8 additions & 52 deletions packages/react-native/React/Fabric/RCTSurfacePointerHandler.mm
Original file line number Diff line number Diff line change
Expand Up @@ -396,28 +396,6 @@ static void UpdateActivePointerWithUITouch(
activePointer.modifierFlags = uiEvent.modifierFlags;
}

static BOOL IsViewListeningToEvent(RCTReactTaggedView *taggedView, ViewEvents::Offset eventType)
{
UIView *view = taggedView.view;
if (view && [view.class conformsToProtocol:@protocol(RCTComponentViewProtocol)]) {
auto props = ((id<RCTComponentViewProtocol>)view).props;
if (SharedViewProps viewProps = std::dynamic_pointer_cast<ViewProps const>(props)) {
return viewProps->events[eventType];
}
}
return NO;
}

static BOOL IsAnyViewInPathListeningToEvent(NSOrderedSet<RCTReactTaggedView *> *viewPath, ViewEvents::Offset eventType)
{
for (RCTReactTaggedView *taggedView in viewPath) {
if (IsViewListeningToEvent(taggedView, eventType)) {
return YES;
}
}
return NO;
}

/**
* Given an ActivePointer determine if it is still within the same event target tree as
* the one which initiated the pointer gesture.
Expand Down Expand Up @@ -634,8 +612,7 @@ - (void)_dispatchActivePointers:(std::vector<ActivePointer>)activePointers event
{
for (const auto &activePointer : activePointers) {
PointerEvent pointerEvent = CreatePointerEventFromActivePointer(activePointer, eventType, _rootComponentView);
NSOrderedSet<RCTReactTaggedView *> *eventPathViews = [self handleIncomingPointerEvent:pointerEvent
onView:activePointer.componentView];
[self handleIncomingPointerEvent:pointerEvent onView:activePointer.componentView];

SharedTouchEventEmitter eventEmitter = GetTouchEmitterFromView(
activePointer.componentView,
Expand All @@ -648,12 +625,7 @@ - (void)_dispatchActivePointers:(std::vector<ActivePointer>)activePointers event
break;
}
case RCTPointerEventTypeMove: {
BOOL hasMoveEventListeners =
IsAnyViewInPathListeningToEvent(eventPathViews, ViewEvents::Offset::PointerMove) ||
IsAnyViewInPathListeningToEvent(eventPathViews, ViewEvents::Offset::PointerMoveCapture);
if (hasMoveEventListeners) {
eventEmitter->onPointerMove(pointerEvent);
}
eventEmitter->onPointerMove(pointerEvent);
break;
}
case RCTPointerEventTypeEnd: {
Expand Down Expand Up @@ -792,11 +764,9 @@ - (void)hovering:(UIHoverGestureRecognizer *)recognizer
PointerEvent event = CreatePointerEventFromIncompleteHoverData(
pointerId, pointerType, clientLocation, screenLocation, offsetLocation, modifierFlags);

NSOrderedSet<RCTReactTaggedView *> *eventPathViews = [self handleIncomingPointerEvent:event onView:targetView];
[self handleIncomingPointerEvent:event onView:targetView];
SharedTouchEventEmitter eventEmitter = GetTouchEmitterFromView(targetView, offsetLocation);
BOOL hasMoveEventListeners = IsAnyViewInPathListeningToEvent(eventPathViews, ViewEvents::Offset::PointerMove) ||
IsAnyViewInPathListeningToEvent(eventPathViews, ViewEvents::Offset::PointerMoveCapture);
if (eventEmitter != nil && hasMoveEventListeners) {
if (eventEmitter != nil) {
eventEmitter->onPointerMove(event);
}
}
Expand Down Expand Up @@ -831,10 +801,9 @@ - (void)hovering:(UIHoverGestureRecognizer *)recognizer

// Out
if (prevTargetView != nil && prevTargetTaggedView.tag != targetTaggedView.tag) {
BOOL shouldEmitOutEvent = IsAnyViewInPathListeningToEvent(currentlyHoveredViews, ViewEvents::Offset::PointerOut);
SharedTouchEventEmitter eventEmitter =
GetTouchEmitterFromView(prevTargetView, [_rootComponentView convertPoint:clientLocation toView:prevTargetView]);
if (shouldEmitOutEvent && eventEmitter != nil) {
if (eventEmitter != nil) {
eventEmitter->onPointerOut(event);
}
}
Expand All @@ -847,20 +816,14 @@ - (void)hovering:(UIHoverGestureRecognizer *)recognizer
// we reverse iterate (now from target to root), actually emitting the events.
NSMutableOrderedSet<UIView *> *viewsToEmitLeaveEventsTo = [NSMutableOrderedSet orderedSet];

BOOL hasParentLeaveListener = NO;
for (RCTReactTaggedView *taggedView in [currentlyHoveredViews reverseObjectEnumerator]) {
UIView *componentView = taggedView.view;

BOOL shouldEmitEvent = componentView != nil &&
(hasParentLeaveListener || IsViewListeningToEvent(taggedView, ViewEvents::Offset::PointerLeave));
BOOL shouldEmitEvent = componentView != nil;

if (shouldEmitEvent && ![eventPathViews containsObject:taggedView]) {
[viewsToEmitLeaveEventsTo addObject:componentView];
}

if (shouldEmitEvent && !hasParentLeaveListener) {
hasParentLeaveListener = YES;
}
}

for (UIView *componentView in [viewsToEmitLeaveEventsTo reverseObjectEnumerator]) {
Expand All @@ -873,10 +836,9 @@ - (void)hovering:(UIHoverGestureRecognizer *)recognizer

// Over
if (targetView != nil && prevTargetTaggedView.tag != targetTaggedView.tag) {
BOOL shouldEmitOverEvent = IsAnyViewInPathListeningToEvent(eventPathViews, ViewEvents::Offset::PointerOver);
SharedTouchEventEmitter eventEmitter =
GetTouchEmitterFromView(targetView, [_rootComponentView convertPoint:clientLocation toView:targetView]);
if (shouldEmitOverEvent && eventEmitter != nil) {
if (eventEmitter != nil) {
eventEmitter->onPointerOver(event);
}
}
Expand All @@ -888,12 +850,10 @@ - (void)hovering:(UIHoverGestureRecognizer *)recognizer
// or if one of its parents is listening in case those listeners care about the capturing phase. Adding the ability
// for native to distinguish between capturing listeners and not could be an optimization to further reduce the number
// of events we send to JS
BOOL hasParentEnterListener = NO;
for (RCTReactTaggedView *taggedView in [eventPathViews reverseObjectEnumerator]) {
UIView *componentView = taggedView.view;

BOOL shouldEmitEvent = componentView != nil &&
(hasParentEnterListener || IsViewListeningToEvent(taggedView, ViewEvents::Offset::PointerEnter));
BOOL shouldEmitEvent = componentView != nil;

if (shouldEmitEvent && ![currentlyHoveredViews containsObject:taggedView]) {
SharedTouchEventEmitter eventEmitter =
Expand All @@ -902,10 +862,6 @@ - (void)hovering:(UIHoverGestureRecognizer *)recognizer
eventEmitter->onPointerEnter(event);
}
}

if (shouldEmitEvent && !hasParentEnterListener) {
hasParentEnterListener = YES;
}
}

[_currentlyHoveredViewsPerPointer setObject:eventPathViews forKey:@(pointerId)];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ struct ViewEvents {
ClickCapture = 31,
GotPointerCapture = 32,
LostPointerCapture = 33,
PointerDown = 34,
PointerDownCapture = 35,
PointerUp = 36,
PointerUpCapture = 37,
};

constexpr bool operator[](const Offset offset) const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,18 +616,32 @@ static inline ViewEvents convertRawProp(
"onClickCapture",
sourceValue[Offset::ClickCapture],
defaultValue[Offset::ClickCapture]);
result[Offset::GotPointerCapture] = convertRawProp(
result[Offset::PointerDown] = convertRawProp(
context,
rawProps,
"onGotPointerCapture",
sourceValue[Offset::GotPointerCapture],
defaultValue[Offset::GotPointerCapture]);
result[Offset::LostPointerCapture] = convertRawProp(
"onPointerDown",
sourceValue[Offset::PointerDown],
defaultValue[Offset::PointerDown]);
result[Offset::PointerDownCapture] = convertRawProp(
context,
rawProps,
"onLostPointerCapture",
sourceValue[Offset::LostPointerCapture],
defaultValue[Offset::LostPointerCapture]);
"onPointerDownCapture",
sourceValue[Offset::PointerDownCapture],
defaultValue[Offset::PointerDownCapture]);
result[Offset::PointerUp] = convertRawProp(
context,
rawProps,
"onPointerUp",
sourceValue[Offset::PointerUp],
defaultValue[Offset::PointerUp]);
result[Offset::PointerUpCapture] = convertRawProp(
context,
rawProps,
"onPointerUpCapture",
sourceValue[Offset::PointerUpCapture],
defaultValue[Offset::PointerUpCapture]);
// TODO: gotPointerCapture & lostPointerCapture (causes issues with
// RawPropsKey for some reason)

// PanResponder callbacks
result[Offset::MoveShouldSetResponder] = convertRawProp(
Expand Down
Loading

0 comments on commit 38e3219

Please sign in to comment.