Skip to content

Commit d16301e

Browse files
committed
fix[devtools/extension]: added a workaround for proxy content script injection in firefox
1 parent 54c2f2a commit d16301e

File tree

2 files changed

+80
-60
lines changed

2 files changed

+80
-60
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ function isNumeric(str: string): boolean {
6060

6161
chrome.runtime.onConnect.addListener(port => {
6262
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+
6369
// Proxy content script is executed in tab, so it should have it specified.
6470
const tabId = port.sender.tab.id;
6571

packages/react-devtools-extensions/src/contentScripts/proxy.js

Lines changed: 74 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,91 @@
22

33
'use strict';
44

5-
let port = null;
6-
let backendInitialized: boolean = false;
7-
8-
connectPort();
9-
sayHelloToBackendManager();
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 {
18-
sayHelloToBackendManager();
5+
window.addEventListener('unload', function ({target}) {
6+
if (target !== window.document) {
7+
return;
198
}
20-
}, 500);
21-
22-
function sayHelloToBackendManager() {
23-
window.postMessage(
24-
{
25-
source: 'react-devtools-content-script',
26-
hello: true,
27-
},
28-
'*',
29-
);
30-
}
319

32-
function handleMessageFromDevtools(message) {
33-
window.postMessage(
34-
{
35-
source: 'react-devtools-content-script',
36-
payload: message,
37-
},
38-
'*',
39-
);
40-
}
10+
delete window.__REACT_DEVTOOLS_PROXY_INJECTED__;
11+
});
12+
13+
// Firefox's behaviour for injecting this content script can be unpredictable
14+
// While navigating the history, some content scripts might not be re-injected and still be alive
15+
if (!window.__REACT_DEVTOOLS_PROXY_INJECTED__) {
16+
window.__REACT_DEVTOOLS_PROXY_INJECTED__ = true;
17+
18+
let port = null;
19+
let backendInitialized: boolean = false;
4120

42-
function handleMessageFromPage(event) {
43-
if (event.source === window && event.data) {
44-
// This is a message from a bridge (initialized by a devtools backend)
45-
if (event.data.source === 'react-devtools-bridge') {
46-
backendInitialized = true;
21+
connectPort();
22+
sayHelloToBackendManager();
4723

48-
port.postMessage(event.data.payload);
24+
// The backend waits to install the global hook until notified by the content script.
25+
// In the event of a page reload, the content script might be loaded before the backend manager is injected.
26+
// Because of this we need to poll the backend manager until it has been initialized.
27+
const intervalID = setInterval(() => {
28+
if (backendInitialized) {
29+
clearInterval(intervalID);
30+
} else {
31+
sayHelloToBackendManager();
4932
}
33+
}, 500);
34+
35+
function sayHelloToBackendManager() {
36+
window.postMessage(
37+
{
38+
source: 'react-devtools-content-script',
39+
hello: true,
40+
},
41+
'*',
42+
);
43+
}
44+
45+
function handleMessageFromDevtools(message) {
46+
window.postMessage(
47+
{
48+
source: 'react-devtools-content-script',
49+
payload: message,
50+
},
51+
'*',
52+
);
53+
}
54+
55+
function handleMessageFromPage(event) {
56+
if (event.source === window && event.data) {
57+
// This is a message from a bridge (initialized by a devtools backend)
58+
if (event.data.source === 'react-devtools-bridge') {
59+
backendInitialized = true;
5060

51-
// This is a message from the backend manager
52-
if (event.data.source === 'react-devtools-backend-manager') {
53-
chrome.runtime.sendMessage({
54-
payload: event.data.payload,
55-
});
61+
port.postMessage(event.data.payload);
62+
}
63+
64+
// This is a message from the backend manager
65+
if (event.data.source === 'react-devtools-backend-manager') {
66+
chrome.runtime.sendMessage({
67+
payload: event.data.payload,
68+
});
69+
}
5670
}
5771
}
58-
}
5972

60-
function handleDisconnect() {
61-
window.removeEventListener('message', handleMessageFromPage);
62-
port = null;
73+
function handleDisconnect() {
74+
window.removeEventListener('message', handleMessageFromPage);
75+
port = null;
6376

64-
connectPort();
65-
}
77+
connectPort();
78+
}
6679

67-
// Creates port from application page to the React DevTools' service worker
68-
// Which then connects it with extension port
69-
function connectPort() {
70-
port = chrome.runtime.connect({
71-
name: 'proxy',
72-
});
80+
// Creates port from application page to the React DevTools' service worker
81+
// Which then connects it with extension port
82+
function connectPort() {
83+
port = chrome.runtime.connect({
84+
name: 'proxy',
85+
});
7386

74-
window.addEventListener('message', handleMessageFromPage);
87+
window.addEventListener('message', handleMessageFromPage);
7588

76-
port.onMessage.addListener(handleMessageFromDevtools);
77-
port.onDisconnect.addListener(handleDisconnect);
89+
port.onMessage.addListener(handleMessageFromDevtools);
90+
port.onDisconnect.addListener(handleDisconnect);
91+
}
7892
}

0 commit comments

Comments
 (0)