Bug 460811 - 'Bring workers up to latest spec'.r+sr=jst.
authorBen Turner <bent.mozilla@gmail.com>
Wed, 05 Nov 2008 22:41:52 -0800
changeset 21376 9f77a26afd904f4349f40df51eda194e31b50b6b
parent 21375 7fc3096ef585821f0453febb4073f79bb56e498d
child 21377 6004a7f6e478fd62fae513c832ac58f4967204ae
push id3514
push userbturner@mozilla.com
push dateThu, 06 Nov 2008 06:43:07 +0000
treeherdermozilla-central@6004a7f6e478 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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/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/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/importScripts_worker.js
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
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;
-};
--- 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 == 1) {
+    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);
@@ -3818,34 +3838,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')
@@ -9433,23 +9454,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;
 };
--- 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)
--- 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;
     }
   }
--- 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, "Couldn't make worker");
-
-  pool.postMessage("no-op");
+  worker.postMessage("asdf");
   worker.postMessage("start");
 
   SimpleTest.waitForExplicitFinish();
+
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/src/threads/test/test_threadErrors.html
+++ b/dom/src/threads/test/test_threadErrors.html
@@ -13,86 +13,47 @@ 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 badWorkerScripts = [
-    // Syntax error
-    "function messageListener(message, source) { " +
-    "  for (var i = 0; i < 10) { } " +
-    "} " +
-    "",
-
-    // Bad function error
-    "function messageListener(message, source) { " +
-    "  foopy(); " +
-    "} " +
-    "",
+  const expectedErrorCount = 4;
 
-    // Unhandled exception in body
-    "function messageListener(message, source) { " +
-    "} " +
-    "throw new Error('Bah!'); " +
-    "",
-
-    // Throwing message listener
-    "function messageListener(message, source) { " +
-    "  throw 'Bad message: ' + message; " +
-    "} " +
-    ""
-  ];
-
-  var expectedErrorCount = badWorkerScripts.length;
-
-  var pool = navigator.newWorkerPool();
-
-  pool.messageListener = function(message, source) {
-    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-
-    prefs.setIntPref("javascript.options.gczeal", 0);
-    ok(false, "Unexpected message");
+  function messageListener(event) {
+    ok(false, "Unexpected message: " + event.data);
     SimpleTest.finish();
   };
 
   var actualErrorCount = 0;
   var failedWorkers = [];
 
-  pool.errorListener = function(error, source) {
-    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-
-    if (failedWorkers.indexOf(source) != -1) {
-      dump("Already seen worker: " + source + "\n");
+  function errorListener(event) {
+    if (failedWorkers.indexOf(event.target) != -1) {
       ok(false, "Seen an extra error from this worker");
-      prefs.setIntPref("javascript.options.gczeal", 0);
       SimpleTest.finish();
       return;
     }
 
-    failedWorkers.push(source);
+    failedWorkers.push(event.target);
     actualErrorCount++;
 
     if (actualErrorCount == expectedErrorCount) {
-      prefs.setIntPref("javascript.options.gczeal", 0);
       SimpleTest.finish();
     }
   };
 
-  for (var i = 0; i < expectedErrorCount; i++) {
-    var worker = pool.createWorker(badWorkerScripts[i]);
+  for (var i = 1; i <= expectedErrorCount; i++) {
+    var worker = new Worker("threadErrors_worker" + i + ".js");
+    worker.onmessage = messageListener;
+    worker.onerror = errorListener;
     worker.postMessage("Hi");
   }
 
   SimpleTest.waitForExplicitFinish();
+
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/src/threads/test/test_threadTimeouts.html
+++ b/dom/src/threads/test/test_threadTimeouts.html
@@ -13,93 +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 worker = new Worker("threadTimeouts_worker.js");
 
-  var workerScript =
-    "var gTimeoutId; " +
-    "var gTimeoutCount = 0; " +
-    "var gIntervalCount = 0; " +
-    "" +
-    "function timeoutFunc() { " +
-    "  if (++gTimeoutCount > 1) { " +
-    "    throw new Error('Timeout called more than once!'); " +
-    "  } " +
-    "  postMessageToPool('timeoutFinished'); " +
-    "} " +
-    "" +
-    "function intervalFunc() { " +
-    "  if (++gIntervalCount == 2) { " +
-    "    postMessageToPool('intervalFinished'); " +
-    "  } " +
-    "} " +
-    "" +
-    "function messageListener(message, source) { " +
-    "  switch (message) { " +
-    "    case 'startTimeout': " +
-    "      gTimeoutId = setTimeout(timeoutFunc, 5000); " +
-    "      clearTimeout(gTimeoutId); " +
-    "      gTimeoutId = setTimeout(timeoutFunc, 5000); " +
-    "      break; " +
-    "    case 'startInterval': " +
-    "      gTimeoutId = setInterval(intervalFunc, 5000); " +
-    "      break; " +
-    "    case 'cancelInterval': " +
-    "      clearInterval(gTimeoutId); " +
-    "      postMessageToPool('intervalCanceled'); " +
-    "      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.onmessage = function(event) {
+    is(event.target, worker);
+    switch (event.data) {
       case "timeoutFinished":
-        source.postMessage("startInterval");
+        event.target.postMessage("startInterval");
         break;
       case "intervalFinished":
-        source.postMessage("cancelInterval");
+        event.target.postMessage("cancelInterval");
         break;
       case "intervalCanceled":
-        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');
-    ok(false, "Worker had an error");
-    prefs.setIntPref("javascript.options.gczeal", 0);
+  worker.onerror = function(event) {
+    is(event.target, worker);
+    ok(false, "Worker had an error: " + event.data);
     SimpleTest.finish();
   };
 
-  var worker = pool.createWorker(workerScript);
-  ok(worker, "Couldn't make worker");
-
   worker.postMessage("startTimeout");
 
   SimpleTest.waitForExplicitFinish();
+
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/src/threads/test/test_xhr.html
+++ b/dom/src/threads/test/test_xhr.html
@@ -13,76 +13,21 @@ Tests of DOM Worker Threads XHR(Bug 4504
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=450452">DOM Worker Threads XHR (Bug 450452)</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-  function workerScript() {
-    var xhr = new XMLHttpRequest();
-
-    function onload(event) {
-      if (event.target.status != 200) {
-        var message = { type: "error",
-                        error: event.target.status };
-        postMessageToPool(message.toSource());
-      }
-
-      var message = { type: "load",
-                      data: xhr.responseText };
-      postMessageToPool(message.toSource());
-    }
-
-    xhr.onload = onload;
-    xhr.addEventListener("load", onload, false);
-    xhr.removeEventListener("load", onload, false);
-    if (!xhr.onload) {
-      var message = { type: "error",
-                      error: "Lost message listener!" };
-      postMessageToPool(message.toSource());
-    }
-
-    xhr.addEventListener("error", function(event) {
-      var message = { type: "error",
-                      error: event.target.status };
-      postMessageToPool(message.toSource());
-    }, false);
+  var worker = new Worker("xhr_worker.js");
 
-    function onprogress(event) {
-      var message = { type: "progress",
-                      current: event.loaded,
-                      total: event.total };
-      postMessageToPool(message.toSource());
-    }
-    xhr.addEventListener("progress", onprogress, false);
-
-    xhr.addEventListener("foopety", function(event) {}, false);
-    xhr.removeEventListener("doopety", function(event) {}, false);
-
-    var upload = xhr.upload;
-    upload.onprogress = function(event) { };
-    upload.addEventListener("readystatechange", function(event) { }, false);
-    upload.removeEventListener("readystatechange", function(event) { }, false);
-    upload.addEventListener("load", function(event) { }, false);
-    upload.removeEventListener("readystatechange", function(event) { }, false);
-
-    this.messageListener = function(message, source) {
-      if (xhr.readystate > 0) {
-        throw "XHR already running!";
-      }
-      xhr.open("GET", message);
-      xhr.send(null);
-    }
-  }
-
-  var pool = navigator.newWorkerPool();
-  pool.messageListener = function(message, source) {
-    var args = eval(message);
+  worker.onmessage = function(event) {
+    is(event.target, worker);
+    var args = eval(event.data);
     switch (args.type) {
       case "progress": {
         ok(parseInt(args.current) <= parseInt(args.total));
       } break;
       case "error": {
         ok(false, "XHR error: " + args.error);
       } break;
       case "load": {
@@ -92,22 +37,22 @@ Tests of DOM Worker Threads XHR(Bug 4504
       } break;
       default: {
         ok(false, "Unexpected message");
         SimpleTest.finish();
       }
     }
   };
 
-  pool.errorListener = function(error, source) {
-    ok(false, "Worker had an error:" + error);
+  worker.onerror = function(event) {
+    is(event.target, worker);
+    ok(false, "Worker had an error:" + event.data);
     SimpleTest.finish();
   }
 
-  var worker = pool.createWorker("(" + workerScript + ")();");
   worker.postMessage("testXHR.txt");
 
   SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
--- a/js/src/xpconnect/src/xpcwrappednativescope.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp
@@ -739,16 +739,19 @@ GetScopeOfObject(JSObject* obj)
 
 #ifdef DEBUG
 void DEBUG_CheckForComponentsInScope(XPCCallContext& ccx, JSObject* obj,
                                      JSBool OKIfNotInitialized)
 {
     if(OKIfNotInitialized)
         return;
 
+    if(!(JS_GetOptions(ccx) & JSOPTION_PRIVATE_IS_NSISUPPORTS))
+        return;
+
     const char* name = ccx.GetRuntime()->GetStringName(XPCJSRuntime::IDX_COMPONENTS);
     jsval prop;
     if(JS_LookupProperty(ccx, obj, name, &prop) && !JSVAL_IS_PRIMITIVE(prop))
         return;
 
     // This is pretty much always bad. It usually means that native code is
     // making a callback to an interface implemented in JavaScript, but the
     // document where the JS object was created has already been cleared and the