I've accomplished sending a single message from background.js to content.js using chrome.runtime.onMessage.addListener
but I need to send a lot of messages throughout the extensions lifecycle, therefore I need to open a port and use long-lived messaging .
However, I can't find any working examples of opening a port and sending messages/objects between a background.js file and a content.js file. Does anyone have a decent example that works?
I've also run into issues with the following when trying the example in the official docs "Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist."
Any help with a modern working 'long-lived messaging' example between a background.js and content.js file would be much appreciated:D
As requested here is the code I'm currently using that works for single message sending. I'd just like to change this so that I can send lots of messages, not just one.
// 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) {
});
});
}
});
Unless you send hundreds of messages per second there's probably no need to use port-based messaging but let's see how it might look.
Your setup is relatively complicated so you would need to maintain a Map
of tabId to port
relations.
The content script would initiate chrome.runtime.connect, the background script would listen in onConnect and update the mapping. Then tabs.onUpdated listener will use this map to send the message to the right port.
content script:
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'});
});
background script:
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});
});
}
This is a very barebone example without any error-checking. Also note, it's not the only or the best solution. Depending on other factors that aren't present in the question there could be other solutions.
For example, it's possible to reverse the handshake and call chrome.tabs.connect in the background script to connect to onConnect inside the content script.
content script:
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'});
});
});
background script:
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 The problem with examples is that people tend to copy them without understanding the mechanics. For instance, your code seems to needlessly use chrome.tabs.query inside onUpdated listener even though it already has tabId
parameter, thus assuming it's always the active tab that got updated even when it's not so.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.