« Previous

This article shows you how to use worker threads in extensions to perform tasks in the background without blocking the user interface.

If you haven't already created an extension, or would like to refresh your memory, take a look at the previous articles in this series:

Download the sample

You may download the complete example:

How this differs from previous versions

This version of the stock ticker extension moves the XMLHttpRequest call that fetches updated stock information into a worker thread, which then passes that information back to the main body of the extension's code to update the display in the status bar. This demonstrates not only how to use workers in an extension, but also how to perform XMLHttpRequest in a worker, and how workers and main thread code can pass data back and forth.

The worker

The worker thread's job in this example is to issue the XMLHttpRequest calls that fetch the updated stock information. That's all it does. Every 10 minutes, it calls XMLHttpRequest, and, when the results are received, sends an event back to the main thread with the received data.

So we need to move the refreshInformation() method from the stockwatcher2.js file into a separate file that will host the worker thread. That file, ticker_worker.js, is shown here:

var symbol = "";

function refreshInformation() {
  if (!symbol) {
    throw "No symbol set!";
  }

  var fullUrl =
    "http://quote.yahoo.com/d/quotes.csv?f=sl1d1t1c1ohgv&e=.csv&s=" + symbol;

  function infoReceived()
  {
    var output = httpRequest.responseText;
    if (output) {
      postMessage(output.trim());
    }
    httpRequest = null;
  }

  var httpRequest = new XMLHttpRequest();
  httpRequest.open("GET", fullUrl, true);
  httpRequest.onload = infoReceived;
  httpRequest.send(null);
}

setInterval(function() {
  refreshInformation();
}, 10*60*1000);

onmessage = function(event) {
  if (event.data) {
    symbol = event.data.toUpperCase();
  }
  refreshInformation();
}

When the worker thread is started, the main body of this code (in lines 26-35) is executed. It starts by setting up an interval handler (in lines 26-28) that calls refreshInformation() every 10 minutes.

Then it sets the worker's onmessage event handler to a function which looks at the event passed into it, and does one of two things:

The refreshInformation() method is fairly similar to the previous versions, with two notable exceptions:

The main thread

The changes here are also relatively minor, but crucial.

The startup() method

This method is called when the extension is first loaded, and needs to be updated to start up and configure the worker. Let's take a look:

  startup: function()
  {
    // Register to receive notifications of preference changes

    this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
        .getService(Components.interfaces.nsIPrefService)
        .getBranch("stockwatcher2.");
    this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
    this.prefs.addObserver("", this, false);

    this.tickerSymbol = this.prefs.getCharPref("symbol").toUpperCase();

    this.worker = new Worker("chrome://stockwatcher2/content/ticker_worker.js");

    // Small little dance to get 'this' to refer to StockWatcher, not the
    // worker, when a message is received.
    var self = this;
    this.worker.onmessage = function(event) {
      self.onworkermessage.call(self, event);
    };

    this.worker.postMessage(this.tickerSymbol);
  },

The worker is set up and configured here in lines 13-22:

The observe() method

This method's job is to update which stock is being monitored when the user changes the preferences. It needs to be updated to post a message to the worker to tell it which stock symbol to track.

  observe: function(subject, topic, data)
  {
    if (topic != "nsPref:changed") {
      return;
    }

    switch(data) {
      case "symbol":
        this.tickerSymbol = this.prefs.getCharPref("symbol").toUpperCase();
        this.worker.postMessage(this.tickerSymbol);
        break;
    }
  },

The key here is line 10, which sends the new ticker symbol to monitor to the ticker thread by calling its postMessage() method. This results in the ticker's onmessage handler being called.

The watchStock() and refreshInformation() methods

These two methods are very simple. watchStock() is updated to pass the symbol to the ticker thread, and refreshInformation(), whose main functionality is now in the worker, is updated to simply pass an empty message to the worker, which tells the worker to refresh the stock information immediately.

  watchStock: function(newSymbol)
  {
    this.tickerSymbol = newSymbol.toUpperCase();
    this.prefs.setCharPref("symbol", newSymbol);
    this.worker.postMessage(this.tickerSymbol);
  },

  refreshInformation: function()
  {
    // Empty message just means 'refresh'.
    this.worker.postMessage("");
  },

The onworkermessage() method

This method is called when the worker posts a message back to the main thread. Its job is to update the ticker information that's currently displayed in the status bar, as well as to update the tooltip that appears while the mouse cursor is hovering over the ticker. This code is essentially identical to what's done in the previous version, except that it's done in response to an event instead of within the refreshInformation() method.

A note about ChromeWorkers

Requires Gecko 2.0(Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)

Gecko 2.0 added the new ChromeWorker object, which provides a special chrome-only worker that can be used by applications and extensions. This worker object has access to js-ctypes, which standard workers do not have.

See also