1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import type {Fiber} from './ReactInternalTypes';
    
  11. import type {
    
  12.   ReactScopeInstance,
    
  13.   ReactContext,
    
  14.   ReactScopeQuery,
    
  15. } from 'shared/ReactTypes';
    
  16. 
    
  17. import {
    
  18.   getPublicInstance,
    
  19.   getInstanceFromNode,
    
  20.   getInstanceFromScope,
    
  21. } from './ReactFiberConfig';
    
  22. import {isFiberSuspenseAndTimedOut} from './ReactFiberTreeReflection';
    
  23. 
    
  24. import {HostComponent, ScopeComponent, ContextProvider} from './ReactWorkTags';
    
  25. import {enableScopeAPI} from 'shared/ReactFeatureFlags';
    
  26. 
    
  27. function getSuspenseFallbackChild(fiber: Fiber): Fiber | null {
    
  28.   return ((((fiber.child: any): Fiber).sibling: any): Fiber).child;
    
  29. }
    
  30. 
    
  31. const emptyObject = {};
    
  32. 
    
  33. function collectScopedNodes(
    
  34.   node: Fiber,
    
  35.   fn: ReactScopeQuery,
    
  36.   scopedNodes: Array<any>,
    
  37. ): void {
    
  38.   if (enableScopeAPI) {
    
  39.     if (node.tag === HostComponent) {
    
  40.       const {type, memoizedProps, stateNode} = node;
    
  41.       const instance = getPublicInstance(stateNode);
    
  42.       if (
    
  43.         instance !== null &&
    
  44.         fn(type, memoizedProps || emptyObject, instance) === true
    
  45.       ) {
    
  46.         scopedNodes.push(instance);
    
  47.       }
    
  48.     }
    
  49.     let child = node.child;
    
  50. 
    
  51.     if (isFiberSuspenseAndTimedOut(node)) {
    
  52.       child = getSuspenseFallbackChild(node);
    
  53.     }
    
  54.     if (child !== null) {
    
  55.       collectScopedNodesFromChildren(child, fn, scopedNodes);
    
  56.     }
    
  57.   }
    
  58. }
    
  59. 
    
  60. function collectFirstScopedNode(
    
  61.   node: Fiber,
    
  62.   fn: ReactScopeQuery,
    
  63. ): null | Object {
    
  64.   if (enableScopeAPI) {
    
  65.     if (node.tag === HostComponent) {
    
  66.       const {type, memoizedProps, stateNode} = node;
    
  67.       const instance = getPublicInstance(stateNode);
    
  68.       if (instance !== null && fn(type, memoizedProps, instance) === true) {
    
  69.         return instance;
    
  70.       }
    
  71.     }
    
  72.     let child = node.child;
    
  73. 
    
  74.     if (isFiberSuspenseAndTimedOut(node)) {
    
  75.       child = getSuspenseFallbackChild(node);
    
  76.     }
    
  77.     if (child !== null) {
    
  78.       return collectFirstScopedNodeFromChildren(child, fn);
    
  79.     }
    
  80.   }
    
  81.   return null;
    
  82. }
    
  83. 
    
  84. function collectScopedNodesFromChildren(
    
  85.   startingChild: Fiber,
    
  86.   fn: ReactScopeQuery,
    
  87.   scopedNodes: Array<any>,
    
  88. ): void {
    
  89.   let child: null | Fiber = startingChild;
    
  90.   while (child !== null) {
    
  91.     collectScopedNodes(child, fn, scopedNodes);
    
  92.     child = child.sibling;
    
  93.   }
    
  94. }
    
  95. 
    
  96. function collectFirstScopedNodeFromChildren(
    
  97.   startingChild: Fiber,
    
  98.   fn: ReactScopeQuery,
    
  99. ): Object | null {
    
  100.   let child: null | Fiber = startingChild;
    
  101.   while (child !== null) {
    
  102.     const scopedNode = collectFirstScopedNode(child, fn);
    
  103.     if (scopedNode !== null) {
    
  104.       return scopedNode;
    
  105.     }
    
  106.     child = child.sibling;
    
  107.   }
    
  108.   return null;
    
  109. }
    
  110. 
    
  111. function collectNearestContextValues<T>(
    
  112.   node: Fiber,
    
  113.   context: ReactContext<T>,
    
  114.   childContextValues: Array<T>,
    
  115. ): void {
    
  116.   if (node.tag === ContextProvider && node.type._context === context) {
    
  117.     const contextValue = node.memoizedProps.value;
    
  118.     childContextValues.push(contextValue);
    
  119.   } else {
    
  120.     let child = node.child;
    
  121. 
    
  122.     if (isFiberSuspenseAndTimedOut(node)) {
    
  123.       child = getSuspenseFallbackChild(node);
    
  124.     }
    
  125.     if (child !== null) {
    
  126.       collectNearestChildContextValues(child, context, childContextValues);
    
  127.     }
    
  128.   }
    
  129. }
    
  130. 
    
  131. function collectNearestChildContextValues<T>(
    
  132.   startingChild: Fiber | null,
    
  133.   context: ReactContext<T>,
    
  134.   childContextValues: Array<T>,
    
  135. ): void {
    
  136.   let child = startingChild;
    
  137.   while (child !== null) {
    
  138.     collectNearestContextValues(child, context, childContextValues);
    
  139.     child = child.sibling;
    
  140.   }
    
  141. }
    
  142. 
    
  143. function DO_NOT_USE_queryAllNodes(
    
  144.   this: $FlowFixMe,
    
  145.   fn: ReactScopeQuery,
    
  146. ): null | Array<Object> {
    
  147.   const currentFiber = getInstanceFromScope(this);
    
  148.   if (currentFiber === null) {
    
  149.     return null;
    
  150.   }
    
  151.   const child = currentFiber.child;
    
  152.   const scopedNodes: Array<any> = [];
    
  153.   if (child !== null) {
    
  154.     collectScopedNodesFromChildren(child, fn, scopedNodes);
    
  155.   }
    
  156.   return scopedNodes.length === 0 ? null : scopedNodes;
    
  157. }
    
  158. 
    
  159. function DO_NOT_USE_queryFirstNode(
    
  160.   this: $FlowFixMe,
    
  161.   fn: ReactScopeQuery,
    
  162. ): null | Object {
    
  163.   const currentFiber = getInstanceFromScope(this);
    
  164.   if (currentFiber === null) {
    
  165.     return null;
    
  166.   }
    
  167.   const child = currentFiber.child;
    
  168.   if (child !== null) {
    
  169.     return collectFirstScopedNodeFromChildren(child, fn);
    
  170.   }
    
  171.   return null;
    
  172. }
    
  173. 
    
  174. function containsNode(this: $FlowFixMe, node: Object): boolean {
    
  175.   let fiber = getInstanceFromNode(node);
    
  176.   while (fiber !== null) {
    
  177.     if (fiber.tag === ScopeComponent && fiber.stateNode === this) {
    
  178.       return true;
    
  179.     }
    
  180.     fiber = fiber.return;
    
  181.   }
    
  182.   return false;
    
  183. }
    
  184. 
    
  185. function getChildContextValues<T>(
    
  186.   this: $FlowFixMe,
    
  187.   context: ReactContext<T>,
    
  188. ): Array<T> {
    
  189.   const currentFiber = getInstanceFromScope(this);
    
  190.   if (currentFiber === null) {
    
  191.     return [];
    
  192.   }
    
  193.   const child = currentFiber.child;
    
  194.   const childContextValues: Array<T> = [];
    
  195.   if (child !== null) {
    
  196.     collectNearestChildContextValues(child, context, childContextValues);
    
  197.   }
    
  198.   return childContextValues;
    
  199. }
    
  200. 
    
  201. export function createScopeInstance(): ReactScopeInstance {
    
  202.   return {
    
  203.     DO_NOT_USE_queryAllNodes,
    
  204.     DO_NOT_USE_queryFirstNode,
    
  205.     containsNode,
    
  206.     getChildContextValues,
    
  207.   };
    
  208. }