GoogleChromeLabs / comlink

Comlink makes WebWorkers enjoyable.
Apache License 2.0
11.23k stars 386 forks source link

Make comlink available for chrome extensions #438

Open kesavkolla opened 4 years ago

kesavkolla commented 4 years ago

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

surma commented 4 years ago

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

felixfbecker commented 4 years ago

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?

surma commented 4 years ago

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

felixfbecker commented 4 years ago

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

felixfbecker commented 4 years ago

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:

samdenty commented 4 years ago

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