Skip to content
Merged
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
45 changes: 10 additions & 35 deletions packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ import {
isFragmentContainedByFiber,
traverseFragmentInstance,
getFragmentParentHostFiber,
getNextSiblingHostFiber,
getInstanceFromHostFiber,
traverseFragmentInstanceDeeply,
fiberIsPortaledIntoHost,
Expand All @@ -70,6 +69,7 @@ import {
markNodeAsHoistable,
isOwnedInstance,
} from './ReactDOMComponentTree';
import {compareDocumentPositionForEmptyFragment} from 'shared/ReactDOMFragmentRefShared';

export {detachDeletedInstance};
import {hasRole} from './DOMAccessibilityRoles';
Expand Down Expand Up @@ -3055,40 +3055,13 @@ FragmentInstance.prototype.compareDocumentPosition = function (
const parentHostInstance =
getInstanceFromHostFiber<Instance>(parentHostFiber);

let result = Node.DOCUMENT_POSITION_DISCONNECTED;
if (children.length === 0) {
// If the fragment has no children, we can use the parent and
// siblings to determine a position.
const parentResult = parentHostInstance.compareDocumentPosition(otherNode);
result = parentResult;
if (parentHostInstance === otherNode) {
result = Node.DOCUMENT_POSITION_CONTAINS;
} else {
if (parentResult & Node.DOCUMENT_POSITION_CONTAINED_BY) {
// otherNode is one of the fragment's siblings. Use the next
// sibling to determine if its preceding or following.
const nextSiblingFiber = getNextSiblingHostFiber(this._fragmentFiber);
if (nextSiblingFiber === null) {
result = Node.DOCUMENT_POSITION_PRECEDING;
} else {
const nextSiblingInstance =
getInstanceFromHostFiber<Instance>(nextSiblingFiber);
const nextSiblingResult =
nextSiblingInstance.compareDocumentPosition(otherNode);
if (
nextSiblingResult === 0 ||
nextSiblingResult & Node.DOCUMENT_POSITION_FOLLOWING
) {
result = Node.DOCUMENT_POSITION_FOLLOWING;
} else {
result = Node.DOCUMENT_POSITION_PRECEDING;
}
}
}
}

result |= Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
return result;
return compareDocumentPositionForEmptyFragment(
this._fragmentFiber,
parentHostInstance,
otherNode,
getInstanceFromHostFiber,
);
}

const firstElement = getInstanceFromHostFiber<Instance>(children[0]);
Expand All @@ -3099,8 +3072,9 @@ FragmentInstance.prototype.compareDocumentPosition = function (
// If the fragment has been portaled into another host instance, we need to
// our best guess is to use the parent of the child instance, rather than
// the fiber tree host parent.
const firstInstance = getInstanceFromHostFiber<Instance>(children[0]);
const parentHostInstanceFromDOM = fiberIsPortaledIntoHost(this._fragmentFiber)
? (getInstanceFromHostFiber<Instance>(children[0]).parentElement: ?Instance)
? (firstInstance.parentElement: ?Instance)
: parentHostInstance;

if (parentHostInstanceFromDOM == null) {
Expand Down Expand Up @@ -3133,6 +3107,7 @@ FragmentInstance.prototype.compareDocumentPosition = function (
firstResult & Node.DOCUMENT_POSITION_FOLLOWING &&
lastResult & Node.DOCUMENT_POSITION_PRECEDING;

let result = Node.DOCUMENT_POSITION_DISCONNECTED;
if (
otherNodeIsFirstOrLastChild ||
otherNodeIsWithinFirstOrLastChild ||
Expand Down
90 changes: 77 additions & 13 deletions packages/react-native-renderer/src/ReactFiberConfigFabric.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import {HostText} from 'react-reconciler/src/ReactWorkTags';
import {
getFragmentParentHostFiber,
getInstanceFromHostFiber,
traverseFragmentInstance,
} from 'react-reconciler/src/ReactFiberTreeReflection';
Expand Down Expand Up @@ -59,6 +60,7 @@ const {
} = nativeFabricUIManager;

import {getClosestInstanceFromNode} from './ReactFabricComponentTree';
import {compareDocumentPositionForEmptyFragment} from 'shared/ReactDOMFragmentRefShared';

import {
getInspectorDataForViewTag,
Expand Down Expand Up @@ -87,7 +89,7 @@ const {get: getViewConfigForType} = ReactNativeViewConfigRegistry;
let nextReactTag = 2;

type InternalInstanceHandle = Object;
type Node = Object;

export type Type = string;
export type Props = Object;
export type Instance = {
Expand Down Expand Up @@ -344,6 +346,15 @@ export function getPublicInstanceFromInternalInstanceHandle(
return getPublicInstance(elementInstance);
}

function getPublicInstanceFromHostFiber(fiber: Fiber): PublicInstance {
const instance = getInstanceFromHostFiber<Instance>(fiber);
const publicInstance = getPublicInstance(instance);
if (publicInstance == null) {
throw new Error('Expected to find a host node. This is a bug in React.');
}
return publicInstance;
}

export function prepareForCommit(containerInfo: Container): null | Object {
// Noop
return null;
Expand Down Expand Up @@ -610,6 +621,7 @@ export type FragmentInstanceType = {
_observers: null | Set<IntersectionObserver>,
observeUsing: (observer: IntersectionObserver) => void,
unobserveUsing: (observer: IntersectionObserver) => void,
compareDocumentPosition: (otherNode: PublicInstance) => number,
};

function FragmentInstance(this: FragmentInstanceType, fragmentFiber: Fiber) {
Expand All @@ -629,12 +641,8 @@ FragmentInstance.prototype.observeUsing = function (
traverseFragmentInstance(this._fragmentFiber, observeChild, observer);
};
function observeChild(child: Fiber, observer: IntersectionObserver) {
const instance = getInstanceFromHostFiber<Instance>(child);
const publicInstance = getPublicInstance(instance);
if (publicInstance == null) {
throw new Error('Expected to find a host node. This is a bug in React.');
}
// $FlowFixMe[incompatible-call] Element types are behind a flag in RN
const publicInstance = getPublicInstanceFromHostFiber(child);
// $FlowFixMe[incompatible-call] DOM types expect Element
observer.observe(publicInstance);
return false;
}
Expand All @@ -656,16 +664,72 @@ FragmentInstance.prototype.unobserveUsing = function (
}
};
function unobserveChild(child: Fiber, observer: IntersectionObserver) {
const instance = getInstanceFromHostFiber<Instance>(child);
const publicInstance = getPublicInstance(instance);
if (publicInstance == null) {
throw new Error('Expected to find a host node. This is a bug in React.');
}
// $FlowFixMe[incompatible-call] Element types are behind a flag in RN
const publicInstance = getPublicInstanceFromHostFiber(child);
// $FlowFixMe[incompatible-call] DOM types expect Element
observer.unobserve(publicInstance);
return false;
}

// $FlowFixMe[prop-missing]
FragmentInstance.prototype.compareDocumentPosition = function (
this: FragmentInstanceType,
otherNode: PublicInstance,
): number {
const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
if (parentHostFiber === null) {
return Node.DOCUMENT_POSITION_DISCONNECTED;
}
const parentHostInstance = getPublicInstanceFromHostFiber(parentHostFiber);
const children: Array<Fiber> = [];
traverseFragmentInstance(this._fragmentFiber, collectChildren, children);
if (children.length === 0) {
return compareDocumentPositionForEmptyFragment(
this._fragmentFiber,
parentHostInstance,
otherNode,
getPublicInstanceFromHostFiber,
);
}

const firstInstance = getPublicInstanceFromHostFiber(children[0]);
const lastInstance = getPublicInstanceFromHostFiber(
children[children.length - 1],
);

// $FlowFixMe[incompatible-use] Fabric PublicInstance is opaque
// $FlowFixMe[prop-missing]
const firstResult = firstInstance.compareDocumentPosition(otherNode);
// $FlowFixMe[incompatible-use] Fabric PublicInstance is opaque
// $FlowFixMe[prop-missing]
const lastResult = lastInstance.compareDocumentPosition(otherNode);

const otherNodeIsFirstOrLastChild =
firstInstance === otherNode || lastInstance === otherNode;
const otherNodeIsWithinFirstOrLastChild =
firstResult & Node.DOCUMENT_POSITION_CONTAINED_BY ||
lastResult & Node.DOCUMENT_POSITION_CONTAINED_BY;
const otherNodeIsBetweenFirstAndLastChildren =
firstResult & Node.DOCUMENT_POSITION_FOLLOWING &&
lastResult & Node.DOCUMENT_POSITION_PRECEDING;
let result;
if (
otherNodeIsFirstOrLastChild ||
otherNodeIsWithinFirstOrLastChild ||
otherNodeIsBetweenFirstAndLastChildren
) {
result = Node.DOCUMENT_POSITION_CONTAINED_BY;
} else {
result = firstResult;
}

return result;
};

function collectChildren(child: Fiber, collection: Array<Fiber>): boolean {
collection.push(child);
return false;
}

export function createFragmentInstance(
fragmentFiber: Fiber,
): FragmentInstanceType {
Expand Down
58 changes: 58 additions & 0 deletions packages/shared/ReactDOMFragmentRefShared.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* 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.
*
* Shared logic for Fragment Ref operations for DOM and Fabric configs
*
* @flow
*/

import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';

import {getNextSiblingHostFiber} from 'react-reconciler/src/ReactFiberTreeReflection';

export function compareDocumentPositionForEmptyFragment<TPublicInstance>(
fragmentFiber: Fiber,
parentHostInstance: TPublicInstance,
otherNode: TPublicInstance,
getPublicInstance: (fiber: Fiber) => TPublicInstance,
): number {
let result;
// If the fragment has no children, we can use the parent and
// siblings to determine a position.
// $FlowFixMe[incompatible-use] Fabric PublicInstance is opaque
// $FlowFixMe[prop-missing]
const parentResult = parentHostInstance.compareDocumentPosition(otherNode);
result = parentResult;
if (parentHostInstance === otherNode) {
result = Node.DOCUMENT_POSITION_CONTAINS;
} else {
if (parentResult & Node.DOCUMENT_POSITION_CONTAINED_BY) {
// otherNode is one of the fragment's siblings. Use the next
// sibling to determine if its preceding or following.
const nextSiblingFiber = getNextSiblingHostFiber(fragmentFiber);
if (nextSiblingFiber === null) {
result = Node.DOCUMENT_POSITION_PRECEDING;
} else {
const nextSiblingInstance = getPublicInstance(nextSiblingFiber);
const nextSiblingResult =
// $FlowFixMe[incompatible-use] Fabric PublicInstance is opaque
// $FlowFixMe[prop-missing]
nextSiblingInstance.compareDocumentPosition(otherNode);
if (
nextSiblingResult === 0 ||
nextSiblingResult & Node.DOCUMENT_POSITION_FOLLOWING
) {
result = Node.DOCUMENT_POSITION_FOLLOWING;
} else {
result = Node.DOCUMENT_POSITION_PRECEDING;
}
}
}
}

result |= Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
return result;
}
Loading