@@ -37,6 +37,11 @@ import {runWithFiberInDEV} from 'react-reconciler/src/ReactCurrentFiber';
3737import hasOwnProperty from 'shared/hasOwnProperty' ;
3838import { checkAttributeStringCoercion } from 'shared/CheckStringCoercion' ;
3939import { REACT_CONTEXT_TYPE } from 'shared/ReactSymbols' ;
40+ import {
41+ isFiberContainedBy ,
42+ isFiberFollowing ,
43+ isFiberPreceding ,
44+ } from 'react-reconciler/src/ReactFiberTreeReflection' ;
4045
4146export {
4247 setCurrentUpdatePriority ,
@@ -60,8 +65,9 @@ import {
6065} from './ReactDOMComponentTree' ;
6166import {
6267 traverseFragmentInstance ,
63- getFragmentParentHostInstance ,
64- getNextSiblingHostInstance ,
68+ getFragmentParentHostFiber ,
69+ getNextSiblingHostFiber ,
70+ getInstanceFromHostFiber ,
6571} from 'react-reconciler/src/ReactFiberTreeReflection' ;
6672
6773export { detachDeletedInstance } ;
@@ -2629,7 +2635,6 @@ FragmentInstance.prototype.addEventListener = function (
26292635 listeners . push ( { type, listener, optionsOrUseCapture} ) ;
26302636 traverseFragmentInstance (
26312637 this . _fragmentFiber ,
2632- false ,
26332638 addEventListenerToChild ,
26342639 type ,
26352640 listener ,
@@ -2639,12 +2644,13 @@ FragmentInstance.prototype.addEventListener = function (
26392644 this . _eventListeners = listeners ;
26402645} ;
26412646function addEventListenerToChild (
2642- child : Instance ,
2647+ child : Fiber ,
26432648 type : string ,
26442649 listener : EventListener ,
26452650 optionsOrUseCapture ?: EventListenerOptionsOrUseCapture ,
26462651) : boolean {
2647- child . addEventListener ( type , listener , optionsOrUseCapture ) ;
2652+ const instance = getInstanceFromHostFiber ( child ) ;
2653+ instance . addEventListener ( type , listener , optionsOrUseCapture ) ;
26482654 return false ;
26492655}
26502656// $FlowFixMe[prop-missing]
@@ -2661,7 +2667,6 @@ FragmentInstance.prototype.removeEventListener = function (
26612667 if ( typeof listeners !== 'undefined' && listeners . length > 0 ) {
26622668 traverseFragmentInstance (
26632669 this . _fragmentFiber ,
2664- false ,
26652670 removeEventListenerFromChild ,
26662671 type ,
26672672 listener ,
@@ -2679,12 +2684,13 @@ FragmentInstance.prototype.removeEventListener = function (
26792684 }
26802685} ;
26812686function removeEventListenerFromChild (
2682- child : Instance ,
2687+ child : Fiber ,
26832688 type : string ,
26842689 listener : EventListener ,
26852690 optionsOrUseCapture ?: EventListenerOptionsOrUseCapture ,
26862691) : boolean {
2687- child . removeEventListener ( type , listener , optionsOrUseCapture ) ;
2692+ const instance = getInstanceFromHostFiber ( child ) ;
2693+ instance . removeEventListener ( type , listener , optionsOrUseCapture ) ;
26882694 return false ;
26892695}
26902696// $FlowFixMe[prop-missing]
@@ -2694,34 +2700,32 @@ FragmentInstance.prototype.focus = function (
26942700) : void {
26952701 traverseFragmentInstance (
26962702 this . _fragmentFiber ,
2697- false ,
2698- setFocusIfFocusable ,
2703+ setFocusOnFiberIfFocusable ,
26992704 focusOptions ,
27002705 ) ;
27012706} ;
2707+ function setFocusOnFiberIfFocusable (
2708+ fiber : Fiber ,
2709+ focusOptions ?: FocusOptions ,
2710+ ) : boolean {
2711+ const instance = getInstanceFromHostFiber ( fiber ) ;
2712+ return setFocusIfFocusable ( instance , focusOptions ) ;
2713+ }
27022714// $FlowFixMe[prop-missing]
27032715FragmentInstance . prototype . focusLast = function (
27042716 this : FragmentInstanceType ,
27052717 focusOptions ?: FocusOptions ,
27062718) : void {
2707- const children : Array < Instance > = [ ] ;
2708- traverseFragmentInstance (
2709- this . _fragmentFiber ,
2710- false ,
2711- collectChildren ,
2712- children ,
2713- ) ;
2719+ const children : Array < Fiber > = [ ] ;
2720+ traverseFragmentInstance ( this . _fragmentFiber , collectChildren , children ) ;
27142721 for ( let i = children . length - 1 ; i >= 0 ; i -- ) {
27152722 const child = children [ i ] ;
2716- if ( setFocusIfFocusable ( child , focusOptions ) ) {
2723+ if ( setFocusOnFiberIfFocusable ( child , focusOptions ) ) {
27172724 break;
27182725 }
27192726 }
27202727} ;
2721- function collectChildren (
2722- child : Instance ,
2723- collection : Array < Instance > ,
2724- ) : boolean {
2728+ function collectChildren ( child : Fiber , collection : Array < Fiber > ) : boolean {
27252729 collection . push ( child ) ;
27262730 return false ;
27272731}
@@ -2731,16 +2735,16 @@ FragmentInstance.prototype.blur = function (this: FragmentInstanceType): void {
27312735 // does not contain document.activeElement
27322736 traverseFragmentInstance (
27332737 this . _fragmentFiber ,
2734- false ,
27352738 blurActiveElementWithinFragment ,
27362739 ) ;
27372740} ;
2738- function blurActiveElementWithinFragment ( child : Instance ) : boolean {
2741+ function blurActiveElementWithinFragment ( child : Fiber ) : boolean {
27392742 // TODO: We can get the activeElement from the parent outside of the loop when we have a reference.
2740- const ownerDocument = child . ownerDocument ;
2741- if ( child === ownerDocument . activeElement ) {
2743+ const instance = getInstanceFromHostFiber ( child ) ;
2744+ const ownerDocument = instance . ownerDocument ;
2745+ if ( instance === ownerDocument . activeElement ) {
27422746 // $FlowFixMe[prop-missing]
2743- child . blur ( ) ;
2747+ instance . blur ( ) ;
27442748 return true ;
27452749 }
27462750 return false ;
@@ -2754,13 +2758,14 @@ FragmentInstance.prototype.observeUsing = function (
27542758 this . _observers = new Set ( ) ;
27552759 }
27562760 this . _observers . add ( observer ) ;
2757- traverseFragmentInstance ( this . _fragmentFiber , false , observeChild , observer ) ;
2761+ traverseFragmentInstance ( this . _fragmentFiber , observeChild , observer ) ;
27582762} ;
27592763function observeChild (
2760- child : Instance ,
2764+ child : Fiber ,
27612765 observer : IntersectionObserver | ResizeObserver ,
27622766) {
2763- observer . observe ( child ) ;
2767+ const instance = getInstanceFromHostFiber ( child ) ;
2768+ observer . observe ( instance ) ;
27642769 return false ;
27652770}
27662771// $FlowFixMe[prop-missing]
@@ -2777,48 +2782,41 @@ FragmentInstance.prototype.unobserveUsing = function (
27772782 }
27782783 } else {
27792784 this . _observers . delete ( observer ) ;
2780- traverseFragmentInstance (
2781- this . _fragmentFiber ,
2782- false ,
2783- unobserveChild ,
2784- observer ,
2785- ) ;
2785+ traverseFragmentInstance ( this . _fragmentFiber , unobserveChild , observer ) ;
27862786 }
27872787} ;
27882788function unobserveChild (
2789- child : Instance ,
2789+ child : Fiber ,
27902790 observer : IntersectionObserver | ResizeObserver ,
27912791) {
2792- observer . unobserve ( child ) ;
2792+ const instance = getInstanceFromHostFiber ( child ) ;
2793+ observer . unobserve ( instance ) ;
27932794 return false ;
27942795}
27952796// $FlowFixMe[prop-missing]
27962797FragmentInstance . prototype . getClientRects = function (
27972798 this : FragmentInstanceType ,
27982799) : Array < DOMRect > {
27992800 const rects : Array < DOMRect > = [ ] ;
2800- traverseFragmentInstance (
2801- this . _fragmentFiber ,
2802- true ,
2803- collectClientRects ,
2804- rects ,
2805- ) ;
2801+ traverseFragmentInstance ( this . _fragmentFiber , collectClientRects , rects ) ;
28062802 return rects ;
28072803} ;
2808- function collectClientRects(child: Instance, rects: Array< DOMRect > ): boolean {
2804+ function collectClientRects(child: Fiber, rects: Array< DOMRect > ): boolean {
2805+ const instance = getInstanceFromHostFiber ( child ) ;
28092806 // $FlowFixMe[method-unbinding]
2810- rects . push . apply ( rects , child . getClientRects ( ) ) ;
2807+ rects . push . apply ( rects , instance . getClientRects ( ) ) ;
28112808 return false ;
28122809}
28132810// $FlowFixMe[prop-missing]
28142811FragmentInstance.prototype.getRootNode = function (
28152812 this: FragmentInstanceType,
28162813 getRootNodeOptions?: { composed : boolean } ,
28172814): Document | ShadowRoot | FragmentInstanceType {
2818- const parentHostInstance = getFragmentParentHostInstance ( this . _fragmentFiber ) ;
2819- if ( parentHostInstance === null ) {
2815+ const parentHostFiber = getFragmentParentHostFiber ( this . _fragmentFiber ) ;
2816+ if ( parentHostFiber === null ) {
28202817 return this ;
28212818 }
2819+ const parentHostInstance = getInstanceFromHostFiber ( parentHostFiber ) ;
28222820 const rootNode =
28232821 // $FlowFixMe[incompatible-cast] Flow expects Node
28242822 ( parentHostInstance . getRootNode ( getRootNodeOptions ) : Document | ShadowRoot ) ;
@@ -2829,56 +2827,54 @@ FragmentInstance.prototype.compareDocumentPosition = function (
28292827 this: FragmentInstanceType,
28302828 otherNode: Instance,
28312829): number {
2832- const parentHostInstance = getFragmentParentHostInstance ( this . _fragmentFiber ) ;
2833- if ( parentHostInstance === null ) {
2830+ const parentHostFiber = getFragmentParentHostFiber ( this . _fragmentFiber ) ;
2831+ if ( parentHostFiber === null ) {
28342832 return Node . DOCUMENT_POSITION_DISCONNECTED ;
28352833 }
2834+ const children : Array < Fiber > = [ ] ;
2835+ traverseFragmentInstance ( this . _fragmentFiber , collectChildren , children ) ;
28362836
2837- const children : Array < Instance > = [ ] ;
2838- traverseFragmentInstance (
2839- this . _fragmentFiber ,
2840- true ,
2841- collectChildren ,
2842- children ,
2843- ) ;
2844-
2837+ let result = Node . DOCUMENT_POSITION_DISCONNECTED ;
28452838 if ( children . length === 0 ) {
28462839 // If the fragment has no children, we can use the parent and
28472840 // siblings to determine a position.
2848- if ( parentHostInstance === otherNode ) {
2849- return Node . DOCUMENT_POSITION_CONTAINS ;
2850- }
2841+ const parentHostInstance = getInstanceFromHostFiber ( parentHostFiber ) ;
28512842 const parentResult = parentHostInstance . compareDocumentPosition ( otherNode ) ;
2852- if (parentResult & Node . DOCUMENT_POSITION_CONTAINED_BY ) {
2853- // otherNode is one of the fragment's siblings. Use the next
2854- // sibling to determine if its preceding or following.
2855- const nextSiblingInstance = getNextSiblingHostInstance (
2856- this . _fragmentFiber ,
2857- ) ;
2858- if ( nextSiblingInstance === null ) {
2859- return Node . DOCUMENT_POSITION_PRECEDING ;
2860- }
2861- if (
2862- nextSiblingInstance === otherNode ||
2863- nextSiblingInstance . compareDocumentPosition ( otherNode ) &
2864- Node . DOCUMENT_POSITION_FOLLOWING
2865- ) {
2866- return Node . DOCUMENT_POSITION_FOLLOWING ;
2867- } else {
2868- return Node . DOCUMENT_POSITION_PRECEDING ;
2843+ result = parentResult ;
2844+ if ( parentHostInstance === otherNode ) {
2845+ result = Node . DOCUMENT_POSITION_CONTAINS ;
2846+ } else {
2847+ if ( parentResult & Node . DOCUMENT_POSITION_CONTAINED_BY ) {
2848+ // otherNode is one of the fragment's siblings. Use the next
2849+ // sibling to determine if its preceding or following.
2850+ const nextSiblingFiber = getNextSiblingHostFiber ( this . _fragmentFiber ) ;
2851+ if ( nextSiblingFiber === null ) {
2852+ result = Node . DOCUMENT_POSITION_PRECEDING ;
2853+ } else {
2854+ const nextSiblingInstance =
2855+ getInstanceFromHostFiber ( nextSiblingFiber ) ;
2856+ const nextSiblingResult =
2857+ nextSiblingInstance . compareDocumentPosition ( otherNode ) ;
2858+ if (
2859+ nextSiblingResult === 0 ||
2860+ nextSiblingResult & Node . DOCUMENT_POSITION_FOLLOWING
2861+ ) {
2862+ result = Node . DOCUMENT_POSITION_FOLLOWING ;
2863+ } else {
2864+ result = Node . DOCUMENT_POSITION_PRECEDING ;
2865+ }
2866+ }
28692867 }
28702868 }
2871- return parentResult ;
2869+
2870+ result |= Node . DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC ;
2871+ return result ;
28722872 }
28732873
2874- const firstElement = children [ 0 ] ;
2875- const lastElement = children [ children . length - 1 ] ;
2874+ const firstElement = getInstanceFromHostFiber ( children [ 0 ] ) ;
2875+ const lastElement = getInstanceFromHostFiber ( children [ children . length - 1 ] ) ;
28762876 const firstResult = firstElement . compareDocumentPosition ( otherNode ) ;
28772877 const lastResult = lastElement . compareDocumentPosition ( otherNode ) ;
2878- let result ;
2879-
2880- // If otherNode is a child of the Fragment, it should only be
2881- // Node.DOCUMENT_POSITION_CONTAINED_BY
28822878 if (
28832879 ( firstResult & Node . DOCUMENT_POSITION_FOLLOWING &&
28842880 lastResult & Node . DOCUMENT_POSITION_PRECEDING ) ||
@@ -2890,9 +2886,67 @@ FragmentInstance.prototype.compareDocumentPosition = function (
28902886 result = firstResult ;
28912887 }
28922888
2893- return result;
2889+ if (
2890+ result & Node . DOCUMENT_POSITION_DISCONNECTED ||
2891+ result & Node . DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
2892+ ) {
2893+ return result ;
2894+ }
2895+
2896+ // Now that we have the result from the DOM API, we double check it matches
2897+ // the state of the React tree. If it doesn't, we have a case of portaled or
2898+ // otherwise injected elements and we return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC.
2899+ const documentPositionMatchesFiberPosition =
2900+ validateDocumentPositionWithFiberTree(
2901+ result,
2902+ this._fragmentFiber,
2903+ children[0],
2904+ children[children.length - 1],
2905+ otherNode,
2906+ );
2907+ if (documentPositionMatchesFiberPosition) {
2908+ return result ;
2909+ }
2910+ return Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
28942911} ;
28952912
2913+ function validateDocumentPositionWithFiberTree (
2914+ documentPosition : number ,
2915+ fragmentFiber : Fiber ,
2916+ precedingBoundaryFiber : Fiber ,
2917+ followingBoundaryFiber : Fiber ,
2918+ otherNode : Instance ,
2919+ ) : boolean {
2920+ const otherFiber = getClosestInstanceFromNode ( otherNode ) ;
2921+ if ( documentPosition & Node . DOCUMENT_POSITION_CONTAINED_BY ) {
2922+ return ! ! otherFiber && isFiberContainedBy ( fragmentFiber , otherFiber ) ;
2923+ }
2924+ if (documentPosition & Node . DOCUMENT_POSITION_CONTAINS ) {
2925+ if ( otherFiber === null ) {
2926+ // otherFiber could be null if its the document or body element
2927+ const ownerDocument = otherNode . ownerDocument ;
2928+ return otherNode === ownerDocument || otherNode === ownerDocument . body ;
2929+ }
2930+ return isFiberContainedBy(otherFiber, fragmentFiber);
2931+ }
2932+ if ( documentPosition & Node . DOCUMENT_POSITION_PRECEDING ) {
2933+ return (
2934+ ! ! otherFiber &&
2935+ ( otherFiber === precedingBoundaryFiber ||
2936+ isFiberPreceding ( precedingBoundaryFiber , otherFiber ) )
2937+ ) ;
2938+ }
2939+ if (documentPosition & Node . DOCUMENT_POSITION_FOLLOWING ) {
2940+ return (
2941+ ! ! otherFiber &&
2942+ ( otherFiber === followingBoundaryFiber ||
2943+ isFiberFollowing ( followingBoundaryFiber , otherFiber ) )
2944+ ) ;
2945+ }
2946+
2947+ return false;
2948+ }
2949+
28962950function normalizeListenerOptions (
28972951 opts : ?EventListenerOptionsOrUseCapture ,
28982952) : string {
0 commit comments