Bug 460811 - 'Bring workers up to latest spec'.r+sr=jst
☠☠ backed out by 1a2fefd12905 ☠ ☠
authorBen Turner <bent.mozilla@gmail.com>
Tue, 04 Nov 2008 13:49:04 -0800
changeset 21320 b83d3c8ac166e5f92f36fa44df498e74efd41832
parent 21319 668acba0c40235e21641151ca6e34a62d7c6cf33
child 21321 dc1aff36a411534b33fd8ba9922eb3149fe53cbb
push idunknown
push userunknown
push dateunknown
bugs460811
milestone1.9.1b2pre
Bug 460811 - 'Bring workers up to latest spec'.r+sr=jst
dom/public/idl/base/nsIDOMNavigator.idl
dom/public/idl/threads/Makefile.in
dom/public/idl/threads/nsIDOMThreads.idl
dom/public/idl/threads/nsIDOMWorkers.idl
dom/public/nsDOMClassInfoID.h
dom/src/base/nsDOMClassInfo.cpp
dom/src/base/nsDOMClassInfo.h
dom/src/base/nsDOMScriptObjectFactory.cpp
dom/src/base/nsGlobalWindow.cpp
dom/src/base/nsGlobalWindow.h
dom/src/base/nsJSEnvironment.cpp
dom/src/threads/Makefile.in
dom/src/threads/nsAutoJSObjectHolder.h
dom/src/threads/nsDOMThreadService.cpp
dom/src/threads/nsDOMThreadService.h
dom/src/threads/nsDOMWorker.cpp
dom/src/threads/nsDOMWorker.h
dom/src/threads/nsDOMWorkerEvents.cpp
dom/src/threads/nsDOMWorkerEvents.h
dom/src/threads/nsDOMWorkerMacros.h
dom/src/threads/nsDOMWorkerMessageHandler.cpp
dom/src/threads/nsDOMWorkerMessageHandler.h
dom/src/threads/nsDOMWorkerPool.cpp
dom/src/threads/nsDOMWorkerPool.h
dom/src/threads/nsDOMWorkerScriptLoader.cpp
dom/src/threads/nsDOMWorkerScriptLoader.h
dom/src/threads/nsDOMWorkerThread.cpp
dom/src/threads/nsDOMWorkerThread.h
dom/src/threads/nsDOMWorkerTimeout.cpp
dom/src/threads/nsDOMWorkerTimeout.h
dom/src/threads/nsDOMWorkerXHR.cpp
dom/src/threads/nsDOMWorkerXHR.h
dom/src/threads/nsDOMWorkerXHRProxy.cpp
dom/src/threads/nsDOMWorkerXHRProxy.h
dom/src/threads/test/Makefile.in
dom/src/threads/test/fibonacci_worker.js
dom/src/threads/test/importScripts_worker.js
dom/src/threads/test/longThread_worker.js
dom/src/threads/test/recursion_worker.js
dom/src/threads/test/regExpStatics_worker.js
dom/src/threads/test/simpleThread_worker.js
dom/src/threads/test/test_fibonacci.html
dom/src/threads/test/test_importScripts.html
dom/src/threads/test/test_longThread.html
dom/src/threads/test/test_recursion.html
dom/src/threads/test/test_regExpStatics.html
dom/src/threads/test/test_simpleThread.html
dom/src/threads/test/test_threadErrors.html
dom/src/threads/test/test_threadTimeouts.html
dom/src/threads/test/test_xhr.html
dom/src/threads/test/threadErrors_worker1.js
dom/src/threads/test/threadErrors_worker2.js
dom/src/threads/test/threadErrors_worker3.js
dom/src/threads/test/threadErrors_worker4.js
dom/src/threads/test/threadTimeouts_worker.js
dom/src/threads/test/xhr_worker.js
js/src/xpconnect/src/xpcwrappednativescope.cpp
--- a/dom/public/idl/base/nsIDOMNavigator.idl
+++ b/dom/public/idl/base/nsIDOMNavigator.idl
@@ -34,19 +34,17 @@
  * 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 "domstubs.idl"
 
-interface nsIDOMWorkerPool;
-
-[scriptable, uuid(c206f746-52e2-47dd-8ccc-ce76ccda6c6d)]
+[scriptable, uuid(777bd8a1-38c1-4b12-ba8f-ff6c2eb8c56b)]
 interface nsIDOMNavigator : nsISupports
 {
   readonly attribute DOMString           appCodeName;
   readonly attribute DOMString           appName;
   readonly attribute DOMString           appVersion;
   readonly attribute DOMString           language;
   readonly attribute nsIDOMMimeTypeArray mimeTypes;
   readonly attribute DOMString           platform;
@@ -60,18 +58,16 @@ interface nsIDOMNavigator : nsISupports
   readonly attribute DOMString           userAgent;
   readonly attribute boolean             cookieEnabled;
   readonly attribute boolean             onLine;
   readonly attribute DOMString           buildID;
 
   boolean                   javaEnabled();
   boolean                   taintEnabled();
 
-  nsIDOMWorkerPool newWorkerPool();
-
   // XXX This one's tough, would nsISupports preference(in DOMString
   // pref /*, ... */); work?
 
   // jsval      preference(/* ... */);
 };
 
 
 [scriptable, uuid(4b4f8316-1dd2-11b2-b265-9a857376d159)]
--- a/dom/public/idl/threads/Makefile.in
+++ b/dom/public/idl/threads/Makefile.in
@@ -42,11 +42,11 @@ srcdir       = @srcdir@
 VPATH        = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE       = dom
 XPIDL_MODULE = dom_threads
 GRE_MODULE   = 1
 
-XPIDLSRCS    =  nsIDOMThreads.idl
+XPIDLSRCS    =  nsIDOMWorkers.idl
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/dom/public/idl/threads/nsIDOMThreads.idl
+++ /dev/null
@@ -1,149 +0,0 @@
-/* -*- 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 "nsISupports.idl"
-
-interface nsIScriptError;
-
-[scriptable, function, uuid(e50ca05d-1381-4abb-a021-02eb720cfc38)]
-interface nsIDOMWorkerMessageListener : nsISupports
-{
-  /**
-   * An nsIDOMWorkerThread receives the onMessage callback when another
-   * worker posts a message to it.
-   *
-   * @param aMessage (in DOMString)
-   *        The message sent from another worker.
-   * @param aSource (in nsISupports)
-   *        The worker that sent the message. Useful for a quick response.
-   */
-  void onMessage(in DOMString aMessage,
-                 in nsISupports aSource);
-};
-
-[scriptable, function, uuid(9df8422e-25dd-43f4-b9b9-709f9e074647)]
-interface nsIDOMWorkerErrorListener : nsISupports
-{
-  /**
-   * An nsIDOMWorkerPool receives the onError callback when one of its child
-   * workers has a parse error or an unhandled exception.
-   * 
-   * @param aMessage (in nsIScriptError)
-   *        Details about the error that occurred. See nsIScriptError.
-   * @param aSource (in nsISupports)
-   *        The worker that sent the message. Depending on the specific error in
-   *        question it may not be possible to use this object (in the case of a
-   *        parse error, for instance, aSource will be unusable).
-   */
-  void onError(in nsIScriptError aError,
-               in nsISupports aSource);
-};
-
-[scriptable, uuid(6f19f3ff-2aaa-4504-9b71-dca3c191efed)]
-interface nsIDOMWorkerThread : nsISupports
-{
-  /**
-   * Sends a message to the worker.
-   *
-   * @param aMessage (in DOMString)
-   *        The message to send.
-   */
-  void postMessage(in DOMString aMessage);
-};
-
-[scriptable, uuid(45312e93-8a3e-4493-9bd9-272a6c23a16c)]
-interface nsIDOMWorkerPool : nsISupports
-{
-  /**
-   * Sends a message to the pool.
-   *
-   * @param aMessage (in DOMString)
-   *        The message to send..
-   */
-  void postMessage(in DOMString aMessage);
-
-  /**
-   * The nsIDOMWorkerMessageListener which handles messages for this worker.
-   * 
-   * Developers should set this attribute to a proper object before another
-   * worker begins sending messages to ensure that all messages are received.
-   */
-  attribute nsIDOMWorkerMessageListener messageListener;
-
-  /**
-   * The nsIDOMWorkerErrorListener which handles errors in child threads.
-   *
-   * Developers should set this attribute to a proper object before calling
-   * createWorker in order to catch parse errors as well as runtime exceptions.
-   */
-  attribute nsIDOMWorkerErrorListener errorListener;
-
-  /**
-   * Create a new worker object by evaluating the given script.
-   *
-   * @param aSourceScript (in DOMString)
-   *        The script to compile. See below for details on the scope in which
-   *        the script will run.
-   */
-  nsIDOMWorkerThread createWorker(in DOMString aSourceScript);
-
-  /**
-   * Create a new worker object by evaluating the given script.
-   *
-   * @param aSourceURL (in AString)
-   *        The script url to load and compile. See below for details on the
-   *        scope in which the script will run.
-   */
-  nsIDOMWorkerThread createWorkerFromURL(in AString aSourceURL);
-};
-
-[scriptable, uuid(0f2a52ea-afc9-49e6-86dd-2d0cb65b5dd5)]
-interface nsIDOMThreadService : nsISupports
-{
-  /**
-   * Creates a new DOM worker pool.
-   */
-  nsIDOMWorkerPool createPool();
-};
-
-[scriptable, uuid(fcf387be-a7e3-4283-8bc5-06bfe13c5e8c)]
-interface nsIDOMWorkerThreadContext : nsISupports
-{
-  readonly attribute nsIDOMWorkerThread thisThread;
-};
new file mode 100644
--- /dev/null
+++ b/dom/public/idl/threads/nsIDOMWorkers.idl
@@ -0,0 +1,98 @@
+/* -*- 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(6c32d0c5-6bfa-438b-ad44-be0df80cd4a8)]
+interface nsIWorkerMessagePort : nsISupports
+{
+  void postMessage(in DOMString 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(3e54b65e-5d25-453e-8d3f-258766fbf6fd)]
+interface nsIWorkerGlobalScope : nsISupports
+{
+  readonly attribute nsIWorkerGlobalScope self;
+};
+
+[scriptable, uuid(b10cfe72-91b9-45c6-ab13-33f89c2d0e56)]
+interface nsIWorkerScope : nsIWorkerGlobalScope
+{
+  void postMessage(in DOMString aMessage,
+                   [optional] in nsIWorkerMessagePort aMessagePort);
+
+  attribute nsIDOMEventListener onmessage;
+};
+
+[scriptable, uuid(b90b7561-b5e2-4545-84b0-280dbaaa94ea)]
+interface nsIAbstractWorker : nsIDOMEventTarget
+{
+  attribute nsIDOMEventListener onerror;
+};
+
+[scriptable, uuid(479c9476-0bc2-4211-8656-6627dfd82b1c)]
+interface nsIWorker : nsIAbstractWorker
+{
+  void postMessage(in DOMString aMessage,
+                   [optional] in nsIWorkerMessagePort aMessagePort);
+
+  attribute nsIDOMEventListener onmessage;
+};
--- a/dom/public/nsDOMClassInfoID.h
+++ b/dom/public/nsDOMClassInfoID.h
@@ -453,16 +453,18 @@ enum nsDOMClassInfoID {
   eDOMClassInfo_NotifyPaintEvent_id,
 
   eDOMClassInfo_SimpleGestureEvent_id,
 
 #ifdef MOZ_MATHML
   eDOMClassInfo_MathMLElement_id,
 #endif
 
+  eDOMClassInfo_Worker_id,
+
   // This one better be the last one in this list
   eDOMClassInfoIDCount
 };
 
 /**
  * nsIClassInfo helper macros
  */
 
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -452,16 +452,19 @@
 #include "nsIDOMLoadStatus.h"
 #include "nsIDOMLoadStatusEvent.h"
 
 // Geolocation
 #include "nsIDOMGeoGeolocation.h"
 #include "nsIDOMGeoPosition.h"
 #include "nsIDOMGeoPositionError.h"
 
+// Workers
+#include "nsDOMWorker.h"
+
 #include "nsDOMFile.h"
 #include "nsIDOMFileException.h"
 
 // Simple gestures include
 #include "nsIDOMSimpleGestureEvent.h"
 
 static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID);
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
@@ -1294,16 +1297,19 @@ static nsDOMClassInfoData sClassInfoData
 
   NS_DEFINE_CLASSINFO_DATA(SimpleGestureEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
 #ifdef MOZ_MATHML
   NS_DEFINE_CLASSINFO_DATA_WITH_NAME(MathMLElement, Element, nsElementSH,
                                      ELEMENT_SCRIPTABLE_FLAGS)
 #endif
+
+  NS_DEFINE_CLASSINFO_DATA(Worker, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 };
 
 // Objects that shuld be constructable through |new Name();|
 struct nsContractIDMapData
 {
   PRInt32 mDOMClassInfoID;
   const char *mContractID;
 };
@@ -1316,16 +1322,30 @@ static const nsContractIDMapData kConstr
   NS_DEFINE_CONSTRUCTOR_DATA(DOMParser, NS_DOMPARSER_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(XMLSerializer, NS_XMLSERIALIZER_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(XMLHttpRequest, NS_XMLHTTPREQUEST_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(XPathEvaluator, NS_XPATH_EVALUATOR_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(XSLTProcessor,
                              "@mozilla.org/document-transformer;1?type=xslt")
 };
 
+struct nsConstructorFuncMapData
+{
+  PRInt32 mDOMClassInfoID;
+  nsDOMConstructorFunc mConstructorFunc;
+};
+
+#define NS_DEFINE_CONSTRUCTOR_FUNC_DATA(_class, _func)                        \
+  { eDOMClassInfo_##_class##_id, _func },
+
+static const nsConstructorFuncMapData kConstructorFuncMap[] =
+{
+  NS_DEFINE_CONSTRUCTOR_FUNC_DATA(Worker, nsDOMWorker::NewWorker)
+};
+
 nsIXPConnect *nsDOMClassInfo::sXPConnect = nsnull;
 nsIScriptSecurityManager *nsDOMClassInfo::sSecMan = nsnull;
 PRBool nsDOMClassInfo::sIsInitialized = PR_FALSE;
 PRBool nsDOMClassInfo::sDisableDocumentAllSupport = PR_FALSE;
 PRBool nsDOMClassInfo::sDisableGlobalScopePollutionSupport = PR_FALSE;
 
 
 jsval nsDOMClassInfo::sTop_id             = JSVAL_VOID;
@@ -3547,16 +3567,22 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMElement)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSElement)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOM3Node)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNodeSelector)
   DOM_CLASSINFO_MAP_END
 #endif
 
+  DOM_CLASSINFO_MAP_BEGIN(Worker, nsIWorker)
+    DOM_CLASSINFO_MAP_ENTRY(nsIWorker)
+    DOM_CLASSINFO_MAP_ENTRY(nsIAbstractWorker)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+  DOM_CLASSINFO_MAP_END
+
 #ifdef NS_DEBUG
   {
     PRUint32 i = NS_ARRAY_LENGTH(sClassInfoData);
 
     if (i != eDOMClassInfoIDCount) {
       NS_ERROR("The number of items in sClassInfoData doesn't match the "
                "number of nsIDOMClassInfo ID's, this is bad! Fix it!");
 
@@ -4961,27 +4987,47 @@ FindConstructorContractID(PRInt32 aDOMCl
   for (i = 0; i < NS_ARRAY_LENGTH(kConstructorMap); ++i) {
     if (kConstructorMap[i].mDOMClassInfoID == aDOMClassInfoID) {
       return kConstructorMap[i].mContractID;
     }
   }
   return nsnull;
 }
 
+static nsDOMConstructorFunc
+FindConstructorFunc(PRInt32 aDOMClassInfoID)
+{
+  for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(kConstructorFuncMap); ++i) {
+    if (kConstructorFuncMap[i].mDOMClassInfoID == aDOMClassInfoID) {
+      return kConstructorFuncMap[i].mConstructorFunc;
+    }
+  }
+  return nsnull;
+}
+
 static nsresult
 BaseStubConstructor(nsIWeakReference* aWeakOwner,
                     const nsGlobalNameStruct *name_struct, JSContext *cx,
                     JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
   nsresult rv;
   nsCOMPtr<nsISupports> native;
   if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
     const char *contractid =
       FindConstructorContractID(name_struct->mDOMClassInfoID);
-    native = do_CreateInstance(contractid, &rv);
+    if (contractid) {
+      native = do_CreateInstance(contractid, &rv);
+    }
+    else {
+      nsDOMConstructorFunc func =
+        FindConstructorFunc(name_struct->mDOMClassInfoID);
+      if (func) {
+        rv = func(getter_AddRefs(native));
+      }
+    }
   } else if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
     native = do_CreateInstance(name_struct->mCID, &rv);
   } else if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias) {
     native = do_CreateInstance(name_struct->mAlias->mCID, &rv);
   } else {
     native = do_CreateInstance(*name_struct->mData->mConstructorCID, &rv);
   }
   if (NS_FAILED(rv)) {
@@ -5189,17 +5235,18 @@ private:
     // Return NS_OK here, aName just isn't a DOM class but nothing failed.
     return NS_OK;
   }
 
   static PRBool IsConstructable(const nsGlobalNameStruct *aNameStruct)
   {
     return
       (aNameStruct->mType == nsGlobalNameStruct::eTypeClassConstructor &&
-       FindConstructorContractID(aNameStruct->mDOMClassInfoID)) ||
+       (FindConstructorContractID(aNameStruct->mDOMClassInfoID) ||
+        FindConstructorFunc(aNameStruct->mDOMClassInfoID))) ||
       (aNameStruct->mType == nsGlobalNameStruct::eTypeExternalClassInfo &&
        aNameStruct->mData->mConstructorCID) ||
       aNameStruct->mType == nsGlobalNameStruct::eTypeExternalConstructor ||
       aNameStruct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias;
   }
 
   const PRUnichar*   mClassName;
   const PRPackedBool mConstructable;
--- a/dom/src/base/nsDOMClassInfo.h
+++ b/dom/src/base/nsDOMClassInfo.h
@@ -56,16 +56,17 @@ class nsIDOMDocument;
 class nsIHTMLDocument;
 class nsGlobalWindow;
 
 struct nsDOMClassInfoData;
 
 typedef nsIClassInfo* (*nsDOMClassInfoConstructorFnc)
   (nsDOMClassInfoData* aData);
 
+typedef nsresult (*nsDOMConstructorFunc)(nsISupports** aNewObject);
 
 struct nsDOMClassInfoData
 {
   const char *mName;
   union {
     nsDOMClassInfoConstructorFnc mConstructorFptr;
     nsDOMClassInfoExternalConstructorFnc mExternalConstructorFptr;
   } u;
--- a/dom/src/base/nsDOMScriptObjectFactory.cpp
+++ b/dom/src/base/nsDOMScriptObjectFactory.cpp
@@ -60,16 +60,17 @@
 #include "nsGlobalWindow.h"
 #include "nsIJSContextStack.h"
 #include "nsISupportsPrimitives.h"
 #include "nsDOMException.h"
 #include "nsCRT.h"
 #ifdef MOZ_XUL
 #include "nsXULPrototypeCache.h"
 #endif
+#include "nsThreadUtils.h"
 
 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 nsIExceptionProvider* gExceptionProvider = nsnull;
 
 nsDOMScriptObjectFactory::nsDOMScriptObjectFactory() :
   mLoadedAllLanguages(PR_FALSE)
 {
@@ -390,16 +391,20 @@ nsresult NS_GetScriptRuntimeByID(PRUint3
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMExceptionProvider, nsIExceptionProvider)
 
 NS_IMETHODIMP
 nsDOMExceptionProvider::GetException(nsresult result,
                                      nsIException *aDefaultException,
                                      nsIException **_retval)
 {
+  if (!NS_IsMainThread()) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
   switch (NS_ERROR_GET_MODULE(result))
   {
     case NS_ERROR_MODULE_DOM_RANGE:
       return NS_NewRangeException(result, aDefaultException, _retval);
 #ifdef MOZ_SVG
     case NS_ERROR_MODULE_SVG:
       return NS_NewSVGException(result, aDefaultException, _retval);
 #endif
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -108,17 +108,16 @@
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMMessageEvent.h"
 #include "nsIDOMPopupBlockedEvent.h"
 #include "nsIDOMPkcs11.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsIDOMGeoGeolocation.h"
-#include "nsIDOMThreads.h"
 #include "nsDOMString.h"
 #include "nsIEmbeddingSiteWindow2.h"
 #include "nsThreadUtils.h"
 #include "nsIEventStateManager.h"
 #include "nsIHttpProtocolHandler.h"
 #include "nsIJSContextStack.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIMarkupDocumentViewer.h"
@@ -221,16 +220,20 @@ static PRBool               gDragService
 #ifdef DEBUG
 static PRUint32             gSerialCounter             = 0;
 #endif
 
 #ifdef DEBUG_jst
 PRInt32 gTimeoutCnt                                    = 0;
 #endif
 
+#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
+static PRBool               gDOMWindowDumpEnabled      = PR_FALSE;
+#endif
+
 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
 #define DEBUG_PAGE_CACHE
 #endif
 
 // The shortest interval/timeout we permit
 #define DOM_MIN_TIMEOUT_VALUE 10 // 10ms
 
 // The longest interval (as PRIntervalTime) we permit, or that our
@@ -650,19 +653,31 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
     Freeze();
 
     mObserver = nsnull;
   }
 
   // We could have failed the first time through trying
   // to create the entropy collector, so we should
   // try to get one until we succeed.
-  if (gRefCnt++ == 0 || !gEntropyCollector) {
+
+  gRefCnt++;
+
+#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
+  if (gRefCnt == 0) {
+    static const char* prefName = "browser.dom.window.dump.enabled";
+    nsContentUtils::AddBoolPrefVarCache(prefName, &gDOMWindowDumpEnabled);
+    gDOMWindowDumpEnabled = nsContentUtils::GetBoolPref(prefName);
+  }
+#endif
+
+  if (!gEntropyCollector) {
     CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector);
   }
+
 #ifdef DEBUG
   printf("++DOMWINDOW == %d (%p) [serial = %d] [outer = %p]\n", gRefCnt,
          static_cast<void*>(static_cast<nsIScriptGlobalObject*>(this)),
          ++gSerialCounter, static_cast<void*>(aOuterWindow));
   mSerial = gSerialCounter;
 #endif
 
 #ifdef PR_LOGGING
@@ -1662,16 +1677,21 @@ nsGlobalWindow::SetNewDocument(nsIDocume
     nsRefPtr<nsGlobalWindow> newInnerWindow;
 
     nsCOMPtr<nsIDOMChromeWindow> thisChrome =
       do_QueryInterface(static_cast<nsIDOMWindow *>(this));
     nsCOMPtr<nsIXPConnectJSObjectHolder> navigatorHolder;
 
     PRBool isChrome = PR_FALSE;
 
+    nsCxPusher cxPusher;
+    if (!cxPusher.Push(cx)) {
+      return NS_ERROR_FAILURE;
+    }
+
     JSAutoRequest ar(cx);
 
     // Make sure to clear scope on the outer window *before* we
     // initialize the new inner window. If we don't, things
     // (Object.prototype etc) could leak from the old outer to the new
     // inner scope.
     NS_STID_FOR_ID(st_id) {
       nsIScriptContext *langContext = GetScriptContextInternal(st_id);
@@ -3823,34 +3843,35 @@ nsGlobalWindow::GetFullScreen(PRBool* aF
     }
   }
 
   // We are the root window, or something went wrong. Return our internal value.
   *aFullScreen = mFullScreen;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsGlobalWindow::Dump(const nsAString& aStr)
+PRBool
+nsGlobalWindow::DOMWindowDumpEnabled()
 {
 #if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
-  {
-    // In optimized builds we check a pref that controls if we should
-    // enable output from dump() or not, in debug builds it's always
-    // enabled.
-
-    // if pref doesn't exist, disable dump output.
-    PRBool enable_dump =
-      nsContentUtils::GetBoolPref("browser.dom.window.dump.enabled");
-
-    if (!enable_dump) {
-      return NS_OK;
-    }
-  }
+  // In optimized builds we check a pref that controls if we should
+  // enable output from dump() or not, in debug builds it's always
+  // enabled.
+  return gDOMWindowDumpEnabled;
+#else
+  return PR_TRUE;
 #endif
+}
+
+NS_IMETHODIMP
+nsGlobalWindow::Dump(const nsAString& aStr)
+{
+  if (!DOMWindowDumpEnabled()) {
+    return NS_OK;
+  }
 
   char *cstr = ToNewUTF8String(aStr);
 
 #if defined(XP_MAC) || defined(XP_MACOSX)
   // have to convert \r to \n so that printing to the console works
   char *c = cstr, *cEnd = cstr + aStr.Length();
   while (c < cEnd) {
     if (*c == '\r')
@@ -9438,23 +9459,8 @@ NS_IMETHODIMP nsNavigator::GetGeolocatio
   if (!mGeolocation) {
     nsCOMPtr<nsIDOMWindow> contentDOMWindow(do_GetInterface(mDocShell));
     mGeolocation = new nsGeolocation(contentDOMWindow);
   }
 
   NS_IF_ADDREF(*_retval = mGeolocation);
   return NS_OK;
 }
-
-NS_IMETHODIMP
-nsNavigator::NewWorkerPool(nsIDOMWorkerPool** _retval)
-{
-  nsCOMPtr<nsIDOMThreadService> threadService =
-    nsDOMThreadService::GetOrInitService();
-  NS_ENSURE_TRUE(threadService, NS_ERROR_OUT_OF_MEMORY);
-
-  nsCOMPtr<nsIDOMWorkerPool> newPool;
-  nsresult rv = threadService->CreatePool(getter_AddRefs(newPool));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  newPool.forget(_retval);
-  return NS_OK;
-}
--- a/dom/src/base/nsGlobalWindow.h
+++ b/dom/src/base/nsGlobalWindow.h
@@ -429,16 +429,17 @@ public:
 
   virtual NS_HIDDEN_(void*)
     GetCachedXBLPrototypeHandler(nsXBLPrototypeHandler* aKey);
 
   virtual NS_HIDDEN_(void)
     CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey,
                              nsScriptObjectHolder& aHandler);
 
+  static PRBool DOMWindowDumpEnabled();
 
 protected:
   // Object Management
   virtual ~nsGlobalWindow();
   void CleanUp();
   void ClearControllers();
 
   void FreeInnerObjects(PRBool aClearScope);
--- a/dom/src/base/nsJSEnvironment.cpp
+++ b/dom/src/base/nsJSEnvironment.cpp
@@ -2368,16 +2368,21 @@ nsJSContext::InitContext(nsIScriptGlobal
   ::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter);
 
   if (!aGlobalObject) {
     // If we don't get a global object then there's nothing more to do here.
 
     return NS_OK;
   }
 
+  nsCxPusher cxPusher;
+  if (!cxPusher.Push(mContext)) {
+    return NS_ERROR_FAILURE;
+  }
+
   nsIXPConnect *xpc = nsContentUtils::XPConnect();
 
   JSObject *global = ::JS_GetGlobalObject(mContext);
 
   nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
 
   // If there's already a global object in mContext we won't tell
   // XPConnect to wrap aGlobalObject since it's already wrapped.
--- a/dom/src/threads/Makefile.in
+++ b/dom/src/threads/Makefile.in
@@ -46,46 +46,47 @@ include $(DEPTH)/config/autoconf.mk
 MODULE           = dom
 LIBRARY_NAME     = domthreads_s
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 
 REQUIRES = \
   caps \
   content \
+  docshell \
   gfx \
   js \
   layout \
   locale \
   necko \
+  plugin \
   pref \
   string \
   thebes \
   widget \
   xpcom \
   xpconnect \
   $(NULL)
 
 CPPSRCS = \
   nsDOMThreadService.cpp \
-  nsDOMWorkerBase.cpp \
+  nsDOMWorker.cpp \
+  nsDOMWorkerEvents.cpp \
+  nsDOMWorkerMessageHandler.cpp \
   nsDOMWorkerPool.cpp \
   nsDOMWorkerScriptLoader.cpp \
   nsDOMWorkerSecurityManager.cpp \
-  nsDOMWorkerThread.cpp \
   nsDOMWorkerTimeout.cpp \
   nsDOMWorkerXHR.cpp \
   nsDOMWorkerXHRProxy.cpp \
   $(NULL)
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/dom/src/base \
   -I$(topsrcdir)/content/base/src \
   -I$(topsrcdir)/content/events/src \
   $(NULL)
 
 ifdef ENABLE_TESTS
 DIRS += test
 endif
 
 include $(topsrcdir)/config/rules.mk
-
-#CXXFLAGS += $(WARNINGS_AS_ERRORS)
--- a/dom/src/threads/nsAutoJSObjectHolder.h
+++ b/dom/src/threads/nsAutoJSObjectHolder.h
@@ -142,17 +142,21 @@ public:
   operator JSObject*() const {
     return get();
   }
 
   /**
    * Pretend to be a JSObject*. Assert if not held.
    */
   JSObject* operator=(JSObject* aOther) {
-    NS_ASSERTION(mHeld, "Not rooted!");
+#ifdef DEBUG
+    if (aOther) {
+      NS_ASSERTION(mHeld, "Not rooted!");
+    }
+#endif
     return mObj = aOther;
   }
 
 private:
   JSRuntime* mRt;
   JSObject* mObj;
   JSBool mHeld;
 };
--- a/dom/src/threads/nsDOMThreadService.cpp
+++ b/dom/src/threads/nsDOMThreadService.cpp
@@ -36,46 +36,50 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsDOMThreadService.h"
 
 // Interfaces
 #include "nsIComponentManager.h"
-#include "nsIConsoleService.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIEventTarget.h"
 #include "nsIGenericFactory.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 "nsAutoLock.h"
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h"
 #include "nsDeque.h"
 #include "nsIClassInfoImpl.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOM.h"
 #include "nsXPCOMCID.h"
 #include "nsXPCOMCIDInternal.h"
 #include "pratom.h"
 #include "prthread.h"
 
 // DOMWorker includes
+#include "nsDOMWorker.h"
+#include "nsDOMWorkerEvents.h"
+#include "nsDOMWorkerMacros.h"
+#include "nsDOMWorkerMessageHandler.h"
 #include "nsDOMWorkerPool.h"
 #include "nsDOMWorkerSecurityManager.h"
 #include "nsDOMWorkerTimeout.h"
 
 #ifdef PR_LOGGING
 PRLogModuleInfo *gDOMThreadsLog = nsnull;
 #endif
 #define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
@@ -100,24 +104,16 @@ PR_STATIC_ASSERT(THREADPOOL_THREAD_CAP >
 
 // The number of times our JS operation callback will be called before yielding
 // the thread
 #define CALLBACK_YIELD_THRESHOLD 100
 
 // A "bad" value for the NSPR TLS functions.
 #define BAD_TLS_INDEX (PRUintn)-1
 
-// 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(); } 
-
 // 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;
@@ -150,113 +146,41 @@ public:
     return cx;
   }
 
 private:
   JSContext* mCx;
 };
 
 /**
- * This class is used as to post an error to the main thread. It logs the error
- * to the console and calls the pool's onError callback.
+ * This class is used as to post an error to the worker's outer handler.
  */
-class nsReportErrorRunnable : public nsRunnable
+class nsReportErrorRunnable : public nsIRunnable
 {
 public:
-  nsReportErrorRunnable(nsIScriptError* aError, nsDOMWorkerThread* aWorker)
-  : mError(aError), mWorker(aWorker) { }
+  NS_DECL_ISUPPORTS
+
+  nsReportErrorRunnable(nsDOMWorker* aWorker, nsIWorkerMessageEvent* aEvent)
+  : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()), mEvent(aEvent) { }
 
   NS_IMETHOD Run() {
-    nsresult rv;
-
-    nsCOMPtr<nsIConsoleService> consoleService =
-      do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
-    if (NS_SUCCEEDED(rv)) {
-      consoleService->LogMessage(mError);
+    if (mWorker->IsCanceled()) {
+      return NS_OK;
     }
 
-    if (!mWorker->IsCanceled()) {
-#ifdef PR_LOGGING
-      nsAutoString message;
-      mError->GetErrorMessage(message);
-#endif
-      nsRefPtr<nsDOMWorkerPool> pool = mWorker->Pool();
-
-      LOG(("Posting error '%s' to pool [0x%p]",
-           NS_LossyConvertUTF16toASCII(message).get(),
-           static_cast<void*>(pool.get())));
-
-      pool->HandleError(mError, mWorker);
-    }
-    return NS_OK;
+    return mWorker->DispatchEvent(mEvent, nsnull);
   }
 
 private:
-  // XXX Maybe this should be an nsIException...
-  nsCOMPtr<nsIScriptError> mError;
-
-  // Have to carry a strong ref since this is used as a parameter to the
-  // onError callback.
-  nsRefPtr<nsDOMWorkerThread> mWorker;
+  nsRefPtr<nsDOMWorker> mWorker;
+  nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
+  nsCOMPtr<nsIWorkerMessageEvent> mEvent;
 };
 
-/**
- * Need this to expose an nsIScriptError to content JS (implement nsIClassInfo
- * with DOM_OBJECT flag set.
- */
-class nsDOMWorkerScriptError : public nsIClassInfo
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSICLASSINFO
-
-  nsDOMWorkerScriptError(nsIScriptError* aError)
-  : mScriptError(this, aError) { }
-
-protected:
-
-  // Lame, nsIScriptError and nsIClassInfo both have 'readonly attribute
-  // unsigned long flags' so we have to use an inner class to do this the
-  // right way...
-  class InnerScriptError : public nsIScriptError
-  {
-  public:
-    NS_FORWARD_NSISUPPORTS(mParent->)
-    NS_FORWARD_NSISCRIPTERROR(mError->)
-    NS_FORWARD_NSICONSOLEMESSAGE(mError->)
-
-    InnerScriptError(nsDOMWorkerScriptError* aParent, nsIScriptError* aError)
-    : mParent(aParent), mError(aError) { }
-
-  protected:
-    nsDOMWorkerScriptError* mParent;
-    nsCOMPtr<nsIScriptError> mError;
-  };
-
-  InnerScriptError mScriptError;
-};
-
-NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerScriptError)
-NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerScriptError)
-
-// More hoops to jump through for the identical IDL methods
-NS_INTERFACE_MAP_BEGIN(nsDOMWorkerScriptError)
-  if (aIID.Equals(NS_GET_IID(nsIScriptError)) ||
-      aIID.Equals(NS_GET_IID(nsIConsoleMessage))) {
-    foundInterface = static_cast<nsIConsoleMessage*>(&mScriptError);
-  }
-  else
-  NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerScriptError, nsIScriptError,
-                                                     nsIConsoleMessage)
-
-NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerScriptError)
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsReportErrorRunnable, nsIRunnable)
 
 /**
  * Used to post an expired timeout to the correct worker.
  */
 class nsDOMWorkerTimeoutRunnable : public nsRunnable
 {
 public:
   nsDOMWorkerTimeoutRunnable(nsDOMWorkerTimeout* aTimeout)
@@ -278,68 +202,64 @@ protected:
  * currently so we cheat by using a runnable that emulates a thread. The
  * nsDOMThreadService's monitor protects the queue of events.
  */
 class nsDOMWorkerRunnable : public nsRunnable
 {
   friend class nsDOMThreadService;
 
 public:
-  nsDOMWorkerRunnable(nsDOMWorkerThread* aWorker)
+  nsDOMWorkerRunnable(nsDOMWorker* aWorker)
   : mWorker(aWorker) { }
 
   virtual ~nsDOMWorkerRunnable() {
     nsCOMPtr<nsIRunnable> runnable;
     while ((runnable = dont_AddRef((nsIRunnable*)mRunnables.PopFront()))) {
       // Loop until all the runnables are dead.
     }
-
-    // Only release mWorker on the main thread!
-    nsDOMWorkerThread* worker = nsnull;
-    mWorker.swap(worker);
-
-    nsISupports* supports = NS_ISUPPORTS_CAST(nsIDOMWorkerThread*, worker);
-    NS_ASSERTION(supports, "This should never be null!");
-
-    nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
-    NS_ProxyRelease(mainThread, supports);
   }
 
   void PutRunnable(nsIRunnable* aRunnable) {
     NS_ASSERTION(aRunnable, "Null pointer!");
 
     NS_ADDREF(aRunnable);
 
     // No need to enter the monitor because we should already be in it.
 
     mRunnables.Push(aRunnable);
   }
 
   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);
     NS_ASSERTION(cx, "nsDOMThreadService didn't give us a context!");
 
+    NS_ASSERTION(!JS_GetGlobalObject(cx), "Shouldn't have a global!");
+
     JS_SetContextPrivate(cx, mWorker);
 
     // Tell the worker which context it will be using
     if (mWorker->SetGlobalForContext(cx)) {
       RunQueue();
 
       // Remove the global object from the context so that it might be garbage
       // collected.
       JS_SetGlobalObject(cx, NULL);
       JS_SetContextPrivate(cx, NULL);
     }
     else {
       // This is usually due to a parse error in the worker script...
       JS_SetGlobalObject(cx, NULL);
+      JS_SetContextPrivate(cx, NULL);
 
       nsAutoMonitor mon(gDOMThreadService->mMonitor);
       gDOMThreadService->WorkerComplete(this);
       mon.NotifyAll();
     }
 
     return NS_OK;
   }
@@ -377,30 +297,30 @@ protected:
       nsresult rv =
 #endif
       runnable->Run();
       NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Runnable failed!");
     }
   }
 
   // Set at construction
-  nsRefPtr<nsDOMWorkerThread> mWorker;
+  nsRefPtr<nsDOMWorker> mWorker;
 
   // Protected by mMonitor
   nsDeque mRunnables;
 };
 
 /*******************************************************************************
  * JS environment function and callbacks
  */
 
 JSBool
 DOMWorkerOperationCallback(JSContext* aCx)
 {
-  nsDOMWorkerThread* worker = (nsDOMWorkerThread*)JS_GetContextPrivate(aCx);
+  nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
 
   // Want a strong ref here to make sure that the monitor we wait on won't go
   // away.
   nsRefPtr<nsDOMWorkerPool> pool;
 
   PRBool wasSuspended = PR_FALSE;
   PRBool extraThreadAllowed = PR_FALSE;
   jsrefcount suspendDepth = 0;
@@ -477,17 +397,17 @@ DOMWorkerOperationCallback(JSContext* aC
 
 void
 DOMWorkerErrorReporter(JSContext* aCx,
                        const char* aMessage,
                        JSErrorReport* aReport)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Huh?!");
 
-  nsDOMWorkerThread* worker = (nsDOMWorkerThread*)JS_GetContextPrivate(aCx);
+  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;
   }
 
@@ -506,31 +426,40 @@ DOMWorkerErrorReporter(JSContext* aCx,
     reinterpret_cast<const PRUnichar*>(aReport->uclinebuf);
 
   PRUint32 column = aReport->uctokenptr - aReport->uclinebuf;
 
   rv = errorObject->Init(message, filename.get(), line, aReport->lineno,
                         column, aReport->flags, "DOM Worker javascript");
   NS_ENSURE_SUCCESS(rv,);
 
-  nsRefPtr<nsDOMWorkerScriptError> domError =
-    new nsDOMWorkerScriptError(errorObject);
-  NS_ENSURE_TRUE(domError,);
+  nsCString finalMessage;
+  rv = errorObject->ToString(finalMessage);
+  NS_ENSURE_SUCCESS(rv,);
+
+  nsRefPtr<nsDOMWorkerMessageEvent> event(new nsDOMWorkerMessageEvent());
+  NS_ENSURE_TRUE(event,);
 
-  nsCOMPtr<nsIScriptError> scriptError(do_QueryInterface(domError));
-  NS_ENSURE_TRUE(scriptError,);
+  rv = event->InitMessageEvent(NS_LITERAL_STRING("error"), PR_FALSE, PR_FALSE,
+                               NS_ConvertUTF8toUTF16(finalMessage),
+                               EmptyString(), nsnull);
+  NS_ENSURE_SUCCESS(rv,);
 
-  nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
-  NS_ENSURE_TRUE(mainThread,);
+  event->SetTarget(worker);
 
-  nsCOMPtr<nsIRunnable> runnable =
-    new nsReportErrorRunnable(scriptError, worker);
+  nsCOMPtr<nsIRunnable> runnable(new nsReportErrorRunnable(worker, event));
   NS_ENSURE_TRUE(runnable,);
 
-  rv = mainThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  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);
   NS_ENSURE_SUCCESS(rv,);
 }
 
 /*******************************************************************************
  * nsDOMThreadService
  */
 
 nsDOMThreadService::nsDOMThreadService()
@@ -553,20 +482,19 @@ nsDOMThreadService::~nsDOMThreadService(
 
   Cleanup();
 
   if (mMonitor) {
     nsAutoMonitor::DestroyMonitor(mMonitor);
   }
 }
 
-NS_IMPL_THREADSAFE_ISUPPORTS4(nsDOMThreadService, nsIEventTarget,
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMThreadService, nsIEventTarget,
                                                   nsIObserver,
-                                                  nsIThreadPoolListener,
-                                                  nsIDOMThreadService)
+                                                  nsIThreadPoolListener)
 
 nsresult
 nsDOMThreadService::Init()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!gDOMThreadService, "Only one instance should ever be created!");
 
   nsresult rv;
@@ -592,16 +520,19 @@ nsDOMThreadService::Init()
   NS_ENSURE_SUCCESS(rv, rv);
 
   mMonitor = nsAutoMonitor::NewMonitor("nsDOMThreadService::mMonitor");
   NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
 
   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);
+
   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);
@@ -617,41 +548,59 @@ nsDOMThreadService::Init()
     gJSContextIndex = BAD_TLS_INDEX;
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 /* static */
-already_AddRefed<nsIDOMThreadService>
+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);
   }
 
-  nsCOMPtr<nsIDOMThreadService> service(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
@@ -682,20 +631,25 @@ nsDOMThreadService::Cleanup()
       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);
+
+  nsAutoMonitor mon(mMonitor);
+  NS_ASSERTION(!mPools.Count(), "Live workers left!");
+
+  mPools.Clear();
 }
 
 nsresult
-nsDOMThreadService::Dispatch(nsDOMWorkerThread* aWorker,
+nsDOMThreadService::Dispatch(nsDOMWorker* aWorker,
                              nsIRunnable* aRunnable)
 {
   NS_ASSERTION(aWorker, "Null pointer!");
   NS_ASSERTION(aRunnable, "Null pointer!");
 
   NS_ASSERTION(mThreadPool, "Dispatch called after 'xpcom-shutdown'!");
 
   if (aWorker->IsCanceled()) {
@@ -750,40 +704,27 @@ nsDOMThreadService::Dispatch(nsDOMWorker
 
 void
 nsDOMThreadService::WorkerComplete(nsDOMWorkerRunnable* aRunnable)
 {
 
   // No need to be in the monitor here because we should already be in it.
 
 #ifdef DEBUG
-  nsRefPtr<nsDOMWorkerThread>& debugWorker = aRunnable->mWorker;
+  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);
 }
 
-void
-nsDOMThreadService::WaitForCanceledWorker(nsDOMWorkerThread* aWorker)
-{
-  NS_ASSERTION(aWorker->IsCanceled(),
-               "Waiting on a worker that isn't canceled!");
-
-  nsAutoMonitor mon(mMonitor);
-
-  while (mWorkersInProgress.Get(aWorker, nsnull)) {
-    mon.Wait();
-  }
-}
-
 /* static */
 JSContext*
 nsDOMThreadService::CreateJSContext()
 {
   JSRuntime* rt;
   gJSRuntimeService->GetRuntime(&rt);
   NS_ENSURE_TRUE(rt, nsnull);
 
@@ -805,52 +746,74 @@ nsDOMThreadService::CreateJSContext()
 
   nsresult rv = nsContentUtils::XPConnect()->
     SetSecurityManagerForJSContext(cx, gWorkerSecurityManager, 0);
   NS_ENSURE_SUCCESS(rv, nsnull);
 
   return cx.forget();
 }
 
-#define LOOP_OVER_POOLS(_func, _args)                     \
-  PR_BEGIN_MACRO                                          \
-    PRUint32 poolCount = mPools.Length();                 \
-    for (PRUint32 i = 0; i < poolCount; i++) {            \
-      mPools[i]-> _func _args ;                           \
-    }                                                     \
-  PR_END_MACRO
+already_AddRefed<nsDOMWorkerPool>
+nsDOMThreadService::GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
+                                     PRBool aRemove)
+{
+  NS_ASSERTION(aGlobalObject, "Null pointer!");
+
+  nsAutoMonitor mon(mMonitor);
+
+  nsRefPtr<nsDOMWorkerPool> pool;
+  mPools.Get(aGlobalObject, getter_AddRefs(pool));
+
+  if (aRemove) {
+    mPools.Remove(aGlobalObject);
+  }
+
+  return pool.forget();
+}
 
 void
 nsDOMThreadService::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  LOOP_OVER_POOLS(CancelWorkersForGlobal, (aGlobalObject));
+  NS_ASSERTION(aGlobalObject, "Null pointer!");
+
+  nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_TRUE);
+  if (pool) {
+    pool->Cancel();
+  }
 }
 
 void
 nsDOMThreadService::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  LOOP_OVER_POOLS(SuspendWorkersForGlobal, (aGlobalObject));
+  NS_ASSERTION(aGlobalObject, "Null pointer!");
+
+  nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
+  if (pool) {
+    pool->Suspend();
+  }
 }
 
 void
 nsDOMThreadService::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  LOOP_OVER_POOLS(ResumeWorkersForGlobal, (aGlobalObject));
+  NS_ASSERTION(aGlobalObject, "Null pointer!");
+
+  nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
+  if (pool) {
+    pool->Resume();
+  }
 }
 
 void
-nsDOMThreadService::NoteDyingPool(nsDOMWorkerPool* aPool)
+nsDOMThreadService::NoteEmptyPool(nsDOMWorkerPool* aPool)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aPool, "Null pointer!");
 
-  NS_ASSERTION(mPools.Contains(aPool), "aPool should be in the array!");
-  mPools.RemoveElement(aPool);
+  nsAutoMonitor mon(mMonitor);
+  mPools.Remove(aPool->ScriptGlobalObject());
 }
 
 void
 nsDOMThreadService::TimeoutReady(nsDOMWorkerTimeout* aTimeout)
 {
   nsRefPtr<nsDOMWorkerTimeoutRunnable> runnable =
     new nsDOMWorkerTimeoutRunnable(aTimeout);
   NS_ENSURE_TRUE(runnable,);
@@ -858,16 +821,18 @@ nsDOMThreadService::TimeoutReady(nsDOMWo
   Dispatch(aTimeout->GetWorker(), runnable);
 }
 
 nsresult
 nsDOMThreadService::ChangeThreadPoolMaxThreads(PRInt16 aDelta)
 {
   NS_ENSURE_ARG(aDelta == 1 || aDelta == -1);
 
+  nsAutoMonitor mon(mMonitor);
+
   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!");
 
@@ -907,17 +872,16 @@ 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
  */
@@ -1006,36 +970,74 @@ nsDOMThreadService::OnThreadShuttingDown
     gThreadJSContextStack->SetSafeJSContext(nsnull);
 
     nsContentUtils::XPConnect()->ReleaseJSContext(cx, PR_TRUE);
   }
 
   return NS_OK;
 }
 
-/**
- * See nsIDOMThreadService
- */
-NS_IMETHODIMP
-nsDOMThreadService::CreatePool(nsIDOMWorkerPool** _retval)
+nsresult
+nsDOMThreadService::RegisterWorker(nsDOMWorker* aWorker,
+                                   nsIScriptGlobalObject* aGlobalObject)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aWorker, "Null pointer!");
+  NS_ASSERTION(aGlobalObject, "Null pointer!");
+
+  if (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);
 
-  NS_ENSURE_TRUE(mThreadPool, NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
+    nsCOMPtr<nsIScriptGlobalObject> newGlobal(do_QueryInterface(innerWindow));
+    NS_ENSURE_TRUE(newGlobal, NS_ERROR_NO_INTERFACE);
+
+    aGlobalObject = newGlobal;
+  }
+
+  nsRefPtr<nsDOMWorkerPool> pool;
+  {
+    nsAutoMonitor mon(mMonitor);
+
+    if (!mThreadPool) {
+      // Shutting down!
+      return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
+    }
 
-  nsIDOMDocument* domDocument = nsContentUtils::GetDocumentFromCaller();
-  NS_ENSURE_TRUE(domDocument, NS_ERROR_UNEXPECTED);
+    mPools.Get(aGlobalObject, getter_AddRefs(pool));
+  }
+
+  nsresult rv;
+
+  if (!pool) {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+    nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(aGlobalObject));
+    NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
+
+    nsIDOMDocument* domDocument = domWindow->GetExtantDocument();
+    NS_ENSURE_STATE(domDocument);
 
-  nsCOMPtr<nsIDocument> callingDocument(do_QueryInterface(domDocument));
-  NS_ENSURE_TRUE(callingDocument, NS_ERROR_NO_INTERFACE);
+    nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
+    NS_ENSURE_STATE(document);
+
+    pool = new nsDOMWorkerPool(aGlobalObject, document);
+    NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
 
-  nsRefPtr<nsDOMWorkerPool> pool(new nsDOMWorkerPool(callingDocument));
-  NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
+    rv = pool->Init();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoMonitor mon(mMonitor);
 
-  nsresult rv = pool->Init();
+    PRBool success = mPools.Put(aGlobalObject, pool);
+    NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  rv = pool->NoteWorker(aWorker);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  NS_ASSERTION(!mPools.Contains(pool), "Um?!");
-  mPools.AppendElement(pool);
-
-  NS_ADDREF(*_retval = pool);
+  aWorker->SetPool(pool);
   return NS_OK;
 }
--- a/dom/src/threads/nsDOMThreadService.h
+++ b/dom/src/threads/nsDOMThreadService.h
@@ -39,68 +39,72 @@
 
 #ifndef __NSDOMTHREADSERVICE_H__
 #define __NSDOMTHREADSERVICE_H__
 
 // Interfaces
 #include "nsIEventTarget.h"
 #include "nsIObserver.h"
 #include "nsIThreadPool.h"
-#include "nsIDOMThreads.h"
 
 // Other includes
 #include "jsapi.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsRefPtrHashtable.h"
 #include "nsTPtrArray.h"
 #include "prmon.h"
 
 #include "prlog.h"
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gDOMThreadsLog;
 #endif
 
+class nsDOMWorker;
 class nsDOMWorkerPool;
 class nsDOMWorkerRunnable;
-class nsDOMWorkerThread;
 class nsDOMWorkerTimeout;
 class nsIJSRuntimeService;
 class nsIScriptGlobalObject;
 class nsIThreadJSContextStack;
 class nsIXPConnect;
 class nsIXPCSecurityManager;
 
 class nsDOMThreadService : public nsIEventTarget,
                            public nsIObserver,
-                           public nsIThreadPoolListener,
-                           public nsIDOMThreadService
+                           public nsIThreadPoolListener
 {
+  friend class nsDOMWorker;
   friend class nsDOMWorkerPool;
   friend class nsDOMWorkerRunnable;
   friend class nsDOMWorkerThread;
   friend class nsDOMWorkerTimeout;
   friend class nsDOMWorkerXHR;
   friend class nsDOMWorkerXHRProxy;
   friend class nsLayoutStatics;
 
+  friend void DOMWorkerErrorReporter(JSContext* aCx,
+                                     const char* aMessage,
+                                     JSErrorReport* aReport);
+
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIEVENTTARGET
   NS_DECL_NSIOBSERVER
   NS_DECL_NSITHREADPOOLLISTENER
-  NS_DECL_NSIDOMTHREADSERVICE
 
   // Any DOM consumers that need access to this service should use this method.
-  static already_AddRefed<nsIDOMThreadService> GetOrInitService();
+  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);
@@ -111,34 +115,39 @@ private:
   nsDOMThreadService();
   ~nsDOMThreadService();
 
   nsresult Init();
   void Cleanup();
 
   static void Shutdown();
 
-  nsresult Dispatch(nsDOMWorkerThread* aWorker,
+  nsresult Dispatch(nsDOMWorker* aWorker,
                     nsIRunnable* aRunnable);
 
   void WorkerComplete(nsDOMWorkerRunnable* aRunnable);
 
-  void WaitForCanceledWorker(nsDOMWorkerThread* aWorker);
-
   static JSContext* CreateJSContext();
 
-  void NoteDyingPool(nsDOMWorkerPool* aPool);
+  already_AddRefed<nsDOMWorkerPool>
+    GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
+                     PRBool aRemove);
+
+  void NoteEmptyPool(nsDOMWorkerPool* aPool);
 
   void TimeoutReady(nsDOMWorkerTimeout* aTimeout);
 
+  nsresult RegisterWorker(nsDOMWorker* aWorker,
+                          nsIScriptGlobalObject* aGlobalObject);
+
   // Our internal thread pool.
   nsCOMPtr<nsIThreadPool> mThreadPool;
 
-  // Weak references, only ever touched on the main thread!
-  nsTPtrArray<nsDOMWorkerPool> mPools;
+  // Maps nsIScriptGlobalObject* to nsDOMWorkerPool.
+  nsRefPtrHashtable<nsISupportsHashKey, nsDOMWorkerPool> mPools;
 
   // mMonitor protects all access to mWorkersInProgress and
   // mCreationsInProgress.
   PRMonitor* mMonitor;
 
   // A map from nsDOMWorkerThread to nsDOMWorkerRunnable.
   nsRefPtrHashtable<nsVoidPtrHashKey, nsDOMWorkerRunnable> mWorkersInProgress;
 };
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorker.cpp
@@ -0,0 +1,1331 @@
+/* -*- 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 "nsDOMWorker.h"
+
+#include "nsIDOMEvent.h"
+#include "nsIEventTarget.h"
+#include "nsIJSRuntimeService.h"
+#include "nsIXPConnect.h"
+
+#ifdef MOZ_SHARK
+#include "jsdbgapi.h"
+#endif
+#include "nsAutoLock.h"
+#include "nsContentUtils.h"
+#include "nsDOMClassInfoID.h"
+#include "nsGlobalWindow.h"
+#include "nsJSUtils.h"
+#include "nsThreadUtils.h"
+
+#include "nsDOMThreadService.h"
+#include "nsDOMWorkerEvents.h"
+#include "nsDOMWorkerPool.h"
+#include "nsDOMWorkerScriptLoader.h"
+#include "nsDOMWorkerTimeout.h"
+#include "nsDOMWorkerXHR.h"
+
+class nsDOMWorkerFunctions
+{
+public:
+  // Same as window.dump().
+  static JSBool Dump(JSContext* aCx, JSObject* aObj, uintN aArgc,
+                     jsval* aArgv, jsval* aRval);
+
+  // Same as window.setTimeout().
+  static JSBool SetTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
+                           jsval* aArgv, jsval* aRval) {
+    return MakeTimeout(aCx, aObj, aArgc, aArgv, aRval, PR_FALSE);
+  }
+
+  // Same as window.setInterval().
+  static JSBool SetInterval(JSContext* aCx, JSObject* aObj, uintN aArgc,
+                            jsval* aArgv, jsval* aRval) {
+    return MakeTimeout(aCx, aObj, aArgc, aArgv, aRval, PR_TRUE);
+  }
+
+  // Used for both clearTimeout() and clearInterval().
+  static JSBool KillTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
+                            jsval* aArgv, jsval* aRval);
+
+  static JSBool LoadScripts(JSContext* aCx, JSObject* aObj, uintN aArgc,
+                            jsval* aArgv, jsval* aRval);
+
+  static JSBool NewXMLHttpRequest(JSContext* aCx, JSObject* aObj, uintN aArgc,
+                                  jsval* aArgv, jsval* aRval);
+
+  static JSBool NewWorker(JSContext* aCx, JSObject* aObj, uintN aArgc,
+                          jsval* aArgv, jsval* aRval);
+
+private:
+  // Internal helper for SetTimeout and SetInterval.
+  static JSBool MakeTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
+                            jsval* aArgv, jsval* aRval, PRBool aIsInterval);
+};
+
+JSBool
+nsDOMWorkerFunctions::Dump(JSContext* aCx,
+                           JSObject* /* aObj */,
+                           uintN aArgc,
+                           jsval* aArgv,
+                           jsval* /* aRval */)
+{
+  if (!nsGlobalWindow::DOMWindowDumpEnabled()) {
+    return JS_TRUE;
+  }
+
+  JSString* str;
+  if (aArgc && (str = JS_ValueToString(aCx, aArgv[0])) && str) {
+    nsDependentJSString string(str);
+    fputs(NS_ConvertUTF16toUTF8(nsDependentJSString(str)).get(), stderr);
+    fflush(stderr);
+  }
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::MakeTimeout(JSContext* aCx,
+                                  JSObject* /* aObj */,
+                                  uintN aArgc,
+                                  jsval* aArgv,
+                                  jsval* aRval,
+                                  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();
+
+  nsRefPtr<nsDOMWorkerTimeout> timeout = new nsDOMWorkerTimeout(worker, id);
+  if (!timeout) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsresult rv = timeout->Init(aCx, aArgc, aArgv, 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;
+  }
+
+  *aRval = INT_TO_JSVAL(id);
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::KillTimeout(JSContext* aCx,
+                                  JSObject* /* aObj */,
+                                  uintN aArgc,
+                                  jsval* aArgv,
+                                  jsval* /* aRval */)
+{
+  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, aArgv[0], &id)) {
+    JS_ReportError(aCx, "First argument must be a timeout id");
+    return JS_FALSE;
+  }
+
+  worker->CancelTimeoutWithId(PRUint32(id));
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::LoadScripts(JSContext* aCx,
+                                  JSObject* /* aObj */,
+                                  uintN aArgc,
+                                  jsval* aArgv,
+                                  jsval* /* aRval */)
+{
+  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 must have at least one argument!");
+    return JS_FALSE;
+  }
+
+  nsAutoTArray<nsString, 10> urls;
+
+  if (!urls.SetCapacity((PRUint32)aArgc)) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  for (uintN index = 0; index < aArgc; index++) {
+    jsval val = aArgv[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!");
+
+    newURL->Assign(nsDependentJSString(str));
+  }
+
+  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);
+  if (NS_FAILED(rv)) {
+    if (!JS_IsExceptionPending(aCx)) {
+      JS_ReportError(aCx, "Failed to load scripts");
+    }
+    return JS_FALSE;
+  }
+
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::NewXMLHttpRequest(JSContext* aCx,
+                                        JSObject* aObj,
+                                        uintN aArgc,
+                                        jsval* /* aArgv */,
+                                        jsval* aRval)
+{
+  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;
+  }
+
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> xhrWrapped;
+  rv = xpc->WrapNative(aCx, aObj, static_cast<nsIXMLHttpRequest*>(xhr),
+                       NS_GET_IID(nsISupports), getter_AddRefs(xhrWrapped));
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to wrap XMLHttpRequest!");
+    return JS_FALSE;
+  }
+
+  JSObject* xhrJSObj;
+  rv = xhrWrapped->GetJSObject(&xhrJSObj);
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to get JSObject from wrapper!");
+    return JS_FALSE;
+  }
+
+  *aRval = OBJECT_TO_JSVAL(xhrJSObj);
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::NewWorker(JSContext* aCx,
+                                JSObject* aObj,
+                                uintN aArgc,
+                                jsval* aArgv,
+                                jsval* aRval)
+{
+  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;
+  }
+
+  nsRefPtr<nsDOMWorkerPool> pool = worker->Pool();
+  if (!pool) {
+    JS_ReportError(aCx, "Couldn't get pool from worker!");
+    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 = pool->ScriptGlobalObject();
+  if (!owner) {
+    JS_ReportError(aCx, "Couldn't get owner from pool!");
+    return JS_FALSE;
+  }
+
+  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);
+  if (!newWorker) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsresult rv = newWorker->InitializeInternal(owner, aCx, aObj, aArgc, aArgv);
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Couldn't initialize new worker!");
+    return JS_FALSE;
+  }
+
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> workerWrapped;
+  rv = xpc->WrapNative(aCx, aObj, static_cast<nsIWorker*>(newWorker),
+                       NS_GET_IID(nsISupports), getter_AddRefs(workerWrapped));
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to wrap new worker!");
+    return JS_FALSE;
+  }
+
+  JSObject* workerJSObj;
+  rv = workerWrapped->GetJSObject(&workerJSObj);
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to get JSObject from wrapper!");
+    return JS_FALSE;
+  }
+
+  *aRval = OBJECT_TO_JSVAL(workerJSObj);
+  return JS_TRUE;
+}
+
+JSFunctionSpec gDOMWorkerFunctions[] = {
+  { "dump",                  nsDOMWorkerFunctions::Dump,              1, 0, 0 },
+  { "setTimeout",            nsDOMWorkerFunctions::SetTimeout,        1, 0, 0 },
+  { "clearTimeout",          nsDOMWorkerFunctions::KillTimeout,       1, 0, 0 },
+  { "setInterval",           nsDOMWorkerFunctions::SetInterval,       1, 0, 0 },
+  { "clearInterval",         nsDOMWorkerFunctions::KillTimeout,       1, 0, 0 },
+  { "importScripts",         nsDOMWorkerFunctions::LoadScripts,       1, 0, 0 },
+  { "XMLHttpRequest",        nsDOMWorkerFunctions::NewXMLHttpRequest, 0, 0, 0 },
+  { "Worker",                nsDOMWorkerFunctions::NewWorker,         1, 0, 0 },
+#ifdef MOZ_SHARK
+  { "startShark",            js_StartShark,                           0, 0, 0 },
+  { "stopShark",             js_StopShark,                            0, 0, 0 },
+  { "connectShark",          js_ConnectShark,                         0, 0, 0 },
+  { "disconnectShark",       js_DisconnectShark,                      0, 0, 0 },
+#endif
+  { nsnull,                  nsnull,                                  0, 0, 0 }
+};
+
+class nsDOMWorkerScope : public nsIWorkerScope,
+                         public nsIDOMEventTarget,
+                         public nsIXPCScriptable,
+                         public nsIClassInfo
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIWORKERGLOBALSCOPE
+  NS_DECL_NSIWORKERSCOPE
+  NS_DECL_NSIDOMEVENTTARGET
+  NS_DECL_NSIXPCSCRIPTABLE
+  NS_DECL_NSICLASSINFO
+
+  nsDOMWorkerScope(nsDOMWorker* aWorker)
+  : mWorker(aWorker) {
+    NS_ASSERTION(aWorker, "Null pointer!");
+  }
+
+private:
+  nsDOMWorker* mWorker;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS5(nsDOMWorkerScope, nsIWorkerScope,
+                                                nsIWorkerGlobalScope,
+                                                nsIDOMEventTarget,
+                                                nsIXPCScriptable,
+                                                nsIClassInfo)
+
+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_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
+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::PostMessage(const nsAString& aMessage,
+                              nsIWorkerMessagePort* aMessagePort)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  if (aMessagePort) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  return mWorker->PostMessageInternal(aMessage, PR_FALSE);
+}
+
+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 =
+    mWorker->mInnerHandler->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 mWorker->mInnerHandler->SetOnXListener(NS_LITERAL_STRING("message"),
+                                                aOnmessage);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::AddEventListener(const nsAString& aType,
+                                   nsIDOMEventListener* aListener,
+                                   PRBool aUseCapture)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return mWorker->mInnerHandler->AddEventListener(aType, aListener,
+                                                  aUseCapture);
+}
+
+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 mWorker->mInnerHandler->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 mWorker->mInnerHandler->DispatchEvent(aEvent, _retval);
+}
+
+class nsWorkerHoldingRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsWorkerHoldingRunnable(nsDOMWorker* aWorker)
+  : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()) { }
+
+  NS_IMETHOD Run() {
+    return NS_OK;
+  }
+
+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;
+    }
+
+    nsCOMPtr<nsIDOMEventTarget> target = mToInner ?
+      static_cast<nsIDOMEventTarget*>(mWorker->GetInnerScope()) :
+      static_cast<nsIDOMEventTarget*>(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 PR_AtomicIncrement((PRInt32*)&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 = PR_AtomicDecrement((PRInt32*)&mRefCnt);
+  if (count == 0) {
+    if (mFreeToDie) {
+      mRefCnt = 1;
+      delete this;
+    }
+    else {
+      mWorker->RemoveFeature(this, nsnull);
+    }
+  }
+  return count;
+}
+
+NS_IMPL_QUERY_INTERFACE0(nsDOMWorkerFeature)
+
+class nsDOMWorkerClassInfo : public nsIClassInfo
+{
+public:
+  NS_DECL_NSICLASSINFO
+
+  NS_IMETHOD_(nsrefcnt) AddRef() {
+    return 2;
+  }
+
+  NS_IMETHOD_(nsrefcnt) Release() {
+    return 1;
+  }
+
+  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
+};
+
+NS_IMPL_QUERY_INTERFACE1(nsDOMWorkerClassInfo, nsIClassInfo)
+
+// Keep this list in sync with the list in nsDOMClassInfo.cpp!
+NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerClassInfo, nsIWorker,
+                                                   nsIAbstractWorker,
+                                                   nsIDOMEventTarget)
+
+NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerClassInfo)
+
+static nsDOMWorkerClassInfo sDOMWorkerClassInfo;
+
+nsDOMWorker::nsDOMWorker(nsDOMWorker* aParent,
+                         nsIXPConnectWrappedNative* aParentWN)
+: mParent(aParent),
+  mParentWN(aParentWN),
+  mCallbackCount(0),
+  mLock(nsnull),
+  mInnerScope(nsnull),
+  mGlobal(NULL),
+  mNextTimeoutId(0),
+  mFeatureSuspendDepth(0),
+  mWrappedNative(nsnull),
+  mCanceled(PR_FALSE),
+  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);
+  }
+
+  if (mLock) {
+    nsAutoLock::DestroyLock(mLock);
+  }
+
+  NS_ASSERTION(!mFeatures.Length(), "Live features!");
+}
+
+/* static */ nsresult
+nsDOMWorker::NewWorker(nsISupports** aNewObject)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsISupports> newWorker =
+    NS_ISUPPORTS_CAST(nsIWorker*, new nsDOMWorker(nsnull, nsnull));
+  NS_ENSURE_TRUE(newWorker, NS_ERROR_OUT_OF_MEMORY);
+
+  newWorker.forget(aNewObject);
+  return NS_OK;
+}
+
+NS_IMPL_THREADSAFE_ADDREF(nsDOMWorker)
+NS_IMPL_THREADSAFE_RELEASE(nsDOMWorker)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMWorker)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWorker)
+  NS_INTERFACE_MAP_ENTRY(nsIWorker)
+  NS_INTERFACE_MAP_ENTRY(nsIAbstractWorker)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
+  NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+  NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
+  if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
+    foundInterface = static_cast<nsIClassInfo*>(&sDOMWorkerClassInfo);
+  } else
+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_POSTCREATE
+#define XPC_MAP_WANT_TRACE
+#define XPC_MAP_WANT_FINALIZE
+
+#define XPC_MAP_FLAGS                                      \
+  nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE            | \
+  nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY            | \
+  nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES
+
+#include "xpc_map_end.h"
+
+NS_IMETHODIMP
+nsDOMWorker::PostCreate(nsIXPConnectWrappedNative* aWrapper,
+                        JSContext* /* aCx */,
+                        JSObject* /* aObj */)
+{
+  mWrappedNative = aWrapper;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorker::Trace(nsIXPConnectWrappedNative* /* aWrapper */,
+                   JSTracer* aTracer,
+                   JSObject* /*aObj */)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!IsCanceled()) {
+    if (mGlobal) {
+      JS_SET_TRACING_DETAILS(aTracer, nsnull, this, 0);
+      JS_CallTracer(aTracer, mGlobal, JSTRACE_OBJECT);
+    }
+    // We should never get null handlers here if our call to Initialize succeeded.
+    NS_ASSERTION(mInnerHandler && mOuterHandler, "Shouldn't be possible!");
+
+    mInnerHandler->Trace(aTracer);
+    mOuterHandler->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!
+  mInnerHandler->ClearAllListeners();
+  mOuterHandler->ClearAllListeners();
+
+  // Clear our wrapped native now that it has died.
+  mWrappedNative = nsnull;
+
+  // We no longer need to keep our inner scope.
+  mGlobal = NULL;
+  mInnerScope = nsnull;
+
+  // And we can let our parent die now too.
+  mParent = nsnull;
+  mParentWN = 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_ENSURE_TRUE(aArgc, NS_ERROR_INVALID_ARG);
+  NS_ENSURE_ARG_POINTER(aArgv);
+  NS_ENSURE_TRUE(JSVAL_IS_STRING(aArgv[0]), NS_ERROR_INVALID_ARG);
+
+  JSString* str = JS_ValueToString(aCx, aArgv[0]);
+  NS_ENSURE_STATE(str);
+
+  mScriptURL.Assign(nsDependentJSString(str));
+  NS_ENSURE_FALSE(mScriptURL.IsEmpty(), NS_ERROR_INVALID_ARG);
+
+  mLock = nsAutoLock::NewLock("nsDOMWorker::mLock");
+  NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
+
+  mInnerHandler = new nsDOMWorkerMessageHandler();
+  NS_ENSURE_TRUE(mInnerHandler, NS_ERROR_OUT_OF_MEMORY);
+
+  mOuterHandler = new nsDOMWorkerMessageHandler();
+  NS_ENSURE_TRUE(mOuterHandler, NS_ERROR_OUT_OF_MEMORY);
+
+  NS_ASSERTION(!mGlobal, "Already got a global?!");
+
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> thisWrapped;
+  nsresult rv = xpc->WrapNative(aCx, aObj, static_cast<nsIWorker*>(this),
+                                NS_GET_IID(nsISupports),
+                                getter_AddRefs(thisWrapped));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(mWrappedNative, "Post-create hook should have set this!");
+
+  // 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()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  mCanceled = PR_TRUE;
+
+  CancelFeatures();
+}
+
+void
+nsDOMWorker::Suspend()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!mSuspended, "Suspended more than once!");
+  mSuspended = PR_TRUE;
+
+  SuspendFeatures();
+}
+
+void
+nsDOMWorker::Resume()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(mSuspended, "Not suspended!");
+  mSuspended = PR_FALSE;
+
+  ResumeFeatures();
+}
+
+nsresult
+nsDOMWorker::PostMessageInternal(const nsAString& aMessage,
+                                 PRBool aToInner)
+{
+  nsRefPtr<nsDOMWorkerMessageEvent> message = new nsDOMWorkerMessageEvent();
+  NS_ENSURE_TRUE(message, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv = message->InitMessageEvent(NS_LITERAL_STRING("message"),
+                                          PR_FALSE, PR_FALSE, aMessage,
+                                          EmptyString(), nsnull);
+  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)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (!CompileGlobalObject(aCx)) {
+    return PR_FALSE;
+  }
+
+  JS_SetGlobalObject(aCx, mGlobal);
+  return PR_TRUE;
+}
+
+PRBool
+nsDOMWorker::CompileGlobalObject(JSContext* aCx)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mGlobal) {
+    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!");
+
+  JSAutoRequest ar(aCx);
+
+  NS_ASSERTION(!JS_GetGlobalObject(aCx), "Global object should be unset!");
+
+  nsRefPtr<nsDOMWorkerScope> scope = new nsDOMWorkerScope(this);
+  NS_ENSURE_TRUE(scope, NS_ERROR_OUT_OF_MEMORY);
+
+  nsISupports* scopeSupports = NS_ISUPPORTS_CAST(nsIWorkerScope*, scope);
+
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> globalWrapper;
+  nsresult rv =
+    xpc->InitClassesWithNewWrappedGlobal(aCx, scopeSupports,
+                                         NS_GET_IID(nsISupports),
+                                         nsIXPConnect::INIT_JS_STANDARD_CLASSES,
+                                         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!");
+
+  // XXX Fix this!
+  PRBool success = JS_DeleteProperty(aCx, global, "Components");
+  NS_ENSURE_TRUE(success, PR_FALSE);
+
+  // Set up worker thread functions
+  success = JS_DefineFunctions(aCx, global, gDOMWorkerFunctions);
+  NS_ENSURE_TRUE(success, PR_FALSE);
+
+  // From here on out we have to remember to null mGlobal and mInnerScope if
+  // something fails!
+  mGlobal = global;
+  mInnerScope = scope;
+
+  nsRefPtr<nsDOMWorkerScriptLoader> loader =
+    new nsDOMWorkerScriptLoader(this);
+  NS_ASSERTION(loader, "Out of memory!");
+  if (!loader) {
+    mGlobal = NULL;
+    mInnerScope = nsnull;
+    return PR_FALSE;
+  }
+
+  rv = AddFeature(loader, aCx);
+  if (NS_FAILED(rv)) {
+    mGlobal = NULL;
+    mInnerScope = nsnull;
+    return PR_FALSE;
+  }
+
+  rv = loader->LoadScript(aCx, mScriptURL);
+
+  JS_ReportPendingException(aCx);
+
+  if (NS_FAILED(rv)) {
+    mGlobal = NULL;
+    mInnerScope = nsnull;
+    return PR_FALSE;
+  }
+
+  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 = mWrappedNative;
+  NS_ASSERTION(wrappedNative, "Null wrapped native!");
+  return wrappedNative.forget();
+}
+
+nsresult
+nsDOMWorker::AddFeature(nsDOMWorkerFeature* aFeature,
+                        JSContext* aCx)
+{
+  NS_ASSERTION(aFeature, "Null pointer!");
+
+  PRBool shouldSuspend;
+
+  {
+    // aCx may be null.
+    JSAutoSuspendRequest asr(aCx);
+
+    nsAutoLock lock(mLock);
+
+    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);
+
+    nsAutoLock 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;
+  {
+    nsAutoLock 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;
+  {
+    nsAutoLock 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;
+  {
+    nsAutoLock 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::CancelFeatures()
+{
+  NS_ASSERTION(IsCanceled(), "More items can still be added!");
+
+  PRUint32 count, index;
+
+  nsAutoTArray<nsRefPtr<nsDOMWorkerFeature>, 20> features;
+  {
+    nsAutoLock lock(mLock);
+
+    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();
+  }
+}
+
+already_AddRefed<nsDOMWorker>
+nsDOMWorker::GetParent()
+{
+  nsRefPtr<nsDOMWorker> parent(mParent);
+  return parent.forget();
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::PostMessage(const nsAString& aMessage,
+                         nsIWorkerMessagePort* aMessagePort)
+{
+  if (aMessagePort) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  return PostMessageInternal(aMessage, PR_TRUE);
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::GetOnerror(nsIDOMEventListener** aOnerror)
+{
+  NS_ENSURE_ARG_POINTER(aOnerror);
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    mOuterHandler->GetOnXListener(NS_LITERAL_STRING("error"));
+
+  listener.forget(aOnerror);
+  return NS_OK;
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::SetOnerror(nsIDOMEventListener* aOnerror)
+{
+  return mOuterHandler->SetOnXListener(NS_LITERAL_STRING("error"), aOnerror);
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::GetOnmessage(nsIDOMEventListener** aOnmessage)
+{
+  NS_ENSURE_ARG_POINTER(aOnmessage);
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    mOuterHandler->GetOnXListener(NS_LITERAL_STRING("message"));
+
+  listener.forget(aOnmessage);
+  return NS_OK;
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::SetOnmessage(nsIDOMEventListener* aOnmessage)
+{
+  return mOuterHandler->SetOnXListener(NS_LITERAL_STRING("message"),
+                                       aOnmessage);
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorker.h
@@ -0,0 +1,254 @@
+/* -*- 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 __NSDOMWORKER_H__
+#define __NSDOMWORKER_H__
+
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMWorkers.h"
+#include "nsIJSNativeInitializer.h"
+#include "nsIXPCScriptable.h"
+
+#include "jsapi.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsTPtrArray.h"
+#include "prlock.h"
+
+#include "nsDOMWorkerMessageHandler.h"
+
+class nsDOMWorkerMessageHandler;
+class nsDOMWorkerPool;
+class nsDOMWorkerScope;
+class nsDOMWorkerTimeout;
+class nsICancelable;
+class nsIDOMEventListener;
+class nsIEventTarget;
+class nsIScriptGlobalObject;
+class nsIXPConnectWrappedNative;
+
+class nsDOMWorkerFeature;
+
+class nsDOMWorker : public nsIWorker,
+                    public nsIJSNativeInitializer,
+                    public nsIXPCScriptable
+{
+  friend class nsDOMWorkerFeature;
+  friend class nsDOMWorkerFunctions;
+  friend class nsDOMWorkerRefPtr;
+  friend class nsDOMWorkerScope;
+  friend class nsDOMWorkerScriptLoader;
+  friend class nsDOMWorkerTimeout;
+  friend class nsDOMWorkerXHR;
+
+  friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
+  friend void DOMWorkerErrorReporter(JSContext* aCx,
+                                     const char* aMessage,
+                                     JSErrorReport* aReport);
+
+#ifdef DEBUG
+  // For fun assertions.
+  friend class nsDOMFireEventRunnable;
+#endif
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIABSTRACTWORKER
+  NS_DECL_NSIWORKER
+  NS_FORWARD_SAFE_NSIDOMEVENTTARGET(mOuterHandler)
+  NS_DECL_NSIXPCSCRIPTABLE
+
+  static nsresult NewWorker(nsISupports** aNewObject);
+
+  nsDOMWorker(nsDOMWorker* aParent,
+              nsIXPConnectWrappedNative* aParentWN);
+
+  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 Suspend();
+  void Resume();
+
+  PRBool IsCanceled() {
+    return mCanceled;
+  }
+
+  PRBool IsSuspended() {
+    return mSuspended;
+  }
+
+  PRBool SetGlobalForContext(JSContext* aCx);
+
+  void SetPool(nsDOMWorkerPool* aPool);
+
+  nsDOMWorkerPool* Pool() {
+    return mPool;
+  }
+
+  PRLock* Lock() {
+    return mLock;
+  }
+
+  already_AddRefed<nsIXPConnectWrappedNative> GetWrappedNative();
+  already_AddRefed<nsDOMWorker> GetParent();
+
+  nsDOMWorkerScope* GetInnerScope() {
+    return mInnerScope;
+  }
+
+private:
+  ~nsDOMWorker();
+
+  nsresult PostMessageInternal(const nsAString& aMessage,
+                               PRBool aToInner);
+
+  PRBool CompileGlobalObject(JSContext* aCx);
+
+  PRUint32 NextTimeoutId() {
+    return mNextTimeoutId++;
+  }
+
+  nsresult AddFeature(nsDOMWorkerFeature* aFeature,
+                      JSContext* aCx);
+  void RemoveFeature(nsDOMWorkerFeature* aFeature,
+                     JSContext* aCx);
+  void CancelTimeoutWithId(PRUint32 aId);
+  void SuspendFeatures();
+  void ResumeFeatures();
+  void CancelFeatures();
+
+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;
+
+  PRUint32 mCallbackCount;
+
+  PRLock* mLock;
+
+  nsRefPtr<nsDOMWorkerMessageHandler> mInnerHandler;
+  nsRefPtr<nsDOMWorkerMessageHandler> mOuterHandler;
+
+  nsRefPtr<nsDOMWorkerPool> mPool;
+
+  nsDOMWorkerScope* mInnerScope;
+  JSObject* mGlobal;
+
+  PRUint32 mNextTimeoutId;
+
+  nsTArray<nsDOMWorkerFeature*> mFeatures;
+  PRUint32 mFeatureSuspendDepth;
+
+  nsString mScriptURL;
+
+  nsIXPConnectWrappedNative* mWrappedNative;
+
+  PRPackedBool mCanceled;
+  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;
+};
+
+#endif /* __NSDOMWORKER_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerEvents.cpp
@@ -0,0 +1,422 @@
+/* -*- 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 "nsThreadUtils.h"
+
+#include "nsDOMWorkerMessageHandler.h"
+#include "nsDOMWorkerXHR.h"
+#include "nsDOMWorkerXHRProxy.h"
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIDOMWorkerPrivateEvent,
+                              NS_IDOMWORKERPRIVATEEVENT_IID)
+
+nsDOMWorkerPrivateEvent::nsDOMWorkerPrivateEvent(nsIDOMEvent* aEvent)
+: mEvent(aEvent),
+  mPreventDefaultCalled(PR_FALSE)
+{
+  NS_ASSERTION(aEvent, "Null pointer!");
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMWorkerPrivateEvent, nsIDOMEvent,
+                                                       nsIDOMWorkerPrivateEvent,
+                                                       nsIClassInfo)
+
+NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerPrivateEvent, nsIDOMEvent,
+                                                      nsIDOMWorkerPrivateEvent)
+
+NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerPrivateEvent)
+
+NS_IMETHODIMP
+nsDOMWorkerPrivateEvent::PreventDefault()
+{
+  mPreventDefaultCalled = PR_TRUE;
+  return mEvent->PreventDefault();
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPrivateEvent::InitEvent(const nsAString& aEventType,
+                                   PRBool aCanBubble,
+                                   PRBool aCancelable)
+{
+  mPreventDefaultCalled = PR_FALSE;
+  return mEvent->InitEvent(aEventType, aCanBubble, aCancelable);
+}
+
+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::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;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerMessageEvent, nsDOMWorkerEvent,
+                                                      nsIWorkerMessageEvent)
+
+NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerMessageEvent, nsIDOMEvent,
+                                                      nsIWorkerMessageEvent)
+
+NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerMessageEvent)
+
+NS_IMETHODIMP
+nsDOMWorkerMessageEvent::GetData(nsAString& aData)
+{
+  aData.Assign(mData);
+  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)
+{
+  mData.Assign(aDataArg);
+  mOrigin.Assign(aOriginArg);
+  mSource = aSourceArg;
+  return nsDOMWorkerEvent::InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
+}
+
+nsDOMWorkerXHREvent::nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy)
+: mXHRProxy(aXHRProxy),
+  mXHREventType(PR_UINT32_MAX),
+  mStatus(NS_OK),
+  mReadyState(0),
+  mLoaded(0),
+  mTotal(0),
+  mChannelID(-1),
+  mUploadEvent(PR_FALSE),
+  mProgressEvent(PR_FALSE),
+  mLengthComputable(PR_FALSE)
+{
+  NS_ASSERTION(aXHRProxy, "Can't be null!");
+}
+
+NS_IMPL_ADDREF_INHERITED(nsDOMWorkerXHREvent, nsDOMWorkerEvent)
+NS_IMPL_RELEASE_INHERITED(nsDOMWorkerXHREvent, nsDOMWorkerEvent)
+
+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)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aEvent, "Don't pass null here!");
+
+  mXHREventType = aXHREventType;
+  mChannelID = mXHRProxy->ChannelID();
+
+  mTarget = static_cast<nsDOMWorkerMessageHandler*>(mXHRProxy->mWorkerXHR);
+  NS_ENSURE_TRUE(mTarget, NS_ERROR_UNEXPECTED);
+
+  mWorkerWN = mXHRProxy->mWorkerXHR->mWorker->GetWrappedNative();
+  NS_ENSURE_STATE(mWorkerWN);
+
+  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 = nsDOMWorkerEvent::InitEvent(aType, bubbles, cancelable);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = SnapshotXHRState(mXHRProxy->mXHR);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHREvent::SnapshotXHRState(nsIXMLHttpRequest* aXHR)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aXHR, "Don't pass null here!");
+
+  nsresult rv = aXHR->GetResponseText(mResponseText);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aXHR->GetStatusText(mStatusText);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aXHR->GetStatus(&mStatus);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aXHR->GetReadyState(&mReadyState);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+void
+nsDOMWorkerXHREvent::EventHandled()
+{
+  // Prevent reference cycles by releasing these here.
+  mXHRProxy = nsnull;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::Run()
+{
+  nsresult rv = mXHRProxy->HandleWorkerEvent(this, mUploadEvent);
+  EventHandled();
+  NS_ENSURE_SUCCESS(rv, rv);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetLengthComputable(PRBool* aLengthComputable)
+{
+  NS_ENSURE_ARG_POINTER(aLengthComputable);
+  *aLengthComputable = mLengthComputable;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetLoaded(PRUint64* aLoaded)
+{
+  NS_ENSURE_ARG_POINTER(aLoaded);
+  *aLoaded = mLoaded;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetTotal(PRUint64* aTotal)
+{
+  NS_ENSURE_ARG_POINTER(aTotal);
+  *aTotal = mTotal;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::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);
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerEvents.h
@@ -0,0 +1,183 @@
+/* -*- 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 "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;
+};
+
+class nsDOMWorkerPrivateEvent : public nsIDOMWorkerPrivateEvent,
+                                public nsIClassInfo
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_FORWARD_NSIDOMEVENT_SPECIAL
+  NS_DECL_NSICLASSINFO
+
+  nsDOMWorkerPrivateEvent(nsIDOMEvent* aEvent);
+
+  NS_IMETHOD PreventDefault();
+
+  NS_IMETHOD InitEvent(const nsAString& aEventType,
+                       PRBool aCanBubble,
+                       PRBool aCancelable);
+
+  virtual PRBool PreventDefaultCalled();
+
+private:
+  nsCOMPtr<nsIDOMEvent> mEvent;
+  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;
+  }
+
+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_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerEvent::)
+  NS_DECL_NSICLASSINFO_GETINTERFACES
+
+protected:
+  nsString mData;
+  nsString mOrigin;
+  nsCOMPtr<nsISupports> mSource;
+};
+
+class nsDOMWorkerXHREvent : public nsDOMWorkerEvent,
+                            public nsIRunnable,
+                            public nsIDOMProgressEvent
+{
+  friend class nsDOMWorkerXHRProxy;
+  friend class nsDOMWorkerXHREventTargetProxy;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIRUNNABLE
+  NS_FORWARD_NSIDOMEVENT(nsDOMWorkerEvent::)
+  NS_DECL_NSIDOMPROGRESSEVENT
+  NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerEvent::)
+  NS_DECL_NSICLASSINFO_GETINTERFACES
+
+  nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy);
+
+  nsresult Init(PRUint32 aXHREventType,
+                const nsAString& aType,
+                nsIDOMEvent* aEvent);
+
+  nsresult SnapshotXHRState(nsIXMLHttpRequest* aXHR);
+
+  void EventHandled();
+
+protected:
+  nsRefPtr<nsDOMWorkerXHRProxy> mXHRProxy;
+  nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
+  PRUint32 mXHREventType;
+  nsString mResponseText;
+  nsCString mStatusText;
+  nsresult mStatus;
+  PRInt32 mReadyState;
+  PRUint64 mLoaded;
+  PRUint64 mTotal;
+  PRInt32 mChannelID;
+  PRPackedBool mUploadEvent;
+  PRPackedBool mProgressEvent;
+  PRPackedBool mLengthComputable;
+};
+
+#endif /* __NSDOMWORKEREVENTS_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerMacros.h
@@ -0,0 +1,150 @@
+/* -*- 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);
+
+#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(); }
+
+// 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(); }
+
+#endif /* __NSDOMWORKERMACROS_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerMessageHandler.cpp
@@ -0,0 +1,353 @@
+/* -*- 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::AddEventListener(const nsAString& aType,
+                                            nsIDOMEventListener* aListener,
+                                            PRBool aUseCapture)
+{
+  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;
+}
+
+/**
+ * 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;
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerMessageHandler.h
@@ -0,0 +1,161 @@
+/* -*- 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;
+};
+
+#endif /* __NSDOMWORKERMESSAGEHANDLER_H__ */
--- a/dom/src/threads/nsDOMWorkerPool.cpp
+++ b/dom/src/threads/nsDOMWorkerPool.cpp
@@ -53,283 +53,190 @@
 // Other includes
 #include "nsAutoLock.h"
 #include "nsContentUtils.h"
 #include "nsDOMJSUtils.h"
 #include "nsThreadUtils.h"
 
 // DOMWorker includes
 #include "nsDOMThreadService.h"
-#include "nsDOMWorkerThread.h"
+#include "nsDOMWorker.h"
 
 #define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
 
-#define LOOP_OVER_WORKERS(_func, _args)                   \
-  PR_BEGIN_MACRO                                          \
-    PRUint32 workerCount = mWorkers.Length();             \
-    for (PRUint32 i = 0; i < workerCount; i++) {          \
-      mWorkers[i]-> _func _args ;                         \
-    }                                                     \
-  PR_END_MACRO
-
-nsDOMWorkerPool::nsDOMWorkerPool(nsIDocument* aDocument)
-: mParentGlobal(nsnull),
-  mParentDocument(aDocument)
+nsDOMWorkerPool::nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject,
+                                 nsIDocument* aDocument)
+: mParentGlobal(aGlobalObject),
+  mParentDocument(aDocument),
+  mMonitor(nsnull),
+  mCanceled(PR_FALSE),
+  mSuspended(PR_FALSE)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aGlobalObject, "Must have a global object!");
   NS_ASSERTION(aDocument, "Must have a document!");
 }
 
 nsDOMWorkerPool::~nsDOMWorkerPool()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  LOOP_OVER_WORKERS(Cancel, ());
-
-  nsDOMThreadService::get()->NoteDyingPool(this);
-
   if (mMonitor) {
     nsAutoMonitor::DestroyMonitor(mMonitor);
   }
 }
 
-NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerPool, nsIDOMWorkerPool,
-                                               nsIClassInfo)
-
-NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerPool, nsIDOMWorkerPool)
-
-NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerPool)
+NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerPool)
+NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerPool)
 
 nsresult
 nsDOMWorkerPool::Init()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsIScriptGlobalObject* globalObject =
-    mParentDocument->GetScriptGlobalObject();
-  NS_ENSURE_STATE(globalObject);
-
-  nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(globalObject));
-  NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
-
-  nsPIDOMWindow* innerWindow = domWindow->IsOuterWindow() ?
-                               domWindow->GetCurrentInnerWindow() :
-                               domWindow.get();
-  NS_ENSURE_STATE(innerWindow);
-
-  nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(innerWindow));
-  NS_ENSURE_TRUE(globalSupports, NS_ERROR_NO_INTERFACE);
-
-  // We don't want a strong ref, this guy owns us.
-  mParentGlobal = globalSupports.get();
-
   mMonitor = nsAutoMonitor::NewMonitor("nsDOMWorkerPool::mMonitor");
   NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
 
   return NS_OK;
 }
 
 nsresult
-nsDOMWorkerPool::HandleMessage(const nsAString& aMessage,
-                               nsDOMWorkerBase* aSource)
+nsDOMWorkerPool::NoteWorker(nsDOMWorker* aWorker)
 {
-  nsCOMPtr<nsIDOMWorkerMessageListener> messageListener =
-    nsDOMWorkerBase::GetMessageListener();
-  if (!messageListener) {
-    LOG(("Message received on a worker with no listener!"));
-    return NS_OK;
+  NS_ASSERTION(aWorker, "Null pointer!");
+
+  PRBool suspendWorker;
+
+  {
+    nsAutoMonitor mon(mMonitor);
+
+    if (mCanceled) {
+      return NS_ERROR_ABORT;
+    }
+
+    nsDOMWorker** newWorker = mWorkers.AppendElement(aWorker);
+    NS_ENSURE_TRUE(newWorker, NS_ERROR_OUT_OF_MEMORY);
+
+    suspendWorker = mSuspended;
   }
 
-  nsCOMPtr<nsISupports> source;
-  aSource->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(source));
-  NS_ASSERTION(source, "Impossible!");
-
-  messageListener->OnMessage(aMessage, source);
-  return NS_OK;
-}
-
-nsresult
-nsDOMWorkerPool::DispatchMessage(nsIRunnable* aRunnable)
-{
-  // Can be called from many different threads!
-
-  nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
-  NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
-
-  nsresult rv = mainThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (suspendWorker) {
+    aWorker->Suspend();
+  }
 
   return NS_OK;
 }
 
 void
-nsDOMWorkerPool::HandleError(nsIScriptError* aError,
-                             nsDOMWorkerThread* aSource)
+nsDOMWorkerPool::NoteDyingWorker(nsDOMWorker* aWorker)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aWorker, "Null pointer!");
+
+  PRBool removeFromThreadService = PR_FALSE;
+
+  {
+    nsAutoMonitor mon(mMonitor);
 
-  if (mErrorListener) {
-    mErrorListener->OnError(aError,
-                            NS_ISUPPORTS_CAST(nsIDOMWorkerThread*, aSource));
+    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::NoteDyingWorker(nsDOMWorkerThread* aWorker)
+nsDOMWorkerPool::GetWorkers(nsTArray<nsDOMWorker*>& aArray)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  aArray.Clear();
 
-  NS_ASSERTION(mWorkers.Contains(aWorker), "Worker from a different pool?!");
-  mWorkers.RemoveElement(aWorker);
+  nsAutoMonitor mon(mMonitor);
+#ifdef DEBUG
+  nsDOMWorker** newWorkers =
+#endif
+  aArray.AppendElements(mWorkers);
+  NS_WARN_IF_FALSE(newWorkers, "Out of memory!");
 }
 
 void
-nsDOMWorkerPool::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
+nsDOMWorkerPool::Cancel()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!mCanceled, "Canceled more than once!");
+
+  {
+    nsAutoMonitor mon(mMonitor);
+
+    mCanceled = PR_TRUE;
+
+    nsAutoTArray<nsDOMWorker*, 10> workers;
+    GetWorkers(workers);
+
+    PRUint32 count = workers.Length();
+    if (count) {
+      for (PRUint32 index = 0; index < count; index++) {
+        workers[index]->Cancel();
+      }
+      mon.NotifyAll();
+    }
+  }
+
+  mParentGlobal = nsnull;
+  mParentDocument = nsnull;
+}
+
+void
+nsDOMWorkerPool::Suspend()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
-  NS_ASSERTION(globalSupports, "Null pointer?!");
+  nsAutoTArray<nsDOMWorker*, 10> workers;
+  {
+    nsAutoMonitor mon(mMonitor);
+
+    NS_ASSERTION(!mSuspended, "Suspended more than once!");
+    mSuspended = PR_TRUE;
 
-  if (globalSupports == mParentGlobal) {
-    LOOP_OVER_WORKERS(Cancel, ());
-    mWorkers.Clear();
-    if (IsSuspended()) {
-      nsAutoMonitor mon(mMonitor);
-      mon.NotifyAll();
-    }
+    GetWorkers(workers);
+  }
+
+  PRUint32 count = workers.Length();
+  for (PRUint32 index = 0; index < count; index++) {
+    workers[index]->Suspend();
   }
 }
 
 void
-nsDOMWorkerPool::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
-  NS_ASSERTION(globalSupports, "Null pointer?!");
-
-  if (globalSupports == mParentGlobal) {
-    LOOP_OVER_WORKERS(Suspend, ());
-    Suspend();
-  }
-}
-
-void
-nsDOMWorkerPool::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
+nsDOMWorkerPool::Resume()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
-  NS_ASSERTION(globalSupports, "Null pointer?!");
+  nsAutoTArray<nsDOMWorker*, 10> workers;
+  {
+    nsAutoMonitor mon(mMonitor);
+
+    NS_ASSERTION(mSuspended, "Not suspended!");
+    mSuspended = PR_FALSE;
 
-  if (globalSupports == mParentGlobal) {
-    LOOP_OVER_WORKERS(Resume, ());
-    Resume();
+    GetWorkers(workers);
+  }
 
+  PRUint32 count = workers.Length();
+  if (count) {
+    for (PRUint32 index = 0; index < count; index++) {
+      workers[index]->Resume();
+    }
     nsAutoMonitor mon(mMonitor);
     mon.NotifyAll();
   }
 }
 
-nsIDocument*
-nsDOMWorkerPool::ParentDocument()
-{
-  NS_ASSERTION(NS_IsMainThread(),
-               "Don't touch the non-threadsafe document off the main thread!");
-  return mParentDocument;
-}
-
 nsIScriptContext*
 nsDOMWorkerPool::ScriptContext()
 {
   NS_ASSERTION(NS_IsMainThread(),
                "Don't touch the non-threadsafe script context off the main "
                "thread!");
-  return mParentDocument->GetScriptGlobalObject()->GetContext();
-}
-
-NS_IMETHODIMP
-nsDOMWorkerPool::PostMessage(const nsAString& aMessage)
-{
-  nsresult rv = PostMessageInternal(aMessage);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerPool::SetMessageListener(nsIDOMWorkerMessageListener* aListener)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  nsDOMWorkerBase::SetMessageListener(aListener);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerPool::GetMessageListener(nsIDOMWorkerMessageListener** aListener)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  nsCOMPtr<nsIDOMWorkerMessageListener> listener = nsDOMWorkerBase::GetMessageListener();
-  listener.forget(aListener);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerPool::SetErrorListener(nsIDOMWorkerErrorListener* aListener)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  mErrorListener = aListener;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerPool::GetErrorListener(nsIDOMWorkerErrorListener** aListener)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_IF_ADDREF(*aListener = mErrorListener);
-  return NS_OK;
+  return mParentGlobal->GetContext();
 }
-
-NS_IMETHODIMP
-nsDOMWorkerPool::CreateWorker(const nsAString& aFullScript,
-                              nsIDOMWorkerThread** _retval)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  NS_ENSURE_ARG(!aFullScript.IsEmpty());
-  NS_ENSURE_ARG_POINTER(_retval);
-
-  nsRefPtr<nsDOMWorkerThread> worker =
-    new nsDOMWorkerThread(this, aFullScript, PR_FALSE);
-  NS_ENSURE_TRUE(worker, NS_ERROR_OUT_OF_MEMORY);
-
-  nsresult rv = worker->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  NS_ASSERTION(!mWorkers.Contains(worker), "Um?!");
-  mWorkers.AppendElement(worker);
-
-  NS_ADDREF(*_retval = worker);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerPool::CreateWorkerFromURL(const nsAString& aScriptURL,
-                                     nsIDOMWorkerThread** _retval)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  NS_ENSURE_ARG(!aScriptURL.IsEmpty());
-  NS_ENSURE_ARG_POINTER(_retval);
-
-  nsRefPtr<nsDOMWorkerThread> worker =
-    new nsDOMWorkerThread(this, aScriptURL, PR_TRUE);
-  NS_ENSURE_TRUE(worker, NS_ERROR_OUT_OF_MEMORY);
-
-  nsresult rv = worker->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  NS_ASSERTION(!mWorkers.Contains(worker), "Um?!");
-  mWorkers.AppendElement(worker);
-
-  NS_ADDREF(*_retval = worker);
-  return NS_OK;
-}
--- a/dom/src/threads/nsDOMWorkerPool.h
+++ b/dom/src/threads/nsDOMWorkerPool.h
@@ -35,99 +35,78 @@
  * 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__
 
-// Bases
-#include "nsDOMWorkerBase.h"
-#include "nsIClassInfo.h"
-#include "nsIDOMThreads.h"
-
 // Other includes
 #include "jsapi.h"
+#include "nsCOMPtr.h"
 #include "nsStringGlue.h"
-#include "nsTPtrArray.h"
+#include "nsTArray.h"
 #include "prmon.h"
 
-class nsDOMWorkerThread;
+class nsDOMWorker;
 class nsIDocument;
 class nsIScriptContext;
 class nsIScriptError;
 class nsIScriptGlobalObject;
 
-/**
- * The pool is almost always touched only on the main thread.
- */
-class nsDOMWorkerPool : public nsDOMWorkerBase,
-                        public nsIDOMWorkerPool,
-                        public nsIClassInfo
+class nsDOMWorkerPool
 {
-  friend class nsDOMThreadService;
-  friend class nsDOMWorkerFunctions;
-  friend class nsDOMWorkerPoolWeakRef;
-  friend class nsDOMWorkerScriptLoader;
-  friend class nsDOMWorkerStreamObserver;
-  friend class nsDOMWorkerThread;
-  friend class nsReportErrorRunnable;
-  friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
+public:
+  nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject,
+                  nsIDocument* aDocument);
 
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMWORKERPOOL
-  NS_DECL_NSICLASSINFO
+  NS_IMETHOD_(nsrefcnt) AddRef();
+  NS_IMETHOD_(nsrefcnt) Release();
 
-  nsDOMWorkerPool(nsIDocument* aDocument);
+  nsIScriptContext* ScriptContext();
 
-  // For nsDOMWorkerBase
-  virtual nsDOMWorkerPool* Pool() {
-    return this;
+  nsIScriptGlobalObject* ScriptGlobalObject() {
+    return mParentGlobal;
   }
 
-  nsIDocument* ParentDocument();
-  nsIScriptContext* ScriptContext();
-
-private:
-  virtual ~nsDOMWorkerPool();
+  nsIDocument* ParentDocument() {
+    return mParentDocument;
+  }
 
   nsresult Init();
 
-  // For nsDOMWorkerBase
-  virtual nsresult HandleMessage(const nsAString& aMessage,
-                                 nsDOMWorkerBase* aSourceThread);
-
-  // For nsDOMWorkerBase
-  virtual nsresult DispatchMessage(nsIRunnable* aRunnable);
+  void Cancel();
+  void Suspend();
+  void Resume();
 
-  void HandleError(nsIScriptError* aError,
-                   nsDOMWorkerThread* aSource);
-
-  void NoteDyingWorker(nsDOMWorkerThread* aWorker);
-
-  void CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
-  void SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
-  void ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
+  nsresult NoteWorker(nsDOMWorker* aWorker);
+  void NoteDyingWorker(nsDOMWorker* aWorker);
 
   PRMonitor* Monitor() {
     return mMonitor;
   }
 
-  // Weak reference to the window that created and owns this pool.
-  nsISupports* mParentGlobal;
+private:
+  virtual ~nsDOMWorkerPool();
+
+  void GetWorkers(nsTArray<nsDOMWorker*>& aArray);
 
-  // Weak reference to the document that created this pool.
-  nsIDocument* mParentDocument;
+  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.
-  nsTPtrArray<nsDOMWorkerThread> mWorkers;
-
-  // An error handler function, may be null.
-  nsCOMPtr<nsIDOMWorkerErrorListener> mErrorListener;
+  nsTArray<nsDOMWorker*> mWorkers;
 
   // Monitor for suspending and resuming workers.
   PRMonitor* mMonitor;
+
+  PRPackedBool mCanceled;
+  PRPackedBool mSuspended;
 };
 
 #endif /* __NSDOMWORKERPOOL_H__ */
--- a/dom/src/threads/nsDOMWorkerScriptLoader.cpp
+++ b/dom/src/threads/nsDOMWorkerScriptLoader.cpp
@@ -60,88 +60,41 @@
 // DOMWorker includes
 #include "nsDOMWorkerPool.h"
 #include "nsDOMWorkerSecurityManager.h"
 #include "nsDOMThreadService.h"
 #include "nsDOMWorkerTimeout.h"
 
 #define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
 
-nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader()
-: mWorker(nsnull),
+nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader(nsDOMWorker* aWorker)
+: nsDOMWorkerFeature(aWorker),
   mTarget(nsnull),
-  mCx(NULL),
   mScriptCount(0),
-  mCanceled(PR_FALSE),
-  mTrackedByWorker(PR_FALSE)
+  mCanceled(PR_FALSE)
 {
   // Created on worker thread.
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aWorker, "Null worker!");
 }
 
-nsDOMWorkerScriptLoader::~nsDOMWorkerScriptLoader()
-{
-  // Can't touch mWorker's lock
-  if (!mCanceled) {
-    // Destroyed on worker thread, unless canceled (and then who knows!).
-    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-    if (mTrackedByWorker) {
-      jsrefcount suspendDepth = 0;
-      if (mCx) {
-        suspendDepth = JS_SuspendRequest(mCx);
-      }
-
-      nsAutoLock lock(mWorker->Lock());
-  #ifdef DEBUG
-      PRBool removed =
-  #endif
-      mWorker->mScriptLoaders.RemoveElement(this);
-      NS_ASSERTION(removed, "Something is wrong here!");
-
-      if (mCx) {
-        JS_ResumeRequest(mCx, suspendDepth);
-      }
-    }
-  }
-}
-
-NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerScriptLoader, nsRunnable,
+NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerScriptLoader, nsDOMWorkerFeature,
+                                                      nsIRunnable,
                                                       nsIStreamLoaderObserver)
 
 nsresult
-nsDOMWorkerScriptLoader::LoadScripts(nsDOMWorkerThread* aWorker,
-                                     JSContext* aCx,
+nsDOMWorkerScriptLoader::LoadScripts(JSContext* aCx,
                                      const nsTArray<nsString>& aURLs)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aWorker, "Null worker!");
   NS_ASSERTION(aCx, "Null context!");
 
-  NS_ASSERTION(!mWorker, "Not designed to be used more than once!");
-
-  mWorker = aWorker;
-  mCx = aCx;
-
   mTarget = NS_GetCurrentThread();
   NS_ASSERTION(mTarget, "This should never be null!");
 
-  {
-    JSAutoSuspendRequest asr(aCx);
-
-    nsAutoLock lock(mWorker->Lock());
-
-    if (mWorker->IsCanceled()) {
-      return NS_ERROR_ABORT;
-    }
-
-    mTrackedByWorker = nsnull != mWorker->mScriptLoaders.AppendElement(this);
-    NS_ASSERTION(mTrackedByWorker, "Failed to add loader to worker's array!");
-  }
-
   if (mCanceled) {
     return NS_ERROR_ABORT;
   }
 
   mScriptCount = aURLs.Length();
   if (!mScriptCount) {
     return NS_ERROR_INVALID_ARG;
   }
@@ -168,60 +121,47 @@ nsDOMWorkerScriptLoader::LoadScripts(nsD
     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();
-
-  {
-    JSAutoSuspendRequest asr(aCx);
-    nsAutoLock lock(mWorker->Lock());
-#ifdef DEBUG
-    PRBool removed =
-#endif
-    mWorker->mScriptLoaders.RemoveElement(this);
-    NS_ASSERTION(removed, "Something is wrong here!");
-    mTrackedByWorker = PR_FALSE;
-   }
-
+  nsresult rv = DoRunLoop(aCx);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   // Verify that all scripts downloaded and compiled.
-  rv = VerifyScripts();
+  rv = VerifyScripts(aCx);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  rv = ExecuteScripts();
+  rv = ExecuteScripts(aCx);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
-nsDOMWorkerScriptLoader::LoadScript(nsDOMWorkerThread* aWorker,
-                                    JSContext* aCx,
+nsDOMWorkerScriptLoader::LoadScript(JSContext* aCx,
                                     const nsString& aURL)
 {
   nsAutoTArray<nsString, 1> url;
   url.AppendElement(aURL);
 
-  return LoadScripts(aWorker, aCx, url);
+  return LoadScripts(aCx, url);
 }
 
 nsresult
-nsDOMWorkerScriptLoader::DoRunLoop()
+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);
@@ -229,31 +169,33 @@ nsDOMWorkerScriptLoader::DoRunLoop()
 
   if (!(done || mCanceled)) {
     // Since we're going to lock up this thread we might as well allow the
     // thread service to schedule another worker on a new thread.
     nsDOMThreadService* threadService = nsDOMThreadService::get();
     PRBool changed = NS_SUCCEEDED(threadService->ChangeThreadPoolMaxThreads(1));
 
     while (!(done || mCanceled)) {
-      JSAutoSuspendRequest asr(mCx);
+      JSAutoSuspendRequest asr(aCx);
       NS_ProcessNextEvent(mTarget);
     }
 
     if (changed) {
       threadService->ChangeThreadPoolMaxThreads(-1);
     }
   }
 
   return mCanceled ? NS_ERROR_ABORT : NS_OK;
 }
 
 nsresult
-nsDOMWorkerScriptLoader::VerifyScripts()
+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) {
       continue;
@@ -269,52 +211,70 @@ nsDOMWorkerScriptLoader::VerifyScripts()
     // 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.
 
-    // Only throw an error there is no other pending exception.
-    if (!JS_IsExceptionPending(mCx)) {
+    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:
+          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(mCx, "Failed to compile script: %s", url.get());
+      JS_ReportError(aCx, message, url.get(), loadInfo.result);
     }
     break;
   }
 
   return rv;
 }
 
 nsresult
-nsDOMWorkerScriptLoader::ExecuteScripts()
+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);
+
     JSScript* script =
-      static_cast<JSScript*>(JS_GetPrivate(mCx, loadInfo.scriptObj));
+      static_cast<JSScript*>(JS_GetPrivate(aCx, loadInfo.scriptObj));
     NS_ASSERTION(script, "This shouldn't ever be null!");
 
     JSObject* global = mWorker->mGlobal ?
                        mWorker->mGlobal :
-                       JS_GetGlobalObject(mCx);
+                       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(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
+      JS_SetOptions(aCx, JS_GetOptions(aCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
 
     jsval val;
-    PRBool success = JS_ExecuteScript(mCx, global, script, &val);
+    PRBool success = JS_ExecuteScript(aCx, global, script, &val);
 
-    JS_SetOptions(mCx, oldOpts);
+    JS_SetOptions(aCx, oldOpts);
 
     if (!success) {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
 }
 
@@ -443,18 +403,21 @@ nsDOMWorkerScriptLoader::OnStreamComplet
 }
 
 nsresult
 nsDOMWorkerScriptLoader::RunInternal()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   // Things we need to make all this work...
-  nsIDocument* parentDoc = mWorker->Pool()->ParentDocument();
-  NS_ASSERTION(parentDoc, "Null parent document?!");
+  nsCOMPtr<nsIDocument> parentDoc = mWorker->Pool()->ParentDocument();
+  if (!parentDoc) {
+    // Must have been canceled.
+    return NS_ERROR_ABORT;
+  }
 
   // All of these can potentially be null, but that should be ok. We'll either
   // succeed without them or fail below.
   nsIURI* parentBaseURI = parentDoc->GetBaseURI();
   nsCOMPtr<nsILoadGroup> loadGroup(parentDoc->GetDocumentLoadGroup());
   nsCOMPtr<nsIIOService> ios(do_GetIOService());
 
   for (PRUint32 index = 0; index < mScriptCount; index++) {
@@ -467,19 +430,17 @@ nsDOMWorkerScriptLoader::RunInternal()
                                                    parentBaseURI);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
     NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
 
-    rv =
-      secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri,
-                                        nsIScriptSecurityManager::ALLOW_CHROME);
+    rv = secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri, 0);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
     rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT,
                                    uri,
                                    parentDoc->NodePrincipal(),
@@ -608,18 +569,17 @@ nsDOMWorkerScriptLoader::OnStreamComplet
   }
   else {
     // This will help callers figure out what their script url resolved to in
     // case of errors.
     loadInfo.url.Assign(NS_ConvertUTF8toUTF16(filename));
   }
 
   nsRefPtr<ScriptCompiler> compiler =
-    new ScriptCompiler(this, mCx, loadInfo.scriptText, filename,
-                       loadInfo.scriptObj);
+    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);
 
@@ -658,24 +618,24 @@ nsDOMWorkerScriptLoader::NotifyDone()
 
   mDoneRunnable = nsnull;
 }
 
 void
 nsDOMWorkerScriptLoader::SuspendWorkerEvents()
 {
   NS_ASSERTION(mWorker, "No worker yet!");
-  mWorker->SuspendTimeouts();
+  mWorker->SuspendFeatures();
 }
 
 void
 nsDOMWorkerScriptLoader::ResumeWorkerEvents()
 {
   NS_ASSERTION(mWorker, "No worker yet!");
-  mWorker->ResumeTimeouts();
+  mWorker->ResumeFeatures();
 }
 
 nsDOMWorkerScriptLoader::
 ScriptLoaderRunnable::ScriptLoaderRunnable(nsDOMWorkerScriptLoader* aLoader)
 : mRevoked(PR_FALSE),
   mLoader(aLoader)
 {
   nsAutoLock lock(aLoader->Lock());
@@ -694,35 +654,35 @@ ScriptLoaderRunnable::~ScriptLoaderRunna
 #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,
-                               JSContext* aCx,
                                const nsString& aScriptText,
                                const nsCString& aFilename,
                                nsAutoJSObjectHolder& aScriptObj)
 : ScriptLoaderRunnable(aLoader),
-  mCx(aCx),
   mScriptText(aScriptText),
   mFilename(aFilename),
   mScriptObj(aScriptObj)
 {
-  NS_ASSERTION(aCx, "Null context!");
   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!");
@@ -730,41 +690,44 @@ nsDOMWorkerScriptLoader::ScriptCompiler:
   if (mRevoked) {
     return NS_OK;
   }
 
   NS_ASSERTION(!mScriptObj, "Already have a script object?!");
   NS_ASSERTION(mScriptObj.IsHeld(), "Not held?!");
   NS_ASSERTION(!mScriptText.IsEmpty(), "Shouldn't have empty source here!");
 
-  JSAutoRequest ar(mCx);
+  JSContext* cx = nsDOMThreadService::GetCurrentContext();
+  NS_ENSURE_STATE(cx);
 
-  JSObject* global = JS_GetGlobalObject(mCx);
+  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(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
+    JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT);
 
   JSPrincipals* principal = nsDOMWorkerSecurityManager::WorkerPrincipal();
 
   JSScript* script =
-    JS_CompileUCScriptForPrincipals(mCx, global, principal,
+    JS_CompileUCScriptForPrincipals(cx, global, principal,
                                     reinterpret_cast<const jschar*>
                                                (mScriptText.BeginReading()),
                                     mScriptText.Length(), mFilename.get(), 1);
 
-  JS_SetOptions(mCx, oldOpts);
+  JS_SetOptions(cx, oldOpts);
 
   if (!script) {
     return NS_ERROR_FAILURE;
   }
 
-  mScriptObj = JS_NewScriptObject(mCx, script);
+  mScriptObj = JS_NewScriptObject(cx, script);
   NS_ENSURE_STATE(mScriptObj);
 
   return NS_OK;
 }
 
 nsDOMWorkerScriptLoader::
 ScriptLoaderDone::ScriptLoaderDone(nsDOMWorkerScriptLoader* aLoader,
                                    volatile PRBool* aDoneFlag)
--- a/dom/src/threads/nsDOMWorkerScriptLoader.h
+++ b/dom/src/threads/nsDOMWorkerScriptLoader.h
@@ -35,34 +35,35 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __NSDOMWORKERSCRIPTLOADER_H__
 #define __NSDOMWORKERSCRIPTLOADER_H__
 
 // Bases
-#include "nsThreadUtils.h"
+#include "nsIRunnable.h"
 #include "nsIStreamLoader.h"
 
 // Interfaces
 #include "nsIChannel.h"
 #include "nsIURI.h"
 
 // Other includes
 #include "jsapi.h"
 #include "nsAutoPtr.h"
 #include "nsAutoJSObjectHolder.h"
 #include "nsCOMPtr.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 #include "prlock.h"
 
-// DOMWorker includes
-#include "nsDOMWorkerThread.h"
+#include "nsDOMWorker.h"
+
+class nsIThread;
 
 /**
  * This class takes a list of script URLs, downloads the scripts, compiles the
  * scripts, and then finally executes them. Due to platform limitations all
  * network operations must happen on the main thread so this object sends events
  * back and forth from the worker thread to the main thread. The flow goes like
  * this:
  *
@@ -81,47 +82,45 @@
  *                     downloads completed.
  *  7. (Worker thread) After all loads complete and all compilation succeeds
  *                     the scripts are executed in the order that the URLs were
  *                     given to LoadScript(s).
  *
  * Currently if *anything* after 2 fails then we cancel any pending loads and
  * bail out entirely.
  */
-class nsDOMWorkerScriptLoader : public nsRunnable,
+class nsDOMWorkerScriptLoader : public nsDOMWorkerFeature,
+                                public nsIRunnable,
                                 public nsIStreamLoaderObserver
 {
   friend class AutoSuspendWorkerEvents;
-  friend class nsDOMWorkerFunctions;
-  friend class nsDOMWorkerThread;
   friend class ScriptLoaderRunnable;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIRUNNABLE
   NS_DECL_NSISTREAMLOADEROBSERVER
 
-  nsDOMWorkerScriptLoader();
+  nsDOMWorkerScriptLoader(nsDOMWorker* aWorker);
 
-  nsresult LoadScripts(nsDOMWorkerThread* aWorker,
-                       JSContext* aCx,
+  nsresult LoadScripts(JSContext* aCx,
                        const nsTArray<nsString>& aURLs);
 
-  nsresult LoadScript(nsDOMWorkerThread* aWorker,
-                       JSContext* aCx,
-                       const nsString& aURL);
+  nsresult LoadScript(JSContext* aCx,
+                      const nsString& aURL);
 
-  void Cancel();
+  virtual void Cancel();
 
 private:
-  ~nsDOMWorkerScriptLoader();
+  ~nsDOMWorkerScriptLoader() { }
+
 
-  nsresult DoRunLoop();
-  nsresult VerifyScripts();
-  nsresult ExecuteScripts();
+  nsresult DoRunLoop(JSContext* aCx);
+  nsresult VerifyScripts(JSContext* aCx);
+  nsresult ExecuteScripts(JSContext* aCx);
 
   nsresult RunInternal();
 
   nsresult OnStreamCompleteInternal(nsIStreamLoader* aLoader,
                                     nsISupports* aContext,
                                     nsresult aStatus,
                                     PRUint32 aStringLen,
                                     const PRUint8* aString);
@@ -130,18 +129,21 @@ private:
 
   void SuspendWorkerEvents();
   void ResumeWorkerEvents();
 
   PRLock* Lock() {
     return mWorker->Lock();
   }
 
-  class ScriptLoaderRunnable : public nsRunnable
+  class ScriptLoaderRunnable : public nsIRunnable
   {
+  public:
+    NS_DECL_ISUPPORTS
+
   protected:
     // Meant to be inherited.
     ScriptLoaderRunnable(nsDOMWorkerScriptLoader* aLoader);
     virtual ~ScriptLoaderRunnable();
 
   public:
     void Revoke();
 
@@ -153,23 +155,21 @@ private:
   };
 
   class ScriptCompiler : public ScriptLoaderRunnable
   {
   public:
     NS_DECL_NSIRUNNABLE
 
     ScriptCompiler(nsDOMWorkerScriptLoader* aLoader,
-                   JSContext* aCx,
                    const nsString& aScriptText,
                    const nsCString& aFilename,
                    nsAutoJSObjectHolder& aScriptObj);
 
   private:
-    JSContext* mCx;
     nsString mScriptText;
     nsCString mFilename;
     nsAutoJSObjectHolder& mScriptObj;
   };
 
   class ScriptLoaderDone : public ScriptLoaderRunnable
   {
   public:
@@ -200,25 +200,22 @@ private:
     nsString scriptText;
     PRBool done;
     nsresult result;
     nsCOMPtr<nsIURI> finalURI;
     nsCOMPtr<nsIChannel> channel;
     nsAutoJSObjectHolder scriptObj;
   };
 
-  nsDOMWorkerThread* mWorker;
   nsIThread* mTarget;
-  JSContext* mCx;
 
   nsRefPtr<ScriptLoaderDone> mDoneRunnable;
 
   PRUint32 mScriptCount;
   nsTArray<ScriptLoadInfo> mLoadInfos;
 
-  PRPackedBool mCanceled;
-  PRPackedBool mTrackedByWorker;
-
   // Protected by mWorker's lock!
   nsTArray<ScriptLoaderRunnable*> mPendingRunnables;
+
+  PRPackedBool mCanceled;
 };
 
 #endif /* __NSDOMWORKERSCRIPTLOADER_H__ */
deleted file mode 100644
--- a/dom/src/threads/nsDOMWorkerThread.cpp
+++ /dev/null
@@ -1,984 +0,0 @@
-/* -*- 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 "nsDOMWorkerThread.h"
-
-// Interfaces
-#include "nsIDOMClassInfo.h"
-#include "nsIJSContextStack.h"
-#include "nsIJSRuntimeService.h"
-#include "nsIScriptContext.h"
-#include "nsIXPConnect.h"
-
-// Other includes
-#ifdef MOZ_SHARK
-#include "jsdbgapi.h"
-#endif
-#include "nsAutoLock.h"
-#include "nsContentUtils.h"
-#include "nsJSUtils.h"
-#include "nsJSEnvironment.h"
-#include "nsThreadUtils.h"
-
-// DOMWorker includes
-#include "nsDOMWorkerPool.h"
-#include "nsDOMWorkerScriptLoader.h"
-#include "nsDOMWorkerSecurityManager.h"
-#include "nsDOMThreadService.h"
-#include "nsDOMWorkerTimeout.h"
-#include "nsDOMWorkerXHR.h"
-
-#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
-
-// XXX Could make these functions of nsDOMWorkerThread instead.
-class nsDOMWorkerFunctions
-{
-public:
-  // Same as window.dump().
-  static JSBool Dump(JSContext* aCx, JSObject* aObj, uintN aArgc, jsval* aArgv,
-                     jsval* aRval);
-
-  // Debug-only version of window.dump(), like the JS component loader has.
-  static JSBool DebugDump(JSContext* aCx, JSObject* aObj, uintN aArgc,
-                          jsval* aArgv, jsval* aRval);
-
-  // Same as nsIDOMWorkerThread::PostMessage
-  static JSBool PostMessage(JSContext* aCx, JSObject* aObj, uintN aArgc,
-                            jsval* aArgv, jsval* aRval);
-
-  // Same as window.setTimeout().
-  static JSBool SetTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
-                           jsval* aArgv, jsval* aRval) {
-    return MakeTimeout(aCx, aObj, aArgc, aArgv, aRval, PR_FALSE);
-  }
-
-  // Same as window.setInterval().
-  static JSBool SetInterval(JSContext* aCx, JSObject* aObj, uintN aArgc,
-                            jsval* aArgv, jsval* aRval) {
-    return MakeTimeout(aCx, aObj, aArgc, aArgv, aRval, PR_TRUE);
-  }
-
-  // Used for both clearTimeout() and clearInterval().
-  static JSBool KillTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
-                            jsval* aArgv, jsval* aRval);
-
-  static JSBool LoadScripts(JSContext* aCx, JSObject* aObj, uintN aArgc,
-                            jsval* aArgv, jsval* aRval);
-
-  static JSBool NewXMLHttpRequest(JSContext* aCx, JSObject* aObj, uintN aArgc,
-                                  jsval* aArgv, jsval* aRval);
-
-private:
-  // Internal helper for SetTimeout and SetInterval.
-  static JSBool MakeTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
-                            jsval* aArgv, jsval* aRval, PRBool aIsInterval);
-};
-
-JSBool
-nsDOMWorkerFunctions::Dump(JSContext* aCx,
-                           JSObject* /* aObj */,
-                           uintN aArgc,
-                           jsval* aArgv,
-                           jsval* /* aRval */)
-{
-  // XXX Expose this to the JS console? Only if that DOM pref is set?
-
-  JSString* str;
-  if (aArgc && (str = JS_ValueToString(aCx, aArgv[0])) && str) {
-    nsDependentJSString string(str);
-    fputs(NS_ConvertUTF16toUTF8(nsDependentJSString(str)).get(), stderr);
-    fflush(stderr);
-  }
-  return JS_TRUE;
-}
-
-JSBool
-nsDOMWorkerFunctions::DebugDump(JSContext* aCx,
-                                JSObject* aObj,
-                                uintN aArgc,
-                                jsval* aArgv,
-                                jsval* aRval)
-{
-#ifdef DEBUG
-  return nsDOMWorkerFunctions::Dump(aCx, aObj, aArgc, aArgv, aRval);
-#else
-  return JS_TRUE;
-#endif
-}
-
-JSBool
-nsDOMWorkerFunctions::PostMessage(JSContext* aCx,
-                                  JSObject* /* aObj */,
-                                  uintN aArgc,
-                                  jsval* aArgv,
-                                  jsval* /* aRval */)
-{
-  nsDOMWorkerThread* worker =
-    static_cast<nsDOMWorkerThread*>(JS_GetContextPrivate(aCx));
-  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
-
-  if (worker->IsCanceled()) {
-    return JS_FALSE;
-  }
-
-  nsRefPtr<nsDOMWorkerPool> pool = worker->Pool();
-  NS_ASSERTION(pool, "Shouldn't ever be null!");
-
-  nsresult rv;
-
-  JSString* str;
-  if (aArgc && (str = JS_ValueToString(aCx, aArgv[0])) && str) {
-    rv = pool->PostMessageInternal(nsDependentJSString(str), worker);
-  }
-  else {
-    rv = pool->PostMessageInternal(EmptyString(), worker);
-  }
-
-  if (NS_FAILED(rv)) {
-    JS_ReportError(aCx, "Failed to post message!");
-    return JS_FALSE;
-  }
-
-  return JS_TRUE;
-}
-
-JSBool
-nsDOMWorkerFunctions::MakeTimeout(JSContext* aCx,
-                                  JSObject* /* aObj */,
-                                  uintN aArgc,
-                                  jsval* aArgv,
-                                  jsval* aRval,
-                                  PRBool aIsInterval)
-{
-  nsDOMWorkerThread* worker =
-    static_cast<nsDOMWorkerThread*>(JS_GetContextPrivate(aCx));
-  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
-
-  if (worker->IsCanceled()) {
-    return JS_FALSE;
-  }
-
-  PRUint32 id = ++worker->mNextTimeoutId;
-
-  nsAutoPtr<nsDOMWorkerTimeout>
-    timeout(new nsDOMWorkerTimeout(worker, id));
-  if (!timeout) {
-    JS_ReportOutOfMemory(aCx);
-    return JS_FALSE;
-  }
-
-  nsresult rv = timeout->Init(aCx, aArgc, aArgv, aIsInterval);
-  if (NS_FAILED(rv)) {
-    JS_ReportError(aCx, "Failed to initialize timeout!");
-    return JS_FALSE;
-  }
-
-  timeout.forget();
-
-  *aRval = INT_TO_JSVAL(id);
-  return JS_TRUE;
-}
-
-JSBool
-nsDOMWorkerFunctions::KillTimeout(JSContext* aCx,
-                                  JSObject* /* aObj */,
-                                  uintN aArgc,
-                                  jsval* aArgv,
-                                  jsval* /* aRval */)
-{
-  nsDOMWorkerThread* worker =
-    static_cast<nsDOMWorkerThread*>(JS_GetContextPrivate(aCx));
-  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
-
-  // A canceled worker should have already killed all timeouts.
-  if (worker->IsCanceled()) {
-    return JS_TRUE;
-  }
-
-  if (!aArgc) {
-    JS_ReportError(aCx, "Function requires at least 1 parameter");
-    return JS_FALSE;
-  }
-
-  uint32 id;
-  if (!JS_ValueToECMAUint32(aCx, aArgv[0], &id)) {
-    JS_ReportError(aCx, "First argument must be a timeout id");
-    return JS_FALSE;
-  }
-
-  worker->CancelTimeout(PRUint32(id));
-  return JS_TRUE;
-}
-
-JSBool
-nsDOMWorkerFunctions::LoadScripts(JSContext* aCx,
-                                  JSObject* /* aObj */,
-                                  uintN aArgc,
-                                  jsval* aArgv,
-                                  jsval* /* aRval */)
-{
-  nsDOMWorkerThread* worker =
-    static_cast<nsDOMWorkerThread*>(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 must have at least one argument!");
-    return JS_FALSE;
-  }
-
-  nsAutoTArray<nsString, 5> urls;
-
-  if (!urls.SetCapacity((PRUint32)aArgc)) {
-    JS_ReportOutOfMemory(aCx);
-    return JS_FALSE;
-  }
-
-  for (uintN index = 0; index < aArgc; index++) {
-    jsval val = aArgv[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!");
-
-    newURL->Assign(nsDependentJSString(str));
-  }
-
-  nsRefPtr<nsDOMWorkerScriptLoader> loader = new nsDOMWorkerScriptLoader();
-  if (!loader) {
-    JS_ReportOutOfMemory(aCx);
-    return JS_FALSE;
-  }
-
-  nsresult rv = loader->LoadScripts(worker, aCx, urls);
-  if (NS_FAILED(rv)) {
-    return JS_FALSE;
-  }
-
-  return JS_TRUE;
-}
-
-JSBool
-nsDOMWorkerFunctions::NewXMLHttpRequest(JSContext* aCx,
-                                        JSObject* aObj,
-                                        uintN aArgc,
-                                        jsval* /* aArgv */,
-                                        jsval* aRval)
-{
-  nsDOMWorkerThread* worker =
-    static_cast<nsDOMWorkerThread*>(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, "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 XHR!");
-    return JS_FALSE;
-  }
-
-  nsCOMPtr<nsISupports> xhrSupports;
-  xhr->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(xhrSupports));
-  NS_ASSERTION(xhrSupports, "Impossible!");
-
-  nsIXPConnect* xpc = nsContentUtils::XPConnect();
-
-  nsCOMPtr<nsIXPConnectJSObjectHolder> xhrWrapped;
-  rv = xpc->WrapNative(aCx, aObj, xhrSupports, NS_GET_IID(nsIXMLHttpRequest),
-                       getter_AddRefs(xhrWrapped));
-  if (NS_FAILED(rv)) {
-    JS_ReportError(aCx, "Failed to wrap XHR!");
-    return JS_FALSE;
-  }
-
-  JSObject* xhrJSObj;
-  rv = xhrWrapped->GetJSObject(&xhrJSObj);
-  if (NS_FAILED(rv)) {
-    JS_ReportError(aCx, "Failed to get JSObject!");
-    return JS_FALSE;
-  }
-
-  *aRval = OBJECT_TO_JSVAL(xhrJSObj);
-  return JS_TRUE;
-}
-
-JSFunctionSpec gDOMWorkerFunctions[] = {
-  { "dump",                  nsDOMWorkerFunctions::Dump,              1, 0, 0 },
-  { "debug",                 nsDOMWorkerFunctions::DebugDump,         1, 0, 0 },
-  { "postMessageToPool",     nsDOMWorkerFunctions::PostMessage,       1, 0, 0 },
-  { "setTimeout",            nsDOMWorkerFunctions::SetTimeout,        1, 0, 0 },
-  { "clearTimeout",          nsDOMWorkerFunctions::KillTimeout,       1, 0, 0 },
-  { "setInterval",           nsDOMWorkerFunctions::SetInterval,       1, 0, 0 },
-  { "clearInterval",         nsDOMWorkerFunctions::KillTimeout,       1, 0, 0 },
-  { "loadScripts",           nsDOMWorkerFunctions::LoadScripts,       1, 0, 0 },
-  { "XMLHttpRequest",        nsDOMWorkerFunctions::NewXMLHttpRequest, 0, 0, 0 },
-#ifdef MOZ_SHARK
-  { "startShark",            js_StartShark,                           0, 0, 0 },
-  { "stopShark",             js_StopShark,                            0, 0, 0 },
-  { "connectShark",          js_ConnectShark,                         0, 0, 0 },
-  { "disconnectShark",       js_DisconnectShark,                      0, 0, 0 },
-#endif
-  { nsnull,                  nsnull,                                  0, 0, 0 }
-};
-
-/**
- * An nsISupports that holds a weak ref to the worker. The worker owns the
- * thread context so we don't have to worry about nulling this out ever.
- */
-class nsDOMWorkerThreadWeakRef : public nsIDOMWorkerThread,
-                                 public nsIClassInfo
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_FORWARD_NSIDOMWORKERTHREAD(mWorker->)
-  NS_FORWARD_NSICLASSINFO(mWorker->)
-
-  nsDOMWorkerThreadWeakRef(nsDOMWorkerThread* aWorker)
-  : mWorker(aWorker) { }
-
-protected:
-  nsDOMWorkerThread* mWorker;
-};
-
-NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerThreadWeakRef, nsIDOMWorkerThread,
-                                                        nsIClassInfo)
-
-/**
- * The 'threadContext' object for a worker's JS global object.
- */
-class nsDOMWorkerThreadContext : public nsIDOMWorkerThreadContext,
-                                 public nsIClassInfo
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMWORKERTHREADCONTEXT
-  NS_DECL_NSICLASSINFO
-
-  nsDOMWorkerThreadContext(nsDOMWorkerThread* aWorker)
-  : mWorker(aWorker) { }
-
-protected:
-  nsDOMWorkerThread* mWorker;
-  nsCOMPtr<nsIDOMWorkerThread> mWeakRef;
-};
-
-NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerThreadContext,
-                              nsIDOMWorkerThreadContext,
-                              nsIClassInfo)
-
-NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerThreadContext,
-                             nsIDOMWorkerThreadContext)
-
-NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerThreadContext)
-
-NS_IMETHODIMP
-nsDOMWorkerThreadContext::GetThisThread(nsIDOMWorkerThread** aThisThread)
-{
-  if (!mWeakRef) {
-    mWeakRef = new nsDOMWorkerThreadWeakRef(mWorker);
-    NS_ENSURE_TRUE(mWeakRef, NS_ERROR_OUT_OF_MEMORY);
-  }
-
-  NS_ADDREF(*aThisThread = mWeakRef);
-  return NS_OK;
-}
-
-nsDOMWorkerThread::nsDOMWorkerThread(nsDOMWorkerPool* aPool,
-                                     const nsAString& aSource,
-                                     PRBool aSourceIsURL)
-: mPool(aPool),
-  mCompiled(PR_FALSE),
-  mCallbackCount(0),
-  mNextTimeoutId(0),
-  mLock(nsnull)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  if (aSourceIsURL) {
-    mSourceURL.Assign(aSource);
-    NS_ASSERTION(!mSourceURL.IsEmpty(), "Empty source url!");
-  }
-  else {
-    mSource.Assign(aSource);
-    NS_ASSERTION(!mSource.IsEmpty(), "Empty source string!");
-  }
-
-  PR_INIT_CLIST(&mTimeouts);
-}
-
-nsDOMWorkerThread::~nsDOMWorkerThread()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  if (!IsCanceled()) {
-    nsRefPtr<nsDOMWorkerPool> pool = Pool();
-    pool->NoteDyingWorker(this);
-  }
-
-  ClearTimeouts();
-
-  if (mLock) {
-    nsAutoLock::DestroyLock(mLock);
-  }
-}
-
-NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerThread, nsIDOMWorkerThread,
-                                                 nsIClassInfo)
-NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerThread, nsIDOMWorkerThread)
-
-NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerThread)
-
-nsresult
-nsDOMWorkerThread::Init()
-{
-  mLock = nsAutoLock::NewLock("nsDOMWorkerThread::mLock");
-  NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
-
-  NS_ASSERTION(!mGlobal, "Already got a global?!");
-
-  JSRuntime* rt;
-  nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRBool success = mGlobal.Hold(rt);
-  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
-
-  // 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 nsRunnable());
-  NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
-
-  rv = nsDOMThreadService::get()->Dispatch(this, runnable);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-// From nsDOMWorkerBase
-nsresult
-nsDOMWorkerThread::HandleMessage(const nsAString& aMessage,
-                                 nsDOMWorkerBase* aSource)
-{
-  nsCOMPtr<nsIDOMWorkerMessageListener> messageListener = GetMessageListener();
-  if (!messageListener) {
-    LOG(("Message received on a worker with no listener!"));
-    return NS_OK;
-  }
-
-  // We have to call this manually because XPConnect will replace our error
-  // reporter with its own and we won't properly notify the pool of any
-  // unhandled exceptions...
-
-  JSContext* cx;
-  nsresult rv =
-    nsDOMThreadService::ThreadJSContextStack()->GetSafeJSContext(&cx);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  JSAutoRequest ar(cx);
-
-  if (JS_IsExceptionPending(cx)) {
-    JS_ClearPendingException(cx);
-  }
-
-  // Get a JS string for the message.
-  JSString* message = JS_NewUCStringCopyN(cx, (jschar*)aMessage.BeginReading(),
-                                          aMessage.Length());
-  NS_ENSURE_TRUE(message, NS_ERROR_FAILURE);
-
-  // Root it
-  jsval messageVal = STRING_TO_JSVAL(message);
-  nsAutoGCRoot rootedMessage(&messageVal, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsIXPConnect* xpc = nsContentUtils::XPConnect();
-
-  nsCOMPtr<nsISupports> source;
-  aSource->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(source));
-  NS_ASSERTION(source, "Impossible!");
-
-  // Wrap the source thread.
-  nsCOMPtr<nsIXPConnectJSObjectHolder> wrappedThread;
-  rv = xpc->WrapNative(cx, mGlobal, source, NS_GET_IID(nsISupports),
-                       getter_AddRefs(wrappedThread));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  JSObject* sourceThread;
-  rv = wrappedThread->GetJSObject(&sourceThread);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Set up our arguments.
-  jsval argv[2] = {
-    STRING_TO_JSVAL(message),
-    OBJECT_TO_JSVAL(sourceThread)
-  };
-
-  // Get the listener object out of our wrapped listener.
-  nsCOMPtr<nsIXPConnectJSObjectHolder> wrappedListener =
-    do_QueryInterface(messageListener);
-  NS_ENSURE_TRUE(wrappedListener, NS_ERROR_NO_INTERFACE);
-
-  JSObject* listener;
-  rv = wrappedListener->GetJSObject(&listener);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // And call it.
-  jsval rval;
-  PRBool success = JS_CallFunctionValue(cx, mGlobal, OBJECT_TO_JSVAL(listener),
-                                        2, argv, &rval);
-  if (!success && JS_IsExceptionPending(cx)) {
-    // Make sure any pending exceptions are converted to errors for the pool.
-    JS_ReportPendingException(cx);
-  }
-
-  return NS_OK;
-}
-
-// From nsDOMWorkerBase
-nsresult
-nsDOMWorkerThread::DispatchMessage(nsIRunnable* aRunnable)
-{
-  nsresult rv = nsDOMThreadService::get()->Dispatch(this, aRunnable);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-void
-nsDOMWorkerThread::Cancel()
-{
-  nsDOMWorkerBase::Cancel();
-
-  // Do this before waiting on the thread service below!
-  CancelScriptLoaders();
-  CancelXHRs();
-
-  // If we're suspended there's a good chance that we're already paused waiting
-  // on the pool's monitor. Waiting on the thread service's lock will deadlock.
-  if (!IsSuspended()) {
-    nsDOMThreadService::get()->WaitForCanceledWorker(this);
-  }
-
-  ClearTimeouts();
-}
-
-void
-nsDOMWorkerThread::Suspend()
-{
-  nsDOMWorkerBase::Suspend();
-  SuspendTimeouts();
-}
-
-void
-nsDOMWorkerThread::Resume()
-{
-  nsDOMWorkerBase::Resume();
-  ResumeTimeouts();
-}
-
-PRBool
-nsDOMWorkerThread::SetGlobalForContext(JSContext* aCx)
-{
-  PRBool success = CompileGlobalObject(aCx);
-  if (!success) {
-    return PR_FALSE;
-  }
-
-  JS_SetGlobalObject(aCx, mGlobal);
-  return PR_TRUE;
-}
-
-PRBool
-nsDOMWorkerThread::CompileGlobalObject(JSContext* aCx)
-{
-  if (mGlobal) {
-    return PR_TRUE;
-  }
-
-  if (mCompiled) {
-    // Don't try to recompile a bad script.
-    return PR_FALSE;
-  }
-
-  mCompiled = PR_TRUE;
-
-  JSAutoRequest ar(aCx);
-
-  JSObject* global = JS_NewObject(aCx, nsnull, nsnull, nsnull);
-  NS_ENSURE_TRUE(global, PR_FALSE);
-
-  NS_ASSERTION(!JS_GetGlobalObject(aCx), "Global object should be unset!");
-
-  // This call will root global.
-  PRBool success = JS_InitStandardClasses(aCx, global);
-  NS_ENSURE_TRUE(success, PR_FALSE);
-
-  // Set up worker thread functions
-  success = JS_DefineFunctions(aCx, global, gDOMWorkerFunctions);
-  NS_ENSURE_TRUE(success, PR_FALSE);
-
-  nsRefPtr<nsDOMWorkerThreadContext>
-    context(new nsDOMWorkerThreadContext(this));
-  NS_ENSURE_TRUE(context, NS_ERROR_OUT_OF_MEMORY);
-
-  nsIXPConnect* xpc = nsContentUtils::XPConnect();
-  nsresult rv = xpc->InitClasses(aCx, global);
-  NS_ENSURE_SUCCESS(rv, PR_FALSE);
-
-  // XXX Fix this!
-  success = JS_DeleteProperty(aCx, global, "Components");
-  NS_ENSURE_TRUE(success, PR_FALSE);
-
-  nsCOMPtr<nsIXPConnectJSObjectHolder> contextWrapper;
-  rv = xpc->WrapNative(aCx, global,
-                       NS_ISUPPORTS_CAST(nsIDOMWorkerThreadContext*, context),
-                       NS_GET_IID(nsIDOMWorkerThreadContext),
-                       getter_AddRefs(contextWrapper));
-  NS_ENSURE_SUCCESS(rv, PR_FALSE);
-
-  JSObject* contextObj;
-  rv = contextWrapper->GetJSObject(&contextObj);
-  NS_ENSURE_SUCCESS(rv, PR_FALSE);
-
-  // Set up a name for our worker object
-  success = JS_DefineProperty(aCx, global, "threadContext",
-                              OBJECT_TO_JSVAL(contextObj), nsnull, nsnull,
-                              JSPROP_ENUMERATE);
-  NS_ENSURE_TRUE(success, PR_FALSE);
-
-  jsval val;
-
-  // From here on out we have to remember to null mGlobal if something fails!
-  mGlobal = global;
-
-  if (mSource.IsEmpty()) {
-    NS_ASSERTION(!mSourceURL.IsEmpty(), "Must have a url here!");
-
-    nsRefPtr<nsDOMWorkerScriptLoader> loader = new nsDOMWorkerScriptLoader();
-    NS_ASSERTION(loader, "Out of memory!");
-    if (!loader) {
-      mGlobal = NULL;
-      return PR_FALSE;
-    }
-
-    rv = loader->LoadScript(this, aCx, mSourceURL);
-    JS_ReportPendingException(aCx);
-    if (NS_FAILED(rv)) {
-      mGlobal = NULL;
-      return PR_FALSE;
-    }
-  }
-  else {
-    NS_ASSERTION(!mSource.IsEmpty(), "No source text!");
-
-    JSPrincipals* principal = nsDOMWorkerSecurityManager::WorkerPrincipal();
-
-    // Evaluate and execute the script
-    success = JS_EvaluateUCScriptForPrincipals(aCx, global, principal,
-                                               reinterpret_cast<const jschar*>
-                                                          (mSource.get()), 
-                                               mSource.Length(),
-                                               "DOMWorker inline script", 1,
-                                               &val);
-    if (!success) {
-      mGlobal = NULL;
-      return PR_FALSE;
-    }
-  }
-
-  // See if the message listener function was defined.
-  nsCOMPtr<nsIDOMWorkerMessageListener> listener;
-  if (JS_LookupProperty(aCx, global, "messageListener", &val) &&
-      JSVAL_IS_OBJECT(val) &&
-      NS_SUCCEEDED(xpc->WrapJS(aCx, JSVAL_TO_OBJECT(val),
-                               NS_GET_IID(nsIDOMWorkerMessageListener),
-                               getter_AddRefs(listener)))) {
-    SetMessageListener(listener);
-  }
-
-  return PR_TRUE;
-}
-
-nsDOMWorkerTimeout*
-nsDOMWorkerThread::FirstTimeout()
-{
-  // Only called within the lock!
-  PRCList* first = PR_LIST_HEAD(&mTimeouts);
-  return first == &mTimeouts ?
-                  nsnull :
-                  static_cast<nsDOMWorkerTimeout*>(first);
-}
-
-nsDOMWorkerTimeout*
-nsDOMWorkerThread::NextTimeout(nsDOMWorkerTimeout* aTimeout)
-{
-  // Only called within the lock!
-  nsDOMWorkerTimeout* next =
-    static_cast<nsDOMWorkerTimeout*>(PR_NEXT_LINK(aTimeout));
-  return next == &mTimeouts ? nsnull : next;
-}
-
-PRBool
-nsDOMWorkerThread::AddTimeout(nsDOMWorkerTimeout* aTimeout)
-{
-  // This should only ever be called on the worker thread... but there's no way
-  // to really assert that since we're using a thread pool.
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aTimeout, "Null pointer!");
-
-  PRIntervalTime newInterval = aTimeout->GetInterval();
-
-  if (IsSuspended()) {
-    aTimeout->Suspend(PR_Now());
-  }
-
-  nsAutoLock lock(mLock);
-
-  if (IsCanceled()) {
-    return PR_FALSE;
-  }
-
-  // XXX Currently stored in the order that they should execute (like the window
-  //     timeouts are) but we don't flush all expired timeouts the same way that
-  //     the window does... Either we should or this is unnecessary.
-  for (nsDOMWorkerTimeout* timeout = FirstTimeout();
-       timeout;
-       timeout = NextTimeout(timeout)) {
-    if (timeout->GetInterval() > newInterval) {
-      PR_INSERT_BEFORE(aTimeout, timeout);
-      return PR_TRUE;
-    }
-  }
-
-  PR_APPEND_LINK(aTimeout, &mTimeouts);
-  return PR_TRUE;
-}
-
-void
-nsDOMWorkerThread::RemoveTimeout(nsDOMWorkerTimeout* aTimeout)
-{
-  nsAutoLock lock(mLock);
-
-  PR_REMOVE_LINK(aTimeout);
-}
-
-void
-nsDOMWorkerThread::ClearTimeouts()
-{
-  nsAutoTArray<nsRefPtr<nsDOMWorkerTimeout>, 20> timeouts;
-  {
-    nsAutoLock lock(mLock);
-    for (nsDOMWorkerTimeout* timeout = FirstTimeout();
-         timeout;
-         timeout = NextTimeout(timeout)) {
-      timeouts.AppendElement(timeout);
-    }
-  }
-
-  PRUint32 count = timeouts.Length();
-  for (PRUint32 i = 0; i < count; i++) {
-    timeouts[i]->Cancel();
-  }
-}
-
-void
-nsDOMWorkerThread::CancelTimeout(PRUint32 aId)
-{
-  nsRefPtr<nsDOMWorkerTimeout> foundTimeout;
-  {
-    nsAutoLock lock(mLock);
-    for (nsDOMWorkerTimeout* timeout = FirstTimeout();
-         timeout;
-         timeout = NextTimeout(timeout)) {
-      if (timeout->GetId() == aId) {
-        foundTimeout = timeout;
-        break;
-      }
-    }
-  }
-
-  if (foundTimeout) {
-    foundTimeout->Cancel();
-  }
-}
-
-void
-nsDOMWorkerThread::SuspendTimeouts()
-{
-  nsAutoTArray<nsRefPtr<nsDOMWorkerTimeout>, 20> timeouts;
-  {
-    nsAutoLock lock(mLock);
-    for (nsDOMWorkerTimeout* timeout = FirstTimeout();
-         timeout;
-         timeout = NextTimeout(timeout)) {
-      timeouts.AppendElement(timeout);
-    }
-  }
-
-  PRTime now = PR_Now();
-
-  PRUint32 count = timeouts.Length();
-  for (PRUint32 i = 0; i < count; i++) {
-    timeouts[i]->Suspend(now);
-  }
-}
-
-void
-nsDOMWorkerThread::ResumeTimeouts()
-{
-  nsAutoTArray<nsRefPtr<nsDOMWorkerTimeout>, 20> timeouts;
-  {
-    nsAutoLock lock(mLock);
-    for (nsDOMWorkerTimeout* timeout = FirstTimeout();
-         timeout;
-         timeout = NextTimeout(timeout)) {
-      NS_ASSERTION(timeout->IsSuspended(), "Should be suspended!");
-      timeouts.AppendElement(timeout);
-    }
-  }
-
-  PRTime now = PR_Now();
-
-  PRUint32 count = timeouts.Length();
-  for (PRUint32 i = 0; i < count; i++) {
-    timeouts[i]->Resume(now);
-  }
-}
-
-void
-nsDOMWorkerThread::CancelScriptLoaders()
-{
-  nsAutoTArray<nsDOMWorkerScriptLoader*, 20> loaders;
-
-  // Must call cancel on the loaders outside the lock!
-  {
-    nsAutoLock lock(mLock);
-    loaders.AppendElements(mScriptLoaders);
-
-    // Don't clear mScriptLoaders, they'll remove themselves as they get
-    // destroyed.
-  }
-
-  PRUint32 loaderCount = loaders.Length();
-  for (PRUint32 index = 0; index < loaderCount; index++) {
-    loaders[index]->Cancel();
-  }
-}
-
-PRBool
-nsDOMWorkerThread::AddXHR(nsDOMWorkerXHR* aXHR)
-{
-  nsAutoLock lock(mLock);
-
-  if (IsCanceled()) {
-    return PR_FALSE;
-  }
-
-#ifdef DEBUG
-  PRBool contains = mXHRs.Contains(aXHR);
-  NS_ASSERTION(!contains, "Adding an XHR twice!");
-#endif
-
-  nsDOMWorkerXHR** newElement = mXHRs.AppendElement(aXHR);
-  NS_ENSURE_TRUE(newElement, PR_FALSE);
-
-  return PR_TRUE;
-}
-
-void
-nsDOMWorkerThread::RemoveXHR(nsDOMWorkerXHR* aXHR)
-{
-  nsAutoLock lock(mLock);
-#ifdef DEBUG
-  PRBool removed =
-#endif
-  mXHRs.RemoveElement(aXHR);
-  NS_WARN_IF_FALSE(removed, "Removed an XHR that was never added?!");
-}
-
-void
-nsDOMWorkerThread::CancelXHRs()
-{
-  nsAutoTArray<nsDOMWorkerXHR*, 20> xhrs;
-
-  // Must call Cancel outside the lock!
-  {
-    nsAutoLock lock(mLock);
-    xhrs.AppendElements(mXHRs);
-  }
-
-  PRUint32 xhrCount = xhrs.Length();
-  for (PRUint32 index = 0; index < xhrCount; index++) {
-    xhrs[index]->Cancel();
-  }
-}
-
-NS_IMETHODIMP
-nsDOMWorkerThread::PostMessage(const nsAString& aMessage)
-{
-  nsresult rv = PostMessageInternal(aMessage);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return NS_OK;
-}
deleted file mode 100644
--- a/dom/src/threads/nsDOMWorkerThread.h
+++ /dev/null
@@ -1,212 +0,0 @@
-/* -*- 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 __NSDOMWORKERTHREAD_H__
-#define __NSDOMWORKERTHREAD_H__
-
-// Bases
-#include "nsDOMWorkerBase.h"
-#include "nsIClassInfo.h"
-#include "nsIDOMThreads.h"
-
-// Other includes
-#include "jsapi.h"
-#include "nsAutoJSObjectHolder.h"
-#include "nsCOMPtr.h"
-#include "nsStringGlue.h"
-#include "nsTArray.h"
-#include "nsThreadUtils.h"
-#include "prclist.h"
-#include "prlock.h"
-
-// DOMWorker includes
-#include "nsDOMThreadService.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_ALL_THE_REST(_class)                        \
-NS_IMETHODIMP                                                                 \
-_class::GetHelperForLanguage(PRUint32 _language, nsISupports** _retval)       \
-{                                                                             \
-  *_retval = nsnull;                                                          \
-  return NS_OK;                                                               \
-}                                                                             \
-                                                                              \
-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_ALL_THE_REST(_class)
-
-class nsDOMWorkerPool;
-class nsDOMWorkerScriptLoader;
-class nsDOMWorkerTimeout;
-class nsDOMWorkerXHR;
-
-class nsDOMWorkerThread : public nsDOMWorkerBase,
-                          public nsIDOMWorkerThread,
-                          public nsIClassInfo
-{
-  friend class nsDOMCreateJSContextRunnable;
-  friend class nsDOMWorkerFunctions;
-  friend class nsDOMWorkerPool;
-  friend class nsDOMWorkerRunnable;
-  friend class nsDOMWorkerScriptLoader;
-  friend class nsDOMWorkerTimeout;
-  friend class nsDOMWorkerXHR;
-
-  friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
-
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMWORKERTHREAD
-  NS_DECL_NSICLASSINFO
-
-  nsDOMWorkerThread(nsDOMWorkerPool* aPool,
-                    const nsAString& aSource,
-                    PRBool aSourceIsURL);
-
-  virtual nsDOMWorkerPool* Pool() {
-    NS_ASSERTION(!IsCanceled(), "Don't touch Pool after we've been canceled!");
-    return mPool;
-  }
-
-private:
-  virtual ~nsDOMWorkerThread();
-
-  nsresult Init();
-
-  // For nsDOMWorkerBase
-  virtual nsresult HandleMessage(const nsAString& aMessage,
-                                 nsDOMWorkerBase* aSourceThread);
-
-  // For nsDOMWorkerBase
-  virtual nsresult DispatchMessage(nsIRunnable* aRunnable);
-
-  virtual void Cancel();
-  virtual void Suspend();
-  virtual void Resume();
-
-  PRBool SetGlobalForContext(JSContext* aCx);
-  PRBool CompileGlobalObject(JSContext* aCx);
-
-  inline nsDOMWorkerTimeout* FirstTimeout();
-  inline nsDOMWorkerTimeout* NextTimeout(nsDOMWorkerTimeout* aTimeout);
-
-  PRBool AddTimeout(nsDOMWorkerTimeout* aTimeout);
-  void RemoveTimeout(nsDOMWorkerTimeout* aTimeout);
-  void ClearTimeouts();
-  void CancelTimeout(PRUint32 aId);
-  void SuspendTimeouts();
-  void ResumeTimeouts();
-
-  void CancelScriptLoaders();
-
-  PRBool AddXHR(nsDOMWorkerXHR* aXHR);
-  void RemoveXHR(nsDOMWorkerXHR* aXHR);
-  void CancelXHRs();
-
-  PRLock* Lock() {
-    return mLock;
-  }
-
-  nsDOMWorkerPool* mPool;
-  nsString mSource;
-  nsString mSourceURL;
-
-  nsAutoJSObjectHolder mGlobal;
-  PRBool mCompiled;
-
-  PRUint32 mCallbackCount;
-
-  PRUint32 mNextTimeoutId;
-
-  PRLock* mLock;
-  PRCList mTimeouts;
-
-  nsTArray<nsDOMWorkerScriptLoader*> mScriptLoaders;
-  nsTArray<nsDOMWorkerXHR*> mXHRs;
-};
-
-#endif /* __NSDOMWORKERTHREAD_H__ */
--- a/dom/src/threads/nsDOMWorkerTimeout.cpp
+++ b/dom/src/threads/nsDOMWorkerTimeout.cpp
@@ -42,17 +42,19 @@
 #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"
 
 #define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
 
 #define CONSTRUCTOR_ENSURE_TRUE(_cond, _rv) \
   PR_BEGIN_MACRO \
@@ -68,25 +70,25 @@
 static const char* kSetIntervalStr = "setInterval";
 static const char* kSetTimeoutStr = "setTimeout";
 
 nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc,
                                                        jsval* aArgv,
                                                        nsresult* aRv)
 : mCallback(nsnull),
   mCallbackArgs(nsnull),
-  mCallbackArgsLength(0)
+  mCallbackArgsLength(0),
+  mRuntime(NULL)
 {
   MOZ_COUNT_CTOR(nsDOMWorkerTimeout::FunctionCallback);
 
-  JSRuntime* rt;
-  *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
+  *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&mRuntime);
   NS_ENSURE_SUCCESS(*aRv,);
 
-  PRBool success = JS_AddNamedRootRT(rt, &mCallback,
+  PRBool success = JS_AddNamedRootRT(mRuntime, &mCallback,
                                      "nsDOMWorkerTimeout Callback Object");
   CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
 
   mCallback = aArgv[0];
 
   // We want enough space for an extra lateness arg.
   mCallbackArgsLength = aArgc > 2 ? aArgc - 1 : 1;
 
@@ -97,32 +99,32 @@ nsDOMWorkerTimeout::FunctionCallback::Fu
 
     NS_ERROR("Out of memory!");
     *aRv = NS_ERROR_OUT_OF_MEMORY;
     return;
   }
 
   for (PRUint32 i = 0; i < mCallbackArgsLength - 1; i++) {
     mCallbackArgs[i] = aArgv[i + 2];
-    success = JS_AddNamedRootRT(rt, &mCallbackArgs[i],
+    success = JS_AddNamedRootRT(mRuntime, &mCallbackArgs[i],
                                 "nsDOMWorkerTimeout Callback Arg");
     if (NS_UNLIKELY(!success)) {
       // Set this to i so that the destructor only unroots the right number of
       // values.
       mCallbackArgsLength = i;
 
       NS_WARNING("Failed to add root!");
       *aRv = NS_ERROR_FAILURE;
       return;
     }
   }
 
   // Take care of the last arg.
   mCallbackArgs[mCallbackArgsLength - 1] = 0;
-  success = JS_AddNamedRootRT(rt, &mCallbackArgs[mCallbackArgsLength - 1],
+  success = JS_AddNamedRootRT(mRuntime, &mCallbackArgs[mCallbackArgsLength - 1],
                               "nsDOMWorkerTimeout Callback Final Arg");
   if (NS_UNLIKELY(!success)) {
     // Decrement this so that the destructor only unroots the right number of
     // values.
     mCallbackArgsLength -= 1;
 
     NS_WARNING("Failed to add root!");
     *aRv = NS_ERROR_FAILURE;
@@ -132,27 +134,20 @@ nsDOMWorkerTimeout::FunctionCallback::Fu
   *aRv = NS_OK;
 }
 
 nsDOMWorkerTimeout::FunctionCallback::~FunctionCallback()
 {
   MOZ_COUNT_DTOR(nsDOMWorkerTimeout::FunctionCallback);
 
   if (mCallback) {
-    JSRuntime* rt;
-    nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
-
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Can't unroot callback objects!");
-
-    if (NS_SUCCEEDED(rv)) {
-      for (PRUint32 i = 0; i < mCallbackArgsLength; i++) {
-        JS_RemoveRootRT(rt, &mCallbackArgs[i]);
-      }
-      JS_RemoveRootRT(rt, &mCallback);
+    for (PRUint32 i = 0; i < mCallbackArgsLength; i++) {
+      JS_RemoveRootRT(mRuntime, &mCallbackArgs[i]);
     }
+    JS_RemoveRootRT(mRuntime, &mCallback);
   }
 
   delete [] mCallbackArgs;
 }
 
 nsresult
 nsDOMWorkerTimeout::FunctionCallback::Run(nsDOMWorkerTimeout* aTimeout,
                                           JSContext* aCx)
@@ -173,29 +168,29 @@ nsDOMWorkerTimeout::FunctionCallback::Ru
   return NS_OK;
 }
 
 nsDOMWorkerTimeout::ExpressionCallback::ExpressionCallback(PRUint32 aArgc,
                                                            jsval* aArgv,
                                                            JSContext* aCx,
                                                            nsresult* aRv)
 : mExpression(nsnull),
-  mLineNumber(0)
+  mLineNumber(0),
+  mRuntime(NULL)
 {
   MOZ_COUNT_CTOR(nsDOMWorkerTimeout::ExpressionCallback);
 
   JSString* expr = JS_ValueToString(aCx, aArgv[0]);
   *aRv = expr ? NS_OK : NS_ERROR_FAILURE;
   NS_ENSURE_SUCCESS(*aRv,);
 
-  JSRuntime* rt;
-  *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
+  *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&mRuntime);
   NS_ENSURE_SUCCESS(*aRv,);
 
-  PRBool success = JS_AddNamedRootRT(rt, &mExpression,
+  PRBool success = JS_AddNamedRootRT(mRuntime, &mExpression,
                                      "nsDOMWorkerTimeout Expression");
   CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
 
   mExpression = expr;
 
   // Get the calling location.
   const char* fileName;
   PRUint32 lineNumber;
@@ -207,66 +202,44 @@ nsDOMWorkerTimeout::ExpressionCallback::
   *aRv = NS_OK;
 }
 
 nsDOMWorkerTimeout::ExpressionCallback::~ExpressionCallback()
 {
   MOZ_COUNT_DTOR(nsDOMWorkerTimeout::ExpressionCallback);
 
   if (mExpression) {
-    JSRuntime* rt;
-    nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
-
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Can't unroot callback objects!");
-
-    if (NS_SUCCEEDED(rv)) {
-      JS_RemoveRootRT(rt, &mExpression);
-    }
+    JS_RemoveRootRT(mRuntime, &mExpression);
   }
 }
 
 nsresult
 nsDOMWorkerTimeout::ExpressionCallback::Run(nsDOMWorkerTimeout* aTimeout,
                                             JSContext* aCx)
 {
   NS_ERROR("Not yet implemented!");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker,
+nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorker* aWorker,
                                        PRUint32 aId)
-: mWorker(aWorker),
+: nsDOMWorkerFeature(aWorker, aId),
   mInterval(0),
-  mIsInterval(PR_FALSE),
-  mId(aId),
   mSuspendSpinlock(0),
+  mSuspendInterval(0),
+  mIsInterval(PR_FALSE),
   mIsSuspended(PR_FALSE),
-  mSuspendInterval(0)
-#ifdef DEBUG
-, mFiredOrCanceled(PR_FALSE)
-#endif
+  mSuspendedBeforeStart(PR_FALSE),
+  mStarted(PR_FALSE)
 {
-  MOZ_COUNT_CTOR(nsDOMWorkerTimeout);
   NS_ASSERTION(mWorker, "Need a worker here!");
 }
 
-nsDOMWorkerTimeout::~nsDOMWorkerTimeout()
-{
-  MOZ_COUNT_DTOR(nsDOMWorkerTimeout);
-
-  // If we have a timer then we assume we added ourselves to the thread's list.
-  if (mTimer) {
-    NS_ASSERTION(mFiredOrCanceled || mWorker->IsCanceled(),
-                 "Timeout should have fired or been canceled!");
-
-    mWorker->RemoveTimeout(this);
-  }
-}
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerTimeout, nsITimerCallback)
+NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerTimeout, nsDOMWorkerFeature,
+                                                 nsITimerCallback)
 
 nsresult
 nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
                          PRBool aIsInterval)
 {
   NS_ASSERTION(aCx, "Null pointer!");
   NS_ASSERTION(aArgv, "Null pointer!");
 
@@ -289,16 +262,18 @@ nsDOMWorkerTimeout::Init(JSContext* aCx,
   else {
     // If no interval was specified, treat this like a timeout, to avoid
     // setting an interval of 0 milliseconds.
     aIsInterval = PR_FALSE;
   }
 
   mInterval = interval;
 
+  mIsInterval = aIsInterval;
+
   mTargetTime = PR_Now() + interval * (PRTime)PR_USEC_PER_MSEC;
 
   nsresult rv;
   switch (JS_TypeOfValue(aCx, aArgv[0])) {
     case JSTYPE_FUNCTION:
       mCallback = new FunctionCallback(aArgc, aArgv, &rv);
       NS_ENSURE_TRUE(mCallback, NS_ERROR_OUT_OF_MEMORY);
       NS_ENSURE_SUCCESS(rv, rv);
@@ -310,65 +285,64 @@ nsDOMWorkerTimeout::Init(JSContext* aCx,
       mCallback = new ExpressionCallback(aArgc, aArgv, aCx, &rv);
       NS_ENSURE_TRUE(mCallback, NS_ERROR_OUT_OF_MEMORY);
       NS_ENSURE_SUCCESS(rv, rv);
       break;
 
     default:
       JS_ReportError(aCx, "useless %s call (missing quotes around argument?)",
                      aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
-  
+
       // Return an error that nsGlobalWindow can recognize and turn into NS_OK.
       return NS_ERROR_INVALID_ARG;
   }
 
-  PRInt32 type;
-  if (aIsInterval) {
-    type = nsITimer::TYPE_REPEATING_SLACK;
-  }
-  else {
-    type = nsITimer::TYPE_ONE_SHOT;
-  }
-  mIsInterval = aIsInterval;
-
   nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsIEventTarget* target =
     static_cast<nsIEventTarget*>(nsDOMThreadService::get());
 
   rv = timer->SetTarget(target);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = timer->InitWithCallback(this, interval, type);
+  mTimer.swap(timer);
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerTimeout::Start()
+{
+  if (IsSuspended()) {
+    NS_ASSERTION(mSuspendedBeforeStart, "Bad state!");
+    return NS_OK;
+  }
+
+  PRInt32 type;
+  if (mIsInterval) {
+    type = nsITimer::TYPE_REPEATING_SLACK;
+  }
+  else {
+    type = nsITimer::TYPE_ONE_SHOT;
+  }
+
+  nsresult rv = mTimer->InitWithCallback(this, mInterval, type);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mTimer.swap(timer);
-
-  if (!mWorker->AddTimeout(this)) {
-    // Must have been canceled.
-    mTimer->Cancel();
-    mTimer = nsnull;
-    return NS_ERROR_ABORT;
-  }
-
+  mStarted = PR_TRUE;
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerTimeout::Run()
 {
   NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
   LOG(("Worker [0x%p] running timeout [0x%p] with id %u",
        static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
 
-#ifdef DEBUG
-  mFiredOrCanceled = PR_TRUE;
-#endif
-
   JSContext* cx;
   nsresult rv =
     nsDOMThreadService::ThreadJSContextStack()->GetSafeJSContext(&cx);
   NS_ENSURE_SUCCESS(rv, rv);
 
   JSAutoRequest ar(cx);
 
   rv = mCallback->Run(this, cx);
@@ -386,71 +360,87 @@ nsDOMWorkerTimeout::Run()
 void
 nsDOMWorkerTimeout::Cancel()
 {
   NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
 
   LOG(("Worker [0x%p] canceling timeout [0x%p] with id %u",
        static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
 
-#ifdef DEBUG
-  mFiredOrCanceled = PR_TRUE;
-#endif
-
   {
     AutoSpinlock lock(this);
 
     if (IsSuspendedNoLock()) {
       mIsSuspended = PR_FALSE;
       // This should kill us when all is said and done.
       mSuspendedRef = nsnull;
     }
   }
 
   // This call to Cancel should kill us.
   mTimer->Cancel();
 }
 
 void
-nsDOMWorkerTimeout::Suspend(PRTime aNow)
+nsDOMWorkerTimeout::Suspend()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
+#ifdef DEBUG
+  if (mStarted) {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  }
+#endif
 
   AutoSpinlock lock(this);
 
-  if (!mIsSuspended) {
-    mIsSuspended = PR_TRUE;
-    mSuspendedRef = this;
+  NS_ASSERTION(!IsSuspendedNoLock(), "Bad state!");
+
+  mIsSuspended = PR_TRUE;
+  mSuspendedRef = this;
+
+  if (!mStarted) {
+    mSuspendedBeforeStart = PR_TRUE;
+    return;
   }
 
   mTimer->Cancel();
 
-  mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - aNow)) /
+  mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - PR_Now())) /
                      (PRTime)PR_USEC_PER_MSEC;
 
   LOG(("Worker [0x%p] suspending timeout [0x%p] with id %u (interval = %u)",
        static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId,
        mSuspendInterval));
 }
 
 void
-nsDOMWorkerTimeout::Resume(PRTime aNow)
+nsDOMWorkerTimeout::Resume()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+#ifdef DEBUG
+  if (!mSuspendedBeforeStart) {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  }
+#endif
+
   NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
 
   LOG(("Worker [0x%p] resuming timeout [0x%p] with id %u",
        static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
 
   AutoSpinlock lock(this);
 
   NS_ASSERTION(IsSuspendedNoLock(), "Should be suspended!");
 
-  mTargetTime = aNow + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC;
+  if (mSuspendedBeforeStart) {
+    NS_ASSERTION(!mSuspendInterval, "Bad state!");
+    mSuspendedBeforeStart = PR_FALSE;
+    mSuspendInterval = mInterval;
+    mStarted = PR_TRUE;
+  }
+
+  mTargetTime = PR_Now() + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC;
 
 #ifdef DEBUG
   nsresult rv =
 #endif
   mTimer->InitWithCallback(this, mSuspendInterval, nsITimer::TYPE_ONE_SHOT);
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to init timer!");
 }
 
@@ -494,29 +484,25 @@ nsDOMWorkerTimeout::Notify(nsITimer* aTi
   nsresult rv = aTimer->GetType(&type);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We only care about one-shot timers here because that may be the one that
   // we set from Resume().
   if (type == nsITimer::TYPE_ONE_SHOT) {
     AutoSpinlock lock(this);
     if (mIsSuspended) {
+      mIsSuspended = PR_FALSE;
+      mSuspendedRef = nsnull;
       if (mIsInterval) {
-        //LOG(("Timeout [0x%p] resuming normal interval (%u) with id %u",
-             //static_cast<void*>(this), mInterval, mId));
-
         // This is the first fire since we resumed. Set our interval back to the
         // real interval.
         mTargetTime = PR_Now() + mInterval * (PRTime)PR_USEC_PER_MSEC;
 
         rv = aTimer->InitWithCallback(this, mInterval,
                                       nsITimer::TYPE_REPEATING_SLACK);
         NS_ENSURE_SUCCESS(rv, rv);
       }
-
-      mIsSuspended = PR_FALSE;
-      mSuspendedRef = nsnull;
     }
   }
 
   nsDOMThreadService::get()->TimeoutReady(this);
   return NS_OK;
 }
--- a/dom/src/threads/nsDOMWorkerTimeout.h
+++ b/dom/src/threads/nsDOMWorkerTimeout.h
@@ -42,70 +42,71 @@
 // Interfaces
 #include "nsITimer.h"
 
 // Other includes
 #include "jsapi.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsStringGlue.h"
-#include "prclist.h"
 
 // DOMWorker includes
-#include "nsDOMWorkerThread.h"
+#include "nsDOMWorker.h"
 
 /**
  * The nsDOMWorkerTimeout has a slightly complicated life cycle. It's created
- * by an nsDOMWorkerThread (or one of its JS context functions) and immediately
- * takes a strong reference to the worker that created it. It does this so that
- * the worker can't be collected while a timeout is outstanding. However, the
- * worker needs a weak reference to the timeout so that it can be canceled if
- * the worker is canceled (in the event that the page falls out of the fastback
+ * by an nsDOMWorker (or one of its JS context functions) and immediately takes
+ * a strong reference to the worker that created it. It does this so that the
+ * worker can't be collected while a timeout is outstanding. However, the worker
+ * needs a weak reference to the timeout so that it can be canceled if the
+ * worker is canceled (in the event that the page falls out of the fastback
  * cache or the application is exiting, for instance). The only thing that holds
  * the timeout alive is it's mTimer via the nsITimerCallback interface. If the
  * timer is single-shot and has run already or if the timer is canceled then
  * this object should die.
  */
-class nsDOMWorkerTimeout : public PRCList,
+class nsDOMWorkerTimeout : public nsDOMWorkerFeature,
                            public nsITimerCallback
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSITIMERCALLBACK
 
-  nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker, PRUint32 aId);
-  ~nsDOMWorkerTimeout();
+  nsDOMWorkerTimeout(nsDOMWorker* aWorker,
+                     PRUint32 aId);
 
-  nsresult Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
+  nsresult Init(JSContext* aCx,
+                PRUint32 aArgc,
+                jsval* aArgv,
                 PRBool aIsInterval);
 
+  nsresult Start();
+
   nsresult Run();
 
-  void Cancel();
-  void Suspend(PRTime aNow);
-  void Resume(PRTime aNow);
+  virtual void Cancel();
+  virtual void Suspend();
+  virtual void Resume();
 
   PRIntervalTime GetInterval() {
     return mInterval;
   }
 
-  nsDOMWorkerThread* GetWorker() {
+  nsDOMWorker* GetWorker() {
     return mWorker;
   }
 
-  PRUint32 GetId() {
-    return mId;
-  }
-
   PRBool IsSuspended() {
     AutoSpinlock lock(this);
     return IsSuspendedNoLock();
   }
 
 private:
+  ~nsDOMWorkerTimeout() { }
+
   void AcquireSpinlock();
   void ReleaseSpinlock();
 
   PRBool IsSuspendedNoLock() {
     return mIsSuspended;
   }
 
   class AutoSpinlock
@@ -132,58 +133,58 @@ private:
     virtual ~CallbackBase() { }
     virtual nsresult Run(nsDOMWorkerTimeout* aTimeout,
                          JSContext* aCx) = 0;
   };
 
   class FunctionCallback : public CallbackBase
   {
   public:
-    FunctionCallback(PRUint32 aArgc, jsval* aArgv, nsresult* aRv);
+    FunctionCallback(PRUint32 aArgc,
+                     jsval* aArgv,
+                     nsresult* aRv);
     virtual ~FunctionCallback();
     virtual nsresult Run(nsDOMWorkerTimeout* aTimeout,
                          JSContext* aCx);
   protected:
     jsval mCallback;
     jsval* mCallbackArgs;
     PRUint32 mCallbackArgsLength;
+    JSRuntime* mRuntime;
   };
-  
+
   class ExpressionCallback : public CallbackBase
   {
   public:
-    ExpressionCallback(PRUint32 aArgc, jsval* aArgv, JSContext* aCx,
+    ExpressionCallback(PRUint32 aArgc,
+                       jsval* aArgv,
+                       JSContext* aCx,
                        nsresult* aRv);
     virtual ~ExpressionCallback();
     virtual nsresult Run(nsDOMWorkerTimeout* aTimeout,
                          JSContext* aCx);
   protected:
     JSString* mExpression;
     nsString mFileName;
     PRUint32 mLineNumber;
+    JSRuntime* mRuntime;
   };
 
-  // Hold the worker alive!
-  nsRefPtr<nsDOMWorkerThread> mWorker;
-
   // Hold this object alive!
   nsCOMPtr<nsITimer> mTimer;
 
   PRUint32 mInterval;
-  PRBool mIsInterval;
 
   PRTime mTargetTime;
 
   nsAutoPtr<CallbackBase> mCallback;
 
-  PRUint32 mId;
-
   PRInt32 mSuspendSpinlock;
-  PRBool mIsSuspended;
   PRUint32 mSuspendInterval;
   nsRefPtr<nsDOMWorkerTimeout> mSuspendedRef;
 
-#ifdef DEBUG
-  PRBool mFiredOrCanceled;
-#endif
+  PRPackedBool mIsInterval;
+  PRPackedBool mIsSuspended;
+  PRPackedBool mSuspendedBeforeStart;
+  PRPackedBool mStarted;
 };
 
 #endif /* __NSDOMWORKERTIMEOUT_H__ */
--- a/dom/src/threads/nsDOMWorkerXHR.cpp
+++ b/dom/src/threads/nsDOMWorkerXHR.cpp
@@ -51,16 +51,17 @@
 #include "nsContentUtils.h"
 #include "nsIClassInfoImpl.h"
 #include "nsJSUtils.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 
 // DOMWorker includes
 #include "nsDOMThreadService.h"
+#include "nsDOMWorkerEvents.h"
 #include "nsDOMWorkerPool.h"
 #include "nsDOMWorkerXHRProxy.h"
 
 // The list of event types that we support. This list and the defines based on
 // it determine the sizes of the listener arrays in nsDOMWorkerXHRProxy. Make
 // sure that any event types shared by both the XHR and Upload objects are
 // together at the beginning of the list. Any changes made to this list may
 // affect sMaxUploadEventTypes, so make sure that it is adjusted accordingly or
@@ -86,339 +87,330 @@ const PRUint32 nsDOMWorkerXHREventTarget
 const PRUint32 nsDOMWorkerXHREventTarget::sMaxUploadEventTypes =
   LISTENER_TYPE_READYSTATECHANGE;
 
 // Enforce the invariant that the upload object supports no more event types
 // than the xhr object.
 PR_STATIC_ASSERT(nsDOMWorkerXHREventTarget::sMaxXHREventTypes >=
                  nsDOMWorkerXHREventTarget::sMaxUploadEventTypes);
 
-NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerXHREventTarget,
-                              nsIDOMEventTarget,
-                              nsIXMLHttpRequestEventTarget)
+NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerXHREventTarget,
+                             nsDOMWorkerMessageHandler,
+                             nsIXMLHttpRequestEventTarget)
 
 PRUint32
 nsDOMWorkerXHREventTarget::GetListenerTypeFromString(const nsAString& aString)
 {
   for (PRUint32 index = 0; index < sMaxXHREventTypes; index++) {
     if (aString.EqualsASCII(sListenerTypes[index])) {
       return index;
     }
   }
   return PR_UINT32_MAX;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::GetOnabort(nsIDOMEventListener** aOnabort)
 {
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ENSURE_ARG_POINTER(aOnabort);
 
-  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_ABORT);
+  nsAutoString type;
+  type.AssignASCII(sListenerTypes[LISTENER_TYPE_ABORT]);
+
+  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
   listener.forget(aOnabort);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::SetOnabort(nsIDOMEventListener* aOnabort)
 {
-  return SetEventListener(LISTENER_TYPE_ABORT, aOnabort, PR_TRUE);
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsAutoString type;
+  type.AssignASCII(sListenerTypes[LISTENER_TYPE_ABORT]);
+
+  return SetOnXListener(type, aOnabort);
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::GetOnerror(nsIDOMEventListener** aOnerror)
 {
   NS_ENSURE_ARG_POINTER(aOnerror);
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
-  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_ERROR);
+  nsAutoString type;
+  type.AssignASCII(sListenerTypes[LISTENER_TYPE_ERROR]);
+
+  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
   listener.forget(aOnerror);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::SetOnerror(nsIDOMEventListener* aOnerror)
 {
-  return SetEventListener(LISTENER_TYPE_ERROR, aOnerror, PR_TRUE);
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsAutoString type;
+  type.AssignASCII(sListenerTypes[LISTENER_TYPE_ERROR]);
+
+  return SetOnXListener(type, aOnerror);
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::GetOnload(nsIDOMEventListener** aOnload)
 {
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ENSURE_ARG_POINTER(aOnload);
 
-  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_LOAD);
+  nsAutoString type;
+  type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOAD]);
+
+  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
   listener.forget(aOnload);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::SetOnload(nsIDOMEventListener* aOnload)
 {
-  return SetEventListener(LISTENER_TYPE_LOAD, aOnload, PR_TRUE);
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsAutoString type;
+  type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOAD]);
+
+  return SetOnXListener(type, aOnload);
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::GetOnloadstart(nsIDOMEventListener** aOnloadstart)
 {
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ENSURE_ARG_POINTER(aOnloadstart);
 
-  nsCOMPtr<nsIDOMEventListener> listener =
-    GetOnXListener(LISTENER_TYPE_LOADSTART);
+  nsAutoString type;
+  type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOADSTART]);
+
+  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
   listener.forget(aOnloadstart);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::SetOnloadstart(nsIDOMEventListener* aOnloadstart)
 {
-  return SetEventListener(LISTENER_TYPE_LOADSTART, aOnloadstart, PR_TRUE);
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsAutoString type;
+  type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOADSTART]);
+
+  return SetOnXListener(type, aOnloadstart);
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::GetOnprogress(nsIDOMEventListener** aOnprogress)
 {
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ENSURE_ARG_POINTER(aOnprogress);
 
-  nsCOMPtr<nsIDOMEventListener> listener =
-    GetOnXListener(LISTENER_TYPE_PROGRESS);
+  nsAutoString type;
+  type.AssignASCII(sListenerTypes[LISTENER_TYPE_PROGRESS]);
+
+  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
   listener.forget(aOnprogress);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::SetOnprogress(nsIDOMEventListener* aOnprogress)
 {
-  return SetEventListener(LISTENER_TYPE_PROGRESS, aOnprogress, PR_TRUE);
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREventTarget::AddEventListener(const nsAString& aType,
-                                            nsIDOMEventListener* aListener,
-                                            PRBool aUseCapture)
-{
-  NS_ENSURE_ARG_POINTER(aListener);
-
-  PRUint32 type = GetListenerTypeFromString(aType);
-  if (type > sMaxXHREventTypes) {
-    // Silently ignore junk events.
-    return NS_OK;
-  }
-
-  return SetEventListener(type, aListener, PR_FALSE);
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREventTarget::RemoveEventListener(const nsAString& aType,
-                                               nsIDOMEventListener* aListener,
-                                               PRBool aUseCapture)
-{
-  NS_ENSURE_ARG_POINTER(aListener);
-
-  PRUint32 type = GetListenerTypeFromString(aType);
-  if (type > sMaxXHREventTypes) {
-    // Silently ignore junk events.
-    return NS_OK;
-  }
-
-  return UnsetEventListener(type, aListener);
-}
-
-/* ec702b78-c30f-439f-9a9b-a5dae17ee0fc */
-#define NS_IPRIVATEWORKERXHREVENT_IID                      \
-{                                                          \
-  0xec702b78,                                              \
-  0xc30f,                                                  \
-  0x439f,                                                  \
-  { 0x9a, 0x9b, 0xa5, 0xda, 0xe1, 0x7e, 0xe0, 0xfc }       \
-}
-
-class nsIPrivateWorkerXHREvent : public nsIDOMEvent
-{
-public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IPRIVATEWORKERXHREVENT_IID)
-  virtual PRBool PreventDefaultCalled() = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsIPrivateWorkerXHREvent,
-                              NS_IPRIVATEWORKERXHREVENT_IID)
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
-#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(); }
-
-class nsDOMWorkerXHREventWrapper : public nsIPrivateWorkerXHREvent
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_FORWARD_NSIDOMEVENT_SPECIAL
-
-  nsDOMWorkerXHREventWrapper(nsIDOMEvent* aEvent)
-  : mEvent(aEvent), mPreventDefaultCalled(PR_FALSE) {
-    NS_ASSERTION(aEvent, "Null pointer!");
-  }
-
-  NS_IMETHOD PreventDefault() {
-    mPreventDefaultCalled = PR_TRUE;
-    return mEvent->PreventDefault();
-  }
+  nsAutoString type;
+  type.AssignASCII(sListenerTypes[LISTENER_TYPE_PROGRESS]);
 
-  NS_IMETHOD InitEvent(const nsAString& aEventType, PRBool aCanBubble,
-                       PRBool aCancelable) {
-    mPreventDefaultCalled = PR_FALSE;
-    return mEvent->InitEvent(aEventType, aCanBubble, aCancelable);
-  } 
-
-  // nsIPrivateWorkerXHREvent
-  virtual PRBool PreventDefaultCalled() {
-    return mPreventDefaultCalled;
-  }
-
-private:
-  nsCOMPtr<nsIDOMEvent> mEvent;
-  PRBool mPreventDefaultCalled;
-};
-
-NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerXHREventWrapper,
-                              nsIDOMEvent,
-                              nsIPrivateWorkerXHREvent)
-
-NS_IMETHODIMP
-nsDOMWorkerXHREventTarget::DispatchEvent(nsIDOMEvent* aEvent,
-                                         PRBool* _retval)
-{
-  NS_ENSURE_ARG_POINTER(aEvent);
-  NS_ENSURE_ARG_POINTER(_retval);
-
-  nsCOMPtr<nsIPrivateWorkerXHREvent> wrapper(do_QueryInterface(aEvent));
-  if (!wrapper) {
-    wrapper = new nsDOMWorkerXHREventWrapper(aEvent);
-    NS_ENSURE_TRUE(wrapper, NS_ERROR_OUT_OF_MEMORY);
-  }
-
-  nsresult rv = HandleWorkerEvent(wrapper);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  *_retval = wrapper->PreventDefaultCalled();
-  return NS_OK;
+  return SetOnXListener(type, aOnprogress);
 }
 
 nsDOMWorkerXHRUpload::nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR)
 : mWorkerXHR(aWorkerXHR)
 {
-  NS_ASSERTION(aWorkerXHR, "Must have a worker XHR!");
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aWorkerXHR, "Null pointer!");
 }
 
-NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget,
-                                                   nsIXMLHttpRequestUpload,
-                                                   nsIClassInfo)
+NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget,
+                                                   nsIXMLHttpRequestUpload)
 
 NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHRUpload, nsIDOMEventTarget,
                                                    nsIXMLHttpRequestEventTarget,
                                                    nsIXMLHttpRequestUpload)
 
-NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHRUpload)
+NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerXHRUpload)
 
-nsresult
-nsDOMWorkerXHRUpload::SetEventListener(PRUint32 aType,
+NS_IMETHODIMP
+nsDOMWorkerXHRUpload::AddEventListener(const nsAString& aType,
                                        nsIDOMEventListener* aListener,
-                                       PRBool aOnXListener)
+                                       PRBool aUseCapture)
 {
-  if (mWorkerXHR->mCanceled) {
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aListener);
+
+  if (mWorkerXHR->mWorker->IsCanceled()) {
     return NS_ERROR_ABORT;
   }
 
-  return mWorkerXHR->mXHRProxy->AddEventListener(aType, aListener, aOnXListener,
-                                                 PR_TRUE);
+  nsresult rv = nsDOMWorkerXHREventTarget::AddEventListener(aType, aListener,
+                                                            aUseCapture);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mWorkerXHR->mXHRProxy->UploadEventListenerAdded();
+  if (NS_FAILED(rv)) {
+    NS_WARNING("UploadEventListenerAdded failed!");
+    RemoveEventListener(aType, aListener, aUseCapture);
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHRUpload::RemoveEventListener(const nsAString& aType,
+                                          nsIDOMEventListener* aListener,
+                                          PRBool aUseCapture)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aListener);
+
+  if (mWorkerXHR->mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return nsDOMWorkerXHREventTarget::RemoveEventListener(aType, aListener,
+                                                        aUseCapture);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHRUpload::DispatchEvent(nsIDOMEvent* aEvent,
+                                    PRBool* _retval)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aEvent);
+
+  if (mWorkerXHR->mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return nsDOMWorkerXHREventTarget::DispatchEvent(aEvent, _retval);
 }
 
 nsresult
-nsDOMWorkerXHRUpload::UnsetEventListener(PRUint32 aType,
-                                         nsIDOMEventListener* aListener)
+nsDOMWorkerXHRUpload::SetOnXListener(const nsAString& aType,
+                                     nsIDOMEventListener* aListener)
 {
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
   if (mWorkerXHR->mCanceled) {
     return NS_ERROR_ABORT;
   }
 
-  return mWorkerXHR->mXHRProxy->RemoveEventListener(aType, aListener, PR_TRUE);
-}
-
-nsresult
-nsDOMWorkerXHRUpload::HandleWorkerEvent(nsIDOMEvent* aEvent)
-{
-  if (mWorkerXHR->mCanceled) {
-    return NS_ERROR_ABORT;
+  PRUint32 type = GetListenerTypeFromString(aType);
+  if (type > sMaxUploadEventTypes) {
+    // Silently ignore junk events.
+    return NS_OK;
   }
 
-  return mWorkerXHR->mXHRProxy->HandleWorkerEvent(aEvent, PR_TRUE);
+  return nsDOMWorkerXHREventTarget::SetOnXListener(aType, aListener);
 }
 
-already_AddRefed<nsIDOMEventListener>
-nsDOMWorkerXHRUpload::GetOnXListener(PRUint32 aType)
-{
-  if (mWorkerXHR->mCanceled) {
-    return nsnull;
-  }
-
-  return mWorkerXHR->mXHRProxy->GetOnXListener(aType, PR_TRUE);
-}
-
-nsDOMWorkerXHR::nsDOMWorkerXHR(nsDOMWorkerThread* aWorker)
-: mWorker(aWorker),
-  mCanceled(PR_TRUE)
+nsDOMWorkerXHR::nsDOMWorkerXHR(nsDOMWorker* aWorker)
+: nsDOMWorkerFeature(aWorker),
+  mWrappedNative(nsnull),
+  mCanceled(PR_FALSE)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aWorker, "Must have a worker!");
 }
 
-nsDOMWorkerXHR::~nsDOMWorkerXHR()
-{
-  if (!mCanceled) {
-    mWorker->RemoveXHR(this);
-  }
-}
+// Tricky! We use the AddRef/Release method of nsDOMWorkerFeature (to make sure
+// we properly remove ourselves from the worker array) but inherit the QI of
+// nsDOMWorkerXHREventTarget.
+NS_IMPL_ADDREF_INHERITED(nsDOMWorkerXHR, nsDOMWorkerFeature)
+NS_IMPL_RELEASE_INHERITED(nsDOMWorkerXHR, nsDOMWorkerFeature)
 
-NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget,
-                                             nsIXMLHttpRequest,
-                                             nsIClassInfo)
+NS_IMPL_QUERY_INTERFACE_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget,
+                                                   nsIXMLHttpRequest,
+                                                   nsIXPCScriptable)
 
 NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHR, nsIDOMEventTarget,
                                              nsIXMLHttpRequestEventTarget,
                                              nsIXMLHttpRequest)
 
-NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHR)
+NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerXHR)
+
+#define XPC_MAP_CLASSNAME nsDOMWorkerXHR
+#define XPC_MAP_QUOTED_CLASSNAME "XMLHttpRequest"
+#define XPC_MAP_WANT_POSTCREATE
+#define XPC_MAP_WANT_TRACE
+#define XPC_MAP_WANT_FINALIZE
+
+#define XPC_MAP_FLAGS                                  \
+  nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE        | \
+  nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY        | \
+  nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES
+
+#include "xpc_map_end.h"
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::Trace(nsIXPConnectWrappedNative* /* aWrapper */,
+                      JSTracer* aTracer,
+                      JSObject* /*aObj */)
+{
+  if (!mCanceled) {
+    nsDOMWorkerMessageHandler::Trace(aTracer);
+    if (mUpload) {
+      mUpload->Trace(aTracer);
+    }
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::Finalize(nsIXPConnectWrappedNative* /* aWrapper */,
+                         JSContext* /* aCx */,
+                         JSObject* /* aObj */)
+{
+  nsDOMWorkerMessageHandler::ClearAllListeners();
+  if (mUpload) {
+    mUpload->ClearAllListeners();
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::PostCreate(nsIXPConnectWrappedNative* aWrapper,
+                           JSContext* /* aCx */,
+                           JSObject* /* aObj */)
+{
+  mWrappedNative = aWrapper;
+  return NS_OK;
+}
 
 nsresult
 nsDOMWorkerXHR::Init()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
-  if (!mWorker->AddXHR(this)) {
-    // Must have been canceled.
-    return NS_ERROR_ABORT;
-  }
-  mCanceled = PR_FALSE;
-
   nsRefPtr<nsDOMWorkerXHRProxy> proxy = new nsDOMWorkerXHRProxy(this);
   NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
 
   nsresult rv = proxy->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   proxy.swap(mXHRProxy);
   return NS_OK;
@@ -441,82 +433,53 @@ nsDOMWorkerXHR::Cancel()
     mCanceled = PR_TRUE;
     mUpload = nsnull;
   }
 
   if (mXHRProxy) {
     mXHRProxy->Destroy();
   }
 
-  mWorker->RemoveXHR(this);
   mWorker = nsnull;
 }
 
 nsresult
-nsDOMWorkerXHR::SetEventListener(PRUint32 aType,
-                                 nsIDOMEventListener* aListener,
-                                 PRBool aOnXListener)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  if (mCanceled) {
-    return NS_ERROR_ABORT;
-  }
-
-  return mXHRProxy->AddEventListener(aType, aListener, aOnXListener, PR_FALSE);
-}
-
-nsresult
-nsDOMWorkerXHR::UnsetEventListener(PRUint32 aType,
-                                   nsIDOMEventListener* aListener)
+nsDOMWorkerXHR::SetOnXListener(const nsAString& aType,
+                               nsIDOMEventListener* aListener)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   if (mCanceled) {
     return NS_ERROR_ABORT;
   }
 
-  return mXHRProxy->RemoveEventListener(aType, aListener, PR_FALSE);
-}
-
-nsresult
-nsDOMWorkerXHR::HandleWorkerEvent(nsIDOMEvent* aEvent)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  if (mCanceled) {
-    return NS_ERROR_ABORT;
+  PRUint32 type = GetListenerTypeFromString(aType);
+  if (type > sMaxXHREventTypes) {
+    // Silently ignore junk events.
+    return NS_OK;
   }
 
-  return mXHRProxy->HandleWorkerEvent(aEvent, PR_FALSE);
-}
-
-already_AddRefed<nsIDOMEventListener>
-nsDOMWorkerXHR::GetOnXListener(PRUint32 aType)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  if (mCanceled) {
-    return nsnull;
-  }
-
-  return mXHRProxy->GetOnXListener(aType, PR_FALSE);
+  return nsDOMWorkerXHREventTarget::SetOnXListener(aType, aListener);
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHR::GetChannel(nsIChannel** aChannel)
 {
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
   NS_ENSURE_ARG_POINTER(aChannel);
   *aChannel = nsnull;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHR::GetResponseXML(nsIDOMDocument** aResponseXML)
 {
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
   NS_ENSURE_ARG_POINTER(aResponseXML);
   *aResponseXML = nsnull;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHR::GetResponseText(nsAString& aResponseText)
 {
@@ -848,17 +811,17 @@ nsDOMWorkerXHR::Init(nsIPrincipal* aPrin
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHR::GetUpload(nsIXMLHttpRequestUpload** aUpload)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
-  nsRefPtr<nsDOMWorkerThread> worker = mWorker;
+  nsRefPtr<nsDOMWorker> worker = mWorker;
   if (!worker) {
     return NS_ERROR_ABORT;
   }
 
   nsAutoLock lock(worker->Lock());
 
   if (mCanceled) {
     return NS_ERROR_ABORT;
@@ -873,42 +836,34 @@ nsDOMWorkerXHR::GetUpload(nsIXMLHttpRequ
 
   NS_ADDREF(*aUpload = mUpload);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHR::GetOnreadystatechange(nsIDOMEventListener** aOnreadystatechange)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  if (mCanceled) {
-    return NS_ERROR_ABORT;
-  }
-
   NS_ENSURE_ARG_POINTER(aOnreadystatechange);
 
-  nsCOMPtr<nsIDOMEventListener> listener =
-    mXHRProxy->GetOnXListener(LISTENER_TYPE_READYSTATECHANGE, PR_FALSE);
+  nsAutoString type;
+  type.AssignASCII(sListenerTypes[LISTENER_TYPE_READYSTATECHANGE]);
 
+  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
   listener.forget(aOnreadystatechange);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHR::SetOnreadystatechange(nsIDOMEventListener* aOnreadystatechange)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  nsAutoString type;
+  type.AssignASCII(sListenerTypes[LISTENER_TYPE_READYSTATECHANGE]);
 
-  if (mCanceled) {
-    return NS_ERROR_ABORT;
-  }
-
-  return mXHRProxy->AddEventListener(LISTENER_TYPE_READYSTATECHANGE,
-                                    aOnreadystatechange, PR_TRUE, PR_FALSE);
+  return SetOnXListener(type, aOnreadystatechange);
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHR::GetWithCredentials(PRBool* aWithCredentials)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   if (mCanceled) {
--- a/dom/src/threads/nsDOMWorkerXHR.h
+++ b/dom/src/threads/nsDOMWorkerXHR.h
@@ -35,143 +35,123 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __NSDOMWORKERXHR_H__
 #define __NSDOMWORKERXHR_H__
 
 // Bases
+#include "nsIClassInfo.h"
 #include "nsIXMLHttpRequest.h"
-#include "nsIClassInfo.h"
-
-// Interfaces
+#include "nsIXPCScriptable.h"
 
 // Other includes
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "prlock.h"
 
 // DOMWorker includes
-#include "nsDOMWorkerThread.h"
+#include "nsDOMWorker.h"
+#include "nsDOMWorkerMacros.h"
+#include "nsDOMWorkerXHRProxy.h"
 
 // Convenience defines for event *indexes* in the sListenerTypes array.
 #define LISTENER_TYPE_ABORT 0
 #define LISTENER_TYPE_ERROR 1
 #define LISTENER_TYPE_LOAD 2
 #define LISTENER_TYPE_LOADSTART 3
 #define LISTENER_TYPE_PROGRESS 4
 #define LISTENER_TYPE_READYSTATECHANGE 5
 
-class nsDOMWorkerXHR;
-class nsDOMWorkerXHREvent;
-class nsDOMWorkerXHRProxy;
+class nsIXPConnectWrappedNative;
 
-class nsDOMWorkerXHREventTarget : public nsIXMLHttpRequestEventTarget
+class nsDOMWorkerXHREventTarget : public nsDOMWorkerMessageHandler,
+                                  public nsIXMLHttpRequestEventTarget
 {
 public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMEVENTTARGET
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIDOMEVENTTARGET(nsDOMWorkerMessageHandler::)
   NS_DECL_NSIXMLHTTPREQUESTEVENTTARGET
 
   static const char* const sListenerTypes[];
   static const PRUint32 sMaxXHREventTypes;
   static const PRUint32 sMaxUploadEventTypes;
 
   static PRUint32 GetListenerTypeFromString(const nsAString& aString);
 
-  virtual nsresult SetEventListener(PRUint32 aType,
-                                    nsIDOMEventListener* aListener,
-                                    PRBool aOnXListener) = 0;
-
-  virtual nsresult UnsetEventListener(PRUint32 aType,
-                                      nsIDOMEventListener* aListener) = 0;
-
-  virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent) = 0;
-
-  virtual already_AddRefed<nsIDOMEventListener>
-    GetOnXListener(PRUint32 aType) = 0;
-
 protected:
   virtual ~nsDOMWorkerXHREventTarget() { }
 };
 
-class nsDOMWorkerXHRUpload : public nsDOMWorkerXHREventTarget,
-                             public nsIXMLHttpRequestUpload,
-                             public nsIClassInfo
-{
-  friend class nsDOMWorkerXHR;
-
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_FORWARD_NSIDOMEVENTTARGET(nsDOMWorkerXHREventTarget::)
-  NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsDOMWorkerXHREventTarget::)
-  NS_DECL_NSIXMLHTTPREQUESTUPLOAD
-  NS_DECL_NSICLASSINFO
-
-  nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR);
-
-  virtual nsresult SetEventListener(PRUint32 aType,
-                                    nsIDOMEventListener* aListener,
-                                    PRBool aOnXListener);
-
-  virtual nsresult UnsetEventListener(PRUint32 aType,
-                                      nsIDOMEventListener* aListener);
-
-  virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent);
-
-  virtual already_AddRefed<nsIDOMEventListener>
-    GetOnXListener(PRUint32 aType);
-
-protected:
-  virtual ~nsDOMWorkerXHRUpload() { }
-
-  nsRefPtr<nsDOMWorkerXHR> mWorkerXHR;
-};
+class nsDOMWorkerXHRUpload;
 
 class nsDOMWorkerXHR : public nsDOMWorkerXHREventTarget,
+                       public nsDOMWorkerFeature,
                        public nsIXMLHttpRequest,
-                       public nsIClassInfo
+                       public nsIXPCScriptable
 {
   friend class nsDOMWorkerXHREvent;
   friend class nsDOMWorkerXHRLastProgressOrLoadEvent;
   friend class nsDOMWorkerXHRProxy;
   friend class nsDOMWorkerXHRUpload;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIXMLHTTPREQUEST
-  NS_DECL_NSICLASSINFO
+  NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerXHREventTarget::)
+  NS_DECL_NSICLASSINFO_GETINTERFACES
+  NS_DECL_NSIXPCSCRIPTABLE
 
-  nsDOMWorkerXHR(nsDOMWorkerThread* aWorker);
+  nsDOMWorkerXHR(nsDOMWorker* aWorker);
 
   nsresult Init();
 
-  void Cancel();
-
-  virtual nsresult SetEventListener(PRUint32 aType,
-                                    nsIDOMEventListener* aListener,
-                                    PRBool aOnXListener);
+  virtual void Cancel();
 
-  virtual nsresult UnsetEventListener(PRUint32 aType,
-                                      nsIDOMEventListener* aListener);
-
-  virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent);
+  virtual nsresult SetOnXListener(const nsAString& aType,
+                                  nsIDOMEventListener* aListener);
 
-  virtual already_AddRefed<nsIDOMEventListener>
-    GetOnXListener(PRUint32 aType);
-
-protected:
-  virtual ~nsDOMWorkerXHR();
+private:
+  virtual ~nsDOMWorkerXHR() { }
 
   PRLock* Lock() {
     return mWorker->Lock();
   }
 
-  nsRefPtr<nsDOMWorkerThread> mWorker;
+  nsIXPConnectWrappedNative* GetWrappedNative() {
+    return mWrappedNative;
+  }
+
   nsRefPtr<nsDOMWorkerXHRProxy> mXHRProxy;
   nsRefPtr<nsDOMWorkerXHRUpload> mUpload;
 
+  nsIXPConnectWrappedNative* mWrappedNative;
+
   volatile PRBool mCanceled;
 };
 
+class nsDOMWorkerXHRUpload : public nsDOMWorkerXHREventTarget,
+                             public nsIXMLHttpRequestUpload
+{
+  friend class nsDOMWorkerXHR;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIDOMEVENTTARGET
+  NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsDOMWorkerXHREventTarget::)
+  NS_DECL_NSIXMLHTTPREQUESTUPLOAD
+  NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerXHREventTarget::)
+  NS_DECL_NSICLASSINFO_GETINTERFACES
+
+  nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR);
+
+  virtual nsresult SetOnXListener(const nsAString& aType,
+                                  nsIDOMEventListener* aListener);
+
+protected:
+  virtual ~nsDOMWorkerXHRUpload() { }
+
+  nsRefPtr<nsDOMWorkerXHR> mWorkerXHR;
+};
+
 #endif /* __NSDOMWORKERXHR_H__ */
--- a/dom/src/threads/nsDOMWorkerXHRProxy.cpp
+++ b/dom/src/threads/nsDOMWorkerXHRProxy.cpp
@@ -54,18 +54,22 @@
 #include "nsIClassInfoImpl.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 #include "nsXMLHttpRequest.h"
 #include "prinrval.h"
 #include "prthread.h"
 
 // DOMWorker includes
+#include "nsDOMThreadService.h"
+#include "nsDOMWorker.h"
+#include "nsDOMWorkerEvents.h"
+#include "nsDOMWorkerMacros.h"
 #include "nsDOMWorkerPool.h"
-#include "nsDOMWorkerThread.h"
+#include "nsDOMWorkerXHR.h"
 #include "nsDOMWorkerXHRProxiedFunctions.h"
 
 #define MAX_XHR_LISTENER_TYPE nsDOMWorkerXHREventTarget::sMaxXHREventTypes
 #define MAX_UPLOAD_LISTENER_TYPE nsDOMWorkerXHREventTarget::sMaxUploadEventTypes
 
 #define RUN_PROXIED_FUNCTION(_name, _args)                                     \
   PR_BEGIN_MACRO                                                               \
     NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");                         \
@@ -104,17 +108,17 @@
 using namespace nsDOMWorkerProxiedXHRFunctions;
 
 class nsResultReturningRunnable : public nsIRunnable
 {
 public:
   NS_DECL_ISUPPORTS
 
   nsResultReturningRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable,
-                            nsDOMWorkerThread* aWorker)
+                            nsDOMWorker* aWorker)
   : mTarget(aTarget), mRunnable(aRunnable), mWorker(aWorker),
     mResult(NS_OK), mDone(PR_FALSE) { }
 
   nsresult Dispatch() {
     if (!mWorker) {
       // Must have been canceled, bail out.
       return NS_ERROR_ABORT;
     }
@@ -152,355 +156,42 @@ public:
     mDone = PR_TRUE;
 
     return mResult;
   }
 
 private:
   nsCOMPtr<nsIEventTarget> mTarget;
   nsCOMPtr<nsIRunnable> mRunnable;
-  nsRefPtr<nsDOMWorkerThread> mWorker;
+  nsRefPtr<nsDOMWorker> mWorker;
   nsresult mResult;
   volatile PRBool mDone;
 };
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsResultReturningRunnable, nsIRunnable)
 
-class nsDOMWorkerXHREvent : public nsIRunnable,
-                            public nsIDOMProgressEvent,
-                            public nsIClassInfo
-{
-  friend class nsDOMWorkerXHRProxy;
-  friend class nsDOMWorkerXHREventTargetProxy;
-
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
-  NS_DECL_NSIDOMEVENT
-  NS_DECL_NSIDOMPROGRESSEVENT
-  NS_DECL_NSICLASSINFO
-
-  nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy);
-
-  nsresult Init(PRUint32 aType,
-                const nsAString& aTypeString,
-                nsIDOMEvent* aEvent);
-
-  nsresult Init(nsIXMLHttpRequest* aXHR);
-
-  void EventHandled();
-
-protected:
-  nsRefPtr<nsDOMWorkerXHRProxy> mXHRProxy;
-  nsCOMPtr<nsIDOMEventTarget> mTarget;
-  nsString mTypeString;
-  PRUint32 mType;
-  PRUint16 mEventPhase;
-  DOMTimeStamp mTimeStamp;
-  nsString mResponseText;
-  nsCString mStatusText;
-  nsresult mStatus;
-  PRInt32 mReadyState;
-  PRUint64 mLoaded;
-  PRUint64 mTotal;
-  PRInt32 mChannelID;
-  PRPackedBool mBubbles;
-  PRPackedBool mCancelable;
-  PRPackedBool mUploadEvent;
-  PRPackedBool mProgressEvent;
-  PRPackedBool mLengthComputable;
-};
-
-nsDOMWorkerXHREvent::nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy)
-: mXHRProxy(aXHRProxy),
-  mType(PR_UINT32_MAX),
-  mEventPhase(0),
-  mTimeStamp(0),
-  mStatus(NS_OK),
-  mReadyState(0),
-  mLoaded(0),
-  mTotal(0),
-  mChannelID(-1),
-  mBubbles(PR_FALSE),
-  mCancelable(PR_FALSE),
-  mUploadEvent(PR_FALSE),
-  mProgressEvent(PR_FALSE),
-  mLengthComputable(PR_FALSE)
-{
-  NS_ASSERTION(aXHRProxy, "Can't be null!");
-}
-
-NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerXHREvent)
-NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerXHREvent)
-
-NS_INTERFACE_MAP_BEGIN(nsDOMWorkerXHREvent)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEvent)
-  NS_INTERFACE_MAP_ENTRY(nsIRunnable)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMEvent)
-  NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
-  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMProgressEvent, mProgressEvent)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerXHREvent, nsIDOMEvent)
-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;
-}
-
-NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(nsDOMWorkerXHREvent)
-
-nsresult
-nsDOMWorkerXHREvent::Init(PRUint32 aType,
-                          const nsAString& aTypeString,
-                          nsIDOMEvent* aEvent)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aEvent, "Don't pass null here!");
-
-  nsresult rv;
-
-  mType = aType;
-  mTypeString.Assign(aTypeString);
-
-  mChannelID = mXHRProxy->ChannelID();
-
-  nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(aEvent));
-  if (progressEvent) {
-    mProgressEvent = PR_TRUE;
-
-    PRBool lengthComputable;
-    rv = progressEvent->GetLengthComputable(&lengthComputable);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mLengthComputable = lengthComputable ? PR_TRUE : PR_FALSE;
-
-    rv = progressEvent->GetLoaded(&mLoaded);
-    NS_ENSURE_SUCCESS(rv, rv); 
-
-    rv = progressEvent->GetTotal(&mTotal);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  nsCOMPtr<nsIDOMEventTarget> mainThreadTarget;
-  rv = aEvent->GetTarget(getter_AddRefs(mainThreadTarget));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIXMLHttpRequestUpload> upload(do_QueryInterface(mainThreadTarget));
-  if (upload) {
-    mUploadEvent = PR_TRUE;
-    mTarget =
-      static_cast<nsDOMWorkerXHREventTarget*>(mXHRProxy->mWorkerXHR->mUpload);
-  }
-  else {
-    mUploadEvent = PR_FALSE;
-    mTarget = mXHRProxy->mWorkerXHR;
-  }
-
-  PRBool boolVal;
-  rv = aEvent->GetBubbles(&boolVal);
-  NS_ENSURE_SUCCESS(rv, rv);
-  mBubbles = boolVal ? PR_TRUE : PR_FALSE;
-
-  rv = aEvent->GetCancelable(&boolVal);
-  NS_ENSURE_SUCCESS(rv, rv);
-  mCancelable = boolVal ? PR_TRUE : PR_FALSE;
-
-  rv = aEvent->GetTimeStamp(&mTimeStamp);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = Init(mXHRProxy->mXHR);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-nsDOMWorkerXHREvent::Init(nsIXMLHttpRequest* aXHR)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aXHR, "Don't pass null here!");
-
-  nsresult rv = aXHR->GetResponseText(mResponseText);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = aXHR->GetStatusText(mStatusText);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = aXHR->GetStatus(&mStatus);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = aXHR->GetReadyState(&mReadyState);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-void
-nsDOMWorkerXHREvent::EventHandled()
-{
-  // Prevent reference cycles by releasing these here.
-  mXHRProxy = nsnull;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::Run()
-{
-  nsresult rv = mXHRProxy->HandleWorkerEvent(this, mUploadEvent);
-
-  EventHandled();
-
-  NS_ENSURE_SUCCESS(rv, rv);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::GetType(nsAString& aType)
-{
-  aType.Assign(mTypeString);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::GetTarget(nsIDOMEventTarget** aTarget)
-{
-  NS_ENSURE_ARG_POINTER(aTarget);
-  NS_ADDREF(*aTarget = mTarget);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget)
-{
-  NS_ENSURE_ARG_POINTER(aCurrentTarget);
-  NS_ADDREF(*aCurrentTarget = mTarget);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::GetEventPhase(PRUint16* aEventPhase)
-{
-  NS_ENSURE_ARG_POINTER(aEventPhase);
-  *aEventPhase = mEventPhase;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::GetBubbles(PRBool* aBubbles)
-{
-  NS_ENSURE_ARG_POINTER(aBubbles);
-  *aBubbles = mBubbles;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::GetCancelable(PRBool* aCancelable)
-{
-  NS_ENSURE_ARG_POINTER(aCancelable);
-  *aCancelable = mCancelable;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::GetTimeStamp(DOMTimeStamp* aTimeStamp)
-{
-  NS_ENSURE_ARG_POINTER(aTimeStamp);
-  *aTimeStamp = mTimeStamp;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::StopPropagation()
-{
-  NS_WARNING("StopPropagation doesn't do anything here!");
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::PreventDefault()
-{
-  NS_WARNING("PreventDefault doesn't do anything yet!");
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::InitEvent(const nsAString& aEventTypeArg,
-                               PRBool aCanBubbleArg,
-                               PRBool aCancelableArg)
-{
-  NS_WARNING("InitEvent doesn't do anything here!");
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::GetLengthComputable(PRBool* aLengthComputable)
-{
-  NS_ENSURE_ARG_POINTER(aLengthComputable);
-  *aLengthComputable = mLengthComputable;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::GetLoaded(PRUint64* aLoaded)
-{
-  NS_ENSURE_ARG_POINTER(aLoaded);
-  *aLoaded = mLoaded;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::GetTotal(PRUint64* aTotal)
-{
-  NS_ENSURE_ARG_POINTER(aTotal);
-  *aTotal = mTotal;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHREvent::InitProgressEvent(const nsAString_internal& aTypeArg,
-                                       PRBool aCanBubbleArg,
-                                       PRBool aCancelableArg,
-                                       PRBool aLengthComputableArg,
-                                       PRUint64 aLoadedArg,
-                                       PRUint64 aTotalArg)
-{
-  NS_WARNING("InitProgressEvent doesn't do anything here!");
-  return NS_OK;
-}
-
 class nsDOMWorkerXHRLastProgressOrLoadEvent : public nsIRunnable
 {
 public:
   NS_DECL_ISUPPORTS
 
   nsDOMWorkerXHRLastProgressOrLoadEvent(nsDOMWorkerXHRProxy* aProxy)
   : mProxy(aProxy) {
     NS_ASSERTION(aProxy, "Null pointer!");
   }
 
   NS_IMETHOD Run() {
     nsRefPtr<nsDOMWorkerXHREvent> lastProgressOrLoadEvent;
 
     if (!mProxy->mCanceled) {
       nsAutoLock lock(mProxy->mWorkerXHR->Lock());
       mProxy->mLastProgressOrLoadEvent.swap(lastProgressOrLoadEvent);
+      if (mProxy->mCanceled) {
+        return NS_ERROR_ABORT;
+      }
     }
 
     if (lastProgressOrLoadEvent) {
       return lastProgressOrLoadEvent->Run();
     }
 
     return NS_OK;
   }
@@ -624,17 +315,17 @@ nsDOMWorkerXHRProxy::nsDOMWorkerXHRProxy
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(MAX_XHR_LISTENER_TYPE >= MAX_UPLOAD_LISTENER_TYPE,
                "Upload should support a subset of XHR's event types!");
 }
 
 nsDOMWorkerXHRProxy::~nsDOMWorkerXHRProxy()
 {
   if (mOwnedByXHR) {
-    mWorkerXHR->Release();
+    mWorkerXHRWN = nsnull;
   }
   else if (mXHR) {
     nsCOMPtr<nsIThread> mainThread;
     NS_GetMainThread(getter_AddRefs(mainThread));
     NS_ASSERTION(mainThread, "This isn't supposed to fail!");
 
     // This will release immediately if we're on the main thread.
     NS_ProxyRelease(mainThread, mXHR);
@@ -647,28 +338,16 @@ NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMWorke
 
 nsresult
 nsDOMWorkerXHRProxy::Init()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   NS_ENSURE_FALSE(mXHR, NS_ERROR_ALREADY_INITIALIZED);
 
-  PRBool success = mXHRListeners.SetLength(MAX_XHR_LISTENER_TYPE);
-  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
-
-  success = mXHROnXListeners.SetLength(MAX_XHR_LISTENER_TYPE);
-  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
-
-  success = mUploadListeners.SetLength(MAX_UPLOAD_LISTENER_TYPE);
-  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
-
-  success = mUploadOnXListeners.SetLength(MAX_UPLOAD_LISTENER_TYPE);
-  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
-
   mMainThread = do_GetMainThread();
   NS_ENSURE_TRUE(mMainThread, NS_ERROR_UNEXPECTED);
 
   nsRefPtr<nsResultReturningRunnable> runnable =
     new nsResultReturningRunnable(mMainThread, this, mWorkerXHR->mWorker);
   NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
 
   nsresult rv = runnable->Dispatch();
@@ -688,41 +367,41 @@ nsDOMWorkerXHRProxy::GetXMLHttpRequest()
   return mCanceled ? nsnull : mXHR;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::Destroy()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  mCanceled = PR_TRUE;
-
-  ClearEventListeners();
-
   {
     nsAutoLock lock(mWorkerXHR->Lock());
+
+    mCanceled = PR_TRUE;
+
     mLastProgressOrLoadEvent = nsnull;
+    mLastXHREvent = nsnull;
   }
 
   if (mXHR) {
     DestroyInternal();
   }
 
-  mLastXHREvent = nsnull;
+  NS_ASSERTION(!(mLastProgressOrLoadEvent && mLastXHREvent), "Going to leak!");
 
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::InitInternal()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!mXHR, "InitInternal shouldn't be called twice!");
 
-  nsDOMWorkerThread* worker = mWorkerXHR->mWorker;
+  nsDOMWorker* worker = mWorkerXHR->mWorker;
   nsRefPtr<nsDOMWorkerPool> pool = worker->Pool();
 
   if (worker->IsCanceled()) {
     return NS_ERROR_ABORT;
   }
 
   nsIPrincipal* nodePrincipal = pool->ParentDocument()->NodePrincipal();
   nsIScriptContext* scriptContext = pool->ScriptContext();
@@ -742,22 +421,22 @@ nsDOMWorkerXHRProxy::InitInternal()
 
   nsCOMPtr<nsIXMLHttpRequestUpload> upload;
   rv = xhr->GetUpload(getter_AddRefs(upload));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsRefPtr<nsDOMWorkerXHREvent> nullEvent = new nsDOMWorkerXHREvent(this);
   NS_ENSURE_TRUE(nullEvent, NS_ERROR_OUT_OF_MEMORY);
 
-  rv = nullEvent->Init(xhr);
+  rv = nullEvent->SnapshotXHRState(xhr);
   NS_ENSURE_SUCCESS(rv, rv);
+
+  nullEvent->EventHandled();
   mLastXHREvent.swap(nullEvent);
 
-  mLastXHREvent->EventHandled();
-
   xhrConcrete->SetRequestObserver(this);
 
   // We now own mXHR and it owns upload.
   xhr.swap(mXHR);
 
   // Weak refs.
   mUpload = upload;
   mConcreteXHR = xhrConcrete;
@@ -792,17 +471,16 @@ nsDOMWorkerXHRProxy::DestroyInternal()
 
     if (syncFinishedRunnable) {
       syncFinishedRunnable->Dispatch();
     }
   }
 
   NS_ASSERTION(!mOwnedByXHR, "Should have flipped already!");
   NS_ASSERTION(!mSyncFinishedRunnable, "Should have fired this already!");
-  NS_ASSERTION(!mLastProgressOrLoadEvent, "Should have killed this already!");
 
   // mXHR could be null if Init fails.
   if (mXHR) {
     AddRemoveXHRListeners(PR_FALSE);
 
     mXHR->Release();
     mXHR = nsnull;
 
@@ -811,347 +489,171 @@ nsDOMWorkerXHRProxy::DestroyInternal()
 }
 
 void
 nsDOMWorkerXHRProxy::AddRemoveXHRListeners(PRBool aAdd)
 {
   nsCOMPtr<nsIDOMEventTarget> xhrTarget(do_QueryInterface(mXHR));
   NS_ASSERTION(xhrTarget, "This shouldn't fail!");
 
-  EventListenerFunction function = aAdd ?
-                                   &nsIDOMEventTarget::AddEventListener :
-                                   &nsIDOMEventTarget::RemoveEventListener;
+  EventListenerFunction addRemoveEventListener =
+    aAdd ?
+    &nsIDOMEventTarget::AddEventListener :
+    &nsIDOMEventTarget::RemoveEventListener;
 
   nsAutoString eventName;
   PRUint32 index = 0;
 
   if (mWantUploadListeners) {
     nsCOMPtr<nsIDOMEventTarget> uploadTarget(do_QueryInterface(mUpload));
     NS_ASSERTION(uploadTarget, "This shouldn't fail!");
 
     for (; index < MAX_UPLOAD_LISTENER_TYPE; index++) {
       eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
-      (xhrTarget.get()->*function)(eventName, this, PR_FALSE);
-      (uploadTarget.get()->*function)(eventName, this, PR_FALSE);
+      (xhrTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
+      (uploadTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
     }
   }
 
   for (; index < MAX_XHR_LISTENER_TYPE; index++) {
     eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
-    (xhrTarget.get()->*function)(eventName, this, PR_FALSE);
+    (xhrTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
   }
 }
 
 void
 nsDOMWorkerXHRProxy::FlipOwnership()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   // Flip!
   mOwnedByXHR = !mOwnedByXHR;
 
   // If mWorkerXHR has no outstanding refs from JS then we are about to die.
   // Hold an extra ref here to make sure that we live through this call.
   nsRefPtr<nsDOMWorkerXHRProxy> kungFuDeathGrip(this);
 
   if (mOwnedByXHR) {
-    mWorkerXHR->AddRef();
+    mWorkerXHRWN = mWorkerXHR->GetWrappedNative();
+    NS_ASSERTION(mWorkerXHRWN, "Null pointer!");
     mXHR->Release();
   }
   else {
     mXHR->AddRef();
-    mWorkerXHR->Release();
+    mWorkerXHRWN = nsnull;
   }
 }
 
 nsresult
-nsDOMWorkerXHRProxy::AddEventListener(PRUint32 aType,
-                                      nsIDOMEventListener* aListener,
-                                      PRBool aOnXListener,
-                                      PRBool aUploadListener)
+nsDOMWorkerXHRProxy::UploadEventListenerAdded()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
-  if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
-      aType >= MAX_XHR_LISTENER_TYPE) {
-    // Silently fail on junk events.
+  // If this is the first time we're setting an upload listener then we have to
+  // hit the main thread to attach the upload listeners. Otherwise there's
+  // nothing to do here.
+  if (mWantUploadListeners) {
     return NS_OK;
   }
 
-  ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
-                                               mXHRListeners[aType];
-  WrappedListener& onXListener = aUploadListener ? mUploadOnXListeners[aType] :
-                                                   mXHROnXListeners[aType];
-
-  {
-    nsAutoLock lock(mWorkerXHR->Lock());
-
-    if (mCanceled) {
-      return NS_ERROR_ABORT;
-    }
-
-#ifdef DEBUG
-    if (!aListener) {
-      NS_ASSERTION(aOnXListener, "Shouldn't pass a null listener!");
-    }
-#endif
+  nsRefPtr<nsDOMWorkerXHRAttachUploadListenersRunnable> attachRunnable =
+    new nsDOMWorkerXHRAttachUploadListenersRunnable(this);
+  NS_ENSURE_TRUE(attachRunnable, NS_ERROR_OUT_OF_MEMORY);
 
-    if (aOnXListener) {
-      // Remove the old one from the array if it exists.
-      if (onXListener) {
-#ifdef DEBUG
-        PRBool removed =
-#endif
-        listeners.RemoveElement(onXListener);
-        NS_ASSERTION(removed, "Should still be in the array!");
-      }
+  nsRefPtr<nsResultReturningRunnable> runnable =
+    new nsResultReturningRunnable(mMainThread, attachRunnable,
+                                  mWorkerXHR->mWorker);
+  NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
 
-      if (!aListener) {
-        onXListener = nsnull;
-        return NS_OK;
-      }
-
-      onXListener = new nsDOMWorkerXHRWrappedListener(aListener);
-      NS_ENSURE_TRUE(onXListener, NS_ERROR_OUT_OF_MEMORY);
-
-      aListener = onXListener;
-    }
-
-    Listener* added = listeners.AppendElement(aListener);
-    NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
+  nsresult rv = runnable->Dispatch();
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
-  // If this is the first time we're setting an upload listener then we have to
-  // hit the main thread to attach the upload listeners.
-  if (aUploadListener && aListener && !mWantUploadListeners) {
-    nsRefPtr<nsDOMWorkerXHRAttachUploadListenersRunnable> attachRunnable =
-      new nsDOMWorkerXHRAttachUploadListenersRunnable(this);
-    NS_ENSURE_TRUE(attachRunnable, NS_ERROR_OUT_OF_MEMORY);
-
-    nsRefPtr<nsResultReturningRunnable> runnable =
-      new nsResultReturningRunnable(mMainThread, attachRunnable,
-                                    mWorkerXHR->mWorker);
-    NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
-
-    nsresult rv = runnable->Dispatch();
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    NS_ASSERTION(mWantUploadListeners, "Should have set this!");
-  }
-
+  NS_ASSERTION(mWantUploadListeners, "Should have set this!");
   return NS_OK;
 }
 
 nsresult
-nsDOMWorkerXHRProxy::RemoveEventListener(PRUint32 aType,
-                                         nsIDOMEventListener* aListener,
-                                         PRBool aUploadListener)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aListener, "Null pointer!");
-
-  if (mCanceled) {
-    return NS_ERROR_ABORT;
-  }
-
-  if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
-      aType >= MAX_XHR_LISTENER_TYPE) {
-    // Silently fail on junk events.
-    return NS_OK;
-  }
-
-  ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
-                                               mXHRListeners[aType];
-
-  nsAutoLock lock(mWorkerXHR->Lock());
-
-  listeners.RemoveElement(aListener);
-
-  return NS_OK;
-}
-
-already_AddRefed<nsIDOMEventListener>
-nsDOMWorkerXHRProxy::GetOnXListener(PRUint32 aType,
-                                    PRBool aUploadListener)
-{
-  if (mCanceled) {
-    return nsnull;
-  }
-
-  if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
-      aType >= MAX_XHR_LISTENER_TYPE) {
-    // Silently fail on junk events.
-    return nsnull;
-  }
-
-  WrappedListener& onXListener = aUploadListener ? mUploadOnXListeners[aType] :
-                                                   mXHROnXListeners[aType];
-
-  nsAutoLock lock(mWorkerXHR->Lock());
-
-  nsCOMPtr<nsIDOMEventListener> listener = onXListener->Inner();
-  return listener.forget();
-}
-
-nsresult
 nsDOMWorkerXHRProxy::HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent,
                                        PRBool aUploadEvent)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aEvent, "Should not be null!");
 
-  if (mCanceled ||
-      (aEvent->mChannelID != -1 && aEvent->mChannelID != mChannelID)) {
-    return NS_OK;
+  {
+    nsAutoLock lock(mWorkerXHR->Lock());
+
+    if (mCanceled ||
+        (aEvent->mChannelID != -1 && aEvent->mChannelID != mChannelID)) {
+      return NS_OK;
+    }
+
+    mLastXHREvent = aEvent;
   }
 
-  mLastXHREvent = aEvent;
-
-  return HandleEventInternal(aEvent->mType, aEvent, aUploadEvent);
-}
-
-nsresult
-nsDOMWorkerXHRProxy::HandleWorkerEvent(nsIDOMEvent* aEvent,
-                                       PRBool aUploadEvent)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aEvent, "Should not be null!");
-
-  nsString typeString;
-  nsresult rv = aEvent->GetType(typeString);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint32 maxType = aUploadEvent ? MAX_UPLOAD_LISTENER_TYPE :
-                                    MAX_XHR_LISTENER_TYPE;
-
-  PRUint32 type =
-    nsDOMWorkerXHREventTarget::GetListenerTypeFromString(typeString);
-  if (type >= maxType) {
-    // Silently fail on junk events.
-    return NS_OK;
-  }
-
-  return HandleEventInternal(type, aEvent, aUploadEvent);
+  nsIDOMEvent* event = static_cast<nsDOMWorkerEvent*>(aEvent);
+  return HandleEventInternal(aEvent->mXHREventType, event, aUploadEvent);
 }
 
 nsresult
 nsDOMWorkerXHRProxy::HandleEventInternal(PRUint32 aType,
                                          nsIDOMEvent* aEvent,
                                          PRBool aUploadListener)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aEvent, "Should not be null!");
 
 #ifdef DEBUG
   if (aUploadListener) {
     NS_ASSERTION(aType < MAX_UPLOAD_LISTENER_TYPE, "Bad type!");
+    NS_ASSERTION(mWorkerXHR->mUpload, "No upload object!");
   }
   else {
     NS_ASSERTION(aType < MAX_XHR_LISTENER_TYPE, "Bad type!");
   }
 #endif
 
   if (mCanceled) {
     return NS_ERROR_ABORT;
   }
 
-  ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
-                                               mXHRListeners[aType];
-
-  nsAutoTArray<Listener, 10> listenerCopy;
-  PRUint32 count;
-
-  {
-    nsAutoLock lock(mWorkerXHR->Lock());
-
-    count = listeners.Length();
-    if (!count) {
-      return NS_OK;
-    }
-
-    Listener* copied = listenerCopy.AppendElements(listeners);
-    NS_ENSURE_TRUE(copied, NS_ERROR_OUT_OF_MEMORY);
-  }
-
-  for (PRUint32 index = 0; index < count; index++) {
-    NS_ASSERTION(listenerCopy[index], "Null listener?!");
-    listenerCopy[index]->HandleEvent(aEvent);
-  }
-
-  return NS_OK;
-}
-
-void
-nsDOMWorkerXHRProxy::ClearEventListeners()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  nsIDOMEventTarget* target = aUploadListener ?
+    static_cast<nsDOMWorkerMessageHandler*>(mWorkerXHR->mUpload) :
+    static_cast<nsDOMWorkerMessageHandler*>(mWorkerXHR);
 
-  nsTArray<ListenerArray> doomedArrays;
-  PRBool ok = doomedArrays.SetLength(MAX_XHR_LISTENER_TYPE +
-                                     MAX_UPLOAD_LISTENER_TYPE);
-  NS_ENSURE_TRUE(ok,);
-
-  nsTArray<WrappedListener> doomedListeners;
-  ok = doomedListeners.SetLength(MAX_XHR_LISTENER_TYPE +
-                                 MAX_UPLOAD_LISTENER_TYPE);
-  NS_ENSURE_TRUE(ok,);
-
-  {
-    PRUint32 listenerIndex, doomedIndex;
-
-    nsAutoLock lock(mWorkerXHR->Lock());
-
-    for (listenerIndex = 0, doomedIndex = 0;
-         listenerIndex < MAX_UPLOAD_LISTENER_TYPE;
-         listenerIndex++, doomedIndex++) {
-      mUploadListeners[listenerIndex].
-        SwapElements(doomedArrays[doomedIndex]);
-      mUploadOnXListeners[listenerIndex].
-        swap(doomedListeners[doomedIndex]);
-    }
-
-    for (listenerIndex = 0;
-         listenerIndex < MAX_XHR_LISTENER_TYPE;
-         listenerIndex++, doomedIndex++) {
-      mXHRListeners[listenerIndex].
-        SwapElements(doomedArrays[doomedIndex]);
-      mXHROnXListeners[listenerIndex].
-        swap(doomedListeners[doomedIndex]);
-    }
-  }
-
-  // Destructors for the nsTArrays actually kill the listeners outside of the
-  // lock.
+  return target->DispatchEvent(aEvent, nsnull);
 }
 
 PRBool
-nsDOMWorkerXHRProxy::HasListenersForType(PRUint32 aType,
+nsDOMWorkerXHRProxy::HasListenersForType(const nsAString& aType,
                                          nsIDOMEvent* aEvent)
 {
-  NS_ASSERTION(aType < MAX_XHR_LISTENER_TYPE, "Bad type!");
+#ifdef DEBUG
+  PRUint32 type = nsDOMWorkerXHREventTarget::GetListenerTypeFromString(aType);
+  NS_ASSERTION(type < MAX_XHR_LISTENER_TYPE, "Bad type!");
+#endif
 
-  if (mXHRListeners[aType].Length()) {
+  if (mWorkerXHR->HasListeners(aType)) {
     return PR_TRUE;
   }
 
   PRBool checkUploadListeners = PR_FALSE;
   if (aEvent) {
     nsCOMPtr<nsIDOMEventTarget> target;
     if (NS_SUCCEEDED(aEvent->GetTarget(getter_AddRefs(target)))) {
       nsCOMPtr<nsIXMLHttpRequestUpload> upload(do_QueryInterface(target));
       checkUploadListeners = !!upload;
     }
   }
   else {
     checkUploadListeners = PR_TRUE;
   }
 
-  if (checkUploadListeners && mUploadListeners[aType].Length()) {
+  if (checkUploadListeners && mWorkerXHR->mUpload->HasListeners(aType)) {
     return PR_TRUE;
   }
 
   return PR_FALSE;
 }
 
 // nsIDOMEventListener
 NS_IMETHODIMP
@@ -1166,30 +668,39 @@ nsDOMWorkerXHRProxy::HandleEvent(nsIDOME
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRUint32 type =
     nsDOMWorkerXHREventTarget::GetListenerTypeFromString(typeString);
   if (type >= MAX_XHR_LISTENER_TYPE) {
     return NS_OK;
   }
 
-  // When Abort is called on nsXMLHttpRequest (either from a proxied Abort call
-  // or from DestroyInternal) the OnStopRequest call is not run synchronously.
-  // Thankfully an abort event *is* fired synchronously so we can flip our
-  // ownership around and fire the sync finished runnable if we're running in
-  // sync mode.
-  if (type == LISTENER_TYPE_ABORT && mCanceled) {
-    OnStopRequest(nsnull, nsnull, NS_ERROR_ABORT);
-  }
+  if (mCanceled) {
+    // When Abort is called on nsXMLHttpRequest (either from a proxied Abort
+    // call or from DestroyInternal) the OnStopRequest call is not run
+    // synchronously. Thankfully an abort event *is* fired synchronously so we
+    // can flip our ownership around and fire the sync finished runnable if
+    // we're running in sync mode.
+    if (type == LISTENER_TYPE_ABORT) {
+      OnStopRequest(nsnull, nsnull, NS_ERROR_ABORT);
+    }
 
-  if (mCanceled) {
+    // Always bail out if we're canceled.
     return NS_ERROR_ABORT;
   }
 
-  if (!HasListenersForType(type, aEvent)) {
+  PRBool ignoreEvent = !HasListenersForType(typeString, aEvent);
+  if (ignoreEvent && mSyncRequest) {
+    // Only ignore the event if it isn't final.
+    ignoreEvent = type != LISTENER_TYPE_ABORT &&
+                  type != LISTENER_TYPE_ERROR &&
+                  type != LISTENER_TYPE_LOAD;
+  }
+
+  if (ignoreEvent) {
     return NS_OK;
   }
 
   nsRefPtr<nsDOMWorkerXHREvent> newEvent = new nsDOMWorkerXHREvent(this);
   NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY);
 
   rv = newEvent->Init(type, typeString, aEvent);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1197,16 +708,21 @@ nsDOMWorkerXHRProxy::HandleEvent(nsIDOME
   nsCOMPtr<nsIRunnable> runnable(newEvent);
 
   if (type == LISTENER_TYPE_LOAD || type == LISTENER_TYPE_PROGRESS) {
     runnable = new nsDOMWorkerXHRLastProgressOrLoadEvent(this);
     NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
 
     {
       nsAutoLock lock(mWorkerXHR->Lock());
+
+      if (mCanceled) {
+        return NS_ERROR_ABORT;
+      }
+
       mLastProgressOrLoadEvent.swap(newEvent);
 
       if (newEvent) {
         // Already had a saved progress/load event so no need to generate
         // another. Bail out rather than dispatching runnable.
         return NS_OK;
       }
     }
--- a/dom/src/threads/nsDOMWorkerXHRProxy.h
+++ b/dom/src/threads/nsDOMWorkerXHRProxy.h
@@ -40,48 +40,44 @@
 #define __NSDOMWORKERXHRPROXY_H__
 
 // Bases
 #include "nsThreadUtils.h"
 #include "nsIDOMEventListener.h"
 #include "nsIRequestObserver.h"
 
 // Other includes
+#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 
-// DOMWorker includes
-#include "nsDOMWorkerXHR.h"
-
 class nsIJSXMLHttpRequest;
 class nsIThread;
 class nsIVariant;
 class nsIXMLHttpRequest;
+class nsIXMLHttpRequestUpload;
+class nsIXPConnectWrappedNative;
+class nsDOMWorkerXHR;
 class nsDOMWorkerXHREvent;
 class nsDOMWorkerXHRFinishSyncXHRRunnable;
 class nsDOMWorkerXHRWrappedListener;
 class nsXMLHttpRequest;
 
 class nsDOMWorkerXHRProxy : public nsIRunnable,
                             public nsIDOMEventListener,
                             public nsIRequestObserver
 {
   friend class nsDOMWorkerXHRAttachUploadListenersRunnable;
   friend class nsDOMWorkerXHREvent;
   friend class nsDOMWorkerXHRFinishSyncXHRRunnable;
   friend class nsDOMWorkerXHRLastProgressOrLoadEvent;
   friend class nsDOMWorkerXHR;
   friend class nsDOMWorkerXHRUpload;
 
-  typedef nsCOMPtr<nsIDOMEventListener> Listener;
-  typedef nsTArray<Listener> ListenerArray;
-
-  typedef nsRefPtr<nsDOMWorkerXHRWrappedListener> WrappedListener;
-
   typedef nsresult (NS_STDCALL nsIDOMEventTarget::*EventListenerFunction)
     (const nsAString&, nsIDOMEventListener*, PRBool);
 
 public:
   typedef nsAutoTArray<nsCOMPtr<nsIRunnable>, 5> SyncEventQueue;
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMEVENTLISTENER
@@ -113,38 +109,25 @@ protected:
   nsresult InitInternal();
   void DestroyInternal();
 
   nsresult Destroy();
 
   void AddRemoveXHRListeners(PRBool aAdd);
   void FlipOwnership();
 
-  nsresult AddEventListener(PRUint32 aType,
-                            nsIDOMEventListener* aListener,
-                            PRBool aOnXListener,
-                            PRBool aUploadListener);
+  nsresult UploadEventListenerAdded();
 
-  nsresult RemoveEventListener(PRUint32 aType,
-                               nsIDOMEventListener* aListener,
-                               PRBool aUploadListener);
-
-  already_AddRefed<nsIDOMEventListener> GetOnXListener(PRUint32 aType,
-                                                       PRBool aUploadListener);
-
-  nsresult HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent, PRBool aUploadEvent);
-
-  nsresult HandleWorkerEvent(nsIDOMEvent* aEvent, PRBool aUploadEvent);
+  nsresult HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent,
+                             PRBool aUploadEvent);
 
   nsresult HandleEventInternal(PRUint32 aType,
                                nsIDOMEvent* aEvent,
                                PRBool aUploadEvent);
 
-  void ClearEventListeners();
-
   // Methods of nsIXMLHttpRequest that we implement
   nsresult GetAllResponseHeaders(char** _retval);
   nsresult GetResponseHeader(const nsACString& aHeader,
                              nsACString& _retval);
   nsresult Send(nsIVariant* aBody);
   nsresult SendAsBinary(const nsAString& aBody);
   nsresult GetResponseText(nsAString& _retval);
   nsresult GetStatusText(nsACString& _retval);
@@ -157,39 +140,35 @@ protected:
   nsresult SetMultipart(PRBool aMultipart);
   nsresult GetWithCredentials(PRBool* aWithCredentials);
   nsresult SetWithCredentials(PRBool aWithCredentials);
 
   nsresult RunSyncEventLoop();
 
   // aEvent is used to see if we should check upload listeners as well. If left
   // unset we always check upload listeners.
-  PRBool HasListenersForType(PRUint32 aType, nsIDOMEvent* aEvent = nsnull);
+  PRBool HasListenersForType(const nsAString& aType,
+                             nsIDOMEvent* aEvent = nsnull);
 
   // May be weak or strong, check mOwnedByXHR.
   nsDOMWorkerXHR* mWorkerXHR;
+  nsCOMPtr<nsIXPConnectWrappedNative> mWorkerXHRWN;
 
   // May be weak or strong, check mOwnedByXHR.
   nsIXMLHttpRequest* mXHR;
 
   // Always weak!
   nsXMLHttpRequest* mConcreteXHR;
   nsIXMLHttpRequestUpload* mUpload;
 
   nsCOMPtr<nsIThread> mMainThread;
 
   nsRefPtr<nsDOMWorkerXHREvent> mLastXHREvent;
   nsRefPtr<nsDOMWorkerXHREvent> mLastProgressOrLoadEvent;
 
-  nsTArray<ListenerArray> mXHRListeners;
-  nsTArray<WrappedListener> mXHROnXListeners;
-
-  nsTArray<ListenerArray> mUploadListeners;
-  nsTArray<WrappedListener> mUploadOnXListeners;
-
   SyncEventQueue* mSyncEventQueue;
 
   PRInt32 mChannelID;
 
   // Only touched on the worker thread!
   nsCOMPtr<nsIThread> mSyncXHRThread;
 
   // Touched on more than one thread, protected by the worker's lock.
--- a/dom/src/threads/test/Makefile.in
+++ b/dom/src/threads/test/Makefile.in
@@ -42,26 +42,38 @@ srcdir           = @srcdir@
 VPATH            = @srcdir@
 
 relativesrcdir   = dom/src/threads/tests
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
+  test_importScripts.html \
   importScripts_worker.js \
   importScripts_worker_imported1.js \
   importScripts_worker_imported2.js \
   importScripts_worker_imported3.js \
   importScripts_worker_imported4.js \
-  test_importScripts.html \
+  test_longThread.html \
+  longThread_worker.js \
+  test_recursion.html \
+  recursion_worker.js \
+  test_regExpStatics.html \
+  regExpStatics_worker.js \
   test_simpleThread.html \
+  simpleThread_worker.js \
   test_threadErrors.html \
+  threadErrors_worker1.js \
+  threadErrors_worker2.js \
+  threadErrors_worker3.js \
+  threadErrors_worker4.js \
   test_threadTimeouts.html \
-  test_longThread.html \
-  test_recursion.html \
-  test_regExpStatics.html \
+  threadTimeouts_worker.js \
   test_xhr.html \
+  xhr_worker.js \
   testXHR.txt \
+  test_fibonacci.html \
+  fibonacci_worker.js \
   $(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/test/fibonacci_worker.js
@@ -0,0 +1,28 @@
+var results = [];
+
+function messageHandler(event) {
+  results.push(parseInt(event.data));
+  if (results.length == 2) {
+    postMessage(results[0] + results[1]);
+  }
+}
+
+function errorHandler(event) {
+  throw event.data;
+}
+
+onmessage = function(event) {
+  var n = parseInt(event.data);
+
+  if (n == 0 || n == 1) {
+    postMessage(n);
+    return;
+  }
+
+  for (var i = 1; i <= 2; i++) {
+    var worker = new Worker("fibonacci_worker.js");
+    worker.onmessage = messageHandler;
+    worker.onerror = errorHandler;
+    worker.postMessage(n - i);
+  }
+}
--- a/dom/src/threads/test/importScripts_worker.js
+++ b/dom/src/threads/test/importScripts_worker.js
@@ -1,30 +1,30 @@
-function messageListener(message, source) {
-  switch (message) {
+onmessage = function(event) {
+  switch (event.data) {
     case 'start':
-      loadScripts("importScripts_worker_imported2.js");
+      importScripts("importScripts_worker_imported2.js");
       importedScriptFunction2();
       tryBadScripts();
-      source.postMessage('started');
+      postMessage('started');
       break;
     case 'stop':
       tryBadScripts();
-      postMessageToPool('stopped');
+      postMessage('stopped');
       break;
     default:
-      throw new Error("Bad message: " + message);
+      throw new Error("Bad message: " + event.data);
       break;
   }
 }
 
 // This caused security exceptions in the past, make sure it doesn't!
 var constructor = {}.constructor;
 
-loadScripts("importScripts_worker_imported1.js");
+importScripts("importScripts_worker_imported1.js");
 
 // Try to call a function defined in the imported script.
 importedScriptFunction();
 
 function tryBadScripts() {
   var badScripts = [
     // Has a syntax error
     "importScripts_worker_imported3.js",
@@ -35,17 +35,17 @@ function tryBadScripts() {
     // Not a valid url
     "http://flippety::foo_js ftw"
   ];
 
   for (var i = 0; i < badScripts.length; i++) {
     var caughtException = false;
     var url = badScripts[i];
     try {
-      loadScripts(url);
+      importScripts(url);
     }
     catch (e) {
       caughtException = true;
     }
     if (!caughtException) {
       throw "Bad script didn't throw exception: " + url;
     }
   }
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/test/longThread_worker.js
@@ -0,0 +1,10 @@
+onmessage = function(event) {
+  switch (event.data) {
+    case "start":
+      for (var i = 0; i < 10000000; i++) { };
+      postMessage("done");
+      break;
+    default:
+      throw "Bad message: " + event.data;
+  }
+};
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/test/recursion_worker.js
@@ -0,0 +1,14 @@
+function recurse() {
+  recurse();
+}
+
+onmessage = function(event) {
+  switch (event.data) {
+    case "start":
+      recurse();
+      throw "Never should have gotten here!";
+      break;
+    default:
+      throw "Bad message: " + event.data;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/test/regExpStatics_worker.js
@@ -0,0 +1,26 @@
+var runCount = 0;
+var timeout;
+
+onmessage = function() {
+  run();
+  timeout = setTimeout(run, 0);
+  timeout = setTimeout(run, 5000);
+};
+
+function run() {
+  if (RegExp.$1) {
+    throw "RegExp.$1 already set!";
+    cancelTimeout(timeout);
+  }
+
+  var match = /a(sd)f/("asdf");
+  if (!RegExp.$1) {
+    throw "RegExp.$1 didn't get set!";
+    cancelTimeout(timeout);
+  }
+
+  if (++runCount == 3) {
+    postMessage("done");
+  }
+}
+
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/test/simpleThread_worker.js
@@ -0,0 +1,20 @@
+function messageListener(event) {
+  switch (event.data) {
+    case "no-op":
+      break;
+    case "start":
+      for (var i = 0; i < 1000; i++) { }
+      postMessage("started");
+      break;
+    case "stop":
+      for (var i = 0; i < 1000; i++) { }
+      self.postMessage('no-op');
+      postMessage("stopped");
+      self.removeEventListener("message", messageListener, false);
+      break;
+    default:
+      throw 'Bad message: ' + event.data;
+  }
+}
+
+addEventListener("message", messageListener, false);
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/test/test_fibonacci.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads with Fibonacci
+-->
+<head>
+  <title>Test for DOM Worker Threads with Fibonacci</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=450452">DOM Worker Threads Fibonacci</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+  var worker = new Worker("fibonacci_worker.js");
+
+  worker.onmessage = function(event) {
+    is(event.target, worker);
+    is(event.data, 5);
+    SimpleTest.finish();
+  };
+
+  worker.onerror = function(event) {
+    is(event.target, worker);
+    ok(false, "Worker had an error: " + event.data);
+    SimpleTest.finish();
+  };
+
+  worker.postMessage(5);
+
+  SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/src/threads/test/test_importScripts.html
+++ b/dom/src/threads/test/test_importScripts.html
@@ -13,37 +13,38 @@ Tests of DOM Worker Threads (Bug 437152)
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-  var pool = navigator.newWorkerPool();
-  pool.messageListener = function(message, source) {
-    switch (message) {
+  var worker = new Worker("importScripts_worker.js");
+
+  worker.onmessage = function(event) {
+    switch (event.data) {
       case "started":
-        source.postMessage("stop");
+        worker.postMessage("stop");
         break;
       case "stopped":
         SimpleTest.finish();
         break;
       default:
         ok(false, "Unexpected message:" + message);
         SimpleTest.finish();
     }
   };
 
-  pool.errorListener = function(error, source) {
-    ok(false, "Worker had an error:" + error);
+  worker.onerror = function(event) {
+    ok(false, "Worker had an error:" + event.data);
     SimpleTest.finish();
   }
 
-  var worker = pool.createWorkerFromURL("importScripts_worker.js");
   worker.postMessage("start");
 
   SimpleTest.waitForExplicitFinish();
+
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/src/threads/test/test_longThread.html
+++ b/dom/src/threads/test/test_longThread.html
@@ -13,70 +13,43 @@ Tests of DOM Worker Threads (Bug 437152)
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-
-  var prefs = Components.classes["@mozilla.org/preferences-service;1"].
-              getService(Components.interfaces.nsIPrefBranch);
-  //prefs.setIntPref("javascript.options.gczeal", 2);
-
-  var workerScript =
-    "function messageListener(message, source) { " +
-    "  switch (message) { " +
-    "    case 'start': " +
-    "      /* do a ton of stuff! */ " +
-    "      for (var i = 0; i < 10000000; i++) { } " +
-    "      dump('done!\\n'); " +
-    "      /* pass message to source */ " +
-    "      source.postMessage('done'); " +
-    "      break; " +
-    "    default: " +
-    "      throw 'Bad message: ' + message; " +
-    "  } " +
-    "} " +
-    "";
-
-  var pool = navigator.newWorkerPool();
-  ok(pool, "Couldn't get worker pool");
-
-  const numThreads = 10;
+  const numThreads = 5;
   var doneThreads = 0;
 
-  pool.messageListener = function(message, source) {
-    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-    switch (message) {
+  function onmessage(event) {
+    switch (event.data) {
       case "done":
         if (++doneThreads == numThreads) {
-          prefs.setIntPref("javascript.options.gczeal", 0);
           SimpleTest.finish();
         }
         break;
       default:
         ok(false, "Unexpected message");
-        prefs.setIntPref("javascript.options.gczeal", 0);
         SimpleTest.finish();
     }
-  };
+  }
 
-  pool.errorListener = function(error, source) {
-    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+  function onerror(event) {
     ok(false, "Worker had an error");
-    prefs.setIntPref("javascript.options.gczeal", 0);
     SimpleTest.finish();
-  };
+  }
 
   for (var i = 0; i < numThreads; i++) {
-    var worker = pool.createWorker(workerScript);
+    var worker = new Worker("longThread_worker.js");
+    worker.onmessage = onmessage;
+    worker.onerror = onerror;
     worker.postMessage("start");
   }
 
   SimpleTest.waitForExplicitFinish();
+
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/src/threads/test/test_recursion.html
+++ b/dom/src/threads/test/test_recursion.html
@@ -12,41 +12,26 @@ Tests of DOM Worker Threads
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-  function workerScript() {
-    function recurse() {
-      recurse();
-    }
+  var worker = new Worker("recursion_worker.js");
 
-    this.messageListener = function(message, source) {
-      switch (message) {
-        case "start":
-          recurse();
-          break;
-        default:
-      }
-    }
-  }
-
-  var pool = navigator.newWorkerPool();
-
-  pool.errorListener = function(error, source) {
-    is(error.message,
-       '[JavaScript Error: "too much recursion" {file: "DOMWorker inline ' +
-       'script" line: 4}]');
+  worker.onerror = function(event) {
+    dump(event.data + "\n");
+    is(event.data,
+       '[JavaScript Error: "too much recursion" {file: "http://localhost:8888' +
+       '/tests/dom/src/threads/tests/recursion_worker.js" line: 2}]');
     SimpleTest.finish();
   }
 
-  var worker = pool.createWorker("(" + workerScript + ")();");
   worker.postMessage("start");
 
   SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/src/threads/test/test_regExpStatics.html
+++ b/dom/src/threads/test/test_regExpStatics.html
@@ -14,60 +14,32 @@ Tests of DOM Worker Threads RegExp stati
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
   const WORKER_COUNT = 25;
 
-  function workerScript() {
-    var runCount = 0;
-    var timeout;
-
-    this.messageListener = function(message, source) {
-      run();
-      timeout = setTimeout(run, 0);
-      timeout = setTimeout(run, 5000);
-    };
-
-    function run() {
-      if (RegExp.$1) {
-        throw "RegExp.$1 already set!";
-        cancelTimeout(timeout);
-      }
-
-      var match = /a(sd)f/("asdf");
-      if (!RegExp.$1) {
-        throw "RegExp.$1 didn't get set!";
-        cancelTimeout(timeout);
-      }
-
-      if (++runCount == 3) {
-        postMessageToPool("done");
-      }
+  var doneWorkers = 0;
+  function onmessage(event) {
+    if (++doneWorkers == WORKER_COUNT) {
+      SimpleTest.finish();
     }
   }
 
-  var pool = navigator.newWorkerPool();
-
-  var doneWorkers = 0;
-  pool.messageListener = function(message, source) {
-    if (++doneWorkers == WORKER_COUNT) {
-      SimpleTest.finish();
-    }
-  };
-
-  pool.errorListener = function(error, source) {
-    ok(false, "Worker had an error: " + error);
+  function onerror(event) {
+    ok(false, "Worker had an error: " + event.data);
     SimpleTest.finish();
   };
 
   for (var i = 0; i < WORKER_COUNT; i++) {
-    var worker = pool.createWorker("(" + workerScript + ")();");
+    var worker = new Worker("regExpStatics_worker.js");
+    worker.onmessage = onmessage;
+    worker.onerror = onerror;
     worker.postMessage("start");
   }
 
   SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
--- a/dom/src/threads/test/test_simpleThread.html
+++ b/dom/src/threads/test/test_simpleThread.html
@@ -13,85 +13,53 @@ Tests of DOM Worker Threads (Bug 437152)
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-
-  var prefs = Components.classes["@mozilla.org/preferences-service;1"].
-              getService(Components.interfaces.nsIPrefBranch);
-  //prefs.setIntPref("javascript.options.gczeal", 2);
+  var worker = new Worker("simpleThread_worker.js");
 
-  var workerScript =
-    "function messageListener(message, source) { " +
-    "  switch (message) { " +
-    "    case 'no-op': " +
-    "      break; " +
-    "    case 'start': " +
-    "      /* do a ton of stuff! */ " +
-    "      for (var i = 0; i < 1000; i++) { } " +
-    "      /* pass message to source */ " +
-    "      source.postMessage('started'); " +
-    "      break; " +
-    "    case 'stop': " +
-    "      /* do some more stuff! */ " +
-    "      for (var i = 0; i < 1000; i++) { } " +
-    "      /* pass message to self */ " +
-    "      threadContext.thisThread.postMessage('no-op'); " +
-    "      /* pass message to pool */ " +
-    "      postMessageToPool('stopped'); " +
-    "      break; " +
-    "    default: " +
-    "      throw 'Bad message: ' + message; " +
-    "  } " +
-    "} " +
-    "";
-
-  var pool = navigator.newWorkerPool();
-  ok(pool, "Couldn't get worker pool");
-
-  pool.messageListener = function(message, source) {
-    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-    switch (message) {
+  worker.addEventListener("message",function(event) {
+    is(event.target, worker);
+    switch (event.data) {
       case "no-op":
         break;
       case "started":
-        // pass message to self
-        pool.postMessage("no-op");
-        // pass message to source
-        source.postMessage("stop");
+        is(gotError, true);
+        worker.postMessage("no-op");
+        worker.postMessage("stop");
         break;
       case "stopped":
-        // pass message to worker
         worker.postMessage("no-op");
-        prefs.setIntPref("javascript.options.gczeal", 0);
         SimpleTest.finish();
         break;
       default:
-        ok(false, "Unexpected message");
-        prefs.setIntPref("javascript.options.gczeal", 0);
+        ok(false, "Unexpected message:" + event.data);
         SimpleTest.finish();
     }
+  }, false);
+
+  var gotError = false;
+  worker.onerror = function(event) {
+    is(event.target, worker);
+    is(event.data,
+       '[JavaScript Error: "uncaught exception: Bad message: asdf"]');
+    gotError = true;
+
+    worker.onerror = function(otherEvent) {
+      ok(false, "Worker had an error:" + otherEvent.data);
+      SimpleTest.finish();
+    }
   };
 
-  pool.errorListener = function(error, source) {
-    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-    ok(false, "Worker had an error");
-    prefs.setIntPref("javascript.options.gczeal", 0);
-    SimpleTest.finish();
-  };
-
-  var worker = pool.createWorker(workerScript);
-  ok(worker