'use strict';
import {IS_FIREFOX, EXTENSION_CONTAINED_VERSIONS} from '../utils';
import './dynamicallyInjectContentScripts';
import './tabsManager';
import setExtensionIconAndPopup from './setExtensionIconAndPopup';
const ports = {};
function registerTab(tabId) {
if (!ports[tabId]) {
ports[tabId] = {
extension: null,
proxy: null,
disconnectPipe: null,
};
}
}
function registerExtensionPort(port, tabId) {
ports[tabId].extension = port;
port.onDisconnect.addListener(() => {
ports[tabId].disconnectPipe?.();
delete ports[tabId].extension;
});
}
function registerProxyPort(port, tabId) {
ports[tabId].proxy = port;
port.onDisconnect.addListener(() => {
ports[tabId].disconnectPipe?.();
delete ports[tabId].proxy;
});
}
function isNumeric(str: string): boolean {
return +str + '' === str;
}
chrome.runtime.onConnect.addListener(port => {
if (port.name === 'proxy') {
const tabId = port.sender.tab.id;
if (ports[tabId]?.proxy) {
port.disconnect();
return;
}
registerTab(tabId);
registerProxyPort(port, tabId);
if (ports[tabId].extension) {
connectExtensionAndProxyPorts(
ports[tabId].extension,
ports[tabId].proxy,
tabId,
);
}
return;
}
if (isNumeric(port.name)) {
const tabId = +port.name;
registerTab(tabId);
registerExtensionPort(port, tabId);
if (ports[tabId].proxy) {
connectExtensionAndProxyPorts(
ports[tabId].extension,
ports[tabId].proxy,
tabId,
);
}
return;
}
console.warn(`Unknown port ${port.name} connected`);
});
function connectExtensionAndProxyPorts(extensionPort, proxyPort, tabId) {
if (!extensionPort) {
throw new Error(
`Attempted to connect ports, when extension port is not present`,
);
}
if (!proxyPort) {
throw new Error(
`Attempted to connect ports, when proxy port is not present`,
);
}
if (ports[tabId].disconnectPipe) {
throw new Error(
`Attempted to connect already connected ports for tab with id ${tabId}`,
);
}
function extensionPortMessageListener(message) {
try {
proxyPort.postMessage(message);
} catch (e) {
if (__DEV__) {
console.log(`Broken pipe ${tabId}: `, e);
}
disconnectListener();
}
}
function proxyPortMessageListener(message) {
try {
extensionPort.postMessage(message);
} catch (e) {
if (__DEV__) {
console.log(`Broken pipe ${tabId}: `, e);
}
disconnectListener();
}
}
function disconnectListener() {
extensionPort.onMessage.removeListener(extensionPortMessageListener);
proxyPort.onMessage.removeListener(proxyPortMessageListener);
delete ports[tabId].disconnectPipe;
}
ports[tabId].disconnectPipe = disconnectListener;
extensionPort.onMessage.addListener(extensionPortMessageListener);
proxyPort.onMessage.addListener(proxyPortMessageListener);
extensionPort.onDisconnect.addListener(disconnectListener);
proxyPort.onDisconnect.addListener(disconnectListener);
}
chrome.runtime.onMessage.addListener((message, sender) => {
const tab = sender.tab;
if (tab) {
const id = tab.id;
if (message.hasDetectedReact) {
setExtensionIconAndPopup(message.reactBuildType, id);
} else {
const extensionPort = ports[id]?.extension;
switch (message.payload?.type) {
case 'fetch-file-with-cache-complete':
case 'fetch-file-with-cache-error':
extensionPort?.postMessage(message);
break;
case 'react-devtools-required-backends':
const backendsToDownload = [];
message.payload.versions.forEach(version => {
if (EXTENSION_CONTAINED_VERSIONS.includes(version)) {
if (!IS_FIREFOX) {
chrome.scripting.executeScript({
target: {tabId: id},
files: [`/build/react_devtools_backend_${version}.js`],
world: chrome.scripting.ExecutionWorld.MAIN,
});
}
} else {
backendsToDownload.push(version);
}
});
extensionPort?.postMessage({
payload: {
type: 'react-devtools-additional-backends',
versions: backendsToDownload,
},
});
break;
}
}
}
if (message?.payload?.type === 'react-devtools-inject-backend-manager') {
const tabId = message.payload.tabId;
if (tabId && !IS_FIREFOX) {
chrome.scripting.executeScript({
target: {tabId},
files: ['/build/backendManager.js'],
world: chrome.scripting.ExecutionWorld.MAIN,
});
}
}
});
chrome.tabs.onActivated.addListener(({tabId: activeTabId}) => {
for (const registeredTabId in ports) {
if (
ports[registeredTabId].proxy != null &&
ports[registeredTabId].extension != null
) {
const numericRegisteredTabId = +registeredTabId;
const event =
activeTabId === numericRegisteredTabId
? 'resumeElementPolling'
: 'pauseElementPolling';
ports[registeredTabId].extension.postMessage({event});
}
}
});