Back out "Refactor conditional event emitting to the C++ layer" (#38973)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/38973

Original commit changeset: 6c00c2fcfdfd

Original Phabricator Diff: D47852371

When #38674 and #38959 are combined, they cause Android apps on Fabric to crash. #38959 is the more urgent fix, so backing out #38674.

## Changelog:
[General] [Fixed] - Rolling back change that broke e2e tests until we can figure out why the e2e tests are broken

Reviewed By: NickGerleman

Differential Revision: D48288752

fbshipit-source-id: b52e28936bbebd21bd3b2f49f9a233f295ba6248
This commit is contained in:
Eric Rozell
2023-08-11 21:36:01 -07:00
committed by Facebook GitHub Bot
parent 9f7dfcc35f
commit ae88aef12c
4 changed files with 77 additions and 202 deletions
@@ -396,6 +396,28 @@ 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.
@@ -612,7 +634,8 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)act
{
for (const auto &activePointer : activePointers) {
PointerEvent pointerEvent = CreatePointerEventFromActivePointer(activePointer, eventType, _rootComponentView);
[self handleIncomingPointerEvent:pointerEvent onView:activePointer.componentView];
NSOrderedSet<RCTReactTaggedView *> *eventPathViews = [self handleIncomingPointerEvent:pointerEvent
onView:activePointer.componentView];
SharedTouchEventEmitter eventEmitter = GetTouchEmitterFromView(
activePointer.componentView,
@@ -625,7 +648,12 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)act
break;
}
case RCTPointerEventTypeMove: {
eventEmitter->onPointerMove(pointerEvent);
BOOL hasMoveEventListeners =
IsAnyViewInPathListeningToEvent(eventPathViews, ViewEvents::Offset::PointerMove) ||
IsAnyViewInPathListeningToEvent(eventPathViews, ViewEvents::Offset::PointerMoveCapture);
if (hasMoveEventListeners) {
eventEmitter->onPointerMove(pointerEvent);
}
break;
}
case RCTPointerEventTypeEnd: {
@@ -764,9 +792,11 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)act
PointerEvent event = CreatePointerEventFromIncompleteHoverData(
pointerId, pointerType, clientLocation, screenLocation, offsetLocation, modifierFlags);
[self handleIncomingPointerEvent:event onView:targetView];
NSOrderedSet<RCTReactTaggedView *> *eventPathViews = [self handleIncomingPointerEvent:event onView:targetView];
SharedTouchEventEmitter eventEmitter = GetTouchEmitterFromView(targetView, offsetLocation);
if (eventEmitter != nil) {
BOOL hasMoveEventListeners = IsAnyViewInPathListeningToEvent(eventPathViews, ViewEvents::Offset::PointerMove) ||
IsAnyViewInPathListeningToEvent(eventPathViews, ViewEvents::Offset::PointerMoveCapture);
if (eventEmitter != nil && hasMoveEventListeners) {
eventEmitter->onPointerMove(event);
}
}
@@ -801,9 +831,10 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)act
// 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 (eventEmitter != nil) {
if (shouldEmitOutEvent && eventEmitter != nil) {
eventEmitter->onPointerOut(event);
}
}
@@ -816,14 +847,20 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)act
// 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;
BOOL shouldEmitEvent = componentView != nil &&
(hasParentLeaveListener || IsViewListeningToEvent(taggedView, ViewEvents::Offset::PointerLeave));
if (shouldEmitEvent && ![eventPathViews containsObject:taggedView]) {
[viewsToEmitLeaveEventsTo addObject:componentView];
}
if (shouldEmitEvent && !hasParentLeaveListener) {
hasParentLeaveListener = YES;
}
}
for (UIView *componentView in [viewsToEmitLeaveEventsTo reverseObjectEnumerator]) {
@@ -836,9 +873,10 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)act
// 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 (eventEmitter != nil) {
if (shouldEmitOverEvent && eventEmitter != nil) {
eventEmitter->onPointerOver(event);
}
}
@@ -850,10 +888,12 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)act
// 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;
BOOL shouldEmitEvent = componentView != nil &&
(hasParentEnterListener || IsViewListeningToEvent(taggedView, ViewEvents::Offset::PointerEnter));
if (shouldEmitEvent && ![currentlyHoveredViews containsObject:taggedView]) {
SharedTouchEventEmitter eventEmitter =
@@ -862,6 +902,10 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)act
eventEmitter->onPointerEnter(event);
}
}
if (shouldEmitEvent && !hasParentEnterListener) {
hasParentEnterListener = YES;
}
}
[_currentlyHoveredViewsPerPointer setObject:eventPathViews forKey:@(pointerId)];
@@ -62,10 +62,6 @@ struct ViewEvents {
ClickCapture = 31,
GotPointerCapture = 32,
LostPointerCapture = 33,
PointerDown = 34,
PointerDownCapture = 35,
PointerUp = 36,
PointerUpCapture = 37,
};
constexpr bool operator[](const Offset offset) const {
@@ -616,32 +616,18 @@ static inline ViewEvents convertRawProp(
"onClickCapture",
sourceValue[Offset::ClickCapture],
defaultValue[Offset::ClickCapture]);
result[Offset::PointerDown] = convertRawProp(
result[Offset::GotPointerCapture] = convertRawProp(
context,
rawProps,
"onPointerDown",
sourceValue[Offset::PointerDown],
defaultValue[Offset::PointerDown]);
result[Offset::PointerDownCapture] = convertRawProp(
"onGotPointerCapture",
sourceValue[Offset::GotPointerCapture],
defaultValue[Offset::GotPointerCapture]);
result[Offset::LostPointerCapture] = convertRawProp(
context,
rawProps,
"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)
"onLostPointerCapture",
sourceValue[Offset::LostPointerCapture],
defaultValue[Offset::LostPointerCapture]);
// PanResponder callbacks
result[Offset::MoveShouldSetResponder] = convertRawProp(
@@ -7,80 +7,9 @@
#include "PointerEventsProcessor.h"
#include <react/renderer/components/view/ViewShadowNode.h>
namespace facebook::react {
static ShadowNode::Shared GetShadowNodeFromEventTarget(
jsi::Runtime &runtime,
EventTarget const &target) {
auto instanceHandle = target.getInstanceHandle(runtime);
if (instanceHandle.isObject()) {
auto handleObj = instanceHandle.asObject(runtime);
if (handleObj.hasProperty(runtime, "stateNode")) {
auto stateNode = handleObj.getProperty(runtime, "stateNode");
if (stateNode.isObject()) {
auto stateNodeObj = stateNode.asObject(runtime);
if (stateNodeObj.hasProperty(runtime, "node")) {
auto node = stateNodeObj.getProperty(runtime, "node");
return shadowNodeFromValue(runtime, node);
}
}
}
}
return nullptr;
}
static bool IsViewListeningToEvents(
ShadowNode const &shadowNode,
std::initializer_list<ViewEvents::Offset> eventTypes) {
if (auto viewShadowNode = traitCast<ViewShadowNode const *>(&shadowNode)) {
auto &viewProps = viewShadowNode->getConcreteProps();
for (const ViewEvents::Offset eventType : eventTypes) {
if (viewProps.events[eventType]) {
return true;
}
}
}
return false;
}
static bool IsAnyViewInPathToRootListeningToEvents(
UIManager const &uiManager,
ShadowNode const &shadowNode,
std::initializer_list<ViewEvents::Offset> eventTypes) {
// Check the target view first
if (IsViewListeningToEvents(shadowNode, eventTypes)) {
return true;
}
// Retrieve the node's root & a list of nodes between the target and the root
auto owningRootShadowNode = ShadowNode::Shared{};
uiManager.getShadowTreeRegistry().visit(
shadowNode.getSurfaceId(),
[&owningRootShadowNode](ShadowTree const &shadowTree) {
owningRootShadowNode = shadowTree.getCurrentRevision().rootShadowNode;
});
if (owningRootShadowNode == nullptr) {
return false;
}
auto &nodeFamily = shadowNode.getFamily();
auto ancestors = nodeFamily.getAncestors(*owningRootShadowNode);
// Check for listeners from the target's parent to the root
for (auto it = ancestors.rbegin(); it != ancestors.rend(); it++) {
auto &currentNode = it->first.get();
if (IsViewListeningToEvents(currentNode, eventTypes)) {
return true;
}
}
return false;
}
static PointerEventTarget RetargetPointerEvent(
static PointerEventTarget retargetPointerEvent(
PointerEvent const &event,
ShadowNode const &nodeToTarget,
UIManager const &uiManager) {
@@ -130,72 +59,6 @@ static ShadowNode::Shared getCaptureTargetOverride(
return maybeTarget.lock();
}
/*
* Centralized method which determines if an event should be sent to JS by
* inspecing the listeners in the target's view path.
*/
static bool ShouldEmitPointerEvent(
ShadowNode const &targetNode,
std::string const &type,
UIManager const &uiManager) {
if (type == "topPointerDown") {
return IsAnyViewInPathToRootListeningToEvents(
uiManager,
targetNode,
{ViewEvents::Offset::PointerDown,
ViewEvents::Offset::PointerDownCapture});
} else if (type == "topPointerUp") {
return IsAnyViewInPathToRootListeningToEvents(
uiManager,
targetNode,
{ViewEvents::Offset::PointerUp, ViewEvents::Offset::PointerUpCapture});
} else if (type == "topPointerMove") {
return IsAnyViewInPathToRootListeningToEvents(
uiManager,
targetNode,
{ViewEvents::Offset::PointerMove,
ViewEvents::Offset::PointerMoveCapture});
} else if (type == "topPointerEnter") {
// This event goes through the capturing phase in full but only bubble
// through the target and no futher up the tree
return IsViewListeningToEvents(
targetNode, {ViewEvents::Offset::PointerEnter}) ||
IsAnyViewInPathToRootListeningToEvents(
uiManager,
targetNode,
{ViewEvents::Offset::PointerEnterCapture});
} else if (type == "topPointerLeave") {
// This event goes through the capturing phase in full but only bubble
// through the target and no futher up the tree
return IsViewListeningToEvents(
targetNode, {ViewEvents::Offset::PointerLeave}) ||
IsAnyViewInPathToRootListeningToEvents(
uiManager,
targetNode,
{ViewEvents::Offset::PointerLeaveCapture});
} else if (type == "topPointerOver") {
return IsAnyViewInPathToRootListeningToEvents(
uiManager,
targetNode,
{ViewEvents::Offset::PointerOver,
ViewEvents::Offset::PointerOverCapture});
} else if (type == "topPointerOut") {
return IsAnyViewInPathToRootListeningToEvents(
uiManager,
targetNode,
{ViewEvents::Offset::PointerOut,
ViewEvents::Offset::PointerOutCapture});
} else if (type == "topClick") {
return IsAnyViewInPathToRootListeningToEvents(
uiManager,
targetNode,
{ViewEvents::Offset::Click, ViewEvents::Offset::ClickCapture});
}
// This is more of an optimization method so if we encounter a type which
// has not been specifically addressed above we should just let it through.
return true;
}
void PointerEventsProcessor::interceptPointerEvent(
jsi::Runtime &runtime,
EventTarget const *target,
@@ -216,18 +79,14 @@ void PointerEventsProcessor::interceptPointerEvent(
if (overrideTarget != nullptr &&
overrideTarget->getTag() != eventTarget->getTag()) {
auto retargeted =
RetargetPointerEvent(pointerEvent, *overrideTarget, uiManager);
retargetPointerEvent(pointerEvent, *overrideTarget, uiManager);
pointerEvent = retargeted.event;
eventTarget = retargeted.target.get();
}
eventTarget->retain(runtime);
auto shadowNode = GetShadowNodeFromEventTarget(runtime, *eventTarget);
if (shadowNode != nullptr &&
ShouldEmitPointerEvent(*shadowNode, type, uiManager)) {
eventDispatcher(runtime, eventTarget, type, priority, pointerEvent);
}
eventDispatcher(runtime, eventTarget, type, priority, pointerEvent);
eventTarget->release(runtime);
// Implicit pointer capture release
@@ -297,38 +156,28 @@ void PointerEventsProcessor::processPendingPointerCapture(
auto activeOverrideTag = (hasActiveOverride) ? activeOverride->getTag() : -1;
if (hasActiveOverride && activeOverrideTag != pendingOverrideTag) {
auto retargeted = RetargetPointerEvent(event, *activeOverride, uiManager);
auto retargeted = retargetPointerEvent(event, *activeOverride, uiManager);
retargeted.target->retain(runtime);
auto shadowNode = GetShadowNodeFromEventTarget(runtime, *retargeted.target);
if (shadowNode != nullptr &&
ShouldEmitPointerEvent(
*shadowNode, "topLostPointerCapture", uiManager)) {
eventDispatcher(
runtime,
retargeted.target.get(),
"topLostPointerCapture",
ReactEventPriority::Discrete,
retargeted.event);
}
eventDispatcher(
runtime,
retargeted.target.get(),
"topLostPointerCapture",
ReactEventPriority::Discrete,
retargeted.event);
retargeted.target->release(runtime);
}
if (hasPendingOverride && activeOverrideTag != pendingOverrideTag) {
auto retargeted = RetargetPointerEvent(event, *pendingOverride, uiManager);
auto retargeted = retargetPointerEvent(event, *pendingOverride, uiManager);
retargeted.target->retain(runtime);
auto shadowNode = GetShadowNodeFromEventTarget(runtime, *retargeted.target);
if (shadowNode != nullptr &&
ShouldEmitPointerEvent(
*shadowNode, "topGotPointerCapture", uiManager)) {
eventDispatcher(
runtime,
retargeted.target.get(),
"topGotPointerCapture",
ReactEventPriority::Discrete,
retargeted.event);
}
eventDispatcher(
runtime,
retargeted.target.get(),
"topGotPointerCapture",
ReactEventPriority::Discrete,
retargeted.event);
retargeted.target->release(runtime);
}