1
/* global chrome */
2
3
import nullthrows from 'nullthrows';
4
-import {installHook} from 'react-devtools-shared/src/hook';
4
import {SESSION_STORAGE_RELOAD_AND_PROFILE_KEY} from 'react-devtools-shared/src/constants';
5
import {sessionStorageGetItem} from 'react-devtools-shared/src/storage';
6
+import {IS_FIREFOX} from '../utils';
7
+
8
+function injectScriptSync(src) {
9
+ let code = '';
10
+ const request = new XMLHttpRequest();
11
+ request.addEventListener('load', function() {
12
+ code = this.responseText;
13
+ });
14
+ request.open('GET', src, false);
15
+ request.send();
16
8
-function injectCode(code) {
17
const script = document.createElement('script');
18
script.textContent = code;
19
23
nullthrows(script.parentNode).removeChild(script);
24
}
25
26
+function injectScriptAsync(src) {
27
+ const script = document.createElement('script');
28
+ script.src = src;
29
+ script.onload = function() {
30
+ script.remove();
31
+ };
32
+ nullthrows(document.documentElement).appendChild(script);
33
+}
34
+
35
let lastDetectionResult;
36
37
// We want to detect when a renderer attaches, and notify the "background page"
94
}
95
break;
96
case 'react-devtools-inject-backend':
80
- const script = document.createElement('script');
81
- script.src = chrome.runtime.getURL('build/react_devtools_backend.js');
82
- document.documentElement.appendChild(script);
83
- script.parentNode.removeChild(script);
97
+ injectScriptAsync(
98
+ chrome.runtime.getURL('build/react_devtools_backend.js'),
99
+ );
100
break;
101
}
102
});
112
chrome.runtime.sendMessage(lastDetectionResult);
113
});
114
99
-const detectReact = `
100
-window.__REACT_DEVTOOLS_GLOBAL_HOOK__.on('renderer', function({reactBuildType}) {
101
- window.postMessage({
102
- source: 'react-devtools-detector',
103
- reactBuildType,
104
- }, '*');
105
-});
106
-`;
107
-const saveNativeValues = `
108
-window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeObjectCreate = Object.create;
109
-window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeMap = Map;
110
-window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeWeakMap = WeakMap;
111
-window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeSet = Set;
112
-`;
115
+// We create a "sync" script tag to page to inject the global hook on Manifest V2 extensions.
116
+// To comply with the new security policy in V3, we use chrome.scripting.registerContentScripts instead (see background.js).
117
+// However, the new API only works for Chrome v102+.
118
+// We insert a "async" script tag as a fallback for older versions.
119
+// It has known issues if JS on the page is faster than the extension.
120
+// Users will see a notice in components tab when that happens (see <Tree>).
121
+// For Firefox, V3 is not ready, so sync injection is still the best approach.
122
+const injectScript = IS_FIREFOX ? injectScriptSync : injectScriptAsync;
123
124
// If we have just reloaded to profile, we need to inject the renderer interface before the app loads.
125
if (sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
116
- const rendererURL = chrome.runtime.getURL('build/renderer.js');
117
- let rendererCode;
118
-
119
- // We need to inject in time to catch the initial mount.
120
- // This means we need to synchronously read the renderer code itself,
121
- // and synchronously inject it into the page.
122
- // There are very few ways to actually do this.
123
- // This seems to be the best approach.
124
- const request = new XMLHttpRequest();
125
- request.addEventListener('load', function() {
126
- rendererCode = this.responseText;
127
- });
128
- request.open('GET', rendererURL, false);
129
- request.send();
130
- injectCode(rendererCode);
126
+ injectScript(chrome.runtime.getURL('build/renderer.js'));
127
}
128
129
// Inject a __REACT_DEVTOOLS_GLOBAL_HOOK__ global for React to interact with.
134
switch (document.contentType) {
135
case 'text/html':
136
case 'application/xhtml+xml': {
141
- injectCode(
142
- ';(' +
143
- installHook.toString() +
144
- '(window))' +
145
- saveNativeValues +
146
- detectReact,
147
- );
137
+ injectScript(chrome.runtime.getURL('build/installHook.js'));
138
break;
139
}
140
}