Если вы не отправляете сотни сообщений в секунду, вероятно, нет необходимости использовать обмен сообщениями на основе портов, но давайте посмотрим, как это может выглядеть.
Ваша настройка относительно сложна, поэтому вам потребуется поддерживать Map
от tabId до port
отношений.
Рукопожатие инициировано в скрипте контента
Скрипт контента инициирует chrome .runtime.connect, фоновый скрипт будет прослушивать onConnect и обновлять сопоставление. Затем tabs.onUpdated listener будет использовать эту карту для отправки сообщения на нужный порт.
сценарий содержимого:
const port = chrome.runtime.connect({name: 'content'});
port.onMessage.addListener(msg => {
console.log(msg.data);
// send a response if needed, may be a simple object/array
port.postMessage({id: msg.id, data: 'gotcha'});
});
фоновый сценарий:
const portMap = new Map();
const resolveMap = new Map();
let messageId = 0;
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete') {
const response = await send(tabId, {message: 'dataready', action: 'foo'});
console.log(response);
}
});
chrome.runtime.onConnect.addListener(port => {
portMap.set(port.sender.tab.id, port);
port.onDisconnect.addListener(onPortDisconnected);
port.onMessage.addListener(onPortMessage);
});
function onPortDisconnected(port) {
portMap.delete(port.sender.tab.id);
}
function onPortMessage(msg, port) {
resolveMap.get(msg.id)(msg.data);
resolveMap.delete(msg.id);
}
function send(tabId, data) {
return new Promise(resolve => {
const id = ++messageId;
resolveMap.set(id, resolve);
portMap.get(tabId).postMessage({id, data});
});
}
Это очень простой пример без проверки ошибок. Также обратите внимание, что это не единственное или лучшее решение. В зависимости от других факторов, которых нет в вопросе, могут быть и другие решения.
Рукопожатие, инициированное в фоновом скрипте
Например, можно отменить рукопожатие и вызвать chrome .tabs.connect в фоновом скрипте для подключения к onConnect внутри скрипта контента.
скрипт содержания:
chrome.runtime.onConnect.addListener(port => {
port.onMessage.addListener(msg => {
console.log(msg.data);
// send a response if needed, may be a simple object/array
port.postMessage({id: msg.id, data: 'gotcha'});
});
});
фоновый скрипт:
const portMap = new Map();
const resolveMap = new Map();
let messageId = 0;
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete') {
const response = await send(tabId, {message: 'dataready', action: 'foo'});
console.log(response);
}
});
function onPortDisconnected(port) {
portMap.delete(port.sender.tab.id);
}
function onPortMessage(msg, port) {
resolveMap.get(msg.id)(msg.data);
resolveMap.delete(msg.id);
}
function send(tabId, data) {
return new Promise(resolve => {
const id = ++messageId;
let port = portMap.get(tabId);
if (!port) {
port = chrome.tabs.connect(tabId, {frameId: 0});
port.onDisconnect.addListener(onPortDisconnected);
port.onMessage.addListener(onPortMessage);
portMap.set(tabId, port);
}
resolveMap.set(id, resolve);
port.postMessage({id, data});
});
}
PS Проблема с примерами состоит в том, что люди, как правило, копируют их, не понимая механики. Например, ваш код, похоже, без необходимости использует chrome .tabs.query внутри слушателя onUpdated, даже если у него уже есть параметр tabId
, поэтому предполагается, что обновленная вкладка всегда обновляется, даже если это не так.