diff --git a/shells/browser/shared/src/main.js b/shells/browser/shared/src/main.js index ab86699f19..af02c900b2 100644 --- a/shells/browser/shared/src/main.js +++ b/shells/browser/shared/src/main.js @@ -162,10 +162,43 @@ function createPanelIfReactLoaded() { container._hasInitialHTMLBeenCleared = true; } + function maybeSetReactSelectionFromBrowser() { + // When the user chooses a different node in the browser Elements tab, + // copy it over to the hook object so that we can sync the selection. + chrome.devtools.inspectedWindow.eval( + // Don't reset selection if it didn't change in Elements tab. + '(window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0 !== $0)' + + ' ? (window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0 = $0, true)' + + ' : false', + (didChangeSelection, error) => { + if (error) { + console.error(error); + } else if (didChangeSelection) { + bridge.send('syncSelectionFromBrowserTools'); + } + } + ); + } + + function maybeSetBrowserSelectionFromReact() { + chrome.devtools.inspectedWindow.eval( + '(window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0 != null)' + + ' ? (inspect(window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0), true)' + + ' : false', + (didChangeSelection, error) => { + if (error) { + console.error(error); + } + } + ); + } + let currentPanel = null; chrome.devtools.panels.create('⚛ Components', '', 'panel.html', panel => { panel.onShown.addListener(panel => { + maybeSetReactSelectionFromBrowser(); + if (currentPanel === panel) { return; } @@ -178,10 +211,9 @@ function createPanelIfReactLoaded() { render('components'); panel.injectStyles(cloneStyleTags); } - - // TODO: When the user switches to the panel, check for an Elements tab selection. }); panel.onHidden.addListener(() => { + maybeSetBrowserSelectionFromReact(); // TODO: Stop highlighting and stuff. }); }); diff --git a/src/backend/agent.js b/src/backend/agent.js index c96b05b0aa..3469d621a4 100644 --- a/src/backend/agent.js +++ b/src/backend/agent.js @@ -87,6 +87,10 @@ export default class Agent extends EventEmitter { bridge.addListener('startProfiling', this.startProfiling); bridge.addListener('stopInspectingDOM', this.stopInspectingDOM); bridge.addListener('stopProfiling', this.stopProfiling); + bridge.addListener( + 'syncSelectionFromBrowserTools', + this.syncSelectionFromBrowserTools + ); bridge.addListener('shutdown', this.shutdown); bridge.addListener('viewElementSource', this.viewElementSource); @@ -292,6 +296,14 @@ export default class Agent extends EventEmitter { if (renderer == null) { console.warn(`Invalid renderer id "${rendererID}" for element "${id}"`); } else { + // Update the active DOM node on the global hook object. + // The content script will read this to update window.$0 + // when we switch tabs. + let node: HTMLElement | null = null; + node = ((renderer.findNativeByFiberID(id): any): HTMLElement); + if (node !== null) { + window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0 = node; + } this._bridge.send('selectElement', renderer.selectElement(id)); } }; @@ -362,6 +374,17 @@ export default class Agent extends EventEmitter { } } + syncSelectionFromBrowserTools = () => { + const target = window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0; + if (target == null) { + return; + } + const id = this.getIDForNode(target); + if (id !== null) { + this._bridge.send('selectFiber', id); + } + }; + shutdown = () => { this.emit('shutdown'); };