Bug 450452 - 'Implement XHR (minus X) for worker threads. r+sr=jst'
authorBen Turner <bent.mozilla@gmail.com>
Tue, 30 Sep 2008 16:56:57 -0700
changeset 19944 85a89d00d9b3a1aeb6e81a58cce2dd3868fdf1cd
parent 19943 08f8c6835a7347217d0e4a1e4a351400aca29be3
child 19945 66d809a1897f9b748551c12b33340ad185163edc
push id2561
push userbturner@mozilla.com
push dateTue, 30 Sep 2008 23:57:07 +0000
treeherdermozilla-central@85a89d00d9b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs450452
milestone1.9.1b1pre
Bug 450452 - 'Implement XHR (minus X) for worker threads. r+sr=jst'
content/base/public/nsIXMLHttpRequest.idl
content/base/src/nsXMLHttpRequest.cpp
content/base/src/nsXMLHttpRequest.h
dom/src/base/nsDOMClassInfo.cpp
dom/src/base/nsGlobalWindow.cpp
dom/src/threads/Makefile.in
dom/src/threads/nsDOMThreadService.cpp
dom/src/threads/nsDOMThreadService.h
dom/src/threads/nsDOMWorkerBase.cpp
dom/src/threads/nsDOMWorkerPool.cpp
dom/src/threads/nsDOMWorkerPool.h
dom/src/threads/nsDOMWorkerScriptLoader.cpp
dom/src/threads/nsDOMWorkerThread.cpp
dom/src/threads/nsDOMWorkerThread.h
dom/src/threads/nsDOMWorkerTimeout.cpp
dom/src/threads/nsDOMWorkerXHR.cpp
dom/src/threads/nsDOMWorkerXHR.h
dom/src/threads/nsDOMWorkerXHRProxiedFunctions.h
dom/src/threads/nsDOMWorkerXHRProxy.cpp
dom/src/threads/nsDOMWorkerXHRProxy.h
dom/src/threads/test/Makefile.in
dom/src/threads/test/testXHR.txt
dom/src/threads/test/test_xhr.html
toolkit/mozapps/update/test/unit/head_update.js
--- a/content/base/public/nsIXMLHttpRequest.idl
+++ b/content/base/public/nsIXMLHttpRequest.idl
@@ -329,103 +329,57 @@ interface nsIXMLHttpRequest : nsISupport
    *                  null.
    * @param scriptContext The script context to use for the request. May be
    *                      null.
    * @param ownerWindow The associated window for the request. May be null.
    */
   [noscript] void init(in nsIPrincipal principal,
                        in nsIScriptContext scriptContext,
                        in nsPIDOMWindow ownerWindow);
-};
 
-[scriptable, uuid(6e127bd2-b4c1-4a82-be0d-012bd24efb37)]
-interface nsIXMLHttpRequestUploadGetter : nsISupports {
   /**
    * Upload process can be tracked by adding event listener to |upload|.
    */
   readonly attribute nsIXMLHttpRequestUpload upload;
-};
-
-[scriptable, uuid(261676b4-d508-43bf-b099-74635a0ee2e9)]
-interface nsIJSXMLHttpRequest : nsISupports {
-  /**
-   * Meant to be a script-only mechanism for setting a load event listener.
-   * The attribute is expected to be JavaScript function object. When
-   * the load event occurs, the function is invoked.
-   * This attribute should not be used from native code!!
-   *
-   * After the initial response, all event listeners will be cleared.
-   * // XXXbz what does that mean, exactly?   
-   *
-   * Call open() before setting an onload listener.
-   *
-   * Mozilla only.
-   */
-  attribute nsIDOMEventListener onload;
-
-  /**
-   * Meant to be a script-only mechanism for setting an error event listener.
-   * The attribute is expected to be JavaScript function object. When
-   * the error event occurs, the function is invoked.
-   * This attribute should not be used from native code!!
-   *
-   * After the initial response, all event listeners will be cleared.
-   * // XXXbz what does that mean, exactly?   
-   *
-   * Call open() before setting an onerror listener.
-   *
-   * Mozilla only.
-   */
-  attribute nsIDOMEventListener onerror;
-
-  /**
-   * Meant to be a script-only mechanism for setting a progress event listener.
-   * The attribute is expected to be JavaScript function object. When
-   * the error event occurs, the function is invoked.
-   * This attribute should not be used from native code!!
-   * This event listener may be called multiple times during the open request.
-   *
-   * After the initial response, all event listeners will be cleared.
-   * // XXXbz what does that mean, exactly?
-   *
-   * This event listener must be set BEFORE calling open().
-   *
-   * Mozilla only.
-   */
-  attribute nsIDOMEventListener onprogress;
-
-  /**
-   * Meant to be a script-only mechanism for setting an upload progress event
-   * listener.
-   * This attribute should not be used from native code!!
-   * This event listener may be called multiple times during the upload..
-   *
-   * After the initial response, all event listeners will be cleared.
-   * // XXXbz what does that mean, exactly?
-   *
-   * This event listener must be set BEFORE calling open().
-   *
-   * Mozilla only.
-   */
-  attribute nsIDOMEventListener onuploadprogress;
 
   /**
    * Meant to be a script-only mechanism for setting a callback function.
    * The attribute is expected to be JavaScript function object. When the
    * readyState changes, the callback function will be called.
    * This attribute should not be used from native code!!
    *
    * After the initial response, all event listeners will be cleared.
    * // XXXbz what does that mean, exactly?   
    *
    * Call open() before setting an onreadystatechange listener.
    */
   attribute nsIDOMEventListener onreadystatechange;
 };
 
+/**
+ * DEPRECATED.
+ */
+[scriptable, uuid(261676b4-d508-43bf-b099-74635a0ee2e9)]
+interface nsIJSXMLHttpRequest : nsISupports {
+  /**
+   * Meant to be a script-only mechanism for setting an upload progress event
+   * listener.
+   * This attribute should not be used from native code!!
+   * This event listener may be called multiple times during the upload..
+   *
+   * After the initial response, all event listeners will be cleared.
+   * // XXXbz what does that mean, exactly?
+   *
+   * This event listener must be set BEFORE calling open().
+   *
+   * Mozilla only.
+   */
+  attribute nsIDOMEventListener onuploadprogress;
+};
+
 %{ C++
 #define NS_XMLHTTPREQUEST_CID                       \
  { /* d164e770-4157-11d4-9a42-000064657374 */       \
   0xd164e770, 0x4157, 0x11d4,                       \
  {0x9a, 0x42, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74} }
 #define NS_XMLHTTPREQUEST_CONTRACTID \
 "@mozilla.org/xmlextras/xmlhttprequest;1"
 %}
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -618,18 +618,19 @@ NS_IMPL_ADDREF_INHERITED(nsXMLHttpReques
 NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
 
 /////////////////////////////////////////////
 //
 //
 /////////////////////////////////////////////
 
 nsXMLHttpRequest::nsXMLHttpRequest()
-  : mState(XML_HTTP_REQUEST_UNINITIALIZED), mUploadTransferred(0),
-    mUploadTotal(0), mUploadComplete(PR_TRUE), mErrorLoad(PR_FALSE)
+  : mRequestObserver(nsnull), mState(XML_HTTP_REQUEST_UNINITIALIZED),
+    mUploadTransferred(0), mUploadTotal(0), mUploadComplete(PR_TRUE),
+    mErrorLoad(PR_FALSE), mFirstStartRequestSeen(PR_FALSE)
 {
   nsLayoutStatics::AddRef();
 }
 
 nsXMLHttpRequest::~nsXMLHttpRequest()
 {
   if (mListenerManager) {
     mListenerManager->Disconnect();
@@ -734,16 +735,22 @@ nsXMLHttpRequest::Initialize(nsISupports
   mPrincipal = scriptPrincipal->GetPrincipal();
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
   NS_ENSURE_STATE(sgo);
   mScriptContext = sgo->GetContext();
   NS_ENSURE_STATE(mScriptContext);
   return NS_OK; 
 }
 
+void
+nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver)
+{
+  mRequestObserver = aObserver;
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLHttpRequest,
                                                   nsXHREventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mReadRequest)
 
@@ -777,17 +784,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mUpload)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 // QueryInterface implementation for nsXMLHttpRequest
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLHttpRequest)
   NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequest)
   NS_INTERFACE_MAP_ENTRY(nsIJSXMLHttpRequest)
-  NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestUploadGetter)
   NS_INTERFACE_MAP_ENTRY(nsIDOMLoadListener)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
   NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
@@ -1566,16 +1572,21 @@ IsSameOrBaseChannel(nsIRequest* aPossibl
 
   return aPossibleBase == aChannel;
 }
 
 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
 NS_IMETHODIMP
 nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
 {
+  if (!mFirstStartRequestSeen && mRequestObserver) {
+    mFirstStartRequestSeen = PR_TRUE;
+    mRequestObserver->OnStartRequest(request, ctxt);
+  }
+
   if (!IsSameOrBaseChannel(request, mChannel)) {
     return NS_OK;
   }
 
   // Don't do anything if we have been aborted
   if (mState & XML_HTTP_REQUEST_UNINITIALIZED)
     return NS_OK;
 
@@ -1786,16 +1797,22 @@ nsXMLHttpRequest::OnStopRequest(nsIReque
     // Force a GC since we could be loading a lot of documents
     // (especially if streaming), and not doing anything that would
     // normally trigger a GC.
     mScriptContext->GC();
   }
 
   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
 
+  if (mRequestObserver && mState & XML_HTTP_REQUEST_GOT_FINAL_STOP) {
+    NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!");
+    mFirstStartRequestSeen = PR_FALSE;
+    mRequestObserver->OnStopRequest(request, ctxt, status);
+  }
+
   return rv;
 }
 
 nsresult
 nsXMLHttpRequest::RequestCompleted()
 {
   nsresult rv = NS_OK;
 
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -175,42 +175,36 @@ class nsXMLHttpRequest : public nsXHREve
                          public nsIXMLHttpRequest,
                          public nsIJSXMLHttpRequest,
                          public nsIDOMLoadListener,
                          public nsIStreamListener,
                          public nsIChannelEventSink,
                          public nsIProgressEventSink,
                          public nsIInterfaceRequestor,
                          public nsSupportsWeakReference,
-                         public nsIJSNativeInitializer,
-                         public nsIXMLHttpRequestUploadGetter
+                         public nsIJSNativeInitializer
 {
 public:
   nsXMLHttpRequest();
   virtual ~nsXMLHttpRequest();
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIXMLHttpRequest
   NS_DECL_NSIXMLHTTPREQUEST
 
   // nsIJSXMLHttpRequest
   NS_IMETHOD GetOnuploadprogress(nsIDOMEventListener** aOnuploadprogress);
   NS_IMETHOD SetOnuploadprogress(nsIDOMEventListener* aOnuploadprogress);
-  NS_IMETHOD GetOnreadystatechange(nsIDOMEventListener** aOnreadystatechange);
-  NS_IMETHOD SetOnreadystatechange(nsIDOMEventListener* aOnreadystatechange);
 
   NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsXHREventTarget::)
 
   // nsIDOMEventListener
   NS_DECL_NSIDOMEVENTLISTENER
 
-  // nsIXMLHttpRequestUploadGetter
-  NS_DECL_NSIXMLHTTPREQUESTUPLOADGETTER
-
   // nsIDOMLoadListener
   NS_IMETHOD Load(nsIDOMEvent* aEvent);
   NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent);
   NS_IMETHOD Unload(nsIDOMEvent* aEvent);
   NS_IMETHOD Abort(nsIDOMEvent* aEvent);
   NS_IMETHOD Error(nsIDOMEvent* aEvent);
 
   // nsIStreamListener
@@ -258,16 +252,18 @@ public:
     DispatchProgressEvent(aTarget, aType, PR_FALSE,
                           aLengthComputable, aLoaded, aTotal,
                           aLoaded, aLengthComputable ? aTotal : LL_MAXUINT);
   }
 
   // This is called by the factory constructor.
   nsresult Init();
 
+  void SetRequestObserver(nsIRequestObserver* aObserver);
+
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXMLHttpRequest,
                                            nsXHREventTarget)
 
 protected:
   friend class nsMultipartProxyListener;
 
   nsresult DetectCharset(nsACString& aCharset);
   nsresult ConvertBodyToText(nsAString& aOutBuffer);
@@ -337,28 +333,32 @@ protected:
   /**
    * Sink interfaces that we implement that mNotificationCallbacks may
    * want to also be notified for.  These are inited lazily if we're
    * asked for the relevant interface.
    */
   nsCOMPtr<nsIChannelEventSink> mChannelEventSink;
   nsCOMPtr<nsIProgressEventSink> mProgressEventSink;
 
+  nsIRequestObserver* mRequestObserver;
+
   PRUint32 mState;
 
   // List of potentially dangerous headers explicitly set using
   // SetRequestHeader.
   nsTArray<nsCString> mExtraRequestHeaders;
 
   nsRefPtr<nsXMLHttpRequestUpload> mUpload;
   PRUint32 mUploadTransferred;
   PRUint32 mUploadTotal;
   PRPackedBool mUploadComplete;
 
   PRPackedBool mErrorLoad;
+
+  PRPackedBool mFirstStartRequestSeen;
 };
 
 // helper class to expose a progress DOM Event
 
 class nsXMLHttpProgressEvent : public nsIDOMProgressEvent,
                                public nsIDOMLSProgressEvent,
                                public nsIDOMNSEvent,
                                public nsIPrivateDOMEvent
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -3382,17 +3382,16 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XMLSerializer, nsIDOMSerializer)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSerializer)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(XMLHttpRequest, nsIXMLHttpRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIXMLHttpRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIJSXMLHttpRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIXMLHttpRequestEventTarget)
-    DOM_CLASSINFO_MAP_ENTRY(nsIXMLHttpRequestUploadGetter)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
     DOM_CLASSINFO_MAP_ENTRY(nsIInterfaceRequestor)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XMLHttpProgressEvent, nsIDOMEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMLSProgressEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMProgressEvent)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -862,16 +862,24 @@ nsGlobalWindow::ClearControllers()
 void
 nsGlobalWindow::FreeInnerObjects(PRBool aClearScope)
 {
   NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window");
 
   // Kill all of the workers for this window.
   nsDOMThreadService* dts = nsDOMThreadService::get();
   if (dts) {
+    nsIScriptContext *scx = GetContextInternal();
+
+    JSContext *cx = scx ? (JSContext *)scx->GetNativeContext() : nsnull;
+
+    // Have to suspend this request here because CancelWorkersForGlobal will
+    // lock until the worker has died and that could cause a deadlock.
+    JSAutoSuspendRequest asr(cx);
+
     dts->CancelWorkersForGlobal(static_cast<nsIScriptGlobalObject*>(this));
   }
 
   ClearAllTimeouts();
 
   mChromeEventHandler = nsnull;
 
   if (mListenerManager) {
--- a/dom/src/threads/Makefile.in
+++ b/dom/src/threads/Makefile.in
@@ -46,37 +46,46 @@ include $(DEPTH)/config/autoconf.mk
 MODULE           = dom
 LIBRARY_NAME     = domthreads_s
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 
 REQUIRES = \
   caps \
   content \
+  gfx \
   js \
   layout \
+  locale \
   necko \
   pref \
   string \
+  thebes \
   widget \
   xpcom \
   xpconnect \
   $(NULL)
 
 CPPSRCS = \
   nsDOMThreadService.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
@@ -473,16 +473,23 @@ void
 DOMWorkerErrorReporter(JSContext* aCx,
                        const char* aMessage,
                        JSErrorReport* aReport)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Huh?!");
 
   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;
+  }
+
   nsresult rv;
   nsCOMPtr<nsIScriptError> errorObject =
     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv,);
 
   const PRUnichar* message =
     reinterpret_cast<const PRUnichar*>(aReport->ucmessage);
 
--- a/dom/src/threads/nsDOMThreadService.h
+++ b/dom/src/threads/nsDOMThreadService.h
@@ -73,16 +73,18 @@ class nsDOMThreadService : public nsIEve
                            public nsIObserver,
                            public nsIThreadPoolListener,
                            public nsIDOMThreadService
 {
   friend class nsDOMWorkerPool;
   friend class nsDOMWorkerRunnable;
   friend class nsDOMWorkerThread;
   friend class nsDOMWorkerTimeout;
+  friend class nsDOMWorkerXHR;
+  friend class nsDOMWorkerXHRProxy;
   friend class nsLayoutStatics;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIEVENTTARGET
   NS_DECL_NSIOBSERVER
   NS_DECL_NSITHREADPOOLLISTENER
   NS_DECL_NSIDOMTHREADSERVICE
--- a/dom/src/threads/nsDOMWorkerBase.cpp
+++ b/dom/src/threads/nsDOMWorkerBase.cpp
@@ -87,22 +87,26 @@ public:
     mSource->QueryInterface(NS_GET_IID(nsIDOMWorkerPool),
                             getter_AddRefs(sourceIsPool));
 
     nsCOMPtr<nsIDOMWorkerPool> targetIsPool;
     mTarget->QueryInterface(NS_GET_IID(nsIDOMWorkerPool),
                             getter_AddRefs(targetIsPool));
 #endif
 
-    LOG(("Posting message '%s' from %s [0x%p] to %s [0x%p]",
-         utf8Message.get(), sourceIsPool ? poolStr : workerStr,
-         static_cast<void*>(mSource.get()), targetIsPool ? poolStr : workerStr,
-         static_cast<void*>(mTarget.get())));
+    if (!(mTarget->IsCanceled() || mSource->IsCanceled())) {
+      LOG(("Posting message '%s' from %s [0x%p] to %s [0x%p]",
+           utf8Message.get(),
+           sourceIsPool ? poolStr : workerStr,
+           static_cast<void*>(mSource.get()),
+           targetIsPool ? poolStr : workerStr,
+           static_cast<void*>(mTarget.get())));
 
-    mTarget->HandleMessage(mMessage, mSource);
+      mTarget->HandleMessage(mMessage, mSource);
+    }
 
     return NS_OK;
   }
 
 protected:
   nsString mMessage;
   nsRefPtr<nsDOMWorkerBase> mSource;
   nsRefPtr<nsDOMWorkerBase> mTarget;
--- a/dom/src/threads/nsDOMWorkerPool.cpp
+++ b/dom/src/threads/nsDOMWorkerPool.cpp
@@ -35,16 +35,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsDOMWorkerPool.h"
 
 // Interfaces
+#include "nsIDocument.h"
 #include "nsIDOMClassInfo.h"
 #include "nsIJSContextStack.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIServiceManager.h"
 #include "nsIThreadManager.h"
 #include "nsIXPConnect.h"
 #include "nsPIDOMWindow.h"
@@ -97,25 +98,18 @@ NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorker
 
 NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerPool)
 
 nsresult
 nsDOMWorkerPool::Init()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  // GetCurrentJSContext () can return a null context... We shouldn't
-  // ever see that here.
-  JSContext* cx = nsContentUtils::GetCurrentJSContext();
-  NS_ENSURE_TRUE(cx, NS_ERROR_UNEXPECTED);
-
-  nsIScriptContext* scriptContext = GetScriptContextFromJSContext(cx);
-  NS_ENSURE_STATE(scriptContext);
-
-  nsIScriptGlobalObject* globalObject = scriptContext->GetGlobalObject();
+  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();
@@ -232,23 +226,32 @@ nsDOMWorkerPool::ResumeWorkersForGlobal(
     Resume();
 
     nsAutoMonitor mon(mMonitor);
     mon.NotifyAll();
   }
 }
 
 nsIDocument*
-nsDOMWorkerPool::GetParentDocument()
+nsDOMWorkerPool::ParentDocument()
 {
   NS_ASSERTION(NS_IsMainThread(),
                "Don't touch the non-threadsafe document off the main thread!");
   return mParentDocument;
 }
 
+nsIScriptContext*
+nsDOMWorkerPool::ScriptContext()
+{
+  NS_ASSERTION(NS_IsMainThread(),
+               "Don't touch the non-threadsafe script context off the main "
+               "thread!");
+  return mParentDocument->GetScriptGlobalObject()->GetContext();
+}
+
 NS_IMETHODIMP
 nsDOMWorkerPool::PostMessage(const nsAString& aMessage)
 {
   nsresult rv = PostMessageInternal(aMessage);
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
--- a/dom/src/threads/nsDOMWorkerPool.h
+++ b/dom/src/threads/nsDOMWorkerPool.h
@@ -48,16 +48,17 @@
 // Other includes
 #include "jsapi.h"
 #include "nsStringGlue.h"
 #include "nsTPtrArray.h"
 #include "prmon.h"
 
 class nsDOMWorkerThread;
 class nsIDocument;
+class nsIScriptContext;
 class nsIScriptError;
 class nsIScriptGlobalObject;
 
 /**
  * The pool is almost always touched only on the main thread.
  */
 class nsDOMWorkerPool : public nsDOMWorkerBase,
                         public nsIDOMWorkerPool,
@@ -79,16 +80,19 @@ public:
 
   nsDOMWorkerPool(nsIDocument* aDocument);
 
   // For nsDOMWorkerBase
   virtual nsDOMWorkerPool* Pool() {
     return this;
   }
 
+  nsIDocument* ParentDocument();
+  nsIScriptContext* ScriptContext();
+
 private:
   virtual ~nsDOMWorkerPool();
 
   nsresult Init();
 
   // For nsDOMWorkerBase
   virtual nsresult HandleMessage(const nsAString& aMessage,
                                  nsDOMWorkerBase* aSourceThread);
@@ -104,18 +108,16 @@ private:
   void CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
   void SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
   void ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
 
   PRMonitor* Monitor() {
     return mMonitor;
   }
 
-  nsIDocument* GetParentDocument();
-
   // Weak reference to the window that created and owns this pool.
   nsISupports* mParentGlobal;
 
   // 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.
--- a/dom/src/threads/nsDOMWorkerScriptLoader.cpp
+++ b/dom/src/threads/nsDOMWorkerScriptLoader.cpp
@@ -121,17 +121,23 @@ nsDOMWorkerScriptLoader::LoadScripts(nsD
   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;
   }
 
@@ -437,17 +443,17 @@ nsDOMWorkerScriptLoader::OnStreamComplet
 }
 
 nsresult
 nsDOMWorkerScriptLoader::RunInternal()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   // Things we need to make all this work...
-  nsIDocument* parentDoc = mWorker->Pool()->GetParentDocument();
+  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());
 
@@ -573,17 +579,17 @@ nsDOMWorkerScriptLoader::OnStreamComplet
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (!(aStringLen && aString)) {
     return rv = NS_ERROR_UNEXPECTED;
   }
 
-  nsIDocument* parentDoc = mWorker->Pool()->GetParentDocument();
+  nsIDocument* parentDoc = mWorker->Pool()->ParentDocument();
   NS_ASSERTION(parentDoc, "Null parent document?!");
 
   // Use the regular nsScriptLoader for this grunt work! Should be just fine
   // because we're running on the main thread.
   rv = nsScriptLoader::ConvertToUTF16(loadInfo.channel, aString, aStringLen,
                                       EmptyString(), parentDoc,
                                       loadInfo.scriptText);
   if (NS_FAILED(rv)) {
--- a/dom/src/threads/nsDOMWorkerThread.cpp
+++ b/dom/src/threads/nsDOMWorkerThread.cpp
@@ -57,16 +57,17 @@
 #include "nsThreadUtils.h"
 
 // DOMWorker includes
 #include "nsDOMWorkerPool.h"
 #include "nsDOMWorkerScriptLoader.h"
 #include "nsDOMWorkerSecurityManager.h"
 #include "nsDOMThreadService.h"
 #include "nsDOMWorkerTimeout.h"
+#include "nsDOMWorkerXHR.h"
 
 #define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
 
 // XXX Could make these functions of nsDOMWorkerThread instead.
 class nsDOMWorkerFunctions
 {
 public:
   // Same as window.dump().
@@ -95,16 +96,19 @@ public:
 
   // Used for both clearTimeout() and clearInterval().
   static JSBool KillTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
                             jsval* aArgv, jsval* aRval);
 
   static JSBool LoadScripts(JSContext* aCx, JSObject* aObj, uintN aArgc,
                             jsval* aArgv, jsval* aRval);
 
+  static JSBool NewXMLHttpRequest(JSContext* aCx, JSObject* aObj, uintN aArgc,
+                                  jsval* aArgv, jsval* aRval);
+
 private:
   // Internal helper for SetTimeout and SetInterval.
   static JSBool MakeTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
                             jsval* aArgv, jsval* aRval, PRBool aIsInterval);
 };
 
 JSBool
 nsDOMWorkerFunctions::Dump(JSContext* aCx,
@@ -298,25 +302,83 @@ nsDOMWorkerFunctions::LoadScripts(JSCont
   nsresult rv = loader->LoadScripts(worker, aCx, urls);
   if (NS_FAILED(rv)) {
     return JS_FALSE;
   }
 
   return JS_TRUE;
 }
 
+JSBool
+nsDOMWorkerFunctions::NewXMLHttpRequest(JSContext* aCx,
+                                        JSObject* aObj,
+                                        uintN aArgc,
+                                        jsval* /* aArgv */,
+                                        jsval* aRval)
+{
+  nsDOMWorkerThread* worker =
+    static_cast<nsDOMWorkerThread*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (aArgc) {
+    JS_ReportError(aCx, "Constructor takes no arguments!");
+    return JS_FALSE;
+  }
+
+  nsRefPtr<nsDOMWorkerXHR> xhr = new nsDOMWorkerXHR(worker);
+  if (!xhr) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsresult rv = xhr->Init();
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to construct XHR!");
+    return JS_FALSE;
+  }
+
+  nsCOMPtr<nsISupports> xhrSupports;
+  xhr->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(xhrSupports));
+  NS_ASSERTION(xhrSupports, "Impossible!");
+
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> xhrWrapped;
+  rv = xpc->WrapNative(aCx, aObj, xhrSupports, NS_GET_IID(nsIXMLHttpRequest),
+                       getter_AddRefs(xhrWrapped));
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to wrap XHR!");
+    return JS_FALSE;
+  }
+
+  JSObject* xhrJSObj;
+  rv = xhrWrapped->GetJSObject(&xhrJSObj);
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to get JSObject!");
+    return JS_FALSE;
+  }
+
+  *aRval = OBJECT_TO_JSVAL(xhrJSObj);
+  return JS_TRUE;
+}
+
 JSFunctionSpec gDOMWorkerFunctions[] = {
   { "dump",                  nsDOMWorkerFunctions::Dump,              1, 0, 0 },
   { "debug",                 nsDOMWorkerFunctions::DebugDump,         1, 0, 0 },
   { "postMessageToPool",     nsDOMWorkerFunctions::PostMessage,       1, 0, 0 },
   { "setTimeout",            nsDOMWorkerFunctions::SetTimeout,        1, 0, 0 },
   { "clearTimeout",          nsDOMWorkerFunctions::KillTimeout,       1, 0, 0 },
   { "setInterval",           nsDOMWorkerFunctions::SetInterval,       1, 0, 0 },
   { "clearInterval",         nsDOMWorkerFunctions::KillTimeout,       1, 0, 0 },
   { "loadScripts",           nsDOMWorkerFunctions::LoadScripts,       1, 0, 0 },
+  { "XMLHttpRequest",        nsDOMWorkerFunctions::NewXMLHttpRequest, 0, 0, 0 },
 #ifdef MOZ_SHARK
   { "startShark",            js_StartShark,                           0, 0, 0 },
   { "stopShark",             js_StopShark,                            0, 0, 0 },
   { "connectShark",          js_ConnectShark,                         0, 0, 0 },
   { "disconnectShark",       js_DisconnectShark,                      0, 0, 0 },
 #endif
   { nsnull,                  nsnull,                                  0, 0, 0 }
 };
@@ -546,16 +608,17 @@ nsDOMWorkerThread::DispatchMessage(nsIRu
 
 void
 nsDOMWorkerThread::Cancel()
 {
   nsDOMWorkerBase::Cancel();
 
   // Do this before waiting on the thread service below!
   CancelScriptLoaders();
+  CancelXHRs();
 
   // If we're suspended there's a good chance that we're already paused waiting
   // on the pool's monitor. Waiting on the thread service's lock will deadlock.
   if (!IsSuspended()) {
     nsDOMThreadService::get()->WaitForCanceledWorker(this);
   }
 
   ClearTimeouts();
@@ -712,45 +775,50 @@ nsDOMWorkerTimeout*
 nsDOMWorkerThread::NextTimeout(nsDOMWorkerTimeout* aTimeout)
 {
   // Only called within the lock!
   nsDOMWorkerTimeout* next =
     static_cast<nsDOMWorkerTimeout*>(PR_NEXT_LINK(aTimeout));
   return next == &mTimeouts ? nsnull : next;
 }
 
-void
+PRBool
 nsDOMWorkerThread::AddTimeout(nsDOMWorkerTimeout* aTimeout)
 {
   // This should only ever be called on the worker thread... but there's no way
   // to really assert that since we're using a thread pool.
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aTimeout, "Null pointer!");
 
   PRIntervalTime newInterval = aTimeout->GetInterval();
 
   if (IsSuspended()) {
     aTimeout->Suspend(PR_Now());
   }
 
   nsAutoLock lock(mLock);
 
+  if (IsCanceled()) {
+    return PR_FALSE;
+  }
+
   // XXX Currently stored in the order that they should execute (like the window
   //     timeouts are) but we don't flush all expired timeouts the same way that
   //     the window does... Either we should or this is unnecessary.
   for (nsDOMWorkerTimeout* timeout = FirstTimeout();
        timeout;
        timeout = NextTimeout(timeout)) {
     if (timeout->GetInterval() > newInterval) {
       PR_INSERT_BEFORE(aTimeout, timeout);
-      return;
+      return PR_TRUE;
     }
   }
 
   PR_APPEND_LINK(aTimeout, &mTimeouts);
+  return PR_TRUE;
 }
 
 void
 nsDOMWorkerThread::RemoveTimeout(nsDOMWorkerTimeout* aTimeout)
 {
   nsAutoLock lock(mLock);
 
   PR_REMOVE_LINK(aTimeout);
@@ -854,15 +922,63 @@ nsDOMWorkerThread::CancelScriptLoaders()
   }
 
   PRUint32 loaderCount = loaders.Length();
   for (PRUint32 index = 0; index < loaderCount; index++) {
     loaders[index]->Cancel();
   }
 }
 
+PRBool
+nsDOMWorkerThread::AddXHR(nsDOMWorkerXHR* aXHR)
+{
+  nsAutoLock lock(mLock);
+
+  if (IsCanceled()) {
+    return PR_FALSE;
+  }
+
+#ifdef DEBUG
+  PRBool contains = mXHRs.Contains(aXHR);
+  NS_ASSERTION(!contains, "Adding an XHR twice!");
+#endif
+
+  nsDOMWorkerXHR** newElement = mXHRs.AppendElement(aXHR);
+  NS_ENSURE_TRUE(newElement, PR_FALSE);
+
+  return PR_TRUE;
+}
+
+void
+nsDOMWorkerThread::RemoveXHR(nsDOMWorkerXHR* aXHR)
+{
+  nsAutoLock lock(mLock);
+#ifdef DEBUG
+  PRBool removed =
+#endif
+  mXHRs.RemoveElement(aXHR);
+  NS_WARN_IF_FALSE(removed, "Removed an XHR that was never added?!");
+}
+
+void
+nsDOMWorkerThread::CancelXHRs()
+{
+  nsAutoTArray<nsDOMWorkerXHR*, 20> xhrs;
+
+  // Must call Cancel outside the lock!
+  {
+    nsAutoLock lock(mLock);
+    xhrs.AppendElements(mXHRs);
+  }
+
+  PRUint32 xhrCount = xhrs.Length();
+  for (PRUint32 index = 0; index < xhrCount; index++) {
+    xhrs[index]->Cancel();
+  }
+}
+
 NS_IMETHODIMP
 nsDOMWorkerThread::PostMessage(const nsAString& aMessage)
 {
   nsresult rv = PostMessageInternal(aMessage);
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
--- a/dom/src/threads/nsDOMWorkerThread.h
+++ b/dom/src/threads/nsDOMWorkerThread.h
@@ -123,16 +123,17 @@ class nsDOMWorkerThread : public nsDOMWo
                           public nsIClassInfo
 {
   friend class nsDOMCreateJSContextRunnable;
   friend class nsDOMWorkerFunctions;
   friend class nsDOMWorkerPool;
   friend class nsDOMWorkerRunnable;
   friend class nsDOMWorkerScriptLoader;
   friend class nsDOMWorkerTimeout;
+  friend class nsDOMWorkerXHR;
 
   friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMWORKERTHREAD
   NS_DECL_NSICLASSINFO
 
@@ -162,25 +163,29 @@ private:
   virtual void Resume();
 
   PRBool SetGlobalForContext(JSContext* aCx);
   PRBool CompileGlobalObject(JSContext* aCx);
 
   inline nsDOMWorkerTimeout* FirstTimeout();
   inline nsDOMWorkerTimeout* NextTimeout(nsDOMWorkerTimeout* aTimeout);
 
-  void AddTimeout(nsDOMWorkerTimeout* aTimeout);
+  PRBool AddTimeout(nsDOMWorkerTimeout* aTimeout);
   void RemoveTimeout(nsDOMWorkerTimeout* aTimeout);
   void ClearTimeouts();
   void CancelTimeout(PRUint32 aId);
   void SuspendTimeouts();
   void ResumeTimeouts();
 
   void CancelScriptLoaders();
 
+  PRBool AddXHR(nsDOMWorkerXHR* aXHR);
+  void RemoveXHR(nsDOMWorkerXHR* aXHR);
+  void CancelXHRs();
+
   PRLock* Lock() {
     return mLock;
   }
 
   nsDOMWorkerPool* mPool;
   nsString mSource;
   nsString mSourceURL;
 
@@ -190,11 +195,12 @@ private:
   PRUint32 mCallbackCount;
 
   PRUint32 mNextTimeoutId;
 
   PRLock* mLock;
   PRCList mTimeouts;
 
   nsTArray<nsDOMWorkerScriptLoader*> mScriptLoaders;
+  nsTArray<nsDOMWorkerXHR*> mXHRs;
 };
 
 #endif /* __NSDOMWORKERTHREAD_H__ */
--- a/dom/src/threads/nsDOMWorkerTimeout.cpp
+++ b/dom/src/threads/nsDOMWorkerTimeout.cpp
@@ -324,29 +324,36 @@ nsDOMWorkerTimeout::Init(JSContext* aCx,
   if (aIsInterval) {
     type = nsITimer::TYPE_REPEATING_SLACK;
   }
   else {
     type = nsITimer::TYPE_ONE_SHOT;
   }
   mIsInterval = aIsInterval;
 
-  mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+  nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsIEventTarget* target =
     static_cast<nsIEventTarget*>(nsDOMThreadService::get());
 
-  rv = mTimer->SetTarget(target);
+  rv = timer->SetTarget(target);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = timer->InitWithCallback(this, interval, type);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = mTimer->InitWithCallback(this, interval, type);
-  NS_ENSURE_SUCCESS(rv, rv);
+  mTimer.swap(timer);
 
-  mWorker->AddTimeout(this);
+  if (!mWorker->AddTimeout(this)) {
+    // Must have been canceled.
+    mTimer->Cancel();
+    mTimer = nsnull;
+    return NS_ERROR_ABORT;
+  }
 
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerTimeout::Run()
 {
   NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerXHR.cpp
@@ -0,0 +1,915 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMWorkerXHR.h"
+
+// Interfaces
+#include "nsIDocument.h"
+#include "nsIDOMEvent.h"
+#include "nsIThread.h"
+#include "nsIXPConnect.h"
+
+// Other includes
+#include "nsAutoLock.h"
+#include "nsAXPCNativeCallContext.h"
+#include "nsComponentManagerUtils.h"
+#include "nsContentUtils.h"
+#include "nsIClassInfoImpl.h"
+#include "nsJSUtils.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+
+// DOMWorker includes
+#include "nsDOMThreadService.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
+// things will break!
+const char* const nsDOMWorkerXHREventTarget::sListenerTypes[] = {
+  // nsIXMLHttpRequestEventTarget listeners.
+  "abort",                             /* LISTENER_TYPE_ABORT */
+  "error",                             /* LISTENER_TYPE_ERROR */
+  "load",                              /* LISTENER_TYPE_LOAD */
+  "loadstart",                         /* LISTENER_TYPE_LOADSTART */
+  "progress",                          /* LISTENER_TYPE_PROGRESS */
+
+  // nsIXMLHttpRequest listeners.
+  "readystatechange"                   /* LISTENER_TYPE_READYSTATECHANGE */
+};
+
+// 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
+
+// This should always be set to the length of sListenerTypes.
+const PRUint32 nsDOMWorkerXHREventTarget::sMaxXHREventTypes =
+  NS_ARRAY_LENGTH(nsDOMWorkerXHREventTarget::sListenerTypes);
+
+// This should be set to the index of the first event type that is *not*
+// supported by the Upload object.
+const PRUint32 nsDOMWorkerXHREventTarget::sMaxUploadEventTypes =
+  LISTENER_TYPE_READYSTATECHANGE;
+
+// Enforce the invariant that the upload object supports no more event types
+// than the xhr object.
+PR_STATIC_ASSERT(nsDOMWorkerXHREventTarget::sMaxXHREventTypes >=
+                 nsDOMWorkerXHREventTarget::sMaxUploadEventTypes);
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerXHREventTarget,
+                              nsIDOMEventTarget,
+                              nsIXMLHttpRequestEventTarget)
+
+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_ENSURE_ARG_POINTER(aOnabort);
+
+  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_ABORT);
+  listener.forget(aOnabort);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::SetOnabort(nsIDOMEventListener* aOnabort)
+{
+  return SetEventListener(LISTENER_TYPE_ABORT, aOnabort, PR_TRUE);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::GetOnerror(nsIDOMEventListener** aOnerror)
+{
+  NS_ENSURE_ARG_POINTER(aOnerror);
+
+  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_ERROR);
+  listener.forget(aOnerror);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::SetOnerror(nsIDOMEventListener* aOnerror)
+{
+  return SetEventListener(LISTENER_TYPE_ERROR, aOnerror, PR_TRUE);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::GetOnload(nsIDOMEventListener** aOnload)
+{
+  NS_ENSURE_ARG_POINTER(aOnload);
+
+  nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_LOAD);
+  listener.forget(aOnload);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::SetOnload(nsIDOMEventListener* aOnload)
+{
+  return SetEventListener(LISTENER_TYPE_LOAD, aOnload, PR_TRUE);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::GetOnloadstart(nsIDOMEventListener** aOnloadstart)
+{
+  NS_ENSURE_ARG_POINTER(aOnloadstart);
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(LISTENER_TYPE_LOADSTART);
+  listener.forget(aOnloadstart);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::SetOnloadstart(nsIDOMEventListener* aOnloadstart)
+{
+  return SetEventListener(LISTENER_TYPE_LOADSTART, aOnloadstart, PR_TRUE);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::GetOnprogress(nsIDOMEventListener** aOnprogress)
+{
+  NS_ENSURE_ARG_POINTER(aOnprogress);
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(LISTENER_TYPE_PROGRESS);
+  listener.forget(aOnprogress);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::SetOnprogress(nsIDOMEventListener* aOnprogress)
+{
+  return SetEventListener(LISTENER_TYPE_PROGRESS, aOnprogress, PR_TRUE);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::AddEventListener(const nsAString& aType,
+                                            nsIDOMEventListener* aListener,
+                                            PRBool aUseCapture)
+{
+  NS_ENSURE_ARG_POINTER(aListener);
+
+  PRUint32 type = GetListenerTypeFromString(aType);
+  if (type > sMaxXHREventTypes) {
+    // Silently ignore junk events.
+    return NS_OK;
+  }
+
+  return SetEventListener(type, aListener, PR_FALSE);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::RemoveEventListener(const nsAString& aType,
+                                               nsIDOMEventListener* aListener,
+                                               PRBool aUseCapture)
+{
+  NS_ENSURE_ARG_POINTER(aListener);
+
+  PRUint32 type = GetListenerTypeFromString(aType);
+  if (type > sMaxXHREventTypes) {
+    // Silently ignore junk events.
+    return NS_OK;
+  }
+
+  return UnsetEventListener(type, aListener);
+}
+
+/* ec702b78-c30f-439f-9a9b-a5dae17ee0fc */
+#define NS_IPRIVATEWORKERXHREVENT_IID                      \
+{                                                          \
+  0xec702b78,                                              \
+  0xc30f,                                                  \
+  0x439f,                                                  \
+  { 0x9a, 0x9b, 0xa5, 0xda, 0xe1, 0x7e, 0xe0, 0xfc }       \
+}
+
+class nsIPrivateWorkerXHREvent : public nsIDOMEvent
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IPRIVATEWORKERXHREVENT_IID)
+  virtual PRBool PreventDefaultCalled() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIPrivateWorkerXHREvent,
+                              NS_IPRIVATEWORKERXHREVENT_IID)
+
+#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();
+  }
+
+  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(aWorkerXHR, "Must have a worker XHR!");
+}
+
+NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget,
+                                                   nsIXMLHttpRequestUpload,
+                                                   nsIClassInfo)
+
+NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHRUpload, nsIDOMEventTarget,
+                                                   nsIXMLHttpRequestEventTarget,
+                                                   nsIXMLHttpRequestUpload)
+
+NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHRUpload)
+
+nsresult
+nsDOMWorkerXHRUpload::SetEventListener(PRUint32 aType,
+                                       nsIDOMEventListener* aListener,
+                                       PRBool aOnXListener)
+{
+  if (mWorkerXHR->mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  return mWorkerXHR->mXHRProxy->AddEventListener(aType, aListener, aOnXListener,
+                                                 PR_TRUE);
+}
+
+nsresult
+nsDOMWorkerXHRUpload::UnsetEventListener(PRUint32 aType,
+                                         nsIDOMEventListener* aListener)
+{
+  if (mWorkerXHR->mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  return mWorkerXHR->mXHRProxy->RemoveEventListener(aType, aListener, PR_TRUE);
+}
+
+nsresult
+nsDOMWorkerXHRUpload::HandleWorkerEvent(nsIDOMEvent* aEvent)
+{
+  if (mWorkerXHR->mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  return mWorkerXHR->mXHRProxy->HandleWorkerEvent(aEvent, PR_TRUE);
+}
+
+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!");
+}
+
+nsDOMWorkerXHR::~nsDOMWorkerXHR()
+{
+  if (!mCanceled) {
+    mWorker->RemoveXHR(this);
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget,
+                                             nsIXMLHttpRequest,
+                                             nsIClassInfo)
+
+NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHR, nsIDOMEventTarget,
+                                             nsIXMLHttpRequestEventTarget,
+                                             nsIXMLHttpRequest)
+
+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;
+}
+
+void
+nsDOMWorkerXHR::Cancel()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // Just in case mUpload holds the only ref to this object we make sure to stay
+  // alive through this call.
+  nsRefPtr<nsDOMWorkerXHR> kungFuDeathGrip(this);
+
+  {
+    // This lock is here to prevent a race between Cancel and GetUpload, not to
+    // protect mCanceled.
+    nsAutoLock lock(mWorker->Lock());
+
+    mCanceled = PR_TRUE;
+    mUpload = nsnull;
+  }
+
+  if (mXHRProxy) {
+    mXHRProxy->Destroy();
+  }
+
+  mWorker->RemoveXHR(this);
+  mWorker = nsnull;
+}
+
+nsresult
+nsDOMWorkerXHR::SetEventListener(PRUint32 aType,
+                                 nsIDOMEventListener* aListener,
+                                 PRBool aOnXListener)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  return mXHRProxy->AddEventListener(aType, aListener, aOnXListener, PR_FALSE);
+}
+
+nsresult
+nsDOMWorkerXHR::UnsetEventListener(PRUint32 aType,
+                                   nsIDOMEventListener* aListener)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  return mXHRProxy->RemoveEventListener(aType, aListener, PR_FALSE);
+}
+
+nsresult
+nsDOMWorkerXHR::HandleWorkerEvent(nsIDOMEvent* aEvent)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  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!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::GetResponseXML(nsIDOMDocument** aResponseXML)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::GetResponseText(nsAString& aResponseText)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = mXHRProxy->GetResponseText(aResponseText);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::GetStatus(PRUint32* aStatus)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aStatus);
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = mXHRProxy->GetStatus(aStatus);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::GetStatusText(nsACString& aStatusText)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = mXHRProxy->GetStatusText(aStatusText);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::Abort()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = mXHRProxy->Abort();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::GetAllResponseHeaders(char** _retval)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(_retval);
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = mXHRProxy->GetAllResponseHeaders(_retval);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::GetResponseHeader(const nsACString& aHeader,
+                                  nsACString& _retval)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = mXHRProxy->GetResponseHeader(aHeader, _retval);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::OpenRequest(const nsACString& aMethod,
+                            const nsACString& aUrl,
+                            PRBool aAsync,
+                            const nsAString& aUser,
+                            const nsAString& aPassword)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = mXHRProxy->OpenRequest(aMethod, aUrl, aAsync, aUser, aPassword);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::Open(const nsACString& aMethod,
+                     const nsACString& aUrl)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  PRBool async = PR_TRUE;
+  nsAutoString user, password;
+
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+  NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
+
+  nsAXPCNativeCallContext* cc;
+  nsresult rv = xpc->GetCurrentNativeCallContext(&cc);
+
+  do {
+    if (NS_FAILED(rv) || !cc) {
+      break;
+    }
+
+    PRUint32 argc;
+    rv = cc->GetArgc(&argc);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (argc < 3) {
+      break;
+    }
+
+    jsval* argv;
+    rv = cc->GetArgvPtr(&argv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    JSContext* cx;
+    rv = cc->GetJSContext(&cx);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    JSAutoRequest ar(cx);
+
+    JSBool asyncBool;
+    JS_ValueToBoolean(cx, argv[2], &asyncBool);
+    async = (PRBool)asyncBool;
+
+    // XXX Remove me once we support sync XHR
+    NS_ENSURE_TRUE(async, NS_ERROR_INVALID_ARG);
+
+    if (argc < 4) {
+      break;
+    }
+
+    JSString* argStr;
+    if (!JSVAL_IS_NULL(argv[3]) && !JSVAL_IS_VOID(argv[3])) {
+      argStr = JS_ValueToString(cx, argv[3]);
+      if (argStr) {
+        user.Assign(nsDependentJSString(argStr));
+      }
+    }
+
+    if (argc < 5) {
+      break;
+    }
+
+    if (!JSVAL_IS_NULL(argv[4]) && !JSVAL_IS_VOID(argv[4])) {
+      argStr = JS_ValueToString(cx, argv[4]);
+      if (argStr) {
+        password.Assign(nsDependentJSString(argStr));
+      }
+    }
+  } while (PR_FALSE);
+
+  return OpenRequest(aMethod, aUrl, async, user, password);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::Send(nsIVariant* aBody)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = mXHRProxy->Send(aBody);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::SendAsBinary(const nsAString& aBody)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = mXHRProxy->SendAsBinary(aBody);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::SetRequestHeader(const nsACString& aHeader,
+                                 const nsACString& aValue)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = mXHRProxy->SetRequestHeader(aHeader, aValue);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::GetReadyState(PRInt32* aReadyState)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  NS_ENSURE_ARG_POINTER(aReadyState);
+
+  nsresult rv = mXHRProxy->GetReadyState(aReadyState);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::OverrideMimeType(const nsACString& aMimetype)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = mXHRProxy->OverrideMimeType(aMimetype);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::GetMultipart(PRBool* aMultipart)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  NS_ENSURE_ARG_POINTER(aMultipart);
+
+  nsresult rv = mXHRProxy->GetMultipart(aMultipart);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::SetMultipart(PRBool aMultipart)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = mXHRProxy->SetMultipart(aMultipart);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::GetMozBackgroundRequest(PRBool* aMozBackgroundRequest)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  NS_ENSURE_ARG_POINTER(aMozBackgroundRequest);
+
+  *aMozBackgroundRequest = PR_FALSE;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::SetMozBackgroundRequest(PRBool aMozBackgroundRequest)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (aMozBackgroundRequest) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::Init(nsIPrincipal* aPrincipal,
+                     nsIScriptContext* aScriptContext,
+                     nsPIDOMWindow* aOwnerWindow)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::GetUpload(nsIXMLHttpRequestUpload** aUpload)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<nsDOMWorkerThread> worker = mWorker;
+  if (!worker) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsAutoLock lock(worker->Lock());
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  NS_ENSURE_ARG_POINTER(aUpload);
+
+  if (!mUpload) {
+    mUpload = new nsDOMWorkerXHRUpload(this);
+    NS_ENSURE_TRUE(mUpload, NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  NS_ADDREF(*aUpload = mUpload);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::GetOnreadystatechange(nsIDOMEventListener** aOnreadystatechange)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  NS_ENSURE_ARG_POINTER(aOnreadystatechange);
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    mXHRProxy->GetOnXListener(LISTENER_TYPE_READYSTATECHANGE, PR_FALSE);
+
+  listener.forget(aOnreadystatechange);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHR::SetOnreadystatechange(nsIDOMEventListener* aOnreadystatechange)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  return mXHRProxy->AddEventListener(LISTENER_TYPE_READYSTATECHANGE,
+                                    aOnreadystatechange, PR_TRUE, PR_FALSE);
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerXHR.h
@@ -0,0 +1,167 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NSDOMWORKERXHR_H__
+#define __NSDOMWORKERXHR_H__
+
+// Bases
+#include "nsIXMLHttpRequest.h"
+#include "nsIClassInfo.h"
+
+// Interfaces
+
+// Other includes
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "prlock.h"
+
+// DOMWorker includes
+#include "nsDOMWorkerThread.h"
+
+class nsDOMWorkerXHRProxy;
+
+class nsDOMWorkerXHREventTarget : public nsIXMLHttpRequestEventTarget
+{
+public:
+  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 nsDOMWorkerXHR;
+
+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 nsIXMLHttpRequest,
+                       public nsIClassInfo
+{
+  friend class nsDOMWorkerXHRProxy;
+  friend class nsDOMWorkerXHRUpload;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIXMLHTTPREQUEST
+  NS_DECL_NSICLASSINFO
+
+  nsDOMWorkerXHR(nsDOMWorkerThread* aWorker);
+
+  nsresult Init();
+
+  void Cancel();
+
+  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 ~nsDOMWorkerXHR();
+
+  PRLock* Lock() {
+    return mWorker->Lock();
+  }
+
+  nsRefPtr<nsDOMWorkerThread> mWorker;
+  nsRefPtr<nsDOMWorkerXHRProxy> mXHRProxy;
+  nsRefPtr<nsDOMWorkerXHRUpload> mUpload;
+
+  volatile PRBool mCanceled;
+};
+
+#endif /* __NSDOMWORKERXHR_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerXHRProxiedFunctions.h
@@ -0,0 +1,248 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NSDOMWORKERXHRPROXIEDFUNCTIONS_H__
+#define __NSDOMWORKERXHRPROXIEDFUNCTIONS_H__
+
+#define MAKE_PROXIED_FUNCTION0(_name) \
+  class _name : public nsRunnable \
+  { \
+  public: \
+    _name (nsDOMWorkerXHRProxy* aXHR) \
+    : mXHR(aXHR) \
+    { \
+      NS_ASSERTION(aXHR, "Null pointer!"); \
+    } \
+  \
+    NS_IMETHOD Run() \
+    { \
+      nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR->GetXMLHttpRequest(); \
+      if (xhr) { \
+        return xhr-> _name (); \
+      } \
+      return NS_OK; \
+    } \
+  private: \
+    nsRefPtr<nsDOMWorkerXHRProxy> mXHR; \
+  }
+
+#define MAKE_PROXIED_FUNCTION1(_name, _arg1) \
+  class _name : public nsRunnable \
+  { \
+  public: \
+     _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1) \
+    : mXHR(aXHR), mArg1(aArg1) \
+    { \
+      NS_ASSERTION(aXHR, "Null pointer!"); \
+    } \
+  \
+    NS_IMETHOD Run() \
+    { \
+      nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR->GetXMLHttpRequest(); \
+      if (xhr) { \
+        return xhr-> _name (mArg1); \
+      } \
+      return NS_OK; \
+    } \
+  private: \
+    nsRefPtr<nsDOMWorkerXHRProxy> mXHR; \
+     _arg1 mArg1; \
+  }
+
+#define MAKE_PROXIED_FUNCTION2(_name, _arg1, _arg2) \
+  class _name : public nsRunnable \
+  { \
+  public: \
+    _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1, _arg2 aArg2) \
+    : mXHR(aXHR), mArg1(aArg1), mArg2(aArg2) \
+    { \
+      NS_ASSERTION(aXHR, "Null pointer!"); \
+    } \
+  \
+    NS_IMETHOD Run() \
+    { \
+      nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR->GetXMLHttpRequest(); \
+      if (xhr) { \
+        return xhr-> _name (mArg1, mArg2); \
+      } \
+      return NS_OK; \
+    } \
+  private: \
+    nsRefPtr<nsDOMWorkerXHRProxy> mXHR; \
+    _arg1 mArg1; \
+    _arg2 mArg2; \
+  }
+
+#define MAKE_PROXIED_FUNCTION3(_name, _arg1, _arg2, _arg3) \
+  class _name : public nsRunnable \
+  { \
+  public: \
+    _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1, _arg2 aArg2, _arg3 aArg3) \
+    : mXHR(aXHR), mArg1(aArg1), mArg2(aArg2), mArg3(aArg3) \
+    { \
+      NS_ASSERTION(aXHR, "Null pointer!"); \
+    } \
+  \
+    NS_IMETHOD Run() \
+    { \
+      nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR->GetXMLHttpRequest(); \
+      if (xhr) { \
+        return xhr-> _name (mArg1, mArg2, mArg3); \
+      } \
+      return NS_OK; \
+    } \
+  private: \
+    nsRefPtr<nsDOMWorkerXHRProxy> mXHR; \
+    _arg1 mArg1; \
+    _arg2 mArg2; \
+    _arg3 mArg3; \
+  }
+
+#define MAKE_PROXIED_FUNCTION4(_name, _arg1, _arg2, _arg3, _arg4) \
+  class _name : public nsRunnable \
+  { \
+  public: \
+    _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1, _arg2 aArg2, _arg3 aArg3, \
+           _arg4 aArg4) \
+    : mXHR(aXHR), mArg1(aArg1), mArg2(aArg2), mArg3(aArg3), mArg4(aArg4) \
+    { \
+      NS_ASSERTION(aXHR, "Null pointer!"); \
+    } \
+  \
+    NS_IMETHOD Run() \
+    { \
+      nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR->GetXMLHttpRequest(); \
+      if (xhr) { \
+        return xhr-> _name (mArg1, mArg2, mArg3, mArg4); \
+      } \
+      return NS_OK; \
+    } \
+  private: \
+    nsRefPtr<nsDOMWorkerXHRProxy> mXHR; \
+    _arg1 mArg1; \
+    _arg2 mArg2; \
+    _arg3 mArg3; \
+    _arg4 mArg4; \
+  }
+
+#define MAKE_PROXIED_FUNCTION5(_name, _arg1, _arg2, _arg3, _arg4, _arg5) \
+  class _name : public nsRunnable \
+  { \
+  public: \
+    _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1, _arg2 aArg2, _arg3 aArg3, \
+           _arg4 aArg4, _arg5 aArg5) \
+    : mXHR(aXHR), mArg1(aArg1), mArg2(aArg2), mArg3(aArg3), mArg4(aArg4), \
+      mArg5(aArg5) \
+    { \
+      NS_ASSERTION(aXHR, "Null pointer!"); \
+    } \
+  \
+    NS_IMETHOD Run() \
+    { \
+      nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR->GetXMLHttpRequest(); \
+      if (xhr) { \
+        return xhr-> _name (mArg1, mArg2, mArg3, mArg4, mArg5); \
+      } \
+      return NS_OK; \
+    } \
+  private: \
+    nsRefPtr<nsDOMWorkerXHRProxy> mXHR; \
+    _arg1 mArg1; \
+    _arg2 mArg2; \
+    _arg3 mArg3; \
+    _arg4 mArg4; \
+    _arg5 mArg5; \
+  }
+
+#define RUN_PROXIED_FUNCTION(_name, _args) \
+  PR_BEGIN_MACRO \
+    if (mCanceled) { \
+      return NS_ERROR_ABORT; \
+    } \
+    \
+    nsCOMPtr<nsIRunnable> method = new :: _name _args; \
+    NS_ENSURE_TRUE(method, NS_ERROR_OUT_OF_MEMORY); \
+    \
+    nsRefPtr<nsResultReturningRunnable> runnable = \
+      new nsResultReturningRunnable(mMainThread, method, mWorkerXHR->mWorker); \
+    NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY); \
+    \
+    nsresult _rv = runnable->Dispatch(); \
+    if (NS_FAILED(_rv)) { \
+      return _rv; \
+    } \
+  PR_END_MACRO
+
+namespace nsDOMWorkerProxiedXHRFunctions
+{
+  class Abort : public nsRunnable
+  {
+  public:
+    Abort (nsDOMWorkerXHRProxy* aXHR)
+    : mXHR(aXHR)
+    {
+      NS_ASSERTION(aXHR, "Null pointer!");
+    }
+
+    NS_IMETHOD Run() {
+      return mXHR->Abort();
+    }
+  private:
+    nsRefPtr<nsDOMWorkerXHRProxy> mXHR;
+  };
+
+  MAKE_PROXIED_FUNCTION1(GetAllResponseHeaders, char**);
+
+  MAKE_PROXIED_FUNCTION2(GetResponseHeader, const nsACString&, nsACString&);
+
+  MAKE_PROXIED_FUNCTION5(OpenRequest, const nsACString&, const nsACString&,
+                         PRBool, const nsAString&, const nsAString&);
+
+  MAKE_PROXIED_FUNCTION1(Send, nsIVariant*);
+
+  MAKE_PROXIED_FUNCTION1(SendAsBinary, const nsAString&);
+
+  MAKE_PROXIED_FUNCTION2(SetRequestHeader, const nsACString&,
+                         const nsACString&);
+
+  MAKE_PROXIED_FUNCTION1(OverrideMimeType, const nsACString&);
+
+  MAKE_PROXIED_FUNCTION1(SetMultipart, PRBool);
+};
+
+#endif /* __NSDOMWORKERXHRPROXIEDFUNCTIONS_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerXHRProxy.cpp
@@ -0,0 +1,1046 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMWorkerXHRProxy.h"
+
+// Interfaces
+#include "nsIThread.h"
+#include "nsIVariant.h"
+#include "nsIXMLHttpRequest.h"
+
+// Other includes
+#include "nsAutoLock.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIClassInfoImpl.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+#include "nsXMLHttpRequest.h"
+#include "prinrval.h"
+#include "prthread.h"
+
+// DOMWorker includes
+#include "nsDOMWorkerPool.h"
+#include "nsDOMWorkerThread.h"
+#include "nsDOMWorkerXHRProxiedFunctions.h"
+
+#define MAX_XHR_LISTENER_TYPE nsDOMWorkerXHREventTarget::sMaxXHREventTypes
+#define MAX_UPLOAD_LISTENER_TYPE nsDOMWorkerXHREventTarget::sMaxUploadEventTypes
+
+using namespace nsDOMWorkerProxiedXHRFunctions;
+
+class nsResultReturningRunnable : public nsRunnable
+{
+public:
+  nsResultReturningRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable,
+                            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;
+    }
+
+    nsIThread* currentThread = NS_GetCurrentThread();
+    NS_ASSERTION(currentThread, "This should never be null!");
+
+    nsresult rv = mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Complicated logic: Spin events while we haven't been canceled (if a
+    // cancel boolean was supplied) and while we're not done.
+    while (!mWorker->IsCanceled() && !mDone) {
+      // Process a single event or yield if no event is pending.
+      if (!NS_ProcessNextEvent(currentThread, PR_FALSE)) {
+        PR_Sleep(PR_INTERVAL_NO_WAIT);
+      }
+    }
+
+    if (mWorker->IsCanceled()) {
+      mResult = NS_ERROR_ABORT;
+    }
+
+    return mResult;
+  }
+
+  NS_IMETHOD Run() {
+#ifdef DEBUG
+    PRBool rightThread = PR_FALSE;
+    mTarget->IsOnCurrentThread(&rightThread);
+    NS_ASSERTION(rightThread, "Run called on the wrong thread!");
+#endif
+
+    mResult = mWorker->IsCanceled() ? NS_ERROR_ABORT : mRunnable->Run();
+    mDone = PR_TRUE;
+
+    return mResult;
+  }
+
+private:
+  nsCOMPtr<nsIEventTarget> mTarget;
+  nsCOMPtr<nsIRunnable> mRunnable;
+  nsRefPtr<nsDOMWorkerThread> mWorker;
+  nsresult mResult;
+  volatile PRBool mDone;
+};
+
+class nsDOMWorkerXHREvent : public nsRunnable,
+                            public nsIDOMEvent,
+                            public nsIClassInfo
+{
+  friend class nsDOMWorkerXHRProxy;
+  friend class nsDOMWorkerXHREventTargetProxy;
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+  NS_DECL_NSIDOMEVENT
+  NS_DECL_NSICLASSINFO
+
+  nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy);
+
+  nsresult Init(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;
+  PRPackedBool mBubbles;
+  PRPackedBool mCancelable;
+  PRPackedBool mUploadEvent;
+};
+
+nsDOMWorkerXHREvent::nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy)
+: mXHRProxy(aXHRProxy),
+  mType(PR_UINT32_MAX),
+  mEventPhase(0),
+  mTimeStamp(0),
+  mStatus(NS_OK),
+  mReadyState(0),
+  mBubbles(PR_FALSE),
+  mCancelable(PR_FALSE),
+  mUploadEvent(PR_FALSE)
+{
+  NS_ASSERTION(aXHRProxy, "Can't be null!");
+
+  nsIDOMEventTarget* target =
+    static_cast<nsIDOMEventTarget*>(aXHRProxy->mWorkerXHR);
+  mTarget = do_QueryInterface(target);
+  NS_ASSERTION(mTarget, "Must support nsIDOMEventTarget!");
+}
+
+NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHREvent, nsRunnable,
+                                                  nsIDOMEvent,
+                                                  nsIClassInfo)
+
+NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerXHREvent, nsIDOMEvent)
+
+NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerXHREvent)
+
+nsresult
+nsDOMWorkerXHREvent::Init(nsIDOMEvent* aEvent)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aEvent, "Don't pass null here!");
+
+  nsresult rv = aEvent->GetType(mTypeString);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mType = nsDOMWorkerXHREventTarget::GetListenerTypeFromString(mTypeString);
+  if (mType >= MAX_XHR_LISTENER_TYPE) {
+    NS_ERROR("Shouldn't get this type of event!");
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsCOMPtr<nsIDOMEventTarget> target;
+  rv = aEvent->GetTarget(getter_AddRefs(target));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIXMLHttpRequestUpload> upload(do_QueryInterface(target));
+  mUploadEvent = !!upload;
+
+  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()
+{
+  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;
+}
+
+class nsDOMWorkerXHRWrappedListener : public nsIDOMEventListener
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsDOMWorkerXHRWrappedListener(nsIDOMEventListener* aInner)
+  : mInner(aInner) {
+    NS_ASSERTION(aInner, "Null pointer!");
+  }
+
+  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) {
+    return mInner->HandleEvent(aEvent);
+  }
+
+  nsIDOMEventListener* Inner() {
+    return mInner;
+  }
+
+private:
+  nsCOMPtr<nsIDOMEventListener> mInner;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerXHRWrappedListener,
+                              nsIDOMEventListener)
+
+nsDOMWorkerXHRProxy::nsDOMWorkerXHRProxy(nsDOMWorkerXHR* aWorkerXHR)
+: mWorkerXHR(aWorkerXHR),
+  mXHR(nsnull),
+  mConcreteXHR(nsnull),
+  mUpload(nsnull),
+  mOwnedByXHR(PR_FALSE),
+  mMultipart(PR_FALSE),
+  mCanceled(PR_FALSE)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(MAX_XHR_LISTENER_TYPE >= MAX_UPLOAD_LISTENER_TYPE,
+               "Upload should support a subset of XHR's event types!");
+}
+
+nsDOMWorkerXHRProxy::~nsDOMWorkerXHRProxy()
+{
+  if (mOwnedByXHR) {
+    mWorkerXHR->Release();
+  }
+  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);
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHRProxy, nsRunnable,
+                                                  nsIDOMEventListener,
+                                                  nsIRequestObserver)
+
+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();
+  if (NS_FAILED(rv)) {
+    // Only warn if we didn't get canceled.
+    NS_WARN_IF_FALSE(rv == NS_ERROR_ABORT, "Dispatch failed!");
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsIXMLHttpRequest*
+nsDOMWorkerXHRProxy::GetXMLHttpRequest()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  return mCanceled ? nsnull : mXHR;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::Destroy()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  mCanceled = PR_TRUE;
+
+  ClearEventListeners();
+  mLastXHREvent = nsnull;
+
+  if (mXHR) {
+    DestroyInternal();
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::InitInternal()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!mXHR, "InitInternal shouldn't be called twice!");
+
+  nsDOMWorkerThread* worker = mWorkerXHR->mWorker;
+  nsRefPtr<nsDOMWorkerPool> pool = worker->Pool();
+
+  if (worker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsIPrincipal* nodePrincipal = pool->ParentDocument()->NodePrincipal();
+  nsIScriptContext* scriptContext = pool->ScriptContext();
+  NS_ASSERTION(nodePrincipal && scriptContext, "Shouldn't be null!");
+
+  nsRefPtr<nsXMLHttpRequest> xhrConcrete = new nsXMLHttpRequest();
+  NS_ENSURE_TRUE(xhrConcrete, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv = xhrConcrete->Init(nodePrincipal, scriptContext, nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Call QI manually here to avoid keeping up with the cast madness of
+  // nsXMLHttpRequest.
+  nsCOMPtr<nsIXMLHttpRequest> xhr =
+    do_QueryInterface(static_cast<nsIXMLHttpRequest*>(xhrConcrete));
+  NS_ENSURE_TRUE(xhr, NS_ERROR_NO_INTERFACE);
+
+  nsCOMPtr<nsIXMLHttpRequestUpload> upload;
+  rv = xhr->GetUpload(getter_AddRefs(upload));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<nsDOMWorkerXHREvent> nullEvent = new nsDOMWorkerXHREvent(this);
+  NS_ENSURE_TRUE(nullEvent, NS_ERROR_OUT_OF_MEMORY);
+
+  rv = nullEvent->Init(xhr);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mLastXHREvent.swap(nullEvent);
+
+  mLastXHREvent->EventHandled();
+
+  xhrConcrete->SetRequestObserver(this);
+
+  // We now own mXHR and it owns upload.
+  xhr.swap(mXHR);
+
+  // Weak refs.
+  mUpload = upload;
+  mConcreteXHR = xhrConcrete;
+
+  return NS_OK;
+}
+
+void
+nsDOMWorkerXHRProxy::DestroyInternal()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<nsDOMWorkerXHRProxy> kungFuDeathGrip;
+
+  if (mOwnedByXHR) {
+    kungFuDeathGrip = this;
+    mXHR->Abort();
+  }
+
+  // mXHR could be null if Init fails.
+  if (mXHR) {
+    mConcreteXHR->SetRequestObserver(nsnull);
+    mXHR->Release();
+    mXHR = nsnull;
+    mUpload = nsnull;
+  }
+}
+
+void
+nsDOMWorkerXHRProxy::FlipOwnership()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // Flip!
+  mOwnedByXHR = !mOwnedByXHR;
+
+  nsCOMPtr<nsIDOMEventTarget> xhrTarget(do_QueryInterface(mXHR));
+  NS_ASSERTION(xhrTarget, "This shouldn't fail!");
+
+  nsCOMPtr<nsIDOMEventTarget> uploadTarget(do_QueryInterface(mUpload));
+  NS_ASSERTION(uploadTarget, "This shouldn't fail!");
+
+  // 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);
+
+  EventListenerFunction function = mOwnedByXHR ?
+                                   &nsIDOMEventTarget::AddEventListener :
+                                   &nsIDOMEventTarget::RemoveEventListener;
+
+  nsAutoString eventName;
+  PRUint32 index = 0;
+
+  for (; index < MAX_UPLOAD_LISTENER_TYPE; index++) {
+    eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
+    (xhrTarget->*function)(eventName, this, PR_FALSE);
+    (uploadTarget->*function)(eventName, this, PR_FALSE);
+  }
+
+  for (; index < MAX_XHR_LISTENER_TYPE; index++) {
+    eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
+    (xhrTarget->*function)(eventName, this, PR_FALSE);
+  }
+
+  if (mOwnedByXHR) {
+    mWorkerXHR->AddRef();
+    mXHR->Release();
+  }
+  else {
+    mXHR->AddRef();
+    mWorkerXHR->Release();
+  }
+}
+
+nsresult
+nsDOMWorkerXHRProxy::AddEventListener(PRUint32 aType,
+                                      nsIDOMEventListener* aListener,
+                                      PRBool aOnXListener,
+                                      PRBool aUploadListener)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  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];
+  WrappedListener& onXListener = aUploadListener ? mUploadOnXListeners[aType] :
+                                                   mXHROnXListeners[aType];
+
+  nsAutoLock lock(mWorkerXHR->Lock());
+
+#ifdef DEBUG
+  if (!aListener) {
+    NS_ASSERTION(aOnXListener, "Shouldn't pass a null listener!");
+  }
+#endif
+
+  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!");
+    }
+
+    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);
+
+  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!");
+
+  mLastXHREvent = aEvent;
+
+  nsresult rv = HandleEventInternal(aEvent->mType, aEvent, aUploadEvent);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+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;
+  }
+
+  rv = HandleEventInternal(type, aEvent, aUploadEvent);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+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!");
+  }
+  else {
+    NS_ASSERTION(aType < MAX_XHR_LISTENER_TYPE, "Bad type!");
+  }
+#endif
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
+                                               mXHRListeners[aType];
+
+  nsAutoTArray<Listener, 10> listenerCopy;
+  PRUint32 count;
+
+  {
+    nsAutoLock lock(mWorkerXHR->Lock());
+
+    count = listeners.Length();
+    if (!count) {
+      return NS_OK;
+    }
+
+    Listener* copied = listenerCopy.AppendElements(listeners);
+    NS_ENSURE_TRUE(copied, NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  for (PRUint32 index = 0; index < count; index++) {
+    NS_ASSERTION(listenerCopy[index], "Null listener?!");
+    listenerCopy[index]->HandleEvent(aEvent);
+  }
+
+  return NS_OK;
+}
+
+void
+nsDOMWorkerXHRProxy::ClearEventListeners()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  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.
+}
+
+// nsIDOMEventListener
+NS_IMETHODIMP
+nsDOMWorkerXHRProxy::HandleEvent(nsIDOMEvent* aEvent)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  NS_ENSURE_ARG_POINTER(aEvent);
+
+  nsRefPtr<nsDOMWorkerXHREvent> newEvent = new nsDOMWorkerXHREvent(this);
+  NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv = newEvent->Init(aEvent);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = nsDOMThreadService::get()->Dispatch(mWorkerXHR->mWorker, newEvent);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::Abort()
+{
+  if (NS_IsMainThread()) {
+    if (mCanceled) {
+      return NS_ERROR_ABORT;
+    }
+
+    nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR;
+    if (mOwnedByXHR) {
+      FlipOwnership();
+    }
+
+    return xhr->Abort();
+  }
+
+  RUN_PROXIED_FUNCTION(Abort, (this));
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::GetAllResponseHeaders(char** _retval)
+{
+  RUN_PROXIED_FUNCTION(GetAllResponseHeaders, (this, _retval));
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::GetResponseHeader(const nsACString& aHeader,
+                                       nsACString& _retval)
+{
+  RUN_PROXIED_FUNCTION(GetResponseHeader, (this, aHeader, _retval));
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::OpenRequest(const nsACString& aMethod,
+                                 const nsACString& aUrl,
+                                 PRBool aAsync,
+                                 const nsAString& aUser,
+                                 const nsAString& aPassword)
+{
+  RUN_PROXIED_FUNCTION(OpenRequest, (this, aMethod, aUrl, aAsync, aUser,
+                                     aPassword));
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::Send(nsIVariant* aBody)
+{
+  RUN_PROXIED_FUNCTION(Send, (this, aBody));
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::SendAsBinary(const nsAString& aBody)
+{
+  RUN_PROXIED_FUNCTION(SendAsBinary, (this, aBody));
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::SetRequestHeader(const nsACString& aHeader,
+                                      const nsACString& aValue)
+{
+  RUN_PROXIED_FUNCTION(SetRequestHeader, (this, aHeader, aValue));
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::OverrideMimeType(const nsACString& aMimetype)
+{
+  RUN_PROXIED_FUNCTION(OverrideMimeType, (this, aMimetype));
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::GetMultipart(PRBool* aMultipart)
+{
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  *aMultipart = mMultipart;
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::SetMultipart(PRBool aMultipart)
+{
+  RUN_PROXIED_FUNCTION(SetMultipart, (this, aMultipart));
+  mMultipart = aMultipart;
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::GetResponseText(nsAString& _retval)
+{
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  _retval.Assign(mLastXHREvent->mResponseText);
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::GetStatusText(nsACString& _retval)
+{
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  _retval.Assign(mLastXHREvent->mStatusText);
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::GetStatus(nsresult* _retval)
+{
+  NS_ASSERTION(_retval, "Null pointer!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  *_retval = mLastXHREvent->mStatus;
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHRProxy::GetReadyState(PRInt32* _retval)
+{
+  NS_ASSERTION(_retval, "Null pointer!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  *_retval = mLastXHREvent->mReadyState;
+  return NS_OK;
+}
+
+// nsIRunnable
+NS_IMETHODIMP
+nsDOMWorkerXHRProxy::Run()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!mXHR, "Run twice?!");
+
+  if (mCanceled) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = InitInternal();
+  if (NS_FAILED(rv)) {
+    NS_WARNING("InitInternal failed!");
+    DestroyInternal();
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+// nsIRequestObserver
+NS_IMETHODIMP
+nsDOMWorkerXHRProxy::OnStartRequest(nsIRequest* /* aRequest */,
+                                    nsISupports* /* aContext */)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_OK;
+  }
+
+  NS_ASSERTION(!mOwnedByXHR, "Inconsistent state!");
+
+  FlipOwnership();
+
+  return NS_OK;
+}
+
+// nsIRequestObserver
+NS_IMETHODIMP
+nsDOMWorkerXHRProxy::OnStopRequest(nsIRequest* /* aRequest */,
+                                   nsISupports* /* aContext */,
+                                   nsresult /* aStatus */)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (mCanceled) {
+    return NS_OK;
+  }
+
+  NS_ASSERTION(mOwnedByXHR, "Inconsistent state!");
+
+  FlipOwnership();
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerXHRProxy.h
@@ -0,0 +1,173 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NSDOMWORKERXHRPROXY_H__
+#define __NSDOMWORKERXHRPROXY_H__
+
+// Bases
+#include "nsThreadUtils.h"
+#include "nsIDOMEventListener.h"
+#include "nsIRequestObserver.h"
+
+// Other includes
+#include "nsCOMPtr.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+
+// DOMWorker includes
+#include "nsDOMWorkerXHR.h"
+
+class nsIJSXMLHttpRequest;
+class nsIThread;
+class nsIVariant;
+class nsIXMLHttpRequest;
+class nsDOMWorkerXHREvent;
+class nsDOMWorkerXHRWrappedListener;
+class nsXMLHttpRequest;
+
+class nsDOMWorkerXHRProxy : public nsRunnable,
+                            public nsIDOMEventListener,
+                            public nsIRequestObserver
+{
+
+  friend class nsDOMWorkerXHREvent;
+  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:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIDOMEVENTLISTENER
+  NS_DECL_NSIRUNNABLE
+  NS_DECL_NSIREQUESTOBSERVER
+
+  nsDOMWorkerXHRProxy(nsDOMWorkerXHR* aWorkerXHR);
+  virtual ~nsDOMWorkerXHRProxy();
+
+  nsresult Init();
+
+  nsIXMLHttpRequest* GetXMLHttpRequest();
+
+  nsresult Abort();
+
+protected:
+  nsresult InitInternal();
+  void DestroyInternal();
+
+  nsresult Destroy();
+
+  void FlipOwnership();
+
+  nsresult AddEventListener(PRUint32 aType,
+                            nsIDOMEventListener* aListener,
+                            PRBool aOnXListener,
+                            PRBool aUploadListener);
+
+  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 OpenRequest(const nsACString& aMethod,
+                       const nsACString& aUrl,
+                       PRBool aAsync,
+                       const nsAString& aUser,
+                       const nsAString& aPassword);
+  nsresult Send(nsIVariant* aBody);
+  nsresult SendAsBinary(const nsAString& aBody);
+  nsresult GetResponseText(nsAString& _retval);
+  nsresult GetStatusText(nsACString& _retval);
+  nsresult GetStatus(nsresult* _retval);
+  nsresult GetReadyState(PRInt32* _retval);
+  nsresult SetRequestHeader(const nsACString& aHeader,
+                            const nsACString& aValue);
+  nsresult OverrideMimeType(const nsACString& aMimetype);
+  nsresult GetMultipart(PRBool* aMultipart);
+  nsresult SetMultipart(PRBool aMultipart);
+
+  // May be weak or strong, check mOwnedByXHR.
+  nsDOMWorkerXHR* mWorkerXHR;
+
+  // May be weak or strong, check mOwnedByXHR.
+  nsIXMLHttpRequest* mXHR;
+
+  // Always weak!
+  nsXMLHttpRequest* mConcreteXHR;
+  nsIXMLHttpRequestUpload* mUpload;
+
+  nsCOMPtr<nsIThread> mMainThread;
+
+  nsRefPtr<nsDOMWorkerXHREvent> mLastXHREvent;
+
+  nsTArray<ListenerArray> mXHRListeners;
+  nsTArray<WrappedListener> mXHROnXListeners;
+
+  nsTArray<ListenerArray> mUploadListeners;
+  nsTArray<WrappedListener> mUploadOnXListeners;
+
+  // Whether or not this object is owned by the real XHR object.
+  PRPackedBool mOwnedByXHR;
+
+  PRPackedBool mMultipart;
+  PRPackedBool mCanceled;
+};
+
+#endif /* __NSDOMWORKERXHRPROXY_H__ */
--- a/dom/src/threads/test/Makefile.in
+++ b/dom/src/threads/test/Makefile.in
@@ -52,12 +52,14 @@ include $(topsrcdir)/config/rules.mk
   importScripts_worker_imported2.js \
   importScripts_worker_imported3.js \
   importScripts_worker_imported4.js \
   test_importScripts.html \
   test_simpleThread.html \
   test_threadErrors.html \
   test_threadTimeouts.html \
   test_longThread.html \
+  test_xhr.html \
+  testXHR.txt \
   $(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/test/testXHR.txt
@@ -0,0 +1,1 @@
+A noisy noise annoys an oyster.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/test/test_xhr.html
@@ -0,0 +1,114 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads XHR(Bug 450452 )
+-->
+<head>
+  <title>Test for DOM Worker Threads XHR (Bug 450452 )</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=450452">DOM Worker Threads XHR (Bug 450452)</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+  function workerScript() {
+    var xhr = new XMLHttpRequest();
+
+    function onload(event) {
+      if (event.target.status != 200) {
+        var message = { type: "error",
+                        error: event.target.status };
+        postMessageToPool(message.toSource());
+      }
+
+      var message = { type: "load",
+                      data: xhr.responseText };
+      postMessageToPool(message.toSource());
+    }
+
+    xhr.onload = onload;
+    xhr.addEventListener("load", onload, false);
+    xhr.removeEventListener("load", onload, false);
+    if (!xhr.onload) {
+      var message = { type: "error",
+                      error: "Lost message listener!" };
+      postMessageToPool(message.toSource());
+    }
+
+    xhr.addEventListener("error", function(event) {
+      var message = { type: "error",
+                      error: event.target.status };
+      postMessageToPool(message.toSource());
+    }, false);
+
+    function onprogress(event) {
+      var message = { type: "progress",
+                      current: event.position,
+                      total: event.totalSize };
+      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": {
+        is(args.data, "A noisy noise annoys an oyster.");
+        document.getElementById("content").textContent = args.data;
+        SimpleTest.finish();
+      } break;
+      default: {
+        ok(false, "Unexpected message");
+        SimpleTest.finish();
+      }
+    }
+  };
+
+  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/toolkit/mozapps/update/test/unit/head_update.js
+++ b/toolkit/mozapps/update/test/unit/head_update.js
@@ -149,31 +149,33 @@ xhr.prototype = {
   get onerror() { return gXHR._onerror; },
   _onload: null,
   set onload(val) { gXHR._onload = val; },
   get onload() { return gXHR._onload; },
   flags: AUS_Ci.nsIClassInfo.SINGLETON,
   implementationLanguage: AUS_Ci.nsIProgrammingLanguage.JAVASCRIPT,
   getHelperForLanguage: function(language) null,
   getInterfaces: function(count) {
-    var interfaces = [AUS_Ci.nsIXMLHttpRequest, AUS_Ci.nsIJSXMLHttpRequest];
+    var interfaces = [AUS_Ci.nsIXMLHttpRequest, AUS_Ci.nsIJSXMLHttpRequest,
+                      AUS_Ci.nsIXMLHttpRequestEventTarget];
     count.value = interfaces.length;
     return interfaces;
   },
   classDescription: "XMLHttpRequest",
   contractID: "@mozilla.org/xmlextras/xmlhttprequest;1",
   classID: Components.ID("{c9b37f43-4278-4304-a5e0-600991ab08cb}"),
   createInstance: function (outer, aIID) {
     if (outer != null)
       throw AUS_Cr.NS_ERROR_NO_AGGREGATION;
     return gXHR.QueryInterface(aIID);
   },
   QueryInterface: function(aIID) {
     if (aIID.equals(AUS_Ci.nsIXMLHttpRequest) ||
         aIID.equals(AUS_Ci.nsIJSXMLHttpRequest) ||
+        aIID.equals(AUS_Ci.nsIXMLHttpRequestEventTarget) ||
         aIID.equals(AUS_Ci.nsIClassInfo) ||
         aIID.equals(AUS_Ci.nsISupports))
       return gXHR;
     throw AUS_Cr.NS_ERROR_NO_INTERFACE;
   }
 };
 
 /**