Skip to content

Commit dd53658

Browse files
authored
[DevTools] remove backend dependency from the global hook (#26563)
## Summary - #26234 is reverted and replaced with a better approach - introduce a new global devtools variable to decouple the global hook's dependency on backend/console.js, and add it to react-devtools-inline and react-devtools-standalone With this PR, I want to introduce a new principle to hook.js: we should always be alert when editing this file and avoid importing from other files. In the past, we try to inline a lot of the implementation because we use `.toString()` to inject this function from the extension (we still have some old comments left). Although it is no longer inlined that way, it has became now more important to keep it clean as it is a de facto global API people are using (9.9K files contains it on Github search as of today). **File size change for extension:** Before: 379K installHook.js After: 21K installHook.js 363K renderer.js
1 parent 85bb7b6 commit dd53658

File tree

12 files changed

+85
-40
lines changed

12 files changed

+85
-40
lines changed

packages/react-devtools-core/src/backend.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Agent from 'react-devtools-shared/src/backend/agent';
1111
import Bridge from 'react-devtools-shared/src/bridge';
1212
import {installHook} from 'react-devtools-shared/src/hook';
1313
import {initBackend} from 'react-devtools-shared/src/backend';
14+
import {installConsoleFunctionsToWindow} from 'react-devtools-shared/src/backend/console';
1415
import {__DEBUG__} from 'react-devtools-shared/src/constants';
1516
import setupNativeStyleEditor from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor';
1617
import {getDefaultComponentFilters} from 'react-devtools-shared/src/utils';
@@ -38,6 +39,9 @@ type ConnectOptions = {
3839
...
3940
};
4041

42+
// Install a global variable to allow patching console early (during injection).
43+
// This provides React Native developers with components stacks even if they don't run DevTools.
44+
installConsoleFunctionsToWindow();
4145
installHook(window);
4246

4347
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;

packages/react-devtools-extensions/chrome/manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"panel.html",
3232
"build/react_devtools_backend.js",
3333
"build/proxy.js",
34+
"build/renderer.js",
3435
"build/installHook.js"
3536
],
3637
"matches": [

packages/react-devtools-extensions/edge/manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"panel.html",
3232
"build/react_devtools_backend.js",
3333
"build/proxy.js",
34+
"build/renderer.js",
3435
"build/installHook.js"
3536
],
3637
"matches": [

packages/react-devtools-extensions/firefox/manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"panel.html",
3333
"build/react_devtools_backend.js",
3434
"build/proxy.js",
35+
"build/renderer.js",
3536
"build/installHook.js"
3637
],
3738
"background": {

packages/react-devtools-extensions/src/background.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ if (!IS_FIREFOX) {
2222
runAt: 'document_start',
2323
world: chrome.scripting.ExecutionWorld.MAIN,
2424
},
25+
{
26+
id: 'renderer',
27+
matches: ['<all_urls>'],
28+
js: ['build/renderer.js'],
29+
runAt: 'document_start',
30+
world: chrome.scripting.ExecutionWorld.MAIN,
31+
},
2532
],
2633
function () {
2734
// When the content scripts are already registered, an error will be thrown.

packages/react-devtools-extensions/src/contentScripts/prepareInjection.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* global chrome */
22

33
import nullthrows from 'nullthrows';
4+
import {SESSION_STORAGE_RELOAD_AND_PROFILE_KEY} from 'react-devtools-shared/src/constants';
5+
import {sessionStorageGetItem} from 'react-devtools-shared/src/storage';
46
import {IS_FIREFOX} from '../utils';
57

68
// We run scripts on the page via the service worker (backgroud.js) for
@@ -109,9 +111,15 @@ window.addEventListener('pageshow', function ({target}) {
109111
chrome.runtime.sendMessage(lastDetectionResult);
110112
});
111113

112-
// Inject a __REACT_DEVTOOLS_GLOBAL_HOOK__ global for React to interact with.
113-
// Only do this for HTML documents though, to avoid e.g. breaking syntax highlighting for XML docs.
114114
if (IS_FIREFOX) {
115+
// If we have just reloaded to profile, we need to inject the renderer interface before the app loads.
116+
if (
117+
sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true'
118+
) {
119+
injectScriptSync(chrome.runtime.getURL('build/renderer.js'));
120+
}
121+
// Inject a __REACT_DEVTOOLS_GLOBAL_HOOK__ global for React to interact with.
122+
// Only do this for HTML documents though, to avoid e.g. breaking syntax highlighting for XML docs.
115123
switch (document.contentType) {
116124
case 'text/html':
117125
case 'application/xhtml+xml': {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* In order to support reload-and-profile functionality, the renderer needs to be injected before any other scripts.
3+
* Since it is a complex file (with imports) we can't just toString() it like we do with the hook itself,
4+
* So this entry point (one of the web_accessible_resources) provides a way to eagerly inject it.
5+
* The hook will look for the presence of a global __REACT_DEVTOOLS_ATTACH__ and attach an injected renderer early.
6+
* The normal case (not a reload-and-profile) will not make use of this entry point though.
7+
*
8+
* @flow
9+
*/
10+
11+
import {attach} from 'react-devtools-shared/src/backend/renderer';
12+
import {SESSION_STORAGE_RELOAD_AND_PROFILE_KEY} from 'react-devtools-shared/src/constants';
13+
import {sessionStorageGetItem} from 'react-devtools-shared/src/storage';
14+
15+
if (
16+
sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true' &&
17+
!window.hasOwnProperty('__REACT_DEVTOOLS_ATTACH__')
18+
) {
19+
Object.defineProperty(
20+
window,
21+
'__REACT_DEVTOOLS_ATTACH__',
22+
({
23+
enumerable: false,
24+
// This property needs to be configurable to allow third-party integrations
25+
// to attach their own renderer. Note that using third-party integrations
26+
// is not officially supported. Use at your own risk.
27+
configurable: true,
28+
get() {
29+
return attach;
30+
},
31+
}: Object),
32+
);
33+
}

packages/react-devtools-extensions/webpack.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ module.exports = {
5555
panel: './src/panel.js',
5656
proxy: './src/contentScripts/proxy.js',
5757
prepareInjection: './src/contentScripts/prepareInjection.js',
58+
renderer: './src/contentScripts/renderer.js',
5859
installHook: './src/contentScripts/installHook.js',
5960
},
6061
output: {

packages/react-devtools-inline/src/backend.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import Agent from 'react-devtools-shared/src/backend/agent';
44
import Bridge from 'react-devtools-shared/src/bridge';
55
import {initBackend} from 'react-devtools-shared/src/backend';
6+
import {installConsoleFunctionsToWindow} from 'react-devtools-shared/src/backend/console';
67
import {installHook} from 'react-devtools-shared/src/hook';
78
import setupNativeStyleEditor from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor';
89

@@ -119,5 +120,8 @@ export function createBridge(contentWindow: any, wall?: Wall): BackendBridge {
119120
}
120121

121122
export function initialize(contentWindow: any): void {
123+
// Install a global variable to allow patching console early (during injection).
124+
// This provides React Native developers with components stacks even if they don't run DevTools.
125+
installConsoleFunctionsToWindow();
122126
installHook(contentWindow);
123127
}

packages/react-devtools-shared/src/backend/console.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,3 +413,10 @@ export function writeConsolePatchSettingsToWindow(
413413
settings.hideConsoleLogsInStrictMode;
414414
window.__REACT_DEVTOOLS_BROWSER_THEME__ = settings.browserTheme;
415415
}
416+
417+
export function installConsoleFunctionsToWindow(): void {
418+
window.__REACT_DEVTOOLS_CONSOLE_FUNCTIONS__ = {
419+
patchConsoleUsingWindowValues,
420+
registerRendererWithConsole: registerRenderer,
421+
};
422+
}

0 commit comments

Comments
 (0)