comlink
comlink copied to clipboard
Make comlink available for chrome extensions
chrome extensions have background javascript and popup javascript they both communicate with each other via postMessage. comlink can provide a great help there to eliminate all message passing nuances. Please add the support of comlink to chrome extensions
cc @DotProto: I know your plate is full, but if you ever get bored, maybe that’s something we can tackle together :D
The problem is that comlink proxies objects by transferring MessagePorts, but browser.runtime.Port objects don't support transferring objects with postMessage().
In v3, we used the string message channel adapter to work around this. I see that this was removed in v4, but there is no changelog entry on why. Would it work to copy-paste the old string message channel adapter code? Or is there something preventing that?
@felixfbecker, you asked the same question in #433 and I replied: I should have never put the adapter on master in v4. It was hacky and leaky. I link to an experimental implementation for v4 in that issue as well.
Sorry, I had forgotten about that response, it's been a while 😅
I'm thinking about how a version of this could look like that does not depend on string message channel.
Browser extension Ports don't allow transferring ports, but it is possible to create multiple channels. This is done by calling browser.runtime.connect(), which returns a new Port. The other side can receive the counterpart Port by listening to browser.runtime.onConnect. browser.runtime.connect() can be passed a unique name for the connection, which will be passed to the onConnect handler.
So a rough sketch of how this could work for browser extensions:
- an adapter could take all
MessagePorts being transferred inpostMessage()and generate a UUID for them, then callbrowser.runtime.connect()with those names. In thepostMessage()data, replace theMessagePorts with references to those IDs instead. - On incoming messages, the adapter looks at the message to see if there are any port IDs referenced. If so, it replaces the
PortID references withMessagePorts and hooks them up to the rightPort(by UUID) received inonConnect. This needs to account for the order of the message and theonConnectbeing indeterministic.
Created https://github.com/samdenty/comlink-extension which does just that:
import { createBackgroundEndpoint, isMessagePort } from "comlink-extension";
import * as Comlink from "comlink";
chrome.runtime.onConnect.addListener((port) => {
if (isMessagePort(port)) return;
Comlink.expose(
{
test() {
console.log("called");
},
},
createBackgroundEndpoint(port)
);
});
import { createEndpoint, forward } from "comlink-extension";
import * as Comlink from "comlink";
// Wrap a chrome.runtime.Port
const obj = Comlink.wrap(createEndpoint(chrome.runtime.connect()));
obj.test();
// Or, wrap an existing Message Channel:
const { port1, port2 } = new MessageChannel();
forward(port1, chrome.runtime.connect());
const obj = Comlink.wrap(port2);
obj.test();