mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[REF] devtools: Better messages forwarding
This commit improves how messages are relayed from the page to the devtools by using a specific port for each instance of the owl devtools panel and refreshing it through the heartbeat message. This allows to prevent sending messages to every devtools instances and sorting the messages inside.
This commit is contained in:
committed by
Géry Debongnie
parent
c049022798
commit
606d14a399
@@ -5,7 +5,7 @@ let owlStatus = 0;
|
|||||||
const browserInstance = IS_FIREFOX ? browser : chrome;
|
const browserInstance = IS_FIREFOX ? browser : chrome;
|
||||||
|
|
||||||
// Used to keep track of the tabs where the owl devtools have been opened
|
// 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
|
// Load the devtools global hook this way when running on manifest v3 chrome
|
||||||
if (!IS_FIREFOX) {
|
if (!IS_FIREFOX) {
|
||||||
@@ -75,8 +75,23 @@ browserInstance.runtime.onMessage.addListener(async (message, sender, sendRespon
|
|||||||
return true;
|
return true;
|
||||||
} else if (message.type === "owlStatus") {
|
} else if (message.type === "owlStatus") {
|
||||||
setOwlStatus(message.data);
|
setOwlStatus(message.data);
|
||||||
// Dummy message to test if the extension context is still valid
|
// Refresh panel connection timeout
|
||||||
} else if (message.type === "keepAlive") {
|
} 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;
|
return;
|
||||||
// Open the devtools documentation in a new tab
|
// Open the devtools documentation in a new tab
|
||||||
} else if (message.type === "openDoc") {
|
} else if (message.type === "openDoc") {
|
||||||
@@ -87,10 +102,15 @@ browserInstance.runtime.onMessage.addListener(async (message, sender, sendRespon
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
// Relay the received message to the devtools app
|
// Register a new port for the devtools panel
|
||||||
} else if (message.type === "newDevtoolsPanel") {
|
} else if (message.type === "newDevtoolsPanel") {
|
||||||
const tab = await getActiveTabURL();
|
const id = message.id;
|
||||||
activePanels.add(tab);
|
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
|
// This is solely for firefox which doesnt allow access to the chrome.tabs api inside devtools
|
||||||
} else if (message.type === "getActiveTabURL") {
|
} else if (message.type === "getActiveTabURL") {
|
||||||
getActiveTabURL().then((tab) => {
|
getActiveTabURL().then((tab) => {
|
||||||
@@ -98,15 +118,13 @@ browserInstance.runtime.onMessage.addListener(async (message, sender, sendRespon
|
|||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
const tab = await getActiveTabURL();
|
const destinationPanel = activePanels.get(sender.tab.id);
|
||||||
if (!activePanels.has(tab)) {
|
if (destinationPanel) {
|
||||||
return;
|
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 }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ function createPanelsIfOwl() {
|
|||||||
"../../assets/icon128.png",
|
"../../assets/icon128.png",
|
||||||
IS_FIREFOX ? "devtools_panel.html" : "devtools_app/devtools_panel.html"
|
IS_FIREFOX ? "devtools_panel.html" : "devtools_app/devtools_panel.html"
|
||||||
);
|
);
|
||||||
browserInstance.runtime.sendMessage({ type: "newDevtoolsPanel" });
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -652,103 +652,89 @@ async function init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
}
|
|
||||||
|
|
||||||
// Heartbeat message to test whether the extension context is still valid or not
|
browserInstance.runtime.sendMessage({ type: "newDevtoolsPanel", id: store.devtoolsId });
|
||||||
setInterval(() => {
|
|
||||||
if (store.extensionContextStatus) {
|
// Heartbeat message to test whether the extension context is still valid or not
|
||||||
try {
|
setInterval(() => {
|
||||||
browserInstance.runtime.sendMessage({ type: "keepAlive" });
|
if (store.extensionContextStatus) {
|
||||||
} catch (e) {
|
try {
|
||||||
store.extensionContextStatus = false;
|
browserInstance.runtime.sendMessage({ type: "keepAlive", id: store.devtoolsId });
|
||||||
|
} catch (e) {
|
||||||
|
store.extensionContextStatus = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}, 500);
|
||||||
}, 1000);
|
}
|
||||||
|
|
||||||
let flushRendersTimeout = false;
|
let flushRendersTimeout = false;
|
||||||
// Connect to the port to communicate to the background script
|
// Connect to the port to communicate to the background script
|
||||||
browserInstance.runtime.onConnect.addListener((port) => {
|
browserInstance.runtime.onConnect.addListener((port) => {
|
||||||
if (!port.name === "OwlDevtoolsPort") {
|
if (port.name === "OwlDevtoolsPort_" + store.devtoolsId) {
|
||||||
return;
|
port.onMessage.addListener(async (msg) => {
|
||||||
}
|
// Reload the tree after checking if the scripts are loaded when this message is received
|
||||||
port.onMessage.addListener(async (msg) => {
|
if (msg.type === "Reload") {
|
||||||
// Reload the tree after checking if the scripts are loaded when this message is received
|
store.owlStatus = await evalInWindow("window.__OWL__DEVTOOLS_GLOBAL_HOOK__ !== undefined;");
|
||||||
if (msg.type === "Reload") {
|
if (store.owlStatus) {
|
||||||
const tab = await getTabURL();
|
evalInWindow("__OWL__DEVTOOLS_GLOBAL_HOOK__.devtoolsId = " + store.devtoolsId + ";");
|
||||||
// Since this message is sent to all devtools windows, only take it into account when this is the active tab
|
store.resetData();
|
||||||
if (tab !== store.devtoolsId) {
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
store.owlStatus = await evalInWindow("window.__OWL__DEVTOOLS_GLOBAL_HOOK__ !== undefined;");
|
// Received when a frame has been delayed when loading the scripts due to owl being lazy loaded
|
||||||
if (store.owlStatus) {
|
if (msg.type === "FrameReady") {
|
||||||
evalInWindow("__OWL__DEVTOOLS_GLOBAL_HOOK__.devtoolsId = " + store.devtoolsId + ";");
|
store.updateIFrameList();
|
||||||
|
store.owlStatus = true;
|
||||||
store.resetData();
|
store.resetData();
|
||||||
}
|
}
|
||||||
}
|
// We need to reload the components tree when the set of apps in the page is modified
|
||||||
// Received when a frame has been delayed when loading the scripts due to owl being lazy loaded
|
if (msg.type === "RefreshApps") {
|
||||||
if (msg.type === "FrameReady") {
|
store.loadComponentsTree(true);
|
||||||
const tab = await getTabURL();
|
|
||||||
// Same as for the reload message
|
|
||||||
if (tab !== store.devtoolsId) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
store.updateIFrameList();
|
// When message of type Flush is received, overwrite the component tree with the new one from page
|
||||||
store.owlStatus = true;
|
// A flush message is sent everytime a component is rendered on the page
|
||||||
store.resetData();
|
if (msg.type === "Flush") {
|
||||||
}
|
if (msg.origin.frame !== store.activeFrame) {
|
||||||
// We need to reload the components tree when the set of apps in the page is modified
|
return;
|
||||||
if (msg.type === "RefreshApps") {
|
}
|
||||||
store.loadComponentsTree(true);
|
if (!(Array.isArray(msg.data) && msg.data.every((val) => typeof val === "string"))) {
|
||||||
}
|
return;
|
||||||
// Filter out the messages that are not destined to this devtools tab. The messages above may be sent before
|
}
|
||||||
// the devtoolsId is set
|
// This determines which components will have a short highlight effect in the tree to indicate they have been rendered
|
||||||
if (msg.origin.id !== store.devtoolsId) {
|
store.renderPaths.add(JSON.stringify(msg.data));
|
||||||
return;
|
clearTimeout(flushRendersTimeout);
|
||||||
}
|
flushRendersTimeout = setTimeout(() => {
|
||||||
// When message of type Flush is received, overwrite the component tree with the new one from page
|
store.renderPaths.clear();
|
||||||
// A flush message is sent everytime a component is rendered on the page
|
}, 100);
|
||||||
if (msg.type === "Flush") {
|
store.loadComponentsTree(true);
|
||||||
if (msg.origin.frame !== store.activeFrame) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!(Array.isArray(msg.data) && msg.data.every((val) => typeof val === "string"))) {
|
// Select the component based on the path received with the SelectElement message
|
||||||
return;
|
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
|
// Stop the DOM element selector tool upon receiving the StopSelector message
|
||||||
store.renderPaths.add(JSON.stringify(msg.data));
|
if (msg.type === "StopSelector") {
|
||||||
clearTimeout(flushRendersTimeout);
|
store.componentSearch.activeSelector = false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
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
|
// Logic for recording an event when the event message is received
|
||||||
if (msg.type === "Event") {
|
if (msg.type === "Event") {
|
||||||
let events = msg.data;
|
let events = msg.data;
|
||||||
loadEvents(events);
|
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();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
// 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
|
// Load all settings from the chrome sync storage
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
this.addedElements = [];
|
this.addedElements = [];
|
||||||
// To keep track of the succession order of the render events
|
// To keep track of the succession order of the render events
|
||||||
this.eventId = 0;
|
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
|
// Set to keep track of the frame on which this script is loaded
|
||||||
this.frame = "top";
|
this.frame = "top";
|
||||||
// Allows to launch a message each time an iframe html element is added to the page
|
// Allows to launch a message each time an iframe html element is added to the page
|
||||||
@@ -38,7 +36,6 @@
|
|||||||
source: "owl-devtools",
|
source: "owl-devtools",
|
||||||
type: "NewIFrame",
|
type: "NewIFrame",
|
||||||
data: addedNode.contentDocument.location.href,
|
data: addedNode.contentDocument.location.href,
|
||||||
origin: { id: self.devtoolsId },
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +260,7 @@
|
|||||||
source: "owl-devtools",
|
source: "owl-devtools",
|
||||||
type: "Flush",
|
type: "Flush",
|
||||||
data: path,
|
data: path,
|
||||||
origin: { id: self.devtoolsId, frame: self.frame },
|
origin: { frame: self.frame },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -372,7 +369,6 @@
|
|||||||
source: "owl-devtools",
|
source: "owl-devtools",
|
||||||
type: "Event",
|
type: "Event",
|
||||||
data: self.eventsBatch,
|
data: self.eventsBatch,
|
||||||
origin: { id: self.devtoolsId },
|
|
||||||
});
|
});
|
||||||
self.eventsBatch = [];
|
self.eventsBatch = [];
|
||||||
}
|
}
|
||||||
@@ -628,7 +624,6 @@
|
|||||||
source: "owl-devtools",
|
source: "owl-devtools",
|
||||||
type: "SelectElement",
|
type: "SelectElement",
|
||||||
data: path,
|
data: path,
|
||||||
origin: { id: this.devtoolsId },
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -656,7 +651,6 @@
|
|||||||
window.top.postMessage({
|
window.top.postMessage({
|
||||||
source: "owl-devtools",
|
source: "owl-devtools",
|
||||||
type: "StopSelector",
|
type: "StopSelector",
|
||||||
origin: { id: this.devtoolsId },
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1767,6 +1761,6 @@
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
checkOwlStatus();
|
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;
|
return window.__OWL__DEVTOOLS_GLOBAL_HOOK__ !== undefined;
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user