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 'react-reconciler/src/ReactInternalTypes';
    
  11. import type {TouchedViewDataAtPoint, InspectorData} from './ReactNativeTypes';
    
  12. 
    
  13. import {
    
  14.   findCurrentHostFiber,
    
  15.   findCurrentFiberUsingSlowPath,
    
  16. } from 'react-reconciler/src/ReactFiberTreeReflection';
    
  17. import getComponentNameFromType from 'shared/getComponentNameFromType';
    
  18. import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
    
  19. // Module provided by RN:
    
  20. import {
    
  21.   UIManager,
    
  22.   getNodeFromPublicInstance,
    
  23. } from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
    
  24. import {enableGetInspectorDataForInstanceInProduction} from 'shared/ReactFeatureFlags';
    
  25. import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
    
  26. import {getNodeFromInternalInstanceHandle} from './ReactNativePublicCompat';
    
  27. 
    
  28. const emptyObject = {};
    
  29. if (__DEV__) {
    
  30.   Object.freeze(emptyObject);
    
  31. }
    
  32. 
    
  33. // $FlowFixMe[missing-local-annot]
    
  34. function createHierarchy(fiberHierarchy) {
    
  35.   return fiberHierarchy.map(fiber => ({
    
  36.     name: getComponentNameFromType(fiber.type),
    
  37.     getInspectorData: findNodeHandle => {
    
  38.       return {
    
  39.         props: getHostProps(fiber),
    
  40.         source: fiber._debugSource,
    
  41.         measure: callback => {
    
  42.           // If this is Fabric, we'll find a shadow node and use that to measure.
    
  43.           const hostFiber = findCurrentHostFiber(fiber);
    
  44.           const node =
    
  45.             hostFiber != null &&
    
  46.             hostFiber.stateNode !== null &&
    
  47.             hostFiber.stateNode.node;
    
  48. 
    
  49.           if (node) {
    
  50.             nativeFabricUIManager.measure(node, callback);
    
  51.           } else {
    
  52.             return UIManager.measure(
    
  53.               getHostNode(fiber, findNodeHandle),
    
  54.               callback,
    
  55.             );
    
  56.           }
    
  57.         },
    
  58.       };
    
  59.     },
    
  60.   }));
    
  61. }
    
  62. 
    
  63. // $FlowFixMe[missing-local-annot]
    
  64. function getHostNode(fiber: Fiber | null, findNodeHandle) {
    
  65.   if (__DEV__ || enableGetInspectorDataForInstanceInProduction) {
    
  66.     let hostNode;
    
  67.     // look for children first for the hostNode
    
  68.     // as composite fibers do not have a hostNode
    
  69.     while (fiber) {
    
  70.       if (fiber.stateNode !== null && fiber.tag === HostComponent) {
    
  71.         hostNode = findNodeHandle(fiber.stateNode);
    
  72.       }
    
  73.       if (hostNode) {
    
  74.         return hostNode;
    
  75.       }
    
  76.       fiber = fiber.child;
    
  77.     }
    
  78.     return null;
    
  79.   }
    
  80. }
    
  81. 
    
  82. // $FlowFixMe[missing-local-annot]
    
  83. function getHostProps(fiber) {
    
  84.   const host = findCurrentHostFiber(fiber);
    
  85.   if (host) {
    
  86.     return host.memoizedProps || emptyObject;
    
  87.   }
    
  88.   return emptyObject;
    
  89. }
    
  90. 
    
  91. function getInspectorDataForInstance(
    
  92.   closestInstance: Fiber | null,
    
  93. ): InspectorData {
    
  94.   if (__DEV__ || enableGetInspectorDataForInstanceInProduction) {
    
  95.     // Handle case where user clicks outside of ReactNative
    
  96.     if (!closestInstance) {
    
  97.       return {
    
  98.         hierarchy: [],
    
  99.         props: emptyObject,
    
  100.         selectedIndex: null,
    
  101.         source: null,
    
  102.       };
    
  103.     }
    
  104. 
    
  105.     const fiber = findCurrentFiberUsingSlowPath(closestInstance);
    
  106.     const fiberHierarchy = getOwnerHierarchy(fiber);
    
  107.     const instance = lastNonHostInstance(fiberHierarchy);
    
  108.     const hierarchy = createHierarchy(fiberHierarchy);
    
  109.     const props = getHostProps(instance);
    
  110.     const source = instance._debugSource;
    
  111.     const selectedIndex = fiberHierarchy.indexOf(instance);
    
  112. 
    
  113.     return {
    
  114.       closestInstance: instance,
    
  115.       hierarchy,
    
  116.       props,
    
  117.       selectedIndex,
    
  118.       source,
    
  119.     };
    
  120.   }
    
  121. 
    
  122.   throw new Error(
    
  123.     'getInspectorDataForInstance() is not available in production',
    
  124.   );
    
  125. }
    
  126. 
    
  127. function getOwnerHierarchy(instance: any) {
    
  128.   const hierarchy: Array<$FlowFixMe> = [];
    
  129.   traverseOwnerTreeUp(hierarchy, instance);
    
  130.   return hierarchy;
    
  131. }
    
  132. 
    
  133. // $FlowFixMe[missing-local-annot]
    
  134. function lastNonHostInstance(hierarchy) {
    
  135.   for (let i = hierarchy.length - 1; i > 1; i--) {
    
  136.     const instance = hierarchy[i];
    
  137. 
    
  138.     if (instance.tag !== HostComponent) {
    
  139.       return instance;
    
  140.     }
    
  141.   }
    
  142.   return hierarchy[0];
    
  143. }
    
  144. 
    
  145. // $FlowFixMe[missing-local-annot]
    
  146. function traverseOwnerTreeUp(
    
  147.   hierarchy: Array<$FlowFixMe>,
    
  148.   instance: any,
    
  149. ): void {
    
  150.   if (__DEV__ || enableGetInspectorDataForInstanceInProduction) {
    
  151.     if (instance) {
    
  152.       hierarchy.unshift(instance);
    
  153.       traverseOwnerTreeUp(hierarchy, instance._debugOwner);
    
  154.     }
    
  155.   }
    
  156. }
    
  157. 
    
  158. function getInspectorDataForViewTag(viewTag: number): InspectorData {
    
  159.   if (__DEV__) {
    
  160.     const closestInstance = getClosestInstanceFromNode(viewTag);
    
  161. 
    
  162.     return getInspectorDataForInstance(closestInstance);
    
  163.   } else {
    
  164.     throw new Error(
    
  165.       'getInspectorDataForViewTag() is not available in production',
    
  166.     );
    
  167.   }
    
  168. }
    
  169. 
    
  170. function getInspectorDataForViewAtPoint(
    
  171.   findNodeHandle: (componentOrHandle: any) => ?number,
    
  172.   inspectedView: Object,
    
  173.   locationX: number,
    
  174.   locationY: number,
    
  175.   callback: (viewData: TouchedViewDataAtPoint) => mixed,
    
  176. ): void {
    
  177.   if (__DEV__) {
    
  178.     let closestInstance = null;
    
  179. 
    
  180.     const fabricNode = getNodeFromPublicInstance(inspectedView);
    
  181.     if (fabricNode) {
    
  182.       // For Fabric we can look up the instance handle directly and measure it.
    
  183.       nativeFabricUIManager.findNodeAtPoint(
    
  184.         fabricNode,
    
  185.         locationX,
    
  186.         locationY,
    
  187.         internalInstanceHandle => {
    
  188.           const node =
    
  189.             internalInstanceHandle != null
    
  190.               ? getNodeFromInternalInstanceHandle(internalInstanceHandle)
    
  191.               : null;
    
  192.           if (internalInstanceHandle == null || node == null) {
    
  193.             callback({
    
  194.               pointerY: locationY,
    
  195.               frame: {left: 0, top: 0, width: 0, height: 0},
    
  196.               ...getInspectorDataForInstance(closestInstance),
    
  197.             });
    
  198.             return;
    
  199.           }
    
  200. 
    
  201.           closestInstance =
    
  202.             internalInstanceHandle.stateNode.canonical.internalInstanceHandle;
    
  203. 
    
  204.           // Note: this is deprecated and we want to remove it ASAP. Keeping it here for React DevTools compatibility for now.
    
  205.           const nativeViewTag =
    
  206.             internalInstanceHandle.stateNode.canonical.nativeTag;
    
  207. 
    
  208.           nativeFabricUIManager.measure(
    
  209.             node,
    
  210.             (x, y, width, height, pageX, pageY) => {
    
  211.               const inspectorData =
    
  212.                 getInspectorDataForInstance(closestInstance);
    
  213.               callback({
    
  214.                 ...inspectorData,
    
  215.                 pointerY: locationY,
    
  216.                 frame: {left: pageX, top: pageY, width, height},
    
  217.                 touchedViewTag: nativeViewTag,
    
  218.               });
    
  219.             },
    
  220.           );
    
  221.         },
    
  222.       );
    
  223.     } else if (inspectedView._internalFiberInstanceHandleDEV != null) {
    
  224.       // For Paper we fall back to the old strategy using the React tag.
    
  225.       UIManager.findSubviewIn(
    
  226.         findNodeHandle(inspectedView),
    
  227.         [locationX, locationY],
    
  228.         (nativeViewTag, left, top, width, height) => {
    
  229.           const inspectorData = getInspectorDataForInstance(
    
  230.             getClosestInstanceFromNode(nativeViewTag),
    
  231.           );
    
  232.           callback({
    
  233.             ...inspectorData,
    
  234.             pointerY: locationY,
    
  235.             frame: {left, top, width, height},
    
  236.             touchedViewTag: nativeViewTag,
    
  237.           });
    
  238.         },
    
  239.       );
    
  240.     } else {
    
  241.       console.error(
    
  242.         'getInspectorDataForViewAtPoint expects to receive a host component',
    
  243.       );
    
  244. 
    
  245.       return;
    
  246.     }
    
  247.   } else {
    
  248.     throw new Error(
    
  249.       'getInspectorDataForViewAtPoint() is not available in production.',
    
  250.     );
    
  251.   }
    
  252. }
    
  253. 
    
  254. export {
    
  255.   getInspectorDataForInstance,
    
  256.   getInspectorDataForViewAtPoint,
    
  257.   getInspectorDataForViewTag,
    
  258. };