Фон. js к содержанию. js с использованием порта и долгоживущих сообщений - PullRequest
0 голосов
/ 03 мая 2020

Я завершил отправку одного сообщения из фона. js в контент. js, используя chrome.runtime.onMessage.addListener, но мне нужно отправлять много сообщений в течение жизненного цикла расширений, поэтому мне нужно открыть порт и использовать долгоживущий обмен сообщениями .

Однако я не могу найти никаких рабочих примеров открытия порта и отправки сообщений / объектов между фоновым файлом. js и файлом содержимого. js. У кого-нибудь есть достойный пример, который работает?

Я также столкнулся со следующими проблемами при попытке попробовать пример в официальных документах. "Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist."

Любая помощь с современным работающим примером "долгоживущих сообщений" между фоном. js и содержимое. js Файл будет очень полезен: D

В соответствии с запросом здесь используется код, который я сейчас использую, который работает для отправки одного сообщения. Я просто хотел бы изменить это так, чтобы я мог отправлять много сообщений, а не только одно.

// Content.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {           
        if (request.message == 'dataready') {
            //Do something
            sendResponse({});
            return true;   
        }        
    }
);



// Background.js
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {          
    if (changeInfo.status == 'complete') {   
        chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
            chrome.tabs.sendMessage(tabs[0].id, {
                message: 'dataready',
                action: dataForiFrame
            }, 
            function(response) {

            });  
        });
    }
});

1 Ответ

1 голос
/ 04 мая 2020

Если вы не отправляете сотни сообщений в секунду, вероятно, нет необходимости использовать обмен сообщениями на основе портов, но давайте посмотрим, как это может выглядеть.

Ваша настройка относительно сложна, поэтому вам потребуется поддерживать 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, поэтому предполагается, что обновленная вкладка всегда обновляется, даже если это не так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...