44

I have a chrome extension that includes a complicated function comp_func(data) which takes a lot of CPU by performing many bitwise operations. Because of that, I'm trying to use WebAssembly.

I've tried to follow several tutorials, for example this one and this one.

The first link says:

fetch('simple.wasm').then(response =>
  response.arrayBuffer()
).then(bytes =>
  WebAssembly.instantiate(bytes, importObject)
).then(results => {
  results.instance.exports.exported_func();
});

but I get an error:

Uncaught (in promise) TypeError: WebAssembly Instantiation: Import #0 module="env" error: module is not an object or function

I've tried a lot to use this approach, but it didn't work. I can't understand how to use WebAssembly that is loaded from the .wasm file.

So I've tried an easier approach: The second link says to put this line in the html file:

<script src="index.js"></script>

and then just use the exported function:

var result = _roll_dice();

BUT, I'm in an extension so I only have a background.html file. So I'm looking for a way to access the Module which was loaded in the background file. And things get complicated, because the function comp_func(data) is called from a Worker.

This is what I've tried so far:

If I call chrome.extension.getBackgroundPage() I can access the Module but I can't send it to the Worker:

Failed to execute 'postMessage' on 'Worker': # could not be cloned.

And if I try to stringify it first:

Uncaught TypeError: Converting circular structure to JSON

(I tried to un-circular it, didn't work...)

And I can't call chrome.extension.getBackgroundPage() from the Worker because I can't access chrome API from there.

So my questions are:

  1. Did someone try to load .wasm file in chrome extension and succeed? The second approach (loading the js file) sounds simpler but if you have a working example for this approach it would be great.

or 2. How to access the Module that has been loaded in background.html (from the second example)?

or 3. How to pass the functions that I needed from the js file to the Worker (via postMessage)?

To summarize, did someone try to use WebAssembly in a chrome extension and survive to tell?

EDIT: I eventually left the approach of WebAssembly. I also posted this question at bugs-chromium, and after few month got an answer. Not sure if this is really working, but maybe this, along with the marked answer, will help someone.

Mike Jack
  • 130
  • 9
nogmos
  • 859
  • 1
  • 8
  • 12
  • I think it's possible extensions can't run wasm yet. As for the messaging part: 1) only simple data can be transferred using chrome API i.e. numbers, strings, booleans, arrays and objects that consist of such simple data, 2) Workers can also transfer [a few specific types](https://developer.mozilla.org/en-US/docs/Web/API/Transferable), 3) functions aren't transferable. – wOxxOm Apr 02 '18 at 12:55
  • Do you have any idea how I can check for sure if extensions can't run wasm? – nogmos Apr 02 '18 at 13:11
  • 1
    You can ask on https://crbug.com and/or [Chromium-Extensions-Announce](https://groups.google.com/a/chromium.org/d/forum/chromium-extensions). – wOxxOm Apr 02 '18 at 13:18

1 Answers1

39

I've been fiddling with WebAssembly recently, and found a way to make it work. Here are the script files:

main.js

chrome.browserAction.onClicked.addListener(function(tab) {
 chrome.tabs.executeScript(null, {file: "content_script.js"});
});

content_script.js

  var importObject = { imports: { imported_func: arg => console.log(arg) } };
  url = 'data:application/wasm;base64,' + "AGFzbQEAAAABCAJgAX8AYAAAAhkBB2ltcG9ydHMNaW1wb3J0ZWRfZnVuYwAAAwIBAQcRAQ1leHBvcnRlZF9mdW5jAAEKCAEGAEEqEAAL";
  WebAssembly.instantiateStreaming(fetch(url), importObject)
  .then(obj => obj.instance.exports.exported_func());

The data URL belongs to the common tutorial wasm sample (simple.wasm), which writes 42 on the console.


PS. If it seems like cheating or bad practice to you, this content_script.js also works:
var importObject = {
   imports: {
    imported_func: function(arg) {
    console.log(arg);
    }
   }
 };

var response = null;
var bytes = null;
var results = null;


var wasmPath = chrome.runtime.getURL("simple.wasm");
fetch(wasmPath).then(response =>
    response.arrayBuffer()
    ).then(bytes =>
       WebAssembly.instantiate(bytes, importObject)
        ).then(results => {
        results.instance.exports.exported_func();
  });

Only if you include the code files in the web_accessible_resources section in manifest.json, though:

    ...
    "web_accessible_resources": [
     "content_script.js",
     "main.js",
     "simple.wasm"
    ],
    ...

Github: https://github.com/inflatablegrade/Extension-with-WASM

It can also be made compatible with Manifest V3.

Nautilus
  • 506
  • 4
  • 4
  • 3
    When you use WASM, does the Chrome app store require the source code for code review before publishing? – zino Dec 03 '21 at 17:20
  • To be honest, I never tried it. I just wanted to see if it's doable as a concept. My extension doesn't serve any practical purpose, so I didn't attempt to publish it. WASM has a lot of potential for computing-intensive web apps and extensions, but I don't have a particular extension idea for that. It's compatible with Manifest V3 though. – Nautilus Dec 04 '21 at 13:51
  • 3
    Actually, uBlock Origin has WASM in it: https://github.com/gorhill/uBlock/tree/master/src/js/wasm – Nautilus Dec 04 '21 at 18:41
  • Nice! Could you pls share what C code and compile command you used to create simple.wasm? – mwag Mar 08 '23 at 22:20