[DevTools] use backend manager to support multiple backends in extension (#26615)

In the extension, currently we do the following:
1. check whether there's at least one React renderer on the page
2. if yes, load the backend to the page
3. initialize the backend 

To support multiple versions of backends, we are changing it to:
1. check the versions of React renders on the page
2. load corresponding React DevTools backends that are shipped with the
extension; if they are not contained (usually prod builds of
prereleases), show a UI to allow users to load them from UI
3. initialize each of the backends

To enable this workflow, a backend will ignore React renderers that does
not match its version

This PR adds a new file "backendManager" in the extension for this
purpose.


------
I've tested it on Chrome, Edge and Firefox extensions
This commit is contained in:
Mengdi Chen
2023-04-18 12:02:42 -04:00
committed by GitHub
parent 77d3b02e5c
commit d962f35cac
17 changed files with 308 additions and 138 deletions
@@ -5,7 +5,7 @@
let backendDisconnected: boolean = false;
let backendInitialized: boolean = false;
function sayHelloToBackend() {
function sayHelloToBackendManager() {
window.postMessage(
{
source: 'react-devtools-content-script',
@@ -26,14 +26,19 @@ function handleMessageFromDevtools(message) {
}
function handleMessageFromPage(event) {
if (
event.source === window &&
event.data &&
event.data.source === 'react-devtools-bridge'
) {
backendInitialized = true;
if (event.source === window && event.data) {
// This is a message from a bridge (initialized by a devtools backend)
if (event.data.source === 'react-devtools-bridge') {
backendInitialized = true;
port.postMessage(event.data.payload);
port.postMessage(event.data.payload);
}
// This is a message from the backend manager
if (event.data.source === 'react-devtools-backend-manager') {
chrome.runtime.sendMessage({
payload: event.data.payload,
});
}
}
}
@@ -63,17 +68,17 @@ port.onDisconnect.addListener(handleDisconnect);
window.addEventListener('message', handleMessageFromPage);
sayHelloToBackend();
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 is injected.
// Because of this we need to poll the backend until it has been initialized.
// 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 {
sayHelloToBackend();
sayHelloToBackendManager();
}
}, 500);
}