Chrome Devpanel Extension Communicating with Background Page

12,236

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

Share:
12,236
Salami
Author by

Salami

Computer

Updated on June 13, 2022

Comments

  • Salami
    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 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
    Salami almost 12 years
    Excellent answer, sending the messages via ports that were not disconnected did the trick.
  • Raymond Camden
    Raymond Camden over 11 years
    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?
  • Rob W
    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 by tabId instead, and use the regular messaging methods to communicate between content scripts and devtools.
  • airportyh
    airportyh about 11 years
    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.
  • Rob W
    Rob W about 11 years
    @airportyh Create a new question or chat room to keep the off-topic comments to a minimum.
  • Kousick Shanmugam Nagaraj
    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
    mindplay.dk over 5 years
    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.
  • Rob W
    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
    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 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.)