mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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
192 lines
6.4 KiB
C++
192 lines
6.4 KiB
C++
/*
|
|
* 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 {
|
|
|
|
static PointerEventTarget retargetPointerEvent(
|
|
PointerEvent const &event,
|
|
ShadowNode const &nodeToTarget,
|
|
UIManager const &uiManager) {
|
|
PointerEvent retargetedEvent(event);
|
|
|
|
// TODO: is dereferencing latestNodeToTarget without null checking safe?
|
|
auto latestNodeToTarget = uiManager.getNewestCloneOfShadowNode(nodeToTarget);
|
|
|
|
// Adjust offsetX/Y to be relative to the retargeted node
|
|
// HACK: This is a basic/incomplete implementation which simply subtracts
|
|
// the retargeted node's origin from the original event's client coordinates.
|
|
// More work will be needed to properly take non-trival transforms into
|
|
// account.
|
|
auto layoutMetrics = uiManager.getRelativeLayoutMetrics(
|
|
*latestNodeToTarget, nullptr, {/* .includeTransform */ true});
|
|
retargetedEvent.offsetPoint = {
|
|
event.clientPoint.x - layoutMetrics.frame.origin.x,
|
|
event.clientPoint.y - layoutMetrics.frame.origin.y,
|
|
};
|
|
|
|
// Retrieve the event target of the retargeted node
|
|
auto retargetedEventTarget =
|
|
latestNodeToTarget->getEventEmitter()->getEventTarget();
|
|
|
|
PointerEventTarget result = {};
|
|
result.event = retargetedEvent;
|
|
result.target = retargetedEventTarget;
|
|
return result;
|
|
}
|
|
|
|
static ShadowNode::Shared getCaptureTargetOverride(
|
|
PointerIdentifier pointerId,
|
|
CaptureTargetOverrideRegistry ®istry) {
|
|
auto pendingPointerItr = registry.find(pointerId);
|
|
if (pendingPointerItr == registry.end()) {
|
|
return nullptr;
|
|
}
|
|
|
|
ShadowNode::Weak maybeTarget = pendingPointerItr->second;
|
|
if (maybeTarget.expired()) {
|
|
// target has expired so it should functionally behave the same as if it
|
|
// was removed from the override list.
|
|
registry.erase(pointerId);
|
|
return nullptr;
|
|
}
|
|
|
|
return maybeTarget.lock();
|
|
}
|
|
|
|
void PointerEventsProcessor::interceptPointerEvent(
|
|
jsi::Runtime &runtime,
|
|
EventTarget const *target,
|
|
std::string const &type,
|
|
ReactEventPriority priority,
|
|
PointerEvent const &event,
|
|
DispatchEvent const &eventDispatcher,
|
|
UIManager const &uiManager) {
|
|
// Process all pending pointer capture assignments
|
|
processPendingPointerCapture(event, runtime, eventDispatcher, uiManager);
|
|
|
|
PointerEvent pointerEvent(event);
|
|
EventTarget const *eventTarget = target;
|
|
|
|
// Retarget the event if it has a pointer capture override target
|
|
auto overrideTarget = getCaptureTargetOverride(
|
|
pointerEvent.pointerId, pendingPointerCaptureTargetOverrides_);
|
|
if (overrideTarget != nullptr &&
|
|
overrideTarget->getTag() != eventTarget->getTag()) {
|
|
auto retargeted =
|
|
retargetPointerEvent(pointerEvent, *overrideTarget, uiManager);
|
|
|
|
pointerEvent = retargeted.event;
|
|
eventTarget = retargeted.target.get();
|
|
}
|
|
|
|
eventTarget->retain(runtime);
|
|
eventDispatcher(runtime, eventTarget, type, priority, pointerEvent);
|
|
eventTarget->release(runtime);
|
|
|
|
// Implicit pointer capture release
|
|
if (overrideTarget != nullptr &&
|
|
(type == "topPointerUp" || type == "topPointerCancel")) {
|
|
releasePointerCapture(pointerEvent.pointerId, overrideTarget.get());
|
|
processPendingPointerCapture(
|
|
pointerEvent, runtime, eventDispatcher, uiManager);
|
|
}
|
|
}
|
|
|
|
void PointerEventsProcessor::setPointerCapture(
|
|
PointerIdentifier pointerId,
|
|
ShadowNode::Shared const &shadowNode) {
|
|
// TODO: Throw DOMException with name "NotFoundError" when pointerId does not
|
|
// match any of the active pointers
|
|
pendingPointerCaptureTargetOverrides_[pointerId] = shadowNode;
|
|
}
|
|
|
|
void PointerEventsProcessor::releasePointerCapture(
|
|
PointerIdentifier pointerId,
|
|
ShadowNode const *shadowNode) {
|
|
// TODO: Throw DOMException with name "NotFoundError" when pointerId does not
|
|
// match any of the active pointers
|
|
|
|
// We only clear the pointer's capture target override if release was called
|
|
// on the shadowNode which has the capture override, otherwise the result
|
|
// should no-op
|
|
auto pendingTarget = getCaptureTargetOverride(
|
|
pointerId, pendingPointerCaptureTargetOverrides_);
|
|
if (pendingTarget != nullptr &&
|
|
pendingTarget->getTag() == shadowNode->getTag()) {
|
|
pendingPointerCaptureTargetOverrides_.erase(pointerId);
|
|
}
|
|
}
|
|
|
|
bool PointerEventsProcessor::hasPointerCapture(
|
|
PointerIdentifier pointerId,
|
|
ShadowNode const *shadowNode) {
|
|
ShadowNode::Shared pendingTarget = getCaptureTargetOverride(
|
|
pointerId, pendingPointerCaptureTargetOverrides_);
|
|
if (pendingTarget != nullptr) {
|
|
return pendingTarget->getTag() == shadowNode->getTag();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PointerEventsProcessor::processPendingPointerCapture(
|
|
PointerEvent const &event,
|
|
jsi::Runtime &runtime,
|
|
DispatchEvent const &eventDispatcher,
|
|
UIManager const &uiManager) {
|
|
auto pendingOverride = getCaptureTargetOverride(
|
|
event.pointerId, pendingPointerCaptureTargetOverrides_);
|
|
bool hasPendingOverride = pendingOverride != nullptr;
|
|
|
|
auto activeOverride = getCaptureTargetOverride(
|
|
event.pointerId, activePointerCaptureTargetOverrides_);
|
|
bool hasActiveOverride = activeOverride != nullptr;
|
|
|
|
if (!hasPendingOverride && !hasActiveOverride) {
|
|
return;
|
|
}
|
|
|
|
auto pendingOverrideTag =
|
|
(hasPendingOverride) ? pendingOverride->getTag() : -1;
|
|
auto activeOverrideTag = (hasActiveOverride) ? activeOverride->getTag() : -1;
|
|
|
|
if (hasActiveOverride && activeOverrideTag != pendingOverrideTag) {
|
|
auto retargeted = retargetPointerEvent(event, *activeOverride, uiManager);
|
|
|
|
retargeted.target->retain(runtime);
|
|
eventDispatcher(
|
|
runtime,
|
|
retargeted.target.get(),
|
|
"topLostPointerCapture",
|
|
ReactEventPriority::Discrete,
|
|
retargeted.event);
|
|
retargeted.target->release(runtime);
|
|
}
|
|
|
|
if (hasPendingOverride && activeOverrideTag != pendingOverrideTag) {
|
|
auto retargeted = retargetPointerEvent(event, *pendingOverride, uiManager);
|
|
|
|
retargeted.target->retain(runtime);
|
|
eventDispatcher(
|
|
runtime,
|
|
retargeted.target.get(),
|
|
"topGotPointerCapture",
|
|
ReactEventPriority::Discrete,
|
|
retargeted.event);
|
|
retargeted.target->release(runtime);
|
|
}
|
|
|
|
if (!hasPendingOverride) {
|
|
activePointerCaptureTargetOverrides_.erase(event.pointerId);
|
|
} else {
|
|
activePointerCaptureTargetOverrides_[event.pointerId] = pendingOverride;
|
|
}
|
|
}
|
|
|
|
} // namespace facebook::react
|