Chrome Devpanel Extension Communicating with Background Page
The goal is to create a channel ("port") for communication. It does not matter how the port is created, as long as the connection is correctly maintained.
The devtools script has to initiate the port, because the background script does not know when a devtools panel is created.
Here's a basic example, which shows a bidirectional communication method:
devtools.js
chrome.devtools.panels.create('Test', '/icon.png', '/panel.html', function(extensionPanel) {
var _window; // Going to hold the reference to panel.html's `window`
var data = [];
var port = chrome.runtime.connect({name: 'devtools'});
port.onMessage.addListener(function(msg) {
// Write information to the panel, if exists.
// If we don't have a panel reference (yet), queue the data.
if (_window) {
_window.do_something(msg);
} else {
data.push(msg);
}
});
extensionPanel.onShown.addListener(function tmp(panelWindow) {
extensionPanel.onShown.removeListener(tmp); // Run once only
_window = panelWindow;
// Release queued data
var msg;
while (msg = data.shift())
_window.do_something(msg);
// Just to show that it's easy to talk to pass a message back:
_window.respond = function(msg) {
port.postMessage(msg);
};
});
});
Now, the panel is capable of sending/receiving messages over a port. The panel's script (external script file, because of the CSP) may look like:
panel.js
function do_something(msg) {
document.body.textContent += '\n' + msg; // Stupid example, PoC
}
document.documentElement.onclick = function() {
// No need to check for the existence of `respond`, because
// the panel can only be clicked when it's visible...
respond('Another stupid example!');
};
Now, the background page's script:
background.js
var ports = [];
chrome.runtime.onConnect.addListener(function(port) {
if (port.name !== "devtools") return;
ports.push(port);
// Remove port when destroyed (eg when devtools instance is closed)
port.onDisconnect.addListener(function() {
var i = ports.indexOf(port);
if (i !== -1) ports.splice(i, 1);
});
port.onMessage.addListener(function(msg) {
// Received message from devtools. Do something:
console.log('Received message from devtools page', msg);
});
});
// Function to send a message to all devtools.html views:
function notifyDevtools(msg) {
ports.forEach(function(port) {
port.postMessage(msg);
});
}
To test, simply run notifyDevtools('Foo');
on the background page (e.g. via the console). In this demo, the message will be sent to all devtools. Upon receipt, the devtools panel will contain the received message.
Put the extension together using:
manifest.json
{
"name": "Test",
"manifest_version": 2,
"version": "1",
"devtools_page": "devtools.html",
"background":{"scripts":["background.js"]}
}
panel.html
<script src="panel.js"></script> <!-- Doctype etc not added for conciseness-->
devtools.html
<script src="devtools.js"></script>
See also
- How to modify content under a devtools panel in a Chrome extension?
chrome.devtools
API- Message passing: Long-lived connections
-
Content Security Policy in Chrome extensions ("Inline JavaScript (...) will not be executed. This restriction bans both inline
<script>
blocks and inline event handlers.")
Comments
-
Salami almost 2 years
I have an extension to the chrome devtools panel. I can send messages to the page using
chrome.devtools.inspectedWindow.eval
... but how do I receive messages in the dev panel? Specifically, I need my devpanel to hook into events that happen on the page. I can't get it to listen to events on my content script, nor the background page.I've tried
chrome.extension.sendMessage
in the content script, along withchrome.extension.onMessage.addListener
in the dev panel script. ButsendMessage
complains withPort error: Could not establish connection. Receiving end does not exist.
The issue persists with long-lived connections:
In content script or background page:
var port = chrome.extension.connect({name: "test"}); port.postMessage({msg: "testing"});
In dev tools panel javascript:
chrome.extension.onConnect.addListener(function(port) { port.onMessage.addListener(function(msg) { // never gets here }); });
How can I listen for events that are triggered in my content script-- in my dev tool panel? A diagram like this from Firefox's Add-On SDK would be great: https://addons.mozilla.org/en-US/developers/docs/sdk/latest/static-files/media/content-scripting-overview.png
-
Salami almost 12 yearsExcellent answer, sending the messages via ports that were not disconnected did the trick.
-
Raymond Camden over 11 yearsI'm having trouble getting this to work with content scripts as well. I need a content script to be able to talk to a devtools script. Got an example of that?
-
Rob W over 11 years@RaymondCamden Use the code in this answer to set up a channel with the background page (from the devtools). Include the tab's ID (derived from
chrome.devtools.inspectedWindow.tabId
in the message. In my answer, I stored the ports by a rather arbitrarily chosen (undocumented) ID.portId_
. You could store the ports bytabId
instead, and use the regular messaging methods to communicate between content scripts and devtools. -
airportyh about 11 yearsThanks for the example! I have a followup question to that: from the documentation it seems it should be possible to make XMLHttpRequests to any website as long as the "permissions" field in the manifest allows it, but this has not worked for after many attempts.
-
Rob W about 11 years
-
Kousick Shanmugam Nagaraj about 9 years@RobW - Worked like a charm. Was looking for this. Good way of describing the solution. This is one good post for chrome devtools since there are no proper documentation.
-
mindplay.dk over 5 yearsDownvoted as this no longer works ... copied/pasted the example verbatim, and it doesn't work anymore. Looks like
addListener
was deprecated? Would love to see an update of this answer to current APIs. -
Rob W over 5 years@mindplay.dk The answer still works. Tested with Chrome 71 and 73.0.3666.0. To test: 1) Load extension 2) open devtools 3) Switch to "Test" panel 4) click on the white panel (i.e. panel.html, to trigger panel.js's onclick handler) 5) switch back to the Console tab and observe the expected message. Note that the background page has to be loaded BEFORE the devtools (otherwise the
runtime.onConnect
listener won't be able to respond). -
mindplay.dk over 5 years@RobW you're right, it does work! My mistake was the panel.html file, where I put the script in
head
rather than inbody
- which appears to make it load too soon. (Consider adding the body tags to the html examples, so others don't make that mistake? I actually knew that the body tag was implied in HTML5, but it's easy to forget when it isn't shown.)