Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Using Web Workers in Firefox Extensions

Web Workers allow you to run code in the background in browsers such as Firefox. This is how to build one into a Firefox Extension, which is slightly different than from just creating one as normal on a page. The documentation for doing this is basically non-existent, so hopefully you’ll find this useful.

Please make sure you have a development environment set up similar to the one described here in my previous post.

How do workers work?

  • Workers in /data/ are not directly connected to scripts in /lib/
  • However, they can communicate by sending messages to each other
  • These messages are text only, so could contain serialized JSON, but nothing else
  • You’ll notice below that we are basically just slinging messages between two scripts

The code for the worker

Navigate to the /data/ directory and create a file called hello_world.js

> pwd
/Users/mruttley/Documents/test
> ls
data		lib		package.json	test
> cd data/
> vim hello_world.js

Now paste the following in there (new users of vim, press

i
  to start typing and
Esc
  followed by
:wq
  to save):
//Code for the worker

self.onmessage = function(messageFromClient) {
    self.postMessage("Hello " + messageFromClient.data);
};

This says that whenever the worker receives a Message from the client, then send a message back with the word “Hello” prepended.

One note here: In workers, you can’t use the useful function 

console.log("message")
 , instead use 
dump("message")

Let’s call the worker from the main code

Let’s navigate back to the

/lib/
  folder and edit the
main.js
  file, which is the first thing that runs in the extension.
> cd ../lib/
> vim main.js

Paste in the following code:

var worker = new Worker("hello_world.js");

worker.onmessage = function(e) {
    console.log(e.data);
};

worker.postMessage("Matthew");

And run 

cfx run
 . You’ll notice a messy error:
> cfx run
Using binary at '/Applications/Firefox.app/Contents/MacOS/firefox-bin'.
Using profile at '/var/folders/p1/zzdzcrrx6pq96hgsmy5xjqmh0000gp/T/tmpgFixDP.mozrunner'.
console.error: test: 
  Message: ReferenceError: Worker is not defined
  Stack:
    @resource://jid1-zmowxggdley0aa-at-jetpack/test/lib/main.js:1:9
CuddlefishLoader/options<.load@resource://gre/modules/commonjs/sdk/loader/cuddlefish.js:129:18
run@resource://gre/modules/commonjs/sdk/addon/runner.js:138:19
startup/</<@resource://gre/modules/commonjs/sdk/addon/runner.js:81:7
Handler.prototype.process@resource://gre/modules/Promise-backend.js:865:23
this.PromiseWalker.walkerLoop@resource://gre/modules/Promise-backend.js:744:7

*************************
A coding exception was thrown in a Promise resolution callback.
See https://developer.mozilla.org/Mozilla/JavaScript_code_modules/Promise.jsm/Promise

Full message: ReferenceError: Worker is not defined
Full stack: @resource://jid1-zmowxggdley0aa-at-jetpack/test/lib/main.js:1:9
CuddlefishLoader/options<.load@resource://gre/modules/commonjs/sdk/loader/cuddlefish.js:129:18
run@resource://gre/modules/commonjs/sdk/addon/runner.js:138:19
startup/</<@resource://gre/modules/commonjs/sdk/addon/runner.js:81:7
Handler.prototype.process@resource://gre/modules/Promise-backend.js:865:23
this.PromiseWalker.walkerLoop@resource://gre/modules/Promise-backend.js:744:7

*************************
console.error: test: 
  Message: ReferenceError: Worker is not defined
  Stack:
    @resource://jid1-zmowxggdley0aa-at-jetpack/test/lib/main.js:1:9
CuddlefishLoader/options<.load@resource://gre/modules/commonjs/sdk/loader/cuddlefish.js:129:18
run@resource://gre/modules/commonjs/sdk/addon/runner.js:138:19
startup/</<@resource://gre/modules/commonjs/sdk/addon/runner.js:81:7
Handler.prototype.process@resource://gre/modules/Promise-backend.js:865:23
this.PromiseWalker.walkerLoop@resource://gre/modules/Promise-backend.js:744:7

Aha! The key line here is: 

ReferenceError: Worker is not defined
 . This is because Firefox Extensions use something called a ChromeWorker instead. We need to import this in main.js by pasting this at the top:

var {ChromeWorker} = require("chrome")

and changing the line that references the hello_world.js file to call a ChromeWorker instead:

//var worker = new Worker("hello_world.js"); //remove this
var worker = new ChromeWorker("hello_world.js"); //add this instead

Ok let’s try running it again! Try 

cfx run
 . Wtf another error?!
> cfx run
Using binary at '/Applications/Firefox.app/Contents/MacOS/firefox-bin'.
Using profile at '/var/folders/p1/zzdzcrrx6pq96hgsmy5xjqmh0000gp/T/tmpJJXeC4.mozrunner'.
console.error: test: 
  Message: Error: Malformed script URI: hello_world.js
  Stack:
    @resource://jid1-zmowxggdley0aa-at-jetpack/test/lib/main.js:3:14
CuddlefishLoader/options<.load@resource://gre/modules/commonjs/sdk/loader/cuddlefish.js:129:18
run@resource://gre/modules/commonjs/sdk/addon/runner.js:138:19
startup/</<@resource://gre/modules/commonjs/sdk/addon/runner.js:81:7
Handler.prototype.process@resource://gre/modules/Promise-backend.js:865:23
this.PromiseWalker.walkerLoop@resource://gre/modules/Promise-backend.js:744:7

console.error: test: 
  Message: Error: Malformed script URI: hello_world.js
  Stack:
    @resource://jid1-zmowxggdley0aa-at-jetpack/test/lib/main.js:3:14
CuddlefishLoader/options<.load@resource://gre/modules/commonjs/sdk/loader/cuddlefish.js:129:18
run@resource://gre/modules/commonjs/sdk/addon/runner.js:138:19
startup/</<@resource://gre/modules/commonjs/sdk/addon/runner.js:81:7
Handler.prototype.process@resource://gre/modules/Promise-backend.js:865:23
this.PromiseWalker.walkerLoop@resource://gre/modules/Promise-backend.js:744:7

The key line here is: 

Malformed script URI: hello_world.js
 . This cryptic error is because firefox can’t yet access anything in the

/data/
  folder. We have to use another part of the SDK to enable access to it.

Open

main.js
  and put this at the top:
var self = require("sdk/self");

Now we can use the function

self.data.url()
 . When you put a filename as the first argument, it will return a string like 
resource://jid1-zmowxggdley0aa-at-jetpack/test/data/whatever_file.js
 which properly refers to it in the case of extensions. Modify the worker import line as follows:
//let worker = new Worker("hello_world.js"); //remove this
let worker = new ChromeWorker(self.data.url("hello_world.js")); //add this

Now let’s run the extension again using

cfx run
 :
> cfx run
Using binary at '/Applications/Firefox.app/Contents/MacOS/firefox-bin'.
Using profile at '/var/folders/p1/zzdzcrrx6pq96hgsmy5xjqmh0000gp/T/tmppvMjZp.mozrunner'.
console.log: test: Hello Matthew

Yay it works! The Worker returned the message “Hello Matthew“.

FAQ

  • What does this
    {notation}
      mean?

It is shorthand for:

var chrome = require("chrome")
var Worker = chrome['ChromeWorker']

Basically this means that 

require("chrome")
 returns an Object, and we just need the value that is referenced by the key “ChromeWorker”. This is a very succinct way of extracting things from JavaScript Objects that will come in handy in the future.
  • Why is Worker now called ChromeWorker? Are we doing something with Google Chrome?

This is a naming coincidence and nothing to do with Chrome as in the browser. Chrome in this case refers to Firefox Addon internals.



This post first appeared on Ikigomu | A Data Science, NLP And Personal Blog By, please read the originial post: here

Share the post

Using Web Workers in Firefox Extensions

×

Subscribe to Ikigomu | A Data Science, Nlp And Personal Blog By

Get updates delivered right to your inbox!

Thank you for your subscription

×