Skip to content

Commit

Permalink
Adds a ReactNativeInspector API to ReactNativeRenderer (#9691)
Browse files Browse the repository at this point in the history
* fixed conflicts with master

* splits fiber and stack implementations

* prettier run

* updated fiber implementation based on feedback

* updated fiber implementation for selecting inspector hierarchy

* run of prettier

* fixed flow issues

* updated stack implementation

* fixes an implementation difference where before it wasnt properly understoof

* addresses comments in PR feedback

* updated stack inspector to use emptyObject

* Update ReactNativeFiberInspector.js

Fixes a flow error

* fixes last flow error

* prettier

* applied changes to how viewConfig works for fibers and extracted measure out

* fixed bad paste

* fixes flow errors

* prettyify

* revmoed getComponentName changes

* updated logic for getHostProps and addressed other PR feedback

* improved throwing to invariant and update stack implemenation based on feedback

* prettier
  • Loading branch information
trueadm authored May 23, 2017
1 parent 964c263 commit 0ea4674
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/renderers/native/ReactNativeFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const ReactNativeInjection = require('ReactNativeInjection');
const ReactNativeTagHandles = require('ReactNativeTagHandles');
const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry');
const ReactPortal = require('ReactPortal');
const ReactNativeFiberInspector = require('ReactNativeFiberInspector');
const ReactVersion = require('ReactVersion');
const UIManager = require('UIManager');

Expand Down Expand Up @@ -465,6 +466,7 @@ if (typeof injectInternals === 'function') {
injectInternals({
findFiberByHostInstance: ReactNativeComponentTree.getClosestInstanceFromNode,
findHostInstanceByFiber: NativeRenderer.findHostInstance,
getInspectorDataForViewTag: ReactNativeFiberInspector.getInspectorDataForViewTag,
// This is an enum because we may add more (e.g. profiler build)
bundleType: __DEV__ ? 1 : 0,
version: ReactVersion,
Expand Down
124 changes: 124 additions & 0 deletions src/renderers/native/ReactNativeFiberInspector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeFiberInspector
* @flow
*/
'use strict';

const ReactNativeComponentTree = require('ReactNativeComponentTree');
const ReactFiberTreeReflection = require('ReactFiberTreeReflection');
const getComponentName = require('getComponentName');
const emptyObject = require('fbjs/lib/emptyObject');
const ReactTypeOfWork = require('ReactTypeOfWork');
const UIManager = require('UIManager');
const invariant = require('fbjs/lib/invariant');

const {getClosestInstanceFromNode} = ReactNativeComponentTree;
const {findCurrentFiberUsingSlowPath} = ReactFiberTreeReflection;
const {HostComponent} = ReactTypeOfWork;

import type {Fiber} from 'ReactFiber';

let getInspectorDataForViewTag;

if (__DEV__) {
var traverseOwnerTreeUp = function(hierarchy, instance: any) {
if (instance) {
hierarchy.unshift(instance);
traverseOwnerTreeUp(hierarchy, instance._debugOwner);
}
};

var getOwnerHierarchy = function(instance: any) {
var hierarchy = [];
traverseOwnerTreeUp(hierarchy, instance);
return hierarchy;
};

var lastNonHostInstance = function(hierarchy) {
for (let i = hierarchy.length - 1; i > 1; i--) {
const instance = hierarchy[i];

if (instance.tag !== HostComponent) {
return instance;
}
}
return hierarchy[0];
};

var getHostProps = function(fiber) {
const host = ReactFiberTreeReflection.findCurrentHostFiber(fiber);
if (host) {
return host.memoizedProps || emptyObject;
}
return emptyObject;
};

var getHostNode = function(fiber: Fiber | null, findNodeHandle) {
let hostNode;
// look for children first for the hostNode
// as composite fibers do not have a hostNode
while (fiber) {
if (fiber.stateNode !== null && fiber.tag === HostComponent) {
hostNode = findNodeHandle(fiber.stateNode);
}
if (hostNode) {
return hostNode;
}
fiber = fiber.child;
}
return null;
};

var stripTopSecret = str =>
typeof str === 'string' && str.replace('topsecret-', '');

var createHierarchy = function(fiberHierarchy) {
return fiberHierarchy.map(fiber => ({
name: stripTopSecret(getComponentName(fiber)),
getInspectorData: findNodeHandle => ({
measure: callback =>
UIManager.measure(getHostNode(fiber, findNodeHandle), callback),
props: getHostProps(fiber),
source: fiber._debugSource,
}),
}));
};

getInspectorDataForViewTag = function(viewTag: number): Object {
const fiber = findCurrentFiberUsingSlowPath(
getClosestInstanceFromNode(viewTag),
);
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,
instance,
props,
selection,
source,
};
};
} else {
getInspectorDataForViewTag = () => {
invariant(
false,
'getInspectorDataForViewTag() is not available in production',
);
};
}

module.exports = {
getInspectorDataForViewTag,
};
2 changes: 2 additions & 0 deletions src/renderers/native/ReactNativeStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var ReactNativeInjection = require('ReactNativeInjection');
var ReactNativeMount = require('ReactNativeMount');
var ReactNativeStackInjection = require('ReactNativeStackInjection');
var ReactUpdates = require('ReactUpdates');
var ReactNativeStackInspector = require('ReactNativeStackInspector');

var findNodeHandle = require('findNodeHandle');

Expand Down Expand Up @@ -81,6 +82,7 @@ if (
},
Mount: ReactNativeMount,
Reconciler: require('ReactReconciler'),
getInspectorDataForViewTag: ReactNativeStackInspector.getInspectorDataForViewTag,
});
}

Expand Down
96 changes: 96 additions & 0 deletions src/renderers/native/ReactNativeStackInspector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeStackInspector
* @flow
*/
'use strict';

const ReactNativeComponentTree = require('ReactNativeComponentTree');
const getComponentName = require('getComponentName');
const emptyObject = require('fbjs/lib/emptyObject');
const UIManager = require('UIManager');
const invariant = require('fbjs/lib/invariant');

let getInspectorDataForViewTag;

if (__DEV__) {
var traverseOwnerTreeUp = function(hierarchy, instance) {
if (instance) {
hierarchy.unshift(instance);
traverseOwnerTreeUp(hierarchy, instance._currentElement._owner);
}
};

var getOwnerHierarchy = function(instance) {
var hierarchy = [];
traverseOwnerTreeUp(hierarchy, instance);
return hierarchy;
};

var lastNotNativeInstance = function(hierarchy) {
for (let i = hierarchy.length - 1; i > 1; i--) {
const instance = hierarchy[i];
if (!instance.viewConfig) {
return instance;
}
}
return hierarchy[0];
};

var getHostProps = function(component) {
const instance = component._instance;
if (instance) {
return instance.props || emptyObject;
}
return emptyObject;
};

var createHierarchy = function(componentHierarchy) {
return componentHierarchy.map(component => ({
name: getComponentName(component),
getInspectorData: () => ({
measure: callback =>
UIManager.measure(component.getHostNode(), callback),
props: getHostProps(component),
source: component._currentElement && component._currentElement._source,
}),
}));
};

getInspectorDataForViewTag = function(viewTag: any): Object {
const component = ReactNativeComponentTree.getClosestInstanceFromNode(
viewTag,
);
const componentHierarchy = getOwnerHierarchy(component);
const instance = lastNotNativeInstance(componentHierarchy);
const hierarchy = createHierarchy(componentHierarchy);
const props = getHostProps(instance);
const source = instance._currentElement && instance._currentElement._source;
const selection = componentHierarchy.indexOf(instance);

return {
hierarchy,
instance,
props,
selection,
source,
};
};
} else {
getInspectorDataForViewTag = () => {
invariant(
false,
'getInspectorDataForViewTag() is not available in production',
);
};
}

module.exports = {
getInspectorDataForViewTag,
};

0 comments on commit 0ea4674

Please sign in to comment.