Skip to content

Commit 8a25302

Browse files
authored
fix[dynamic-scripts-injection]: unregister content scripts before registration (#26765)
## Summary Fixes #26756. DevTools is failing to inject `__REACT_DEVTOOLS_GLOBAL_HOOK__` hook in incognito mode. This is not happening straight-forward, but if extension is toggled on and off, the next time I try to open it I am receiving an error that content script was already registered. <img width="676" alt="Screenshot 2023-05-02 at 14 36 53" src="https://user-images.githubusercontent.com/28902667/235877692-51c5d284-79d9-4b00-b62e-d25d5bb5e056.png"> - Unregistering content scripts before attempting to register them again. We need to inject `__REACT_DEVTOOLS_GLOBAL_HOOK__` on each page, so this should be expected behaviour. - Fixed error logging ## How did you test this change? Local build of extension for Chrome, trying the same steps, which resulted in an error. No regression in performance, tested on react.dev, still the same.
1 parent 2c24768 commit 8a25302

File tree

1 file changed

+40
-31
lines changed

1 file changed

+40
-31
lines changed

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

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,47 @@ import {IS_FIREFOX, EXTENSION_CONTAINED_VERSIONS} from './utils';
66

77
const ports = {};
88

9-
if (!IS_FIREFOX) {
10-
// equivalent logic for Firefox is in prepareInjection.js
11-
// Manifest V3 method of injecting content scripts (not yet supported in Firefox)
12-
// Note: the "world" option in registerContentScripts is only available in Chrome v102+
13-
// It's critical since it allows us to directly run scripts on the "main" world on the page
14-
// "document_start" allows it to run before the page's scripts
15-
// so the hook can be detected by react reconciler
16-
chrome.scripting.registerContentScripts(
17-
[
18-
{
19-
id: 'hook',
20-
matches: ['<all_urls>'],
21-
js: ['build/installHook.js'],
22-
runAt: 'document_start',
23-
world: chrome.scripting.ExecutionWorld.MAIN,
24-
},
25-
{
26-
id: 'renderer',
27-
matches: ['<all_urls>'],
28-
js: ['build/renderer.js'],
29-
runAt: 'document_start',
30-
world: chrome.scripting.ExecutionWorld.MAIN,
31-
},
32-
],
33-
function () {
34-
// When the content scripts are already registered, an error will be thrown.
35-
// It happens when the service worker process is incorrectly duplicated.
36-
if (chrome.runtime.lastError) {
37-
console.error(chrome.runtime.lastError);
38-
}
9+
async function dynamicallyInjectContentScripts() {
10+
const contentScriptsToInject = [
11+
{
12+
id: 'hook',
13+
matches: ['<all_urls>'],
14+
js: ['build/installHook.js'],
15+
runAt: 'document_start',
16+
world: chrome.scripting.ExecutionWorld.MAIN,
3917
},
40-
);
18+
{
19+
id: 'renderer',
20+
matches: ['<all_urls>'],
21+
js: ['build/renderer.js'],
22+
runAt: 'document_start',
23+
world: chrome.scripting.ExecutionWorld.MAIN,
24+
},
25+
];
26+
27+
try {
28+
// For some reason dynamically injected scripts might be already registered
29+
// Registering them again will fail, which will result into
30+
// __REACT_DEVTOOLS_GLOBAL_HOOK__ hook not being injected
31+
await chrome.scripting.unregisterContentScripts({
32+
ids: contentScriptsToInject.map(s => s.id),
33+
});
34+
35+
// equivalent logic for Firefox is in prepareInjection.js
36+
// Manifest V3 method of injecting content script
37+
// TODO(hoxyq): migrate Firefox to V3 manifests
38+
// Note: the "world" option in registerContentScripts is only available in Chrome v102+
39+
// It's critical since it allows us to directly run scripts on the "main" world on the page
40+
// "document_start" allows it to run before the page's scripts
41+
// so the hook can be detected by react reconciler
42+
await chrome.scripting.registerContentScripts(contentScriptsToInject);
43+
} catch (error) {
44+
console.error(error);
45+
}
46+
}
47+
48+
if (!IS_FIREFOX) {
49+
dynamicallyInjectContentScripts();
4150
}
4251

4352
chrome.runtime.onConnect.addListener(function (port) {

0 commit comments

Comments
 (0)