@samitouri / QOS-React / commits / f9d75e32ba

fix[devtools/extension]: added a workaround for proxy content script injection in firefox (#27375)

Changes: 1. [Firefox-only] For some reason, Firefox might try to inject dynamically registered content script in pages like `about:blank`. I couldn't find a way to change this behaviour, `about:` is not a valid scheme, so we can't exclude it and `match_about_blank` flag is not supported in `chrome.scripting.registerContentScripts`. 2. While navigating the history in Firefox, some content scripts might not be re-injected and still be alive. To handle this, we are now patching `window` with `__REACT_DEVTOOLS_PROXY_INJECTED__` flag, to make sure that proxy is injected and only once. This flag is cleared on `pagehide` event.

Ruslan Lesiutin committed Sep 22, 2023 at 19:36 UTC f9d75e32ba85b3c6a2a5d20fa27ba7e23d5529b2
2 files changed +35 -13
packages/react-devtools-extensions/src/background/index.js
+6
@@ -60,6 +60,12 @@ function isNumeric(str: string): boolean {
60
61 chrome.runtime.onConnect.addListener(port => {
62 if (port.name === 'proxy') {
63 + // Might not be present for restricted pages in Firefox
64 + if (port.sender?.tab?.id == null) {
65 + // Not disconnecting it, so it would not reconnect
66 + return;
67 + }
68 +
69 // Proxy content script is executed in tab, so it should have it specified.
70 const tabId = port.sender.tab.id;
71
packages/react-devtools-extensions/src/contentScripts/proxy.js
+29 -13
@@ -2,22 +2,38 @@
2
3 'use strict';
4
5 -let port = null;
6 -let backendInitialized: boolean = false;
7 -
8 -connectPort();
9 -sayHelloToBackendManager();
5 +window.addEventListener('pageshow', function ({target}) {
6 + // Firefox's behaviour for injecting this content script can be unpredictable
7 + // While navigating the history, some content scripts might not be re-injected and still be alive
8 + if (!window.__REACT_DEVTOOLS_PROXY_INJECTED__) {
9 + window.__REACT_DEVTOOLS_PROXY_INJECTED__ = true;
10
11 -// The backend waits to install the global hook until notified by the content script.
12 -// In the event of a page reload, the content script might be loaded before the backend manager is injected.
13 -// Because of this we need to poll the backend manager until it has been initialized.
14 -const intervalID = setInterval(() => {
15 - if (backendInitialized) {
16 - clearInterval(intervalID);
17 - } else {
11 + connectPort();
12 sayHelloToBackendManager();
13 +
14 + // The backend waits to install the global hook until notified by the content script.
15 + // In the event of a page reload, the content script might be loaded before the backend manager is injected.
16 + // Because of this we need to poll the backend manager until it has been initialized.
17 + const intervalID = setInterval(() => {
18 + if (backendInitialized) {
19 + clearInterval(intervalID);
20 + } else {
21 + sayHelloToBackendManager();
22 + }
23 + }, 500);
24 + }
25 +});
26 +
27 +window.addEventListener('pagehide', function ({target}) {
28 + if (target !== window.document) {
29 + return;
30 }
20 -}, 500);
31 +
32 + delete window.__REACT_DEVTOOLS_PROXY_INJECTED__;
33 +});
34 +
35 +let port = null;
36 +let backendInitialized: boolean = false;
37
38 function sayHelloToBackendManager() {
39 window.postMessage(