refactor: refactored devtools browser extension scripts to improve port management and service worker lifetime (#27215)

Fixes https://github.com/facebook/react/issues/27119,
https://github.com/facebook/react/issues/27185.

Fixed:
- React DevTools now works as expected when user performs in-tab
navigation, previously it was just stuck.


https://github.com/facebook/react/assets/28902667/b11c5f84-7155-47a5-8b5a-7e90baca5347

- When user closes browser DevTools panel, we now do some cleanup to
disconnect ports and emit shutdown event for bridge. This should fix the
issue with registering duplicated fibers with the same id in Store.

Changed:
- We reconnect proxy port once in 25 seconds, in order to [keep service
worker
alive](https://developer.chrome.com/docs/extensions/whatsnew/#m110-sw-idle).
- Instead of unregistering dynamically injected content scripts, wen now
get list of already registered scripts and filter them out from scripts
that we want to inject again, see dynamicallyInjectContentScripts.js.
- Split `main.js` and `background.js` into multiple files.

Tested on Chromium and Firefox browsers.
This commit is contained in:
Ruslan Lesiutin
2023-08-29 12:09:26 +01:00
committed by GitHub
parent f47956239f
commit 8fbd307942
22 changed files with 1159 additions and 847 deletions
@@ -2,9 +2,23 @@
'use strict';
let backendDisconnected: boolean = false;
let port = null;
let backendInitialized: boolean = false;
connectPort();
sayHelloToBackendManager();
// The backend waits to install the global hook until notified by the content script.
// In the event of a page reload, the content script might be loaded before the backend manager is injected.
// Because of this we need to poll the backend manager until it has been initialized.
const intervalID = setInterval(() => {
if (backendInitialized) {
clearInterval(intervalID);
} else {
sayHelloToBackendManager();
}
}, 500);
function sayHelloToBackendManager() {
window.postMessage(
{
@@ -16,6 +30,18 @@ function sayHelloToBackendManager() {
}
function handleMessageFromDevtools(message) {
if (message.source === 'react-devtools-service-worker' && message.stop) {
window.removeEventListener('message', handleMessageFromPage);
// Calling disconnect here should not emit onDisconnect event inside this script
// This port will not attempt to reconnect again
// It will connect only once this content script will be injected again
port?.disconnect();
port = null;
return;
}
window.postMessage(
{
source: 'react-devtools-content-script',
@@ -33,6 +59,7 @@ function handleMessageFromPage(event) {
port.postMessage(event.data.payload);
}
// This is a message from the backend manager
if (event.data.source === 'react-devtools-backend-manager') {
chrome.runtime.sendMessage({
@@ -43,42 +70,21 @@ function handleMessageFromPage(event) {
}
function handleDisconnect() {
backendDisconnected = true;
window.removeEventListener('message', handleMessageFromPage);
port = null;
window.postMessage(
{
source: 'react-devtools-content-script',
payload: {
type: 'event',
event: 'shutdown',
},
},
'*',
);
connectPort();
}
// proxy from main page to devtools (via the background page)
const port = chrome.runtime.connect({
name: 'content-script',
});
port.onMessage.addListener(handleMessageFromDevtools);
port.onDisconnect.addListener(handleDisconnect);
// Creates port from application page to the React DevTools' service worker
// Which then connects it with extension port
function connectPort() {
port = chrome.runtime.connect({
name: 'proxy',
});
window.addEventListener('message', handleMessageFromPage);
window.addEventListener('message', handleMessageFromPage);
sayHelloToBackendManager();
// The backend waits to install the global hook until notified by the content script.
// In the event of a page reload, the content script might be loaded before the backend manager is injected.
// Because of this we need to poll the backend manager until it has been initialized.
if (!backendInitialized) {
const intervalID = setInterval(() => {
if (backendInitialized || backendDisconnected) {
clearInterval(intervalID);
} else {
sayHelloToBackendManager();
}
}, 500);
port.onMessage.addListener(handleMessageFromDevtools);
port.onDisconnect.addListener(handleDisconnect);
}