@samitouri / QOS-React-2 / commits / 8a25302c66

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.

Ruslan Lesiutin committed May 3, 2023 at 17:27 UTC 8a25302c668d6f9a24f6680fb4947e9b388bb7d9
1 file changed +40 -31
packages/react-devtools-extensions/src/background.js
+40 -31
@@ -6,38 +6,47 @@ import {IS_FIREFOX, EXTENSION_CONTAINED_VERSIONS} from './utils';
6
7 const ports = {};
8
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,
17 },
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();
50 }
51
52 chrome.runtime.onConnect.addListener(function (port) {