Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -261,5 +261,66 @@ if (isOSS) {
root.destroy();
},
},
)
.test(
'dispatch event, nested 50 deep (bubbling), stable tree',
() => {
Fantom.dispatchNativeEvent(
ref,
'onPointerUp',
{x: 0, y: 0},
{
category: Fantom.NativeEventCategory.Discrete,
},
);
},
{
beforeAll: () => {
ref = React.createRef();
root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(createNestedViews(50, ref));
});
},
afterAll: () => {
root.destroy();
},
},
)
.test(
'dispatch event, nested 50 deep (no handlers on ancestors), stable tree',
() => {
Fantom.dispatchNativeEvent(
ref,
'onPointerUp',
{x: 0, y: 0},
{
category: Fantom.NativeEventCategory.Discrete,
},
);
},
{
beforeAll: () => {
ref = React.createRef();
root = Fantom.createRoot();
Fantom.runTask(() => {
let views: React.MixedElement = (
<View
ref={ref}
collapsable={false}
onPointerUp={() => {}}
style={{width: 10, height: 10}}
/>
);
for (let i = 0; i < 50; i++) {
views = <View collapsable={false}>{views}</View>;
}
root.render(views);
});
},
afterAll: () => {
root.destroy();
},
},
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
setCurrentTarget,
setTarget,
} from '../../webapis/dom/events/internals/EventInternals';
import {getEventTargetParent} from '../../webapis/dom/events/internals/EventTargetInternals';
import {
getCurrentProps,
getNativeElementReference,
Expand Down Expand Up @@ -78,6 +79,23 @@ function isEndish(topLevelType: string): boolean {
return topLevelType === 'topTouchEnd' || topLevelType === 'topTouchCancel';
}

// Routes through the event-dispatch parent cache (shared with
// `EventTarget.getEventPath`) and stops the walk when the parent is not
// an element (e.g., when reaching the document at the top of the tree).
function getResponderParentElement(
node: ReadOnlyElement,
): ReadOnlyElement | null {
// `ReadOnlyElement` extends `EventTarget` at runtime when the new
// event-dispatching pipeline is enabled (the only case this module runs in).
// $FlowFixMe[incompatible-type]
const eventTarget: EventTarget = node;
const parent = getEventTargetParent(eventTarget);
if (parent instanceof ReadOnlyElement) {
return parent;
}
return null;
}

/**
* Return the lowest common ancestor of A and B, or null if they are in
* different trees.
Expand All @@ -95,12 +113,12 @@ function getLowestCommonAncestor(
}

// Walk up from A until we find an ancestor that contains B
let current: ?ReadOnlyElement = instA.parentElement;
let current: ?ReadOnlyElement = getResponderParentElement(instA);
while (current != null) {
if (current.contains(instB)) {
return current;
}
current = current.parentElement;
current = getResponderParentElement(current);
}

return null;
Expand Down Expand Up @@ -265,7 +283,7 @@ function negotiateResponder(
}

const dispatchNode: ReadOnlyElement | null = skipSelf
? negotiationNode.parentElement
? getResponderParentElement(negotiationNode)
: negotiationNode;
if (dispatchNode == null) {
return null;
Expand All @@ -276,7 +294,7 @@ function negotiateResponder(
let node: ?ReadOnlyElement = dispatchNode;
while (node != null) {
path.unshift(node);
node = node.parentElement;
node = getResponderParentElement(node);
}

const dispatchConfig = responderEventTypes[shouldSetEventName];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
EVENT_TARGET_GET_DECLARATIVE_LISTENER_KEY,
EVENT_TARGET_GET_THE_PARENT_KEY,
INTERNAL_DISPATCH_METHOD_KEY,
getEventTargetParent,
} from './internals/EventTargetInternals';

export type EventCallback = (event: Event) => void;
Expand Down Expand Up @@ -341,8 +342,7 @@ function getEventPath(

while (target != null) {
path.push(target);
// $FlowExpectedError[prop-missing]
target = target[EVENT_TARGET_GET_THE_PARENT_KEY]();
target = getEventTargetParent(target);
}

return path;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ export const INTERNAL_DISPATCH_METHOD_KEY: symbol = Symbol(
'EventTarget[dispatch]',
);

const EVENT_DISPATCH_PARENT_CACHE_KEY: symbol = Symbol(
'EventTarget[dispatch parent cache]',
);

export function getEventTargetParent(target: EventTarget): EventTarget | null {
// The slot is `undefined` until populated; a populated slot may hold
// `null` (no parent), so check against `undefined` rather than nullishness.
// $FlowExpectedError[prop-missing] symbol-keyed slot
const cached: EventTarget | null | void =
// $FlowExpectedError[prop-missing] symbol-keyed slot
target[EVENT_DISPATCH_PARENT_CACHE_KEY];
if (cached !== undefined) {
return cached;
}
// $FlowExpectedError[prop-missing] symbol-keyed method
const parent: EventTarget | null = target[EVENT_TARGET_GET_THE_PARENT_KEY]();
// $FlowExpectedError[prop-missing] symbol-keyed slot
target[EVENT_DISPATCH_PARENT_CACHE_KEY] = parent;
return parent;
}

/**
* Dispatches a trusted event to the given event target. Mirrors the
* `dispatchEvent` method on `EventTarget`: returns `false` if the event
Expand Down
Loading