Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DevTools] Make function inspection instant #30786

Merged
merged 1 commit into from
Aug 26, 2024
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
16 changes: 3 additions & 13 deletions packages/react-devtools-extensions/src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
setBrowserSelectionFromReact,
setReactSelectionFromBrowser,
} from './elementSelection';
import {viewAttributeSource} from './sourceSelection';

import {startReactPolling} from './reactPolling';
import cloneStyleTags from './cloneStyleTags';
import fetchFileWithCaching from './fetchFileWithCaching';
Expand Down Expand Up @@ -113,19 +115,7 @@ function createBridgeAndStore() {
const viewAttributeSourceFunction = (id, path) => {
const rendererID = store.getRendererIDForElement(id);
if (rendererID != null) {
// Ask the renderer interface to find the specified attribute,
// and store it as a global variable on the window.
bridge.send('viewAttributeSource', {id, path, rendererID});

setTimeout(() => {
// Ask Chrome to display the location of the attribute,
// assuming the renderer found a match.
chrome.devtools.inspectedWindow.eval(`
if (window.$attribute != null) {
inspect(window.$attribute);
}
`);
}, 100);
viewAttributeSource(rendererID, id, path);
}
};

Expand Down
59 changes: 59 additions & 0 deletions packages/react-devtools-extensions/src/main/sourceSelection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* global chrome */

export function viewAttributeSource(rendererID, elementID, path) {
chrome.devtools.inspectedWindow.eval(
'{' + // The outer block is important because it means we can declare local variables.
'const renderer = window.__REACT_DEVTOOLS_GLOBAL_HOOK__.rendererInterfaces.get(' +
JSON.stringify(rendererID) +
');' +
'if (renderer) {' +
' const value = renderer.getElementAttributeByPath(' +
JSON.stringify(elementID) +
',' +
JSON.stringify(path) +
');' +
' if (value) {' +
' inspect(value);' +
' true;' +
' } else {' +
' false;' +
' }' +
'} else {' +
' false;' +
'}' +
'}',
(didInspect, evalError) => {
if (evalError) {
console.error(evalError);
}
},
);
}

export function viewElementSource(rendererID, elementID) {
chrome.devtools.inspectedWindow.eval(
'{' + // The outer block is important because it means we can declare local variables.
'const renderer = window.__REACT_DEVTOOLS_GLOBAL_HOOK__.rendererInterfaces.get(' +
JSON.stringify(rendererID) +
');' +
'if (renderer) {' +
' const value = renderer.getElementSourceFunctionById(' +
JSON.stringify(elementID) +
');' +
' if (value) {' +
' inspect(value);' +
' true;' +
' } else {' +
' false;' +
' }' +
'} else {' +
' false;' +
'}' +
'}',
(didInspect, evalError) => {
if (evalError) {
console.error(evalError);
}
},
);
}
20 changes: 0 additions & 20 deletions packages/react-devtools-shared/src/backend/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,6 @@ export default class Agent extends EventEmitter<{
this.updateConsolePatchSettings,
);
bridge.addListener('updateComponentFilters', this.updateComponentFilters);
bridge.addListener('viewAttributeSource', this.viewAttributeSource);
bridge.addListener('viewElementSource', this.viewElementSource);

// Temporarily support older standalone front-ends sending commands to newer embedded backends.
// We do this because React Native embeds the React DevTools backend,
Expand Down Expand Up @@ -816,24 +814,6 @@ export default class Agent extends EventEmitter<{
}
};

viewAttributeSource: CopyElementParams => void = ({id, path, rendererID}) => {
const renderer = this._rendererInterfaces[rendererID];
if (renderer == null) {
console.warn(`Invalid renderer id "${rendererID}" for element "${id}"`);
} else {
renderer.prepareViewAttributeSource(id, path);
}
};

viewElementSource: ElementAndRendererID => void = ({id, rendererID}) => {
const renderer = this._rendererInterfaces[rendererID];
if (renderer == null) {
console.warn(`Invalid renderer id "${rendererID}" for element "${id}"`);
} else {
renderer.prepareViewElementSource(id);
}
};

onTraceUpdates: (nodes: Set<HostInstance>) => void = nodes => {
this.emit('traceUpdates', nodes);
};
Expand Down
34 changes: 15 additions & 19 deletions packages/react-devtools-shared/src/backend/fiber/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3874,27 +3874,28 @@ export function attach(

// END copied code

function prepareViewAttributeSource(
function getElementAttributeByPath(
id: number,
path: Array<string | number>,
): void {
): mixed {
if (isMostRecentlyInspectedElement(id)) {
window.$attribute = getInObject(
return getInObject(
((mostRecentlyInspectedElement: any): InspectedElement),
path,
);
}
return undefined;
}

function prepareViewElementSource(id: number): void {
function getElementSourceFunctionById(id: number): null | Function {
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
console.warn(`Could not find DevToolsInstance with id "${id}"`);
return;
return null;
}
if (devtoolsInstance.kind !== FIBER_INSTANCE) {
// TODO: Handle VirtualInstance.
return;
return null;
}
const fiber = devtoolsInstance.data;

Expand All @@ -3906,21 +3907,16 @@ export function attach(
case IncompleteFunctionComponent:
case IndeterminateComponent:
case FunctionComponent:
global.$type = type;
break;
return type;
case ForwardRef:
global.$type = type.render;
break;
return type.render;
case MemoComponent:
case SimpleMemoComponent:
global.$type =
elementType != null && elementType.type != null
? elementType.type
: type;
break;
return elementType != null && elementType.type != null
? elementType.type
: type;
default:
global.$type = null;
break;
return null;
}
}

Expand Down Expand Up @@ -5727,8 +5723,8 @@ export function attach(
inspectElement,
logElementToConsole,
patchConsoleForStrictMode,
prepareViewAttributeSource,
prepareViewElementSource,
getElementAttributeByPath,
getElementSourceFunctionById,
overrideError,
overrideSuspense,
overrideValueAtPath,
Expand Down
19 changes: 10 additions & 9 deletions packages/react-devtools-shared/src/backend/legacy/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -907,30 +907,31 @@ export function attach(
}
}

function prepareViewAttributeSource(
function getElementAttributeByPath(
id: number,
path: Array<string | number>,
): void {
): mixed {
const inspectedElement = inspectElementRaw(id);
if (inspectedElement !== null) {
window.$attribute = getInObject(inspectedElement, path);
return getInObject(inspectedElement, path);
}
return undefined;
}

function prepareViewElementSource(id: number): void {
function getElementSourceFunctionById(id: number): null | Function {
const internalInstance = idToInternalInstanceMap.get(id);
if (internalInstance == null) {
console.warn(`Could not find instance with id "${id}"`);
return;
return null;
}

const element = internalInstance._currentElement;
if (element == null) {
console.warn(`Could not find element with id "${id}"`);
return;
return null;
}

global.$type = element.type;
return element.type;
}

function deletePath(
Expand Down Expand Up @@ -1141,8 +1142,8 @@ export function attach(
overrideValueAtPath,
renamePath,
patchConsoleForStrictMode,
prepareViewAttributeSource,
prepareViewElementSource,
getElementAttributeByPath,
getElementSourceFunctionById,
renderer,
setTraceUpdatesEnabled,
setTrackedPath,
Expand Down
6 changes: 3 additions & 3 deletions packages/react-devtools-shared/src/backend/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -394,11 +394,11 @@ export type RendererInterface = {
value: any,
) => void,
patchConsoleForStrictMode: () => void,
prepareViewAttributeSource: (
getElementAttributeByPath: (
id: number,
path: Array<string | number>,
) => void,
prepareViewElementSource: (id: number) => void,
) => mixed,
getElementSourceFunctionById: (id: number) => null | Function,
renamePath: (
type: Type,
id: number,
Expand Down
Loading