Back out changeset b83d3c8ac166 (bug 460811) to try to fix bustage
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 05 Nov 2008 12:47:52 +1300
changeset 21328 1a2fefd12905bbc6aae0b495c005959593b11fce
parent 21327 3afda1e3f55a366b48da9681fa9e4ce07648d56e
child 21329 b5cba763fe6da4cd635d05a00ac63a13e62a3371
push id3477
push userrocallahan@mozilla.com
push dateTue, 04 Nov 2008 23:48:43 +0000
treeherdermozilla-central@1a2fefd12905 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs460811
milestone1.9.1b2pre
backs outb83d3c8ac166e5f92f36fa44df498e74efd41832
Back out changeset b83d3c8ac166 (bug 460811) to try to fix bustage
dom/public/idl/base/nsIDOMNavigator.idl
dom/public/idl/threads/Makefile.in
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/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/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,17 +34,19 @@
  * 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"
 
-[scriptable, uuid(777bd8a1-38c1-4b12-ba8f-ff6c2eb8c56b)]
+interface nsIDOMWorkerPool;
+
+[scriptable, uuid(c206f746-52e2-47dd-8ccc-ce76ccda6c6d)]
 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;
@@ -58,16 +60,18 @@ 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    =  nsIDOMWorkers.idl
+XPIDLSRCS    =  nsIDOMThreads.idl
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/public/nsDOMClassInfoID.h
+++ b/dom/public/nsDOMClassInfoID.h
@@ -453,18 +453,16 @@ 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,19 +452,16 @@
 #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);
@@ -1297,19 +1294,16 @@ 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;
 };
@@ -1322,30 +1316,16 @@ 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;
@@ -3567,22 +3547,16 @@ 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!");
 
@@ -4987,47 +4961,27 @@ 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);
-    if (contractid) {
-      native = do_CreateInstance(contractid, &rv);
-    }
-    else {
-      nsDOMConstructorFunc func =
-        FindConstructorFunc(name_struct->mDOMClassInfoID);
-      if (func) {
-        rv = func(getter_AddRefs(native));
-      }
-    }
+    native = do_CreateInstance(contractid, &rv);
   } 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)) {
@@ -5235,18 +5189,17 @@ 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) ||
-        FindConstructorFunc(aNameStruct->mDOMClassInfoID))) ||
+       FindConstructorContractID(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,17 +56,16 @@ 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,17 +60,16 @@
 #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)
 {
@@ -391,20 +390,16 @@ 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,16 +108,17 @@
 #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"
@@ -220,20 +221,16 @@ 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
@@ -653,31 +650,19 @@ 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.
-
-  gRefCnt++;
-
-#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
-  if (gRefCnt == 0) {
-    static const char* prefName = "browser.dom.window.dump.enabled";
-    nsContentUtils::AddBoolPrefVarCache(prefName, &gDOMWindowDumpEnabled);
-    gDOMWindowDumpEnabled = nsContentUtils::GetBoolPref(prefName);
-  }
-#endif
-
-  if (!gEntropyCollector) {
+  if (gRefCnt++ == 0 || !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
@@ -1677,21 +1662,16 @@ 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);
@@ -3843,35 +3823,34 @@ nsGlobalWindow::GetFullScreen(PRBool* aF
     }
   }
 
   // We are the root window, or something went wrong. Return our internal value.
   *aFullScreen = mFullScreen;
   return NS_OK;
 }
 
-PRBool
-nsGlobalWindow::DOMWindowDumpEnabled()
+NS_IMETHODIMP
+nsGlobalWindow::Dump(const nsAString& aStr)
 {
 #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.
-  return gDOMWindowDumpEnabled;
-#else
-  return PR_TRUE;
+  {
+    // 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;
+    }
+  }
 #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')
@@ -9459,8 +9438,23 @@ 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,17 +429,16 @@ 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,21 +2368,16 @@ 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,47 +46,46 @@ 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 \
-  nsDOMWorker.cpp \
-  nsDOMWorkerEvents.cpp \
-  nsDOMWorkerMessageHandler.cpp \
+  nsDOMWorkerBase.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/nsDOMThreadService.cpp
+++ b/dom/src/threads/nsDOMThreadService.cpp
@@ -36,50 +36,46 @@
  * 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)
@@ -104,16 +100,24 @@ 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;
@@ -146,41 +150,113 @@ public:
     return cx;
   }
 
 private:
   JSContext* mCx;
 };
 
 /**
- * This class is used as to post an error to the worker's outer handler.
+ * 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.
  */
-class nsReportErrorRunnable : public nsIRunnable
+class nsReportErrorRunnable : public nsRunnable
+{
+public:
+  nsReportErrorRunnable(nsIScriptError* aError, nsDOMWorkerThread* aWorker)
+  : mError(aError), mWorker(aWorker) { }
+
+  NS_IMETHOD Run() {
+    nsresult rv;
+
+    nsCOMPtr<nsIConsoleService> consoleService =
+      do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
+    if (NS_SUCCEEDED(rv)) {
+      consoleService->LogMessage(mError);
+    }
+
+    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;
+  }
+
+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;
+};
+
+/**
+ * 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
 
-  nsReportErrorRunnable(nsDOMWorker* aWorker, nsIWorkerMessageEvent* aEvent)
-  : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()), mEvent(aEvent) { }
+  nsDOMWorkerScriptError(nsIScriptError* aError)
+  : mScriptError(this, aError) { }
+
+protected:
 
-  NS_IMETHOD Run() {
-    if (mWorker->IsCanceled()) {
-      return NS_OK;
-    }
+  // 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->)
 
-    return mWorker->DispatchEvent(mEvent, nsnull);
-  }
+    InnerScriptError(nsDOMWorkerScriptError* aParent, nsIScriptError* aError)
+    : mParent(aParent), mError(aError) { }
 
-private:
-  nsRefPtr<nsDOMWorker> mWorker;
-  nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
-  nsCOMPtr<nsIWorkerMessageEvent> mEvent;
+  protected:
+    nsDOMWorkerScriptError* mParent;
+    nsCOMPtr<nsIScriptError> mError;
+  };
+
+  InnerScriptError mScriptError;
 };
 
-NS_IMPL_THREADSAFE_ISUPPORTS1(nsReportErrorRunnable, nsIRunnable)
+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)
 
 /**
  * Used to post an expired timeout to the correct worker.
  */
 class nsDOMWorkerTimeoutRunnable : public nsRunnable
 {
 public:
   nsDOMWorkerTimeoutRunnable(nsDOMWorkerTimeout* aTimeout)
@@ -202,64 +278,68 @@ 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(nsDOMWorker* aWorker)
+  nsDOMWorkerRunnable(nsDOMWorkerThread* 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;
   }
@@ -297,30 +377,30 @@ protected:
       nsresult rv =
 #endif
       runnable->Run();
       NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Runnable failed!");
     }
   }
 
   // Set at construction
-  nsRefPtr<nsDOMWorker> mWorker;
+  nsRefPtr<nsDOMWorkerThread> mWorker;
 
   // Protected by mMonitor
   nsDeque mRunnables;
 };
 
 /*******************************************************************************
  * JS environment function and callbacks
  */
 
 JSBool
 DOMWorkerOperationCallback(JSContext* aCx)
 {
-  nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
+  nsDOMWorkerThread* worker = (nsDOMWorkerThread*)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;
@@ -397,17 +477,17 @@ DOMWorkerOperationCallback(JSContext* aC
 
 void
 DOMWorkerErrorReporter(JSContext* aCx,
                        const char* aMessage,
                        JSErrorReport* aReport)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Huh?!");
 
-  nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
+  nsDOMWorkerThread* worker = (nsDOMWorkerThread*)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;
   }
 
@@ -426,40 +506,31 @@ 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,);
 
-  nsCString finalMessage;
-  rv = errorObject->ToString(finalMessage);
-  NS_ENSURE_SUCCESS(rv,);
-
-  nsRefPtr<nsDOMWorkerMessageEvent> event(new nsDOMWorkerMessageEvent());
-  NS_ENSURE_TRUE(event,);
+  nsRefPtr<nsDOMWorkerScriptError> domError =
+    new nsDOMWorkerScriptError(errorObject);
+  NS_ENSURE_TRUE(domError,);
 
-  rv = event->InitMessageEvent(NS_LITERAL_STRING("error"), PR_FALSE, PR_FALSE,
-                               NS_ConvertUTF8toUTF16(finalMessage),
-                               EmptyString(), nsnull);
-  NS_ENSURE_SUCCESS(rv,);
+  nsCOMPtr<nsIScriptError> scriptError(do_QueryInterface(domError));
+  NS_ENSURE_TRUE(scriptError,);
 
-  event->SetTarget(worker);
+  nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
+  NS_ENSURE_TRUE(mainThread,);
 
-  nsCOMPtr<nsIRunnable> runnable(new nsReportErrorRunnable(worker, event));
+  nsCOMPtr<nsIRunnable> runnable =
+    new nsReportErrorRunnable(scriptError, worker);
   NS_ENSURE_TRUE(runnable,);
 
-  nsRefPtr<nsDOMWorker> parent = worker->GetParent();
-
-  // If this worker has a parent then we need to send the message through the
-  // thread service to be run on the parent's thread. Otherwise it is a
-  // top-level worker and we send the message to the main thread.
-  rv = parent ? nsDOMThreadService::get()->Dispatch(parent, runnable)
-              : NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+  rv = mainThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
   NS_ENSURE_SUCCESS(rv,);
 }
 
 /*******************************************************************************
  * nsDOMThreadService
  */
 
 nsDOMThreadService::nsDOMThreadService()
@@ -482,19 +553,20 @@ nsDOMThreadService::~nsDOMThreadService(
 
   Cleanup();
 
   if (mMonitor) {
     nsAutoMonitor::DestroyMonitor(mMonitor);
   }
 }
 
-NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMThreadService, nsIEventTarget,
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsDOMThreadService, nsIEventTarget,
                                                   nsIObserver,
-                                                  nsIThreadPoolListener)
+                                                  nsIThreadPoolListener,
+                                                  nsIDOMThreadService)
 
 nsresult
 nsDOMThreadService::Init()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!gDOMThreadService, "Only one instance should ever be created!");
 
   nsresult rv;
@@ -520,19 +592,16 @@ 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);
@@ -548,59 +617,41 @@ nsDOMThreadService::Init()
     gJSContextIndex = BAD_TLS_INDEX;
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 /* static */
-already_AddRefed<nsDOMThreadService>
+already_AddRefed<nsIDOMThreadService>
 nsDOMThreadService::GetOrInitService()
 {
   if (!gDOMThreadService) {
     nsRefPtr<nsDOMThreadService> service = new nsDOMThreadService();
     NS_ENSURE_TRUE(service, nsnull);
 
     nsresult rv = service->Init();
     NS_ENSURE_SUCCESS(rv, nsnull);
 
     service.swap(gDOMThreadService);
   }
 
-  nsRefPtr<nsDOMThreadService> service(gDOMThreadService);
+  nsCOMPtr<nsIDOMThreadService> 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
@@ -631,25 +682,20 @@ 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(nsDOMWorker* aWorker,
+nsDOMThreadService::Dispatch(nsDOMWorkerThread* aWorker,
                              nsIRunnable* aRunnable)
 {
   NS_ASSERTION(aWorker, "Null pointer!");
   NS_ASSERTION(aRunnable, "Null pointer!");
 
   NS_ASSERTION(mThreadPool, "Dispatch called after 'xpcom-shutdown'!");
 
   if (aWorker->IsCanceled()) {
@@ -704,27 +750,40 @@ 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<nsDOMWorker>& debugWorker = aRunnable->mWorker;
+  nsRefPtr<nsDOMWorkerThread>& 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);
 
@@ -746,74 +805,52 @@ nsDOMThreadService::CreateJSContext()
 
   nsresult rv = nsContentUtils::XPConnect()->
     SetSecurityManagerForJSContext(cx, gWorkerSecurityManager, 0);
   NS_ENSURE_SUCCESS(rv, nsnull);
 
   return cx.forget();
 }
 
-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();
-}
+#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
 
 void
 nsDOMThreadService::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
 {
-  NS_ASSERTION(aGlobalObject, "Null pointer!");
-
-  nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_TRUE);
-  if (pool) {
-    pool->Cancel();
-  }
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  LOOP_OVER_POOLS(CancelWorkersForGlobal, (aGlobalObject));
 }
 
 void
 nsDOMThreadService::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
 {
-  NS_ASSERTION(aGlobalObject, "Null pointer!");
-
-  nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
-  if (pool) {
-    pool->Suspend();
-  }
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  LOOP_OVER_POOLS(SuspendWorkersForGlobal, (aGlobalObject));
 }
 
 void
 nsDOMThreadService::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
 {
-  NS_ASSERTION(aGlobalObject, "Null pointer!");
-
-  nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
-  if (pool) {
-    pool->Resume();
-  }
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  LOOP_OVER_POOLS(ResumeWorkersForGlobal, (aGlobalObject));
 }
 
 void
-nsDOMThreadService::NoteEmptyPool(nsDOMWorkerPool* aPool)
+nsDOMThreadService::NoteDyingPool(nsDOMWorkerPool* aPool)
 {
-  NS_ASSERTION(aPool, "Null pointer!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsAutoMonitor mon(mMonitor);
-  mPools.Remove(aPool->ScriptGlobalObject());
+  NS_ASSERTION(mPools.Contains(aPool), "aPool should be in the array!");
+  mPools.RemoveElement(aPool);
 }
 
 void
 nsDOMThreadService::TimeoutReady(nsDOMWorkerTimeout* aTimeout)
 {
   nsRefPtr<nsDOMWorkerTimeoutRunnable> runnable =
     new nsDOMWorkerTimeoutRunnable(aTimeout);
   NS_ENSURE_TRUE(runnable,);
@@ -821,18 +858,16 @@ 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!");
 
@@ -872,16 +907,17 @@ 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
  */
@@ -970,74 +1006,36 @@ nsDOMThreadService::OnThreadShuttingDown
     gThreadJSContextStack->SetSafeJSContext(nsnull);
 
     nsContentUtils::XPConnect()->ReleaseJSContext(cx, PR_TRUE);
   }
 
   return NS_OK;
 }
 
-nsresult
-nsDOMThreadService::RegisterWorker(nsDOMWorker* aWorker,
-                                   nsIScriptGlobalObject* aGlobalObject)
+/**
+ * See nsIDOMThreadService
+ */
+NS_IMETHODIMP
+nsDOMThreadService::CreatePool(nsIDOMWorkerPool** _retval)
 {
-  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);
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-    nsPIDOMWindow* innerWindow = domWindow->IsOuterWindow() ?
-                                 domWindow->GetCurrentInnerWindow() :
-                                 domWindow.get();
-    NS_ENSURE_STATE(innerWindow);
-
-    nsCOMPtr<nsIScriptGlobalObject> newGlobal(do_QueryInterface(innerWindow));
-    NS_ENSURE_TRUE(newGlobal, NS_ERROR_NO_INTERFACE);
-
-    aGlobalObject = newGlobal;
-  }
-
-  nsRefPtr<nsDOMWorkerPool> pool;
-  {
-    nsAutoMonitor mon(mMonitor);
-
-    if (!mThreadPool) {
-      // Shutting down!
-      return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
-    }
+  NS_ENSURE_TRUE(mThreadPool, NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
 
-    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);
+  nsIDOMDocument* domDocument = nsContentUtils::GetDocumentFromCaller();
+  NS_ENSURE_TRUE(domDocument, NS_ERROR_UNEXPECTED);
 
-    nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
-    NS_ENSURE_STATE(document);
-
-    pool = new nsDOMWorkerPool(aGlobalObject, document);
-    NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
+  nsCOMPtr<nsIDocument> callingDocument(do_QueryInterface(domDocument));
+  NS_ENSURE_TRUE(callingDocument, NS_ERROR_NO_INTERFACE);
 
-    rv = pool->Init();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsAutoMonitor mon(mMonitor);
+  nsRefPtr<nsDOMWorkerPool> pool(new nsDOMWorkerPool(callingDocument));
+  NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
 
-    PRBool success = mPools.Put(aGlobalObject, pool);
-    NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
-  }
-
-  rv = pool->NoteWorker(aWorker);
+  nsresult rv = pool->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  aWorker->SetPool(pool);
+  NS_ASSERTION(!mPools.Contains(pool), "Um?!");
+  mPools.AppendElement(pool);
+
+  NS_ADDREF(*_retval = pool);
   return NS_OK;
 }
--- a/dom/src/threads/nsDOMThreadService.h
+++ b/dom/src/threads/nsDOMThreadService.h
@@ -39,72 +39,68 @@
 
 #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 nsIThreadPoolListener,
+                           public nsIDOMThreadService
 {
-  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<nsDOMThreadService> GetOrInitService();
+  static already_AddRefed<nsIDOMThreadService> 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);
@@ -115,39 +111,34 @@ private:
   nsDOMThreadService();
   ~nsDOMThreadService();
 
   nsresult Init();
   void Cleanup();
 
   static void Shutdown();
 
-  nsresult Dispatch(nsDOMWorker* aWorker,
+  nsresult Dispatch(nsDOMWorkerThread* aWorker,
                     nsIRunnable* aRunnable);
 
   void WorkerComplete(nsDOMWorkerRunnable* aRunnable);
 
+  void WaitForCanceledWorker(nsDOMWorkerThread* aWorker);
+
   static JSContext* CreateJSContext();
 
-  already_AddRefed<nsDOMWorkerPool>
-    GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
-                     PRBool aRemove);
-
-  void NoteEmptyPool(nsDOMWorkerPool* aPool);
+  void NoteDyingPool(nsDOMWorkerPool* aPool);
 
   void TimeoutReady(nsDOMWorkerTimeout* aTimeout);
 
-  nsresult RegisterWorker(nsDOMWorker* aWorker,
-                          nsIScriptGlobalObject* aGlobalObject);
-
   // Our internal thread pool.
   nsCOMPtr<nsIThreadPool> mThreadPool;
 
-  // Maps nsIScriptGlobalObject* to nsDOMWorkerPool.
-  nsRefPtrHashtable<nsISupportsHashKey, nsDOMWorkerPool> mPools;
+  // Weak references, only ever touched on the main thread!
+  nsTPtrArray<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,190 +53,283 @@
 // Other includes
 #include "nsAutoLock.h"
 #include "nsContentUtils.h"
 #include "nsDOMJSUtils.h"
 #include "nsThreadUtils.h"
 
 // DOMWorker includes
 #include "nsDOMThreadService.h"
-#include "nsDOMWorker.h"
+#include "nsDOMWorkerThread.h"
 
 #define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
 
-nsDOMWorkerPool::nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject,
-                                 nsIDocument* aDocument)
-: mParentGlobal(aGlobalObject),
-  mParentDocument(aDocument),
-  mMonitor(nsnull),
-  mCanceled(PR_FALSE),
-  mSuspended(PR_FALSE)
+#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)
 {
-  NS_ASSERTION(aGlobalObject, "Must have a global object!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   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_ADDREF(nsDOMWorkerPool)
-NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerPool)
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerPool, nsIDOMWorkerPool,
+                                               nsIClassInfo)
+
+NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerPool, nsIDOMWorkerPool)
+
+NS_IMPL_THREADSAFE_DOM_CI(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::NoteWorker(nsDOMWorker* aWorker)
+nsDOMWorkerPool::HandleMessage(const nsAString& aMessage,
+                               nsDOMWorkerBase* aSource)
 {
-  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<nsIDOMWorkerMessageListener> messageListener =
+    nsDOMWorkerBase::GetMessageListener();
+  if (!messageListener) {
+    LOG(("Message received on a worker with no listener!"));
+    return NS_OK;
   }
 
-  if (suspendWorker) {
-    aWorker->Suspend();
-  }
+  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);
 
   return NS_OK;
 }
 
 void
-nsDOMWorkerPool::NoteDyingWorker(nsDOMWorker* aWorker)
+nsDOMWorkerPool::HandleError(nsIScriptError* aError,
+                             nsDOMWorkerThread* aSource)
 {
-  NS_ASSERTION(aWorker, "Null pointer!");
-
-  PRBool removeFromThreadService = PR_FALSE;
-
-  {
-    nsAutoMonitor mon(mMonitor);
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-    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);
+  if (mErrorListener) {
+    mErrorListener->OnError(aError,
+                            NS_ISUPPORTS_CAST(nsIDOMWorkerThread*, aSource));
   }
 }
 
 void
-nsDOMWorkerPool::GetWorkers(nsTArray<nsDOMWorker*>& aArray)
+nsDOMWorkerPool::NoteDyingWorker(nsDOMWorkerThread* aWorker)
 {
-  aArray.Clear();
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsAutoMonitor mon(mMonitor);
-#ifdef DEBUG
-  nsDOMWorker** newWorkers =
-#endif
-  aArray.AppendElements(mWorkers);
-  NS_WARN_IF_FALSE(newWorkers, "Out of memory!");
+  NS_ASSERTION(mWorkers.Contains(aWorker), "Worker from a different pool?!");
+  mWorkers.RemoveElement(aWorker);
 }
 
 void
-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()
+nsDOMWorkerPool::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsAutoTArray<nsDOMWorker*, 10> workers;
-  {
-    nsAutoMonitor mon(mMonitor);
-
-    NS_ASSERTION(!mSuspended, "Suspended more than once!");
-    mSuspended = PR_TRUE;
+  nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
+  NS_ASSERTION(globalSupports, "Null pointer?!");
 
-    GetWorkers(workers);
-  }
-
-  PRUint32 count = workers.Length();
-  for (PRUint32 index = 0; index < count; index++) {
-    workers[index]->Suspend();
+  if (globalSupports == mParentGlobal) {
+    LOOP_OVER_WORKERS(Cancel, ());
+    mWorkers.Clear();
+    if (IsSuspended()) {
+      nsAutoMonitor mon(mMonitor);
+      mon.NotifyAll();
+    }
   }
 }
 
 void
-nsDOMWorkerPool::Resume()
+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)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsAutoTArray<nsDOMWorker*, 10> workers;
-  {
-    nsAutoMonitor mon(mMonitor);
-
-    NS_ASSERTION(mSuspended, "Not suspended!");
-    mSuspended = PR_FALSE;
+  nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
+  NS_ASSERTION(globalSupports, "Null pointer?!");
 
-    GetWorkers(workers);
-  }
+  if (globalSupports == mParentGlobal) {
+    LOOP_OVER_WORKERS(Resume, ());
+    Resume();
 
-  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 mParentGlobal->GetContext();
+  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;
 }
+
+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,78 +35,99 @@
  * 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 "nsTArray.h"
+#include "nsTPtrArray.h"
 #include "prmon.h"
 
-class nsDOMWorker;
+class nsDOMWorkerThread;
 class nsIDocument;
 class nsIScriptContext;
 class nsIScriptError;
 class nsIScriptGlobalObject;
 
-class nsDOMWorkerPool
+/**
+ * The pool is almost always touched only on the main thread.
+ */
+class nsDOMWorkerPool : public nsDOMWorkerBase,
+                        public nsIDOMWorkerPool,
+                        public nsIClassInfo
 {
+  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);
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMWORKERPOOL
+  NS_DECL_NSICLASSINFO
+
+  nsDOMWorkerPool(nsIDocument* aDocument);
 
-  NS_IMETHOD_(nsrefcnt) AddRef();
-  NS_IMETHOD_(nsrefcnt) Release();
+  // For nsDOMWorkerBase
+  virtual nsDOMWorkerPool* Pool() {
+    return this;
+  }
 
+  nsIDocument* ParentDocument();
   nsIScriptContext* ScriptContext();
 
-  nsIScriptGlobalObject* ScriptGlobalObject() {
-    return mParentGlobal;
-  }
-
-  nsIDocument* ParentDocument() {
-    return mParentDocument;
-  }
+private:
+  virtual ~nsDOMWorkerPool();
 
   nsresult Init();
 
-  void Cancel();
-  void Suspend();
-  void Resume();
+  // For nsDOMWorkerBase
+  virtual nsresult HandleMessage(const nsAString& aMessage,
+                                 nsDOMWorkerBase* aSourceThread);
+
+  // For nsDOMWorkerBase
+  virtual nsresult DispatchMessage(nsIRunnable* aRunnable);
 
-  nsresult NoteWorker(nsDOMWorker* aWorker);
-  void NoteDyingWorker(nsDOMWorker* aWorker);
+  void HandleError(nsIScriptError* aError,
+                   nsDOMWorkerThread* aSource);
+
+  void NoteDyingWorker(nsDOMWorkerThread* aWorker);
+
+  void CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
+  void SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
+  void ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
 
   PRMonitor* Monitor() {
     return mMonitor;
   }
 
-private:
-  virtual ~nsDOMWorkerPool();
-
-  void GetWorkers(nsTArray<nsDOMWorker*>& aArray);
+  // Weak reference to the window that created and owns this pool.
+  nsISupports* mParentGlobal;
 
-  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 reference to the document that created this pool.
+  nsIDocument* mParentDocument;
 
   // Weak array of workers. The idea is that workers can be garbage collected
   // independently of the owning pool and other workers.
-  nsTArray<nsDOMWorker*> mWorkers;
+  nsTPtrArray<nsDOMWorkerThread> mWorkers;
+
+  // An error handler function, may be null.
+  nsCOMPtr<nsIDOMWorkerErrorListener> mErrorListener;
 
   // 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,41 +60,88 @@
 // 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(nsDOMWorker* aWorker)
-: nsDOMWorkerFeature(aWorker),
+nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader()
+: mWorker(nsnull),
   mTarget(nsnull),
+  mCx(NULL),
   mScriptCount(0),
-  mCanceled(PR_FALSE)
+  mCanceled(PR_FALSE),
+  mTrackedByWorker(PR_FALSE)
 {
   // Created on worker thread.
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aWorker, "Null worker!");
 }
 
-NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerScriptLoader, nsDOMWorkerFeature,
-                                                      nsIRunnable,
+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,
                                                       nsIStreamLoaderObserver)
 
 nsresult
-nsDOMWorkerScriptLoader::LoadScripts(JSContext* aCx,
+nsDOMWorkerScriptLoader::LoadScripts(nsDOMWorkerThread* aWorker,
+                                     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;
   }
@@ -121,47 +168,60 @@ nsDOMWorkerScriptLoader::LoadScripts(JSC
     success = newInfo->scriptObj.Hold(aCx);
     NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
   }
 
   // Don't want timeouts, etc., from queuing up while we're waiting on the
   // network or compiling.
   AutoSuspendWorkerEvents aswe(this);
 
-  nsresult rv = DoRunLoop(aCx);
+  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;
+   }
+
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   // Verify that all scripts downloaded and compiled.
-  rv = VerifyScripts(aCx);
+  rv = VerifyScripts();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  rv = ExecuteScripts(aCx);
+  rv = ExecuteScripts();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
-nsDOMWorkerScriptLoader::LoadScript(JSContext* aCx,
+nsDOMWorkerScriptLoader::LoadScript(nsDOMWorkerThread* aWorker,
+                                    JSContext* aCx,
                                     const nsString& aURL)
 {
   nsAutoTArray<nsString, 1> url;
   url.AppendElement(aURL);
 
-  return LoadScripts(aCx, url);
+  return LoadScripts(aWorker, aCx, url);
 }
 
 nsresult
-nsDOMWorkerScriptLoader::DoRunLoop(JSContext* aCx)
+nsDOMWorkerScriptLoader::DoRunLoop()
 {
   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);
@@ -169,33 +229,31 @@ nsDOMWorkerScriptLoader::DoRunLoop(JSCon
 
   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(aCx);
+      JSAutoSuspendRequest asr(mCx);
       NS_ProcessNextEvent(mTarget);
     }
 
     if (changed) {
       threadService->ChangeThreadPoolMaxThreads(-1);
     }
   }
 
   return mCanceled ? NS_ERROR_ABORT : NS_OK;
 }
 
 nsresult
-nsDOMWorkerScriptLoader::VerifyScripts(JSContext* aCx)
+nsDOMWorkerScriptLoader::VerifyScripts()
 {
-  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;
@@ -211,70 +269,52 @@ nsDOMWorkerScriptLoader::VerifyScripts(J
     // since that's the code we set when some other script had a problem and the
     // rest were canceled.
     if (NS_SUCCEEDED(loadInfo.result) || loadInfo.result == NS_BINDING_ABORTED) {
       continue;
     }
 
     // Ok, this is the script that caused us to fail.
 
-    JSAutoRequest ar(aCx);
-
-    // Only throw an error if there is no other pending exception.
-    if (!JS_IsExceptionPending(aCx)) {
-      const char* message;
-      switch (loadInfo.result) {
-        case NS_ERROR_MALFORMED_URI:
-          message = "Malformed script URI: %s";
-          break;
-        case NS_ERROR_FILE_NOT_FOUND:
-          message = "Script file not found: %s";
-          break;
-        default:
-          message = "Failed to load script: %s (nsresult = 0x%x)";
-          break;
-      }
+    // Only throw an error there is no other pending exception.
+    if (!JS_IsExceptionPending(mCx)) {
       NS_ConvertUTF16toUTF8 url(loadInfo.url);
-      JS_ReportError(aCx, message, url.get(), loadInfo.result);
+      JS_ReportError(mCx, "Failed to compile script: %s", url.get());
     }
     break;
   }
 
   return rv;
 }
 
 nsresult
-nsDOMWorkerScriptLoader::ExecuteScripts(JSContext* aCx)
+nsDOMWorkerScriptLoader::ExecuteScripts()
 {
-  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(aCx, loadInfo.scriptObj));
+      static_cast<JSScript*>(JS_GetPrivate(mCx, loadInfo.scriptObj));
     NS_ASSERTION(script, "This shouldn't ever be null!");
 
     JSObject* global = mWorker->mGlobal ?
                        mWorker->mGlobal :
-                       JS_GetGlobalObject(aCx);
+                       JS_GetGlobalObject(mCx);
     NS_ENSURE_STATE(global);
 
     // Because we may have nested calls to this function we don't want the
     // execution to automatically report errors. We let them propagate instead.
     uint32 oldOpts =
-      JS_SetOptions(aCx, JS_GetOptions(aCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
+      JS_SetOptions(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
 
     jsval val;
-    PRBool success = JS_ExecuteScript(aCx, global, script, &val);
+    PRBool success = JS_ExecuteScript(mCx, global, script, &val);
 
-    JS_SetOptions(aCx, oldOpts);
+    JS_SetOptions(mCx, oldOpts);
 
     if (!success) {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
 }
 
@@ -403,21 +443,18 @@ nsDOMWorkerScriptLoader::OnStreamComplet
 }
 
 nsresult
 nsDOMWorkerScriptLoader::RunInternal()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   // Things we need to make all this work...
-  nsCOMPtr<nsIDocument> parentDoc = mWorker->Pool()->ParentDocument();
-  if (!parentDoc) {
-    // Must have been canceled.
-    return NS_ERROR_ABORT;
-  }
+  nsIDocument* parentDoc = mWorker->Pool()->ParentDocument();
+  NS_ASSERTION(parentDoc, "Null parent document?!");
 
   // 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++) {
@@ -430,17 +467,19 @@ 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, 0);
+    rv =
+      secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri,
+                                        nsIScriptSecurityManager::ALLOW_CHROME);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
     rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT,
                                    uri,
                                    parentDoc->NodePrincipal(),
@@ -569,17 +608,18 @@ 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, loadInfo.scriptText, filename, loadInfo.scriptObj);
+    new ScriptCompiler(this, mCx, 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);
 
@@ -618,24 +658,24 @@ nsDOMWorkerScriptLoader::NotifyDone()
 
   mDoneRunnable = nsnull;
 }
 
 void
 nsDOMWorkerScriptLoader::SuspendWorkerEvents()
 {
   NS_ASSERTION(mWorker, "No worker yet!");
-  mWorker->SuspendFeatures();
+  mWorker->SuspendTimeouts();
 }
 
 void
 nsDOMWorkerScriptLoader::ResumeWorkerEvents()
 {
   NS_ASSERTION(mWorker, "No worker yet!");
-  mWorker->ResumeFeatures();
+  mWorker->ResumeTimeouts();
 }
 
 nsDOMWorkerScriptLoader::
 ScriptLoaderRunnable::ScriptLoaderRunnable(nsDOMWorkerScriptLoader* aLoader)
 : mRevoked(PR_FALSE),
   mLoader(aLoader)
 {
   nsAutoLock lock(aLoader->Lock());
@@ -654,35 +694,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!");
@@ -690,44 +730,41 @@ 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!");
 
-  JSContext* cx = nsDOMThreadService::GetCurrentContext();
-  NS_ENSURE_STATE(cx);
+  JSAutoRequest ar(mCx);
 
-  JSAutoRequest ar(cx);
-
-  JSObject* global = JS_GetGlobalObject(cx);
+  JSObject* global = JS_GetGlobalObject(mCx);
   NS_ENSURE_STATE(global);
 
   // Because we may have nested calls to this function we don't want the
   // execution to automatically report errors. We let them propagate instead.
   uint32 oldOpts =
-    JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT);
+    JS_SetOptions(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
 
   JSPrincipals* principal = nsDOMWorkerSecurityManager::WorkerPrincipal();
 
   JSScript* script =
-    JS_CompileUCScriptForPrincipals(cx, global, principal,
+    JS_CompileUCScriptForPrincipals(mCx, global, principal,
                                     reinterpret_cast<const jschar*>
                                                (mScriptText.BeginReading()),
                                     mScriptText.Length(), mFilename.get(), 1);
 
-  JS_SetOptions(cx, oldOpts);
+  JS_SetOptions(mCx, oldOpts);
 
   if (!script) {
     return NS_ERROR_FAILURE;
   }
 
-  mScriptObj = JS_NewScriptObject(cx, script);
+  mScriptObj = JS_NewScriptObject(mCx, 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,35 +35,34 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __NSDOMWORKERSCRIPTLOADER_H__
 #define __NSDOMWORKERSCRIPTLOADER_H__
 
 // Bases
-#include "nsIRunnable.h"
+#include "nsThreadUtils.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"
 
-#include "nsDOMWorker.h"
-
-class nsIThread;
+// DOMWorker includes
+#include "nsDOMWorkerThread.h"
 
 /**
  * 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:
  *
@@ -82,45 +81,47 @@ class nsIThread;
  *                     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 nsDOMWorkerFeature,
-                                public nsIRunnable,
+class nsDOMWorkerScriptLoader : public nsRunnable,
                                 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(nsDOMWorker* aWorker);
+  nsDOMWorkerScriptLoader();
 
-  nsresult LoadScripts(JSContext* aCx,
+  nsresult LoadScripts(nsDOMWorkerThread* aWorker,
+                       JSContext* aCx,
                        const nsTArray<nsString>& aURLs);
 
-  nsresult LoadScript(JSContext* aCx,
-                      const nsString& aURL);
+  nsresult LoadScript(nsDOMWorkerThread* aWorker,
+                       JSContext* aCx,
+                       const nsString& aURL);
 
-  virtual void Cancel();
+  void Cancel();
 
 private:
-  ~nsDOMWorkerScriptLoader() { }
-
+  ~nsDOMWorkerScriptLoader();
 
-  nsresult DoRunLoop(JSContext* aCx);
-  nsresult VerifyScripts(JSContext* aCx);
-  nsresult ExecuteScripts(JSContext* aCx);
+  nsresult DoRunLoop();
+  nsresult VerifyScripts();
+  nsresult ExecuteScripts();
 
   nsresult RunInternal();
 
   nsresult OnStreamCompleteInternal(nsIStreamLoader* aLoader,
                                     nsISupports* aContext,
                                     nsresult aStatus,
                                     PRUint32 aStringLen,
                                     const PRUint8* aString);
@@ -129,21 +130,18 @@ private:
 
   void SuspendWorkerEvents();
   void ResumeWorkerEvents();
 
   PRLock* Lock() {
     return mWorker->Lock();
   }
 
-  class ScriptLoaderRunnable : public nsIRunnable
+  class ScriptLoaderRunnable : public nsRunnable
   {
-  public:
-    NS_DECL_ISUPPORTS
-
   protected:
     // Meant to be inherited.
     ScriptLoaderRunnable(nsDOMWorkerScriptLoader* aLoader);
     virtual ~ScriptLoaderRunnable();
 
   public:
     void Revoke();
 
@@ -155,21 +153,23 @@ 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,22 +200,25 @@ 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__ */
--- a/dom/src/threads/nsDOMWorkerTimeout.cpp
+++ b/dom/src/threads/nsDOMWorkerTimeout.cpp
@@ -42,19 +42,17 @@
 #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 \
@@ -70,25 +68,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),
-  mRuntime(NULL)
+  mCallbackArgsLength(0)
 {
   MOZ_COUNT_CTOR(nsDOMWorkerTimeout::FunctionCallback);
 
-  *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&mRuntime);
+  JSRuntime* rt;
+  *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
   NS_ENSURE_SUCCESS(*aRv,);
 
-  PRBool success = JS_AddNamedRootRT(mRuntime, &mCallback,
+  PRBool success = JS_AddNamedRootRT(rt, &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;
 
@@ -99,32 +97,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(mRuntime, &mCallbackArgs[i],
+    success = JS_AddNamedRootRT(rt, &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(mRuntime, &mCallbackArgs[mCallbackArgsLength - 1],
+  success = JS_AddNamedRootRT(rt, &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;
@@ -134,20 +132,27 @@ nsDOMWorkerTimeout::FunctionCallback::Fu
   *aRv = NS_OK;
 }
 
 nsDOMWorkerTimeout::FunctionCallback::~FunctionCallback()
 {
   MOZ_COUNT_DTOR(nsDOMWorkerTimeout::FunctionCallback);
 
   if (mCallback) {
-    for (PRUint32 i = 0; i < mCallbackArgsLength; i++) {
-      JS_RemoveRootRT(mRuntime, &mCallbackArgs[i]);
+    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);
     }
-    JS_RemoveRootRT(mRuntime, &mCallback);
   }
 
   delete [] mCallbackArgs;
 }
 
 nsresult
 nsDOMWorkerTimeout::FunctionCallback::Run(nsDOMWorkerTimeout* aTimeout,
                                           JSContext* aCx)
@@ -168,29 +173,29 @@ nsDOMWorkerTimeout::FunctionCallback::Ru
   return NS_OK;
 }
 
 nsDOMWorkerTimeout::ExpressionCallback::ExpressionCallback(PRUint32 aArgc,
                                                            jsval* aArgv,
                                                            JSContext* aCx,
                                                            nsresult* aRv)
 : mExpression(nsnull),
-  mLineNumber(0),
-  mRuntime(NULL)
+  mLineNumber(0)
 {
   MOZ_COUNT_CTOR(nsDOMWorkerTimeout::ExpressionCallback);
 
   JSString* expr = JS_ValueToString(aCx, aArgv[0]);
   *aRv = expr ? NS_OK : NS_ERROR_FAILURE;
   NS_ENSURE_SUCCESS(*aRv,);
 
-  *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&mRuntime);
+  JSRuntime* rt;
+  *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
   NS_ENSURE_SUCCESS(*aRv,);
 
-  PRBool success = JS_AddNamedRootRT(mRuntime, &mExpression,
+  PRBool success = JS_AddNamedRootRT(rt, &mExpression,
                                      "nsDOMWorkerTimeout Expression");
   CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
 
   mExpression = expr;
 
   // Get the calling location.
   const char* fileName;
   PRUint32 lineNumber;
@@ -202,44 +207,66 @@ nsDOMWorkerTimeout::ExpressionCallback::
   *aRv = NS_OK;
 }
 
 nsDOMWorkerTimeout::ExpressionCallback::~ExpressionCallback()
 {
   MOZ_COUNT_DTOR(nsDOMWorkerTimeout::ExpressionCallback);
 
   if (mExpression) {
-    JS_RemoveRootRT(mRuntime, &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);
+    }
   }
 }
 
 nsresult
 nsDOMWorkerTimeout::ExpressionCallback::Run(nsDOMWorkerTimeout* aTimeout,
                                             JSContext* aCx)
 {
   NS_ERROR("Not yet implemented!");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorker* aWorker,
+nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker,
                                        PRUint32 aId)
-: nsDOMWorkerFeature(aWorker, aId),
+: mWorker(aWorker),
   mInterval(0),
+  mIsInterval(PR_FALSE),
+  mId(aId),
   mSuspendSpinlock(0),
-  mSuspendInterval(0),
-  mIsInterval(PR_FALSE),
   mIsSuspended(PR_FALSE),
-  mSuspendedBeforeStart(PR_FALSE),
-  mStarted(PR_FALSE)
+  mSuspendInterval(0)
+#ifdef DEBUG
+, mFiredOrCanceled(PR_FALSE)
+#endif
 {
+  MOZ_COUNT_CTOR(nsDOMWorkerTimeout);
   NS_ASSERTION(mWorker, "Need a worker here!");
 }
 
-NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerTimeout, nsDOMWorkerFeature,
-                                                 nsITimerCallback)
+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)
 
 nsresult
 nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
                          PRBool aIsInterval)
 {
   NS_ASSERTION(aCx, "Null pointer!");
   NS_ASSERTION(aArgv, "Null pointer!");
 
@@ -262,18 +289,16 @@ 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);
@@ -285,64 +310,65 @@ 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);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   mTimer.swap(timer);
-  return NS_OK;
-}
 
-nsresult
-nsDOMWorkerTimeout::Start()
-{
-  if (IsSuspended()) {
-    NS_ASSERTION(mSuspendedBeforeStart, "Bad state!");
-    return NS_OK;
+  if (!mWorker->AddTimeout(this)) {
+    // Must have been canceled.
+    mTimer->Cancel();
+    mTimer = nsnull;
+    return NS_ERROR_ABORT;
   }
 
-  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);
-
-  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);
@@ -360,87 +386,71 @@ 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()
+nsDOMWorkerTimeout::Suspend(PRTime aNow)
 {
-#ifdef DEBUG
-  if (mStarted) {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  }
-#endif
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
 
   AutoSpinlock lock(this);
 
-  NS_ASSERTION(!IsSuspendedNoLock(), "Bad state!");
-
-  mIsSuspended = PR_TRUE;
-  mSuspendedRef = this;
-
-  if (!mStarted) {
-    mSuspendedBeforeStart = PR_TRUE;
-    return;
+  if (!mIsSuspended) {
+    mIsSuspended = PR_TRUE;
+    mSuspendedRef = this;
   }
 
   mTimer->Cancel();
 
-  mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - PR_Now())) /
+  mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - aNow)) /
                      (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()
+nsDOMWorkerTimeout::Resume(PRTime aNow)
 {
-#ifdef DEBUG
-  if (!mSuspendedBeforeStart) {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  }
-#endif
-
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   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!");
 
-  if (mSuspendedBeforeStart) {
-    NS_ASSERTION(!mSuspendInterval, "Bad state!");
-    mSuspendedBeforeStart = PR_FALSE;
-    mSuspendInterval = mInterval;
-    mStarted = PR_TRUE;
-  }
-
-  mTargetTime = PR_Now() + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC;
+  mTargetTime = aNow + 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!");
 }
 
@@ -484,25 +494,29 @@ 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,71 +42,70 @@
 // Interfaces
 #include "nsITimer.h"
 
 // Other includes
 #include "jsapi.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsStringGlue.h"
+#include "prclist.h"
 
 // DOMWorker includes
-#include "nsDOMWorker.h"
+#include "nsDOMWorkerThread.h"
 
 /**
  * The nsDOMWorkerTimeout has a slightly complicated life cycle. It's created
- * 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
+ * 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
  * 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 nsDOMWorkerFeature,
+class nsDOMWorkerTimeout : public PRCList,
                            public nsITimerCallback
 {
 public:
-  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
 
-  nsDOMWorkerTimeout(nsDOMWorker* aWorker,
-                     PRUint32 aId);
+  nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker, PRUint32 aId);
+  ~nsDOMWorkerTimeout();
 
-  nsresult Init(JSContext* aCx,
-                PRUint32 aArgc,
-                jsval* aArgv,
+  nsresult Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
                 PRBool aIsInterval);
 
-  nsresult Start();
-
   nsresult Run();
 
-  virtual void Cancel();
-  virtual void Suspend();
-  virtual void Resume();
+  void Cancel();
+  void Suspend(PRTime aNow);
+  void Resume(PRTime aNow);
 
   PRIntervalTime GetInterval() {
     return mInterval;
   }
 
-  nsDOMWorker* GetWorker() {
+  nsDOMWorkerThread* 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
@@ -133,58 +132,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;
 
-  PRPackedBool mIsInterval;
-  PRPackedBool mIsSuspended;
-  PRPackedBool mSuspendedBeforeStart;
-  PRPackedBool mStarted;
+#ifdef DEBUG
+  PRBool mFiredOrCanceled;
+#endif
 };
 
 #endif /* __NSDOMWORKERTIMEOUT_H__ */
--- a/dom/src/threads/nsDOMWorkerXHR.cpp
+++ b/dom/src/threads/nsDOMWorkerXHR.cpp
@@ -51,17 +51,16 @@
 #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
@@ -87,330 +86,339 @@ 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_ISUPPORTS_INHERITED1(nsDOMWorkerXHREventTarget,
-                             nsDOMWorkerMessageHandler,
-                             nsIXMLHttpRequestEventTarget)
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerXHREventTarget,
+                              nsIDOMEventTarget,
+                              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);
 
-  nsAutoString type;
-  type.AssignASCII(sListenerTypes[LISTENER_TYPE_ABORT]);
-
-  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
+  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_ABORT);
   listener.forget(aOnabort);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::SetOnabort(nsIDOMEventListener* aOnabort)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  nsAutoString type;
-  type.AssignASCII(sListenerTypes[LISTENER_TYPE_ABORT]);
-
-  return SetOnXListener(type, aOnabort);
+  return SetEventListener(LISTENER_TYPE_ABORT, aOnabort, PR_TRUE);
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::GetOnerror(nsIDOMEventListener** aOnerror)
 {
   NS_ENSURE_ARG_POINTER(aOnerror);
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
-  nsAutoString type;
-  type.AssignASCII(sListenerTypes[LISTENER_TYPE_ERROR]);
-
-  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
+  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_ERROR);
   listener.forget(aOnerror);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::SetOnerror(nsIDOMEventListener* aOnerror)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  nsAutoString type;
-  type.AssignASCII(sListenerTypes[LISTENER_TYPE_ERROR]);
-
-  return SetOnXListener(type, aOnerror);
+  return SetEventListener(LISTENER_TYPE_ERROR, aOnerror, PR_TRUE);
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::GetOnload(nsIDOMEventListener** aOnload)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ENSURE_ARG_POINTER(aOnload);
 
-  nsAutoString type;
-  type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOAD]);
-
-  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
+  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_LOAD);
   listener.forget(aOnload);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::SetOnload(nsIDOMEventListener* aOnload)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  nsAutoString type;
-  type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOAD]);
-
-  return SetOnXListener(type, aOnload);
+  return SetEventListener(LISTENER_TYPE_LOAD, aOnload, PR_TRUE);
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::GetOnloadstart(nsIDOMEventListener** aOnloadstart)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ENSURE_ARG_POINTER(aOnloadstart);
 
-  nsAutoString type;
-  type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOADSTART]);
-
-  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(LISTENER_TYPE_LOADSTART);
   listener.forget(aOnloadstart);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::SetOnloadstart(nsIDOMEventListener* aOnloadstart)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  nsAutoString type;
-  type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOADSTART]);
-
-  return SetOnXListener(type, aOnloadstart);
+  return SetEventListener(LISTENER_TYPE_LOADSTART, aOnloadstart, PR_TRUE);
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::GetOnprogress(nsIDOMEventListener** aOnprogress)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ENSURE_ARG_POINTER(aOnprogress);
 
-  nsAutoString type;
-  type.AssignASCII(sListenerTypes[LISTENER_TYPE_PROGRESS]);
-
-  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(LISTENER_TYPE_PROGRESS);
   listener.forget(aOnprogress);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHREventTarget::SetOnprogress(nsIDOMEventListener* aOnprogress)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  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)
 
-  nsAutoString type;
-  type.AssignASCII(sListenerTypes[LISTENER_TYPE_PROGRESS]);
+#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();
+  }
 
-  return SetOnXListener(type, aOnprogress);
+  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;
 }
 
 nsDOMWorkerXHRUpload::nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR)
 : mWorkerXHR(aWorkerXHR)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aWorkerXHR, "Null pointer!");
+  NS_ASSERTION(aWorkerXHR, "Must have a worker XHR!");
 }
 
-NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget,
-                                                   nsIXMLHttpRequestUpload)
+NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget,
+                                                   nsIXMLHttpRequestUpload,
+                                                   nsIClassInfo)
 
 NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHRUpload, nsIDOMEventTarget,
                                                    nsIXMLHttpRequestEventTarget,
                                                    nsIXMLHttpRequestUpload)
 
-NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerXHRUpload)
+NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHRUpload)
 
-NS_IMETHODIMP
-nsDOMWorkerXHRUpload::AddEventListener(const nsAString& aType,
+nsresult
+nsDOMWorkerXHRUpload::SetEventListener(PRUint32 aType,
                                        nsIDOMEventListener* aListener,
-                                       PRBool aUseCapture)
+                                       PRBool aOnXListener)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-  NS_ENSURE_ARG_POINTER(aListener);
-
-  if (mWorkerXHR->mWorker->IsCanceled()) {
+  if (mWorkerXHR->mCanceled) {
     return NS_ERROR_ABORT;
   }
 
-  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);
+  return mWorkerXHR->mXHRProxy->AddEventListener(aType, aListener, aOnXListener,
+                                                 PR_TRUE);
 }
 
 nsresult
-nsDOMWorkerXHRUpload::SetOnXListener(const nsAString& aType,
-                                     nsIDOMEventListener* aListener)
+nsDOMWorkerXHRUpload::UnsetEventListener(PRUint32 aType,
+                                         nsIDOMEventListener* aListener)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
   if (mWorkerXHR->mCanceled) {
     return NS_ERROR_ABORT;
   }
 
-  PRUint32 type = GetListenerTypeFromString(aType);
-  if (type > sMaxUploadEventTypes) {
-    // Silently ignore junk events.
-    return NS_OK;
+  return mWorkerXHR->mXHRProxy->RemoveEventListener(aType, aListener, PR_TRUE);
+}
+
+nsresult
+nsDOMWorkerXHRUpload::HandleWorkerEvent(nsIDOMEvent* aEvent)
+{
+  if (mWorkerXHR->mCanceled) {
+    return NS_ERROR_ABORT;
   }
 
-  return nsDOMWorkerXHREventTarget::SetOnXListener(aType, aListener);
+  return mWorkerXHR->mXHRProxy->HandleWorkerEvent(aEvent, PR_TRUE);
 }
 
-nsDOMWorkerXHR::nsDOMWorkerXHR(nsDOMWorker* aWorker)
-: nsDOMWorkerFeature(aWorker),
-  mWrappedNative(nsnull),
-  mCanceled(PR_FALSE)
+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)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aWorker, "Must have a worker!");
 }
 
-// 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)
+nsDOMWorkerXHR::~nsDOMWorkerXHR()
+{
+  if (!mCanceled) {
+    mWorker->RemoveXHR(this);
+  }
+}
 
-NS_IMPL_QUERY_INTERFACE_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget,
-                                                   nsIXMLHttpRequest,
-                                                   nsIXPCScriptable)
+NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget,
+                                             nsIXMLHttpRequest,
+                                             nsIClassInfo)
 
 NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHR, nsIDOMEventTarget,
                                              nsIXMLHttpRequestEventTarget,
                                              nsIXMLHttpRequest)
 
-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;
-}
+NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHR)
 
 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;
@@ -433,53 +441,82 @@ nsDOMWorkerXHR::Cancel()
     mCanceled = PR_TRUE;
     mUpload = nsnull;
   }
 
   if (mXHRProxy) {
     mXHRProxy->Destroy();
   }
 
+  mWorker->RemoveXHR(this);
   mWorker = nsnull;
 }
 
 nsresult
-nsDOMWorkerXHR::SetOnXListener(const nsAString& aType,
-                               nsIDOMEventListener* aListener)
+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)
 {
   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->RemoveEventListener(aType, aListener, PR_FALSE);
+}
+
+nsresult
+nsDOMWorkerXHR::HandleWorkerEvent(nsIDOMEvent* aEvent)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
   }
 
-  return nsDOMWorkerXHREventTarget::SetOnXListener(aType, aListener);
+  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);
 }
 
 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)
 {
@@ -811,17 +848,17 @@ nsDOMWorkerXHR::Init(nsIPrincipal* aPrin
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHR::GetUpload(nsIXMLHttpRequestUpload** aUpload)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
-  nsRefPtr<nsDOMWorker> worker = mWorker;
+  nsRefPtr<nsDOMWorkerThread> worker = mWorker;
   if (!worker) {
     return NS_ERROR_ABORT;
   }
 
   nsAutoLock lock(worker->Lock());
 
   if (mCanceled) {
     return NS_ERROR_ABORT;
@@ -836,34 +873,42 @@ 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);
 
-  nsAutoString type;
-  type.AssignASCII(sListenerTypes[LISTENER_TYPE_READYSTATECHANGE]);
+  nsCOMPtr<nsIDOMEventListener> listener =
+    mXHRProxy->GetOnXListener(LISTENER_TYPE_READYSTATECHANGE, PR_FALSE);
 
-  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
   listener.forget(aOnreadystatechange);
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWorkerXHR::SetOnreadystatechange(nsIDOMEventListener* aOnreadystatechange)
 {
-  nsAutoString type;
-  type.AssignASCII(sListenerTypes[LISTENER_TYPE_READYSTATECHANGE]);
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
-  return SetOnXListener(type, aOnreadystatechange);
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  return mXHRProxy->AddEventListener(LISTENER_TYPE_READYSTATECHANGE,
+                                    aOnreadystatechange, PR_TRUE, PR_FALSE);
 }
 
 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,123 +35,143 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __NSDOMWORKERXHR_H__
 #define __NSDOMWORKERXHR_H__
 
 // Bases
+#include "nsIXMLHttpRequest.h"
 #include "nsIClassInfo.h"
-#include "nsIXMLHttpRequest.h"
-#include "nsIXPCScriptable.h"
+
+// Interfaces
 
 // Other includes
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "prlock.h"
 
 // DOMWorker includes
-#include "nsDOMWorker.h"
-#include "nsDOMWorkerMacros.h"
-#include "nsDOMWorkerXHRProxy.h"
+#include "nsDOMWorkerThread.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 nsIXPConnectWrappedNative;
+class nsDOMWorkerXHR;
+class nsDOMWorkerXHREvent;
+class nsDOMWorkerXHRProxy;
 
-class nsDOMWorkerXHREventTarget : public nsDOMWorkerMessageHandler,
-                                  public nsIXMLHttpRequestEventTarget
+class nsDOMWorkerXHREventTarget : public nsIXMLHttpRequestEventTarget
 {
 public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_FORWARD_NSIDOMEVENTTARGET(nsDOMWorkerMessageHandler::)
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMEVENTTARGET
   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;
+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 nsDOMWorkerXHR : public nsDOMWorkerXHREventTarget,
-                       public nsDOMWorkerFeature,
                        public nsIXMLHttpRequest,
-                       public nsIXPCScriptable
+                       public nsIClassInfo
 {
   friend class nsDOMWorkerXHREvent;
   friend class nsDOMWorkerXHRLastProgressOrLoadEvent;
   friend class nsDOMWorkerXHRProxy;
   friend class nsDOMWorkerXHRUpload;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIXMLHTTPREQUEST
-  NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerXHREventTarget::)
-  NS_DECL_NSICLASSINFO_GETINTERFACES
-  NS_DECL_NSIXPCSCRIPTABLE
+  NS_DECL_NSICLASSINFO
 
-  nsDOMWorkerXHR(nsDOMWorker* aWorker);
+  nsDOMWorkerXHR(nsDOMWorkerThread* aWorker);
 
   nsresult Init();
 
-  virtual void Cancel();
+  void Cancel();
+
+  virtual nsresult SetEventListener(PRUint32 aType,
+                                    nsIDOMEventListener* aListener,
+                                    PRBool aOnXListener);
 
-  virtual nsresult SetOnXListener(const nsAString& aType,
-                                  nsIDOMEventListener* aListener);
+  virtual nsresult UnsetEventListener(PRUint32 aType,
+                                      nsIDOMEventListener* aListener);
+
+  virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent);
 
-private:
-  virtual ~nsDOMWorkerXHR() { }
+  virtual already_AddRefed<nsIDOMEventListener>
+    GetOnXListener(PRUint32 aType);
+
+protected:
+  virtual ~nsDOMWorkerXHR();
 
   PRLock* Lock() {
     return mWorker->Lock();
   }
 
-  nsIXPConnectWrappedNative* GetWrappedNative() {
-    return mWrappedNative;
-  }
-
+  nsRefPtr<nsDOMWorkerThread> mWorker;
   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,22 +54,18 @@
 #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 "nsDOMWorkerXHR.h"
+#include "nsDOMWorkerThread.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!");                         \
@@ -108,17 +104,17 @@
 using namespace nsDOMWorkerProxiedXHRFunctions;
 
 class nsResultReturningRunnable : public nsIRunnable
 {
 public:
   NS_DECL_ISUPPORTS
 
   nsResultReturningRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable,
-                            nsDOMWorker* aWorker)
+                            nsDOMWorkerThread* 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;
     }
@@ -156,42 +152,355 @@ public:
     mDone = PR_TRUE;
 
     return mResult;
   }
 
 private:
   nsCOMPtr<nsIEventTarget> mTarget;
   nsCOMPtr<nsIRunnable> mRunnable;
-  nsRefPtr<nsDOMWorker> mWorker;
+  nsRefPtr<nsDOMWorkerThread> 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;
   }
@@ -315,17 +624,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) {
-    mWorkerXHRWN = nsnull;
+    mWorkerXHR->Release();
   }
   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);
@@ -338,16 +647,28 @@ 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();
@@ -367,41 +688,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();
   }
 
-  NS_ASSERTION(!(mLastProgressOrLoadEvent && mLastXHREvent), "Going to leak!");
+  mLastXHREvent = nsnull;
 
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::InitInternal()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!mXHR, "InitInternal shouldn't be called twice!");
 
-  nsDOMWorker* worker = mWorkerXHR->mWorker;
+  nsDOMWorkerThread* worker = mWorkerXHR->mWorker;
   nsRefPtr<nsDOMWorkerPool> pool = worker->Pool();
 
   if (worker->IsCanceled()) {
     return NS_ERROR_ABORT;
   }
 
   nsIPrincipal* nodePrincipal = pool->ParentDocument()->NodePrincipal();
   nsIScriptContext* scriptContext = pool->ScriptContext();
@@ -421,21 +742,21 @@ 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->SnapshotXHRState(xhr);
+  rv = nullEvent->Init(xhr);
   NS_ENSURE_SUCCESS(rv, rv);
+  mLastXHREvent.swap(nullEvent);
 
-  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;
@@ -471,16 +792,17 @@ 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;
 
@@ -489,171 +811,347 @@ nsDOMWorkerXHRProxy::DestroyInternal()
 }
 
 void
 nsDOMWorkerXHRProxy::AddRemoveXHRListeners(PRBool aAdd)
 {
   nsCOMPtr<nsIDOMEventTarget> xhrTarget(do_QueryInterface(mXHR));
   NS_ASSERTION(xhrTarget, "This shouldn't fail!");
 
-  EventListenerFunction addRemoveEventListener =
-    aAdd ?
-    &nsIDOMEventTarget::AddEventListener :
-    &nsIDOMEventTarget::RemoveEventListener;
+  EventListenerFunction function = 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()->*addRemoveEventListener)(eventName, this, PR_FALSE);
-      (uploadTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
+      (xhrTarget.get()->*function)(eventName, this, PR_FALSE);
+      (uploadTarget.get()->*function)(eventName, this, PR_FALSE);
     }
   }
 
   for (; index < MAX_XHR_LISTENER_TYPE; index++) {
     eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
-    (xhrTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
+    (xhrTarget.get()->*function)(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) {
-    mWorkerXHRWN = mWorkerXHR->GetWrappedNative();
-    NS_ASSERTION(mWorkerXHRWN, "Null pointer!");
+    mWorkerXHR->AddRef();
     mXHR->Release();
   }
   else {
     mXHR->AddRef();
-    mWorkerXHRWN = nsnull;
+    mWorkerXHR->Release();
   }
 }
 
 nsresult
-nsDOMWorkerXHRProxy::UploadEventListenerAdded()
+nsDOMWorkerXHRProxy::AddEventListener(PRUint32 aType,
+                                      nsIDOMEventListener* aListener,
+                                      PRBool aOnXListener,
+                                      PRBool aUploadListener)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
-  // 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) {
+  if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
+      aType >= MAX_XHR_LISTENER_TYPE) {
+    // Silently fail on junk events.
     return NS_OK;
   }
 
-  nsRefPtr<nsDOMWorkerXHRAttachUploadListenersRunnable> attachRunnable =
-    new nsDOMWorkerXHRAttachUploadListenersRunnable(this);
-  NS_ENSURE_TRUE(attachRunnable, NS_ERROR_OUT_OF_MEMORY);
+  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<nsResultReturningRunnable> runnable =
-    new nsResultReturningRunnable(mMainThread, attachRunnable,
-                                  mWorkerXHR->mWorker);
-  NS_ENSURE_TRUE(runnable, 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!");
+      }
 
-  nsresult rv = runnable->Dispatch();
-  if (NS_FAILED(rv)) {
-    return rv;
+      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);
   }
 
-  NS_ASSERTION(mWantUploadListeners, "Should have set this!");
+  // 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!");
+  }
+
   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!");
 
-  {
-    nsAutoLock lock(mWorkerXHR->Lock());
-
-    if (mCanceled ||
-        (aEvent->mChannelID != -1 && aEvent->mChannelID != mChannelID)) {
-      return NS_OK;
-    }
-
-    mLastXHREvent = aEvent;
+  if (mCanceled ||
+      (aEvent->mChannelID != -1 && aEvent->mChannelID != mChannelID)) {
+    return NS_OK;
   }
 
-  nsIDOMEvent* event = static_cast<nsDOMWorkerEvent*>(aEvent);
-  return HandleEventInternal(aEvent->mXHREventType, event, aUploadEvent);
+  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);
 }
 
 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;
   }
 
-  nsIDOMEventTarget* target = aUploadListener ?
-    static_cast<nsDOMWorkerMessageHandler*>(mWorkerXHR->mUpload) :
-    static_cast<nsDOMWorkerMessageHandler*>(mWorkerXHR);
+  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!");
 
-  return target->DispatchEvent(aEvent, nsnull);
+  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.
 }
 
 PRBool
-nsDOMWorkerXHRProxy::HasListenersForType(const nsAString& aType,
+nsDOMWorkerXHRProxy::HasListenersForType(PRUint32 aType,
                                          nsIDOMEvent* aEvent)
 {
-#ifdef DEBUG
-  PRUint32 type = nsDOMWorkerXHREventTarget::GetListenerTypeFromString(aType);
-  NS_ASSERTION(type < MAX_XHR_LISTENER_TYPE, "Bad type!");
-#endif
+  NS_ASSERTION(aType < MAX_XHR_LISTENER_TYPE, "Bad type!");
 
-  if (mWorkerXHR->HasListeners(aType)) {
+  if (mXHRListeners[aType].Length()) {
     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 && mWorkerXHR->mUpload->HasListeners(aType)) {
+  if (checkUploadListeners && mUploadListeners[aType].Length()) {
     return PR_TRUE;
   }
 
   return PR_FALSE;
 }
 
 // nsIDOMEventListener
 NS_IMETHODIMP
@@ -668,39 +1166,30 @@ 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);
-    }
-
-    // Always bail out if we're canceled.
     return NS_ERROR_ABORT;
   }
 
-  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) {
+  if (!HasListenersForType(type, aEvent)) {
     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);
@@ -708,21 +1197,16 @@ 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,44 +40,48 @@
 #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
@@ -109,25 +113,38 @@ protected:
   nsresult InitInternal();
   void DestroyInternal();
 
   nsresult Destroy();
 
   void AddRemoveXHRListeners(PRBool aAdd);
   void FlipOwnership();
 
-  nsresult UploadEventListenerAdded();
+  nsresult AddEventListener(PRUint32 aType,
+                            nsIDOMEventListener* aListener,
+                            PRBool aOnXListener,
+                            PRBool aUploadListener);
 
-  nsresult HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent,
-                             PRBool aUploadEvent);
+  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 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);
@@ -140,35 +157,39 @@ 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(const nsAString& aType,
-                             nsIDOMEvent* aEvent = nsnull);
+  PRBool HasListenersForType(PRUint32 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,38 +42,26 @@ 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_longThread.html \
-  longThread_worker.js \
-  test_recursion.html \
-  recursion_worker.js \
-  test_regExpStatics.html \
-  regExpStatics_worker.js \
+  test_importScripts.html \
   test_simpleThread.html \
-  simpleThread_worker.js \
   test_threadErrors.html \
-  threadErrors_worker1.js \
-  threadErrors_worker2.js \
-  threadErrors_worker3.js \
-  threadErrors_worker4.js \
   test_threadTimeouts.html \
-  threadTimeouts_worker.js \
+  test_longThread.html \
+  test_recursion.html \
+  test_regExpStatics.html \
   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 @@
-onmessage = function(event) {
-  switch (event.data) {
+function messageListener(message, source) {
+  switch (message) {
     case 'start':
-      importScripts("importScripts_worker_imported2.js");
+      loadScripts("importScripts_worker_imported2.js");
       importedScriptFunction2();
       tryBadScripts();
-      postMessage('started');
+      source.postMessage('started');
       break;
     case 'stop':
       tryBadScripts();
-      postMessage('stopped');
+      postMessageToPool('stopped');
       break;
     default:
-      throw new Error("Bad message: " + event.data);
+      throw new Error("Bad message: " + message);
       break;
   }
 }
 
 // This caused security exceptions in the past, make sure it doesn't!
 var constructor = {}.constructor;
 
-importScripts("importScripts_worker_imported1.js");
+loadScripts("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 {
-      importScripts(url);
+      loadScripts(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,38 +13,37 @@ 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 worker = new Worker("importScripts_worker.js");
-
-  worker.onmessage = function(event) {
-    switch (event.data) {
+  var pool = navigator.newWorkerPool();
+  pool.messageListener = function(message, source) {
+    switch (message) {
       case "started":
-        worker.postMessage("stop");
+        source.postMessage("stop");
         break;
       case "stopped":
         SimpleTest.finish();
         break;
       default:
         ok(false, "Unexpected message:" + message);
         SimpleTest.finish();
     }
   };
 
-  worker.onerror = function(event) {
-    ok(false, "Worker had an error:" + event.data);
+  pool.errorListener = function(error, source) {
+    ok(false, "Worker had an error:" + error);
     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,43 +13,70 @@ 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">
 
-  const numThreads = 5;
+  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;
   var doneThreads = 0;
 
-  function onmessage(event) {
-    switch (event.data) {
+  pool.messageListener = function(message, source) {
+    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+    switch (message) {
       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();
     }
-  }
+  };
 
-  function onerror(event) {
+  pool.errorListener = function(error, source) {
+    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
     ok(false, "Worker had an error");
+    prefs.setIntPref("javascript.options.gczeal", 0);
     SimpleTest.finish();
-  }
+  };
 
   for (var i = 0; i < numThreads; i++) {
-    var worker = new Worker("longThread_worker.js");
-    worker.onmessage = onmessage;
-    worker.onerror = onerror;
+    var worker = pool.createWorker(workerScript);
     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,26 +12,41 @@ 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">
 
-  var worker = new Worker("recursion_worker.js");
+  function workerScript() {
+    function recurse() {
+      recurse();
+    }
 
-  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}]');
+    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}]');
     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,32 +14,60 @@ 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 pool = navigator.newWorkerPool();
+
   var doneWorkers = 0;
-  function onmessage(event) {
+  pool.messageListener = function(message, source) {
     if (++doneWorkers == WORKER_COUNT) {
       SimpleTest.finish();
     }
-  }
+  };
 
-  function onerror(event) {
-    ok(false, "Worker had an error: " + event.data);
+  pool.errorListener = function(error, source) {
+    ok(false, "Worker had an error: " + error);
     SimpleTest.finish();
   };
 
   for (var i = 0; i < WORKER_COUNT; i++) {
-    var worker = new Worker("regExpStatics_worker.js");
-    worker.onmessage = onmessage;
-    worker.onerror = onerror;
+    var worker = pool.createWorker("(" + workerScript + ")();");
     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,53 +13,85 @@ 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 worker = new Worker("simpleThread_worker.js");
+  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+  var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+              getService(Components.interfaces.nsIPrefBranch);
+  //prefs.setIntPref("javascript.options.gczeal", 2);
 
-  worker.addEventListener("message",function(event) {
-    is(event.target, worker);
-    switch (event.data) {
+  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) {
       case "no-op":
         break;
       case "started":
-        is(gotError, true);
-        worker.postMessage("no-op");
-        worker.postMessage("stop");
+        // pass message to self
+        pool.postMessage("no-op");
+        // pass message to source
+        source.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:" + event.data);
+        ok(false, "Unexpected message");
+        prefs.setIntPref("javascript.options.gczeal", 0);
         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();
-    }
   };
 
-  worker.postMessage("asdf");
+  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("start");
 
   SimpleTest.waitForExplicitFinish();
-
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/src/threads/test/test_threadErrors.html
+++ b/dom/src/threads/test/test_threadErrors.html
@@ -13,47 +13,86 @@ 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">
 
-  const expectedErrorCount = 4;
+  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(); " +
+    "} " +
+    "",
 
-  function messageListener(event) {
-    ok(false, "Unexpected message: " + event.data);
+    // 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");
     SimpleTest.finish();
   };
 
   var actualErrorCount = 0;
   var failedWorkers = [];
 
-  function errorListener(event) {
-    if (failedWorkers.indexOf(event.target) != -1) {
+  pool.errorListener = function(error, source) {
+    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+    if (failedWorkers.indexOf(source) != -1) {
+      dump("Already seen worker: " + source + "\n");
       ok(false, "Seen an extra error from this worker");
+      prefs.setIntPref("javascript.options.gczeal", 0);
       SimpleTest.finish();
       return;
     }
 
-    failedWorkers.push(event.target);
+    failedWorkers.push(source);
     actualErrorCount++;
 
     if (actualErrorCount == expectedErrorCount) {
+      prefs.setIntPref("javascript.options.gczeal", 0);
       SimpleTest.finish();
     }
   };
 
-  for (var i = 1; i <= expectedErrorCount; i++) {
-    var worker = new Worker("threadErrors_worker" + i + ".js");
-    worker.onmessage = messageListener;
-    worker.onerror = errorListener;
+  for (var i = 0; i < expectedErrorCount; i++) {
+    var worker = pool.createWorker(badWorkerScripts[i]);
     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,43 +13,93 @@ 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 worker = new Worker("threadTimeouts_worker.js");
+  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+  var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+              getService(Components.interfaces.nsIPrefBranch);
+  //prefs.setIntPref("javascript.options.gczeal", 2);
 
-  worker.onmessage = function(event) {
-    is(event.target, worker);
-    switch (event.data) {
+  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) {
       case "timeoutFinished":
-        event.target.postMessage("startInterval");
+        source.postMessage("startInterval");
         break;
       case "intervalFinished":
-        event.target.postMessage("cancelInterval");
+        source.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();
     }
   };
 
-  worker.onerror = function(event) {
-    is(event.target, worker);
-    ok(false, "Worker had an error: " + event.data);
+  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");
+
   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,21 +13,76 @@ 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">
 
-  var worker = new Worker("xhr_worker.js");
+  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);
 
-  worker.onmessage = function(event) {
-    is(event.target, worker);
-    var args = eval(event.data);
+    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);
     switch (args.type) {
       case "progress": {
         ok(parseInt(args.current) <= parseInt(args.total));
       } break;
       case "error": {
         ok(false, "XHR error: " + args.error);
       } break;
       case "load": {
@@ -37,22 +92,22 @@ Tests of DOM Worker Threads XHR(Bug 4504
       } break;
       default: {
         ok(false, "Unexpected message");
         SimpleTest.finish();
       }
     }
   };
 
-  worker.onerror = function(event) {
-    is(event.target, worker);
-    ok(false, "Worker had an error:" + event.data);
+  pool.errorListener = function(error, source) {
+    ok(false, "Worker had an error:" + error);
     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,19 +739,16 @@ 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