Расширение Chrome Devpanel, взаимодействующее с фоновой страницей

У меня есть расширение для панели chrome devtools. Я могу отправлять сообщения на страницу, используя chrome.devtools.inspectedWindow.eval ... но как мне получать сообщения в панели разработчика? В частности, мне нужна моя devpanel, чтобы подключаться к событиям, которые происходят на странице. Я не могу заставить его прослушивать события в моем сценарии содержимого или на фоновой странице.

Я пробовал chrome.extension.sendMessage в сценарии содержимого вместе с chrome.extension.onMessage.addListener в сценарии панели разработки. Но sendMessage жалуется на Port error: Could not establish connection. Receiving end does not exist.

Проблема сохраняется с долгоживущими соединениями:

В сценарии содержимого или на фоновой странице:

var port = chrome.extension.connect({name: "test"});
port.postMessage({msg: "testing"});

В панели инструментов разработчика javascript:

chrome.extension.onConnect.addListener(function(port) {
    port.onMessage.addListener(function(msg) {
         // never gets here
    });
 });

Как я могу отслеживать события, которые запускаются в моем сценарии содержимого - на панели инструментов разработчика? Такая диаграмма из Firefox Add-On SDK была бы замечательной: https://addons.mozilla.org/en-US/developers/docs/sdk/latest/static-files/media/content-scripting-overview.png


person Salami    schedule 26.07.2012    source источник
comment
В документации приведен пример взаимодействия между фоновым скриптом и скриптом devtools. См. демонстрацию битых ссылок. (background.js и devtools.js )   -  person Rob W    schedule 26.07.2012
comment
@RobW Хотя фоновый скрипт и инструменты разработки взаимодействуют в этом примере, такое общение для меня не работает. В этом примере devtools.js отправляет сообщение и получает обратный вызов. Мне нужен фоновый скрипт для отправки сообщения на страницу devtools при возникновении события, это не будет работать с подходом обратного вызова, если я не опрашиваю постоянно - плохой способ сделать это.   -  person Salami    schedule 26.07.2012
comment
Я хотел показать, что соединение инициализируется скриптом devtools, а не фоновым скриптом. Теперь я превратил эту концепцию в полноценный ответ, включая образец, который показывает, как передавать сообщения в обоих направлениях и изменять содержимое панели после сообщения.   -  person Rob W    schedule 27.07.2012


Ответы (1)


Цель - создать канал (порт) для связи. Неважно, как создается порт, если соединение поддерживается правильно.

Сценарий devtools должен инициировать порт, потому что фоновый сценарий не знает, когда создается панель devtools.

Вот базовый пример, показывающий метод двунаправленной связи:

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);
        };
    });
});

Теперь панель может отправлять / получать сообщения через порт. Скрипт панели (внешний файл скрипта из-за CSP) может выглядеть так:

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!');
};

Теперь скрипт фоновой страницы:

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);
    });
}

Для проверки просто запустите notifyDevtools('Foo'); на фоновой странице (например, через консоль). В этой демонстрации сообщение будет отправлено всем инструментам разработки. При получении панель devtools будет содержать полученное сообщение.

Соберите расширение, используя:

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>

Смотрите также

person Rob W    schedule 26.07.2012
comment
Отличный ответ, отправка сообщений через порты, которые не были отключены, сделала свое дело. - person Salami; 27.07.2012
comment
У меня проблемы с тем, чтобы это работало и со сценариями содержимого. Мне нужен сценарий содержимого, чтобы иметь возможность общаться со сценарием devtools. Есть такой пример? - person Raymond Camden; 11.01.2013
comment
@RaymondCamden Используйте код из этого ответа, чтобы настроить канал с фоновой страницей (из инструментов разработчика). Включите в сообщение идентификатор вкладки (полученный из chrome.devtools.inspectedWindow.tabId). В своем ответе я сохранил порты по довольно произвольно выбранному (недокументированному) идентификатору .portId_. Вместо этого вы можете сохранить порты по tabId и использовать обычные методы обмена сообщениями для связи между сценариями содержимого и инструментами разработки. - person Rob W; 11.01.2013
comment
Спасибо за пример! У меня есть дополнительный вопрос к этому: из документации кажется, что можно сделать XMLHttpRequests для любого веб-сайта, если это позволяет поле разрешений в манифесте, но это не сработало после многих попыток. - person airportyh; 01.03.2013
comment
@airportyh Создайте новый вопрос или чат возможность свести к минимуму не относящиеся к теме комментарии. - person Rob W; 02.03.2013
comment
@RobW - Сработал как шарм. Искал это. Хорошее описание решения. Это хороший пост для chrome devtools, поскольку нет надлежащей документации. - person Kousick Shanmugam Nagaraj; 19.02.2015
comment
Проголосовали против, поскольку это больше не работает ... скопировал / вставил пример дословно, и он больше не работает. Похоже, addListener устарел? Хотелось бы увидеть обновление этого ответа на текущие API. - person mindplay.dk; 08.01.2019
comment
@ mindplay.dk Ответ по-прежнему работает. Протестировано с Chrome 71 и 73.0.3666.0. Для тестирования: 1) Загрузите расширение 2) откройте инструменты разработчика 3) Переключитесь на панель «Тест» 4) щелкните белую панель (например, panel.html, чтобы запустить обработчик onclick в panel.js) 5) вернитесь на вкладку «Консоль» и наблюдайте за ожидаемым сообщение. Обратите внимание, что фоновая страница должна быть загружена ПЕРЕД инструментами разработчика (в противном случае прослушиватель runtime.onConnect не сможет ответить). - person Rob W; 09.01.2019
comment
@RobW, ты прав, это работает! Моя ошибка была связана с файлом panel.html, где я поместил сценарий в head, а не в body, что, похоже, заставляет его загружаться слишком рано. (Рассмотрите возможность добавления тегов body к примерам html, чтобы другие не совершили эту ошибку? Я действительно знал, что тег body подразумевается в HTML5, но его легко забыть, когда он не отображается.) - person mindplay.dk; 09.01.2019