Reputation: 1982
I have the following code
browser.runtime.onConnect.addListener(function (externalPort) {
externalPort.onMessage.addListener((message, sender, sendResponse) => {
sendResponse(42);
}
});
However, it seems that listeners for Port.onMessage
do not get called with a sendResponse
as listeners for browser.runtime.onMessage
.
Any idea how to send responses for messages to ports?
Upvotes: 1
Views: 1164
Reputation: 73806
Port-based messaging doesn't use sendResponse. Simply post another message to the port.
Here's a very simplified example of a port-based messaging system. It doesn't transfer errors or exceptions, doesn't have a timeout. The idea is to pass an id
, save the callback for the id in a map, and use the same id in the response to call that saved callback.
Unlike browser.runtime.sendMessage that creates a new port each time (a relatively expensive operation in case you send a lot of messages), we reuse the same port.
sender:
const port = browser.runtime.connect({name: 'foo'});
const portMap = new Map();
let portMessageId = 0;
port.onMessage.addListener(msg => {
const {id, data} = msg;
const resolve = portMap.get(id);
portMap.delete(id);
resolve(data);
});
function send(data) {
return new Promise(resolve => {
const id = ++portMessageId;
portMap.set(id, resolve);
port.postMessage({id, data});
});
}
usage:
(async () => {
const response = await send({foo: 'whatever'});
console.log(response);
})();
receiver:
/** @param {chrome.runtime.Port} port */
browser.runtime.onConnect.addListener(port => {
if (port.name === 'foo') {
port.onMessage.addListener(msg => {
const {id, data} = msg;
port.postMessage({id, data: processMessage(data)});
});
}
});
Upvotes: 1
Reputation: 309
The Port.postMessage()
is a push-only messaging method, so you need to use regular runtime.sendMessage()
method in parallel. Here is an example:
manifest.json:
{
"name": "panel example",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_title": "panel",
"default_popup": "panel.html"
},
"permissions": [
"tabs"
]
}
background.js:
browser.runtime.onConnect.addListener(port => {
let tabId;
const listenerForPort = (message, sender) => {
if (message &&
typeof message == 'object' &&
message.portName == port.name) {
switch (message.type) {
case 'get-tabId':
return Promise.resolve(tabId);
}
}
};
browser.runtime.onMessage.addListener(listenerForPort);
port.onMessage.addListener(message => {
if (message &&
typeof message == 'object' &&
message.tabId)
tabId = message.tabId;
});
port.onDisconnect.addListener(port => {
browser.runtime.onMessage.removeListener(listenerForPort);
if (tabId)
browser.tabs.remove(tabId);
});
});
panel.html:
<!DOCTYPE html>
<script type="application/javascript" src="panel.js"></script>
<button id="button">Click Me</button>
panel.js:
browser.windows.getCurrent({ populate: true }).then(win => {
const portName = `port for window ${win.id}`;
const activeTab = win.tabs.find(tab => tab.active);
const port = browser.runtime.connect({
name: portName
});
port.postMessage({ tabId: activeTab.id });
const button = document.getElementById('button');
button.addEventListener('click', async event => {
const tabIdFromBackground = await browser.runtime.sendMessage({
type: 'get-tabId',
portName
});
button.textContent = tabIdFromBackground;
});
});
In this example, there is a listener corresponding to a connection and it is designed to respond only to messages sent with the corresponding port name.
Upvotes: 1