Repository: facebook/react
Pull request: fix[dynamic-scripts-injection]: unregister content scripts before registration

## 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. 


Changed files:
filename: packages/react-devtools-extensions/src/background.js
patch: @@ -6,38 +6,47 @@ import {IS_FIREFOX, EXTENSION_CONTAINED_VERSIONS} from './utils';
 
 const ports = {};
 
-if (!IS_FIREFOX) {
-  // equivalent logic for Firefox is in prepareInjection.js
-  // Manifest V3 method of injecting content scripts (not yet supported in Firefox)
-  // Note: the "world" option in registerContentScripts is only available in Chrome v102+
-  // It's critical since it allows us to directly run scripts on the "main" world on the page
-  // "document_start" allows it to run before the page's scripts
-  // so the hook can be detected by react reconciler
-  chrome.scripting.registerContentScripts(
-    [
-      {
-        id: 'hook',
-        matches: ['<all_urls>'],
-        js: ['build/installHook.js'],
-        runAt: 'document_start',
-        world: chrome.scripting.ExecutionWorld.MAIN,
-      },
-      {
-        id: 'renderer',
-        matches: ['<all_urls>'],
-        js: ['build/renderer.js'],
-        runAt: 'document_start',
-        world: chrome.scripting.ExecutionWorld.MAIN,
-      },
-    ],
-    function () {
-      // When the content scripts are already registered, an error will be thrown.
-      // It happens when the service worker process is incorrectly duplicated.
-      if (chrome.runtime.lastError) {
-        console.error(chrome.runtime.lastError);
-      }
+async function dynamicallyInjectContentScripts() {
+  const contentScriptsToInject = [
+    {
+      id: 'hook',
+      matches: ['<all_urls>'],
+      js: ['build/installHook.js'],
+      runAt: 'document_start',
+      world: chrome.scripting.ExecutionWorld.MAIN,
     },
-  );
+    {
+      id: 'renderer',
+      matches: ['<all_urls>'],
+      js: ['build/renderer.js'],
+      runAt: 'document_start',
+      world: chrome.scripting.ExecutionWorld.MAIN,
+    },
+  ];
+
+  try {
+    // For some reason dynamically injected scripts might be already registered
+    // Registering them again will fail, which will result into
+    // __REACT_DEVTOOLS_GLOBAL_HOOK__ hook not being injected
+    await chrome.scripting.unregisterContentScripts({
+      ids: contentScriptsToInject.map(s => s.id),
+    });
+
+    // equivalent logic for Firefox is in prepareInjection.js
+    // Manifest V3 method of injecting content script
+    // TODO(hoxyq): migrate Firefox to V3 manifests
+    // Note: the "world" option in registerContentScripts is only available in Chrome v102+
+    // It's critical since it allows us to directly run scripts on the "main" world on the page
+    // "document_start" allows it to run before the page's scripts
+    // so the hook can be detected by react reconciler
+    await chrome.scripting.registerContentScripts(contentScriptsToInject);
+  } catch (error) {
+    console.error(error);
+  }
+}
+
+if (!IS_FIREFOX) {
+  dynamicallyInjectContentScripts();
 }
 
 chrome.runtime.onConnect.addListener(function (port) {