Merge mozilla-central to mozilla-inbound
authorEd Morley <bmo@edmorley.co.uk>
Tue, 30 Aug 2011 14:28:34 +0100
changeset 76216 fdfc74d7e8268802102a28977b00204b89421140
parent 76215 aa98455bd116dc6f7e7a0b455b5c9fa46e2c8815 (current diff)
parent 76200 31b79d4e90f45f9e8922098b109748029663db8b (diff)
child 76217 ed85e33792e086643c1ff66b65e7909525efe34e
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
milestone9.0a1
Merge mozilla-central to mozilla-inbound
js/src/configure.in
js/src/jsanalyze.cpp
js/src/jsarena.h
js/src/jscntxt.cpp
js/src/jsparse.cpp
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7326,17 +7326,18 @@ var FeedHandler = {
     return this._feedMenupopup = document.getElementById("multipleFeedsMenuState");
   },
 
   /**
    * Update the browser UI to show whether or not feeds are available when
    * a page is loaded or the user switches tabs to a page that has feeds.
    */
   updateFeeds: function() {
-    clearTimeout(this._updateFeedTimeout);
+    if (this._updateFeedTimeout)
+      clearTimeout(this._updateFeedTimeout);
 
     var feeds = gBrowser.selectedBrowser.feeds;
     var haveFeeds = feeds && feeds.length > 0;
 
     var feedButton = document.getElementById("feed-button");
     if (feedButton)
       feedButton.disabled = !haveFeeds;
 
--- a/browser/base/content/inspector.js
+++ b/browser/base/content/inspector.js
@@ -535,16 +535,17 @@ Highlighter.prototype = {
  */
 var InspectorUI = {
   browser: null,
   tools: {},
   showTextNodesWithWhitespace: false,
   inspecting: false,
   treeLoaded: false,
   prefEnabledName: "devtools.inspector.enabled",
+  isDirty: false,
 
   /**
    * Toggle the inspector interface elements on or off.
    *
    * @param aEvent
    *        The event that requested the UI change. Toolbar button or menu.
    */
   toggleInspectorUI: function IUI_toggleInspectorUI(aEvent)
@@ -580,17 +581,17 @@ var InspectorUI = {
   },
 
   /**
    * Return the default selection element for the inspected document.
    */
   get defaultSelection()
   {
     let doc = this.win.document;
-    return doc.documentElement.lastElementChild;
+    return doc.documentElement ? doc.documentElement.lastElementChild : null;
   },
 
   initializeTreePanel: function IUI_initializeTreePanel()
   {
     this.treeBrowserDocument = this.treeIFrame.contentDocument;
     this.treePanelDiv = this.treeBrowserDocument.createElement("div");
     this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
     this.treePanelDiv.ownerPanel = this;
@@ -774,16 +775,18 @@ var InspectorUI = {
       Cu.import("resource:///modules/domplate.jsm", this);
       this.domplateUtils.setDOM(window);
     }
 
     this.openTreePanel();
 
     this.toolbar.hidden = false;
     this.inspectCmd.setAttribute("checked", true);
+
+    gBrowser.addProgressListener(InspectorProgressListener);
   },
 
   /**
    * Initialize highlighter.
    */
   initializeHighlighter: function IUI_initializeHighlighter()
   {
     this.highlighter = new Highlighter(this.browser);
@@ -833,16 +836,18 @@ var InspectorUI = {
 
     if (this.closing || !this.win || !this.browser) {
       return;
     }
 
     this.closing = true;
     this.toolbar.hidden = true;
 
+    gBrowser.removeProgressListener(InspectorProgressListener);
+
     if (!aKeepStore) {
       InspectorStore.deleteStore(this.winID);
       this.win.removeEventListener("pagehide", this, true);
     } else {
       // Update the store before closing.
       if (this.selection) {
         InspectorStore.setValue(this.winID, "selectedNode",
           this.selection);
@@ -1310,16 +1315,18 @@ var InspectorUI = {
 
     // set the new attribute value on the original target DOM element
     this.editingContext.repObj.setAttribute(this.editingContext.attrName, 
                                               editorInput.value);
 
     // update the HTML tree attribute value
     this.editingContext.attrObj.innerHTML = editorInput.value;
 
+    this.isDirty = true;
+
     // event notification    
     Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, 
                                   null);
     
     this.closeEditor();
   },
 
   /**
@@ -1736,14 +1743,127 @@ var InspectorStore = {
       delete this.store[aID][aKey];
       result = true;
     }
 
     return result;
   }
 };
 
+/**
+ * The InspectorProgressListener object is an nsIWebProgressListener which
+ * handles onStateChange events for the inspected browser. If the user makes
+ * changes to the web page and he tries to navigate away, he is prompted to
+ * confirm page navigation, such that he's given the chance to prevent the loss
+ * of edits.
+ */
+var InspectorProgressListener = {
+  onStateChange:
+  function IPL_onStateChange(aProgress, aRequest, aFlag, aStatus)
+  {
+    // Remove myself if the Inspector is no longer open.
+    if (!InspectorUI.isTreePanelOpen) {
+      gBrowser.removeProgressListener(InspectorProgressListener);
+      return;
+    }
+
+    // Skip non-start states.
+    if (!(aFlag & Ci.nsIWebProgressListener.STATE_START)) {
+      return;
+    }
+
+    // If the request is about to happen in a new window, we are not concerned
+    // about the request.
+    if (aProgress.DOMWindow != InspectorUI.win) {
+      return;
+    }
+
+    if (InspectorUI.isDirty) {
+      this.showNotification(aRequest);
+    } else {
+      InspectorUI.closeInspectorUI();
+    }
+  },
+
+  /**
+   * Show an asynchronous notification which asks the user to confirm or cancel
+   * the page navigation request.
+   *
+   * @param nsIRequest aRequest
+   *        The request initiated by the user or by the page itself.
+   * @returns void
+   */
+  showNotification: function IPL_showNotification(aRequest)
+  {
+    aRequest.suspend();
+
+    let notificationBox = gBrowser.getNotificationBox(InspectorUI.browser);
+    let notification = notificationBox.
+      getNotificationWithValue("inspector-page-navigation");
+
+    if (notification) {
+      notificationBox.removeNotification(notification, true);
+    }
+
+    let cancelRequest = function onCancelRequest() {
+      if (aRequest) {
+        aRequest.cancel(Cr.NS_BINDING_ABORTED);
+        aRequest.resume(); // needed to allow the connection to be cancelled.
+        aRequest = null;
+      }
+    };
+
+    let eventCallback = function onNotificationCallback(aEvent) {
+      if (aEvent == "removed") {
+        cancelRequest();
+      }
+    };
+
+    let buttons = [
+      {
+        id: "inspector.confirmNavigationAway.buttonLeave",
+        label: InspectorUI.strings.
+          GetStringFromName("confirmNavigationAway.buttonLeave"),
+        accessKey: InspectorUI.strings.
+          GetStringFromName("confirmNavigationAway.buttonLeaveAccesskey"),
+        callback: function onButtonLeave() {
+          if (aRequest) {
+            aRequest.resume();
+            aRequest = null;
+            InspectorUI.closeInspectorUI();
+          }
+        },
+      },
+      {
+        id: "inspector.confirmNavigationAway.buttonStay",
+        label: InspectorUI.strings.
+          GetStringFromName("confirmNavigationAway.buttonStay"),
+        accessKey: InspectorUI.strings.
+          GetStringFromName("confirmNavigationAway.buttonStayAccesskey"),
+        callback: cancelRequest
+      },
+    ];
+
+    let message = InspectorUI.strings.
+      GetStringFromName("confirmNavigationAway.message");
+
+    notification = notificationBox.appendNotification(message,
+      "inspector-page-navigation", "chrome://browser/skin/Info.png",
+      notificationBox.PRIORITY_WARNING_HIGH, buttons, eventCallback);
+
+    // Make sure this not a transient notification, to avoid the automatic
+    // transient notification removal.
+    notification.persistence = -1;
+  },
+};
+
 /////////////////////////////////////////////////////////////////////////
-//// Initializors
+//// Initializers
 
 XPCOMUtils.defineLazyGetter(InspectorUI, "inspectCmd", function () {
   return document.getElementById("Tools:Inspect");
 });
+
+XPCOMUtils.defineLazyGetter(InspectorUI, "strings", function () {
+  return Services.strings.
+         createBundle("chrome://browser/locale/inspector.properties");
+});
+
--- a/browser/base/content/test/inspector/Makefile.in
+++ b/browser/base/content/test/inspector/Makefile.in
@@ -52,12 +52,13 @@ include $(topsrcdir)/config/rules.mk
 		browser_inspector_store.js \
 		browser_inspector_tab_switch.js \
 		browser_inspector_treePanel_output.js \
 		browser_inspector_treePanel_input.html \
 		browser_inspector_treePanel_result.html \
 		browser_inspector_registertools.js \
 		browser_inspector_bug_665880.js \
 		browser_inspector_editor.js \
+		browser_inspector_bug_566084_location_changed.js \
 		$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/inspector/browser_inspector_bug_566084_location_changed.js
@@ -0,0 +1,121 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let notificationBox = null;
+
+function startLocationTests() {
+  ok(window.InspectorUI, "InspectorUI variable exists");
+  Services.obs.addObserver(runInspectorTests, INSPECTOR_NOTIFICATIONS.OPENED, null);
+  InspectorUI.toggleInspectorUI();
+}
+
+function runInspectorTests() {
+  Services.obs.removeObserver(runInspectorTests, INSPECTOR_NOTIFICATIONS.OPENED, null);
+
+  let para = content.document.querySelector("p");
+  ok(para, "found the paragraph element");
+  is(para.textContent, "init", "paragraph content is correct");
+
+  ok(InspectorUI.inspecting, "Inspector is highlighting");
+  ok(InspectorUI.isTreePanelOpen, "Inspector Panel is open");
+
+  InspectorUI.isDirty = true;
+
+  notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
+  notificationBox.addEventListener("AlertActive", alertActive1, false);
+
+  gBrowser.selectedBrowser.addEventListener("load", onPageLoad, true);
+
+  content.location = "data:text/html,<div>location change test 1 for " +
+    "inspector</div><p>test1</p>";
+}
+
+function alertActive1() {
+  notificationBox.removeEventListener("AlertActive", alertActive1, false);
+
+  let notification = notificationBox.
+    getNotificationWithValue("inspector-page-navigation");
+  ok(notification, "found the inspector-page-navigation notification");
+
+  // By closing the notification it is expected that page navigation is
+  // canceled.
+  executeSoon(function() {
+    notification.close();
+    locationTest2();
+  });
+}
+
+function onPageLoad() {
+  gBrowser.selectedBrowser.removeEventListener("load", onPageLoad, true);
+
+  isnot(content.location.href.indexOf("test2"), -1,
+        "page navigated to the correct location");
+
+  let para = content.document.querySelector("p");
+  ok(para, "found the paragraph element, third time");
+  is(para.textContent, "test2", "paragraph content is correct");
+
+  ok(!InspectorUI.inspecting, "Inspector is not highlighting");
+  ok(!InspectorUI.isTreePanelOpen, "Inspector Panel is not open");
+
+  testEnd();
+}
+
+function locationTest2() {
+  // Location did not change.
+  let para = content.document.querySelector("p");
+  ok(para, "found the paragraph element, second time");
+  is(para.textContent, "init", "paragraph content is correct");
+
+  ok(InspectorUI.inspecting, "Inspector is highlighting");
+  ok(InspectorUI.isTreePanelOpen, "Inspector Panel is open");
+
+  notificationBox.addEventListener("AlertActive", alertActive2, false);
+
+  content.location = "data:text/html,<div>location change test 2 for " +
+    "inspector</div><p>test2</p>";
+}
+
+function alertActive2() {
+  notificationBox.removeEventListener("AlertActive", alertActive2, false);
+
+  let notification = notificationBox.
+    getNotificationWithValue("inspector-page-navigation");
+  ok(notification, "found the inspector-page-navigation notification");
+
+  let buttons = notification.querySelectorAll("button");
+  let buttonLeave = null;
+  for (let i = 0; i < buttons.length; i++) {
+    if (buttons[i].buttonInfo.id == "inspector.confirmNavigationAway.buttonLeave") {
+      buttonLeave = buttons[i];
+      break;
+    }
+  }
+
+  ok(buttonLeave, "the Leave page button was found");
+
+  // Accept page navigation.
+  executeSoon(function(){
+    buttonLeave.doCommand();
+  });
+}
+
+function testEnd() {
+  notificationBox = null;
+  InspectorUI.isDirty = false;
+  gBrowser.removeCurrentTab();
+  executeSoon(finish);
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onBrowserLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onBrowserLoad, true);
+    waitForFocus(startLocationTests, content);
+  }, true);
+
+  content.location = "data:text/html,<div>location change tests for " +
+    "inspector.</div><p>init</p>";
+}
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -275,17 +275,17 @@ var Scratchpad = {
       let contentWindow = this.gBrowser.selectedBrowser.contentWindow;
 
       let scriptError = Cc["@mozilla.org/scripterror;1"].
                         createInstance(Ci.nsIScriptError2);
 
       scriptError.initWithWindowID(ex.message + "\n" + ex.stack, ex.fileName,
                                    "", ex.lineNumber, 0, scriptError.errorFlag,
                                    "content javascript",
-                                   this.getWindowId(contentWindow));
+                                   this.getInnerWindowId(contentWindow));
 
       Services.console.logMessage(scriptError);
     }
 
     return result;
   },
 
   /**
@@ -628,26 +628,26 @@ var Scratchpad = {
     this._chromeSandbox = null;
     this._contentSandbox = null;
     this._previousWindow = null;
     this._previousBrowser = null;
     this._previousLocation = null;
   },
 
   /**
-   * Gets the ID of the outer window of the given DOM window object.
+   * Gets the ID of the inner window of the given DOM window object.
    *
    * @param nsIDOMWindow aWindow
    * @return integer
-   *         the outer window ID
+   *         the inner window ID
    */
-  getWindowId: function SP_getWindowId(aWindow)
+  getInnerWindowId: function SP_getInnerWindowId(aWindow)
   {
     return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
-           getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
+           getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
   },
 
   /**
    * The Scratchpad window DOMContentLoaded event handler. This method
    * initializes the Scratchpad window and source editor.
    *
    * @param nsIDOMEvent aEvent
    */
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -85,17 +85,17 @@ XPCOMUtils.defineLazyGetter(this, "Prope
     Cu.reportError(err);
   }
   return obj.PropertyPanel;
 });
 
 XPCOMUtils.defineLazyGetter(this, "AutocompletePopup", function () {
   var obj = {};
   try {
-    Cu.import("resource://gre/modules/AutocompletePopup.jsm", obj);
+    Cu.import("resource:///modules/AutocompletePopup.jsm", obj);
   }
   catch (err) {
     Cu.reportError(err);
   }
   return obj.AutocompletePopup;
 });
 
 XPCOMUtils.defineLazyGetter(this, "namesAndValuesOf", function () {
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_651501_document_body_autocomplete.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_651501_document_body_autocomplete.js
@@ -1,17 +1,17 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Tests that document.body autocompletes in the web console.
 
-Cu.import("resource://gre/modules/PropertyPanel.jsm");
+Cu.import("resource:///modules/PropertyPanel.jsm");
 
 function test() {
   addTab("data:text/html,Web Console autocompletion bug in document.body");
   browser.addEventListener("load", onLoad, true);
 }
 
 var gHUD;
 
--- a/browser/devtools/webconsole/test/browser/head.js
+++ b/browser/devtools/webconsole/test/browser/head.js
@@ -31,17 +31,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-Cu.import("resource://gre/modules/HUDService.jsm");
+Cu.import("resource:///modules/HUDService.jsm");
 
 function log(aMsg)
 {
   dump("*** WebConsoleTest: " + aMsg + "\n");
 }
 
 function pprint(aObj)
 {
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/inspector.properties
@@ -0,0 +1,9 @@
+# LOCALIZATION NOTE (confirmNavigationAway): Used in the Inspector tool, when
+# the user tries to navigate away from a web page, to confirm the change of
+# page.
+confirmNavigationAway.message=Leaving this page will close the Inspector and the changes you have made will be lost.
+confirmNavigationAway.buttonLeave=Leave Page
+confirmNavigationAway.buttonLeaveAccesskey=L
+confirmNavigationAway.buttonStay=Stay on Page
+confirmNavigationAway.buttonStayAccesskey=S
+
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -11,16 +11,17 @@
 #ifdef MOZ_SERVICES_SYNC
     locale/browser/aboutSyncTabs.dtd               (%chrome/browser/aboutSyncTabs.dtd)
 #endif
 *   locale/browser/browser.dtd                     (%chrome/browser/browser.dtd)
     locale/browser/baseMenuOverlay.dtd             (%chrome/browser/baseMenuOverlay.dtd)
     locale/browser/browser.properties              (%chrome/browser/browser.properties)
     locale/browser/scratchpad.properties           (%chrome/browser/scratchpad.properties)
     locale/browser/scratchpad.dtd                  (%chrome/browser/scratchpad.dtd)
+    locale/browser/inspector.properties            (%chrome/browser/inspector.properties)
     locale/browser/openLocation.dtd                (%chrome/browser/openLocation.dtd)
     locale/browser/openLocation.properties         (%chrome/browser/openLocation.properties)
 *   locale/browser/pageInfo.dtd                    (%chrome/browser/pageInfo.dtd)
     locale/browser/pageInfo.properties             (%chrome/browser/pageInfo.properties)
     locale/browser/quitDialog.properties           (%chrome/browser/quitDialog.properties)
 *   locale/browser/safeMode.dtd                    (%chrome/browser/safeMode.dtd)
     locale/browser/sanitize.dtd                    (%chrome/browser/sanitize.dtd)
     locale/browser/search.properties               (%chrome/browser/search.properties)
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -738,18 +738,18 @@ public:
    *   @param aParamsLength Length of aParams.
    *   @param aURI URI of resource containing error (may be null).
    *   @param aSourceLine The text of the line that contains the error (may be
               empty).
    *   @param aLineNumber Line number within resource containing error.
    *   @param aColumnNumber Column number within resource containing error.
    *   @param aErrorFlags See nsIScriptError.
    *   @param aCategory Name of module reporting error.
-   *   @param [aWindowId=0] (Optional) The window ID of the outer window the
-   *          message originates from.
+   *   @param [aInnerWindowId=0] (Optional) The window ID of the inner window
+   *          the message originates from.
    */
   enum PropertiesFile {
     eCSS_PROPERTIES,
     eXBL_PROPERTIES,
     eXUL_PROPERTIES,
     eLAYOUT_PROPERTIES,
     eFORMS_PROPERTIES,
     ePRINTING_PROPERTIES,
@@ -764,17 +764,17 @@ public:
                                   const PRUnichar **aParams,
                                   PRUint32 aParamsLength,
                                   nsIURI* aURI,
                                   const nsAFlatString& aSourceLine,
                                   PRUint32 aLineNumber,
                                   PRUint32 aColumnNumber,
                                   PRUint32 aErrorFlags,
                                   const char *aCategory,
-                                  PRUint64 aWindowId = 0);
+                                  PRUint64 aInnerWindowId = 0);
 
   /**
    * Report a localized error message to the error console.
    *   @param aFile Properties file containing localized message.
    *   @param aMessageName Name of localized message.
    *   @param aParams Parameters to be substituted into localized message.
    *   @param aParamsLength Length of aParams.
    *   @param aURI URI of resource containing error (may be null).
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -716,16 +716,25 @@ public:
    */
   PRUint64 OuterWindowID() const
   {
     nsPIDOMWindow *window = GetWindow();
     return window ? window->WindowID() : 0;
   }
 
   /**
+   * Return the inner window ID.
+   */
+  PRUint64 InnerWindowID()
+  {
+    nsPIDOMWindow *window = GetInnerWindow();
+    return window ? window->WindowID() : 0;
+  }
+
+  /**
    * Get the script loader for this document
    */ 
   virtual nsScriptLoader* ScriptLoader() = 0;
 
   /**
    * Add/Remove an element to the document's id and name hashes
    */
   virtual void AddToIdTable(Element* aElement, nsIAtom* aId) = 0;
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -2728,17 +2728,17 @@ nsContentUtils::ReportToConsole(Properti
                                 const PRUnichar **aParams,
                                 PRUint32 aParamsLength,
                                 nsIURI* aURI,
                                 const nsAFlatString& aSourceLine,
                                 PRUint32 aLineNumber,
                                 PRUint32 aColumnNumber,
                                 PRUint32 aErrorFlags,
                                 const char *aCategory,
-                                PRUint64 aWindowId)
+                                PRUint64 aInnerWindowId)
 {
   NS_ASSERTION((aParams && aParamsLength) || (!aParams && !aParamsLength),
                "Supply either both parameters and their number or no"
                "parameters and 0.");
 
   nsresult rv;
   if (!sConsoleService) { // only need to bother null-checking here
     rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
@@ -2762,17 +2762,18 @@ nsContentUtils::ReportToConsole(Properti
   nsCOMPtr<nsIScriptError2> errorObject =
       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = errorObject->InitWithWindowID(errorText.get(),
                                      NS_ConvertUTF8toUTF16(spec).get(), // file name
                                      aSourceLine.get(),
                                      aLineNumber, aColumnNumber,
-                                     aErrorFlags, aCategory, aWindowId);
+                                     aErrorFlags, aCategory,
+                                     aInnerWindowId);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIScriptError> logError = do_QueryInterface(errorObject);
   return sConsoleService->LogMessage(logError);
 }
 
 /* static */ nsresult
 nsContentUtils::ReportToConsole(PropertiesFile aFile,
@@ -2783,27 +2784,27 @@ nsContentUtils::ReportToConsole(Properti
                                 const nsAFlatString& aSourceLine,
                                 PRUint32 aLineNumber,
                                 PRUint32 aColumnNumber,
                                 PRUint32 aErrorFlags,
                                 const char *aCategory,
                                 nsIDocument* aDocument)
 {
   nsIURI* uri = aURI;
-  PRUint64 windowID = 0;
+  PRUint64 innerWindowID = 0;
   if (aDocument) {
     if (!uri) {
       uri = aDocument->GetDocumentURI();
     }
-    windowID = aDocument->OuterWindowID();
+    innerWindowID = aDocument->InnerWindowID();
   }
 
   return ReportToConsole(aFile, aMessageName, aParams, aParamsLength, uri,
                          aSourceLine, aLineNumber, aColumnNumber, aErrorFlags,
-                         aCategory, windowID);
+                         aCategory, innerWindowID);
 }
 
 PRBool
 nsContentUtils::IsChromeDoc(nsIDocument *aDocument)
 {
   if (!aDocument) {
     return PR_FALSE;
   }
--- a/content/base/src/nsEventSource.cpp
+++ b/content/base/src/nsEventSource.cpp
@@ -80,17 +80,17 @@ using namespace mozilla;
 nsEventSource::nsEventSource() :
   mStatus(PARSE_STATE_OFF),
   mFrozen(PR_FALSE),
   mErrorLoadOnRedirect(PR_FALSE),
   mGoingToDispatchAllMessages(PR_FALSE),
   mLastConvertionResult(NS_OK),
   mReadyState(nsIEventSource::CONNECTING),
   mScriptLine(0),
-  mWindowID(0)
+  mInnerWindowID(0)
 {
 }
 
 nsEventSource::~nsEventSource()
 {
   Close();
 
   if (mListenerManager) {
@@ -248,17 +248,17 @@ nsEventSource::Init(nsIPrincipal* aPrinc
     do_GetService("@mozilla.org/js/xpc/ContextStack;1");
   JSContext* cx = nsnull;
   if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) {
     const char *filename;
     if (nsJSUtils::GetCallingLocation(cx, &filename, &mScriptLine)) {
       mScriptFile.AssignASCII(filename);
     }
 
-    mWindowID = nsJSUtils::GetCurrentlyRunningCodeWindowID(cx);
+    mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
   }
 
   // Get the load group for the page. When requesting we'll add ourselves to it.
   // This way any pending requests will be automatically aborted if the user
   // leaves the page.
   if (mScriptContext) {
     nsCOMPtr<nsIDocument> doc =
       nsContentUtils::GetDocumentFromScriptContext(mScriptContext);
@@ -1067,17 +1067,17 @@ nsEventSource::PrintErrorOnConsole(const
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   errObj->InitWithWindowID(message.get(),
                            mScriptFile.get(),
                            nsnull,
                            mScriptLine, 0,
                            nsIScriptError::errorFlag,
-                           "Event Source", mWindowID);
+                           "Event Source", mInnerWindowID);
 
   // print the error message directly to the JS console
   nsCOMPtr<nsIScriptError> logError = do_QueryInterface(errObj);
   rv = console->LogMessage(logError);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
--- a/content/base/src/nsEventSource.h
+++ b/content/base/src/nsEventSource.h
@@ -247,21 +247,21 @@ protected:
 
   PRUint32 mRedirectFlags;
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
   nsCOMPtr<nsIChannel> mNewRedirectChannel;
 
   // Event Source owner information:
   // - the script file name
   // - source code line number where the Event Source object was constructed.
-  // - the window ID of the outer window where the script lives. Note that this
-  // may not be the same as the Event Source owner window.
+  // - the ID of the inner window where the script lives. Note that this may not
+  //   be the same as the Event Source owner window.
   // These attributes are used for error reporting.
   nsString mScriptFile;
   PRUint32 mScriptLine;
-  PRUint64 mWindowID;
+  PRUint64 mInnerWindowID;
 
 private:
   nsEventSource(const nsEventSource& x);   // prevent bad usage
   nsEventSource& operator=(const nsEventSource& x);
 };
 
 #endif // nsEventSource_h__
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -352,17 +352,17 @@ nsWebSocketEstablishedConnection::PrintE
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   errorObject->InitWithWindowID
     (message.get(),
      NS_ConvertUTF8toUTF16(mOwner->GetScriptFile()).get(),
      nsnull,
      mOwner->GetScriptLine(), 0, nsIScriptError::errorFlag,
-     "Web Socket", mOwner->WindowID()
+     "Web Socket", mOwner->InnerWindowID()
      );
   
   // print the error message directly to the JS console
   nsCOMPtr<nsIScriptError> logError(do_QueryInterface(errorObject));
   rv = console->LogMessage(logError);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
@@ -626,17 +626,17 @@ nsWebSocketEstablishedConnection::GetInt
 nsWebSocket::nsWebSocket() : mKeepingAlive(PR_FALSE),
                              mCheckMustKeepAlive(PR_TRUE),
                              mTriggeredCloseEvent(PR_FALSE),
                              mClientReasonCode(0),
                              mServerReasonCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
                              mReadyState(nsIMozWebSocket::CONNECTING),
                              mOutgoingBufferedAmount(0),
                              mScriptLine(0),
-                             mWindowID(0)
+                             mInnerWindowID(0)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 }
 
 nsWebSocket::~nsWebSocket()
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   if (mConnection) {
@@ -1423,17 +1423,17 @@ nsWebSocket::Init(nsIPrincipal* aPrincip
       }
 
       jsbytecode *pc = JS_GetFramePC(cx, fp);
       if (script && pc) {
         mScriptLine = JS_PCToLineNumber(cx, script, pc);
       }
     }
 
-    mWindowID = nsJSUtils::GetCurrentlyRunningCodeWindowID(cx);
+    mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
   }
 
   // parses the url
   rv = ParseURL(PromiseFlatString(aURL));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Don't allow https:// to open ws://
   if (!mSecure && 
--- a/content/base/src/nsWebSocket.h
+++ b/content/base/src/nsWebSocket.h
@@ -94,17 +94,17 @@ public:
                               PRUint8 optional_argc);
   NS_IMETHOD RemoveEventListener(const nsAString& aType,
                                  nsIDOMEventListener* aListener,
                                  PRBool aUseCapture);
 
   // Determine if preferences allow WebSocket
   static PRBool PrefEnabled();
 
-  const PRUint64 WindowID() const { return mWindowID; }
+  const PRUint64 InnerWindowID() const { return mInnerWindowID; }
   const nsCString& GetScriptFile() const { return mScriptFile; }
   const PRUint32 GetScriptLine() const { return mScriptLine; }
 
 protected:
   nsresult ParseURL(const nsString& aURL);
   nsresult EstablishConnection();
 
   nsresult CreateAndDispatchSimpleEvent(const nsString& aName);
@@ -159,21 +159,21 @@ protected:
   nsRefPtr<nsWebSocketEstablishedConnection> mConnection;
   PRUint32 mOutgoingBufferedAmount; // actually, we get this value from
                                     // mConnection when we are connected,
                                     // but we need this one after disconnecting.
 
   // Web Socket owner information:
   // - the script file name, UTF8 encoded.
   // - source code line number where the Web Socket object was constructed.
-  // - the window ID of the outer window where the script lives. Note that this 
-  // may not be the same as the Web Socket owner window.
+  // - the ID of the inner window where the script lives. Note that this may not
+  //   be the same as the Web Socket owner window.
   // These attributes are used for error reporting.
   nsCString mScriptFile;
   PRUint32 mScriptLine;
-  PRUint64 mWindowID;
+  PRUint64 mInnerWindowID;
 
 private:
   nsWebSocket(const nsWebSocket& x);   // prevent bad usage
   nsWebSocket& operator=(const nsWebSocket& x);
 };
 
 #endif
--- a/content/xml/document/src/nsXMLDocument.cpp
+++ b/content/xml/document/src/nsXMLDocument.cpp
@@ -377,18 +377,18 @@ nsXMLDocument::Load(const nsAString& aUr
       nsCOMPtr<nsIScriptError2> errorObject =
           do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
 
       rv = errorObject->InitWithWindowID(error.get(), NS_ConvertUTF8toUTF16(spec).get(),
                                          nsnull, 0, 0, nsIScriptError::warningFlag,
                                          "DOM",
                                          callingDoc ?
-                                           callingDoc->OuterWindowID() :
-                                           this->OuterWindowID());
+                                           callingDoc->InnerWindowID() :
+                                           this->InnerWindowID());
 
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsIConsoleService> consoleService =
         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
       nsCOMPtr<nsIScriptError> logError = do_QueryInterface(errorObject);
       if (consoleService && logError) {
         consoleService->LogMessage(logError);
--- a/dom/base/ConsoleAPI.js
+++ b/dom/base/ConsoleAPI.js
@@ -38,59 +38,64 @@
  * ***** END LICENSE BLOCK ***** */
 
 let Cu = Components.utils;
 let Ci = Components.interfaces;
 let Cc = Components.classes;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm");
 
 function ConsoleAPI() {}
 ConsoleAPI.prototype = {
 
   classID: Components.ID("{b49c18f8-3379-4fc0-8c90-d7772c1a9ff3}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
 
   // nsIDOMGlobalPropertyInitializer
   init: function CA_init(aWindow) {
-    let id;
+    let outerID;
+    let innerID;
     try {
-      id = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                  .getInterface(Ci.nsIDOMWindowUtils)
-                  .outerWindowID;
-    } catch (ex) {
+      let windowUtils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDOMWindowUtils);
+
+      outerID = windowUtils.outerWindowID;
+      innerID = windowUtils.currentInnerWindowID;
+    }
+    catch (ex) {
       Cu.reportError(ex);
     }
 
     let self = this;
     let chromeObject = {
       // window.console API
       log: function CA_log() {
-        self.notifyObservers(id, "log", arguments);
+        self.notifyObservers(outerID, innerID, "log", arguments);
       },
       info: function CA_info() {
-        self.notifyObservers(id, "info", arguments);
+        self.notifyObservers(outerID, innerID, "info", arguments);
       },
       warn: function CA_warn() {
-        self.notifyObservers(id, "warn", arguments);
+        self.notifyObservers(outerID, innerID, "warn", arguments);
       },
       error: function CA_error() {
-        self.notifyObservers(id, "error", arguments);
+        self.notifyObservers(outerID, innerID, "error", arguments);
       },
       debug: function CA_debug() {
-        self.notifyObservers(id, "log", arguments);
+        self.notifyObservers(outerID, innerID, "log", arguments);
       },
       trace: function CA_trace() {
-        self.notifyObservers(id, "trace", self.getStackTrace());
+        self.notifyObservers(outerID, innerID, "trace", self.getStackTrace());
       },
       // Displays an interactive listing of all the properties of an object.
       dir: function CA_dir() {
-        self.notifyObservers(id, "dir", arguments);
+        self.notifyObservers(outerID, innerID, "dir", arguments);
       },
       __exposedProps__: {
         log: "r",
         info: "r",
         warn: "r",
         error: "r",
         debug: "r",
         trace: "r",
@@ -120,38 +125,52 @@ ConsoleAPI.prototype = {
 
     Object.defineProperties(contentObj, properties);
     Cu.makeObjectPropsNormal(contentObj);
 
     return contentObj;
   },
 
   /**
-   * Notify all observers of any console API call
+   * Notify all observers of any console API call.
+   *
+   * @param number aOuterWindowID
+   *        The outer window ID from where the message came from.
+   * @param number aInnerWindowID
+   *        The inner window ID from where the message came from.
+   * @param string aLevel
+   *        The message level.
+   * @param mixed aArguments
+   *        The arguments given to the console API call.
    **/
-  notifyObservers: function CA_notifyObservers(aID, aLevel, aArguments) {
-    if (!aID)
+  notifyObservers:
+  function CA_notifyObservers(aOuterWindowID, aInnerWindowID, aLevel, aArguments) {
+    if (!aOuterWindowID) {
       return;
+    }
 
     let stack = this.getStackTrace();
     // Skip the first frame since it contains an internal call.
     let frame = stack[1];
     let consoleEvent = {
-      ID: aID,
+      ID: aOuterWindowID,
+      innerID: aInnerWindowID,
       level: aLevel,
       filename: frame.filename,
       lineNumber: frame.lineNumber,
       functionName: frame.functionName,
       arguments: aArguments
     };
 
     consoleEvent.wrappedJSObject = consoleEvent;
 
+    ConsoleAPIStorage.recordEvent(aInnerWindowID, consoleEvent);
+
     Services.obs.notifyObservers(consoleEvent,
-                                 "console-api-log-event", aID);
+                                 "console-api-log-event", aOuterWindowID);
   },
 
   /**
    * Build the stacktrace array for the console.trace() call.
    *
    * @return array
    *         Each element is a stack frame that holds the following properties:
    *         filename, lineNumber, functionName and language.
new file mode 100644
--- /dev/null
+++ b/dom/base/ConsoleAPIStorage.jsm
@@ -0,0 +1,180 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is ConsoleStorageService code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  David Dahl <ddahl@mozilla.com>  (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+let Cu = Components.utils;
+let Ci = Components.interfaces;
+let Cc = Components.classes;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+const STORAGE_MAX_EVENTS = 200;
+
+XPCOMUtils.defineLazyGetter(this, "gPrivBrowsing", function () {
+  // private browsing may not be available in some Gecko Apps
+  try {
+    return Cc["@mozilla.org/privatebrowsing;1"].getService(Ci.nsIPrivateBrowsingService);
+  }
+  catch (ex) {
+    return null;
+  }
+});
+
+var EXPORTED_SYMBOLS = ["ConsoleAPIStorage"];
+
+var _consoleStorage = {};
+
+/**
+ * The ConsoleAPIStorage is meant to cache window.console API calls for later
+ * reuse by other components when needed. For example, the Web Console code can
+ * display the cached messages when it opens for the active tab.
+ *
+ * ConsoleAPI messages are stored as they come from the ConsoleAPI code, with
+ * all their properties. They are kept around until the inner window object that
+ * created the messages is destroyed. Messages are indexed by the inner window
+ * ID.
+ *
+ * Usage:
+ *    Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm");
+ *
+ *    // Get the cached events array for the window you want (use the inner
+ *    // window ID).
+ *    let events = ConsoleAPIStorage.getEvents(innerWindowID);
+ *    events.forEach(function(event) { ... });
+ *
+ *    // Clear the events for the given inner window ID.
+ *    ConsoleAPIStorage.clearEvents(innerWindowID);
+ */
+var ConsoleAPIStorage = {
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+  /** @private */
+  observe: function CS_observe(aSubject, aTopic, aData)
+  {
+    if (aTopic == "xpcom-shutdown") {
+      Services.obs.removeObserver(this, "xpcom-shutdown");
+      Services.obs.removeObserver(this, "inner-window-destroyed");
+      Services.obs.removeObserver(this, "memory-pressure");
+      delete _consoleStorage;
+    }
+    else if (aTopic == "inner-window-destroyed") {
+      let innerWindowID = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+      this.clearEvents(innerWindowID);
+    }
+    else if (aTopic == "memory-pressure") {
+      if (aData == "low-memory") {
+        this.clearEvents();
+      }
+    }
+  },
+
+  /** @private */
+  init: function CS_init()
+  {
+    Services.obs.addObserver(this, "xpcom-shutdown", false);
+    Services.obs.addObserver(this, "inner-window-destroyed", false);
+    Services.obs.addObserver(this, "memory-pressure", false);
+  },
+
+  /**
+   * Get the events array by inner window ID.
+   *
+   * @param string aId
+   *        The inner window ID for which you want to get the array of cached
+   *        events.
+   * @returns array
+   *          The array of cached events for the given window.
+   */
+  getEvents: function CS_getEvents(aId)
+  {
+    return _consoleStorage[aId] || [];
+  },
+
+  /**
+   * Record an event associated with the given window ID.
+   *
+   * @param string aWindowID
+   *        The ID of the inner window for which the event occurred.
+   * @param object aEvent
+   *        A JavaScript object you want to store.
+   */
+  recordEvent: function CS_recordEvent(aWindowID, aEvent)
+  {
+    let ID = parseInt(aWindowID);
+    if (isNaN(ID)) {
+      throw new Error("Invalid window ID argument");
+    }
+
+    if (gPrivBrowsing && gPrivBrowsing.privateBrowsingEnabled) {
+      return;
+    }
+
+    if (!_consoleStorage[ID]) {
+      _consoleStorage[ID] = [];
+    }
+    let storage = _consoleStorage[ID];
+    storage.push(aEvent);
+
+    // truncate
+    if (storage.length > STORAGE_MAX_EVENTS) {
+      storage.shift();
+    }
+
+    Services.obs.notifyObservers(aEvent, "console-storage-cache-event", ID);
+  },
+
+  /**
+   * Clear storage data for the given window.
+   *
+   * @param string [aId]
+   *        Optional, the inner window ID for which you want to clear the
+   *        messages. If this is not specified all of the cached messages are
+   *        cleared, from all window objects.
+   */
+  clearEvents: function CS_clearEvents(aId)
+  {
+    if (aId != null) {
+      delete _consoleStorage[aId];
+    }
+    else {
+      _consoleStorage = {};
+      Services.obs.notifyObservers(null, "console-storage-reset", null);
+    }
+  },
+};
+
+ConsoleAPIStorage.init();
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -47,16 +47,19 @@ LIBRARY_NAME	= jsdombase_s
 LIBXUL_LIBRARY	= 1
 FORCE_STATIC_LIB = 1
 
 EXTRA_PP_COMPONENTS = \
 		ConsoleAPI.js \
 		ConsoleAPI.manifest \
 		$(NULL)
 
+EXTRA_JS_MODULES = ConsoleAPIStorage.jsm \
+		$(NULL)
+
 XPIDLSRCS = \
   nsIEntropyCollector.idl \
   nsIScriptChannel.idl \
   $(NULL)
 
 EXPORTS = \
   nsDOMCID.h \
   nsDOMClassInfoClasses.h \
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1697,17 +1697,17 @@ PrintWarningOnConsole(JSContext *cx, con
 
   nsresult rv = scriptError->InitWithWindowID(msg.get(),
                                               sourcefile.get(),
                                               EmptyString().get(),
                                               lineno,
                                               0, // column for error is not available
                                               nsIScriptError::warningFlag,
                                               "DOM:HTML",
-                                              nsJSUtils::GetCurrentlyRunningCodeWindowID(cx));
+                                              nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx));
 
   if (NS_SUCCEEDED(rv)){
     nsCOMPtr<nsIScriptError> logError = do_QueryInterface(scriptError);
     consoleService->LogMessage(logError);
   }
 }
 
 static inline JSString *
@@ -5078,17 +5078,17 @@ nsWindowSH::InvalidateGlobalScopePollute
       nsIHTMLDocument *doc = (nsIHTMLDocument *)::JS_GetPrivate(cx, proto);
 
       NS_IF_RELEASE(doc);
 
       ::JS_SetPrivate(cx, proto, nsnull);
 
       // Pull the global scope polluter out of the prototype chain so
       // that it can be freed.
-      ::JS_SetPrototype(cx, obj, ::JS_GetPrototype(cx, proto));
+      ::JS_SplicePrototype(cx, obj, ::JS_GetPrototype(cx, proto));
 
       break;
     }
 
     obj = proto;
   }
 }
 
@@ -5100,44 +5100,40 @@ nsWindowSH::InstallGlobalScopePolluter(J
   // If global scope pollution is disabled, or if our document is not
   // a HTML document, do nothing
   if (sDisableGlobalScopePollutionSupport || !doc) {
     return NS_OK;
   }
 
   JSAutoRequest ar(cx);
 
-  JSObject *gsp = ::JS_NewObject(cx, &sGlobalScopePolluterClass, nsnull, obj);
+  JSObject *gsp = ::JS_NewObjectWithUniqueType(cx, &sGlobalScopePolluterClass, nsnull, obj);
   if (!gsp) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   JSObject *o = obj, *proto;
 
   // Find the place in the prototype chain where we want this global
   // scope polluter (right before Object.prototype).
 
   while ((proto = ::JS_GetPrototype(cx, o))) {
     if (JS_GET_CLASS(cx, proto) == sObjectClass) {
       // Set the global scope polluters prototype to Object.prototype
-      if (!::JS_SetPrototype(cx, gsp, proto)) {
-        return NS_ERROR_UNEXPECTED;
-      }
+      ::JS_SplicePrototype(cx, gsp, proto);
 
       break;
     }
 
     o = proto;
   }
 
   // And then set the prototype of the object whose prototype was
   // Object.prototype to be the global scope polluter.
-  if (!::JS_SetPrototype(cx, o, gsp)) {
-    return NS_ERROR_UNEXPECTED;
-  }
+  ::JS_SplicePrototype(cx, o, gsp);
 
   if (!::JS_SetPrivate(cx, gsp, doc)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   // The global scope polluter will release doc on destruction (or
   // invalidation).
   NS_ADDREF(doc);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -535,16 +535,21 @@ public:
     return mSerial;
   }
 
   static nsGlobalWindow* GetOuterWindowWithId(PRUint64 aWindowID) {
     nsGlobalWindow* outerWindow = sWindowsById->Get(aWindowID);
     return outerWindow && !outerWindow->IsInnerWindow() ? outerWindow : nsnull;
   }
 
+  static nsGlobalWindow* GetInnerWindowWithId(PRUint64 aInnerWindowID) {
+    nsGlobalWindow* innerWindow = sWindowsById->Get(aInnerWindowID);
+    return innerWindow && innerWindow->IsInnerWindow() ? innerWindow : nsnull;
+  }
+
   static bool HasIndexedDBSupport();
 
   static bool HasPerformanceSupport();
 
   static WindowByIdTable* GetWindowsTable() {
     return sWindowsById;
   }
 
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -262,21 +262,21 @@ class ScriptErrorEvent : public nsRunnab
 {
 public:
   ScriptErrorEvent(nsIScriptGlobalObject* aScriptGlobal,
                    PRUint32 aLineNr, PRUint32 aColumn, PRUint32 aFlags,
                    const nsAString& aErrorMsg,
                    const nsAString& aFileName,
                    const nsAString& aSourceLine,
                    PRBool aDispatchEvent,
-                   PRUint64 aWindowID)
+                   PRUint64 aInnerWindowID)
   : mScriptGlobal(aScriptGlobal), mLineNr(aLineNr), mColumn(aColumn),
     mFlags(aFlags), mErrorMsg(aErrorMsg), mFileName(aFileName),
     mSourceLine(aSourceLine), mDispatchEvent(aDispatchEvent),
-    mWindowID(aWindowID)
+    mInnerWindowID(aInnerWindowID)
   {}
 
   NS_IMETHOD Run()
   {
     nsEventStatus status = nsEventStatus_eIgnore;
     // First, notify the DOM that we have a script error.
     if (mDispatchEvent) {
       nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
@@ -359,17 +359,17 @@ public:
           ? "chrome javascript"
           : "content javascript";
 
         nsCOMPtr<nsIScriptError2> error2(do_QueryInterface(errorObject));
         if (error2) {
           rv = error2->InitWithWindowID(mErrorMsg.get(), mFileName.get(),
                                         mSourceLine.get(),
                                         mLineNr, mColumn, mFlags,
-                                        category, mWindowID);
+                                        category, mInnerWindowID);
         } else {
           rv = errorObject->Init(mErrorMsg.get(), mFileName.get(),
                                  mSourceLine.get(),
                                  mLineNr, mColumn, mFlags,
                                  category);
         }
 
         if (NS_SUCCEEDED(rv)) {
@@ -388,17 +388,17 @@ public:
   nsCOMPtr<nsIScriptGlobalObject> mScriptGlobal;
   PRUint32                        mLineNr;
   PRUint32                        mColumn;
   PRUint32                        mFlags;
   nsString                        mErrorMsg;
   nsString                        mFileName;
   nsString                        mSourceLine;
   PRBool                          mDispatchEvent;
-  PRUint64                        mWindowID;
+  PRUint64                        mInnerWindowID;
 
   static PRBool sHandlingScriptError;
 };
 
 PRBool ScriptErrorEvent::sHandlingScriptError = PR_FALSE;
 
 // NOTE: This function could be refactored to use the above.  The only reason
 // it has not been done is that the code below only fills the error event
@@ -469,23 +469,29 @@ NS_ScriptErrorReporter(JSContext *cx,
        * event because the dom event handler would encounter
        * an OOM exception trying to process the event, and
        * then we'd need to generate a new OOM event for that
        * new OOM instance -- this isn't pretty.
        */
       nsAutoString sourceLine;
       sourceLine.Assign(reinterpret_cast<const PRUnichar*>(report->uclinebuf));
       nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
-      PRUint64 windowID = win ? win->WindowID() : 0;
+      PRUint64 innerWindowID = 0;
+      if (win) {
+        nsCOMPtr<nsPIDOMWindow> innerWin = win->GetCurrentInnerWindow();
+        if (innerWin) {
+          innerWindowID = innerWin->WindowID();
+        }
+      }
       nsContentUtils::AddScriptRunner(
         new ScriptErrorEvent(globalObject, report->lineno,
                              report->uctokenptr - report->uclinebuf,
                              report->flags, msg, fileName, sourceLine,
                              report->errorNumber != JSMSG_OUT_OF_MEMORY,
-                             windowID));
+                             innerWindowID));
     }
   }
 
 #ifdef DEBUG
   // Print it to stderr as well, for the benefit of those invoking
   // mozilla with -console.
   nsCAutoString error;
   error.Assign("JavaScript ");
@@ -904,16 +910,17 @@ static const char js_zeal_compartment_st
 #endif
 static const char js_tracejit_content_str[]   = JS_OPTIONS_DOT_STR "tracejit.content";
 static const char js_tracejit_chrome_str[]    = JS_OPTIONS_DOT_STR "tracejit.chrome";
 static const char js_methodjit_content_str[]  = JS_OPTIONS_DOT_STR "methodjit.content";
 static const char js_methodjit_chrome_str[]   = JS_OPTIONS_DOT_STR "methodjit.chrome";
 static const char js_profiling_content_str[]  = JS_OPTIONS_DOT_STR "jitprofiling.content";
 static const char js_profiling_chrome_str[]   = JS_OPTIONS_DOT_STR "jitprofiling.chrome";
 static const char js_methodjit_always_str[]   = JS_OPTIONS_DOT_STR "methodjit_always";
+static const char js_typeinfer_str[]          = JS_OPTIONS_DOT_STR "typeinference";
 static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log";
 
 int
 nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
 {
   nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
   PRUint32 oldDefaultJSOptions = context->mDefaultJSOptions;
   PRUint32 newDefaultJSOptions = oldDefaultJSOptions;
@@ -936,24 +943,26 @@ nsJSContext::JSOptionChangedCallback(con
                                               js_tracejit_content_str);
   PRBool useMethodJIT = Preferences::GetBool(chromeWindow ?
                                                js_methodjit_chrome_str :
                                                js_methodjit_content_str);
   PRBool useProfiling = Preferences::GetBool(chromeWindow ?
                                                js_profiling_chrome_str :
                                                js_profiling_content_str);
   PRBool useMethodJITAlways = Preferences::GetBool(js_methodjit_always_str);
+  PRBool useTypeInference = !chromeWindow && Preferences::GetBool(js_typeinfer_str);
   nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
   if (xr) {
     PRBool safeMode = PR_FALSE;
     xr->GetInSafeMode(&safeMode);
     if (safeMode) {
       useTraceJIT = PR_FALSE;
       useMethodJIT = PR_FALSE;
       useProfiling = PR_FALSE;
+      useTypeInference = PR_FALSE;
       useMethodJITAlways = PR_TRUE;
     }
   }    
 
   if (useTraceJIT)
     newDefaultJSOptions |= JSOPTION_JIT;
   else
     newDefaultJSOptions &= ~JSOPTION_JIT;
@@ -968,16 +977,21 @@ nsJSContext::JSOptionChangedCallback(con
   else
     newDefaultJSOptions &= ~JSOPTION_PROFILING;
 
   if (useMethodJITAlways)
     newDefaultJSOptions |= JSOPTION_METHODJIT_ALWAYS;
   else
     newDefaultJSOptions &= ~JSOPTION_METHODJIT_ALWAYS;
 
+  if (useTypeInference)
+    newDefaultJSOptions |= JSOPTION_TYPE_INFERENCE;
+  else
+    newDefaultJSOptions &= ~JSOPTION_TYPE_INFERENCE;
+
 #ifdef DEBUG
   // In debug builds, warnings are enabled in chrome context if
   // javascript.options.strict.debug is true
   PRBool strictDebug = Preferences::GetBool(js_strict_debug_option_str);
   // Note this callback is also called from context's InitClasses thus we don't
   // need to enable this directly from InitContext
   if (strictDebug && (newDefaultJSOptions & JSOPTION_STRICT) == 0) {
     if (chromeWindow)
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -158,29 +158,29 @@ nsJSUtils::GetDynamicScriptGlobal(JSCont
 
 nsIScriptContext *
 nsJSUtils::GetDynamicScriptContext(JSContext *aContext)
 {
   return GetScriptContextFromJSContext(aContext);
 }
 
 PRUint64
-nsJSUtils::GetCurrentlyRunningCodeWindowID(JSContext *aContext)
+nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext)
 {
   if (!aContext)
     return 0;
 
-  PRUint64 windowID = 0;
+  PRUint64 innerWindowID = 0;
 
   JSObject *jsGlobal = JS_GetGlobalForScopeChain(aContext);
   if (jsGlobal) {
     nsIScriptGlobalObject *scriptGlobal = GetStaticScriptGlobal(aContext,
                                                                 jsGlobal);
     if (scriptGlobal) {
       nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(scriptGlobal);
       if (win)
-        windowID = win->GetOuterWindow()->WindowID();
+        innerWindowID = win->WindowID();
     }
   }
 
-  return windowID;
+  return innerWindowID;
 }
 
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -66,24 +66,24 @@ public:
   static nsIScriptContext *GetStaticScriptContext(JSContext* aContext,
                                                   JSObject* aObj);
 
   static nsIScriptGlobalObject *GetDynamicScriptGlobal(JSContext *aContext);
 
   static nsIScriptContext *GetDynamicScriptContext(JSContext *aContext);
 
   /**
-   * Retrieve the outer window ID based on the given JSContext.
+   * Retrieve the inner window ID based on the given JSContext.
    *
    * @param JSContext aContext
-   *        The JSContext from which you want to find the outer window ID.
+   *        The JSContext from which you want to find the inner window ID.
    *
-   * @returns PRUint64 the outer window ID.
+   * @returns PRUint64 the inner window ID.
    */
-  static PRUint64 GetCurrentlyRunningCodeWindowID(JSContext *aContext);
+  static PRUint64 GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext);
 };
 
 
 class nsDependentJSString : public nsDependentString
 {
 public:
   /**
    * In the case of string ids, getting the string's chars is infallible, so
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/threads/Makefile.in
@@ -0,0 +1,52 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
+#   Ben Turner <bent.mozilla@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH        = ../../..
+topsrcdir    = @top_srcdir@
+srcdir       = @srcdir@
+VPATH        = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE       = dom
+XPIDL_MODULE = dom_threads
+GRE_MODULE   = 1
+
+XPIDLSRCS    =  nsIDOMWorkers.idl
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/threads/nsIDOMWorkers.idl
@@ -0,0 +1,156 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * From http://www.whatwg.org/specs/web-workers/current-work
+ */
+
+#include "nsIDOMEvent.idl"
+#include "nsIDOMEventTarget.idl"
+
+interface nsIDOMEventListener;
+
+[scriptable, uuid(ab3725b8-3fca-40cc-a42c-92fb154ef01d)]
+interface nsIWorkerMessagePort : nsISupports
+{
+  void postMessage(/* in JSObject aMessage */);
+};
+
+[scriptable, uuid(508f2d49-e9a0-4fe8-bd33-321820173b4a)]
+interface nsIWorkerMessageEvent : nsIDOMEvent
+{
+  readonly attribute DOMString data;
+  readonly attribute DOMString origin;
+
+  readonly attribute nsISupports source;
+
+  void initMessageEvent(in DOMString aTypeArg,
+                        in boolean aCanBubbleArg,
+                        in boolean aCancelableArg,
+                        in DOMString aDataArg,
+                        in DOMString aOriginArg,
+                        in nsISupports aSourceArg);
+};
+
+[scriptable, uuid(73d82c1d-05de-49c9-a23b-7121ff09a67a)]
+interface nsIWorkerErrorEvent : nsIDOMEvent
+{
+  readonly attribute DOMString message;
+  readonly attribute DOMString filename;
+
+  readonly attribute unsigned long lineno;
+
+  void initErrorEvent(in DOMString aTypeArg,
+                      in boolean aCanBubbleArg,
+                      in boolean aCancelableArg,
+                      in DOMString aMessageArg,
+                      in DOMString aFilenameArg,
+                      in unsigned long aLinenoArg);
+};
+
+[scriptable, uuid(17a005c3-4f2f-4bb6-b169-c181fa6873de)]
+interface nsIWorkerLocation : nsISupports
+{
+  readonly attribute AUTF8String href;
+  readonly attribute AUTF8String protocol;
+  readonly attribute AUTF8String host;
+  readonly attribute AUTF8String hostname;
+  readonly attribute AUTF8String port;
+  readonly attribute AUTF8String pathname;
+  readonly attribute AUTF8String search;
+  readonly attribute AUTF8String hash;
+
+  AUTF8String toString();
+};
+
+[scriptable, uuid(74fb665a-e477-4ce2-b3c6-c58b1b28b6c3)]
+interface nsIWorkerNavigator : nsISupports
+{
+  readonly attribute DOMString appName;
+  readonly attribute DOMString appVersion;
+  readonly attribute DOMString platform;
+  readonly attribute DOMString userAgent;
+};
+
+[scriptable, uuid(c111e7d3-8044-4458-aa7b-637696ffb841)]
+interface nsIWorkerGlobalScope : nsISupports
+{
+  readonly attribute nsIWorkerGlobalScope self;
+  readonly attribute nsIWorkerNavigator navigator;
+  readonly attribute nsIWorkerLocation location;
+
+  attribute nsIDOMEventListener onerror;
+};
+
+[scriptable, uuid(5c55ea4b-e4ac-4ceb-bfeb-46bd5e521b8a)]
+interface nsIWorkerScope : nsIWorkerGlobalScope
+{
+  void postMessage(/* in JSObject aMessage */);
+
+  void close();
+
+  attribute nsIDOMEventListener onmessage;
+  attribute nsIDOMEventListener onclose;
+};
+
+[scriptable, builtinclass, uuid(b90b7561-b5e2-4545-84b0-280dbaaa94ea)]
+interface nsIAbstractWorker : nsIDOMEventTarget
+{
+  attribute nsIDOMEventListener onerror;
+};
+
+[scriptable, builtinclass, uuid(daf945c3-8d29-4724-8939-dd383f7d27a7)]
+interface nsIWorker : nsIAbstractWorker
+{
+  void postMessage(/* in JSObject aMessage */);
+
+  attribute nsIDOMEventListener onmessage;
+
+  void terminate();
+};
+
+[scriptable, uuid(cfc4bb32-ca83-4d58-9b6f-66f8054a333a)]
+interface nsIWorkerFactory : nsISupports
+{
+    nsIWorker newChromeWorker(/* in DOMString aScriptURL */);
+};
+
+%{ C++
+#define NS_WORKERFACTORY_CONTRACTID \
+"@mozilla.org/threads/workerfactory;1"
+%}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMThreadService.cpp
@@ -0,0 +1,1608 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
+ *   Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "jscntxt.h"
+
+#include "nsDOMThreadService.h"
+
+// Interfaces
+#include "nsIComponentManager.h"
+#include "nsIConsoleService.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIEventTarget.h"
+#include "nsIJSContextStack.h"
+#include "nsIJSRuntimeService.h"
+#include "nsIObserverService.h"
+#include "nsIScriptError.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIServiceManager.h"
+#include "nsISupportsPriority.h"
+#include "nsIThreadPool.h"
+#include "nsIXPConnect.h"
+#include "nsPIDOMWindow.h"
+
+// Other includes
+#include "nsAutoPtr.h"
+#include "nsContentUtils.h"
+#include "nsDeque.h"
+#include "nsGlobalWindow.h"
+#include "nsIClassInfoImpl.h"
+#include "nsStringBuffer.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMCID.h"
+#include "nsXPCOMCIDInternal.h"
+#include "pratom.h"
+#include "prthread.h"
+#include "mozilla/Preferences.h"
+
+// DOMWorker includes
+#include "nsDOMWorker.h"
+#include "nsDOMWorkerEvents.h"
+#include "nsDOMWorkerMacros.h"
+#include "nsDOMWorkerMessageHandler.h"
+#include "nsDOMWorkerPool.h"
+#include "nsDOMWorkerSecurityManager.h"
+#include "nsDOMWorkerTimeout.h"
+
+using namespace mozilla;
+
+#ifdef PR_LOGGING
+PRLogModuleInfo *gDOMThreadsLog = nsnull;
+#endif
+#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
+
+// The maximum number of threads in the internal thread pool
+#define THREADPOOL_MAX_THREADS 3
+
+PR_STATIC_ASSERT(THREADPOOL_MAX_THREADS >= 1);
+
+// The maximum number of idle threads in the internal thread pool
+#define THREADPOOL_IDLE_THREADS 3
+
+PR_STATIC_ASSERT(THREADPOOL_MAX_THREADS >= THREADPOOL_IDLE_THREADS);
+
+// As we suspend threads for various reasons (navigating away from the page,
+// loading scripts, etc.) we open another slot in the thread pool for another
+// worker to use. We can't do this forever so we set an absolute cap on the
+// number of threads we'll allow to prevent DOS attacks.
+#define THREADPOOL_THREAD_CAP 20
+
+PR_STATIC_ASSERT(THREADPOOL_THREAD_CAP >= THREADPOOL_MAX_THREADS);
+
+// A "bad" value for the NSPR TLS functions.
+#define BAD_TLS_INDEX (PRUintn)-1
+
+// Easy access for static functions. No reference here.
+static nsDOMThreadService* gDOMThreadService = nsnull;
+
+// These pointers actually carry references and must be released.
+static nsIObserverService* gObserverService = nsnull;
+static nsIJSRuntimeService* gJSRuntimeService = nsnull;
+static nsIThreadJSContextStack* gThreadJSContextStack = nsnull;
+static nsIXPCSecurityManager* gWorkerSecurityManager = nsnull;
+
+PRUintn gJSContextIndex = BAD_TLS_INDEX;
+
+static const char* sPrefsToWatch[] = {
+  "dom.max_script_run_time"
+};
+
+// The length of time the close handler is allowed to run in milliseconds.
+static PRUint32 gWorkerCloseHandlerTimeoutMS = 10000;
+
+/**
+ * Simple class to automatically destroy a JSContext to make error handling
+ * easier.
+ */
+class JSAutoContextDestroyer
+{
+public:
+  JSAutoContextDestroyer(JSContext* aCx)
+  : mCx(aCx) { }
+
+  ~JSAutoContextDestroyer() {
+    if (mCx) {
+      nsContentUtils::XPConnect()->ReleaseJSContext(mCx, PR_TRUE);
+    }
+  }
+
+  operator JSContext*() {
+    return mCx;
+  }
+
+  JSContext* forget() {
+    JSContext* cx = mCx;
+    mCx = nsnull;
+    return cx;
+  }
+
+private:
+  JSContext* mCx;
+};
+
+class nsDestroyJSContextRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsDestroyJSContextRunnable(JSContext* aCx)
+  : mCx(aCx)
+  {
+    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+    NS_ASSERTION(aCx, "Null pointer!");
+    NS_ASSERTION(!JS_GetGlobalObject(aCx), "Should not have a global!");
+
+    // We're removing this context from this thread. Let the JS engine know.
+    JS_ClearContextThread(aCx);
+  }
+
+  NS_IMETHOD Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+    // We're about to use this context on this thread. Let the JS engine know.
+    if (!!JS_SetContextThread(mCx)) {
+      NS_WARNING("JS_SetContextThread failed!");
+    }
+
+    if (nsContentUtils::XPConnect()) {
+      nsContentUtils::XPConnect()->ReleaseJSContext(mCx, PR_TRUE);
+    }
+    else {
+      NS_WARNING("Failed to release JSContext!");
+    }
+
+    return NS_OK;
+  }
+
+private:
+  JSContext* mCx;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDestroyJSContextRunnable, nsIRunnable)
+
+/**
+ * This class is used as to post an error to the worker's outer handler.
+ */
+class nsReportErrorRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsReportErrorRunnable(nsDOMWorker* aWorker,
+                        nsIScriptError* aScriptError)
+  : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()),
+    mScriptError(aScriptError) {
+      NS_ASSERTION(aScriptError, "Null pointer!");
+    }
+
+  NS_IMETHOD Run() {
+    if (mWorker->IsCanceled()) {
+      return NS_OK;
+    }
+
+#ifdef DEBUG
+    {
+      nsRefPtr<nsDOMWorker> parent = mWorker->GetParent();
+      if (NS_IsMainThread()) {
+        NS_ASSERTION(!parent, "Shouldn't have a parent on the main thread!");
+      }
+      else {
+        NS_ASSERTION(parent, "Should have a parent!");
+
+        JSContext* cx = nsDOMThreadService::get()->GetCurrentContext();
+        NS_ASSERTION(cx, "No context!");
+
+        nsDOMWorker* currentWorker = (nsDOMWorker*)JS_GetContextPrivate(cx);
+        NS_ASSERTION(currentWorker == parent, "Wrong worker!");
+      }
+    }
+#endif
+
+    NS_NAMED_LITERAL_STRING(errorStr, "error");
+
+    nsresult rv;
+
+    if (mWorker->HasListeners(errorStr)) {
+      // Construct the error event.
+      nsString message;
+      rv = mScriptError->GetErrorMessage(message);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsString filename;
+      rv = mScriptError->GetSourceName(filename);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      PRUint32 lineno;
+      rv = mScriptError->GetLineNumber(&lineno);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsRefPtr<nsDOMWorkerErrorEvent> event(new nsDOMWorkerErrorEvent());
+      NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
+
+      rv = event->InitErrorEvent(errorStr, PR_FALSE, PR_TRUE, message,
+                                 filename, lineno);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      event->SetTarget(static_cast<nsDOMWorkerMessageHandler*>(mWorker));
+
+      PRBool stopPropagation = PR_FALSE;
+      rv = mWorker->DispatchEvent(static_cast<nsDOMWorkerEvent*>(event),
+                                  &stopPropagation);
+      if (NS_SUCCEEDED(rv) && stopPropagation) {
+        return NS_OK;
+      }
+    }
+
+    nsRefPtr<nsDOMWorker> parent = mWorker->GetParent();
+    if (!parent) {
+      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+      nsCOMPtr<nsIConsoleService> consoleService =
+        do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+      if (consoleService) {
+        rv = consoleService->LogMessage(mScriptError);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+      return NS_OK;
+    }
+
+    nsRefPtr<nsReportErrorRunnable> runnable =
+      new nsReportErrorRunnable(parent, mScriptError);
+    if (runnable) {
+      nsRefPtr<nsDOMWorker> grandparent = parent->GetParent();
+      rv = grandparent ?
+           nsDOMThreadService::get()->Dispatch(grandparent, runnable) :
+           NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<nsDOMWorker> mWorker;
+  nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
+  nsCOMPtr<nsIScriptError> mScriptError;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsReportErrorRunnable, nsIRunnable)
+
+/**
+ * Used to post an expired timeout to the correct worker.
+ */
+class nsDOMWorkerTimeoutRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsDOMWorkerTimeoutRunnable(nsDOMWorkerTimeout* aTimeout)
+  : mTimeout(aTimeout) { }
+
+  NS_IMETHOD Run() {
+    return mTimeout->Run();
+  }
+protected:
+  nsRefPtr<nsDOMWorkerTimeout> mTimeout;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerTimeoutRunnable, nsIRunnable)
+
+class nsDOMWorkerKillRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsDOMWorkerKillRunnable(nsDOMWorker* aWorker)
+  : mWorker(aWorker) { }
+
+  NS_IMETHOD Run() {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    mWorker->Kill();
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<nsDOMWorker> mWorker;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerKillRunnable, nsIRunnable)
+
+/**
+ * This class exists to solve a particular problem: Calling Dispatch on a
+ * thread pool will always create a new thread to service the runnable as long
+ * as the thread limit has not been reached. Since our DOM workers can only be
+ * accessed by one thread at a time we could end up spawning a new thread that
+ * does nothing but wait initially. There is no way to control this behavior
+ * currently so we cheat by using a runnable that emulates a thread. The
+ * nsDOMThreadService's monitor protects the queue of events.
+ */
+class nsDOMWorkerRunnable : public nsIRunnable
+{
+  friend class nsDOMThreadService;
+
+public:
+  NS_DECL_ISUPPORTS
+
+  nsDOMWorkerRunnable(nsDOMWorker* aWorker)
+  : mWorker(aWorker), mCloseTimeoutInterval(0), mKillWorkerWhenDone(PR_FALSE) {
+  }
+
+  virtual ~nsDOMWorkerRunnable() {
+    ClearQueue();
+  }
+
+  void PutRunnable(nsIRunnable* aRunnable,
+                   PRIntervalTime aTimeoutInterval,
+                   PRBool aClearQueue) {
+    NS_ASSERTION(aRunnable, "Null pointer!");
+
+    gDOMThreadService->mReentrantMonitor.AssertCurrentThreadIn();
+
+    if (NS_LIKELY(!aTimeoutInterval)) {
+      NS_ADDREF(aRunnable);
+      mRunnables.Push(aRunnable);
+    }
+    else {
+      NS_ASSERTION(!mCloseRunnable, "More than one close runnable?!");
+      if (aClearQueue) {
+        ClearQueue();
+      }
+      mCloseRunnable = aRunnable;
+      mCloseTimeoutInterval = aTimeoutInterval;
+      mKillWorkerWhenDone = PR_TRUE;
+    }
+  }
+
+  void SetCloseRunnableTimeout(PRIntervalTime aTimeoutInterval) {
+    NS_ASSERTION(aTimeoutInterval, "No timeout specified!");
+    NS_ASSERTION(aTimeoutInterval!= PR_INTERVAL_NO_TIMEOUT, "Bad timeout!");
+
+    // No need to enter the monitor because we should already be in it.
+
+    NS_ASSERTION(mWorker->GetExpirationTime() == PR_INTERVAL_NO_TIMEOUT,
+                 "Asked to set timeout on a runnable with no close handler!");
+
+    // This may actually overflow but we don't care - the worst that could
+    // happen is that the close handler could run for a slightly different
+    // amount of time and the spec leaves the time up to us anyway.
+    mWorker->SetExpirationTime(PR_IntervalNow() + aTimeoutInterval);
+  }
+
+  NS_IMETHOD Run() {
+    NS_ASSERTION(!NS_IsMainThread(),
+                 "This should *never* run on the main thread!");
+
+    // This must have been set up by the thread service
+    NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
+
+    // Make sure we have a JSContext to run everything on.
+    JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
+    if (!cx) {
+        NS_ERROR("nsDOMThreadService didn't give us a context! Are we out of memory?");
+        return NS_ERROR_FAILURE;
+    }
+
+    NS_ASSERTION(!JS_GetGlobalObject(cx), "Shouldn't have a global!");
+
+    if (mWorker->IsPrivileged()) {
+      JS_SetVersion(cx, JSVERSION_LATEST);
+    }
+    else {
+      JS_SetVersion(cx, JSVERSION_DEFAULT);
+    }
+
+    JS_SetContextPrivate(cx, mWorker);
+
+    // Go ahead and trigger the operation callback for this context before we
+    // try to run any JS. That way we'll be sure to cancel or suspend as soon as
+    // possible if the compilation takes too long.
+    JS_TriggerOperationCallback(cx);
+
+    PRBool killWorkerWhenDone;
+    {
+      nsLazyAutoRequest ar;
+      JSAutoEnterCompartment ac;
+
+      // Tell the worker which context it will be using
+      if (mWorker->SetGlobalForContext(cx, &ar, &ac)) {
+        NS_ASSERTION(ar.entered(), "SetGlobalForContext must enter request on success");
+        NS_ASSERTION(ac.entered(), "SetGlobalForContext must enter compartment on success");
+
+        RunQueue(cx, &killWorkerWhenDone);
+
+        // Remove the global object from the context so that it might be garbage
+        // collected.
+        JS_SetGlobalObject(cx, NULL);
+        JS_SetContextPrivate(cx, NULL);
+      }
+      else {
+        NS_ASSERTION(!ar.entered(), "SetGlobalForContext must not enter request on failure");
+        NS_ASSERTION(!ac.entered(), "SetGlobalForContext must not enter compartment on failure");
+
+        {
+          // Code in XPConnect assumes that the context's global object won't be
+          // replaced outside of a request.
+          JSAutoRequest ar2(cx);
+
+          // This is usually due to a parse error in the worker script...
+          JS_SetGlobalObject(cx, NULL);
+          JS_SetContextPrivate(cx, NULL);
+        }
+
+        ReentrantMonitorAutoEnter mon(gDOMThreadService->mReentrantMonitor);
+        killWorkerWhenDone = mKillWorkerWhenDone;
+        gDOMThreadService->WorkerComplete(this);
+        mon.NotifyAll();
+      }
+    }
+
+    if (killWorkerWhenDone) {
+      nsCOMPtr<nsIRunnable> runnable = new nsDOMWorkerKillRunnable(mWorker);
+      NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+
+      nsresult rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    return NS_OK;
+  }
+
+protected:
+  void ClearQueue() {
+    nsCOMPtr<nsIRunnable> runnable;
+    while ((runnable = dont_AddRef((nsIRunnable*)mRunnables.PopFront()))) {
+      // Loop until all the runnables are dead.
+    }
+  }
+
+  void RunQueue(JSContext* aCx, PRBool* aCloseRunnableSet) {
+    while (1) {
+      nsCOMPtr<nsIRunnable> runnable;
+      {
+        ReentrantMonitorAutoEnter mon(gDOMThreadService->mReentrantMonitor);
+
+        runnable = dont_AddRef((nsIRunnable*)mRunnables.PopFront());
+
+        if (!runnable && mCloseRunnable) {
+          PRIntervalTime expirationTime;
+          if (mCloseTimeoutInterval == PR_INTERVAL_NO_TIMEOUT) {
+            expirationTime = mCloseTimeoutInterval;
+          }
+          else {
+            expirationTime = PR_IntervalNow() + mCloseTimeoutInterval;
+          }
+          mWorker->SetExpirationTime(expirationTime);
+
+          runnable.swap(mCloseRunnable);
+        }
+
+        if (!runnable || mWorker->IsCanceled()) {
+#ifdef PR_LOGGING
+          if (mWorker->IsCanceled()) {
+            LOG(("Bailing out of run loop for canceled worker[0x%p]",
+                 static_cast<void*>(mWorker.get())));
+          }
+#endif
+          *aCloseRunnableSet = mKillWorkerWhenDone;
+          gDOMThreadService->WorkerComplete(this);
+          mon.NotifyAll();
+          return;
+        }
+      }
+
+      // Clear out any old cruft hanging around in the regexp statics.
+      if (JSObject *global = JS_GetGlobalObject(aCx))
+          JS_ClearRegExpStatics(aCx, global);
+
+      runnable->Run();
+    }
+    NS_NOTREACHED("Shouldn't ever get here!");
+  }
+
+  // Set at construction
+  nsRefPtr<nsDOMWorker> mWorker;
+
+  // Protected by mReentrantMonitor
+  nsDeque mRunnables;
+  nsCOMPtr<nsIRunnable> mCloseRunnable;
+  PRIntervalTime mCloseTimeoutInterval;
+  PRBool mKillWorkerWhenDone;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerRunnable, nsIRunnable)
+
+/*******************************************************************************
+ * JS environment function and callbacks
+ */
+
+JSBool
+DOMWorkerOperationCallback(JSContext* aCx)
+{
+  nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
+  NS_ASSERTION(worker, "This must never be null!");
+
+  PRBool canceled = worker->IsCanceled();
+  if (!canceled && worker->IsSuspended()) {
+    JSAutoSuspendRequest suspended(aCx);
+
+    // Since we're going to block this thread we should open up a new thread
+    // in the thread pool for other workers. Must check the return value to
+    // make sure we don't decrement when we failed.
+    PRBool extraThreadAllowed =
+      NS_SUCCEEDED(gDOMThreadService->ChangeThreadPoolMaxThreads(1));
+
+    // Flush JIT caches now before suspending to avoid holding memory that we
+    // are not going to use.
+    JS_FlushCaches(aCx);
+
+    for (;;) {
+      ReentrantMonitorAutoEnter mon(worker->Pool()->GetReentrantMonitor());
+
+      // There's a small chance that the worker was canceled after our check
+      // above in which case we shouldn't wait here. We're guaranteed not to
+      // race here because the pool reenters its monitor after canceling each
+      // worker in order to notify its condition variable.
+      canceled = worker->IsCanceled();
+      if (!canceled && worker->IsSuspended()) {
+        mon.Wait();
+      }
+      else {
+        break;
+      }
+    }
+
+    if (extraThreadAllowed) {
+      gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
+    }
+  }
+
+  if (canceled) {
+    LOG(("Forcefully killing JS for worker [0x%p]",
+         static_cast<void*>(worker)));
+    // Kill execution of the currently running JS.
+    JS_ClearPendingException(aCx);
+    return JS_FALSE;
+  }
+  return JS_TRUE;
+}
+
+void
+DOMWorkerErrorReporter(JSContext* aCx,
+                       const char* aMessage,
+                       JSErrorReport* aReport)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Huh?!");
+
+  nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
+
+  if (worker->IsCanceled()) {
+    // We don't want to report errors from canceled workers. It's very likely
+    // that we only returned an error in the first place because the worker was
+    // already canceled.
+    return;
+  }
+
+  if (worker->mErrorHandlerRecursionCount == 2) {
+    // We've somehow ended up in a recursive onerror loop. Bail out.
+    return;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsIScriptError> scriptError;
+
+  {
+    // CreateInstance will lock, make sure we suspend our request!
+    JSAutoSuspendRequest ar(aCx);
+
+    scriptError = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
+  }
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsCOMPtr<nsIScriptError2> scriptError2(do_QueryInterface(scriptError));
+
+  nsAutoString message, filename, line;
+  PRUint32 lineNumber, columnNumber, flags, errorNumber;
+
+  if (aReport) {
+    if (aReport->ucmessage) {
+      message.Assign(reinterpret_cast<const PRUnichar*>(aReport->ucmessage));
+    }
+    filename.AssignWithConversion(aReport->filename);
+    line.Assign(reinterpret_cast<const PRUnichar*>(aReport->uclinebuf));
+    lineNumber = aReport->lineno;
+    columnNumber = aReport->uctokenptr - aReport->uclinebuf;
+    flags = aReport->flags;
+    errorNumber = aReport->errorNumber;
+  }
+  else {
+    lineNumber = columnNumber = errorNumber = 0;
+    flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
+  }
+
+  if (message.IsEmpty()) {
+    message.AssignWithConversion(aMessage);
+  }
+
+  rv = scriptError2->InitWithWindowID(message.get(), filename.get(), line.get(),
+                                      lineNumber, columnNumber, flags,
+                                      "DOM Worker javascript",
+                                      worker->Pool()->WindowID());
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  // Don't call the error handler if we're out of stack space.
+  if (errorNumber != JSMSG_OVER_RECURSED) {
+    // Try the onerror handler for the worker's scope.
+    nsRefPtr<nsDOMWorkerScope> scope = worker->GetInnerScope();
+    NS_ASSERTION(scope, "Null scope!");
+
+    PRBool hasListeners = scope->HasListeners(NS_LITERAL_STRING("error"));
+    if (hasListeners) {
+      nsRefPtr<nsDOMWorkerErrorEvent> event(new nsDOMWorkerErrorEvent());
+      if (event) {
+        rv = event->InitErrorEvent(NS_LITERAL_STRING("error"), PR_FALSE,
+                                   PR_TRUE, nsDependentString(message),
+                                   filename, lineNumber);
+        if (NS_SUCCEEDED(rv)) {
+          event->SetTarget(scope);
+
+          NS_ASSERTION(worker->mErrorHandlerRecursionCount >= 0,
+                       "Bad recursion count logic!");
+          worker->mErrorHandlerRecursionCount++;
+
+          PRBool preventDefaultCalled = PR_FALSE;
+          scope->DispatchEvent(static_cast<nsDOMWorkerEvent*>(event),
+                               &preventDefaultCalled);
+
+          worker->mErrorHandlerRecursionCount--;
+
+          if (preventDefaultCalled) {
+            return;
+          }
+        }
+      }
+    }
+  }
+
+  // Still unhandled, fire at the onerror handler on the worker.
+  nsCOMPtr<nsIRunnable> runnable =
+    new nsReportErrorRunnable(worker, scriptError);
+  NS_ENSURE_TRUE(runnable,);
+
+  nsRefPtr<nsDOMWorker> parent = worker->GetParent();
+
+  // If this worker has a parent then we need to send the message through the
+  // thread service to be run on the parent's thread. Otherwise it is a
+  // top-level worker and we send the message to the main thread.
+  rv = parent ? nsDOMThreadService::get()->Dispatch(parent, runnable)
+              : NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+}
+
+/*******************************************************************************
+ * nsDOMThreadService
+ */
+
+nsDOMThreadService::nsDOMThreadService()
+: mReentrantMonitor("nsDOMThreadServer.mReentrantMonitor"),
+  mNavigatorStringsLoaded(PR_FALSE)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+#ifdef PR_LOGGING
+  if (!gDOMThreadsLog) {
+    gDOMThreadsLog = PR_NewLogModule("nsDOMThreads");
+  }
+#endif
+  LOG(("Initializing DOM Thread service"));
+}
+
+nsDOMThreadService::~nsDOMThreadService()
+{
+  LOG(("DOM Thread service destroyed"));
+
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  Cleanup();
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMThreadService, nsIEventTarget,
+                                                  nsIObserver,
+                                                  nsIThreadPoolListener)
+
+nsresult
+nsDOMThreadService::Init()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!gDOMThreadService, "Only one instance should ever be created!");
+
+  nsresult rv;
+  nsCOMPtr<nsIObserverService> obs =
+    do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  obs.forget(&gObserverService);
+
+  RegisterPrefCallbacks();
+
+  mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mThreadPool->SetListener(this);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mThreadPool->SetThreadLimit(THREADPOOL_MAX_THREADS);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mThreadPool->SetIdleThreadLimit(THREADPOOL_IDLE_THREADS);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRBool success = mWorkersInProgress.Init();
+  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+  success = mPools.Init();
+  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+  success = mThreadsafeContractIDs.Init();
+  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+  success = mJSContexts.SetCapacity(THREADPOOL_THREAD_CAP);
+  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+  nsCOMPtr<nsIJSRuntimeService>
+    runtimeSvc(do_GetService("@mozilla.org/js/xpc/RuntimeService;1"));
+  NS_ENSURE_TRUE(runtimeSvc, NS_ERROR_FAILURE);
+  runtimeSvc.forget(&gJSRuntimeService);
+
+  nsCOMPtr<nsIThreadJSContextStack>
+    contextStack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
+  NS_ENSURE_TRUE(contextStack, NS_ERROR_FAILURE);
+  contextStack.forget(&gThreadJSContextStack);
+
+  nsCOMPtr<nsIXPCSecurityManager> secMan(new nsDOMWorkerSecurityManager());
+  NS_ENSURE_TRUE(secMan, NS_ERROR_OUT_OF_MEMORY);
+  secMan.forget(&gWorkerSecurityManager);
+
+  if (gJSContextIndex == BAD_TLS_INDEX &&
+      PR_NewThreadPrivateIndex(&gJSContextIndex, NULL) != PR_SUCCESS) {
+    NS_ERROR("PR_NewThreadPrivateIndex failed!");
+    gJSContextIndex = BAD_TLS_INDEX;
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+/* static */
+already_AddRefed<nsDOMThreadService>
+nsDOMThreadService::GetOrInitService()
+{
+  if (!gDOMThreadService) {
+    nsRefPtr<nsDOMThreadService> service = new nsDOMThreadService();
+    NS_ENSURE_TRUE(service, nsnull);
+
+    nsresult rv = service->Init();
+    NS_ENSURE_SUCCESS(rv, nsnull);
+
+    service.swap(gDOMThreadService);
+  }
+
+  nsRefPtr<nsDOMThreadService> service(gDOMThreadService);
+  return service.forget();
+}
+
+/* static */
+nsDOMThreadService*
+nsDOMThreadService::get()
+{
+  return gDOMThreadService;
+}
+
+/* static */
+JSContext*
+nsDOMThreadService::GetCurrentContext()
+{
+  JSContext* cx;
+
+  if (NS_IsMainThread()) {
+    nsresult rv = ThreadJSContextStack()->GetSafeJSContext(&cx);
+    NS_ENSURE_SUCCESS(rv, nsnull);
+  }
+  else {
+    NS_ENSURE_TRUE(gJSContextIndex, nsnull);
+    cx = static_cast<JSContext*>(PR_GetThreadPrivate(gJSContextIndex));
+  }
+
+  return cx;
+}
+
+/* static */
+void
+nsDOMThreadService::Shutdown()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_IF_RELEASE(gDOMThreadService);
+}
+
+void
+nsDOMThreadService::Cleanup()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // This will either be called at 'xpcom-shutdown' or earlier if the call to
+  // Init fails somehow. We can therefore assume that all services will still
+  // be available here.
+
+  // Cancel all workers that weren't tied to a window.
+  CancelWorkersForGlobal(nsnull);
+
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    NS_ASSERTION(!mPools.Count(), "Live workers left!");
+    mPools.Clear();
+
+    NS_ASSERTION(!mSuspendedWorkers.Length(), "Suspended workers left!");
+    mSuspendedWorkers.Clear();
+  }
+
+  if (gObserverService) {
+    gObserverService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+    NS_RELEASE(gObserverService);
+
+    UnregisterPrefCallbacks();
+  }
+
+  // The thread pool holds a circular reference to this service through its
+  // listener. We must shut down the thread pool manually to break this cycle.
+  if (mThreadPool) {
+    mThreadPool->Shutdown();
+    mThreadPool = nsnull;
+  }
+
+  // Need to force a GC so that all of our workers get cleaned up.
+  if (gThreadJSContextStack) {
+    JSContext* safeContext;
+    if (NS_SUCCEEDED(gThreadJSContextStack->GetSafeJSContext(&safeContext))) {
+      JS_GC(safeContext);
+    }
+    NS_RELEASE(gThreadJSContextStack);
+  }
+
+  // These must be released after the thread pool is shut down.
+  NS_IF_RELEASE(gJSRuntimeService);
+  NS_IF_RELEASE(gWorkerSecurityManager);
+}
+
+nsresult
+nsDOMThreadService::Dispatch(nsDOMWorker* aWorker,
+                             nsIRunnable* aRunnable,
+                             PRIntervalTime aTimeoutInterval,
+                             PRBool aClearQueue)
+{
+  NS_ASSERTION(aWorker, "Null pointer!");
+  NS_ASSERTION(aRunnable, "Null pointer!");
+
+  if (!mThreadPool) {
+    // This can happen from a nsDOMWorker::Finalize call after the thread pool
+    // has been shutdown. It should never be possible off the main thread.
+    NS_ASSERTION(NS_IsMainThread(),
+                 "This should be impossible on a non-main thread!");
+    return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
+  }
+
+  // Don't accept the runnable if the worker's close handler has been triggered
+  // (unless, of course, this is the close runnable as indicated by the non-0
+  // timeout value).
+  if (aWorker->IsClosing() && !aTimeoutInterval) {
+    LOG(("Will not dispatch runnable [0x%p] for closing worker [0x%p]",
+         static_cast<void*>(aRunnable), static_cast<void*>(aWorker)));
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsRefPtr<nsDOMWorkerRunnable> workerRunnable;
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    if (mWorkersInProgress.Get(aWorker, getter_AddRefs(workerRunnable))) {
+      workerRunnable->PutRunnable(aRunnable, aTimeoutInterval, aClearQueue);
+      return NS_OK;
+    }
+
+    workerRunnable = new nsDOMWorkerRunnable(aWorker);
+    NS_ENSURE_TRUE(workerRunnable, NS_ERROR_OUT_OF_MEMORY);
+
+    workerRunnable->PutRunnable(aRunnable, aTimeoutInterval, PR_FALSE);
+
+    PRBool success = mWorkersInProgress.Put(aWorker, workerRunnable);
+    NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  nsresult rv = mThreadPool->Dispatch(workerRunnable, NS_DISPATCH_NORMAL);
+
+  // XXX This is a mess and it could probably be removed once we have an
+  // infallible malloc implementation.
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch runnable to thread pool!");
+
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    // We exited the monitor after inserting the runnable into the table so make
+    // sure we're removing the right one!
+    nsRefPtr<nsDOMWorkerRunnable> tableRunnable;
+    if (mWorkersInProgress.Get(aWorker, getter_AddRefs(tableRunnable)) &&
+        workerRunnable == tableRunnable) {
+      mWorkersInProgress.Remove(aWorker);
+
+      // And don't forget to tell anyone who's waiting.
+      mon.NotifyAll();
+    }
+
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+nsDOMThreadService::SetWorkerTimeout(nsDOMWorker* aWorker,
+                                     PRIntervalTime aTimeoutInterval)
+{
+  NS_ASSERTION(aWorker, "Null pointer!");
+  NS_ASSERTION(aTimeoutInterval, "No timeout specified!");
+
+  NS_ASSERTION(mThreadPool, "Dispatch called after 'xpcom-shutdown'!");
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  nsRefPtr<nsDOMWorkerRunnable> workerRunnable;
+  if (mWorkersInProgress.Get(aWorker, getter_AddRefs(workerRunnable))) {
+    workerRunnable->SetCloseRunnableTimeout(aTimeoutInterval);
+  }
+}
+
+void
+nsDOMThreadService::WorkerComplete(nsDOMWorkerRunnable* aRunnable)
+{
+  mReentrantMonitor.AssertCurrentThreadIn();
+
+#ifdef DEBUG
+  nsRefPtr<nsDOMWorker>& debugWorker = aRunnable->mWorker;
+
+  nsRefPtr<nsDOMWorkerRunnable> runnable;
+  NS_ASSERTION(mWorkersInProgress.Get(debugWorker, getter_AddRefs(runnable)) &&
+               runnable == aRunnable,
+               "Removing a worker that isn't in our hashtable?!");
+#endif
+
+  mWorkersInProgress.Remove(aRunnable->mWorker);
+}
+
+PRBool
+nsDOMThreadService::QueueSuspendedWorker(nsDOMWorkerRunnable* aRunnable)
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+#ifdef DEBUG
+    {
+      // Make sure that the runnable is in mWorkersInProgress.
+      nsRefPtr<nsDOMWorkerRunnable> current;
+      mWorkersInProgress.Get(aRunnable->mWorker, getter_AddRefs(current));
+      NS_ASSERTION(current == aRunnable, "Something crazy wrong here!");
+    }
+#endif
+
+  return mSuspendedWorkers.AppendElement(aRunnable) ? PR_TRUE : PR_FALSE;
+}
+
+/* static */
+JSContext*
+nsDOMThreadService::CreateJSContext()
+{
+  JSRuntime* rt;
+  gJSRuntimeService->GetRuntime(&rt);
+  NS_ENSURE_TRUE(rt, nsnull);
+
+  JSAutoContextDestroyer cx(JS_NewContext(rt, 8192));
+  NS_ENSURE_TRUE(cx, nsnull);
+
+  JS_SetErrorReporter(cx, DOMWorkerErrorReporter);
+
+  JS_SetOperationCallback(cx, DOMWorkerOperationCallback);
+
+  static JSSecurityCallbacks securityCallbacks = {
+    nsDOMWorkerSecurityManager::JSCheckAccess,
+    nsDOMWorkerSecurityManager::JSTranscodePrincipals,
+    nsDOMWorkerSecurityManager::JSFindPrincipal
+  };
+  JS_SetContextSecurityCallbacks(cx, &securityCallbacks);
+
+  JS_ClearContextDebugHooks(cx);
+
+  nsresult rv = nsContentUtils::XPConnect()->
+    SetSecurityManagerForJSContext(cx, gWorkerSecurityManager, 0);
+  NS_ENSURE_SUCCESS(rv, nsnull);
+
+  JS_SetNativeStackQuota(cx, 256*1024);
+
+  JS_SetOptions(cx,
+    JS_GetOptions(cx) | JSOPTION_METHODJIT | JSOPTION_JIT | JSOPTION_PROFILING);
+  JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 1 * 1024 * 1024);
+
+  return cx.forget();
+}
+
+already_AddRefed<nsDOMWorkerPool>
+nsDOMThreadService::GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
+                                     PRBool aRemove)
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  nsRefPtr<nsDOMWorkerPool> pool;
+  mPools.Get(aGlobalObject, getter_AddRefs(pool));
+
+  if (aRemove) {
+    mPools.Remove(aGlobalObject);
+  }
+
+  return pool.forget();
+}
+
+void
+nsDOMThreadService::TriggerOperationCallbackForPool(nsDOMWorkerPool* aPool)
+{
+  mReentrantMonitor.AssertCurrentThreadIn();
+
+  // See if we need to trigger the operation callback on any currently running
+  // contexts.
+  PRUint32 contextCount = mJSContexts.Length();
+  for (PRUint32 index = 0; index < contextCount; index++) {
+    JSContext*& cx = mJSContexts[index];
+    nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(cx);
+    if (worker && worker->Pool() == aPool) {
+      JS_TriggerOperationCallback(cx);
+    }
+  }
+}
+
+void
+nsDOMThreadService::RescheduleSuspendedWorkerForPool(nsDOMWorkerPool* aPool)
+{
+  mReentrantMonitor.AssertCurrentThreadIn();
+
+  PRUint32 count = mSuspendedWorkers.Length();
+  if (!count) {
+    // Nothing to do here.
+    return;
+  }
+
+  nsTArray<nsDOMWorkerRunnable*> others(count);
+
+  for (PRUint32 index = 0; index < count; index++) {
+    nsDOMWorkerRunnable* runnable = mSuspendedWorkers[index];
+
+#ifdef DEBUG
+    {
+      // Make sure that the runnable never left mWorkersInProgress.
+      nsRefPtr<nsDOMWorkerRunnable> current;
+      mWorkersInProgress.Get(runnable->mWorker, getter_AddRefs(current));
+      NS_ASSERTION(current == runnable, "Something crazy wrong here!");
+    }
+#endif
+
+    if (runnable->mWorker->Pool() == aPool) {
+#ifdef DEBUG
+      nsresult rv =
+#endif
+      mThreadPool->Dispatch(runnable, NS_DISPATCH_NORMAL);
+      NS_ASSERTION(NS_SUCCEEDED(rv), "This shouldn't ever fail!");
+    }
+    else {
+      others.AppendElement(runnable);
+    }
+  }
+
+  mSuspendedWorkers.SwapElements(others);
+}
+
+void
+nsDOMThreadService::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
+{
+  nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_TRUE);
+  if (pool) {
+    pool->Cancel();
+
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    TriggerOperationCallbackForPool(pool);
+    RescheduleSuspendedWorkerForPool(pool);
+  }
+}
+
+void
+nsDOMThreadService::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
+{
+  NS_ASSERTION(aGlobalObject, "Null pointer!");
+
+  nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
+  if (pool) {
+    pool->Suspend();
+
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    TriggerOperationCallbackForPool(pool);
+  }
+}
+
+void
+nsDOMThreadService::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
+{
+  NS_ASSERTION(aGlobalObject, "Null pointer!");
+
+  nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
+  if (pool) {
+    pool->Resume();
+
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    TriggerOperationCallbackForPool(pool);
+    RescheduleSuspendedWorkerForPool(pool);
+  }
+}
+
+void
+nsDOMThreadService::NoteEmptyPool(nsDOMWorkerPool* aPool)
+{
+  NS_ASSERTION(aPool, "Null pointer!");
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  mPools.Remove(aPool->ScriptGlobalObject());
+}
+
+void
+nsDOMThreadService::TimeoutReady(nsDOMWorkerTimeout* aTimeout)
+{
+  nsRefPtr<nsDOMWorkerTimeoutRunnable> runnable =
+    new nsDOMWorkerTimeoutRunnable(aTimeout);
+  NS_ENSURE_TRUE(runnable,);
+
+  Dispatch(aTimeout->GetWorker(), runnable);
+}
+
+nsresult
+nsDOMThreadService::ChangeThreadPoolMaxThreads(PRInt16 aDelta)
+{
+  NS_ENSURE_ARG(aDelta == 1 || aDelta == -1);
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  PRUint32 currentThreadCount;
+  nsresult rv = mThreadPool->GetThreadLimit(&currentThreadCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRInt32 newThreadCount = (PRInt32)currentThreadCount + (PRInt32)aDelta;
+  NS_ASSERTION(newThreadCount >= THREADPOOL_MAX_THREADS,
+               "Can't go below initial thread count!");
+
+  if (newThreadCount > THREADPOOL_THREAD_CAP) {
+    NS_WARNING("Thread pool cap reached!");
+    return NS_ERROR_FAILURE;
+  }
+
+  rv = mThreadPool->SetThreadLimit((PRUint32)newThreadCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // If we're allowing an extra thread then post a dummy event to the thread
+  // pool so that any pending workers can get started. The thread pool doesn't
+  // do this on its own like it probably should...
+  if (aDelta == 1) {
+    nsCOMPtr<nsIRunnable> dummy(new nsRunnable());
+    if (dummy) {
+      rv = mThreadPool->Dispatch(dummy, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+nsDOMThreadService::NoteThreadsafeContractId(const nsACString& aContractId,
+                                             PRBool aIsThreadsafe)
+{
+  NS_ASSERTION(!aContractId.IsEmpty(), "Empty contract id!");
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+#ifdef DEBUG
+  {
+    PRBool isThreadsafe;
+    if (mThreadsafeContractIDs.Get(aContractId, &isThreadsafe)) {
+      NS_ASSERTION(aIsThreadsafe == isThreadsafe, "Inconsistent threadsafety!");
+    }
+  }
+#endif
+
+  if (!mThreadsafeContractIDs.Put(aContractId, aIsThreadsafe)) {
+    NS_WARNING("Out of memory!");
+  }
+}
+
+ThreadsafeStatus
+nsDOMThreadService::GetContractIdThreadsafeStatus(const nsACString& aContractId)
+{
+  NS_ASSERTION(!aContractId.IsEmpty(), "Empty contract id!");
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  PRBool isThreadsafe;
+  if (mThreadsafeContractIDs.Get(aContractId, &isThreadsafe)) {
+    return isThreadsafe ? Threadsafe : NotThreadsafe;
+  }
+
+  return Unknown;
+}
+
+// static
+nsIJSRuntimeService*
+nsDOMThreadService::JSRuntimeService()
+{
+  return gJSRuntimeService;
+}
+
+// static
+nsIThreadJSContextStack*
+nsDOMThreadService::ThreadJSContextStack()
+{
+  return gThreadJSContextStack;
+}
+
+// static
+nsIXPCSecurityManager*
+nsDOMThreadService::WorkerSecurityManager()
+{
+  return gWorkerSecurityManager;
+}
+
+/**
+ * See nsIEventTarget
+ */
+NS_IMETHODIMP
+nsDOMThreadService::Dispatch(nsIRunnable* aEvent,
+                             PRUint32 aFlags)
+{
+  NS_ENSURE_ARG_POINTER(aEvent);
+  NS_ENSURE_FALSE(aFlags & NS_DISPATCH_SYNC, NS_ERROR_NOT_IMPLEMENTED);
+
+  // This should only ever be called by the timer code! We run the event right
+  // now, but all that does is queue the real event for the proper worker.
+  aEvent->Run();
+
+  return NS_OK;
+}
+
+/**
+ * See nsIEventTarget
+ */
+NS_IMETHODIMP
+nsDOMThreadService::IsOnCurrentThread(PRBool* _retval)
+{
+  NS_NOTREACHED("No one should call this!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/**
+ * See nsIObserver
+ */
+NS_IMETHODIMP
+nsDOMThreadService::Observe(nsISupports* aSubject,
+                            const char* aTopic,
+                            const PRUnichar* aData)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    Cleanup();
+    return NS_OK;
+  }
+
+  NS_NOTREACHED("Unknown observer topic!");
+  return NS_OK;
+}
+
+/**
+ * See nsIThreadPoolListener
+ */
+NS_IMETHODIMP
+nsDOMThreadService::OnThreadCreated()
+{
+  LOG(("Thread created"));
+
+  nsIThread* current = NS_GetCurrentThread();
+
+  // We want our worker threads to always have a lower priority than the main
+  // thread. NSPR docs say that this isn't incredibly reliable across all
+  // platforms but we hope for the best.
+  nsCOMPtr<nsISupportsPriority> priority(do_QueryInterface(current));
+  NS_ENSURE_TRUE(priority, NS_ERROR_FAILURE);
+
+  nsresult rv = priority->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
+
+  // Set the context up for the worker.
+  JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
+  if (!cx) {
+    cx = nsDOMThreadService::CreateJSContext();
+    NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
+
+    PRStatus status = PR_SetThreadPrivate(gJSContextIndex, cx);
+    if (status != PR_SUCCESS) {
+      NS_WARNING("Failed to set context on thread!");
+      nsContentUtils::XPConnect()->ReleaseJSContext(cx, PR_TRUE);
+      return NS_ERROR_FAILURE;
+    }
+
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+#ifdef DEBUG
+    JSContext** newContext =
+#endif
+    mJSContexts.AppendElement(cx);
+
+    // We ensure the capacity of this array in Init.
+    NS_ASSERTION(newContext, "Should never fail!");
+  }
+
+  // Make sure that XPConnect knows about this context.
+  gThreadJSContextStack->Push(cx);
+  gThreadJSContextStack->SetSafeJSContext(cx);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMThreadService::OnThreadShuttingDown()
+{
+  LOG(("Thread shutting down"));
+
+  NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
+
+  JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
+  NS_WARN_IF_FALSE(cx, "Thread died with no context?");
+  if (cx) {
+    {
+      ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+      mJSContexts.RemoveElement(cx);
+    }
+
+    JSContext* pushedCx;
+    gThreadJSContextStack->Pop(&pushedCx);
+    NS_ASSERTION(pushedCx == cx, "Popped the wrong context!");
+
+    gThreadJSContextStack->SetSafeJSContext(nsnull);
+
+    // The cycle collector may be running on the main thread. If so we cannot
+    // simply destroy this context. Instead we proxy the context destruction to
+    // the main thread. If that fails somehow then we simply leak the context.
+    nsCOMPtr<nsIRunnable> runnable = new nsDestroyJSContextRunnable(cx);
+
+    if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
+      NS_WARNING("Failed to dispatch release runnable!");
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsDOMThreadService::RegisterWorker(nsDOMWorker* aWorker,
+                                   nsIScriptGlobalObject* aGlobalObject)
+{
+  NS_ASSERTION(aWorker, "Null pointer!");
+
+  if (aGlobalObject && NS_IsMainThread()) {
+    nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(aGlobalObject));
+    NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
+
+    nsPIDOMWindow* innerWindow = domWindow->IsOuterWindow() ?
+                                 domWindow->GetCurrentInnerWindow() :
+                                 domWindow.get();
+    NS_ENSURE_STATE(innerWindow);
+
+    nsCOMPtr<nsIScriptGlobalObject> newGlobal(do_QueryInterface(innerWindow));
+    NS_ENSURE_TRUE(newGlobal, NS_ERROR_NO_INTERFACE);
+
+    aGlobalObject = newGlobal;
+  }
+
+  nsRefPtr<nsDOMWorkerPool> pool;
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    if (!mThreadPool) {
+      // Shutting down!
+      return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
+    }
+
+    mPools.Get(aGlobalObject, getter_AddRefs(pool));
+  }
+
+  nsresult rv;
+
+  if (!pool) {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+    if (!mNavigatorStringsLoaded) {
+      rv = NS_GetNavigatorAppName(mAppName);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = NS_GetNavigatorAppVersion(mAppVersion);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = NS_GetNavigatorPlatform(mPlatform);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = NS_GetNavigatorUserAgent(mUserAgent);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      mNavigatorStringsLoaded = PR_TRUE;
+    }
+
+    nsCOMPtr<nsIDocument> document;
+    if (aGlobalObject) {
+      nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(aGlobalObject));
+      NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
+
+      nsIDOMDocument* domDocument = domWindow->GetExtantDocument();
+      NS_ENSURE_STATE(domDocument);
+
+      document = do_QueryInterface(domDocument);
+      NS_ENSURE_STATE(document);
+    }
+
+    pool = new nsDOMWorkerPool(aGlobalObject, document);
+    NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
+
+    rv = pool->Init();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    PRBool success = mPools.Put(aGlobalObject, pool);
+    NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  rv = pool->NoteWorker(aWorker);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aWorker->SetPool(pool);
+  return NS_OK;
+}
+
+void
+nsDOMThreadService::GetAppName(nsAString& aAppName)
+{
+  NS_ASSERTION(mNavigatorStringsLoaded,
+               "Shouldn't call this before we have loaded strings!");
+  aAppName.Assign(mAppName);
+}
+
+void
+nsDOMThreadService::GetAppVersion(nsAString& aAppVersion)
+{
+  NS_ASSERTION(mNavigatorStringsLoaded,
+               "Shouldn't call this before we have loaded strings!");
+  aAppVersion.Assign(mAppVersion);
+}
+
+void
+nsDOMThreadService::GetPlatform(nsAString& aPlatform)
+{
+  NS_ASSERTION(mNavigatorStringsLoaded,
+               "Shouldn't call this before we have loaded strings!");
+  aPlatform.Assign(mPlatform);
+}
+
+void
+nsDOMThreadService::GetUserAgent(nsAString& aUserAgent)
+{
+  NS_ASSERTION(mNavigatorStringsLoaded,
+               "Shouldn't call this before we have loaded strings!");
+  aUserAgent.Assign(mUserAgent);
+}
+
+void
+nsDOMThreadService::RegisterPrefCallbacks()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  for (PRUint32 index = 0; index < NS_ARRAY_LENGTH(sPrefsToWatch); index++) {
+    Preferences::RegisterCallback(PrefCallback, sPrefsToWatch[index]);
+    PrefCallback(sPrefsToWatch[index], nsnull);
+  }
+}
+
+void
+nsDOMThreadService::UnregisterPrefCallbacks()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  for (PRUint32 index = 0; index < NS_ARRAY_LENGTH(sPrefsToWatch); index++) {
+    Preferences::UnregisterCallback(PrefCallback, sPrefsToWatch[index]);
+  }
+}
+
+// static
+int
+nsDOMThreadService::PrefCallback(const char* aPrefName,
+                                 void* aClosure)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  if(!strcmp(aPrefName, "dom.max_script_run_time")) {
+    // We assume atomic 32bit reads/writes. If this assumption doesn't hold on
+    // some wacky platform then the worst that could happen is that the close
+    // handler will run for a slightly different amount of time.
+    PRUint32 timeoutMS =
+      Preferences::GetUint(aPrefName, gWorkerCloseHandlerTimeoutMS);
+
+    // We must have a timeout value, 0 is not ok. If the pref is set to 0 then
+    // fall back to our default.
+    if (timeoutMS) {
+      gWorkerCloseHandlerTimeoutMS = timeoutMS;
+    }
+  }
+  return 0;
+}
+
+// static
+PRUint32
+nsDOMThreadService::GetWorkerCloseHandlerTimeoutMS()
+{
+  return gWorkerCloseHandlerTimeoutMS;
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMThreadService.h
@@ -0,0 +1,212 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
+ *   Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NSDOMTHREADSERVICE_H__
+#define __NSDOMTHREADSERVICE_H__
+
+// Interfaces
+#include "nsIEventTarget.h"
+#include "nsIObserver.h"
+#include "nsIThreadPool.h"
+
+// Other includes
+#include "jsapi.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsDataHashtable.h"
+#include "nsRefPtrHashtable.h"
+#include "nsStringGlue.h"
+#include "nsTPtrArray.h"
+
+#include "prlog.h"
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gDOMThreadsLog;
+#endif
+
+class nsDOMWorker;
+class nsDOMWorkerPool;
+class nsDOMWorkerRunnable;
+class nsDOMWorkerTimeout;
+class nsIJSRuntimeService;
+class nsIScriptGlobalObject;
+class nsIThreadJSContextStack;
+class nsIXPConnect;
+class nsIXPCSecurityManager;
+
+enum ThreadsafeStatus
+{
+  Threadsafe,
+  NotThreadsafe,
+  Unknown
+};
+
+class nsDOMThreadService : public nsIEventTarget,
+                           public nsIObserver,
+                           public nsIThreadPoolListener
+{
+  friend class nsDOMWorker;
+  friend class nsDOMWorkerNavigator;
+  friend class nsDOMWorkerPool;
+  friend class nsDOMWorkerRunnable;
+  friend class nsDOMWorkerThread;
+  friend class nsDOMWorkerTimeout;
+  friend class nsDOMWorkerXHR;
+  friend class nsDOMWorkerXHRProxy;
+  friend class nsLayoutStatics;
+  friend class nsReportErrorRunnable;
+
+  friend void DOMWorkerErrorReporter(JSContext* aCx,
+                                     const char* aMessage,
+                                     JSErrorReport* aReport);
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIEVENTTARGET
+  NS_DECL_NSIOBSERVER
+  NS_DECL_NSITHREADPOOLLISTENER
+
+  // Any DOM consumers that need access to this service should use this method.
+  static already_AddRefed<nsDOMThreadService> GetOrInitService();
+
+  // Simple getter for this service. This does not create the service if it
+  // hasn't been created already, and it never AddRef's!
+  static nsDOMThreadService* get();
+
+  static JSContext* GetCurrentContext();
+
+  // Easy access to the services we care about.
+  static nsIJSRuntimeService* JSRuntimeService();
+  static nsIThreadJSContextStack* ThreadJSContextStack();
+  static nsIXPCSecurityManager* WorkerSecurityManager();
+
+  void CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
+  void SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
+  void ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
+
+  nsresult ChangeThreadPoolMaxThreads(PRInt16 aDelta);
+
+  void NoteThreadsafeContractId(const nsACString& aContractId,
+                                PRBool aIsThreadsafe);
+
+  ThreadsafeStatus GetContractIdThreadsafeStatus(const nsACString& aContractId);
+
+private:
+  nsDOMThreadService();
+  ~nsDOMThreadService();
+
+  nsresult Init();
+  void Cleanup();
+
+  static void Shutdown();
+
+  nsresult Dispatch(nsDOMWorker* aWorker,
+                    nsIRunnable* aRunnable,
+                    PRIntervalTime aTimeoutInterval = 0,
+                    PRBool aClearQueue = PR_FALSE);
+
+  void SetWorkerTimeout(nsDOMWorker* aWorker,
+                        PRIntervalTime aTimeoutInterval);
+
+  void WorkerComplete(nsDOMWorkerRunnable* aRunnable);
+
+  static JSContext* CreateJSContext();
+
+  already_AddRefed<nsDOMWorkerPool>
+    GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
+                     PRBool aRemove);
+
+  void TriggerOperationCallbackForPool(nsDOMWorkerPool* aPool);
+  void RescheduleSuspendedWorkerForPool(nsDOMWorkerPool* aPool);
+
+  void NoteEmptyPool(nsDOMWorkerPool* aPool);
+
+  void TimeoutReady(nsDOMWorkerTimeout* aTimeout);
+
+  nsresult RegisterWorker(nsDOMWorker* aWorker,
+                          nsIScriptGlobalObject* aGlobalObject);
+
+  void GetAppName(nsAString& aAppName);
+  void GetAppVersion(nsAString& aAppVersion);
+  void GetPlatform(nsAString& aPlatform);
+  void GetUserAgent(nsAString& aUserAgent);
+
+  void RegisterPrefCallbacks();
+  void UnregisterPrefCallbacks();
+
+  static int PrefCallback(const char* aPrefName,
+                          void* aClosure);
+
+  static PRUint32 GetWorkerCloseHandlerTimeoutMS();
+
+  PRBool QueueSuspendedWorker(nsDOMWorkerRunnable* aRunnable);
+
+  // Our internal thread pool.
+  nsCOMPtr<nsIThreadPool> mThreadPool;
+
+  // Maps nsIScriptGlobalObject* to nsDOMWorkerPool.
+  nsRefPtrHashtable<nsVoidPtrHashKey, nsDOMWorkerPool> mPools;
+
+  // mReentrantMonitor protects all access to mWorkersInProgress and
+  // mCreationsInProgress.
+  mozilla::ReentrantMonitor mReentrantMonitor;
+
+  // A map from nsDOMWorkerThread to nsDOMWorkerRunnable.
+  nsRefPtrHashtable<nsVoidPtrHashKey, nsDOMWorkerRunnable> mWorkersInProgress;
+
+  // A list of active JSContexts that we've created. Always protected with
+  // mReentrantMonitor.
+  nsTArray<JSContext*> mJSContexts;
+
+  // A list of worker runnables that were never started because the worker was
+  // suspended. Always protected with mReentrantMonitor.
+  nsTArray<nsDOMWorkerRunnable*> mSuspendedWorkers;
+
+  // Always protected with mReentrantMonitor.
+  nsDataHashtable<nsCStringHashKey, PRBool> mThreadsafeContractIDs;
+
+  nsString mAppName;
+  nsString mAppVersion;
+  nsString mPlatform;
+  nsString mUserAgent;
+
+  PRBool mNavigatorStringsLoaded;
+};
+
+#endif /* __NSDOMTHREADSERVICE_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorker.cpp
@@ -0,0 +1,2752 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMWorker.h"
+
+#include "nsIDOMEvent.h"
+#include "nsIEventTarget.h"
+#include "nsIJSRuntimeService.h"
+#include "nsIXPConnect.h"
+
+#include "jscntxt.h"
+#ifdef MOZ_SHARK
+#include "jsdbgapi.h"
+#endif
+#include "nsAtomicRefcnt.h"
+#include "nsAXPCNativeCallContext.h"
+#include "nsContentUtils.h"
+#include "nsDOMClassInfo.h"
+#include "nsDOMClassInfoID.h"
+#include "nsGlobalWindow.h"
+#include "nsJSON.h"
+#include "nsJSUtils.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+#include "nsNativeCharsetUtils.h"
+#include "xpcprivate.h"
+
+#include "nsDOMThreadService.h"
+#include "nsDOMWorkerEvents.h"
+#include "nsDOMWorkerLocation.h"
+#include "nsDOMWorkerNavigator.h"
+#include "nsDOMWorkerPool.h"
+#include "nsDOMWorkerScriptLoader.h"
+#include "nsDOMWorkerTimeout.h"
+#include "nsDOMWorkerXHR.h"
+
+using namespace mozilla;
+
+class TestComponentThreadsafetyRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  TestComponentThreadsafetyRunnable(const nsACString& aContractId,
+                                    PRBool aService)
+  : mContractId(aContractId),
+    mService(aService),
+    mIsThreadsafe(PR_FALSE)
+  { }
+
+  NS_IMETHOD Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+    nsresult rv;
+    nsCOMPtr<nsISupports> instance;
+    if (mService) {
+      instance = do_GetService(mContractId.get(), &rv);
+    }
+    else {
+      instance = do_CreateInstance(mContractId.get(), &rv);
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(instance, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint32 flags;
+    rv = classInfo->GetFlags(&flags);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mIsThreadsafe = !!(flags & nsIClassInfo::THREADSAFE);
+    return NS_OK;
+  }
+
+  PRBool IsThreadsafe()
+  {
+    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+    return mIsThreadsafe;
+  }
+
+private:
+  nsCString mContractId;
+  PRBool mService;
+  PRBool mIsThreadsafe;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(TestComponentThreadsafetyRunnable, nsIRunnable)
+
+class nsDOMWorkerFunctions
+{
+public:
+  typedef nsDOMWorker::WorkerPrivilegeModel WorkerPrivilegeModel;
+
+  // Same as window.dump().
+  static JSBool
+  Dump(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  // Same as window.setTimeout().
+  static JSBool
+  SetTimeout(JSContext* aCx, uintN aArgc, jsval* aVp) {
+    return MakeTimeout(aCx, aArgc, aVp, PR_FALSE);
+  }
+
+  // Same as window.setInterval().
+  static JSBool
+  SetInterval(JSContext* aCx, uintN aArgc, jsval* aVp) {
+    return MakeTimeout(aCx, aArgc, aVp, PR_TRUE);
+  }
+
+  // Used for both clearTimeout() and clearInterval().
+  static JSBool
+  KillTimeout(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  static JSBool
+  LoadScripts(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  static JSBool
+  NewXMLHttpRequest(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  static JSBool
+  NewWorker(JSContext* aCx, uintN aArgc, jsval* aVp) {
+    return MakeNewWorker(aCx, aArgc, aVp, nsDOMWorker::CONTENT);
+  }
+
+  static JSBool
+  AtoB(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  static JSBool
+  BtoA(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  // Chrome-only functions
+  static JSBool
+  NewChromeWorker(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  static JSBool
+  XPCOMLazyGetter(JSContext* aCx, JSObject* aObj, jsid aId, jsval* aVp);
+
+  static JSBool
+  CreateInstance(JSContext* aCx, uintN aArgc, jsval* aVp) {
+    return GetInstanceCommon(aCx, aArgc, aVp, PR_FALSE);
+  }
+
+  static JSBool
+  GetService(JSContext* aCx, uintN aArgc, jsval* aVp) {
+    return GetInstanceCommon(aCx, aArgc, aVp, PR_TRUE);
+  }
+
+#ifdef BUILD_CTYPES
+  static JSBool
+  CTypesLazyGetter(JSContext* aCx, JSObject* aObj, jsid aId, jsval* aVp);
+#endif
+
+private:
+  // Internal helper for SetTimeout and SetInterval.
+  static JSBool
+  MakeTimeout(JSContext* aCx, uintN aArgc, jsval* aVp, PRBool aIsInterval);
+
+  static JSBool
+  MakeNewWorker(JSContext* aCx, uintN aArgc, jsval* aVp,
+                WorkerPrivilegeModel aPrivilegeModel);
+
+  static JSBool
+  GetInstanceCommon(JSContext* aCx, uintN aArgc, jsval* aVp, PRBool aService);
+};
+
+JSFunctionSpec gDOMWorkerXPCOMFunctions[] = {
+  {"createInstance", nsDOMWorkerFunctions::CreateInstance, 1, JSPROP_ENUMERATE},
+  {"getService",     nsDOMWorkerFunctions::GetService,     1, JSPROP_ENUMERATE},
+  { nsnull,          nsnull,                               0, 0 }
+};
+
+JSBool
+nsDOMWorkerFunctions::Dump(JSContext* aCx,
+                           uintN aArgc,
+                           jsval* aVp)
+{
+  JS_SET_RVAL(cx, aVp, JSVAL_VOID);
+  if (!nsGlobalWindow::DOMWindowDumpEnabled()) {
+    return JS_TRUE;
+  }
+
+  JSString* str;
+  if (aArgc && (str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0])) && str) {
+    nsDependentJSString depStr;
+    if (depStr.init(aCx, str)) {
+      fputs(NS_ConvertUTF16toUTF8(depStr).get(), stderr);
+      fflush(stderr);
+    }
+  }
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::MakeTimeout(JSContext* aCx,
+                                  uintN aArgc,
+                                  jsval* aVp,
+                                  PRBool aIsInterval)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  PRUint32 id = worker->NextTimeoutId();
+
+  if (worker->IsClosing()) {
+    // Timeouts won't run in the close handler, fake success and bail.
+    JS_SET_RVAL(aCx, aVp, INT_TO_JSVAL(id));
+    return JS_TRUE;
+  }
+
+  nsRefPtr<nsDOMWorkerTimeout> timeout = new nsDOMWorkerTimeout(worker, id);
+  if (!timeout) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsresult rv = timeout->Init(aCx, aArgc, JS_ARGV(aCx, aVp), aIsInterval);
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to initialize timeout!");
+    return JS_FALSE;
+  }
+
+  rv = worker->AddFeature(timeout, aCx);
+  if (NS_FAILED(rv)) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  rv = timeout->Start();
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to start timeout!");
+    return JS_FALSE;
+  }
+
+  JS_SET_RVAL(aCx, aVp, INT_TO_JSVAL(id));
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::KillTimeout(JSContext* aCx,
+                                  uintN aArgc,
+                                  jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    JS_ReportError(aCx, "Function requires at least 1 parameter");
+    return JS_FALSE;
+  }
+
+  uint32 id;
+  if (!JS_ValueToECMAUint32(aCx, JS_ARGV(aCx, aVp)[0], &id)) {
+    JS_ReportError(aCx, "First argument must be a timeout id");
+    return JS_FALSE;
+  }
+
+  worker->CancelTimeoutWithId(PRUint32(id));
+  JS_SET_RVAL(aCx, aVp, JSVAL_VOID);
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::LoadScripts(JSContext* aCx,
+                                  uintN aArgc,
+                                  jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    // No argument is ok according to spec.
+    return JS_TRUE;
+  }
+
+  nsAutoTArray<nsString, 10> urls;
+
+  if (!urls.SetCapacity((PRUint32)aArgc)) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  jsval* argv = JS_ARGV(aCx, aVp);
+  for (uintN index = 0; index < aArgc; index++) {
+    jsval val = argv[index];
+
+    if (!JSVAL_IS_STRING(val)) {
+      JS_ReportError(aCx, "Argument %d must be a string", index);
+      return JS_FALSE;
+    }
+
+    JSString* str = JS_ValueToString(aCx, val);
+    if (!str) {
+      JS_ReportError(aCx, "Couldn't convert argument %d to a string", index);
+      return JS_FALSE;
+    }
+
+    nsString* newURL = urls.AppendElement();
+    NS_ASSERTION(newURL, "Shouldn't fail if SetCapacity succeeded above!");
+
+    nsDependentJSString depStr;
+    if (!depStr.init(aCx, str)) {
+      return JS_FALSE;
+    }
+
+    newURL->Assign(depStr);
+  }
+
+  nsRefPtr<nsDOMWorkerScriptLoader> loader =
+    new nsDOMWorkerScriptLoader(worker);
+  if (!loader) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsresult rv = worker->AddFeature(loader, aCx);
+  if (NS_FAILED(rv)) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  rv = loader->LoadScripts(aCx, urls, PR_TRUE);
+  if (NS_FAILED(rv)) {
+    if (!JS_IsExceptionPending(aCx)) {
+      JS_ReportError(aCx, "Failed to load scripts");
+    }
+    return JS_FALSE;
+  }
+
+  JS_SET_RVAL(aCx, aVp, JSVAL_VOID);
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::NewXMLHttpRequest(JSContext* aCx,
+                                        uintN aArgc,
+                                        jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (aArgc) {
+    JS_ReportError(aCx, "XMLHttpRequest constructor takes no arguments!");
+    return JS_FALSE;
+  }
+
+  nsRefPtr<nsDOMWorkerXHR> xhr = new nsDOMWorkerXHR(worker);
+  if (!xhr) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsresult rv = xhr->Init();
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to construct XMLHttpRequest!");
+    return JS_FALSE;
+  }
+
+  rv = worker->AddFeature(xhr, aCx);
+  if (NS_FAILED(rv)) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> xhrWrapped;
+  jsval v;
+  rv = nsContentUtils::WrapNative(aCx, JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp)),
+                                  static_cast<nsIXMLHttpRequest*>(xhr), &v,
+                                  getter_AddRefs(xhrWrapped));
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to wrap XMLHttpRequest!");
+    return JS_FALSE;
+  }
+
+  JS_SET_RVAL(aCs, aVp, v);
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::AtoB(JSContext* aCx,
+                           uintN aArgc,
+                           jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    JS_ReportError(aCx, "Function requires at least 1 parameter");
+    return JS_FALSE;
+  }
+
+  return nsXPConnect::Base64Decode(aCx, JS_ARGV(aCx, aVp)[0],
+                                   &JS_RVAL(aCx, aVp));
+}
+
+JSBool
+nsDOMWorkerFunctions::BtoA(JSContext* aCx,
+                           uintN aArgc,
+                           jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    JS_ReportError(aCx, "Function requires at least 1 parameter");
+    return JS_FALSE;
+  }
+
+  return nsXPConnect::Base64Encode(aCx, JS_ARGV(aCx, aVp)[0],
+                                   &JS_RVAL(aCx, aVp));
+}
+
+JSBool
+nsDOMWorkerFunctions::NewChromeWorker(JSContext* aCx,
+                                      uintN aArgc,
+                                      jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (!worker->IsPrivileged()) {
+    JS_ReportError(aCx, "Cannot create a priviliged worker!");
+    return JS_FALSE;
+  }
+
+  return MakeNewWorker(aCx, aArgc, aVp, nsDOMWorker::CHROME);
+}
+
+JSBool
+nsDOMWorkerFunctions::XPCOMLazyGetter(JSContext* aCx,
+                                      JSObject* aObj,
+                                      jsid aId,
+                                      jsval* aVp)
+{
+#ifdef DEBUG
+  {
+    NS_ASSERTION(JS_GetGlobalForObject(aCx, aObj) == aObj, "Bad object!");
+    NS_ASSERTION(JSID_IS_STRING(aId), "Not a string!");
+    NS_ASSERTION(nsDependentJSString(aId).EqualsLiteral("XPCOM"), "Bad id!");
+  }
+#endif
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  PRUint16 dummy;
+  nsCOMPtr<nsIXPCSecurityManager> secMan;
+  nsContentUtils::XPConnect()->
+    GetSecurityManagerForJSContext(aCx, getter_AddRefs(secMan), &dummy);
+  if (!secMan) {
+    JS_ReportError(aCx, "Could not get security manager!");
+    return JS_FALSE;
+  }
+
+  nsCID dummyCID;
+  if (NS_FAILED(secMan->CanGetService(aCx, dummyCID))) {
+    JS_ReportError(aCx, "Access to the XPCOM object is denied!");
+    return JS_FALSE;
+  }
+
+  JSObject* xpcom = JS_NewObject(aCx, nsnull, nsnull, nsnull);
+  NS_ENSURE_TRUE(xpcom, JS_FALSE);
+
+  JSBool ok = JS_DefineFunctions(aCx, xpcom, gDOMWorkerXPCOMFunctions);
+  NS_ENSURE_TRUE(ok, JS_FALSE);
+
+  ok = JS_DeletePropertyById(aCx, aObj, aId);
+  NS_ENSURE_TRUE(ok, JS_FALSE);
+
+  jsval xpcomVal = OBJECT_TO_JSVAL(xpcom);
+  ok = JS_SetPropertyById(aCx, aObj, aId, &xpcomVal);
+  NS_ENSURE_TRUE(ok, JS_FALSE);
+
+  JS_SET_RVAL(aCx, aVp, xpcomVal);
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::GetInstanceCommon(JSContext* aCx,
+                                        uintN aArgc,
+                                        jsval* aVp,
+                                        PRBool aService)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    JS_ReportError(aCx, "Function requires at least 1 parameter");
+    return JS_FALSE;
+  }
+
+  JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
+  if (!str) {
+    NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
+    return JS_FALSE;
+  }
+
+  JSAutoByteString strBytes(aCx, str);
+  if (!strBytes) {
+    NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
+    return JS_FALSE;
+  }
+
+  nsDependentCString contractId(strBytes.ptr(), JS_GetStringLength(str));
+
+  nsDOMThreadService* threadService = nsDOMThreadService::get();
+
+  ThreadsafeStatus status =
+    threadService->GetContractIdThreadsafeStatus(contractId);
+
+  if (status == Unknown) {
+    nsCOMPtr<nsIThread> mainThread;
+    nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+    if (NS_FAILED(rv)) {
+      JS_ReportError(aCx, "Failed to get main thread!");
+      return JS_FALSE;
+    }
+
+    nsRefPtr<TestComponentThreadsafetyRunnable> runnable =
+      new TestComponentThreadsafetyRunnable(contractId, aService);
+
+    rv = mainThread->Dispatch(runnable, NS_DISPATCH_SYNC);
+    if (NS_FAILED(rv)) {
+      JS_ReportError(aCx, "Failed to check threadsafety!");
+      return JS_FALSE;
+    }
+
+    // The worker may have been canceled while waiting above. Check again.
+    if (worker->IsCanceled()) {
+      return JS_FALSE;
+    }
+
+    if (runnable->IsThreadsafe()) {
+      threadService->NoteThreadsafeContractId(contractId, PR_TRUE);
+      status = Threadsafe;
+    }
+    else {
+      threadService->NoteThreadsafeContractId(contractId, PR_FALSE);
+      status = NotThreadsafe;
+    }
+  }
+
+  if (status == NotThreadsafe) {
+    JS_ReportError(aCx, "ChromeWorker may not create an XPCOM object that is "
+                   "not threadsafe!");
+    return JS_FALSE;
+  }
+
+  nsCOMPtr<nsISupports> instance;
+  if (aService) {
+    instance = do_GetService(contractId.get());
+    if (!instance) {
+      JS_ReportError(aCx, "Could not get the service!");
+      return JS_FALSE;
+    }
+  }
+  else {
+    instance = do_CreateInstance(contractId.get());
+    if (!instance) {
+      JS_ReportError(aCx, "Could not create the instance!");
+      return JS_FALSE;
+    }
+  }
+
+  JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx));
+  if (!global) {
+    NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
+    return JS_FALSE;
+  }
+
+  jsval val;
+  nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
+  if (NS_FAILED(nsContentUtils::WrapNative(aCx, global, instance, &val,
+                                           getter_AddRefs(wrapper)))) {
+    JS_ReportError(aCx, "Failed to wrap object!");
+    return JS_FALSE;
+  }
+
+  JS_SET_RVAL(aCx, aVp, val);
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::MakeNewWorker(JSContext* aCx,
+                                    uintN aArgc,
+                                    jsval* aVp,
+                                    WorkerPrivilegeModel aPrivilegeModel)
+{
+  JSObject *obj = JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp));
+    
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    JS_ReportError(aCx, "Worker constructor must have an argument!");
+    return JS_FALSE;
+  }
+
+  // This pointer is protected by our pool, but it is *not* threadsafe and must
+  // not be used in any way other than to pass it along to the Initialize call.
+  nsIScriptGlobalObject* owner = worker->Pool()->ScriptGlobalObject();
+
+  nsCOMPtr<nsIXPConnectWrappedNative> wrappedWorker =
+    worker->GetWrappedNative();
+  if (!wrappedWorker) {
+    JS_ReportError(aCx, "Couldn't get wrapped native of worker!");
+    return JS_FALSE;
+  }
+
+  nsRefPtr<nsDOMWorker> newWorker =
+    new nsDOMWorker(worker, wrappedWorker, aPrivilegeModel);
+  if (!newWorker) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsresult rv = newWorker->InitializeInternal(owner, aCx, obj, aArgc,
+                                              JS_ARGV(aCx, aVp));
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Couldn't initialize new worker!");
+    return JS_FALSE;
+  }
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> workerWrapped;
+  jsval v;
+  rv = nsContentUtils::WrapNative(aCx, obj, static_cast<nsIWorker*>(newWorker), &v, 
+                                  getter_AddRefs(workerWrapped));
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to wrap new worker!");
+    return JS_FALSE;
+  }
+
+  JS_SET_RVAL(aCx, aVp, v);
+  return JS_TRUE;
+}
+
+#ifdef BUILD_CTYPES
+static char*
+UnicodeToNative(JSContext *cx, const jschar *source, size_t slen)
+{
+  nsCAutoString native;
+  nsDependentString unicode(reinterpret_cast<const PRUnichar*>(source), slen);
+  nsresult rv = NS_CopyUnicodeToNative(unicode, native);
+  if (NS_FAILED(rv)) {
+    JS_ReportError(cx, "could not convert string to native charset");
+    return NULL;
+  }
+
+  char* result = static_cast<char*>(JS_malloc(cx, native.Length() + 1));
+  if (!result)
+    return NULL;
+
+  memcpy(result, native.get(), native.Length() + 1);
+  return result;
+}
+
+static JSCTypesCallbacks sCallbacks = {
+  UnicodeToNative
+};
+
+JSBool
+nsDOMWorkerFunctions::CTypesLazyGetter(JSContext* aCx,
+                                       JSObject* aObj,
+                                       jsid aId,
+                                       jsval* aVp)
+{
+#ifdef DEBUG
+  {
+    NS_ASSERTION(JS_GetGlobalForObject(aCx, aObj) == aObj, "Bad object!");
+    NS_ASSERTION(JSID_IS_STRING(aId), "Not a string!");
+    NS_ASSERTION(nsDependentJSString(aId).EqualsLiteral("ctypes"), "Bad id!");
+  }
+#endif
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+  NS_ASSERTION(worker->IsPrivileged(), "This shouldn't be possible!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  jsval ctypes;
+  return JS_DeletePropertyById(aCx, aObj, aId) &&
+         JS_InitCTypesClass(aCx, aObj) &&
+         JS_GetProperty(aCx, aObj, "ctypes", &ctypes) &&
+         JS_SetCTypesCallbacks(aCx, JSVAL_TO_OBJECT(ctypes), &sCallbacks) &&
+         JS_GetPropertyById(aCx, aObj, aId, aVp);
+}
+#endif
+JSFunctionSpec gDOMWorkerFunctions[] = {
+  { "dump",                nsDOMWorkerFunctions::Dump,                1, 0 },
+  { "setTimeout",          nsDOMWorkerFunctions::SetTimeout,          1, 0 },
+  { "clearTimeout",        nsDOMWorkerFunctions::KillTimeout,         1, 0 },
+  { "setInterval",         nsDOMWorkerFunctions::SetInterval,         1, 0 },
+  { "clearInterval",       nsDOMWorkerFunctions::KillTimeout,         1, 0 },
+  { "importScripts",       nsDOMWorkerFunctions::LoadScripts,         1, 0 },
+  { "XMLHttpRequest",      nsDOMWorkerFunctions::NewXMLHttpRequest,   0, JSFUN_CONSTRUCTOR },
+  { "Worker",              nsDOMWorkerFunctions::NewWorker,           1, JSFUN_CONSTRUCTOR },
+  { "atob",                nsDOMWorkerFunctions::AtoB,                1, 0 },
+  { "btoa",                nsDOMWorkerFunctions::BtoA,                1, 0 },
+  { nsnull,                nsnull,                                    0, 0 }
+};
+JSFunctionSpec gDOMWorkerChromeFunctions[] = {
+  { "ChromeWorker",        nsDOMWorkerFunctions::NewChromeWorker,     1, JSFUN_CONSTRUCTOR },
+  { nsnull,                nsnull,                                    0, 0 }
+};
+enum DOMWorkerStructuredDataType
+{
+  // We have a special tag for XPCWrappedNatives that are being passed between
+  // threads. This will not work across processes and cannot be persisted. Only
+  // for ChromeWorker use at present.
+  DOMWORKER_SCTAG_WRAPPEDNATIVE = JS_SCTAG_USER_MIN + 0x1000,
+
+  DOMWORKER_SCTAG_END
+};
+
+PR_STATIC_ASSERT(DOMWORKER_SCTAG_END <= JS_SCTAG_USER_MAX);
+
+// static
+JSBool
+WriteStructuredClone(JSContext* aCx,
+                     JSStructuredCloneWriter* aWriter,
+                     JSObject* aObj,
+                     void* aClosure)
+{
+  NS_ASSERTION(aClosure, "Null pointer!");
+
+  // We'll stash any nsISupports pointers that need to be AddRef'd here.
+  nsTArray<nsCOMPtr<nsISupports> >* wrappedNatives =
+    static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
+
+  // See if this is a wrapped native.
+  nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+  nsContentUtils::XPConnect()->
+    GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
+  if (wrappedNative) {
+    // Get the raw nsISupports out of it.
+    nsISupports* wrappedObject = wrappedNative->Native();
+    NS_ASSERTION(wrappedObject, "Null pointer?!");
+
+    // See if this nsISupports is threadsafe.
+    nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(wrappedObject);
+    if (classInfo) {
+      PRUint32 flags;
+      if (NS_SUCCEEDED(classInfo->GetFlags(&flags)) &&
+          (flags & nsIClassInfo::THREADSAFE)) {
+        // Write the raw pointer into the stream, and add it to the list we're
+        // building.
+        return JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_WRAPPEDNATIVE, 0) &&
+               JS_WriteBytes(aWriter, &wrappedObject, sizeof(wrappedObject)) &&
+               wrappedNatives->AppendElement(wrappedObject);
+      }
+    }
+  }
+
+  // Something failed above, try using the runtime callbacks instead.
+  const JSStructuredCloneCallbacks* runtimeCallbacks =
+    aCx->runtime->structuredCloneCallbacks;
+  if (runtimeCallbacks) {
+    return runtimeCallbacks->write(aCx, aWriter, aObj, nsnull);
+  }
+
+  // We can't handle this object, throw an exception if one hasn't been thrown
+  // already.
+  if (!JS_IsExceptionPending(aCx)) {
+    nsDOMClassInfo::ThrowJSException(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+  }
+  return JS_FALSE;
+}
+
+nsDOMWorkerScope::nsDOMWorkerScope(nsDOMWorker* aWorker)
+: mWorker(aWorker),
+  mWrappedNative(nsnull),
+  mHasOnerror(PR_FALSE)
+{
+  NS_ASSERTION(aWorker, "Null pointer!");
+}
+
+NS_IMPL_ISUPPORTS_INHERITED3(nsDOMWorkerScope, nsDOMWorkerMessageHandler,
+                                               nsIWorkerScope,
+                                               nsIWorkerGlobalScope,
+                                               nsIXPCScriptable)
+
+NS_IMPL_CI_INTERFACE_GETTER4(nsDOMWorkerScope, nsIWorkerScope,
+                                               nsIWorkerGlobalScope,
+                                               nsIDOMEventTarget,
+                                               nsIXPCScriptable)
+
+NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerScope)
+NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(nsDOMWorkerScope)
+
+// Need to return a scriptable helper so that XPConnect can get our
+// nsIXPCScriptable flags properly (to not enumerate QI, for instance).
+NS_IMETHODIMP
+nsDOMWorkerScope::GetHelperForLanguage(PRUint32 aLanguage,
+                                       nsISupports** _retval)
+{
+  if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
+    NS_ADDREF(*_retval = NS_ISUPPORTS_CAST(nsIWorkerScope*, this));
+  }
+  else {
+    *_retval = nsnull;
+  }
+  return NS_OK;
+}
+
+// Use the xpc_map_end.h macros to generate the nsIXPCScriptable methods we want
+// for the scope.
+
+#define XPC_MAP_CLASSNAME nsDOMWorkerScope
+#define XPC_MAP_QUOTED_CLASSNAME "DedicatedWorkerGlobalScope"
+#define XPC_MAP_WANT_POSTCREATE
+#define XPC_MAP_WANT_TRACE
+#define XPC_MAP_WANT_FINALIZE
+
+#define XPC_MAP_FLAGS                                      \
+  nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY           | \
+  nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY           | \
+  nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY           | \
+  nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE            | \
+  nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY            | \
+  nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES         | \
+  nsIXPCScriptable::WANT_ADDPROPERTY
+
+#define XPC_MAP_WANT_ADDPROPERTY
+
+#include "xpc_map_end.h"
+
+NS_IMETHODIMP
+nsDOMWorkerScope::PostCreate(nsIXPConnectWrappedNative*  aWrapper,
+                             JSContext* /* aCx */,
+                             JSObject* /* aObj */)
+{
+  NS_ASSERTION(!mWrappedNative, "Already got a wrapper?!");
+  mWrappedNative = aWrapper;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::Trace(nsIXPConnectWrappedNative* /* aWrapper */,
+                        JSTracer* aTracer,
+                        JSObject* /*aObj */)
+{
+  nsDOMWorkerMessageHandler::Trace(aTracer);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::Finalize(nsIXPConnectWrappedNative* /* aWrapper */,
+                           JSContext* /* aCx */,
+                           JSObject* /* aObj */)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  ClearAllListeners();
+  mWrappedNative = nsnull;
+  return NS_OK;
+}
+
+already_AddRefed<nsIXPConnectWrappedNative>
+nsDOMWorkerScope::GetWrappedNative()
+{
+  nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative = mWrappedNative;
+  NS_ASSERTION(wrappedNative, "Null wrapped native!");
+  return wrappedNative.forget();
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::AddProperty(nsIXPConnectWrappedNative* aWrapper,
+                              JSContext* aCx,
+                              JSObject* aObj,
+                              jsid aId,
+                              jsval* aVp,
+                              PRBool* _retval)
+{
+  // We're not going to be setting any exceptions manually so set _retval to
+  // true in the beginning.
+  *_retval = PR_TRUE;
+
+  // Bail out now if any of our prerequisites are not met. We only care about
+  // someone making an 'onmessage' or 'onerror' function so aId must be a
+  // string and aVp must be a function.
+  JSObject* funObj;
+  if (!(JSID_IS_STRING(aId) &&
+        JSVAL_IS_OBJECT(*aVp) &&
+        (funObj = JSVAL_TO_OBJECT(*aVp)) &&
+        JS_ObjectIsFunction(aCx, funObj))) {
+    return NS_OK;
+  }
+
+  JSFlatString *str = JSID_TO_FLAT_STRING(aId);
+
+  // Figure out which listener we're setting.
+  SetListenerFunc func;
+  if (JS_FlatStringEqualsAscii(str, "onmessage")) {
+    func = &nsDOMWorkerScope::SetOnmessage;
+  }
+  else if (JS_FlatStringEqualsAscii(str, "onerror")) {
+    func = &nsDOMWorkerScope::SetOnerror;
+  }
+  else {
+    // Some other function, we don't need to do anything special after all.
+    return NS_OK;
+  }
+
+  // Wrap the function as an nsIDOMEventListener.
+  nsCOMPtr<nsIDOMEventListener> listener;
+  nsresult rv =
+    nsContentUtils::XPConnect()->WrapJS(aCx, funObj,
+                                        NS_GET_IID(nsIDOMEventListener),
+                                        getter_AddRefs(listener));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // And pass the listener to the appropriate setter.
+  rv = (this->*func)(listener);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::GetSelf(nsIWorkerGlobalScope** aSelf)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aSelf);
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  NS_ADDREF(*aSelf = this);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::GetNavigator(nsIWorkerNavigator** _retval)
+{
+  if (!mNavigator) {
+    mNavigator = new nsDOMWorkerNavigator();
+    NS_ENSURE_TRUE(mNavigator, NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  NS_ADDREF(*_retval = mNavigator);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::GetLocation(nsIWorkerLocation** _retval)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsIWorkerLocation> location = mWorker->GetLocation();
+  NS_ASSERTION(location, "This should never be null!");
+
+  location.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::GetOnerror(nsIDOMEventListener** aOnerror)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aOnerror);
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  if (!mHasOnerror) {
+    // Spec says we have to return 'undefined' until something is set here.
+    nsIXPConnect* xpc = nsContentUtils::XPConnect();
+    NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
+
+    nsAXPCNativeCallContext* cc;
+    nsresult rv = xpc->GetCurrentNativeCallContext(&cc);
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED);
+
+    jsval* retval;
+    rv = cc->GetRetValPtr(&retval);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    *retval = JSVAL_VOID;
+    return cc->SetReturnValueWasSet(PR_TRUE);
+  }
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(NS_LITERAL_STRING("error"));
+  listener.forget(aOnerror);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::SetOnerror(nsIDOMEventListener* aOnerror)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  mHasOnerror = PR_TRUE;
+
+  return SetOnXListener(NS_LITERAL_STRING("error"), aOnerror);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::PostMessage(/* JSObject aMessage */)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return mWorker->PostMessageInternal(PR_FALSE);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::Close()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  return mWorker->Close();
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::GetOnmessage(nsIDOMEventListener** aOnmessage)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aOnmessage);
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(NS_LITERAL_STRING("message"));
+  listener.forget(aOnmessage);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::SetOnmessage(nsIDOMEventListener* aOnmessage)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return SetOnXListener(NS_LITERAL_STRING("message"), aOnmessage);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::GetOnclose(nsIDOMEventListener** aOnclose)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aOnclose);
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(NS_LITERAL_STRING("close"));
+  listener.forget(aOnclose);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::SetOnclose(nsIDOMEventListener* aOnclose)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = SetOnXListener(NS_LITERAL_STRING("close"), aOnclose);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::RemoveEventListener(const nsAString& aType,
+                                      nsIDOMEventListener* aListener,
+                                      PRBool aUseCapture)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return nsDOMWorkerMessageHandler::RemoveEventListener(aType, aListener,
+                                                        aUseCapture);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::DispatchEvent(nsIDOMEvent* aEvent,
+                                PRBool* _retval)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return nsDOMWorkerMessageHandler::DispatchEvent(aEvent, _retval);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::AddEventListener(const nsAString& aType,
+                                   nsIDOMEventListener* aListener,
+                                   PRBool aUseCapture,
+                                   PRBool aWantsUntrusted,
+                                   PRUint8 optional_argc)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return nsDOMWorkerMessageHandler::AddEventListener(aType, aListener,
+                                                     aUseCapture,
+                                                     aWantsUntrusted,
+                                                     optional_argc);
+}
+
+class nsWorkerHoldingRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsWorkerHoldingRunnable(nsDOMWorker* aWorker)
+  : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()) { }
+
+  NS_IMETHOD Run() {
+    return NS_OK;
+  }
+
+  void ReplaceWrappedNative(nsIXPConnectWrappedNative* aWrappedNative) {
+    mWorkerWN = aWrappedNative;
+  }
+
+protected:
+  virtual ~nsWorkerHoldingRunnable() { }
+
+  nsRefPtr<nsDOMWorker> mWorker;
+
+private:
+  nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsWorkerHoldingRunnable, nsIRunnable)
+
+class nsDOMFireEventRunnable : public nsWorkerHoldingRunnable
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  nsDOMFireEventRunnable(nsDOMWorker* aWorker,
+                         nsDOMWorkerEvent* aEvent,
+                         PRBool aToInner)
+  : nsWorkerHoldingRunnable(aWorker), mEvent(aEvent), mToInner(aToInner)
+  {
+    NS_ASSERTION(aWorker && aEvent, "Null pointer!");
+  }
+
+  NS_IMETHOD Run() {
+#ifdef DEBUG
+    if (NS_IsMainThread()) {
+      NS_ASSERTION(!mToInner, "Should only run outer events on main thread!");
+      NS_ASSERTION(!mWorker->mParent, "Worker shouldn't have a parent!");
+    }
+    else {
+      JSContext* cx = nsDOMThreadService::GetCurrentContext();
+      nsDOMWorker* currentWorker = (nsDOMWorker*)JS_GetContextPrivate(cx);
+      NS_ASSERTION(currentWorker, "Must have a worker here!");
+
+      nsDOMWorker* targetWorker = mToInner ? mWorker.get() : mWorker->mParent;
+      NS_ASSERTION(currentWorker == targetWorker, "Wrong worker!");
+    }
+#endif
+    if (mWorker->IsCanceled()) {
+      return NS_ERROR_ABORT;
+    }
+
+    // If the worker is suspended and we're running on the main thread then we
+    // can't actually dispatch the event yet. Instead we queue it for whenever
+    // we resume.
+    if (mWorker->IsSuspended() && NS_IsMainThread()) {
+      if (!mWorker->QueueSuspendedRunnable(this)) {
+        NS_ERROR("Out of memory?!");
+        return NS_ERROR_ABORT;
+      }
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIDOMEventTarget> target = mToInner ?
+      static_cast<nsDOMWorkerMessageHandler*>(mWorker->GetInnerScope()) :
+      static_cast<nsDOMWorkerMessageHandler*>(mWorker);
+
+    NS_ASSERTION(target, "Null target!");
+    NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
+
+    mEvent->SetTarget(target);
+    return target->DispatchEvent(mEvent, nsnull);
+  }
+
+protected:
+  nsRefPtr<nsDOMWorkerEvent> mEvent;
+  PRBool mToInner;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(nsDOMFireEventRunnable, nsWorkerHoldingRunnable)
+
+// Standard NS_IMPL_THREADSAFE_ADDREF without the logging stuff (since this
+// class is made to be inherited anyway).
+NS_IMETHODIMP_(nsrefcnt)
+nsDOMWorkerFeature::AddRef()
+{
+  NS_ASSERTION(mRefCnt >= 0, "Illegal refcnt!");
+  return NS_AtomicIncrementRefcnt(mRefCnt);
+}
+
+// Custom NS_IMPL_THREADSAFE_RELEASE. Checks the mFreeToDie flag before calling
+// delete. If the flag is false then the feature still lives in the worker's
+// list and must be removed. We rely on the fact that the RemoveFeature method
+// calls AddRef and Release after setting the mFreeToDie flag so we won't leak.
+NS_IMETHODIMP_(nsrefcnt)
+nsDOMWorkerFeature::Release()
+{
+  NS_ASSERTION(mRefCnt, "Double release!");
+  nsrefcnt count = NS_AtomicDecrementRefcnt(mRefCnt);
+  if (count == 0) {
+    if (mFreeToDie) {
+      mRefCnt = 1;
+      delete this;
+    }
+    else {
+      mWorker->RemoveFeature(this, nsnull);
+    }
+  }
+  return count;
+}
+
+NS_IMPL_QUERY_INTERFACE0(nsDOMWorkerFeature)
+
+nsDOMWorker::nsDOMWorker(nsDOMWorker* aParent,
+                         nsIXPConnectWrappedNative* aParentWN,
+                         WorkerPrivilegeModel aPrivilegeModel)
+: mParent(aParent),
+  mParentWN(aParentWN),
+  mPrivilegeModel(aPrivilegeModel),
+  mLock("nsDOMWorker.mLock"),
+  mInnerScope(nsnull),
+  mGlobal(NULL),
+  mNextTimeoutId(0),
+  mFeatureSuspendDepth(0),
+  mWrappedNative(nsnull),
+  mErrorHandlerRecursionCount(0),
+  mStatus(eRunning),
+  mExpirationTime(0),
+  mSuspended(PR_FALSE),
+  mCompileAttempted(PR_FALSE)
+{
+#ifdef DEBUG
+  PRBool mainThread = NS_IsMainThread();
+  NS_ASSERTION(aParent ? !mainThread : mainThread, "Wrong thread!");
+#endif
+}
+
+nsDOMWorker::~nsDOMWorker()
+{
+  if (mPool) {
+    mPool->NoteDyingWorker(this);
+  }
+
+  NS_ASSERTION(!mFeatures.Length(), "Live features!");
+  NS_ASSERTION(!mQueuedRunnables.Length(), "Events that never ran!");
+
+  nsCOMPtr<nsIThread> mainThread;
+  NS_GetMainThread(getter_AddRefs(mainThread));
+
+  nsIPrincipal* principal;
+  mPrincipal.forget(&principal);
+  if (principal) {
+    NS_ProxyRelease(mainThread, principal, PR_FALSE);
+  }
+
+  nsIURI* uri;
+  mBaseURI.forget(&uri);
+  if (uri) {
+    NS_ProxyRelease(mainThread, uri, PR_FALSE);
+  }
+}
+
+// static
+nsresult
+nsDOMWorker::NewWorker(nsISupports** aNewObject)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsISupports> newWorker =
+    NS_ISUPPORTS_CAST(nsIWorker*, new nsDOMWorker(nsnull, nsnull, CONTENT));
+  NS_ENSURE_TRUE(newWorker, NS_ERROR_OUT_OF_MEMORY);
+
+  newWorker.forget(aNewObject);
+  return NS_OK;
+}
+
+// static
+nsresult
+nsDOMWorker::NewChromeDOMWorker(nsDOMWorker** aNewObject)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // Subsumes nsContentUtils::IsCallerChrome
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+  NS_ASSERTION(ssm, "Should never be null!");
+
+  PRBool enabled;
+  nsresult rv = ssm->IsCapabilityEnabled("UniversalXPConnect", &enabled);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(enabled, NS_ERROR_DOM_SECURITY_ERR);
+
+  nsRefPtr<nsDOMWorker> newWorker = new nsDOMWorker(nsnull, nsnull, CHROME);
+  NS_ENSURE_TRUE(newWorker, NS_ERROR_OUT_OF_MEMORY);
+
+  newWorker.forget(aNewObject);
+  return NS_OK;
+}
+
+// static
+nsresult
+nsDOMWorker::NewChromeWorker(nsISupports** aNewObject)
+{
+  nsDOMWorker* newWorker;
+  nsresult rv = NewChromeDOMWorker(&newWorker);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aNewObject = NS_ISUPPORTS_CAST(nsIWorker*, newWorker);
+  return NS_OK;
+}
+
+NS_IMPL_ADDREF_INHERITED(nsDOMWorker, nsDOMWorkerMessageHandler)
+NS_IMPL_RELEASE_INHERITED(nsDOMWorker, nsDOMWorkerMessageHandler)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMWorker)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWorker)
+  NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+  NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+  NS_INTERFACE_MAP_ENTRY(nsIWorker)
+  NS_INTERFACE_MAP_ENTRY(nsIAbstractWorker)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventTarget,
+                                   nsDOMWorkerMessageHandler)
+  NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
+  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+NS_INTERFACE_MAP_END
+
+// Use the xpc_map_end.h macros to generate the nsIXPCScriptable methods we want
+// for the worker.
+
+#define XPC_MAP_CLASSNAME nsDOMWorker
+#define XPC_MAP_QUOTED_CLASSNAME "Worker"
+#define XPC_MAP_WANT_PRECREATE
+#define XPC_MAP_WANT_POSTCREATE
+#define XPC_MAP_WANT_TRACE
+#define XPC_MAP_WANT_FINALIZE
+
+#define XPC_MAP_FLAGS                                      \
+  nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY           | \
+  nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY           | \
+  nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY           | \
+  nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE            | \
+  nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY            | \
+  nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES
+
+#include "xpc_map_end.h"
+
+NS_IMETHODIMP
+nsDOMWorker::PreCreate(nsISupports* aObject,
+                       JSContext* aCx,
+                       JSObject* /* aPlannedParent */,
+                       JSObject** aParent)
+{
+  nsCOMPtr<nsIWorker> iworker(do_QueryInterface(aObject));
+  NS_ENSURE_TRUE(iworker, NS_ERROR_UNEXPECTED);
+
+  nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+  {
+    MutexAutoLock lock(mLock);
+    wrappedNative = mWrappedNative;
+  }
+
+  // Don't allow XPConnect to create multiple WrappedNatives for this object.
+  if (wrappedNative) {
+    JSObject* object;
+    nsresult rv = wrappedNative->GetJSObject(&object);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    *aParent = JS_GetParent(aCx, object);
+  }
+
+  return IsPrivileged() ? NS_SUCCESS_CHROME_ACCESS_ONLY : NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorker::PostCreate(nsIXPConnectWrappedNative* aWrapper,
+                        JSContext* /* aCx */,
+                        JSObject* /* aObj */)
+{
+  MutexAutoLock lock(mLock);
+  mWrappedNative = aWrapper;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorker::Trace(nsIXPConnectWrappedNative* /* aWrapper */,
+                   JSTracer* aTracer,
+                   JSObject* /*aObj */)
+{
+  PRBool canceled = PR_FALSE;
+  {
+    MutexAutoLock lock(mLock);
+    canceled = mStatus == eKilled;
+  }
+
+  if (!canceled) {
+    nsDOMWorkerMessageHandler::Trace(aTracer);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorker::Finalize(nsIXPConnectWrappedNative* /* aWrapper */,
+                      JSContext* aCx,
+                      JSObject* /* aObj */)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // Don't leave dangling JSObject pointers in our handlers!
+  ClearAllListeners();
+
+  // Clear our wrapped native now that it has died.
+  {
+    MutexAutoLock lock(mLock);
+    mWrappedNative = nsnull;
+  }
+
+  // Do this *after* we null out mWrappedNative so that we don't hand out a
+  // freed pointer.
+  if (TerminateInternal(PR_TRUE) == NS_ERROR_ILLEGAL_DURING_SHUTDOWN) {
+    // We're shutting down, jump right to Kill.
+    Kill();
+  }
+
+  return NS_OK;
+}
+
+NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorker, nsIWorker,
+                                          nsIAbstractWorker,
+                                          nsIDOMEventTarget)
+NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorker)
+NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(nsDOMWorker)
+
+NS_IMETHODIMP
+nsDOMWorker::GetHelperForLanguage(PRUint32 aLanguage,
+                                  nsISupports** _retval)
+{
+  if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
+    NS_ADDREF(*_retval = NS_ISUPPORTS_CAST(nsIWorker*, this));
+  }
+  else {
+    *_retval = nsnull;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorker::Initialize(nsISupports* aOwner,
+                        JSContext* aCx,
+                        JSObject* aObj,
+                        PRUint32 aArgc,
+                        jsval* aArgv)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aOwner);
+
+  nsCOMPtr<nsIScriptGlobalObject> globalObj(do_QueryInterface(aOwner));
+  NS_ENSURE_TRUE(globalObj, NS_NOINTERFACE);
+
+  return InitializeInternal(globalObj, aCx, aObj, aArgc, aArgv);
+}
+
+nsresult
+nsDOMWorker::InitializeInternal(nsIScriptGlobalObject* aOwner,
+                                JSContext* aCx,
+                                JSObject* aObj,
+                                PRUint32 aArgc,
+                                jsval* aArgv)
+{
+  NS_ASSERTION(aCx, "Null context!");
+  NS_ASSERTION(aObj, "Null global object!");
+
+  NS_ENSURE_TRUE(aArgc, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
+  NS_ENSURE_ARG_POINTER(aArgv);
+
+  JSString* str = JS_ValueToString(aCx, aArgv[0]);
+  NS_ENSURE_TRUE(str, NS_ERROR_XPC_BAD_CONVERT_JS);
+
+  nsDependentJSString depStr;
+  NS_ENSURE_TRUE(depStr.init(aCx, str), NS_ERROR_OUT_OF_MEMORY);
+
+  mScriptURL.Assign(depStr);
+  NS_ENSURE_FALSE(mScriptURL.IsEmpty(), NS_ERROR_INVALID_ARG);
+
+  nsresult rv;
+
+  // Figure out the principal and base URI to use if we're on the main thread.
+  // Otherwise this is a sub-worker and it will have its principal set by the
+  // script loader.
+  if (NS_IsMainThread()) {
+    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+    NS_ASSERTION(ssm, "Should never be null!");
+
+    PRBool isChrome;
+    rv = ssm->IsCapabilityEnabled("UniversalXPConnect", &isChrome);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    NS_ASSERTION(isChrome || aOwner, "How can we have a non-chrome, non-window "
+                 "worker?!");
+
+    // Chrome callers (whether ChromeWorker of Worker) always get the system
+    // principal here as they're allowed to load anything. The script loader may
+    // change the principal later depending on the script uri.
+    if (isChrome) {
+      rv = ssm->GetSystemPrincipal(getter_AddRefs(mPrincipal));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    if (aOwner) {
+      // We're being created inside a window. Get the document's base URI and
+      // use it as our base URI.
+      nsCOMPtr<nsPIDOMWindow> domWindow = do_QueryInterface(aOwner, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsIDOMDocument* domDocument = domWindow->GetExtantDocument();
+      NS_ENSURE_STATE(domDocument);
+
+      nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      mBaseURI = document->GetDocBaseURI();
+
+      if (!mPrincipal) {
+        // Use the document's NodePrincipal as our principal if we're not being
+        // called from chrome.
+        mPrincipal = document->NodePrincipal();
+        NS_ENSURE_STATE(mPrincipal);
+      }
+    }
+    else {
+      // We're being created outside of a window. Need to figure out the script
+      // that is creating us in order for us to use relative URIs later on.
+      JSStackFrame* frame = JS_GetScriptedCaller(aCx, nsnull);
+      if (frame) {
+        JSScript* script = JS_GetFrameScript(aCx, frame);
+        NS_ENSURE_STATE(script);
+
+        const char* filename = JS_GetScriptFilename(aCx, script);
+
+        rv = NS_NewURI(getter_AddRefs(mBaseURI), filename);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+
+    NS_ASSERTION(mPrincipal, "Should have set the principal!");
+  }
+
+  NS_ASSERTION(!mGlobal, "Already got a global?!");
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> thisWrapped;
+  jsval v;
+  rv = nsContentUtils::WrapNative(aCx, aObj, static_cast<nsIWorker*>(this), &v,
+                                  getter_AddRefs(thisWrapped));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(mWrappedNative, "Post-create hook should have set this!");
+
+  mKillTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIThread> mainThread;
+  rv = NS_GetMainThread(getter_AddRefs(mainThread));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mKillTimer->SetTarget(mainThread);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // This is pretty cool - all we have to do to get our script executed is to
+  // pass a no-op runnable to the thread service and it will make sure we have
+  // a context and global object.
+  nsCOMPtr<nsIRunnable> runnable(new nsWorkerHoldingRunnable(this));
+  NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+
+  nsRefPtr<nsDOMThreadService> threadService =
+    nsDOMThreadService::GetOrInitService();
+  NS_ENSURE_STATE(threadService);
+
+  rv = threadService->RegisterWorker(this, aOwner);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(mPool, "RegisterWorker should have set our pool!");
+
+  rv = threadService->Dispatch(this, runnable);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+void
+nsDOMWorker::Cancel()
+{
+  // Called by the pool when the window that created us is being torn down. Must
+  // always be on the main thread.
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // We set the eCanceled status to indicate this. It behaves just like the
+  // eTerminated status (canceled while close runnable is unscheduled, not
+  // canceled while close runnable is running) except that it always reports
+  // that it is canceled when running on the main thread. This status trumps all
+  // others (except eKilled). Have to do this because the window that created
+  // us has gone away and had its scope cleared so XPConnect will assert all
+  // over the place if we try to run anything.
+
+  PRBool enforceTimeout = PR_FALSE;
+  {
+    MutexAutoLock lock(mLock);
+
+    NS_ASSERTION(mStatus != eCanceled, "Canceled more than once?!");
+
+    if (mStatus == eKilled) {
+      return;
+    }
+
+    DOMWorkerStatus oldStatus = mStatus;
+    mStatus = eCanceled;
+    if (oldStatus != eRunning) {
+      enforceTimeout = PR_TRUE;
+    }
+  }
+
+  PRUint32 timeoutMS = nsDOMThreadService::GetWorkerCloseHandlerTimeoutMS();
+  NS_ASSERTION(timeoutMS, "This must not be 0!");
+
+#ifdef DEBUG
+  nsresult rv;
+#endif
+  if (enforceTimeout) {
+    // Tell the thread service to enforce a timeout on the close handler that
+    // is already scheduled.
+    nsDOMThreadService::get()->
+      SetWorkerTimeout(this, PR_MillisecondsToInterval(timeoutMS));
+
+#ifdef DEBUG
+    rv =
+#endif
+    mKillTimer->InitWithCallback(this, timeoutMS, nsITimer::TYPE_ONE_SHOT);
+    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to init kill timer!");
+
+    return;
+  }
+
+#ifdef DEBUG
+  rv =
+#endif
+  FireCloseRunnable(PR_MillisecondsToInterval(timeoutMS), PR_TRUE, PR_FALSE);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to fire close runnable!");
+}
+
+void
+nsDOMWorker::Kill()
+{
+  // Cancel all features and set our status to eKilled. This should only be
+  // called on the main thread by the thread service or our kill timer to
+  // indicate that the worker's close handler has run (or timed out).
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(IsClosing(), "Close handler should have run by now!");
+
+  // If the close handler finished before our kill timer then we don't need it
+  // any longer.
+  if (mKillTimer) {
+    mKillTimer->Cancel();
+    mKillTimer = nsnull;
+  }
+
+  PRUint32 count, index;
+  nsAutoTArray<nsRefPtr<nsDOMWorkerFeature>, 20> features;
+  {
+    MutexAutoLock lock(mLock);
+
+    if (mStatus == eKilled) {
+      NS_ASSERTION(mFeatures.Length() == 0, "Features added after killed!");
+      return;
+    }
+    mStatus = eKilled;
+
+    count = mFeatures.Length();
+    for (index = 0; index < count; index++) {
+      nsDOMWorkerFeature*& feature = mFeatures[index];
+
+#ifdef DEBUG
+      nsRefPtr<nsDOMWorkerFeature>* newFeature =
+#endif
+      features.AppendElement(feature);
+      NS_ASSERTION(newFeature, "Out of memory!");
+
+      feature->FreeToDie(PR_TRUE);
+    }
+
+    mFeatures.Clear();
+  }
+
+  count = features.Length();
+  for (index = 0; index < count; index++) {
+    features[index]->Cancel();
+  }
+
+  // Make sure we kill any queued runnables that we never had a chance to run.
+  mQueuedRunnables.Clear();
+
+  // We no longer need to keep our inner scope.
+  mInnerScope = nsnull;
+  mScopeWN = nsnull;
+  mGlobal = NULL;
+
+  // And we can let our parent die now too.
+  mParent = nsnull;
+  mParentWN = nsnull;
+}
+
+void
+nsDOMWorker::Suspend()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  PRBool shouldSuspendFeatures;
+  {
+    MutexAutoLock lock(mLock);
+    NS_ASSERTION(!mSuspended, "Suspended more than once!");
+    shouldSuspendFeatures = !mSuspended;
+    mSuspended = PR_TRUE;
+  }
+
+  if (shouldSuspendFeatures) {
+    SuspendFeatures();
+  }
+}
+
+void
+nsDOMWorker::Resume()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  PRBool shouldResumeFeatures;
+  {
+    MutexAutoLock lock(mLock);
+#ifdef DEBUG
+    // Should only have a mismatch if GC or Cancel happened while suspended.
+    if (!mSuspended) {
+      NS_ASSERTION(mStatus == eCanceled ||
+                   (mStatus == eTerminated && !mWrappedNative),
+                   "Not suspended!");
+    }
+#endif
+    shouldResumeFeatures = mSuspended;
+    mSuspended = PR_FALSE;
+  }
+
+  if (shouldResumeFeatures) {
+    ResumeFeatures();
+  }
+
+  // Repost any events that were queued for the main thread while suspended.
+  PRUint32 count = mQueuedRunnables.Length();
+  for (PRUint32 index = 0; index < count; index++) {
+    NS_DispatchToCurrentThread(mQueuedRunnables[index]);
+  }
+  mQueuedRunnables.Clear();
+}
+
+PRBool
+nsDOMWorker::IsCanceled()
+{
+  MutexAutoLock lock(mLock);
+  return IsCanceledNoLock();
+}
+
+PRBool
+nsDOMWorker::IsCanceledNoLock()
+{
+  // If we haven't started the close process then we're not canceled.
+  if (mStatus == eRunning) {
+    return PR_FALSE;
+  }
+
+  // There are several conditions under which we want JS code to abort and all
+  // other functions to bail:
+  // 1. If we've already run our close handler then we are canceled forevermore.
+  // 2. If we've been terminated then we want to pretend to be canceled until
+  //    our close handler is scheduled and running.
+  // 3. If we've been canceled then we pretend to be canceled until the close
+  //    handler has been scheduled.
+  // 4. If the close handler has run for longer than the allotted time then we
+  //    should be canceled as well.
+  // 5. If we're on the main thread then we'll pretend to be canceled if the
+  //    user has navigated away from the page.
+  return mStatus == eKilled ||
+         (mStatus == eTerminated && !mExpirationTime) ||
+         (mStatus == eCanceled && !mExpirationTime) ||
+         (mExpirationTime && mExpirationTime != PR_INTERVAL_NO_TIMEOUT &&
+          mExpirationTime <= PR_IntervalNow()) ||
+         (mStatus == eCanceled && NS_IsMainThread());
+}
+
+PRBool
+nsDOMWorker::IsClosing()
+{
+  MutexAutoLock lock(mLock);
+  return mStatus != eRunning;
+}
+
+PRBool
+nsDOMWorker::IsSuspended()
+{
+  MutexAutoLock lock(mLock);
+  return mSuspended;
+}
+
+nsresult
+nsDOMWorker::PostMessageInternal(PRBool aToInner)
+{
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+  NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
+
+  nsAXPCNativeCallContext* cc;
+  nsresult rv = xpc->GetCurrentNativeCallContext(&cc);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED);
+
+  PRUint32 argc;
+  rv = cc->GetArgc(&argc);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!argc) {
+    return NS_ERROR_XPC_NOT_ENOUGH_ARGS;
+  }
+
+  jsval* argv;
+  rv = cc->GetArgvPtr(&argv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  JSContext* cx;
+  rv = cc->GetJSContext(&cx);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // If we're a ChromeWorker then we allow wrapped natives to be passed via
+  // structured cloning by supplying a custom write callback. To do that we need
+  // to make sure they stay alive while the message is being sent, so we collect
+  // the wrapped natives in an array to be packaged with the message.
+  JSStructuredCloneCallbacks callbacks = {
+    nsnull, IsPrivileged() ? WriteStructuredClone : nsnull, nsnull
+  };
+
+  JSAutoRequest ar(cx);
+
+  JSAutoStructuredCloneBuffer buffer;
+  nsTArray<nsCOMPtr<nsISupports> > wrappedNatives;
+  if (!buffer.write(cx, argv[0], &callbacks, &wrappedNatives)) {
+    return NS_ERROR_DOM_DATA_CLONE_ERR;
+  }
+
+  nsRefPtr<nsDOMWorkerMessageEvent> message = new nsDOMWorkerMessageEvent();
+  NS_ENSURE_TRUE(message, NS_ERROR_OUT_OF_MEMORY);
+
+  rv = message->InitMessageEvent(NS_LITERAL_STRING("message"), PR_FALSE,
+                                 PR_FALSE, EmptyString(), EmptyString(),
+                                 nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = message->SetJSData(cx, buffer, wrappedNatives);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<nsDOMFireEventRunnable> runnable =
+    new nsDOMFireEventRunnable(this, message, aToInner);
+  NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+
+  // If aToInner is true then we want to target the runnable at this worker's
+  // thread. Otherwise we need to target the parent's thread.
+  nsDOMWorker* target = aToInner ? this : mParent;
+
+  // If this is a top-level worker then target the main thread. Otherwise use
+  // the thread service to find the target's thread.
+  if (!target) {
+    nsCOMPtr<nsIThread> mainThread;
+    rv = NS_GetMainThread(getter_AddRefs(mainThread));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mainThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  else {
+    rv = nsDOMThreadService::get()->Dispatch(target, runnable);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+PRBool
+nsDOMWorker::SetGlobalForContext(JSContext* aCx, nsLazyAutoRequest *aRequest,
+                                 JSAutoEnterCompartment *aComp)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (!CompileGlobalObject(aCx, aRequest, aComp)) {
+    return PR_FALSE;
+  }
+
+  JS_SetGlobalObject(aCx, mGlobal);
+  return PR_TRUE;
+}
+
+PRBool
+nsDOMWorker::CompileGlobalObject(JSContext* aCx, nsLazyAutoRequest *aRequest,
+                                 JSAutoEnterCompartment *aComp)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  // On success, we enter a request and a cross-compartment call that both
+  // belong to the caller. But on failure, we must not remain in a request or
+  // cross-compartment call. So we enter both only locally at first. On
+  // failure, the local request and call will automatically get cleaned
+  // up. Once success is certain, we swap them into *aRequest and *aCall.
+  nsLazyAutoRequest localRequest;
+  JSAutoEnterCompartment localAutoCompartment;
+  localRequest.enter(aCx);
+
+  PRBool success;
+  if (mGlobal) {
+    success = localAutoCompartment.enter(aCx, mGlobal);
+    NS_ENSURE_TRUE(success, PR_FALSE);
+
+    aRequest->swap(localRequest);
+    aComp->swap(localAutoCompartment);
+    return PR_TRUE;
+  }
+
+  if (mCompileAttempted) {
+    // Don't try to recompile a bad script.
+    return PR_FALSE;
+  }
+  mCompileAttempted = PR_TRUE;
+
+  NS_ASSERTION(!mScriptURL.IsEmpty(), "Must have a url here!");
+
+  NS_ASSERTION(!JS_GetGlobalObject(aCx), "Global object should be unset!");
+
+  nsRefPtr<nsDOMWorkerScope> scope = new nsDOMWorkerScope(this);
+  NS_ENSURE_TRUE(scope, PR_FALSE);
+
+  nsISupports* scopeSupports = NS_ISUPPORTS_CAST(nsIWorkerScope*, scope);
+
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+
+  const PRUint32 flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES |
+                         nsIXPConnect::OMIT_COMPONENTS_OBJECT;
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> globalWrapper;
+  nsresult rv =
+    xpc->InitClassesWithNewWrappedGlobal(aCx, scopeSupports,
+                                         NS_GET_IID(nsISupports), nsnull,
+                                         NS_ISUPPORTS_CAST(nsIWorker*, this),
+                                         flags, getter_AddRefs(globalWrapper));
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+
+  JSObject* global;
+  rv = globalWrapper->GetJSObject(&global);
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+
+  NS_ASSERTION(JS_GetGlobalObject(aCx) == global, "Global object mismatch!");
+
+  success = localAutoCompartment.enter(aCx, global);
+  NS_ENSURE_TRUE(success, PR_FALSE);
+
+#ifdef DEBUG
+  {
+    jsval components;
+    if (JS_GetProperty(aCx, global, "Components", &components)) {
+      NS_ASSERTION(components == JSVAL_VOID,
+                   "Components property still defined!");
+    }
+  }
+#endif
+
+  // Set up worker thread functions.
+  success = JS_DefineFunctions(aCx, global, gDOMWorkerFunctions);
+  NS_ENSURE_TRUE(success, PR_FALSE);
+
+  success = JS_DefineProfilingFunctions(aCx, global);
+  NS_ENSURE_TRUE(success, PR_FALSE);
+
+  if (IsPrivileged()) {
+    // Add chrome functions.
+    success = JS_DefineFunctions(aCx, global, gDOMWorkerChromeFunctions);
+    NS_ENSURE_TRUE(success, PR_FALSE);
+
+    success = JS_DefineProperty(aCx, global, "XPCOM", JSVAL_VOID,
+                                nsDOMWorkerFunctions::XPCOMLazyGetter, nsnull,
+                                0);
+    NS_ENSURE_TRUE(success, PR_FALSE);
+
+#ifdef BUILD_CTYPES
+    // Add the lazy getter for ctypes.
+    success = JS_DefineProperty(aCx, global, "ctypes", JSVAL_VOID,
+                                nsDOMWorkerFunctions::CTypesLazyGetter, nsnull,
+                                0);
+    NS_ENSURE_TRUE(success, PR_FALSE);
+#endif
+  }
+
+  // From here on out we have to remember to null mGlobal, mInnerScope, and
+  // mScopeWN if something fails! We really don't need to hang on to mGlobal
+  // as long as we have mScopeWN, but it saves us a virtual call every time the
+  // worker is scheduled. Meh.
+  mGlobal = global;
+  mInnerScope = scope;
+  mScopeWN = scope->GetWrappedNative();
+  NS_ASSERTION(mScopeWN, "Should have a wrapped native here!");
+
+  nsRefPtr<nsDOMWorkerScriptLoader> loader =
+    new nsDOMWorkerScriptLoader(this);
+
+  rv = AddFeature(loader, aCx);
+  if (NS_FAILED(rv)) {
+    mGlobal = NULL;
+    mInnerScope = nsnull;
+    mScopeWN = nsnull;
+    return PR_FALSE;
+  }
+
+  rv = loader->LoadWorkerScript(aCx, mScriptURL);
+
+  JS_ReportPendingException(aCx);
+
+  if (NS_FAILED(rv)) {
+    mGlobal = NULL;
+    mInnerScope = nsnull;
+    mScopeWN = nsnull;
+    return PR_FALSE;
+  }
+
+  NS_ASSERTION(mPrincipal, "Script loader didn't set our principal!");
+  NS_ASSERTION(mBaseURI, "Script loader didn't set our base uri!");
+
+  // Make sure we kept the system principal.
+  if (IsPrivileged() && !nsContentUtils::IsSystemPrincipal(mPrincipal)) {
+    static const char warning[] = "ChromeWorker attempted to load a "
+                                  "non-chrome worker script!";
+    NS_WARNING(warning);
+
+    JS_ReportError(aCx, warning);
+
+    mGlobal = NULL;
+    mInnerScope = nsnull;
+    mScopeWN = nsnull;
+    return PR_FALSE;
+  }
+
+  rv = loader->ExecuteScripts(aCx);
+
+  JS_ReportPendingException(aCx);
+
+  if (NS_FAILED(rv)) {
+    mGlobal = NULL;
+    mInnerScope = nsnull;
+    mScopeWN = nsnull;
+    return PR_FALSE;
+  }
+
+  aRequest->swap(localRequest);
+  aComp->swap(localAutoCompartment);
+  return PR_TRUE;
+}
+
+void
+nsDOMWorker::SetPool(nsDOMWorkerPool* aPool)
+{
+  NS_ASSERTION(!mPool, "Shouldn't ever set pool more than once!");
+  mPool = aPool;
+}
+
+already_AddRefed<nsIXPConnectWrappedNative>
+nsDOMWorker::GetWrappedNative()
+{
+  nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+  {
+    MutexAutoLock lock(mLock);
+    wrappedNative = mWrappedNative;
+  }
+  return wrappedNative.forget();
+}
+
+nsresult
+nsDOMWorker::AddFeature(nsDOMWorkerFeature* aFeature,
+                        JSContext* aCx)
+{
+  NS_ASSERTION(aFeature, "Null pointer!");
+
+  PRBool shouldSuspend;
+  {
+    // aCx may be null.
+    JSAutoSuspendRequest asr(aCx);
+
+    MutexAutoLock lock(mLock);
+
+    if (mStatus == eKilled) {
+      // No features may be added after we've been canceled. Sorry.
+      return NS_ERROR_FAILURE;
+    }
+
+    nsDOMWorkerFeature** newFeature = mFeatures.AppendElement(aFeature);
+    NS_ENSURE_TRUE(newFeature, NS_ERROR_OUT_OF_MEMORY);
+
+    aFeature->FreeToDie(PR_FALSE);
+    shouldSuspend = mFeatureSuspendDepth > 0;
+  }
+
+  if (shouldSuspend) {
+    aFeature->Suspend();
+  }
+
+  return NS_OK;
+}
+
+void
+nsDOMWorker::RemoveFeature(nsDOMWorkerFeature* aFeature,
+                           JSContext* aCx)
+{
+  NS_ASSERTION(aFeature, "Null pointer!");
+
+  // This *must* be a nsRefPtr so that we call Release after setting FreeToDie.
+  nsRefPtr<nsDOMWorkerFeature> feature(aFeature);
+  {
+    // aCx may be null.
+    JSAutoSuspendRequest asr(aCx);
+
+    MutexAutoLock lock(mLock);
+
+#ifdef DEBUG
+    PRBool removed =
+#endif
+    mFeatures.RemoveElement(aFeature);
+    NS_ASSERTION(removed, "Feature not in the list!");
+
+    aFeature->FreeToDie(PR_TRUE);
+  }
+}
+
+void
+nsDOMWorker::CancelTimeoutWithId(PRUint32 aId)
+{
+  nsRefPtr<nsDOMWorkerFeature> foundFeature;
+  {
+    MutexAutoLock lock(mLock);
+    PRUint32 count = mFeatures.Length();
+    for (PRUint32 index = 0; index < count; index++) {
+      nsDOMWorkerFeature*& feature = mFeatures[index];
+      if (feature->HasId() && feature->GetId() == aId) {
+        foundFeature = feature;
+        feature->FreeToDie(PR_TRUE);
+        mFeatures.RemoveElementAt(index);
+        break;
+      }
+    }
+  }
+
+  if (foundFeature) {
+    foundFeature->Cancel();
+  }
+}
+
+void
+nsDOMWorker::SuspendFeatures()
+{
+  nsAutoTArray<nsRefPtr<nsDOMWorkerFeature>, 20> features;
+  {
+    MutexAutoLock lock(mLock);
+
+    // We don't really have to worry about overflow here because the only way
+    // to do this is through recursive script loading, which uses the stack. We
+    // would exceed our stack limit long before this counter.
+    NS_ASSERTION(mFeatureSuspendDepth < PR_UINT32_MAX, "Shouldn't happen!");
+    if (++mFeatureSuspendDepth != 1) {
+      // Allow nested suspending of timeouts.
+      return;
+    }
+
+#ifdef DEBUG
+    nsRefPtr<nsDOMWorkerFeature>* newFeatures =
+#endif
+    features.AppendElements(mFeatures);
+    NS_WARN_IF_FALSE(newFeatures, "Out of memory!");
+  }
+
+  PRUint32 count = features.Length();
+  for (PRUint32 i = 0; i < count; i++) {
+    features[i]->Suspend();
+  }
+}
+
+void
+nsDOMWorker::ResumeFeatures()
+{
+  nsAutoTArray<nsRefPtr<nsDOMWorkerFeature>, 20> features;
+  {
+    MutexAutoLock lock(mLock);
+
+    NS_ASSERTION(mFeatureSuspendDepth > 0, "Shouldn't happen!");
+    if (--mFeatureSuspendDepth != 0) {
+      return;
+    }
+
+    features.AppendElements(mFeatures);
+  }
+
+  PRUint32 count = features.Length();
+  for (PRUint32 i = 0; i < count; i++) {
+    features[i]->Resume();
+  }
+}
+
+void
+nsDOMWorker::SetPrincipal(nsIPrincipal* aPrincipal)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aPrincipal, "Null pointer!");
+
+  mPrincipal = aPrincipal;
+}
+
+nsresult
+nsDOMWorker::SetBaseURI(nsIURI* aURI)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aURI, "Don't hand me a null pointer!");
+
+  mBaseURI = aURI;
+
+  nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
+  NS_ENSURE_TRUE(url, NS_ERROR_NO_INTERFACE);
+
+  mLocation = nsDOMWorkerLocation::NewLocation(url);
+  NS_ENSURE_TRUE(mLocation, NS_ERROR_FAILURE);
+
+  return NS_OK;
+}
+
+void
+nsDOMWorker::ClearBaseURI()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  mBaseURI = nsnull;
+  mLocation = nsnull;
+}
+
+nsresult
+nsDOMWorker::FireCloseRunnable(PRIntervalTime aTimeoutInterval,
+                               PRBool aClearQueue,
+                               PRBool aFromFinalize)
+{
+  // Resume the worker (but not its features) if we're currently suspended. This
+  // should only ever happen if we are being called from Cancel (page falling
+  // out of bfcache or quitting) or Finalize, in which case all we really want
+  // to do is unblock the waiting thread.
+  PRBool wakeUp;
+  {
+    MutexAutoLock lock(mLock);
+    NS_ASSERTION(mExpirationTime == 0,
+                 "Close runnable should not be scheduled already!");
+
+    if ((wakeUp = mSuspended)) {
+      NS_ASSERTION(mStatus == eCanceled ||
+                   (mStatus == eTerminated && aFromFinalize),
+                   "How can this happen otherwise?!");
+      mSuspended = PR_FALSE;
+    }
+  }
+
+  if (wakeUp) {
+    ReentrantMonitorAutoEnter mon(mPool->GetReentrantMonitor());
+    mon.NotifyAll();
+  }
+
+  nsRefPtr<nsDOMWorkerEvent> event = new nsDOMWorkerEvent();
+  NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv =
+    event->InitEvent(NS_LITERAL_STRING("close"), PR_FALSE, PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<nsDOMFireEventRunnable> runnable =
+    new nsDOMFireEventRunnable(this, event, PR_TRUE);
+  NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+
+  // Our worker has been collected and we want to keep the inner scope alive,
+  // so pass that along in the runnable.
+  if (aFromFinalize) {
+    // Make sure that our scope wrapped native exists here, but if the worker
+    // script failed to compile then it will be null already.
+    if (mGlobal) {
+      NS_ASSERTION(mScopeWN, "This shouldn't be null!");
+    }
+    runnable->ReplaceWrappedNative(mScopeWN);
+  }
+
+  return nsDOMThreadService::get()->Dispatch(this, runnable, aTimeoutInterval,
+                                             aClearQueue);
+}
+
+nsresult
+nsDOMWorker::Close()
+{
+  {
+    MutexAutoLock lock(mLock);
+    NS_ASSERTION(mStatus != eKilled, "This should be impossible!");
+    if (mStatus != eRunning) {
+      return NS_OK;
+    }
+    mStatus = eClosed;
+  }
+
+  nsresult rv = FireCloseRunnable(PR_INTERVAL_NO_TIMEOUT, PR_FALSE, PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorker::TerminateInternal(PRBool aFromFinalize)
+{
+  {
+    MutexAutoLock lock(mLock);
+#ifdef DEBUG
+    if (!aFromFinalize) {
+      NS_ASSERTION(mStatus != eCanceled, "Shouldn't be able to get here!");
+    }
+#endif
+
+    if (mStatus == eRunning) {
+      // This is the beginning of the close process, fire an event and prevent
+      // any other close events from being generated.
+      mStatus = eTerminated;
+    }
+    else {
+      if (mStatus == eClosed) {
+        // The worker was previously closed which means that an expiration time
+        // might not be set. Setting the status to eTerminated will force the
+        // worker to jump to its close handler.
+        mStatus = eTerminated;
+      }
+      // No need to fire another close handler, it has already been done.
+      return NS_OK;
+    }
+  }
+
+  nsresult rv = FireCloseRunnable(PR_INTERVAL_NO_TIMEOUT, PR_TRUE,
+                                  aFromFinalize);
+  if (rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN) {
+    return rv;
+  }
+
+  // Warn about other kinds of failures.
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+already_AddRefed<nsDOMWorker>
+nsDOMWorker::GetParent()
+{
+  nsRefPtr<nsDOMWorker> parent(mParent);
+  return parent.forget();
+}
+
+void
+nsDOMWorker::SetExpirationTime(PRIntervalTime aExpirationTime)
+{
+  {
+    MutexAutoLock lock(mLock);
+
+    NS_ASSERTION(mStatus != eRunning && mStatus != eKilled, "Bad status!");
+    NS_ASSERTION(!mExpirationTime || mExpirationTime == PR_INTERVAL_NO_TIMEOUT,
+                 "Overwriting a timeout that was previously set!");
+
+    mExpirationTime = aExpirationTime;
+  }
+}
+
+#ifdef DEBUG
+PRIntervalTime
+nsDOMWorker::GetExpirationTime()
+{
+  MutexAutoLock lock(mLock);
+  return mExpirationTime;
+}
+#endif
+
+// static
+JSObject*
+nsDOMWorker::ReadStructuredClone(JSContext* aCx,
+                                 JSStructuredCloneReader* aReader,
+                                 uint32 aTag,
+                                 uint32 aData,
+                                 void* aClosure)
+{
+  NS_ASSERTION(aCx, "Null context!");
+  NS_ASSERTION(aReader, "Null reader!");
+  NS_ASSERTION(!aClosure, "Shouldn't have a closure here!");
+
+  if (aTag == DOMWORKER_SCTAG_WRAPPEDNATIVE) {
+    NS_ASSERTION(!aData, "Huh?");
+
+    nsISupports* wrappedNative;
+    if (JS_ReadBytes(aReader, &wrappedNative, sizeof(wrappedNative))) {
+      NS_ASSERTION(wrappedNative, "Null pointer?!");
+
+      JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx));
+      if (global) {
+        jsval val;
+        nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
+        if (NS_SUCCEEDED(nsContentUtils::WrapNative(aCx, global, wrappedNative,
+                                                    &val,
+                                                    getter_AddRefs(wrapper)))) {
+          return JSVAL_TO_OBJECT(val);
+        }
+      }
+    }
+  }
+
+  // Something failed above, try using the runtime callbacks instead.
+  const JSStructuredCloneCallbacks* runtimeCallbacks =
+    aCx->runtime->structuredCloneCallbacks;
+  if (runtimeCallbacks) {
+    return runtimeCallbacks->read(aCx, aReader, aTag, aData, nsnull);
+  }
+
+  // We can't handle this object, throw an exception if one hasn't been thrown
+  // already.
+  if (!JS_IsExceptionPending(aCx)) {
+    nsDOMClassInfo::ThrowJSException(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+  }
+  return nsnull;
+}
+
+PRBool
+nsDOMWorker::QueueSuspendedRunnable(nsIRunnable* aRunnable)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  return mQueuedRunnables.AppendElement(aRunnable) ? PR_TRUE : PR_FALSE;
+}
+
+NS_IMETHODIMP
+nsDOMWorker::RemoveEventListener(const nsAString& aType,
+                                 nsIDOMEventListener* aListener,
+                                 PRBool aUseCapture)
+{
+  if (IsCanceled()) {
+    return NS_OK;
+  }
+
+  return nsDOMWorkerMessageHandler::RemoveEventListener(aType, aListener,
+                                                        aUseCapture);
+}
+
+NS_IMETHODIMP
+nsDOMWorker::DispatchEvent(nsIDOMEvent* aEvent,
+                           PRBool* _retval)
+{
+  {
+    MutexAutoLock lock(mLock);
+    if (IsCanceledNoLock()) {
+      return NS_OK;
+    }
+    if (mStatus == eTerminated) {
+      nsCOMPtr<nsIWorkerMessageEvent> messageEvent(do_QueryInterface(aEvent));
+      if (messageEvent) {
+        // This is a message event targeted to a terminated worker. Ignore it.
+        return NS_OK;
+      }
+    }
+  }
+
+  return nsDOMWorkerMessageHandler::DispatchEvent(aEvent, _retval);
+}
+
+NS_IMETHODIMP
+nsDOMWorker::AddEventListener(const nsAString& aType,
+                              nsIDOMEventListener* aListener,
+                              PRBool aUseCapture,
+                              PRBool aWantsUntrusted,
+                              PRUint8 aOptionalArgc)
+{
+  NS_ASSERTION(mWrappedNative, "Called after Finalize!");
+  if (IsCanceled()) {
+    return NS_OK;
+  }
+
+  return nsDOMWorkerMessageHandler::AddEventListener(aType, aListener,
+                                                     aUseCapture,
+                                                     aWantsUntrusted,
+                                                     aOptionalArgc);
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::PostMessage(/* JSObject aMessage */)
+{
+  {
+    MutexAutoLock lock(mLock);
+    // There's no reason to dispatch this message after the close handler has
+    // been triggered since it will never be allowed to run.
+    if (mStatus != eRunning) {
+      return NS_OK;
+    }
+  }
+
+  return PostMessageInternal(PR_TRUE);
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::GetOnerror(nsIDOMEventListener** aOnerror)
+{
+  NS_ENSURE_ARG_POINTER(aOnerror);
+
+  if (IsCanceled()) {
+    *aOnerror = nsnull;
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(NS_LITERAL_STRING("error"));
+
+  listener.forget(aOnerror);
+  return NS_OK;
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::SetOnerror(nsIDOMEventListener* aOnerror)
+{
+  NS_ASSERTION(mWrappedNative, "Called after Finalize!");
+  if (IsCanceled()) {
+    return NS_OK;
+  }
+
+  return SetOnXListener(NS_LITERAL_STRING("error"), aOnerror);
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::GetOnmessage(nsIDOMEventListener** aOnmessage)
+{
+  NS_ENSURE_ARG_POINTER(aOnmessage);
+
+  if (IsCanceled()) {
+    *aOnmessage = nsnull;
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(NS_LITERAL_STRING("message"));
+
+  listener.forget(aOnmessage);
+  return NS_OK;
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::SetOnmessage(nsIDOMEventListener* aOnmessage)
+{
+  NS_ASSERTION(mWrappedNative, "Called after Finalize!");
+  if (IsCanceled()) {
+    return NS_OK;
+  }
+
+  return SetOnXListener(NS_LITERAL_STRING("message"), aOnmessage);
+}
+
+NS_IMETHODIMP
+nsDOMWorker::Terminate()
+{
+  return TerminateInternal(PR_FALSE);
+}
+
+NS_IMETHODIMP
+nsDOMWorker::Notify(nsITimer* aTimer)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  Kill();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWorkerFactory::NewChromeWorker(nsIWorker** _retval)
+{
+  nsresult rv;
+
+  // Get the arguments from XPConnect.
+  nsCOMPtr<nsIXPConnect> xpc;
+  xpc = do_GetService(nsIXPConnect::GetCID());
+  NS_ASSERTION(xpc, "Could not get XPConnect");
+
+  nsAXPCNativeCallContext* cc;
+  rv = xpc->GetCurrentNativeCallContext(&cc);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  JSContext* cx;
+  rv = cc->GetJSContext(&cx);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 argc;
+  rv = cc->GetArgc(&argc);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  jsval* argv;
+  rv = cc->GetArgvPtr(&argv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Determine the current script global. We need it to register the worker.
+  // NewChromeDOMWorker will check that we are chrome, so no access check.
+  JSObject* global = JS_GetGlobalForScopeChain(cx);
+  NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
+
+  // May be null if we're being called from a JSM or something.
+  nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
+    nsJSUtils::GetStaticScriptGlobal(cx, global);
+
+  // Create, initialize, and return the worker.
+  nsRefPtr<nsDOMWorker> chromeWorker;
+  rv = nsDOMWorker::NewChromeDOMWorker(getter_AddRefs(chromeWorker));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = chromeWorker->InitializeInternal(scriptGlobal, cx, global, argc, argv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  chromeWorker.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS1(nsWorkerFactory, nsIWorkerFactory)
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorker.h
@@ -0,0 +1,471 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NSDOMWORKER_H__
+#define __NSDOMWORKER_H__
+
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMWorkers.h"
+#include "nsIJSNativeInitializer.h"
+#include "nsIPrincipal.h"
+#include "nsITimer.h"
+#include "nsIURI.h"
+#include "nsIXPCScriptable.h"
+
+#include "jsapi.h"
+#include "mozilla/Mutex.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsTPtrArray.h"
+
+#include "nsDOMWorkerMessageHandler.h"
+
+// {1295EFB5-8644-42B2-8B8E-80EEF56E4284}
+#define NS_WORKERFACTORY_CID \
+ {0x1295efb5, 0x8644, 0x42b2, \
+  {0x8b, 0x8e, 0x80, 0xee, 0xf5, 0x6e, 0x42, 0x84} }
+
+class nsDOMWorker;
+class nsDOMWorkerFeature;
+class nsDOMWorkerMessageHandler;
+class nsDOMWorkerNavigator;
+class nsDOMWorkerPool;
+class nsDOMWorkerTimeout;
+class nsICancelable;
+class nsIDOMEventListener;
+class nsIEventTarget;
+class nsIRunnable;
+class nsIScriptGlobalObject;
+class nsIXPConnectWrappedNative;
+
+class nsDOMWorkerScope : public nsDOMWorkerMessageHandler,
+                         public nsIWorkerScope,
+                         public nsIXPCScriptable
+{
+  friend class nsDOMWorker;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsIDOMEventHandler
+  NS_FORWARD_INTERNAL_NSIDOMEVENTTARGET(nsDOMWorkerMessageHandler::)
+  NS_IMETHOD AddEventListener(const nsAString& aType,
+                              nsIDOMEventListener* aListener,
+                              PRBool aUseCapture,
+                              PRBool aWantsUntrusted,
+                              PRUint8 optional_argc);
+  NS_IMETHOD RemoveEventListener(const nsAString& aType,
+                                 nsIDOMEventListener* aListener,
+                                 PRBool aUseCapture);
+  NS_IMETHOD DispatchEvent(nsIDOMEvent* aEvent,
+                           PRBool* _retval);
+  NS_DECL_NSIWORKERGLOBALSCOPE
+  NS_DECL_NSIWORKERSCOPE
+  NS_DECL_NSIXPCSCRIPTABLE
+  NS_DECL_NSICLASSINFO
+
+  typedef NS_STDCALL_FUNCPROTO(nsresult, SetListenerFunc, nsDOMWorkerScope,
+                               SetOnmessage, (nsIDOMEventListener*));
+
+  nsDOMWorkerScope(nsDOMWorker* aWorker);
+
+protected:
+  already_AddRefed<nsIXPConnectWrappedNative> GetWrappedNative();
+
+private:
+  nsDOMWorker* mWorker;
+  nsIXPConnectWrappedNative* mWrappedNative;
+
+  nsRefPtr<nsDOMWorkerNavigator> mNavigator;
+
+  PRPackedBool mHasOnerror;
+};
+
+class nsLazyAutoRequest
+{
+public:
+  nsLazyAutoRequest() : mCx(nsnull) {}
+
+  ~nsLazyAutoRequest() {
+    if (mCx)
+      JS_EndRequest(mCx);
+  }
+
+  void enter(JSContext *aCx) {
+    JS_BeginRequest(aCx);
+    mCx = aCx;
+  }
+
+  bool entered() const { return mCx != nsnull; }
+
+  void swap(nsLazyAutoRequest &other) {
+    JSContext *tmp = mCx;
+    mCx = other.mCx;
+    other.mCx = tmp;
+  }
+
+private:
+  JSContext *mCx;
+};
+
+class nsDOMWorker : public nsDOMWorkerMessageHandler,
+                    public nsIWorker,
+                    public nsITimerCallback,
+                    public nsIJSNativeInitializer,
+                    public nsIXPCScriptable
+{
+  typedef mozilla::Mutex Mutex;
+
+  friend class nsDOMWorkerFeature;
+  friend class nsDOMWorkerFunctions;
+  friend class nsDOMWorkerScope;
+  friend class nsDOMWorkerScriptLoader;
+  friend class nsDOMWorkerTimeout;
+  friend class nsDOMWorkerXHR;
+  friend class nsDOMWorkerXHRProxy;
+  friend class nsReportErrorRunnable;
+  friend class nsDOMFireEventRunnable;
+
+  friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
+  friend void DOMWorkerErrorReporter(JSContext* aCx,
+                                     const char* aMessage,
+                                     JSErrorReport* aReport);
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsIDOMEventHandler
+  NS_FORWARD_INTERNAL_NSIDOMEVENTTARGET(nsDOMWorkerMessageHandler::)
+  NS_IMETHOD AddEventListener(const nsAString& aType,
+                              nsIDOMEventListener* aListener,
+                              PRBool aUseCapture,
+                              PRBool aWantsUntrusted,
+                              PRUint8 optional_argc);
+  NS_IMETHOD RemoveEventListener(const nsAString& aType,
+                                 nsIDOMEventListener* aListener,
+                                 PRBool aUseCapture);
+  NS_IMETHOD DispatchEvent(nsIDOMEvent* aEvent,
+                           PRBool* _retval);
+
+  NS_DECL_NSIABSTRACTWORKER
+  NS_DECL_NSIWORKER
+  NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSICLASSINFO
+  NS_DECL_NSIXPCSCRIPTABLE
+
+  static nsresult NewWorker(nsISupports** aNewObject);
+  static nsresult NewChromeWorker(nsISupports** aNewObject);
+  static nsresult NewChromeDOMWorker(nsDOMWorker** aNewObject);
+
+  enum WorkerPrivilegeModel { CONTENT, CHROME };
+
+  nsDOMWorker(nsDOMWorker* aParent,
+              nsIXPConnectWrappedNative* aParentWN,
+              WorkerPrivilegeModel aModel);
+
+  NS_IMETHOD Initialize(nsISupports* aOwner,
+                        JSContext* aCx,
+                        JSObject* aObj,
+                        PRUint32 aArgc,
+                        jsval* aArgv);
+
+  nsresult InitializeInternal(nsIScriptGlobalObject* aOwner,
+                              JSContext* aCx,
+                              JSObject* aObj,
+                              PRUint32 aArgc,
+                              jsval* aArgv);
+
+  void Cancel();
+  void Kill();
+  void Suspend();
+  void Resume();
+
+  // This just calls IsCanceledNoLock with an autolock around the call.
+  PRBool IsCanceled();
+
+  PRBool IsClosing();
+  PRBool IsSuspended();
+
+  PRBool SetGlobalForContext(JSContext* aCx, nsLazyAutoRequest *aRequest, JSAutoEnterCompartment *aComp);
+
+  void SetPool(nsDOMWorkerPool* aPool);
+
+  nsDOMWorkerPool* Pool() {
+    return mPool;
+  }
+
+  Mutex& GetLock() {
+    return mLock;
+  }
+
+  already_AddRefed<nsIXPConnectWrappedNative> GetWrappedNative();
+  already_AddRefed<nsDOMWorker> GetParent();
+
+  nsDOMWorkerScope* GetInnerScope() {
+    return mInnerScope;
+  }
+
+  void SetExpirationTime(PRIntervalTime aExpirationTime);
+#ifdef DEBUG
+  PRIntervalTime GetExpirationTime();
+#endif
+
+  PRBool IsPrivileged() {
+    return mPrivilegeModel == CHROME;
+  }
+
+  static JSObject* ReadStructuredClone(JSContext* aCx,
+                                       JSStructuredCloneReader* aReader,
+                                       uint32 aTag,
+                                       uint32 aData,
+                                       void* aClosure);
+
+  /**
+   * Use this chart to help figure out behavior during each of the closing
+   * statuses. Details below.
+   * 
+   * +=============+=============+=================+=======================+
+   * |   status    | clear queue | abort execution | close handler timeout |
+   * +=============+=============+=================+=======================+
+   * |   eClosed   |     yes     |       no        |          no           |
+   * +-------------+-------------+-----------------+-----------------------+
+   * | eTerminated |     yes     |       yes       |          no           |
+   * +-------------+-------------+-----------------+-----------------------+
+   * |  eCanceled  |     yes     |       yes       |          yes          |
+   * +-------------+-------------+-----------------+-----------------------+
+   * 
+   */
+
+  enum DOMWorkerStatus {
+    // This status means that the close handler has not yet been scheduled.
+    eRunning = 0,
+
+    // Inner script called Close() on the worker global scope. Setting this
+    // status causes the worker to clear its queue of events but does not abort
+    // the currently running script. The close handler is also scheduled with
+    // no expiration time. This status may be superseded by 'eTerminated' in
+    // which case the currently running script will be aborted as detailed
+    // below. It may also be superseded by 'eCanceled' at which point the close
+    // handler will be assigned an expiration time. Once the close handler has
+    // completed or timed out the status will be changed to 'eKilled'.
+    eClosed,
+
+    // Outer script called Terminate() on the worker or the worker object was
+    // garbage collected in its outer script. Setting this status causes the
+    // worker to abort immediately, clear its queue of events, and schedules the
+    // close handler with no expiration time. This status may be superseded by
+    // 'eCanceled' at which point the close handler will have an expiration time
+    // assigned. Once the close handler has completed or timed out the status
+    // will be changed to 'eKilled'.
+    eTerminated,
+
+    // Either the user navigated away from the owning page, the owning page fell
+    // out of bfcache, or the user quit the application. Setting this status
+    // causes the worker to abort immediately and schedules the close handler
+    // with an expiration time. Since the page has gone away the worker may not
+    // post any messages. Once the close handler has completed or timed out the
+    // status will be changed to 'eKilled'.
+    eCanceled,
+
+    // The close handler has run and the worker is effectively dead.
+    eKilled
+  };
+
+private:
+  ~nsDOMWorker();
+
+  nsresult PostMessageInternal(PRBool aToInner);
+
+  PRBool CompileGlobalObject(JSContext* aCx, nsLazyAutoRequest *aRequest, JSAutoEnterCompartment *aComp);
+
+  PRUint32 NextTimeoutId() {
+    return ++mNextTimeoutId;
+  }
+
+  nsresult AddFeature(nsDOMWorkerFeature* aFeature,
+                      JSContext* aCx);
+  void RemoveFeature(nsDOMWorkerFeature* aFeature,
+                     JSContext* aCx);
+  void CancelTimeoutWithId(PRUint32 aId);
+  void SuspendFeatures();
+  void ResumeFeatures();
+
+  nsIPrincipal* GetPrincipal() {
+    return mPrincipal;
+  }
+
+  void SetPrincipal(nsIPrincipal* aPrincipal);
+
+  nsIURI* GetBaseURI() {
+    return mBaseURI;
+  }
+
+  nsresult SetBaseURI(nsIURI* aURI);
+
+  void ClearBaseURI();
+
+  nsresult FireCloseRunnable(PRIntervalTime aTimeoutInterval,
+                             PRBool aClearQueue,
+                             PRBool aFromFinalize);
+  nsresult Close();
+
+  nsresult TerminateInternal(PRBool aFromFinalize);
+
+  nsIWorkerLocation* GetLocation() {
+    return mLocation;
+  }
+
+  PRBool QueueSuspendedRunnable(nsIRunnable* aRunnable);
+
+  // Determines if the worker should be considered "canceled". See the large
+  // comment in the implementation for more details.
+  PRBool IsCanceledNoLock();
+
+private:
+
+  // mParent will live as long as mParentWN but only mParentWN will keep the JS
+  // reflection alive, so we only hold one strong reference to mParentWN.
+  nsDOMWorker* mParent;
+  nsCOMPtr<nsIXPConnectWrappedNative> mParentWN;
+
+  // Whether or not this worker has chrome privileges. Never changed after the
+  // worker is created.
+  WorkerPrivilegeModel mPrivilegeModel;
+
+  Mutex mLock;
+
+  nsRefPtr<nsDOMWorkerPool> mPool;
+
+  nsDOMWorkerScope* mInnerScope;
+  nsCOMPtr<nsIXPConnectWrappedNative> mScopeWN;
+  JSObject* mGlobal;
+
+  PRUint32 mNextTimeoutId;
+
+  nsTArray<nsDOMWorkerFeature*> mFeatures;
+  PRUint32 mFeatureSuspendDepth;
+
+  nsString mScriptURL;
+
+  nsIXPConnectWrappedNative* mWrappedNative;
+
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  nsCOMPtr<nsIURI> mBaseURI;
+
+  PRInt32 mErrorHandlerRecursionCount;
+
+  // Always protected by mLock
+  DOMWorkerStatus mStatus;
+
+  // Always protected by mLock
+  PRIntervalTime mExpirationTime;
+
+  nsCOMPtr<nsITimer> mKillTimer;
+
+  nsCOMPtr<nsIWorkerLocation> mLocation;
+
+  nsTArray<nsCOMPtr<nsIRunnable> > mQueuedRunnables;
+
+  PRPackedBool mSuspended;
+  PRPackedBool mCompileAttempted;
+};
+
+/**
+ * A worker "feature" holds the worker alive yet can be canceled, paused, and
+ * resumed by the worker. It is up to each derived class to implement these
+ * methods. This class uses a custom implementation of Release in order to
+ * ensure no races between Cancel and object destruction can occur, so derived
+ * classes must use the ISUPPORTS_INHERITED macros.
+ *
+ * To use this class you should inherit it and use the ISUPPORTS_INHERITED
+ * macros. Then add or remove an instance to the worker using the
+ * AddFeature/RemoveFeature functions. 
+ */
+class nsDOMWorkerFeature : public nsISupports
+{
+  friend class nsDOMWorker;
+
+public:
+  NS_DECL_ISUPPORTS
+
+  nsDOMWorkerFeature(nsDOMWorker* aWorker)
+  : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()), mId(0),
+    mHasId(PR_FALSE), mFreeToDie(PR_TRUE) { }
+
+  nsDOMWorkerFeature(nsDOMWorker* aWorker, PRUint32 aId)
+  : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()), mId(aId),
+    mHasId(PR_TRUE), mFreeToDie(PR_TRUE) { }
+
+  virtual void Cancel() = 0;
+  virtual void Suspend() { }
+  virtual void Resume() { }
+
+  PRUint32 GetId() {
+    return mId;
+  }
+
+  PRBool HasId() {
+    return mHasId;
+  }
+
+protected:
+  virtual ~nsDOMWorkerFeature() { }
+
+private:
+  void FreeToDie(PRBool aFreeToDie) {
+    mFreeToDie = aFreeToDie;
+  }
+
+protected:
+  nsRefPtr<nsDOMWorker> mWorker;
+  nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
+  PRUint32 mId;
+
+private:
+  PRPackedBool mHasId;
+  PRPackedBool mFreeToDie;
+};
+
+class nsWorkerFactory : public nsIWorkerFactory
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIWORKERFACTORY
+};
+
+#endif /* __NSDOMWORKER_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerEvents.cpp
@@ -0,0 +1,635 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMWorkerEvents.h"
+
+#include "nsIXMLHttpRequest.h"
+#include "nsIXPConnect.h"
+
+#include "jsapi.h"
+#include "nsAXPCNativeCallContext.h"
+#include "nsContentUtils.h"
+#include "nsThreadUtils.h"
+
+#include "nsDOMWorkerMessageHandler.h"
+#include "nsDOMThreadService.h"
+#include "nsDOMWorkerXHR.h"
+#include "nsDOMWorkerXHRProxy.h"
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIDOMWorkerPrivateEvent,
+                              NS_IDOMWORKERPRIVATEEVENT_IID)
+
+nsDOMWorkerPrivateEvent::nsDOMWorkerPrivateEvent(nsIDOMEvent* aEvent)
+: mEvent(aEvent),
+  mProgressEvent(do_QueryInterface(aEvent)),
+  mMessageEvent(do_QueryInterface(aEvent)),
+  mErrorEvent(do_QueryInterface(aEvent)),
+  mPreventDefaultCalled(PR_FALSE)
+{
+  NS_ASSERTION(aEvent, "Null pointer!");
+}
+
+NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerPrivateEvent)
+NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerPrivateEvent)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMWorkerPrivateEvent)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMWorkerPrivateEvent)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEvent, nsIDOMWorkerPrivateEvent)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMWorkerPrivateEvent)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMProgressEvent, mProgressEvent)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIWorkerMessageEvent, mMessageEvent)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIWorkerErrorEvent, mErrorEvent)
+  NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerPrivateEvent, nsIDOMEvent)
+
+NS_IMPL_THREADSAFE_DOM_CI_HELPER(nsDOMWorkerPrivateEvent)
+NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(nsDOMWorkerPrivateEvent)
+
+NS_IMETHODIMP
+nsDOMWorkerPrivateEvent::GetInterfaces(PRUint32* aCount, nsIID*** aArray)
+{
+  nsCOMPtr<nsIClassInfo> ci(do_QueryInterface(mEvent));
+  if (ci) {
+    return ci->GetInterfaces(aCount, aArray);
+  }
+  return NS_CI_INTERFACE_GETTER_NAME(nsDOMWorkerPrivateEvent)(aCount, aArray);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPrivateEvent::PreventDefault()
+{
+  PRBool cancelable = PR_FALSE;
+  mEvent->GetCancelable(&cancelable);
+
+  if (cancelable) {
+    mPreventDefaultCalled = PR_TRUE;
+  }
+
+  return mEvent->PreventDefault();
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPrivateEvent::GetDefaultPrevented(PRBool* aRetVal)
+{
+  *aRetVal = mPreventDefaultCalled;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPrivateEvent::InitEvent(const nsAString& aEventType,
+                                   PRBool aCanBubble,
+                                   PRBool aCancelable)
+{
+  mPreventDefaultCalled = PR_FALSE;
+  return mEvent->InitEvent(aEventType, aCanBubble, aCancelable);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPrivateEvent::InitProgressEvent(const nsAString& aTypeArg,
+                                           PRBool aCanBubbleArg,
+                                           PRBool aCancelableArg,
+                                           PRBool aLengthComputableArg,
+                                           PRUint64 aLoadedArg,
+                                           PRUint64 aTotalArg)
+{
+  NS_ASSERTION(mProgressEvent, "Impossible!");
+
+  mPreventDefaultCalled = PR_FALSE;
+  return mProgressEvent->InitProgressEvent(aTypeArg, aCanBubbleArg,
+                                           aCancelableArg, aLengthComputableArg,
+                                           aLoadedArg, aTotalArg);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPrivateEvent::InitMessageEvent(const nsAString& aTypeArg,
+                                          PRBool aCanBubbleArg,
+                                          PRBool aCancelableArg,
+                                          const nsAString& aDataArg,
+                                          const nsAString& aOriginArg,
+                                          nsISupports* aSourceArg)
+{
+  NS_ASSERTION(mMessageEvent, "Impossible!");
+
+  mPreventDefaultCalled = PR_FALSE;
+  return mMessageEvent->InitMessageEvent(aTypeArg, aCanBubbleArg,
+                                         aCancelableArg, aDataArg, aOriginArg,
+                                         aSourceArg);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPrivateEvent::InitErrorEvent(const nsAString& aTypeArg,
+                                        PRBool aCanBubbleArg,
+                                        PRBool aCancelableArg,
+                                        const nsAString& aMessageArg,
+                                        const nsAString& aFilenameArg,
+                                        PRUint32 aLinenoArg)
+{
+  NS_ASSERTION(mErrorEvent, "Impossible!");
+
+  mPreventDefaultCalled = PR_FALSE;
+  return mErrorEvent->InitErrorEvent(aTypeArg, aCanBubbleArg, aCancelableArg,
+                                     aMessageArg, aFilenameArg, aLinenoArg);
+}
+
+PRBool
+nsDOMWorkerPrivateEvent::PreventDefaultCalled()
+{
+  return mPreventDefaultCalled;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerEvent, nsIDOMEvent,
+                                                nsIClassInfo)
+
+NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerEvent, nsIDOMEvent)
+
+NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerEvent)
+
+NS_IMETHODIMP
+nsDOMWorkerEvent::GetType(nsAString& aType)
+{
+  aType.Assign(mType);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerEvent::GetTarget(nsIDOMEventTarget** aTarget)
+{
+  NS_ENSURE_ARG_POINTER(aTarget);
+  NS_IF_ADDREF(*aTarget = mTarget);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerEvent::GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget)
+{
+  NS_ENSURE_ARG_POINTER(aCurrentTarget);
+  NS_IF_ADDREF(*aCurrentTarget = mTarget);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerEvent::GetEventPhase(PRUint16* aEventPhase)
+{
+  NS_ENSURE_ARG_POINTER(aEventPhase);
+  *aEventPhase = mEventPhase;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerEvent::GetBubbles(PRBool* aBubbles)
+{
+  NS_ENSURE_ARG_POINTER(aBubbles);
+  *aBubbles = mBubbles;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerEvent::GetCancelable(PRBool* aCancelable)
+{
+  NS_ENSURE_ARG_POINTER(aCancelable);
+  *aCancelable = mCancelable;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerEvent::GetTimeStamp(DOMTimeStamp* aTimeStamp)
+{
+  NS_ENSURE_ARG_POINTER(aTimeStamp);
+  *aTimeStamp = mTimeStamp;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerEvent::StopPropagation()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerEvent::PreventDefault()
+{
+  mPreventDefaultCalled = PR_TRUE;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerEvent::GetDefaultPrevented(PRBool* aRetVal)
+{
+  *aRetVal = mPreventDefaultCalled;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerEvent::InitEvent(const nsAString& aEventTypeArg,
+                            PRBool aCanBubbleArg,
+                            PRBool aCancelableArg)
+{
+  NS_ENSURE_FALSE(aEventTypeArg.IsEmpty(), NS_ERROR_INVALID_ARG);
+
+  mType.Assign(aEventTypeArg);
+  mBubbles = aCanBubbleArg;
+  mCancelable = aCancelableArg;
+  mPreventDefaultCalled = PR_FALSE;
+  mTimeStamp = PR_Now();
+  return NS_OK;
+}
+
+nsDOMWorkerMessageEvent::~nsDOMWorkerMessageEvent()
+{
+  if (mData) {
+    JSContext* cx = nsDOMThreadService::GetCurrentContext();
+    if (cx) {
+      JS_free(cx, mData);
+    }
+    else {
+      NS_WARNING("Failed to get safe JSContext, leaking event data!");
+    }
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerMessageEvent, nsDOMWorkerEvent,
+                                                      nsIWorkerMessageEvent)
+
+NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerMessageEvent, nsIDOMEvent,
+                                                      nsIWorkerMessageEvent)
+
+NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerMessageEvent)
+
+nsresult
+nsDOMWorkerMessageEvent::SetJSData(
+                              JSContext* aCx,
+                              JSAutoStructuredCloneBuffer& aBuffer,
+                              nsTArray<nsCOMPtr<nsISupports> >& aWrappedNatives)
+{
+  NS_ASSERTION(aCx, "Null context!");
+
+  if (!mDataVal.Hold(aCx)) {
+    NS_WARNING("Failed to hold jsval!");
+    return NS_ERROR_FAILURE;
+  }
+
+  if (!mWrappedNatives.SwapElements(aWrappedNatives)) {
+    NS_ERROR("This should never fail!");
+  }
+
+  aBuffer.steal(&mData, &mDataLen);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerMessageEvent::GetData(nsAString& aData)
+{
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+  NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
+
+  nsAXPCNativeCallContext* cc;
+  nsresult rv = xpc->GetCurrentNativeCallContext(&cc);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED);
+
+  if (mData) {
+    JSContext* cx;
+    rv = cc->GetJSContext(&cx);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    JSAutoRequest ar(cx);
+    JSAutoStructuredCloneBuffer buffer;
+    buffer.adopt(cx, mData, mDataLen);
+    mData = nsnull;
+    mDataLen = 0;
+
+    JSStructuredCloneCallbacks callbacks = {
+      nsDOMWorker::ReadStructuredClone, nsnull, nsnull
+    };
+
+    JSBool ok = buffer.read(mDataVal.ToJSValPtr(), cx, &callbacks);
+
+    // Release wrapped natives now, regardless of whether or not the deserialize
+    // succeeded.
+    mWrappedNatives.Clear();
+
+    if (!ok) {
+      NS_WARNING("Failed to deserialize!");
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  jsval* retval;
+  rv = cc->GetRetValPtr(&retval);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  cc->SetReturnValueWasSet(PR_TRUE);
+  *retval = mDataVal;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerMessageEvent::GetOrigin(nsAString& aOrigin)
+{
+  aOrigin.Assign(mOrigin);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerMessageEvent::GetSource(nsISupports** aSource)
+{
+  NS_ENSURE_ARG_POINTER(aSource);
+  NS_IF_ADDREF(*aSource = mSource);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerMessageEvent::InitMessageEvent(const nsAString& aTypeArg,
+                                          PRBool aCanBubbleArg,
+                                          PRBool aCancelableArg,
+                                          const nsAString& aDataArg,
+                                          const nsAString& aOriginArg,
+                                          nsISupports* aSourceArg)
+{
+  mOrigin.Assign(aOriginArg);
+  mSource = aSourceArg;
+  return nsDOMWorkerEvent::InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerProgressEvent, nsDOMWorkerEvent,
+                                                       nsIDOMProgressEvent)
+
+NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerProgressEvent, nsIDOMEvent,
+                                                       nsIDOMProgressEvent)
+
+NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerProgressEvent)
+
+NS_IMETHODIMP
+nsDOMWorkerProgressEvent::GetLengthComputable(PRBool* aLengthComputable)
+{
+  NS_ENSURE_ARG_POINTER(aLengthComputable);
+  *aLengthComputable = mLengthComputable;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerProgressEvent::GetLoaded(PRUint64* aLoaded)
+{
+  NS_ENSURE_ARG_POINTER(aLoaded);
+  *aLoaded = mLoaded;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerProgressEvent::GetTotal(PRUint64* aTotal)
+{
+  NS_ENSURE_ARG_POINTER(aTotal);
+  *aTotal = mTotal;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerProgressEvent::InitProgressEvent(const nsAString_internal& aTypeArg,
+                                            PRBool aCanBubbleArg,
+                                            PRBool aCancelableArg,
+                                            PRBool aLengthComputableArg,
+                                            PRUint64 aLoadedArg,
+                                            PRUint64 aTotalArg)
+{
+  mLengthComputable = aLengthComputableArg;
+  mLoaded = aLoadedArg;
+  mTotal = aTotalArg;
+  return nsDOMWorkerEvent::InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
+}
+
+NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerXHRState)
+NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerXHRState)
+
+nsDOMWorkerXHREvent::nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy)
+: mXHRProxy(aXHRProxy),
+  mXHREventType(PR_UINT32_MAX),
+  mChannelID(-1),
+  mUploadEvent(PR_FALSE),
+  mProgressEvent(PR_FALSE)
+{
+  NS_ASSERTION(aXHRProxy, "Can't be null!");
+}
+
+NS_IMPL_ADDREF_INHERITED(nsDOMWorkerXHREvent, nsDOMWorkerProgressEvent)
+NS_IMPL_RELEASE_INHERITED(nsDOMWorkerXHREvent, nsDOMWorkerProgressEvent)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMWorkerXHREvent)
+  NS_INTERFACE_MAP_ENTRY(nsIRunnable)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMProgressEvent, mProgressEvent)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMWorkerEvent)
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetInterfaces(PRUint32* aCount,
+                                   nsIID*** aArray)
+{
+  PRUint32 count = *aCount = mProgressEvent ? 2 : 1;
+
+  *aArray = (nsIID**)nsMemory::Alloc(sizeof(nsIID*) * count);
+
+  if (mProgressEvent) {
+    (*aArray)[--count] =
+      (nsIID*)nsMemory::Clone(&NS_GET_IID(nsIDOMProgressEvent), sizeof(nsIID));
+  }
+
+  (*aArray)[--count] =
+    (nsIID *)nsMemory::Clone(&NS_GET_IID(nsIDOMEvent), sizeof(nsIID));
+
+  NS_ASSERTION(!count, "Bad math!");
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHREvent::Init(PRUint32 aXHREventType,
+                          const nsAString& aType,
+                          nsIDOMEvent* aEvent,
+                          SnapshotChoice aSnapshot)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aEvent, "Don't pass null here!");
+
+  mXHREventType = aXHREventType;
+
+  // Only set a channel id if we're not going to be run immediately.
+  mChannelID = mXHRProxy->mSyncEventQueue ? -1 : mXHRProxy->ChannelID();
+
+  mTarget = static_cast<nsDOMWorkerMessageHandler*>(mXHRProxy->mWorkerXHR);
+  NS_ENSURE_TRUE(mTarget, NS_ERROR_UNEXPECTED);
+
+  mXHRWN = mXHRProxy->mWorkerXHR->GetWrappedNative();
+  NS_ENSURE_STATE(mXHRWN);
+
+  nsCOMPtr<nsIDOMEventTarget> mainThreadTarget;
+  nsresult rv = aEvent->GetTarget(getter_AddRefs(mainThreadTarget));
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_STATE(mainThreadTarget);
+
+  nsCOMPtr<nsIXMLHttpRequestUpload> upload(do_QueryInterface(mainThreadTarget));
+  if (upload) {
+    mUploadEvent = PR_TRUE;
+    mTarget =
+      static_cast<nsDOMWorkerMessageHandler*>(mXHRProxy->mWorkerXHR->mUpload);
+  }
+  else {
+    mUploadEvent = PR_FALSE;
+    mTarget = static_cast<nsDOMWorkerMessageHandler*>(mXHRProxy->mWorkerXHR);
+  }
+  NS_ASSERTION(mTarget, "Null target!");
+
+  PRBool bubbles;
+  rv = aEvent->GetBubbles(&bubbles);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRBool cancelable;
+  rv = aEvent->GetCancelable(&cancelable);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aEvent->GetTimeStamp(&mTimeStamp);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aEvent->GetEventPhase(&mEventPhase);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ASSERTION(mEventPhase == nsIDOMEvent::AT_TARGET, "Unsupported phase!");
+
+  nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(aEvent));
+  if (progressEvent) {
+    mProgressEvent = PR_TRUE;
+
+    PRBool lengthComputable;
+    rv = progressEvent->GetLengthComputable(&lengthComputable);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint64 loaded;
+    rv = progressEvent->GetLoaded(&loaded);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint64 total;
+    rv = progressEvent->GetTotal(&total);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = InitProgressEvent(aType, bubbles, cancelable, lengthComputable, loaded,
+                           total);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  else {
+    mProgressEvent = PR_FALSE;
+
+    rv = InitEvent(aType, bubbles, cancelable);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  mState = new nsDOMWorkerXHRState();
+  NS_ENSURE_TRUE(mState, NS_ERROR_OUT_OF_MEMORY);
+
+  if (aSnapshot == SNAPSHOT) {
+    SnapshotXHRState(mXHRProxy->mXHR, mState);
+  }
+
+  return NS_OK;
+}
+
+/* static */
+void
+nsDOMWorkerXHREvent::SnapshotXHRState(nsIXMLHttpRequest* aXHR,
+                                      nsDOMWorkerXHRState* aState)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aXHR && aState, "Don't pass null here!");
+
+  aState->responseTextResult = aXHR->GetResponseText(aState->responseText);
+  aState->statusTextResult = aXHR->GetStatusText(aState->statusText);
+  aState->statusResult = aXHR->GetStatus(&aState->status);
+  aState->readyStateResult = aXHR->GetReadyState(&aState->readyState);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::Run()
+{
+  nsresult rv = mXHRProxy->HandleWorkerEvent(this, mUploadEvent);
+
+  // Prevent reference cycles by releasing this here.
+  mXHRProxy = nsnull;
+
+  NS_ENSURE_SUCCESS(rv, rv);
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerErrorEvent, nsDOMWorkerEvent,
+                                                    nsIWorkerErrorEvent)
+
+NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerErrorEvent, nsIDOMEvent,
+                                                    nsIWorkerErrorEvent)
+
+NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerErrorEvent)
+
+nsresult
+nsDOMWorkerErrorEvent::GetMessage(nsAString& aMessage)
+{
+  aMessage.Assign(mMessage);
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerErrorEvent::GetFilename(nsAString& aFilename)
+{
+  aFilename.Assign(mFilename);
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerErrorEvent::GetLineno(PRUint32* aLineno)
+{
+  NS_ENSURE_ARG_POINTER(aLineno);
+  *aLineno = mLineno;
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerErrorEvent::InitErrorEvent(const nsAString& aTypeArg,
+                                      PRBool aCanBubbleArg,
+                                      PRBool aCancelableArg,
+                                      const nsAString& aMessageArg,
+                                      const nsAString& aFilenameArg,
+                                      PRUint32 aLinenoArg)
+{
+  mMessage.Assign(aMessageArg);
+  mFilename.Assign(aFilenameArg);
+  mLineno = aLinenoArg;
+  return InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerEvents.h
@@ -0,0 +1,340 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NSDOMWORKEREVENTS_H__
+#define __NSDOMWORKEREVENTS_H__
+
+#include "nsIClassInfo.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMProgressEvent.h"
+#include "nsIDOMWorkers.h"
+#include "nsIRunnable.h"
+
+#include "jsapi.h"
+#include "jsutil.h"
+#include "nsAutoJSValHolder.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsStringGlue.h"
+
+#include "nsDOMWorkerMacros.h"
+
+class nsDOMWorkerXHRProxy;
+class nsIXMLHttpRequest;
+class nsIXPConnectWrappedNative;
+
+/* 4d5794d6-98ab-4a6b-ad5a-8ed1fa1d4839 */
+#define NS_IDOMWORKERPRIVATEEVENT_IID                      \
+{                                                          \
+  0x4d5794d6,                                              \
+  0x98ab,                                                  \
+  0x4a6b,                                                  \
+  { 0xad, 0x5a, 0x8e, 0xd1, 0xfa, 0x1d, 0x48, 0x39 }       \
+}
+
+class nsIDOMWorkerPrivateEvent : public nsIDOMEvent
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOMWORKERPRIVATEEVENT_IID)
+  virtual PRBool PreventDefaultCalled() = 0;
+};
+
+#define NS_FORWARD_NSIDOMEVENT_SPECIAL                                        \
+  NS_IMETHOD GetType(nsAString& aType)                                        \
+    { return mEvent->GetType(aType); }                                        \
+  NS_IMETHOD GetTarget(nsIDOMEventTarget** aTarget)                           \
+    { return mEvent->GetTarget(aTarget); }                                    \
+  NS_IMETHOD GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget)             \
+    { return mEvent->GetCurrentTarget(aCurrentTarget); }                      \
+  NS_IMETHOD GetEventPhase(PRUint16* aEventPhase)                             \
+    { return mEvent->GetEventPhase(aEventPhase); }                            \
+  NS_IMETHOD GetBubbles(PRBool* aBubbles)                                     \
+    { return mEvent->GetBubbles(aBubbles); }                                  \
+  NS_IMETHOD GetCancelable(PRBool* aCancelable)                               \
+    { return mEvent->GetCancelable(aCancelable); }                            \
+  NS_IMETHOD GetTimeStamp(DOMTimeStamp* aTimeStamp)                           \
+    { return mEvent->GetTimeStamp(aTimeStamp); }                              \
+  NS_IMETHOD StopPropagation()                                                \
+    { return mEvent->StopPropagation(); }
+
+#define NS_FORWARD_NSIDOMPROGRESSEVENT_SPECIAL                                \
+  NS_IMETHOD GetLengthComputable(PRBool* aLengthComputable)                   \
+    { return mProgressEvent->GetLengthComputable(aLengthComputable); }        \
+  NS_IMETHOD GetLoaded(PRUint64* aLoaded)                                     \
+    { return mProgressEvent->GetLoaded(aLoaded); }                            \
+  NS_IMETHOD GetTotal(PRUint64* aTotal)                                       \
+    { return mProgressEvent->GetTotal(aTotal); }
+
+#define NS_FORWARD_NSIWORKERMESSAGEEVENT_SPECIAL                              \
+  NS_IMETHOD GetData(nsAString& aData)                                        \
+    { return mMessageEvent->GetData(aData); }                                 \
+  NS_IMETHOD GetOrigin(nsAString& aOrigin)                                    \
+    { return mMessageEvent->GetOrigin(aOrigin); }                             \
+  NS_IMETHOD GetSource(nsISupports** aSource)                                 \
+    { return mMessageEvent->GetSource(aSource); }
+
+#define NS_FORWARD_NSIWORKERERROREVENT_SPECIAL                                \
+  NS_IMETHOD GetMessage(nsAString& aMessage)                                  \
+    { return mErrorEvent->GetMessage(aMessage); }                             \
+  NS_IMETHOD GetFilename(nsAString& aFilename)                                \
+    { return mErrorEvent->GetFilename(aFilename); }                           \
+  NS_IMETHOD GetLineno(PRUint32* aLineno)                                     \
+    { return mErrorEvent->GetLineno(aLineno); }
+
+class nsDOMWorkerPrivateEvent : public nsIDOMWorkerPrivateEvent,
+                                public nsIDOMProgressEvent,
+                                public nsIWorkerMessageEvent,
+                                public nsIWorkerErrorEvent,
+                                public nsIClassInfo
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_FORWARD_NSIDOMEVENT_SPECIAL
+  NS_FORWARD_NSIWORKERMESSAGEEVENT_SPECIAL
+  NS_FORWARD_NSIDOMPROGRESSEVENT_SPECIAL
+  NS_FORWARD_NSIWORKERERROREVENT_SPECIAL
+  NS_DECL_NSICLASSINFO
+
+  nsDOMWorkerPrivateEvent(nsIDOMEvent* aEvent);
+
+  NS_IMETHOD PreventDefault();
+
+  NS_IMETHOD InitEvent(const nsAString& aEventType,
+                       PRBool aCanBubble,
+                       PRBool aCancelable);
+
+  NS_IMETHOD InitProgressEvent(const nsAString& aTypeArg,
+                               PRBool aCanBubbleArg,
+                               PRBool aCancelableArg,
+                               PRBool aLengthComputableArg,
+                               PRUint64 aLoadedArg,
+                               PRUint64 aTotalArg); 
+
+  NS_IMETHOD InitMessageEvent(const nsAString& aTypeArg,
+                              PRBool aCanBubbleArg,
+                              PRBool aCancelableArg,
+                              const nsAString& aDataArg,
+                              const nsAString& aOriginArg,
+                              nsISupports* aSourceArg);
+
+  NS_IMETHOD InitErrorEvent(const nsAString& aTypeArg,
+                            PRBool aCanBubbleArg,
+                            PRBool aCancelableArg,
+                            const nsAString& aMessageArg,
+                            const nsAString& aFilenameArg,
+                            PRUint32 aLinenoArg);
+
+  NS_IMETHOD GetDefaultPrevented(PRBool* aRetVal);
+
+  virtual PRBool PreventDefaultCalled();
+
+private:
+  nsCOMPtr<nsIDOMEvent> mEvent;
+  nsCOMPtr<nsIDOMProgressEvent> mProgressEvent;
+  nsCOMPtr<nsIWorkerMessageEvent> mMessageEvent;
+  nsCOMPtr<nsIWorkerErrorEvent> mErrorEvent;
+  PRBool mPreventDefaultCalled;
+};
+
+class nsDOMWorkerEvent : public nsIDOMEvent,
+                         public nsIClassInfo
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMEVENT
+  NS_DECL_NSICLASSINFO
+
+  nsDOMWorkerEvent()
+  : mEventPhase(nsIDOMEvent::AT_TARGET), mTimeStamp(0), mBubbles(PR_FALSE),
+    mCancelable(PR_FALSE), mPreventDefaultCalled(PR_FALSE) { }
+
+  void SetTarget(nsIDOMEventTarget* aTarget) {
+    mTarget = aTarget;
+  }
+
+  PRBool PreventDefaultCalled() {
+    return PRBool(mPreventDefaultCalled);
+  }
+
+protected:
+  virtual ~nsDOMWorkerEvent() { }
+
+  nsString mType;
+  nsCOMPtr<nsIDOMEventTarget> mTarget;
+  PRUint16 mEventPhase;
+  DOMTimeStamp mTimeStamp;
+  PRPackedBool mBubbles;
+  PRPackedBool mCancelable;
+  PRPackedBool mPreventDefaultCalled;
+};
+
+class nsDOMWorkerMessageEvent : public nsDOMWorkerEvent,
+                                public nsIWorkerMessageEvent
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIDOMEVENT(nsDOMWorkerEvent::)
+  NS_DECL_NSIWORKERMESSAGEEVENT
+  NS_DECL_NSICLASSINFO_GETINTERFACES
+
+  nsDOMWorkerMessageEvent() : mData(nsnull) { }
+  ~nsDOMWorkerMessageEvent();
+
+  nsresult SetJSData(JSContext* aCx,
+                     JSAutoStructuredCloneBuffer& aBuffer,
+                     nsTArray<nsCOMPtr<nsISupports> >& aWrappedNatives);
+
+protected:
+  nsString mOrigin;
+  nsCOMPtr<nsISupports> mSource;
+
+  nsAutoJSValHolder mDataVal;
+  uint64* mData;
+  size_t mDataLen;
+  nsTArray<nsCOMPtr<nsISupports> > mWrappedNatives;
+};
+
+class nsDOMWorkerProgressEvent : public nsDOMWorkerEvent,
+                                 public nsIDOMProgressEvent
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIDOMEVENT(nsDOMWorkerEvent::)
+  NS_DECL_NSIDOMPROGRESSEVENT
+  NS_DECL_NSICLASSINFO_GETINTERFACES
+
+  nsDOMWorkerProgressEvent()
+  : mLoaded(0), mTotal(0), mLengthComputable(PR_FALSE) { }
+
+protected:
+  PRUint64 mLoaded;
+  PRUint64 mTotal;
+  PRBool mLengthComputable;
+};
+
+class nsDOMWorkerXHRState
+{
+public:
+  nsDOMWorkerXHRState()
+  : responseTextResult(NS_OK), statusTextResult(NS_OK), status(NS_OK),
+    statusResult(NS_OK), readyState(0), readyStateResult(NS_OK) { }
+
+  NS_IMETHOD_(nsrefcnt) AddRef();
+  NS_IMETHOD_(nsrefcnt) Release();
+
+  nsString responseText;
+  nsresult responseTextResult;
+
+  nsCString statusText;
+  nsresult statusTextResult;
+
+  nsresult status;
+  nsresult statusResult;
+
+  PRUint16 readyState;
+  nsresult readyStateResult;
+
+protected:
+  virtual ~nsDOMWorkerXHRState() { }
+
+  nsAutoRefCnt mRefCnt;
+};
+
+class nsDOMWorkerXHREvent : public nsDOMWorkerProgressEvent,
+                            public nsIRunnable
+{
+  friend class nsDOMWorkerXHRProxy;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIRUNNABLE
+  NS_DECL_NSICLASSINFO_GETINTERFACES
+
+  enum SnapshotChoice {
+    SNAPSHOT,
+    NO_SNAPSHOT
+  };
+
+  nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy);
+
+  nsresult Init(PRUint32 aXHREventType,
+                const nsAString& aType,
+                nsIDOMEvent* aEvent,
+                SnapshotChoice = SNAPSHOT);
+
+  static void SnapshotXHRState(nsIXMLHttpRequest* aXHR,
+                               nsDOMWorkerXHRState* aState);
+
+  already_AddRefed<nsDOMWorkerXHRState> ForgetState() {
+    return mState.forget();
+  }
+
+protected:
+  nsDOMWorkerXHRState* GetState() {
+    return mState;
+  }
+
+  nsRefPtr<nsDOMWorkerXHRProxy> mXHRProxy;
+  nsCOMPtr<nsIXPConnectWrappedNative> mXHRWN;
+  nsRefPtr<nsDOMWorkerXHRState> mState;
+  PRUint32 mXHREventType;
+  PRInt32 mChannelID;
+  PRPackedBool mUploadEvent;
+  PRPackedBool mProgressEvent;
+};
+
+class nsDOMWorkerErrorEvent : public nsDOMWorkerEvent,
+                              public nsIWorkerErrorEvent
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIDOMEVENT(nsDOMWorkerEvent::)
+  NS_DECL_NSIWORKERERROREVENT
+  NS_DECL_NSICLASSINFO_GETINTERFACES
+
+  nsDOMWorkerErrorEvent()
+  : mLineno(0) { }
+
+protected:
+  nsString mMessage;
+  nsString mFilename;
+  PRUint32 mLineno;
+};
+
+#endif /* __NSDOMWORKEREVENTS_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerMacros.h
@@ -0,0 +1,135 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NSDOMWORKERMACROS_H__
+#define __NSDOMWORKERMACROS_H__
+
+// Macro to generate nsIClassInfo methods for these threadsafe DOM classes
+#define NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(_class)                       \
+NS_IMETHODIMP                                                                 \
+_class::GetInterfaces(PRUint32* _count, nsIID*** _array)                      \
+{                                                                             \
+  return NS_CI_INTERFACE_GETTER_NAME(_class)(_count, _array);                 \
+}                                                                             \
+
+#define NS_IMPL_THREADSAFE_DOM_CI_HELPER(_class)                              \
+NS_IMETHODIMP                                                                 \
+_class::GetHelperForLanguage(PRUint32 _language, nsISupports** _retval)       \
+{                                                                             \
+  *_retval = nsnull;                                                          \
+  return NS_OK;                                                               \
+}
+
+#define NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(_class)                        \
+NS_IMETHODIMP                                                                 \
+_class::GetContractID(char** _contractID)                                     \
+{                                                                             \
+  *_contractID = nsnull;                                                      \
+  return NS_OK;                                                               \
+}                                                                             \
+                                                                              \
+NS_IMETHODIMP                                                                 \
+_class::GetClassDescription(char** _classDescription)                         \
+{                                                                             \
+  *_classDescription = nsnull;                                                \
+  return NS_OK;                                                               \
+}                                                                             \
+                                                                              \
+NS_IMETHODIMP                                                                 \
+_class::GetClassID(nsCID** _classID)                                          \
+{                                                                             \
+  *_classID = nsnull;                                                         \
+  return NS_OK;                                                               \
+}                                                                             \
+                                                                              \
+NS_IMETHODIMP                                                                 \
+_class::GetImplementationLanguage(PRUint32* _language)                        \
+{                                                                             \
+  *_language = nsIProgrammingLanguage::CPLUSPLUS;                             \
+  return NS_OK;                                                               \
+}                                                                             \
+                                                                              \
+NS_IMETHODIMP                                                                 \
+_class::GetFlags(PRUint32* _flags)                                            \
+{                                                                             \
+  *_flags = nsIClassInfo::THREADSAFE | nsIClassInfo::DOM_OBJECT;              \
+  return NS_OK;                                                               \
+}                                                                             \
+                                                                              \
+NS_IMETHODIMP                                                                 \
+_class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc)                             \
+{                                                                             \
+  return NS_ERROR_NOT_AVAILABLE;                                              \
+}
+
+#define NS_IMPL_THREADSAFE_DOM_CI(_class)                                     \
+NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(_class)                               \
+NS_IMPL_THREADSAFE_DOM_CI_HELPER(_class)                                      \
+NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(_class)
+
+#define NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(_to)                          \
+  NS_IMETHOD GetHelperForLanguage(PRUint32 aLanguage, nsISupports** _retval)  \
+    { return _to GetHelperForLanguage(aLanguage, _retval); }                  \
+  NS_IMETHOD GetContractID(char** aContractID)                                \
+    { return _to GetContractID(aContractID); }                                \
+  NS_IMETHOD GetClassDescription(char** aClassDescription)                    \
+    { return _to GetClassDescription(aClassDescription); }                    \
+  NS_IMETHOD GetClassID(nsCID** aClassID)                                     \
+    { return _to GetClassID(aClassID); }                                      \
+  NS_IMETHOD GetImplementationLanguage(PRUint32* aImplementationLanguage)     \
+    { return _to GetImplementationLanguage(aImplementationLanguage); }        \
+  NS_IMETHOD GetFlags(PRUint32* aFlags)                                       \
+    { return _to GetFlags(aFlags); }                                          \
+  NS_IMETHOD GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)                        \
+    { return _to GetClassIDNoAlloc(aClassIDNoAlloc); }
+
+#define NS_DECL_NSICLASSINFO_GETINTERFACES                                    \
+  NS_IMETHOD GetInterfaces(PRUint32* aCount, nsIID*** aArray);
+
+// Don't know why nsISupports.idl defines this out...
+#define NS_FORWARD_NSISUPPORTS(_to)                                           \
+  NS_IMETHOD QueryInterface(const nsIID& uuid, void** result) {               \
+    return _to QueryInterface(uuid, result);                                  \
+  }                                                                           \
+  NS_IMETHOD_(nsrefcnt) AddRef(void) { return _to AddRef(); }                 \
+  NS_IMETHOD_(nsrefcnt) Release(void) { return _to Release(); }
+
+#define JSON_PRIMITIVE_PROPNAME                                               \
+  "primitive"
+
+#endif /* __NSDOMWORKERMACROS_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerMessageHandler.cpp
@@ -0,0 +1,440 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMWorkerMessageHandler.h"
+
+#include "nsIDOMEvent.h"
+#include "nsIXPConnect.h"
+
+#include "nsContentUtils.h"
+
+#include "nsDOMThreadService.h"
+#include "nsDOMWorkerEvents.h"
+
+NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerEventListenerBase)
+NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerEventListenerBase)
+
+nsresult
+nsDOMWorkerWeakEventListener::Init(nsIDOMEventListener* aListener)
+{
+  NS_ENSURE_ARG_POINTER(aListener);
+
+  nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(aListener));
+  NS_ENSURE_TRUE(wrappedJS, NS_NOINTERFACE);
+
+  JSObject* obj;
+  nsresult rv = wrappedJS->GetJSObject(&obj);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mObj = obj;
+
+  return NS_OK;
+}
+
+already_AddRefed<nsIDOMEventListener>
+nsDOMWorkerWeakEventListener::GetListener()
+{
+  JSContext* cx = nsDOMThreadService::GetCurrentContext();
+  NS_ENSURE_TRUE(cx, nsnull);
+
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+
+  nsCOMPtr<nsIDOMEventListener> listener;
+  nsresult rv = xpc->WrapJS(cx, mObj, NS_GET_IID(nsIDOMEventListener),
+                            getter_AddRefs(listener));
+  NS_ENSURE_SUCCESS(rv, nsnull);
+
+  return listener.forget();
+}
+
+nsDOMWorkerWrappedWeakEventListener::
+nsDOMWorkerWrappedWeakEventListener(nsDOMWorkerWeakEventListener* aInner)
+: mInner(aInner)
+{
+  NS_ASSERTION(aInner, "Null pointer!");
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerMessageHandler,
+                              nsIDOMEventTarget,
+                              nsIClassInfo)
+
+NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerMessageHandler,
+                             nsIDOMEventTarget)
+
+NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerMessageHandler)
+
+const nsDOMWorkerMessageHandler::ListenerCollection*
+nsDOMWorkerMessageHandler::GetListenerCollection(const nsAString& aType) const
+{
+  PRUint32 count = mCollections.Length();
+  for (PRUint32 index = 0; index < count; index++) {
+    const ListenerCollection& collection = mCollections[index];
+    if (collection.type.Equals(aType)) {
+      return &collection;
+    }
+  }
+  return nsnull;
+}
+
+void
+nsDOMWorkerMessageHandler::GetListenersForType(const nsAString& aType,
+                                               ListenerArray& _retval) const
+{
+  _retval.Clear();
+
+  const ListenerCollection* collection = GetListenerCollection(aType);
+  if (collection) {
+    PRUint32 count = collection->listeners.Length();
+
+    if (!_retval.SetLength(count)) {
+      NS_WARNING("Out of memory!");
+      return;
+    }
+
+    for (PRUint32 index = 0; index < count; index++) {
+      nsCOMPtr<nsIDOMEventListener> listener =
+        collection->listeners[index]->GetListener();
+      _retval[index].swap(listener);
+    }
+  }
+}
+
+nsresult
+nsDOMWorkerMessageHandler::SetOnXListener(const nsAString& aType,
+                                          nsIDOMEventListener* aListener)
+{
+  nsRefPtr<nsDOMWorkerWrappedWeakEventListener> wrappedListener;
+
+  ListenerCollection* collection =
+    const_cast<ListenerCollection*>(GetListenerCollection(aType));
+
+#ifdef DEBUG
+  PRBool removed;
+#endif
+
+  if (collection) {
+    wrappedListener.swap(collection->onXListener);
+    if (wrappedListener) {
+#ifdef DEBUG
+      removed =
+#endif
+      collection->listeners.RemoveElement(wrappedListener);
+      NS_ASSERTION(removed, "Element wasn't in the list!");
+    }
+  }
+
+  if (!aListener) {
+    if (collection && !collection->listeners.Length()) {
+#ifdef DEBUG
+      removed =
+#endif
+      mCollections.RemoveElement(*collection);
+      NS_ASSERTION(removed, "Element wasn't in the list!");
+    }
+    return NS_OK;
+  }
+
+  nsRefPtr<nsDOMWorkerWeakEventListener> weakListener =
+    new nsDOMWorkerWeakEventListener();
+  NS_ENSURE_TRUE(weakListener, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv = weakListener->Init(aListener);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  wrappedListener = new nsDOMWorkerWrappedWeakEventListener(weakListener);
+  NS_ENSURE_TRUE(wrappedListener, NS_ERROR_OUT_OF_MEMORY);
+
+  if (!collection) {
+    collection = mCollections.AppendElement(aType);
+    NS_ENSURE_TRUE(collection, NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  WeakListener* newListener =
+    collection->listeners.AppendElement(wrappedListener);
+  NS_ENSURE_TRUE(newListener, NS_ERROR_OUT_OF_MEMORY);
+
+  wrappedListener.swap(collection->onXListener);
+  return NS_OK;
+}
+
+already_AddRefed<nsIDOMEventListener>
+nsDOMWorkerMessageHandler::GetOnXListener(const nsAString& aType) const
+{
+  const ListenerCollection* collection = GetListenerCollection(aType);
+  if (collection && collection->onXListener) {
+    return collection->onXListener->GetListener();
+  }
+
+  return nsnull;
+}
+
+void
+nsDOMWorkerMessageHandler::ClearListeners(const nsAString& aType)
+{
+  PRUint32 count = mCollections.Length();
+  for (PRUint32 index = 0; index < count; index++) {
+    if (mCollections[index].type.Equals(aType)) {
+      mCollections.RemoveElementAt(index);
+      return;
+    }
+  }
+}
+
+PRBool
+nsDOMWorkerMessageHandler::HasListeners(const nsAString& aType)
+{
+  const ListenerCollection* collection = GetListenerCollection(aType);
+  return collection && collection->listeners.Length();
+}
+
+void
+nsDOMWorkerMessageHandler::ClearAllListeners()
+{
+  mCollections.Clear();
+}
+
+void
+nsDOMWorkerMessageHandler::Trace(JSTracer* aTracer)
+{
+  PRUint32 cCount = mCollections.Length();
+  for (PRUint32 cIndex = 0; cIndex < cCount; cIndex++) {
+    const ListenerCollection& collection = mCollections[cIndex];
+    PRUint32 lCount = collection.listeners.Length();
+    for (PRUint32 lIndex = 0; lIndex < lCount; lIndex++) {
+      JSObject* obj = collection.listeners[lIndex]->GetJSObject();
+      NS_ASSERTION(obj, "Null object!");
+      JS_SET_TRACING_DETAILS(aTracer, nsnull, this, 0);
+      JS_CallTracer(aTracer, obj, JSTRACE_OBJECT);
+    }
+  }
+}
+
+/**
+ * See nsIDOMEventTarget
+ */
+NS_IMETHODIMP
+nsDOMWorkerMessageHandler::RemoveEventListener(const nsAString& aType,
+                                               nsIDOMEventListener* aListener,
+                                               PRBool aUseCapture)
+{
+  ListenerCollection* collection =
+    const_cast<ListenerCollection*>(GetListenerCollection(aType));
+
+  if (collection) {
+    PRUint32 count = collection->listeners.Length();
+    for (PRUint32 index = 0; index < count; index++) {
+      WeakListener& weakListener = collection->listeners[index];
+      if (weakListener == collection->onXListener) {
+        continue;
+      }
+      nsCOMPtr<nsIDOMEventListener> listener = weakListener->GetListener();
+      if (listener == aListener) {
+        collection->listeners.RemoveElementAt(index);
+        break;
+      }
+    }
+
+    if (!collection->listeners.Length()) {
+#ifdef DEBUG
+      PRBool removed =
+#endif
+      mCollections.RemoveElement(*collection);
+      NS_ASSERTION(removed, "Somehow this wasn't in the list!");
+    }
+  }
+
+  return NS_OK;
+}
+
+/**
+ * See nsIDOMEventTarget
+ */
+NS_IMETHODIMP
+nsDOMWorkerMessageHandler::DispatchEvent(nsIDOMEvent* aEvent,
+                                         PRBool* _retval)
+{
+  NS_ENSURE_ARG_POINTER(aEvent);
+
+  nsCOMPtr<nsIDOMWorkerPrivateEvent> event;
+
+  if (_retval) {
+    event = do_QueryInterface(aEvent);
+    if (!event) {
+      event = new nsDOMWorkerPrivateEvent(aEvent);
+      NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
+    }
+    aEvent = event;
+  }
+
+  nsAutoString type;
+  nsresult rv = aEvent->GetType(type);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoTArray<Listener, 10>  listeners;
+  GetListenersForType(type, listeners);
+
+  PRUint32 count = listeners.Length();
+  for (PRUint32 index = 0; index < count; index++) {
+    const Listener& listener = listeners[index];
+    NS_ASSERTION(listener, "Null listener in array!");
+
+    listener->HandleEvent(aEvent);
+  }
+
+  if (_retval) {
+    *_retval = event->PreventDefaultCalled();
+  }
+
+  return NS_OK;
+}
+
+/**
+ * See nsIDOMEventTarget
+ */
+NS_IMETHODIMP
+nsDOMWorkerMessageHandler::AddEventListener(const nsAString& aType,
+                                            nsIDOMEventListener* aListener,
+                                            PRBool aUseCapture,
+                                            PRBool aWantsUntrusted,
+                                            PRUint8 aOptionalArgc)
+{
+  // We don't support aWantsUntrusted yet.
+  NS_ENSURE_TRUE(aOptionalArgc < 2, NS_ERROR_NOT_IMPLEMENTED);
+
+  ListenerCollection* collection =
+    const_cast<ListenerCollection*>(GetListenerCollection(aType));
+
+  if (!collection) {
+    collection = mCollections.AppendElement(aType);
+    NS_ENSURE_TRUE(collection, NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  nsRefPtr<nsDOMWorkerWeakEventListener> weakListener =
+    new nsDOMWorkerWeakEventListener();
+  NS_ENSURE_TRUE(weakListener, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv = weakListener->Init(aListener);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  WeakListener* newListener = collection->listeners.AppendElement(weakListener);
+  NS_ENSURE_TRUE(newListener, NS_ERROR_OUT_OF_MEMORY);
+
+  return NS_OK;
+}
+
+nsIDOMEventTarget *
+nsDOMWorkerMessageHandler::GetTargetForDOMEvent()
+{
+  NS_ERROR("Should not be called");
+  return nsnull;
+}
+
+nsIDOMEventTarget *
+nsDOMWorkerMessageHandler::GetTargetForEventTargetChain()
+{
+  NS_ERROR("Should not be called");
+  return nsnull;
+}
+
+nsresult
+nsDOMWorkerMessageHandler::PreHandleEvent(nsEventChainPreVisitor & aVisitor)
+{
+  NS_ERROR("Should not be called");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsDOMWorkerMessageHandler::WillHandleEvent(nsEventChainPostVisitor & aVisitor)
+{
+  NS_ERROR("Should not be called");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsDOMWorkerMessageHandler::PostHandleEvent(nsEventChainPostVisitor & aVisitor)
+{
+  NS_ERROR("Should not be called");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsDOMWorkerMessageHandler::DispatchDOMEvent(nsEvent *aEvent, nsIDOMEvent *aDOMEvent,
+                                            nsPresContext *aPresContext,
+                                            nsEventStatus *aEventStatus)
+{
+  NS_ERROR("Should not be called");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsEventListenerManager*
+nsDOMWorkerMessageHandler::GetListenerManager(PRBool aMayCreate)
+{
+  NS_ERROR("Should not be called");
+  return nsnull;
+}
+
+nsresult
+nsDOMWorkerMessageHandler::AddEventListenerByIID(nsIDOMEventListener *aListener,
+                                                 const nsIID & aIID)
+{
+  NS_ERROR("Should not be called");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsDOMWorkerMessageHandler::RemoveEventListenerByIID(nsIDOMEventListener *aListener,
+                                                    const nsIID & aIID)
+{
+  NS_ERROR("Should not be called");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsIScriptContext*
+nsDOMWorkerMessageHandler::GetContextForEventHandlers(nsresult *aRv)
+{
+  NS_ERROR("Should not be called");
+  *aRv = NS_ERROR_NOT_IMPLEMENTED;
+  return nsnull;
+}
+
+JSContext*
+nsDOMWorkerMessageHandler::GetJSContextForEventHandlers()
+{
+  NS_ERROR("Should not be called");
+  return nsnull;
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerMessageHandler.h
@@ -0,0 +1,175 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NSDOMWORKERMESSAGEHANDLER_H__
+#define __NSDOMWORKERMESSAGEHANDLER_H__
+
+#include "nsIClassInfo.h"
+#include "nsIDOMEventListener.h"
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMWorkers.h"
+
+#include "nsIProgrammingLanguage.h"
+
+#include "jsapi.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIClassInfoImpl.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+#include "nsIWeakReference.h"
+
+class nsDOMWorkerEventListenerBase
+{
+public:
+  NS_IMETHOD_(nsrefcnt) AddRef();
+  NS_IMETHOD_(nsrefcnt) Release();
+
+  virtual already_AddRefed<nsIDOMEventListener> GetListener() = 0;
+  virtual JSObject* GetJSObject() = 0;
+
+protected:
+  virtual ~nsDOMWorkerEventListenerBase() { }
+
+  nsAutoRefCnt mRefCnt;
+};
+
+class nsDOMWorkerWeakEventListener : public nsDOMWorkerEventListenerBase
+{
+public:
+  nsDOMWorkerWeakEventListener()
+  : mObj(NULL) { }
+
+  nsresult Init(nsIDOMEventListener* aListener);
+
+  already_AddRefed<nsIDOMEventListener> GetListener();
+
+  virtual JSObject* GetJSObject() {
+    return mObj;
+  }
+
+private:
+  JSObject* mObj;
+};
+
+class nsDOMWorkerWrappedWeakEventListener : public nsDOMWorkerEventListenerBase
+{
+public:
+  nsDOMWorkerWrappedWeakEventListener(nsDOMWorkerWeakEventListener* aInner);
+
+  already_AddRefed<nsIDOMEventListener> GetListener() {
+    return mInner->GetListener();
+  }
+
+  virtual JSObject* GetJSObject() {
+    return mInner->GetJSObject();
+  }
+
+private:
+  nsRefPtr<nsDOMWorkerWeakEventListener> mInner;
+};
+
+class nsDOMWorkerMessageHandler : public nsIDOMEventTarget,
+                                  public nsIClassInfo
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMEVENTTARGET
+  NS_DECL_NSICLASSINFO
+
+  virtual nsresult SetOnXListener(const nsAString& aType,
+                                  nsIDOMEventListener* aListener);
+
+  already_AddRefed<nsIDOMEventListener>
+    GetOnXListener(const nsAString& aType) const;
+
+  void ClearListeners(const nsAString& aType);
+
+  PRBool HasListeners(const nsAString& aType);
+
+  void ClearAllListeners();
+
+  void Trace(JSTracer* aTracer);
+
+protected:
+  virtual ~nsDOMWorkerMessageHandler() { }
+
+private:
+
+  typedef nsCOMPtr<nsIDOMEventListener> Listener;
+  typedef nsTArray<Listener> ListenerArray;
+
+  typedef nsRefPtr<nsDOMWorkerEventListenerBase> WeakListener;
+  typedef nsTArray<WeakListener> WeakListenerArray;
+
+  struct ListenerCollection {
+    PRBool operator==(const ListenerCollection& aOther) const {
+      return this == &aOther;
+    }
+
+    ListenerCollection(const nsAString& aType)
+    : type(aType) { }
+
+    nsString type;
+    WeakListenerArray listeners;
+    nsRefPtr<nsDOMWorkerWrappedWeakEventListener> onXListener;
+  };
+
+  const ListenerCollection* GetListenerCollection(const nsAString& aType) const;
+
+  void GetListenersForType(const nsAString& aType,
+                           ListenerArray& _retval) const;
+
+  nsTArray<ListenerCollection> mCollections;
+};
+
+#define NS_FORWARD_INTERNAL_NSIDOMEVENTTARGET(_to) \
+  virtual nsIDOMEventTarget * GetTargetForDOMEvent(void) { return _to GetTargetForDOMEvent(); } \
+  virtual nsIDOMEventTarget * GetTargetForEventTargetChain(void) { return _to GetTargetForEventTargetChain(); } \
+  virtual nsresult PreHandleEvent(nsEventChainPreVisitor & aVisitor) { return _to PreHandleEvent(aVisitor); } \
+  virtual nsresult WillHandleEvent(nsEventChainPostVisitor & aVisitor) { return _to WillHandleEvent(aVisitor); } \
+  virtual nsresult PostHandleEvent(nsEventChainPostVisitor & aVisitor) { return _to PostHandleEvent(aVisitor); } \
+  virtual nsresult DispatchDOMEvent(nsEvent *aEvent, nsIDOMEvent *aDOMEvent, nsPresContext *aPresContext, nsEventStatus *aEventStatus) { return _to DispatchDOMEvent(aEvent, aDOMEvent, aPresContext, aEventStatus); } \
+  virtual nsEventListenerManager * GetListenerManager(PRBool aMayCreate) { return _to GetListenerManager(aMayCreate); } \
+  virtual nsresult AddEventListenerByIID(nsIDOMEventListener *aListener, const nsIID & aIID) { return _to AddEventListenerByIID(aListener, aIID); } \
+  virtual nsresult RemoveEventListenerByIID(nsIDOMEventListener *aListener, const nsIID & aIID) { return _to RemoveEventListenerByIID(aListener, aIID); } \
+  virtual nsIScriptContext * GetContextForEventHandlers(nsresult *aRv NS_OUTPARAM) { return _to GetContextForEventHandlers(aRv); } \
+  virtual JSContext * GetJSContextForEventHandlers(void) { return _to GetJSContextForEventHandlers(); } 
+
+
+#endif /* __NSDOMWORKERMESSAGEHANDLER_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerPool.cpp
@@ -0,0 +1,238 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
+ *   Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMWorkerPool.h"
+
+// Interfaces
+#include "nsIDocument.h"
+#include "nsIDOMClassInfo.h"
+#include "nsIJSContextStack.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIServiceManager.h"
+#include "nsIThreadManager.h"
+#include "nsIXPConnect.h"
+#include "nsPIDOMWindow.h"
+
+// Other includes
+#include "nsContentUtils.h"
+#include "nsDOMJSUtils.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+
+// DOMWorker includes
+#include "nsDOMThreadService.h"
+#include "nsDOMWorker.h"
+
+using namespace mozilla;
+
+#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
+
+nsDOMWorkerPool::nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject,
+                                 nsIDocument* aDocument)
+: mParentGlobal(aGlobalObject),
+  mParentDocument(aDocument),
+  mReentrantMonitor("nsDOMWorkerPool.mReentrantMonitor"),
+  mCanceled(PR_FALSE),
+  mSuspended(PR_FALSE),
+  mWindowID(aDocument ? aDocument->OuterWindowID() : 0)
+{
+}
+
+nsDOMWorkerPool::~nsDOMWorkerPool()
+{
+  nsCOMPtr<nsIThread> mainThread;
+  NS_GetMainThread(getter_AddRefs(mainThread));
+
+  nsIScriptGlobalObject* global;
+  mParentGlobal.forget(&global);
+  if (global) {
+    NS_ProxyRelease(mainThread, global, PR_FALSE);
+  }
+
+  nsIDocument* document;
+  mParentDocument.forget(&document);
+  if (document) {
+    NS_ProxyRelease(mainThread, document, PR_FALSE);
+  }
+}
+
+NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerPool)
+NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerPool)
+
+nsresult
+nsDOMWorkerPool::Init()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerPool::NoteWorker(nsDOMWorker* aWorker)
+{
+  NS_ASSERTION(aWorker, "Null pointer!");
+
+  PRBool suspendWorker;
+
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    if (mCanceled) {
+      return NS_ERROR_ABORT;
+    }
+
+    nsDOMWorker** newWorker = mWorkers.AppendElement(aWorker);
+    NS_ENSURE_TRUE(newWorker, NS_ERROR_OUT_OF_MEMORY);
+
+    suspendWorker = mSuspended;
+  }
+
+  if (suspendWorker) {
+    aWorker->Suspend();
+  }
+
+  return NS_OK;
+}
+
+void
+nsDOMWorkerPool::NoteDyingWorker(nsDOMWorker* aWorker)
+{
+  NS_ASSERTION(aWorker, "Null pointer!");
+
+  PRBool removeFromThreadService = PR_FALSE;
+
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    NS_ASSERTION(mWorkers.Contains(aWorker), "Worker from a different pool?!");
+    mWorkers.RemoveElement(aWorker);
+
+    if (!mCanceled && !mWorkers.Length()) {
+      removeFromThreadService = PR_TRUE;
+    }
+  }
+
+  if (removeFromThreadService) {
+    nsRefPtr<nsDOMWorkerPool> kungFuDeathGrip(this);
+    nsDOMThreadService::get()->NoteEmptyPool(this);
+  }
+}
+
+void
+nsDOMWorkerPool::GetWorkers(nsTArray<nsDOMWorker*>& aArray)
+{
+  mReentrantMonitor.AssertCurrentThreadIn();
+  NS_ASSERTION(!aArray.Length(), "Should be empty!");
+
+#ifdef DEBUG
+  nsDOMWorker** newWorkers =
+#endif
+  aArray.AppendElements(mWorkers);
+  NS_WARN_IF_FALSE(newWorkers, "Out of memory!");
+}
+
+void
+nsDOMWorkerPool::Cancel()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!mCanceled, "Canceled more than once!");
+
+  nsAutoTArray<nsDOMWorker*, 10> workers;
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    mCanceled = PR_TRUE;
+
+    GetWorkers(workers);
+  }
+
+  PRUint32 count = workers.Length();
+  if (count) {
+    for (PRUint32 index = 0; index < count; index++) {
+      workers[index]->Cancel();
+    }
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    mon.NotifyAll();
+  }
+}
+
+void
+nsDOMWorkerPool::Suspend()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsAutoTArray<nsDOMWorker*, 10> workers;
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    NS_ASSERTION(!mSuspended, "Suspended more than once!");
+    mSuspended = PR_TRUE;
+
+    GetWorkers(workers);
+  }
+
+  PRUint32 count = workers.Length();
+  for (PRUint32 index = 0; index < count; index++) {
+    workers[index]->Suspend();
+  }
+}
+
+void
+nsDOMWorkerPool::Resume()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsAutoTArray<nsDOMWorker*, 10> workers;
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    NS_ASSERTION(mSuspended, "Not suspended!");
+    mSuspended = PR_FALSE;
+
+    GetWorkers(workers);
+  }
+
+  PRUint32 count = workers.Length();
+  if (count) {
+    for (PRUint32 index = 0; index < count; index++) {
+      workers[index]->Resume();
+    }
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    mon.NotifyAll();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerPool.h
@@ -0,0 +1,118 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
+ *   Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NSDOMWORKERPOOL_H__
+#define __NSDOMWORKERPOOL_H__
+
+// Other includes
+#include "jsapi.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsCOMPtr.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+
+class nsDOMWorker;
+class nsIDocument;
+class nsIScriptContext;
+class nsIScriptError;
+class nsIScriptGlobalObject;
+
+class nsDOMWorkerPool
+{
+  typedef mozilla::ReentrantMonitor ReentrantMonitor;
+
+public:
+  nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject,
+                  nsIDocument* aDocument);
+
+  NS_IMETHOD_(nsrefcnt) AddRef();
+  NS_IMETHOD_(nsrefcnt) Release();
+
+  nsIScriptGlobalObject* ScriptGlobalObject() {
+    return mParentGlobal;
+  }
+
+  nsIDocument* ParentDocument() {
+    return mParentDocument;
+  }
+
+  nsresult Init();
+
+  void Cancel();
+  void Suspend();
+  void Resume();
+
+  nsresult NoteWorker(nsDOMWorker* aWorker);
+  void NoteDyingWorker(nsDOMWorker* aWorker);
+
+  ReentrantMonitor& GetReentrantMonitor() {
+    return mReentrantMonitor;
+  }
+
+  const PRUint64 WindowID() const {
+    return mWindowID;
+  }
+
+private:
+  virtual ~nsDOMWorkerPool();
+
+  void GetWorkers(nsTArray<nsDOMWorker*>& aArray);
+
+  nsAutoRefCnt mRefCnt;
+
+  // Reference to the window that created and owns this pool.
+  nsCOMPtr<nsIScriptGlobalObject> mParentGlobal;
+
+  // Reference to the document that created this pool.
+  nsCOMPtr<nsIDocument> mParentDocument;
+
+  // Weak array of workers. The idea is that workers can be garbage collected
+  // independently of the owning pool and other workers.
+  nsTArray<nsDOMWorker*> mWorkers;
+
+  // ReentrantMonitor for suspending and resuming workers.
+  ReentrantMonitor mReentrantMonitor;
+
+  PRPackedBool mCanceled;
+  PRPackedBool mSuspended;
+
+  const PRUint64 mWindowID;
+};
+
+#endif /* __NSDOMWORKERPOOL_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerScriptLoader.cpp
@@ -0,0 +1,881 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMWorkerScriptLoader.h"
+
+// Interfaces
+#include "nsIChannel.h"
+#include "nsIContentPolicy.h"
+#include "nsIHttpChannel.h"
+#include "nsIIOService.h"
+#include "nsIProtocolHandler.h"
+#include "nsIRequest.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIStreamLoader.h"
+
+// Other includes
+#include "nsContentErrors.h"
+#include "nsContentPolicyUtils.h"
+#include "nsContentUtils.h"
+#include "nsISupportsPrimitives.h"
+#include "nsNetError.h"
+#include "nsNetUtil.h"
+#include "nsScriptLoader.h"
+#include "nsThreadUtils.h"
+#include "pratom.h"
+#include "nsDocShellCID.h"
+#include "nsIChannelPolicy.h"
+#include "nsChannelPolicy.h"
+#include "nsIContentSecurityPolicy.h"
+
+// DOMWorker includes
+#include "nsDOMWorkerPool.h"
+#include "nsDOMWorkerSecurityManager.h"
+#include "nsDOMThreadService.h"
+#include "nsDOMWorkerTimeout.h"
+
+using namespace mozilla;
+
+#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
+
+nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader(nsDOMWorker* aWorker)
+: nsDOMWorkerFeature(aWorker),
+  mTarget(nsnull),
+  mScriptCount(0),
+  mCanceled(PR_FALSE),
+  mForWorker(PR_FALSE)
+{
+  // Created on worker thread.
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aWorker, "Null worker!");
+}
+
+NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerScriptLoader, nsDOMWorkerFeature,
+                                                      nsIRunnable,
+                                                      nsIStreamLoaderObserver)
+
+nsresult
+nsDOMWorkerScriptLoader::LoadScripts(JSContext* aCx,
+                                     const nsTArray<nsString>& aURLs,
+                                     PRBool aExecute)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aCx, "Null context!");
+
+  mTarget = NS_GetCurrentThread();
+  NS_ASSERTION(mTarget, "This should never be null!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  mScriptCount = aURLs.Length();
+  if (!mScriptCount) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  // Do all the memory work for these arrays now rather than checking for
+  // failures all along the way.
+  PRBool success = mLoadInfos.SetCapacity(mScriptCount);
+  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+  // Need one runnable per script and then an extra for the finished
+  // notification.
+  success = mPendingRunnables.SetCapacity(mScriptCount + 1);
+  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+  for (PRUint32 index = 0; index < mScriptCount; index++) {
+    ScriptLoadInfo* newInfo = mLoadInfos.AppendElement();
+    NS_ASSERTION(newInfo, "Shouldn't fail if SetCapacity succeeded above!");
+
+    newInfo->url.Assign(aURLs[index]);
+    if (newInfo->url.IsEmpty()) {
+      return NS_ERROR_INVALID_ARG;
+    }
+
+    success = newInfo->scriptObj.Hold(aCx);
+    NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
+  }
+
+  // Don't want timeouts, etc., from queuing up while we're waiting on the
+  // network or compiling.
+  AutoSuspendWorkerEvents aswe(this);
+
+  nsresult rv = DoRunLoop(aCx);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // Verify that all scripts downloaded and compiled.
+  rv = VerifyScripts(aCx);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (aExecute) {
+    rv = ExecuteScripts(aCx);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerScriptLoader::LoadWorkerScript(JSContext* aCx,
+                                          const nsString& aURL)
+{
+  mForWorker = PR_TRUE;
+
+  nsAutoTArray<nsString, 1> url;
+  url.AppendElement(aURL);
+
+  return LoadScripts(aCx, url, PR_FALSE);
+}
+
+nsresult
+nsDOMWorkerScriptLoader::DoRunLoop(JSContext* aCx)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  volatile PRBool done = PR_FALSE;
+  mDoneRunnable = new ScriptLoaderDone(this, &done);
+  NS_ENSURE_TRUE(mDoneRunnable, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv = NS_DispatchToMainThread(this);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  while (!(done || mCanceled)) {
+    JSAutoSuspendRequest asr(aCx);
+    NS_ProcessNextEvent(mTarget);
+  }
+
+  return mCanceled ? NS_ERROR_ABORT : NS_OK;
+}
+
+nsresult
+nsDOMWorkerScriptLoader::VerifyScripts(JSContext* aCx)
+{
+  NS_ASSERTION(aCx, "Shouldn't be null!");
+
+  nsresult rv = NS_OK;
+
+  for (PRUint32 index = 0; index < mScriptCount; index++) {
+    ScriptLoadInfo& loadInfo = mLoadInfos[index];
+    NS_ASSERTION(loadInfo.done, "Inconsistent state!");
+
+    if (NS_SUCCEEDED(loadInfo.result) && loadInfo.scriptObj.ToJSObject()) {
+      continue;
+    }
+
+    NS_ASSERTION(!loadInfo.scriptObj.ToJSObject(), "Inconsistent state!");
+
+    // Flag failure before worrying about whether or not to report an error.
+    rv = NS_FAILED(loadInfo.result) ? loadInfo.result : NS_ERROR_FAILURE;
+
+    // If loadInfo.result is a success code then the compiler probably reported
+    // an error already. Also we don't really care about NS_BINDING_ABORTED
+    // since that's the code we set when some other script had a problem and the
+    // rest were canceled.
+    if (NS_SUCCEEDED(loadInfo.result) || loadInfo.result == NS_BINDING_ABORTED) {
+      continue;
+    }
+
+    // Ok, this is the script that caused us to fail.
+
+    JSAutoRequest ar(aCx);
+
+    // Only throw an error if there is no other pending exception.
+    if (!JS_IsExceptionPending(aCx)) {
+      const char* message;
+      switch (loadInfo.result) {
+        case NS_ERROR_MALFORMED_URI:
+          message = "Malformed script URI: %s";
+          break;
+        case NS_ERROR_FILE_NOT_FOUND:
+        case NS_ERROR_NOT_AVAILABLE:
+          message = "Script file not found: %s";
+          break;
+        default:
+          message = "Failed to load script: %s (nsresult = 0x%x)";
+          break;
+      }
+      NS_ConvertUTF16toUTF8 url(loadInfo.url);
+      JS_ReportError(aCx, message, url.get(), loadInfo.result);
+    }
+    break;
+  }
+
+  return rv;
+}
+
+nsresult
+nsDOMWorkerScriptLoader::ExecuteScripts(JSContext* aCx)
+{
+  NS_ASSERTION(aCx, "Shouldn't be null!");
+
+  // Now execute all the scripts.
+  for (PRUint32 index = 0; index < mScriptCount; index++) {
+    ScriptLoadInfo& loadInfo = mLoadInfos[index];
+
+    JSAutoRequest ar(aCx);
+
+    JSObject* scriptObj = loadInfo.scriptObj.ToJSObject();
+    NS_ASSERTION(scriptObj, "This shouldn't ever be null!");
+
+    JSObject* global = mWorker->mGlobal ?
+                       mWorker->mGlobal :
+                       JS_GetGlobalObject(aCx);
+    NS_ENSURE_STATE(global);
+
+    // Because we may have nested calls to this function we don't want the
+    // execution to automatically report errors. We let them propagate instead.
+    uint32 oldOpts =
+      JS_SetOptions(aCx, JS_GetOptions(aCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
+
+    PRBool success = JS_ExecuteScript(aCx, global, scriptObj, NULL);
+
+    JS_SetOptions(aCx, oldOpts);
+
+    if (!success) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  return NS_OK;
+}
+
+void
+nsDOMWorkerScriptLoader::Cancel()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  NS_ASSERTION(!mCanceled, "Cancel called more than once!");
+  mCanceled = PR_TRUE;
+
+  for (PRUint32 index = 0; index < mScriptCount; index++) {
+    ScriptLoadInfo& loadInfo = mLoadInfos[index];
+
+    nsIRequest* request =
+      static_cast<nsIRequest*>(loadInfo.channel.get());
+    if (request) {
+#ifdef DEBUG
+      nsresult rv =
+#endif
+      request->Cancel(NS_BINDING_ABORTED);
+      NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to cancel channel!");
+    }
+  }
+
+  nsAutoTArray<ScriptLoaderRunnable*, 10> runnables;
+  {
+    MutexAutoLock lock(mWorker->GetLock());
+    runnables.AppendElements(mPendingRunnables);
+    mPendingRunnables.Clear();
+  }
+
+  PRUint32 runnableCount = runnables.Length();
+  for (PRUint32 index = 0; index < runnableCount; index++) {
+    runnables[index]->Revoke();
+  }
+
+  // We're about to post a revoked event to the worker thread, which seems
+  // silly, but we have to do this because the worker thread may be sleeping
+  // waiting on its event queue.
+  NotifyDone();
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScriptLoader::Run()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // We may have been canceled already.
+  if (mCanceled) {
+    return NS_BINDING_ABORTED;
+  }
+
+  nsresult rv = RunInternal();
+  if (NS_SUCCEEDED(rv)) {
+    return rv;
+  }
+
+  // Ok, something failed beyond a normal cancel.
+
+  // If necko is holding a ref to us then we'll end up notifying in the
+  // OnStreamComplete method, not here.
+  PRBool needsNotify = PR_TRUE;
+
+  // Cancel any async channels that were already opened.
+  for (PRUint32 index = 0; index < mScriptCount; index++) {
+    ScriptLoadInfo& loadInfo = mLoadInfos[index];
+
+    nsIRequest* request = static_cast<nsIRequest*>(loadInfo.channel.get());
+    if (request) {
+#ifdef DEBUG
+      nsresult rvInner =
+#endif
+      request->Cancel(NS_BINDING_ABORTED);
+      NS_WARN_IF_FALSE(NS_SUCCEEDED(rvInner), "Failed to cancel channel!");
+
+      // Necko is holding a ref to us so make sure that the OnStreamComplete
+      // code sends the done event.
+      needsNotify = PR_FALSE;
+    }
+    else {
+      // Make sure to set this so that the OnStreamComplete code will dispatch
+      // the done event.
+      loadInfo.done = PR_TRUE;
+    }
+  }
+
+  if (needsNotify) {
+    NotifyDone();
+  }
+
+  return rv;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
+                                          nsISupports* aContext,
+                                          nsresult aStatus,
+                                          PRUint32 aStringLen,
+                                          const PRUint8* aString)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // We may have been canceled already.
+  if (mCanceled) {
+    return NS_BINDING_ABORTED;
+  }
+
+  nsresult rv = OnStreamCompleteInternal(aLoader, aContext, aStatus, aStringLen,
+                                         aString);
+
+  // Dispatch the done event if we've received all the data.
+  for (PRUint32 index = 0; index < mScriptCount; index++) {
+    if (!mLoadInfos[index].done) {
+      // Some async load is still outstanding, don't notify yet.
+      break;
+    }
+
+    if (index == mScriptCount - 1) {
+      // All loads complete, signal the thread.
+      NotifyDone();
+    }
+  }
+
+  return rv;
+}
+
+nsresult
+nsDOMWorkerScriptLoader::RunInternal()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (mForWorker) {
+    NS_ASSERTION(mScriptCount == 1, "Bad state!");
+  }
+
+  nsRefPtr<nsDOMWorker> parentWorker = mWorker->GetParent();
+
+  // Figure out which principal to use.
+  nsIPrincipal* principal = mWorker->GetPrincipal();
+  if (!principal) {
+    if (!parentWorker) {
+      NS_ERROR("Must have a principal if this is not a subworker!");
+    }
+    principal = parentWorker->GetPrincipal();
+  }
+  NS_ASSERTION(principal, "This should never be null here!");
+
+  // Figure out our base URI.
+  nsCOMPtr<nsIURI> baseURI;
+  if (mForWorker) {
+    if (parentWorker) {
+      baseURI = parentWorker->GetBaseURI();
+      NS_ASSERTION(baseURI, "Should have been set already!");
+    }
+    else {
+      // May be null.
+      baseURI = mWorker->GetBaseURI();
+
+      // Don't leave a temporary URI hanging around.
+      mWorker->ClearBaseURI();
+    }
+    NS_ASSERTION(!mWorker->GetBaseURI(), "Should not be set here!");
+  }
+  else {
+    baseURI = mWorker->GetBaseURI();
+    NS_ASSERTION(baseURI, "Should have been set already!");
+  }
+
+  nsCOMPtr<nsIDocument> parentDoc = mWorker->Pool()->ParentDocument();
+
+  // All of these can potentially be null, but that should be ok. We'll either
+  // succeed without them or fail below.
+  nsCOMPtr<nsILoadGroup> loadGroup;
+  if (parentDoc) {
+    loadGroup = parentDoc->GetDocumentLoadGroup();
+  }
+
+  nsCOMPtr<nsIIOService> ios(do_GetIOService());
+
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  NS_ASSERTION(secMan, "This should never be null!");
+
+  for (PRUint32 index = 0; index < mScriptCount; index++) {
+    ScriptLoadInfo& loadInfo = mLoadInfos[index];
+    nsresult& rv = loadInfo.result;
+
+    nsCOMPtr<nsIURI>& uri = loadInfo.finalURI;
+    rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
+                                                   loadInfo.url, parentDoc,
+                                                   baseURI);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    // If we're part of a document then check the content load policy.
+    if (parentDoc) {
+      PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
+      rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT, uri,
+                                     principal, parentDoc,
+                                     NS_LITERAL_CSTRING("text/javascript"),
+                                     nsnull, &shouldLoad,
+                                     nsContentUtils::GetContentPolicy(),
+                                     secMan);
+      if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
+        if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
+          return (rv = NS_ERROR_CONTENT_BLOCKED);
+        }
+        return (rv = NS_ERROR_CONTENT_BLOCKED_SHOW_ALT);
+      }
+    }
+
+    // If this script loader is being used to make a new worker then we need to
+    // do a same-origin check. Otherwise we need to clear the load with the
+    // security manager.
+    rv = mForWorker ?
+         principal->CheckMayLoad(uri, PR_FALSE):
+         secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // We need to know which index we're on in OnStreamComplete so we know where
+    // to put the result.
+    nsCOMPtr<nsISupportsPRUint32> indexSupports =
+      do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = indexSupports->SetData(index);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // We don't care about progress so just use the simple stream loader for
+    // OnStreamComplete notification only.
+    nsCOMPtr<nsIStreamLoader> loader;
+    rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Get Content Security Policy from parent document to pass into channel
+    nsCOMPtr<nsIContentSecurityPolicy> csp;
+    rv = principal->GetCsp(getter_AddRefs(csp));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIChannelPolicy> channelPolicy;
+    if (csp) {
+      channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1", &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = channelPolicy->SetContentSecurityPolicy(csp);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    rv = NS_NewChannel(getter_AddRefs(loadInfo.channel),
+                       uri, ios, loadGroup, nsnull,
+                       nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI,
+                       channelPolicy);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = loadInfo.channel->AsyncOpen(loader, indexSupports);
+    if (NS_FAILED(rv)) {
+      // Null this out so we don't try to cancel it later.
+      loadInfo.channel = nsnull;
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerScriptLoader::OnStreamCompleteInternal(nsIStreamLoader* aLoader,
+                                                  nsISupports* aContext,
+                                                  nsresult aStatus,
+                                                  PRUint32 aStringLen,
+                                                  const PRUint8* aString)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsISupportsPRUint32> indexSupports(do_QueryInterface(aContext));
+  NS_ENSURE_TRUE(indexSupports, NS_ERROR_NO_INTERFACE);
+
+  PRUint32 index = PR_UINT32_MAX;
+  indexSupports->GetData(&index);
+
+  if (index >= mScriptCount) {
+    NS_NOTREACHED("This really can't fail or we'll hang!");
+    return NS_ERROR_FAILURE;
+  }
+
+  ScriptLoadInfo& loadInfo = mLoadInfos[index];
+
+  NS_ASSERTION(!loadInfo.done, "Got complete on the same load twice!");
+  loadInfo.done = PR_TRUE;
+
+  // Use an alias to keep rv and loadInfo.result in sync.
+  nsresult& rv = loadInfo.result;
+
+  if (NS_FAILED(aStatus)) {
+    return rv = aStatus;
+  }
+
+  if (!(aStringLen && aString)) {
+    return rv = NS_ERROR_UNEXPECTED;
+  }
+
+  // Make sure we're not seeing the result of a 404 or something by checking the
+  // 'requestSucceeded' attribute on the http channel.
+  nsCOMPtr<nsIRequest> request;
+  rv = aLoader->GetRequest(getter_AddRefs(request));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
+  if (httpChannel) {
+    PRBool requestSucceeded;
+    rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!requestSucceeded) {
+      return rv = NS_ERROR_NOT_AVAILABLE;
+    }
+  }
+
+  // May be null.
+  nsIDocument* parentDoc = mWorker->Pool()->ParentDocument();
+
+  // Use the regular nsScriptLoader for this grunt work! Should be just fine
+  // because we're running on the main thread.
+  rv = nsScriptLoader::ConvertToUTF16(loadInfo.channel, aString, aStringLen,
+                                      EmptyString(), parentDoc,
+                                      loadInfo.scriptText);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (loadInfo.scriptText.IsEmpty()) {
+    return rv = NS_ERROR_FAILURE;
+  }
+
+  nsCString filename;
+  rv = loadInfo.finalURI->GetSpec(filename);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (filename.IsEmpty()) {
+    filename.Assign(NS_LossyConvertUTF16toASCII(loadInfo.url));
+  }
+  else {
+    // This will help callers figure out what their script url resolved to in
+    // case of errors.
+    loadInfo.url.Assign(NS_ConvertUTF8toUTF16(filename));
+  }
+
+  // Update the principal of the worker and its base URI if we just loaded the
+  // worker's primary script.
+  if (mForWorker) {
+    nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+    NS_ASSERTION(channel, "This should never fail!");
+
+    // Take care of the base URI first.
+    nsCOMPtr<nsIURI> finalURI;
+    rv = NS_GetFinalChannelURI(channel, getter_AddRefs(finalURI));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mWorker->SetBaseURI(finalURI);
+
+    // Now to figure out which principal to give this worker.
+    nsRefPtr<nsDOMWorker> parent = mWorker->GetParent();
+    NS_ASSERTION(mWorker->GetPrincipal() || parent, "Must have one of these!");
+
+    nsCOMPtr<nsIPrincipal> loadPrincipal = mWorker->GetPrincipal() ?
+                                           mWorker->GetPrincipal() :
+                                           parent->GetPrincipal();
+
+    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+    NS_ASSERTION(ssm, "Should never be null!");
+
+    nsCOMPtr<nsIPrincipal> channelPrincipal;
+    rv = ssm->GetChannelPrincipal(channel, getter_AddRefs(channelPrincipal));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // See if this is a resource URI. Since JSMs usually come from resource://
+    // URIs we're currently considering all URIs with the URI_IS_UI_RESOURCE
+    // flag as valid for creating privileged workers.
+    if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
+      PRBool isResource;
+      rv = NS_URIChainHasFlags(finalURI,
+                               nsIProtocolHandler::URI_IS_UI_RESOURCE,
+                               &isResource);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (isResource) {
+        rv = ssm->GetSystemPrincipal(getter_AddRefs(channelPrincipal));
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+
+    // If the load principal is the system principal then the channel principal
+    // must also be the system principal (we do not allow chrome code to create
+    // workers with non-chrome scripts). Otherwise this channel principal must
+    // be same origin with the load principal (we check again here in case
+    // redirects changed the location of the script).
+    if (nsContentUtils::IsSystemPrincipal(loadPrincipal)) {
+      if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
+        return rv = NS_ERROR_DOM_BAD_URI;
+      }
+    }
+    else if (NS_FAILED(loadPrincipal->CheckMayLoad(finalURI, PR_FALSE))) {
+      return rv = NS_ERROR_DOM_BAD_URI;
+    }
+
+    mWorker->SetPrincipal(channelPrincipal);
+  }
+
+  nsRefPtr<ScriptCompiler> compiler =
+    new ScriptCompiler(this, loadInfo.scriptText, filename, loadInfo.scriptObj);
+  NS_ASSERTION(compiler, "Out of memory!");
+  if (!compiler) {
+    return rv = NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  rv = mTarget->Dispatch(compiler, NS_DISPATCH_NORMAL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return rv;
+}
+
+void
+nsDOMWorkerScriptLoader::NotifyDone()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!mDoneRunnable) {
+    // We've already completed, no need to cancel anything.
+    return;
+  }
+
+  for (PRUint32 index = 0; index < mScriptCount; index++) {
+    ScriptLoadInfo& loadInfo = mLoadInfos[index];
+    // Null both of these out because they aren't threadsafe and must be
+    // destroyed on this thread.
+    loadInfo.channel = nsnull;
+    loadInfo.finalURI = nsnull;
+
+    if (mCanceled) {
+      // Simulate a complete, yet failed, load.
+      loadInfo.done = PR_TRUE;
+      loadInfo.result = NS_BINDING_ABORTED;
+    }
+  }
+
+#ifdef DEBUG
+  nsresult rv =
+#endif
+  mTarget->Dispatch(mDoneRunnable, NS_DISPATCH_NORMAL);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Couldn't dispatch done event!");
+
+  mDoneRunnable = nsnull;
+}
+
+void
+nsDOMWorkerScriptLoader::SuspendWorkerEvents()
+{
+  NS_ASSERTION(mWorker, "No worker yet!");
+  mWorker->SuspendFeatures();
+}
+
+void
+nsDOMWorkerScriptLoader::ResumeWorkerEvents()
+{
+  NS_ASSERTION(mWorker, "No worker yet!");
+  mWorker->ResumeFeatures();
+}
+
+nsDOMWorkerScriptLoader::
+ScriptLoaderRunnable::ScriptLoaderRunnable(nsDOMWorkerScriptLoader* aLoader)
+: mRevoked(PR_FALSE),
+  mLoader(aLoader)
+{
+  MutexAutoLock lock(aLoader->GetLock());
+#ifdef DEBUG
+  nsDOMWorkerScriptLoader::ScriptLoaderRunnable** added =
+#endif
+  aLoader->mPendingRunnables.AppendElement(this);
+  NS_ASSERTION(added, "This shouldn't fail because we SetCapacity earlier!");
+}
+
+nsDOMWorkerScriptLoader::
+ScriptLoaderRunnable::~ScriptLoaderRunnable()
+{
+  if (!mRevoked) {
+    MutexAutoLock lock(mLoader->GetLock());
+#ifdef DEBUG
+    PRBool removed =
+#endif
+    mLoader->mPendingRunnables.RemoveElement(this);
+    NS_ASSERTION(removed, "Someone has changed the array!");
+  }
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerScriptLoader::ScriptLoaderRunnable,
+                              nsIRunnable)
+
+void
+nsDOMWorkerScriptLoader::ScriptLoaderRunnable::Revoke()
+{
+  mRevoked = PR_TRUE;
+}
+
+nsDOMWorkerScriptLoader::
+ScriptCompiler::ScriptCompiler(nsDOMWorkerScriptLoader* aLoader,
+                               const nsString& aScriptText,
+                               const nsCString& aFilename,
+                               nsAutoJSValHolder& aScriptObj)
+: ScriptLoaderRunnable(aLoader),
+  mScriptText(aScriptText),
+  mFilename(aFilename),
+  mScriptObj(aScriptObj)
+{
+  NS_ASSERTION(!aScriptText.IsEmpty(), "No script to compile!");
+  NS_ASSERTION(aScriptObj.IsHeld(), "Should be held!");
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScriptLoader::ScriptCompiler::Run()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mRevoked) {
+    return NS_OK;
+  }
+
+  NS_ASSERTION(!mScriptObj.ToJSObject(), "Already have a script object?!");
+  NS_ASSERTION(mScriptObj.IsHeld(), "Not held?!");
+  NS_ASSERTION(!mScriptText.IsEmpty(), "Shouldn't have empty source here!");
+
+  JSContext* cx = nsDOMThreadService::GetCurrentContext();
+  NS_ENSURE_STATE(cx);
+
+  JSAutoRequest ar(cx);
+
+  JSObject* global = JS_GetGlobalObject(cx);
+  NS_ENSURE_STATE(global);
+
+  // Because we may have nested calls to this function we don't want the
+  // execution to automatically report errors. We let them propagate instead.
+  uint32 oldOpts =
+    JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT |
+                      JSOPTION_NO_SCRIPT_RVAL);
+
+  JSPrincipals* principal = nsDOMWorkerSecurityManager::WorkerPrincipal();
+
+  JSObject* scriptObj =
+    JS_CompileUCScriptForPrincipals(cx, global, principal,
+                                    reinterpret_cast<const jschar*>
+                                               (mScriptText.BeginReading()),
+                                    mScriptText.Length(), mFilename.get(), 1);
+
+  JS_SetOptions(cx, oldOpts);
+
+  if (!scriptObj) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mScriptObj = scriptObj;
+
+  return NS_OK;
+}
+
+nsDOMWorkerScriptLoader::
+ScriptLoaderDone::ScriptLoaderDone(nsDOMWorkerScriptLoader* aLoader,
+                                   volatile PRBool* aDoneFlag)
+: ScriptLoaderRunnable(aLoader),
+  mDoneFlag(aDoneFlag)
+{
+  NS_ASSERTION(aDoneFlag && !*aDoneFlag, "Bad setup!");
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScriptLoader::ScriptLoaderDone::Run()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mRevoked) {
+    return NS_OK;
+  }
+
+  *mDoneFlag = PR_TRUE;
+  return NS_OK;
+}
+
+nsDOMWorkerScriptLoader::
+AutoSuspendWorkerEvents::AutoSuspendWorkerEvents(nsDOMWorkerScriptLoader* aLoader)
+: mLoader(aLoader)
+{
+  NS_ASSERTION(aLoader, "Don't hand me null!");
+  aLoader->SuspendWorkerEvents();
+}
+
+nsDOMWorkerScriptLoader::
+AutoSuspendWorkerEvents::~AutoSuspendWorkerEvents()
+{
+  mLoader->ResumeWorkerEvents();
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerSecurityManager.cpp
@@ -0,0 +1,148 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMWorkerSecurityManager.h"
+
+// Interfaces
+#include "nsIClassInfo.h"
+
+// Other includes
+#include "jsapi.h"
+#include "nsDOMError.h"
+#include "nsThreadUtils.h"
+
+// DOMWorker includes
+#include "nsDOMThreadService.h"
+#include "nsDOMWorker.h"
+
+#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
+
+class nsDOMWorkerPrincipal
+{
+public:
+  static void Destroy(JSContext*, JSPrincipals*) {
+    // nothing
+  }
+
+  static JSBool Subsume(JSPrincipals*, JSPrincipals*) {
+    return JS_TRUE;
+  }
+};
+
+static JSPrincipals gWorkerPrincipal =
+{ "domworkerthread" /* codebase */,
+  NULL /* getPrincipalArray */,
+  NULL /* globalPrivilegesEnabled */,
+  1 /* refcount */,
+  nsDOMWorkerPrincipal::Destroy /* destroy */,
+  nsDOMWorkerPrincipal::Subsume /* subsume */ };
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerSecurityManager,
+                              nsIXPCSecurityManager)
+
+NS_IMETHODIMP
+nsDOMWorkerSecurityManager::CanCreateWrapper(JSContext* aCx,
+                                             const nsIID& aIID,
+                                             nsISupports* aObj,
+                                             nsIClassInfo* aClassInfo,
+                                             void** aPolicy)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerSecurityManager::CanCreateInstance(JSContext* aCx,
+                                              const nsCID& aCID)
+{
+  return CanGetService(aCx, aCID);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerSecurityManager::CanGetService(JSContext* aCx,
+                                          const nsCID& aCID)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  return worker->IsPrivileged() ? NS_OK : NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerSecurityManager::CanAccess(PRUint32 aAction,
+                                      nsAXPCNativeCallContext* aCallContext,
+                                      JSContext* aJSContext,
+                                      JSObject* aJSObject,
+                                      nsISupports* aObj,
+                                      nsIClassInfo* aClassInfo,
+                                      jsid aName,
+                                      void** aPolicy)
+{
+  return NS_OK;
+}
+
+JSPrincipals*
+nsDOMWorkerSecurityManager::WorkerPrincipal()
+{
+  return &gWorkerPrincipal;
+}
+
+JSBool
+nsDOMWorkerSecurityManager::JSCheckAccess(JSContext* aCx,
+                                          JSObject* aObj,
+                                          jsid aId,
+                                          JSAccessMode aMode,
+                                          jsval* aVp)
+{
+  return JS_TRUE;
+}
+
+JSPrincipals*
+nsDOMWorkerSecurityManager::JSFindPrincipal(JSContext* aCx, JSObject* aObj)
+{
+  return WorkerPrincipal();
+}
+
+JSBool
+nsDOMWorkerSecurityManager::JSTranscodePrincipals(JSXDRState* aXdr,
+                                                 JSPrincipals** aJsprinp)
+{
+  NS_NOTREACHED("Shouldn't ever call this!");
+  return JS_FALSE;
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerSecurityManager.h
@@ -0,0 +1,62 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NSDOMWORKERSECURITYMANAGER_H__
+#define __NSDOMWORKERSECURITYMANAGER_H__
+
+#include "nsIXPCSecurityManager.h"
+#include "jsapi.h"
+
+class nsDOMWorkerSecurityManager : public nsIXPCSecurityManager
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIXPCSECURITYMANAGER
+
+  static JSPrincipals* WorkerPrincipal();
+
+  static JSBool JSCheckAccess(JSContext* aCx, JSObject* aObj, jsid aId,
+                              JSAccessMode aMode, jsval* aVp);
+
+  static JSPrincipals* JSFindPrincipal(JSContext* aCx, JSObject* aObj);
+
+  static JSBool JSTranscodePrincipals(JSXDRState* aXdr,
+                                      JSPrincipals** aJsprinp);
+};
+
+#endif /* __NSDOMWORKERSECURITYMANAGER_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerTimeout.cpp
@@ -0,0 +1,485 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMWorkerTimeout.h"
+
+// Interfaces
+#include "nsIJSContextStack.h"
+#include "nsIJSRuntimeService.h"
+#include "nsITimer.h"
+#include "nsIXPConnect.h"
+
+// Other includes
+#include "nsContentUtils.h"
+#include "nsJSUtils.h"
+#include "nsThreadUtils.h"
+#include "pratom.h"
+#include "prtime.h"
+
+// DOMWorker includes
+#include "nsDOMThreadService.h"
+#include "nsDOMWorkerSecurityManager.h"
+
+#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
+
+#define CONSTRUCTOR_ENSURE_TRUE(_cond, _rv) \
+  PR_BEGIN_MACRO \
+    if (NS_UNLIKELY(!(_cond))) { \
+      NS_WARNING("CONSTRUCTOR_ENSURE_TRUE(" #_cond ") failed"); \
+      (_rv) = NS_ERROR_FAILURE; \
+      return; \
+    } \
+  PR_END_MACRO
+
+#define SUSPEND_SPINLOCK_COUNT 5000
+
+static const char* kSetIntervalStr = "setInterval";
+static const char* kSetTimeoutStr = "setTimeout";
+
+nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc,
+                                                       jsval* aArgv,
+                                                       nsresult* aRv)
+: mCallbackArgsLength(0)
+{
+  MOZ_COUNT_CTOR(nsDOMWorkerTimeout::FunctionCallback);
+
+  JSRuntime* rt;
+  *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
+  if (NS_FAILED(*aRv))
+    return;
+
+  JSBool ok = mCallback.Hold(rt);
+  CONSTRUCTOR_ENSURE_TRUE(ok, *aRv);
+
+  mCallback = aArgv[0];
+
+  // We want enough space for an extra lateness arg.
+  mCallbackArgsLength = aArgc > 2 ? aArgc - 1 : 1;
+
+  PRBool success = mCallbackArgs.SetLength(mCallbackArgsLength);
+  CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
+
+  PRUint32 index = 0;
+  for (; index < mCallbackArgsLength - 1; index++) {
+    ok = mCallbackArgs[index].Hold(rt);
+    CONSTRUCTOR_ENSURE_TRUE(ok, *aRv);
+
+    mCallbackArgs[index] = aArgv[index + 2];
+  }
+
+  // Take care of the last arg.
+  index = mCallbackArgsLength - 1;
+
+  ok = mCallbackArgs[index].Hold(rt);
+  CONSTRUCTOR_ENSURE_TRUE(ok, *aRv);
+
+  *aRv = NS_OK;
+}
+
+nsDOMWorkerTimeout::FunctionCallback::~FunctionCallback()
+{
+  MOZ_COUNT_DTOR(nsDOMWorkerTimeout::FunctionCallback);
+}
+
+nsresult
+nsDOMWorkerTimeout::FunctionCallback::Run(nsDOMWorkerTimeout* aTimeout,
+                                          JSContext* aCx)
+{
+  PRInt32 lateness = NS_MAX(0, PRInt32(PR_Now() - aTimeout->mTargetTime)) /
+                     (PRTime)PR_USEC_PER_MSEC;
+  mCallbackArgs[mCallbackArgsLength - 1] = INT_TO_JSVAL(lateness);
+
+  JSObject* global = JS_GetGlobalObject(aCx);
+  NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
+
+  nsTArray<jsval> argv;
+  PRBool success = argv.SetCapacity(mCallbackArgsLength);
+  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+  for (PRUint32 index = 0; index < mCallbackArgsLength; index++) {
+    argv.AppendElement(mCallbackArgs[index]);
+  }
+
+  jsval rval;
+  JSBool ok =
+    JS_CallFunctionValue(aCx, global, mCallback, mCallbackArgsLength,
+                         argv.Elements(), &rval);
+  NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+
+  return NS_OK;
+}
+
+nsDOMWorkerTimeout::ExpressionCallback::ExpressionCallback(PRUint32 aArgc,
+                                                           jsval* aArgv,
+                                                           JSContext* aCx,
+                                                           nsresult* aRv)
+: mLineNumber(0)
+{
+  MOZ_COUNT_CTOR(nsDOMWorkerTimeout::ExpressionCallback);
+
+  JSString* expr = JS_ValueToString(aCx, aArgv[0]);
+  *aRv = expr ? NS_OK : NS_ERROR_FAILURE;
+  if (NS_FAILED(*aRv))
+    return;
+
+  JSRuntime* rt;
+  *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
+  if (NS_FAILED(*aRv))
+    return;
+
+  JSBool ok = mExpression.Hold(rt);
+  CONSTRUCTOR_ENSURE_TRUE(ok, *aRv);
+
+  mExpression = aArgv[0];
+
+  // Get the calling location.
+  const char* fileName;
+  PRUint32 lineNumber;