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
9 changes: 8 additions & 1 deletion packages/react-native-renderer/src/ReactFabric.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ import ReactVersion from 'shared/ReactVersion';
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';

import {getClosestInstanceFromNode} from './ReactFabricComponentTree';
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
import {
getInspectorDataForViewAtPoint,
getInspectorDataForViewTag,
} from './ReactNativeFiberInspector';

import {LegacyRoot} from 'shared/ReactRootTags';
import ReactSharedInternals from 'shared/ReactSharedInternals';
Expand Down Expand Up @@ -233,6 +236,10 @@ export {
injectIntoDevTools({
findFiberByHostInstance: getClosestInstanceFromNode,
getInspectorDataForViewTag: getInspectorDataForViewTag,
getInspectorDataForViewAtPoint: getInspectorDataForViewAtPoint.bind(
null,
findNodeHandle,
),
bundleType: __DEV__ ? 1 : 0,
version: ReactVersion,
rendererPackageName: 'react-native-renderer',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,17 @@ class ReactNativeFiberHostComponent {
_children: Array<Instance | number>;
_nativeTag: number;
viewConfig: ReactNativeBaseComponentViewConfig<>;
_internalFiberInstanceHandle: Object;

constructor(tag: number, viewConfig: ReactNativeBaseComponentViewConfig<>) {
constructor(
tag: number,
viewConfig: ReactNativeBaseComponentViewConfig<>,
internalInstanceHandle: Object,
) {
this._nativeTag = tag;
this._children = [];
this.viewConfig = viewConfig;
this._internalFiberInstanceHandle = internalInstanceHandle;
}

blur() {
Expand Down
146 changes: 139 additions & 7 deletions packages/react-native-renderer/src/ReactNativeFiberInspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import type {Fiber} from 'react-reconciler/src/ReactFiber';
import type {TouchedViewDataAtPoint, InspectorData} from './ReactNativeTypes';

import {
findCurrentHostFiber,
Expand All @@ -27,6 +28,7 @@ if (__DEV__) {
}

let getInspectorDataForViewTag;
let getInspectorDataForViewAtPoint;

if (__DEV__) {
const traverseOwnerTreeUp = function(hierarchy, instance: any) {
Expand Down Expand Up @@ -80,15 +82,68 @@ if (__DEV__) {
const createHierarchy = function(fiberHierarchy) {
return fiberHierarchy.map(fiber => ({
name: getComponentName(fiber.type),
getInspectorData: findNodeHandle => ({
measure: callback =>
UIManager.measure(getHostNode(fiber, findNodeHandle), callback),
props: getHostProps(fiber),
source: fiber._debugSource,
}),
getInspectorData: findNodeHandle => {
return {
props: getHostProps(fiber),
source: fiber._debugSource,
measure: callback => {
// If this is Fabric, we'll find a ShadowNode and use that to measure.
const hostFiber = findCurrentHostFiber(fiber);
const shadowNode =
hostFiber != null &&
hostFiber.stateNode !== null &&
hostFiber.stateNode.node;

if (shadowNode) {
nativeFabricUIManager.measure(shadowNode, function(
x,
y,
width,
height,
pageX,
pageY,
) {
callback(x, y, width, height, pageX, pageY);
});
} else {
return UIManager.measure(
getHostNode(fiber, findNodeHandle),
callback,
);
}
},
};
},
}));
};

const getInspectorDataForInstance = function(closestInstance): InspectorData {
// Handle case where user clicks outside of ReactNative
if (!closestInstance) {
return {
hierarchy: [],
props: emptyObject,
selection: null,
source: null,
};
}

const fiber = findCurrentFiberUsingSlowPath(closestInstance);
const fiberHierarchy = getOwnerHierarchy(fiber);
const instance = lastNonHostInstance(fiberHierarchy);
const hierarchy = createHierarchy(fiberHierarchy);
const props = getHostProps(instance);
const source = instance._debugSource;
const selection = fiberHierarchy.indexOf(instance);

return {
hierarchy,
props,
selection,
source,
};
};

getInspectorDataForViewTag = function(viewTag: number): Object {
const closestInstance = getClosestInstanceFromNode(viewTag);

Expand Down Expand Up @@ -117,13 +172,90 @@ if (__DEV__) {
source,
};
};

getInspectorDataForViewAtPoint = function(
findNodeHandle: (componentOrHandle: any) => ?number,
inspectedView: Object,
locationX: number,
locationY: number,
callback: (viewData: TouchedViewDataAtPoint) => mixed,
): void {
let closestInstance = null;

if (inspectedView._internalInstanceHandle != null) {
// For Fabric we can look up the instance handle directly and measure it.
nativeFabricUIManager.findNodeAtPoint(
inspectedView._internalInstanceHandle.stateNode.node,
locationX,
locationY,
internalInstanceHandle => {
if (internalInstanceHandle == null) {
callback({
pointerY: locationY,
frame: {left: 0, top: 0, width: 0, height: 0},
...getInspectorDataForInstance(closestInstance),
});
}

closestInstance =
internalInstanceHandle.stateNode.canonical._internalInstanceHandle;
nativeFabricUIManager.measure(
internalInstanceHandle.stateNode.node,
(x, y, width, height, pageX, pageY) => {
callback({
pointerY: locationY,
frame: {left: pageX, top: pageY, width, height},
...getInspectorDataForInstance(closestInstance),
});
},
);
},
);
} else if (inspectedView._internalFiberInstanceHandle != null) {
// For Paper we fall back to the old strategy using the React tag.
UIManager.findSubviewIn(
findNodeHandle(inspectedView),
[locationX, locationY],
(nativeViewTag, left, top, width, height) => {
const inspectorData = getInspectorDataForInstance(
getClosestInstanceFromNode(nativeViewTag),
);
callback({
...inspectorData,
pointerY: locationY,
frame: {left, top, width, height},
touchedViewTag: nativeViewTag,
});
},
);
} else if (__DEV__) {
console.error(
'getInspectorDataForViewAtPoint expects to receieve a host component',
);

return;
}
};
} else {
getInspectorDataForViewTag = () => {
invariant(
false,
'getInspectorDataForViewTag() is not available in production',
);
};

getInspectorDataForViewAtPoint = (
findNodeHandle: (componentOrHandle: any) => ?number,
inspectedView: Object,
locationX: number,
locationY: number,
callback: (viewData: TouchedViewDataAtPoint) => mixed,
): void => {
invariant(
false,
'getInspectorDataForViewAtPoint() is not available in production.',
);
};
}

export {getInspectorDataForViewTag};
export {getInspectorDataForViewAtPoint, getInspectorDataForViewTag};
6 changes: 5 additions & 1 deletion packages/react-native-renderer/src/ReactNativeHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,11 @@ export function createInstance(
updatePayload, // props
);

const component = new ReactNativeFiberHostComponent(tag, viewConfig);
const component = new ReactNativeFiberHostComponent(
tag,
viewConfig,
internalInstanceHandle,
);

precacheFiberNode(internalInstanceHandle, tag);
updateFiberProps(tag, props);
Expand Down
9 changes: 8 additions & 1 deletion packages/react-native-renderer/src/ReactNativeRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ import ReactVersion from 'shared/ReactVersion';
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';

import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
import {
getInspectorDataForViewTag,
getInspectorDataForViewAtPoint,
} from './ReactNativeFiberInspector';

import {LegacyRoot} from 'shared/ReactRootTags';
import ReactSharedInternals from 'shared/ReactSharedInternals';
Expand Down Expand Up @@ -247,6 +250,10 @@ export {
injectIntoDevTools({
findFiberByHostInstance: getClosestInstanceFromNode,
getInspectorDataForViewTag: getInspectorDataForViewTag,
getInspectorDataForViewAtPoint: getInspectorDataForViewAtPoint.bind(
null,
findNodeHandle,
),
bundleType: __DEV__ ? 1 : 0,
version: ReactVersion,
rendererPackageName: 'react-native-renderer',
Expand Down
40 changes: 40 additions & 0 deletions packages/react-native-renderer/src/ReactNativeTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,46 @@ type SecretInternalsType = {
...
};

type InspectorDataProps = $ReadOnly<{
[propName: string]: string,
...,
}>;

type InspectorDataSource = $ReadOnly<{|
fileName?: string,
lineNumber?: number,
|}>;

type InspectorDataGetter = (
(componentOrHandle: any) => ?number,
) => $ReadOnly<{|
measure: Function,
props: InspectorDataProps,
source: InspectorDataSource,
|}>;

export type InspectorData = $ReadOnly<{|
hierarchy: Array<{|
name: ?string,
getInspectorData: InspectorDataGetter,
|}>,
selection: ?number,
props: InspectorDataProps,
source: ?InspectorDataSource,
|}>;

export type TouchedViewDataAtPoint = $ReadOnly<{|
pointerY: number,
touchedViewTag?: number,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this used for? I thought we're getting rid of all these tags so seems odd to add new users of it and expose more implementation details about to change to future work. Shouldn't there be another mechanism?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to support the current devtools behavior in Paper where tapping in the app will open the element in the tree. I was thinking we would add this now to prevent the regression, while we figure out the correct solution for Fabric and Paper moving forward.

https://github.com/facebook/react-native/blob/84b7b8d07645b528c784e0bacecf1293797e0ec7/Libraries/Inspector/Inspector.js#L209-L212

What do you think?

frame: $ReadOnly<{|
top: number,
left: number,
width: number,
height: number,
|}>,
...InspectorData,
|}>;

/**
* Flat ReactNative renderer bundles are too big for Flow to parse efficiently.
* Provide minimal Flow typing for the high-level RN API and call it a day.
Expand Down
8 changes: 8 additions & 0 deletions packages/react-reconciler/src/ReactFiberReconciler.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import type {Fiber} from './ReactFiber';
import type {FiberRoot} from './ReactFiberRoot';
import type {RootTag} from 'shared/ReactRootTags';
import type {TouchedViewDataAtPoint} from 'react-native-renderer/src/ReactNativeTypes';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import is causing CI to fail.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't have dependencies on RN from the reconciler bundle. The folders are checked independently.

Also from a layering perspective it's probably not right.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused that some of the commits seemed to pass CI

import type {
Instance,
TextInstance,
Expand Down Expand Up @@ -109,6 +110,13 @@ type DevToolsConfig = {|
// This API is unfortunately RN-specific.
// TODO: Change it to accept Fiber instead and type it properly.
getInspectorDataForViewTag?: (tag: number) => Object,
// Used by RN in-app inspector.
getInspectorDataForViewAtPoint?: (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think either of these two should be part of this type because they're not actually used by the core DevTools protocol. It's kind of an adhoc RN specific addition.

Instead we should make this a renderer specific object.

rendererConfig: RendererInspectionConfig

Then you can define the type for RendererInspectionConfig through the HostConfig mechanism. So that each renderer gets their own type. That can then be safely referenced in the react-native folder.

So Fabric and RN host configs gets this type:

export type RendererInspectionConfig = {
  getInspectorDataForViewTag?: (tag: number) => Object,
  getInspectorDataForViewAtPoint?: (...) => ...;
};

You then import that from here using:

import type {RendererInspectionConfig} from './ReactFiberHostConfig';

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this suggestion.

inspectedView: Object,
locationX: number,
locationY: number,
callback: (viewData: TouchedViewDataAtPoint) => mixed,
) => void,
|};

let didWarnAboutNestedUpdates;
Expand Down
3 changes: 2 additions & 1 deletion scripts/error-codes/codes.json
Original file line number Diff line number Diff line change
Expand Up @@ -346,5 +346,6 @@
"345": "Root did not complete. This is a bug in React.",
"346": "An event responder context was used outside of an event cycle.",
"347": "Maps are not valid as a React child (found: %s). Consider converting children to an array of keyed ReactElements instead.",
"348": "ensureListeningTo(): received a container that was not an element node. This is likely a bug in React."
"348": "ensureListeningTo(): received a container that was not an element node. This is likely a bug in React.",
"349": "getInspectorDataForViewAtPoint() is not available in production."
}
18 changes: 18 additions & 0 deletions scripts/flow/react-native-host-hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
} from 'react-native-renderer/src/ReactNativeTypes';
import type {RNTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
import type {CapturedError} from 'react-reconciler/src/ReactCapturedValue';
import type {Fiber} from 'react-reconciler/src/ReactFiber';

type DeepDifferOptions = {|+unsafelyIgnoreFunctions?: boolean|};

Expand Down Expand Up @@ -96,6 +97,17 @@ declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'
) => Promise<any>,
setJSResponder: (reactTag: number, blockNativeResponder: boolean) => void,
clearJSResponder: () => void,
findSubviewIn: (
reactTag: ?number,
point: Array<number>,
callback: (
nativeViewTag: number,
left: number,
top: number,
width: number,
height: number,
) => void,
) => void,
...
};
declare export var BatchedBridge: {
Expand Down Expand Up @@ -156,6 +168,12 @@ declare var nativeFabricUIManager: {
onFail: () => void,
onSuccess: MeasureLayoutOnSuccessCallback,
) => void,
findNodeAtPoint: (
node: Node,
locationX: number,
locationY: number,
callback: (Fiber) => void,
) => void,
...
};

Expand Down