diff --git a/tools/devtools/src/background.js b/tools/devtools/src/background.js index ccb45014..b06214e6 100644 --- a/tools/devtools/src/background.js +++ b/tools/devtools/src/background.js @@ -5,7 +5,7 @@ let owlStatus = 0; const browserInstance = IS_FIREFOX ? browser : chrome; // Used to keep track of the tabs where the owl devtools have been opened -const activePanels = new Set(); +const activePanels = new Map(); // Load the devtools global hook this way when running on manifest v3 chrome if (!IS_FIREFOX) { @@ -75,8 +75,23 @@ browserInstance.runtime.onMessage.addListener(async (message, sender, sendRespon return true; } else if (message.type === "owlStatus") { setOwlStatus(message.data); - // Dummy message to test if the extension context is still valid + // Refresh panel connection timeout } else if (message.type === "keepAlive") { + const panel = activePanels.get(message.id); + if (panel) { + clearTimeout(panel.expirationTimeout); + panel.expirationTimeout = setTimeout(() => { + activePanels.delete(message.id); + panel.port.disconnect(); + }, 750); + } else { + const port = browserInstance.runtime.connect({ name: "OwlDevtoolsPort_" + message.id }); + const expirationTimeout = setTimeout(() => { + activePanels.delete(message.id); + port.disconnect(); + }, 750); + activePanels.set(message.id, { port: port, expirationTimeout: expirationTimeout }); + } return; // Open the devtools documentation in a new tab } else if (message.type === "openDoc") { @@ -87,10 +102,15 @@ browserInstance.runtime.onMessage.addListener(async (message, sender, sendRespon } ); return; - // Relay the received message to the devtools app + // Register a new port for the devtools panel } else if (message.type === "newDevtoolsPanel") { - const tab = await getActiveTabURL(); - activePanels.add(tab); + const id = message.id; + const port = browserInstance.runtime.connect({ name: "OwlDevtoolsPort_" + id }); + const expirationTimeout = setTimeout(() => { + activePanels.delete(id); + port.disconnect(); + }, 750); + activePanels.set(message.id, { port: port, expirationTimeout: expirationTimeout }); // This is solely for firefox which doesnt allow access to the chrome.tabs api inside devtools } else if (message.type === "getActiveTabURL") { getActiveTabURL().then((tab) => { @@ -98,15 +118,13 @@ browserInstance.runtime.onMessage.addListener(async (message, sender, sendRespon }); return true; } else { - const tab = await getActiveTabURL(); - if (!activePanels.has(tab)) { - return; + const destinationPanel = activePanels.get(sender.tab.id); + if (destinationPanel) { + destinationPanel.port.postMessage( + message.data + ? { type: message.type, data: message.data, origin: message.origin } + : { type: message.type, origin: message.origin } + ); } - const port = browserInstance.runtime.connect({ name: "OwlDevtoolsPort" }); - port.postMessage( - message.data - ? { type: message.type, data: message.data, origin: message.origin } - : { type: message.type, origin: message.origin } - ); } }); diff --git a/tools/devtools/src/devtools_app/devtools.js b/tools/devtools/src/devtools_app/devtools.js index 3300937c..10f4c099 100644 --- a/tools/devtools/src/devtools_app/devtools.js +++ b/tools/devtools/src/devtools_app/devtools.js @@ -27,7 +27,6 @@ function createPanelsIfOwl() { "../../assets/icon128.png", IS_FIREFOX ? "devtools_panel.html" : "devtools_app/devtools_panel.html" ); - browserInstance.runtime.sendMessage({ type: "newDevtoolsPanel" }); } ); } diff --git a/tools/devtools/src/devtools_app/store/store.js b/tools/devtools/src/devtools_app/store/store.js index 0f533302..ead97046 100644 --- a/tools/devtools/src/devtools_app/store/store.js +++ b/tools/devtools/src/devtools_app/store/store.js @@ -652,103 +652,89 @@ async function init() { } loadSettings(); -} -// Heartbeat message to test whether the extension context is still valid or not -setInterval(() => { - if (store.extensionContextStatus) { - try { - browserInstance.runtime.sendMessage({ type: "keepAlive" }); - } catch (e) { - store.extensionContextStatus = false; + browserInstance.runtime.sendMessage({ type: "newDevtoolsPanel", id: store.devtoolsId }); + + // Heartbeat message to test whether the extension context is still valid or not + setInterval(() => { + if (store.extensionContextStatus) { + try { + browserInstance.runtime.sendMessage({ type: "keepAlive", id: store.devtoolsId }); + } catch (e) { + store.extensionContextStatus = false; + } } - } -}, 1000); + }, 500); +} let flushRendersTimeout = false; // Connect to the port to communicate to the background script browserInstance.runtime.onConnect.addListener((port) => { - if (!port.name === "OwlDevtoolsPort") { - return; - } - port.onMessage.addListener(async (msg) => { - // Reload the tree after checking if the scripts are loaded when this message is received - if (msg.type === "Reload") { - const tab = await getTabURL(); - // Since this message is sent to all devtools windows, only take it into account when this is the active tab - if (tab !== store.devtoolsId) { - return; + if (port.name === "OwlDevtoolsPort_" + store.devtoolsId) { + port.onMessage.addListener(async (msg) => { + // Reload the tree after checking if the scripts are loaded when this message is received + if (msg.type === "Reload") { + store.owlStatus = await evalInWindow("window.__OWL__DEVTOOLS_GLOBAL_HOOK__ !== undefined;"); + if (store.owlStatus) { + evalInWindow("__OWL__DEVTOOLS_GLOBAL_HOOK__.devtoolsId = " + store.devtoolsId + ";"); + store.resetData(); + } } - store.owlStatus = await evalInWindow("window.__OWL__DEVTOOLS_GLOBAL_HOOK__ !== undefined;"); - if (store.owlStatus) { - evalInWindow("__OWL__DEVTOOLS_GLOBAL_HOOK__.devtoolsId = " + store.devtoolsId + ";"); + // Received when a frame has been delayed when loading the scripts due to owl being lazy loaded + if (msg.type === "FrameReady") { + store.updateIFrameList(); + store.owlStatus = true; store.resetData(); } - } - // Received when a frame has been delayed when loading the scripts due to owl being lazy loaded - if (msg.type === "FrameReady") { - const tab = await getTabURL(); - // Same as for the reload message - if (tab !== store.devtoolsId) { - return; + // We need to reload the components tree when the set of apps in the page is modified + if (msg.type === "RefreshApps") { + store.loadComponentsTree(true); } - store.updateIFrameList(); - store.owlStatus = true; - store.resetData(); - } - // We need to reload the components tree when the set of apps in the page is modified - if (msg.type === "RefreshApps") { - store.loadComponentsTree(true); - } - // Filter out the messages that are not destined to this devtools tab. The messages above may be sent before - // the devtoolsId is set - if (msg.origin.id !== store.devtoolsId) { - return; - } - // When message of type Flush is received, overwrite the component tree with the new one from page - // A flush message is sent everytime a component is rendered on the page - if (msg.type === "Flush") { - if (msg.origin.frame !== store.activeFrame) { - return; + // When message of type Flush is received, overwrite the component tree with the new one from page + // A flush message is sent everytime a component is rendered on the page + if (msg.type === "Flush") { + if (msg.origin.frame !== store.activeFrame) { + return; + } + if (!(Array.isArray(msg.data) && msg.data.every((val) => typeof val === "string"))) { + return; + } + // This determines which components will have a short highlight effect in the tree to indicate they have been rendered + store.renderPaths.add(JSON.stringify(msg.data)); + clearTimeout(flushRendersTimeout); + flushRendersTimeout = setTimeout(() => { + store.renderPaths.clear(); + }, 100); + store.loadComponentsTree(true); } - if (!(Array.isArray(msg.data) && msg.data.every((val) => typeof val === "string"))) { - return; + // Select the component based on the path received with the SelectElement message + if (msg.type === "SelectElement") { + if (!(Array.isArray(msg.data) && msg.data.every((val) => typeof val === "string"))) { + return; + } + store.selectComponent(msg.data); } - // This determines which components will have a short highlight effect in the tree to indicate they have been rendered - store.renderPaths.add(JSON.stringify(msg.data)); - clearTimeout(flushRendersTimeout); - flushRendersTimeout = setTimeout(() => { - store.renderPaths.clear(); - }, 100); - store.loadComponentsTree(true); - } - // Select the component based on the path received with the SelectElement message - if (msg.type === "SelectElement") { - if (!(Array.isArray(msg.data) && msg.data.every((val) => typeof val === "string"))) { - return; + // Stop the DOM element selector tool upon receiving the StopSelector message + if (msg.type === "StopSelector") { + store.componentSearch.activeSelector = false; } - store.selectComponent(msg.data); - } - // Stop the DOM element selector tool upon receiving the StopSelector message - if (msg.type === "StopSelector") { - store.componentSearch.activeSelector = false; - } - // Logic for recording an event when the event message is received - if (msg.type === "Event") { - let events = msg.data; - loadEvents(events); - } - - // If we know a new iframe has been added to the page, load scripts into it and update the - // frames list if it has been directly loaded. - if (msg.type === "NewIFrame") { - const isLoaded = await loadScripts(msg.data); - if (isLoaded) { - store.updateIFrameList(); + // Logic for recording an event when the event message is received + if (msg.type === "Event") { + let events = msg.data; + loadEvents(events); } - } - }); + + // If we know a new iframe has been added to the page, load scripts into it and update the + // frames list if it has been directly loaded. + if (msg.type === "NewIFrame") { + const isLoaded = await loadScripts(msg.data); + if (isLoaded) { + store.updateIFrameList(); + } + } + }); + } }); // Load all settings from the chrome sync storage diff --git a/tools/devtools/src/page_scripts/owl_devtools_global_hook.js b/tools/devtools/src/page_scripts/owl_devtools_global_hook.js index bc8a2584..31a762f9 100644 --- a/tools/devtools/src/page_scripts/owl_devtools_global_hook.js +++ b/tools/devtools/src/page_scripts/owl_devtools_global_hook.js @@ -17,8 +17,6 @@ this.addedElements = []; // To keep track of the succession order of the render events this.eventId = 0; - // Will be reset as soon as a new devtools owl tab is opened. Allows to avoid sending messages to the wrong devtools tab later on - this.devtoolsId = 0; // Set to keep track of the frame on which this script is loaded this.frame = "top"; // Allows to launch a message each time an iframe html element is added to the page @@ -38,7 +36,6 @@ source: "owl-devtools", type: "NewIFrame", data: addedNode.contentDocument.location.href, - origin: { id: self.devtoolsId }, }); } } @@ -263,7 +260,7 @@ source: "owl-devtools", type: "Flush", data: path, - origin: { id: self.devtoolsId, frame: self.frame }, + origin: { frame: self.frame }, }); } }); @@ -372,7 +369,6 @@ source: "owl-devtools", type: "Event", data: self.eventsBatch, - origin: { id: self.devtoolsId }, }); self.eventsBatch = []; } @@ -628,7 +624,6 @@ source: "owl-devtools", type: "SelectElement", data: path, - origin: { id: this.devtoolsId }, }); } }; @@ -656,7 +651,6 @@ window.top.postMessage({ source: "owl-devtools", type: "StopSelector", - origin: { id: this.devtoolsId }, }); }; @@ -1767,6 +1761,6 @@ false ); checkOwlStatus(); - // Indicates whether the scripts loaded successfully or not (useful when loaded with eval in iframes only) + // Indicates whether the scripts loaded successfully or not (only useful when loaded with eval in iframes) return window.__OWL__DEVTOOLS_GLOBAL_HOOK__ !== undefined; })();