Repository: facebook/react
Pull request: feat[Fabric/Paper]: support isChildPublicInstance api method

Adds `isChildPublicInstance` method to both renderers (Fabric and Paper), which will receive 2 public instances and return if first argument is an ancestor of the second, based on fibers.

This will be used as a fallback when DOM node APIs are not available: for Paper renderer or for Fabric without DOM node APIs.

How it is going to be used: to determine which `AppContainer` component in RN is responsible for highlighting an inspected element on the screen.

Changed files:
filename: packages/react-native-renderer/src/ReactFabric.js
patch: @@ -39,6 +39,7 @@ import {
   dispatchCommand,
   sendAccessibilityEvent,
   getNodeFromInternalInstanceHandle,
+  isChildPublicInstance,
 } from './ReactNativePublicCompat';
 import {getPublicInstanceFromInternalInstanceHandle} from './ReactFiberConfigFabric';
 
@@ -128,6 +129,8 @@ export {
   // instance handles we use to dispatch events. This provides a way to access
   // the public instances we created from them (potentially created lazily).
   getPublicInstanceFromInternalInstanceHandle,
+  // DEV-only:
+  isChildPublicInstance,
 };
 
 injectIntoDevTools({

filename: packages/react-native-renderer/src/ReactNativePublicCompat.js
patch: @@ -8,6 +8,8 @@
  */
 
 import type {Node, HostComponent} from './ReactNativeTypes';
+import type {PublicInstance as FabricPublicInstance} from './ReactFiberConfigFabric';
+import type {PublicInstance as PaperPublicInstance} from './ReactFiberConfigNative';
 import type {ElementRef, ElementType} from 'react';
 
 // Modules provided by RN:
@@ -16,15 +18,19 @@ import {
   legacySendAccessibilityEvent,
   getNodeFromPublicInstance,
   getNativeTagFromPublicInstance,
+  getInternalInstanceHandleFromPublicInstance,
 } from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
 
 import {
   findHostInstance,
   findHostInstanceWithWarning,
 } from 'react-reconciler/src/ReactFiberReconciler';
+import {doesFiberContain} from 'react-reconciler/src/ReactFiberTreeReflection';
 import ReactSharedInternals from 'shared/ReactSharedInternals';
 import getComponentNameFromType from 'shared/getComponentNameFromType';
 
+import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent';
+
 const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
 
 export function findHostInstance_DEPRECATED<TElementType: ElementType>(
@@ -218,3 +224,50 @@ export function getNodeFromInternalInstanceHandle(
     internalInstanceHandle.stateNode.node
   );
 }
+
+// Remove this once Paper is no longer supported and DOM Node API are enabled by default in RN.
+export function isChildPublicInstance(
+  parentInstance: FabricPublicInstance | PaperPublicInstance,
+  childInstance: FabricPublicInstance | PaperPublicInstance,
+): boolean {
+  if (__DEV__) {
+    // Paper
+    if (
+      parentInstance instanceof ReactNativeFiberHostComponent ||
+      childInstance instanceof ReactNativeFiberHostComponent
+    ) {
+      if (
+        parentInstance instanceof ReactNativeFiberHostComponent &&
+        childInstance instanceof ReactNativeFiberHostComponent
+      ) {
+        return doesFiberContain(
+          parentInstance._internalFiberInstanceHandleDEV,
+          childInstance._internalFiberInstanceHandleDEV,
+        );
+      }
+
+      // Means that one instance is from Fabric and other is from Paper.
+      return false;
+    }
+
+    const parentInternalInstanceHandle =
+      getInternalInstanceHandleFromPublicInstance(parentInstance);
+    const childInternalInstanceHandle =
+      getInternalInstanceHandleFromPublicInstance(childInstance);
+
+    // Fabric
+    if (
+      parentInternalInstanceHandle != null &&
+      childInternalInstanceHandle != null
+    ) {
+      return doesFiberContain(
+        parentInternalInstanceHandle,
+        childInternalInstanceHandle,
+      );
+    }
+
+    return false;
+  } else {
+    throw new Error('isChildPublicInstance() is not available in production.');
+  }
+}

filename: packages/react-native-renderer/src/ReactNativeRenderer.js
patch: @@ -44,6 +44,7 @@ import {
   findNodeHandle,
   dispatchCommand,
   sendAccessibilityEvent,
+  isChildPublicInstance,
 } from './ReactNativePublicCompat';
 
 // $FlowFixMe[missing-local-annot]
@@ -137,6 +138,8 @@ export {
   // This export is typically undefined in production builds.
   // See the "enableGetInspectorDataForInstanceInProduction" flag.
   getInspectorDataForInstance,
+  // DEV-only:
+  isChildPublicInstance,
 };
 
 injectIntoDevTools({

filename: scripts/flow/react-native-host-hooks.js
patch: @@ -159,6 +159,9 @@ declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'
   declare export function createPublicTextInstance(
     internalInstanceHandle: mixed,
   ): PublicTextInstance;
+  declare export function getInternalInstanceHandleFromPublicInstance(
+    publicInstance: PublicInstance,
+  ): ?Object;
 }
 
 declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInitializeCore' {