Skip to content

Commit

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

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.

(This is a second attempt at landing D48288752 which had to be backed out. It should be fine this time because of D48331909 which this diff now depends on)

Reviewed By: rozele

Differential Revision: D48322586

fbshipit-source-id: 7674c123e96ae71b18c8a3a780667ffaa2f25e7c
  • Loading branch information
vincentriemer authored and facebook-github-bot committed Aug 21, 2023
1 parent f4f1894 commit 22ee08e
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 73 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,31 @@ 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

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

0 comments on commit 22ee08e

Please sign in to comment.