20

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 with chrome.extension.onMessage.addListener in the dev panel script. But sendMessage complains with Port 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
  • 2,849
  • 4
  • 24
  • 33
  • The documentation provides an example of communicating between a background script and the devtools script. See the ["Broken links" demo](http://code.google.com/chrome/extensions/samples.html#9004d1a1b975859d07bdff94ea0a4baa106c4fd1). ([background.js](http://code.google.com/chrome/extensions/examples/api/devtools/audits/broken-links/background.js) and [devtools.js](http://code.google.com/chrome/extensions/examples/api/devtools/audits/broken-links/devtools.js)) – Rob W Jul 26 '12 at 09:05
  • @RobW Although the background script and devtools are communicating in that example, this sort of communication wont work for me. In that example, devtools.js sends a message and gets a callback. I need the background script to send a message to the devtools page when a event occurs, this wont work with the callback approach unless I poll continuously -- a bad way to do it. – Salami Jul 26 '12 at 19:17
  • The concept I wanted to show is that the connection is initialized by the devtools script, not by the background script. I've now turned that concept in a full-fledged answer, including a sample which shows how to pass messages in both directons, and modify the panel's contents following the message. – Rob W Jul 26 '12 at 20:46

1 Answers1

57

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

Saiansh Singh
  • 583
  • 5
  • 16
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Excellent answer, sending the messages via ports that were not disconnected did the trick. – Salami Jul 26 '12 at 22:32
  • I'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? – Raymond Camden Jan 10 '13 at 21:01
  • @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`](http://developer.chrome.com/extensions/devtools.inspectedWindow.html#property-tabId) in the message. In my answer, I stored the ports by a rather arbitrarily chosen (undocumented) ID `.portId_`. You could store the ports by `tabId` instead, and use the regular messaging methods to communicate between content scripts and devtools. – Rob W Jan 10 '13 at 21:08
  • Thanks 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. – airportyh Mar 01 '13 at 19:11
  • @airportyh Create a [new](http://stackoverflow.com/questions/ask) question or [chat](http://chat.stackoverflow.com/) room to keep the off-topic comments to a minimum. – Rob W Mar 01 '13 at 20:14
  • @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. – Kousick Shanmugam Nagaraj Feb 19 '15 at 09:59
  • Downvoted 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. – mindplay.dk Jan 08 '19 at 16:24
  • 1
    @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). – Rob W Jan 08 '19 at 22:09
  • @RobW you're right, it does work! My mistake was the panel.html file, where I put the script in `head` rather than in `body` - 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.) – mindplay.dk Jan 09 '19 at 07:36