comlink icon indicating copy to clipboard operation
comlink copied to clipboard

Make comlink available for chrome extensions

Open kesavkolla opened this issue 4 years ago • 6 comments

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

kesavkolla avatar Mar 17 '20 09:03 kesavkolla

cc @DotProto: I know your plate is full, but if you ever get bored, maybe that’s something we can tackle together :D

surma avatar Mar 17 '20 09:03 surma

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 avatar Apr 23 '20 07:04 felixfbecker

@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.

surma avatar Apr 23 '20 09:04 surma

Sorry, I had forgotten about that response, it's been a while 😅

felixfbecker avatar Apr 23 '20 09:04 felixfbecker

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 in postMessage() and generate a UUID for them, then call browser.runtime.connect() with those names. In the postMessage() data, replace the MessagePorts 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 with MessagePorts and hooks them up to the right Port (by UUID) received in onConnect. This needs to account for the order of the message and the onConnect being indeterministic.

felixfbecker avatar Apr 30 '20 09:04 felixfbecker

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();

samdenty avatar Jul 06 '20 23:07 samdenty