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 MessagePort
s, 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 Port
s 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
MessagePort
s being transferred inpostMessage()
and generate a UUID for them, then callbrowser.runtime.connect()
with those names. In thepostMessage()
data, replace theMessagePort
s 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
Port
ID references withMessagePort
s and hooks them up to the rightPort
(by UUID) received inonConnect
. This needs to account for the order of the message and theonConnect
being 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();