dom/workers/XMLHttpRequest.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     4  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "XMLHttpRequest.h"
     8 #include "nsIDOMEvent.h"
     9 #include "nsIDOMEventListener.h"
    10 #include "nsIDOMProgressEvent.h"
    11 #include "nsIRunnable.h"
    12 #include "nsIVariant.h"
    13 #include "nsIXMLHttpRequest.h"
    14 #include "nsIXPConnect.h"
    16 #include "jsfriendapi.h"
    17 #include "mozilla/ArrayUtils.h"
    18 #include "mozilla/dom/Exceptions.h"
    19 #include "nsComponentManagerUtils.h"
    20 #include "nsContentUtils.h"
    21 #include "nsCxPusher.h"
    22 #include "nsJSUtils.h"
    23 #include "nsThreadUtils.h"
    25 #include "File.h"
    26 #include "RuntimeService.h"
    27 #include "WorkerPrivate.h"
    28 #include "WorkerRunnable.h"
    29 #include "XMLHttpRequestUpload.h"
    31 using namespace mozilla;
    33 using namespace mozilla::dom;
    34 USING_WORKERS_NAMESPACE
    36 // XXX Need to figure this out...
    37 #define UNCATCHABLE_EXCEPTION NS_ERROR_OUT_OF_MEMORY
    39 /**
    40  *  XMLHttpRequest in workers
    41  *
    42  *  XHR in workers is implemented by proxying calls/events/etc between the
    43  *  worker thread and an nsXMLHttpRequest on the main thread.  The glue
    44  *  object here is the Proxy, which lives on both threads.  All other objects
    45  *  live on either the main thread (the nsXMLHttpRequest) or the worker thread
    46  *  (the worker and XHR private objects).
    47  *
    48  *  The main thread XHR is always operated in async mode, even for sync XHR
    49  *  in workers.  Calls made on the worker thread are proxied to the main thread
    50  *  synchronously (meaning the worker thread is blocked until the call
    51  *  returns).  Each proxied call spins up a sync queue, which captures any
    52  *  synchronously dispatched events and ensures that they run synchronously
    53  *  on the worker as well.  Asynchronously dispatched events are posted to the
    54  *  worker thread to run asynchronously.  Some of the XHR state is mirrored on
    55  *  the worker thread to avoid needing a cross-thread call on every property
    56  *  access.
    57  *
    58  *  The XHR private is stored in the private slot of the XHR JSObject on the
    59  *  worker thread.  It is destroyed when that JSObject is GCd.  The private
    60  *  roots its JSObject while network activity is in progress.  It also
    61  *  adds itself as a feature to the worker to give itself a chance to clean up
    62  *  if the worker goes away during an XHR call.  It is important that the
    63  *  rooting and feature registration (collectively called pinning) happens at
    64  *  the proper times.  If we pin for too long we can cause memory leaks or even
    65  *  shutdown hangs.  If we don't pin for long enough we introduce a GC hazard.
    66  *
    67  *  The XHR is pinned from the time Send is called to roughly the time loadend
    68  *  is received.  There are some complications involved with Abort and XHR
    69  *  reuse.  We maintain a counter on the main thread of how many times Send was
    70  *  called on this XHR, and we decrement the counter every time we receive a
    71  *  loadend event.  When the counter reaches zero we dispatch a runnable to the
    72  *  worker thread to unpin the XHR.  We only decrement the counter if the 
    73  *  dispatch was successful, because the worker may no longer be accepting
    74  *  regular runnables.  In the event that we reach Proxy::Teardown and there
    75  *  the outstanding Send count is still non-zero, we dispatch a control
    76  *  runnable which is guaranteed to run.
    77  *
    78  *  NB: Some of this could probably be simplified now that we have the
    79  *  inner/outer channel ids.
    80  */
    82 BEGIN_WORKERS_NAMESPACE
    84 class Proxy MOZ_FINAL : public nsIDOMEventListener
    85 {
    86 public:
    87   // Read on multiple threads.
    88   WorkerPrivate* mWorkerPrivate;
    89   XMLHttpRequest* mXMLHttpRequestPrivate;
    91   // XHR Params:
    92   bool mMozAnon;
    93   bool mMozSystem;
    95   // Only touched on the main thread.
    96   nsRefPtr<nsXMLHttpRequest> mXHR;
    97   nsCOMPtr<nsIXMLHttpRequestUpload> mXHRUpload;
    98   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
    99   nsCOMPtr<nsIEventTarget> mSyncEventResponseTarget;
   100   uint32_t mInnerEventStreamId;
   101   uint32_t mInnerChannelId;
   102   uint32_t mOutstandingSendCount;
   104   // Only touched on the worker thread.
   105   uint32_t mOuterEventStreamId;
   106   uint32_t mOuterChannelId;
   107   uint64_t mLastLoaded;
   108   uint64_t mLastTotal;
   109   uint64_t mLastUploadLoaded;
   110   uint64_t mLastUploadTotal;
   111   bool mIsSyncXHR;
   112   bool mLastLengthComputable;
   113   bool mLastUploadLengthComputable;
   114   bool mSeenLoadStart;
   115   bool mSeenUploadLoadStart;
   117   // Only touched on the main thread.
   118   bool mUploadEventListenersAttached;
   119   bool mMainThreadSeenLoadStart;
   120   bool mInOpen;
   122 public:
   123   Proxy(XMLHttpRequest* aXHRPrivate, bool aMozAnon, bool aMozSystem)
   124   : mWorkerPrivate(nullptr), mXMLHttpRequestPrivate(aXHRPrivate),
   125     mMozAnon(aMozAnon), mMozSystem(aMozSystem),
   126     mInnerEventStreamId(0), mInnerChannelId(0), mOutstandingSendCount(0),
   127     mOuterEventStreamId(0), mOuterChannelId(0), mLastLoaded(0), mLastTotal(0),
   128     mLastUploadLoaded(0), mLastUploadTotal(0), mIsSyncXHR(false),
   129     mLastLengthComputable(false), mLastUploadLengthComputable(false),
   130     mSeenLoadStart(false), mSeenUploadLoadStart(false),
   131     mUploadEventListenersAttached(false), mMainThreadSeenLoadStart(false),
   132     mInOpen(false)
   133   { }
   135   NS_DECL_THREADSAFE_ISUPPORTS
   136   NS_DECL_NSIDOMEVENTLISTENER
   138   bool
   139   Init();
   141   void
   142   Teardown();
   144   bool
   145   AddRemoveEventListeners(bool aUpload, bool aAdd);
   147   void
   148   Reset()
   149   {
   150     AssertIsOnMainThread();
   152     if (mUploadEventListenersAttached) {
   153       AddRemoveEventListeners(true, false);
   154     }
   155   }
   157   already_AddRefed<nsIEventTarget>
   158   GetEventTarget()
   159   {
   160     AssertIsOnMainThread();
   162     nsCOMPtr<nsIEventTarget> target = mSyncEventResponseTarget ?
   163                                       mSyncEventResponseTarget :
   164                                       mSyncLoopTarget;
   165     return target.forget();
   166   }
   168 private:
   169   ~Proxy()
   170   {
   171     MOZ_ASSERT(!mXHR);
   172     MOZ_ASSERT(!mXHRUpload);
   173     MOZ_ASSERT(!mOutstandingSendCount);
   174   }
   175 };
   177 END_WORKERS_NAMESPACE
   179 namespace {
   181 inline void
   182 ConvertResponseTypeToString(XMLHttpRequestResponseType aType,
   183                             nsString& aString)
   184 {
   185   using namespace
   186     mozilla::dom::XMLHttpRequestResponseTypeValues;
   188   size_t index = static_cast<size_t>(aType);
   189   MOZ_ASSERT(index < ArrayLength(strings), "Codegen gave us a bad value!");
   191   aString.AssignASCII(strings[index].value, strings[index].length);
   192 }
   194 inline XMLHttpRequestResponseType
   195 ConvertStringToResponseType(const nsAString& aString)
   196 {
   197   using namespace
   198     mozilla::dom::XMLHttpRequestResponseTypeValues;
   200   for (size_t index = 0; index < ArrayLength(strings) - 1; index++) {
   201     if (aString.EqualsASCII(strings[index].value, strings[index].length)) {
   202       return static_cast<XMLHttpRequestResponseType>(index);
   203     }
   204   }
   206   MOZ_ASSUME_UNREACHABLE("Don't know anything about this response type!");
   207 }
   209 enum
   210 {
   211   STRING_abort = 0,
   212   STRING_error,
   213   STRING_load,
   214   STRING_loadstart,
   215   STRING_progress,
   216   STRING_timeout,
   217   STRING_readystatechange,
   218   STRING_loadend,
   220   STRING_COUNT,
   222   STRING_LAST_XHR = STRING_loadend,
   223   STRING_LAST_EVENTTARGET = STRING_timeout
   224 };
   226 static_assert(STRING_LAST_XHR >= STRING_LAST_EVENTTARGET, "Bad string setup!");
   227 static_assert(STRING_LAST_XHR == STRING_COUNT - 1, "Bad string setup!");
   229 const char* const sEventStrings[] = {
   230   // nsIXMLHttpRequestEventTarget event types, supported by both XHR and Upload.
   231   "abort",
   232   "error",
   233   "load",
   234   "loadstart",
   235   "progress",
   236   "timeout",
   238   // nsIXMLHttpRequest event types, supported only by XHR.
   239   "readystatechange",
   240   "loadend",
   241 };
   243 static_assert(MOZ_ARRAY_LENGTH(sEventStrings) == STRING_COUNT,
   244               "Bad string count!");
   246 class MainThreadProxyRunnable : public MainThreadWorkerSyncRunnable
   247 {
   248 protected:
   249   nsRefPtr<Proxy> mProxy;
   251   MainThreadProxyRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
   252   : MainThreadWorkerSyncRunnable(aWorkerPrivate, aProxy->GetEventTarget()),
   253     mProxy(aProxy)
   254   {
   255     MOZ_ASSERT(aProxy);
   256   }
   258   virtual ~MainThreadProxyRunnable()
   259   { }
   260 };
   262 class XHRUnpinRunnable MOZ_FINAL : public MainThreadWorkerControlRunnable
   263 {
   264   XMLHttpRequest* mXMLHttpRequestPrivate;
   266 public:
   267   XHRUnpinRunnable(WorkerPrivate* aWorkerPrivate,
   268                    XMLHttpRequest* aXHRPrivate)
   269   : MainThreadWorkerControlRunnable(aWorkerPrivate),
   270     mXMLHttpRequestPrivate(aXHRPrivate)
   271   {
   272     MOZ_ASSERT(aXHRPrivate);
   273   }
   275 private:
   276   ~XHRUnpinRunnable()
   277   { }
   279   bool
   280   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   281   {
   282     mXMLHttpRequestPrivate->Unpin();
   284     return true;
   285   }
   286 };
   288 class AsyncTeardownRunnable MOZ_FINAL : public nsRunnable
   289 {
   290   nsRefPtr<Proxy> mProxy;
   292 public:
   293   AsyncTeardownRunnable(Proxy* aProxy)
   294   : mProxy(aProxy)
   295   {
   296     MOZ_ASSERT(aProxy);
   297   }
   299   NS_DECL_ISUPPORTS_INHERITED
   301 private:
   302   ~AsyncTeardownRunnable()
   303   { }
   305   NS_IMETHOD
   306   Run() MOZ_OVERRIDE
   307   {
   308     AssertIsOnMainThread();
   310     mProxy->Teardown();
   311     mProxy = nullptr;
   313     return NS_OK;
   314   }
   315 };
   317 class LoadStartDetectionRunnable MOZ_FINAL : public nsRunnable,
   318                                              public nsIDOMEventListener
   319 {
   320   WorkerPrivate* mWorkerPrivate;
   321   nsRefPtr<Proxy> mProxy;
   322   nsRefPtr<nsXMLHttpRequest> mXHR;
   323   XMLHttpRequest* mXMLHttpRequestPrivate;
   324   nsString mEventType;
   325   uint32_t mChannelId;
   326   bool mReceivedLoadStart;
   328   class ProxyCompleteRunnable MOZ_FINAL : public MainThreadProxyRunnable
   329   {
   330     XMLHttpRequest* mXMLHttpRequestPrivate;
   331     uint32_t mChannelId;
   333   public:
   334     ProxyCompleteRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   335                           XMLHttpRequest* aXHRPrivate, uint32_t aChannelId)
   336     : MainThreadProxyRunnable(aWorkerPrivate, aProxy),
   337       mXMLHttpRequestPrivate(aXHRPrivate), mChannelId(aChannelId)
   338     { }
   340   private:
   341     ~ProxyCompleteRunnable()
   342     { }
   344     virtual bool
   345     WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   346     {
   347       if (mChannelId != mProxy->mOuterChannelId) {
   348         // Threads raced, this event is now obsolete.
   349         return true;
   350       }
   352       if (mSyncLoopTarget) {
   353         aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, true);
   354       }
   356       mXMLHttpRequestPrivate->Unpin();
   358       return true;
   359     }
   361     NS_IMETHOD
   362     Cancel() MOZ_OVERRIDE
   363     {
   364       // This must run!
   365       nsresult rv = MainThreadProxyRunnable::Cancel();
   366       nsresult rv2 = Run();
   367       return NS_FAILED(rv) ? rv : rv2;
   368     }
   369   };
   371 public:
   372   LoadStartDetectionRunnable(Proxy* aProxy, XMLHttpRequest* aXHRPrivate)
   373   : mWorkerPrivate(aProxy->mWorkerPrivate), mProxy(aProxy), mXHR(aProxy->mXHR),
   374     mXMLHttpRequestPrivate(aXHRPrivate), mChannelId(mProxy->mInnerChannelId),
   375     mReceivedLoadStart(false)
   376   {
   377     AssertIsOnMainThread();
   378     mEventType.AssignWithConversion(sEventStrings[STRING_loadstart]);
   379   }
   381   NS_DECL_ISUPPORTS_INHERITED
   382   NS_DECL_NSIRUNNABLE
   383   NS_DECL_NSIDOMEVENTLISTENER
   385   bool
   386   RegisterAndDispatch()
   387   {
   388     AssertIsOnMainThread();
   390     if (NS_FAILED(mXHR->AddEventListener(mEventType, this, false, false, 2))) {
   391       NS_WARNING("Failed to add event listener!");
   392       return false;
   393     }
   395     return NS_SUCCEEDED(NS_DispatchToCurrentThread(this));
   396   }
   398 private:
   399   ~LoadStartDetectionRunnable()
   400   {
   401     AssertIsOnMainThread();
   402     }
   403 };
   405 class EventRunnable MOZ_FINAL : public MainThreadProxyRunnable
   406 {
   407   nsString mType;
   408   nsString mResponseType;
   409   JSAutoStructuredCloneBuffer mResponseBuffer;
   410   nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
   411   JS::Heap<JS::Value> mResponse;
   412   nsString mResponseText;
   413   nsCString mStatusText;
   414   uint64_t mLoaded;
   415   uint64_t mTotal;
   416   uint32_t mEventStreamId;
   417   uint32_t mStatus;
   418   uint16_t mReadyState;
   419   bool mUploadEvent;
   420   bool mProgressEvent;
   421   bool mLengthComputable;
   422   nsresult mResponseTextResult;
   423   nsresult mStatusResult;
   424   nsresult mResponseResult;
   426 public:
   427   class StateDataAutoRooter : private JS::CustomAutoRooter
   428   {
   429     XMLHttpRequest::StateData* mStateData;
   430     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   432   public:
   433     explicit StateDataAutoRooter(JSContext* aCx, XMLHttpRequest::StateData* aData
   434                                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
   435     : CustomAutoRooter(aCx), mStateData(aData)
   436     {
   437       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
   438     }
   440   private:
   441     virtual void trace(JSTracer* aTrc)
   442     {
   443       JS_CallHeapValueTracer(aTrc, &mStateData->mResponse,
   444                              "XMLHttpRequest::StateData::mResponse");
   445     }
   446   };
   448   EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
   449                 bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal)
   450   : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
   451     mResponse(JSVAL_VOID), mLoaded(aLoaded), mTotal(aTotal),
   452     mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
   453     mUploadEvent(aUploadEvent), mProgressEvent(true),
   454     mLengthComputable(aLengthComputable), mResponseTextResult(NS_OK),
   455     mStatusResult(NS_OK), mResponseResult(NS_OK)
   456   { }
   458   EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType)
   459   : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
   460     mResponse(JSVAL_VOID), mLoaded(0), mTotal(0),
   461     mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
   462     mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
   463     mResponseTextResult(NS_OK), mStatusResult(NS_OK), mResponseResult(NS_OK)
   464   { }
   466 private:
   467   ~EventRunnable()
   468   { }
   470   virtual bool
   471   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE;
   473   virtual bool
   474   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE;
   475 };
   477 class WorkerThreadProxySyncRunnable : public nsRunnable
   478 {
   479 protected:
   480   WorkerPrivate* mWorkerPrivate;
   481   nsRefPtr<Proxy> mProxy;
   482   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   484 private:
   485   class ResponseRunnable MOZ_FINAL: public MainThreadStopSyncLoopRunnable
   486   {
   487     nsRefPtr<Proxy> mProxy;
   488     nsresult mErrorCode;
   490   public:
   491     ResponseRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   492                      nsresult aErrorCode)
   493     : MainThreadStopSyncLoopRunnable(aWorkerPrivate, aProxy->GetEventTarget(),
   494                                      NS_SUCCEEDED(aErrorCode)),
   495       mProxy(aProxy), mErrorCode(aErrorCode)
   496     {
   497       MOZ_ASSERT(aProxy);
   498     }
   500   private:
   501     ~ResponseRunnable()
   502     { }
   504     virtual void
   505     MaybeSetException(JSContext* aCx) MOZ_OVERRIDE
   506     {
   507       MOZ_ASSERT(NS_FAILED(mErrorCode));
   509       Throw(aCx, mErrorCode);
   510     }
   511   };
   513 public:
   514   WorkerThreadProxySyncRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
   515   : mWorkerPrivate(aWorkerPrivate), mProxy(aProxy)
   516   {
   517     MOZ_ASSERT(aWorkerPrivate);
   518     MOZ_ASSERT(aProxy);
   519     aWorkerPrivate->AssertIsOnWorkerThread();
   520   }
   522   NS_DECL_ISUPPORTS_INHERITED
   524   bool
   525   Dispatch(JSContext* aCx)
   526   {
   527     mWorkerPrivate->AssertIsOnWorkerThread();
   529     AutoSyncLoopHolder syncLoop(mWorkerPrivate);
   530     mSyncLoopTarget = syncLoop.EventTarget();
   532     if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
   533       JS_ReportError(aCx, "Failed to dispatch to main thread!");
   534       return false;
   535     }
   537     return syncLoop.Run();
   538   }
   540 protected:
   541   virtual ~WorkerThreadProxySyncRunnable()
   542   { }
   544   virtual nsresult
   545   MainThreadRun() = 0;
   547 private:
   548   NS_DECL_NSIRUNNABLE
   549 };
   551 class SyncTeardownRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   552 {
   553 public:
   554   SyncTeardownRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
   555   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
   556   { }
   558 private:
   559   ~SyncTeardownRunnable()
   560   { }
   562   virtual nsresult
   563   MainThreadRun() MOZ_OVERRIDE
   564   {
   565     mProxy->Teardown();
   566     MOZ_ASSERT(!mProxy->mSyncLoopTarget);
   567     return NS_OK;
   568   }
   569 };
   571 class SetBackgroundRequestRunnable MOZ_FINAL :
   572   public WorkerThreadProxySyncRunnable
   573 {
   574   bool mValue;
   576 public:
   577   SetBackgroundRequestRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   578                                bool aValue)
   579   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue)
   580   { }
   582 private:
   583   ~SetBackgroundRequestRunnable()
   584   { }
   586   virtual nsresult
   587   MainThreadRun() MOZ_OVERRIDE
   588   {
   589     return mProxy->mXHR->SetMozBackgroundRequest(mValue);
   590   }
   591 };
   593 class SetWithCredentialsRunnable MOZ_FINAL :
   594   public WorkerThreadProxySyncRunnable
   595 {
   596   bool mValue;
   598 public:
   599   SetWithCredentialsRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   600                              bool aValue)
   601   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue)
   602   { }
   604 private:
   605   ~SetWithCredentialsRunnable()
   606   { }
   608   virtual nsresult
   609   MainThreadRun() MOZ_OVERRIDE
   610   {
   611     return mProxy->mXHR->SetWithCredentials(mValue);
   612   }
   613 };
   615 class SetResponseTypeRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   616 {
   617   nsString mResponseType;
   619 public:
   620   SetResponseTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   621                           const nsAString& aResponseType)
   622   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
   623     mResponseType(aResponseType)
   624   { }
   626   void
   627   GetResponseType(nsAString& aResponseType)
   628   {
   629     aResponseType.Assign(mResponseType);
   630   }
   632 private:
   633   ~SetResponseTypeRunnable()
   634   { }
   636   virtual nsresult
   637   MainThreadRun() MOZ_OVERRIDE
   638   {
   639     nsresult rv = mProxy->mXHR->SetResponseType(mResponseType);
   640     mResponseType.Truncate();
   641     if (NS_SUCCEEDED(rv)) {
   642       rv = mProxy->mXHR->GetResponseType(mResponseType);
   643     }
   644     return rv;
   645   }
   646 };
   648 class SetTimeoutRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   649 {
   650   uint32_t mTimeout;
   652 public:
   653   SetTimeoutRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   654                      uint32_t aTimeout)
   655   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mTimeout(aTimeout)
   656   { }
   658 private:
   659   ~SetTimeoutRunnable()
   660   { }
   662   virtual nsresult
   663   MainThreadRun() MOZ_OVERRIDE
   664   {
   665     return mProxy->mXHR->SetTimeout(mTimeout);
   666   }
   667 };
   669 class AbortRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   670 {
   671 public:
   672   AbortRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
   673   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
   674   { }
   676 private:
   677   ~AbortRunnable()
   678   { }
   680   virtual nsresult
   681   MainThreadRun() MOZ_OVERRIDE;
   682 };
   684 class GetAllResponseHeadersRunnable MOZ_FINAL :
   685   public WorkerThreadProxySyncRunnable
   686 {
   687   nsCString& mResponseHeaders;
   689 public:
   690   GetAllResponseHeadersRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   691                                 nsCString& aResponseHeaders)
   692   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
   693     mResponseHeaders(aResponseHeaders)
   694   { }
   696 private:
   697   ~GetAllResponseHeadersRunnable()
   698   { }
   700   virtual nsresult
   701   MainThreadRun() MOZ_OVERRIDE
   702   {
   703     mProxy->mXHR->GetAllResponseHeaders(mResponseHeaders);
   704     return NS_OK;
   705   }
   706 };
   708 class GetResponseHeaderRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   709 {
   710   const nsCString mHeader;
   711   nsCString& mValue;
   713 public:
   714   GetResponseHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   715                             const nsACString& aHeader, nsCString& aValue)
   716   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mHeader(aHeader),
   717     mValue(aValue)
   718   { }
   720 private:
   721   ~GetResponseHeaderRunnable()
   722   { }
   724   virtual nsresult
   725   MainThreadRun() MOZ_OVERRIDE
   726   {
   727     return mProxy->mXHR->GetResponseHeader(mHeader, mValue);
   728   }
   729 };
   731 class OpenRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   732 {
   733   nsCString mMethod;
   734   nsString mURL;
   735   Optional<nsAString> mUser;
   736   nsString mUserStr;
   737   Optional<nsAString> mPassword;
   738   nsString mPasswordStr;
   739   bool mBackgroundRequest;
   740   bool mWithCredentials;
   741   uint32_t mTimeout;
   743 public:
   744   OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   745                const nsACString& aMethod, const nsAString& aURL,
   746                const Optional<nsAString>& aUser,
   747                const Optional<nsAString>& aPassword,
   748                bool aBackgroundRequest, bool aWithCredentials,
   749                uint32_t aTimeout)
   750   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mMethod(aMethod),
   751     mURL(aURL), mBackgroundRequest(aBackgroundRequest),
   752     mWithCredentials(aWithCredentials), mTimeout(aTimeout)
   753   {
   754     if (aUser.WasPassed()) {
   755       mUserStr = aUser.Value();
   756       mUser = &mUserStr;
   757     }
   758     if (aPassword.WasPassed()) {
   759       mPasswordStr = aPassword.Value();
   760       mPassword = &mPasswordStr;
   761     }
   762   }
   764 private:
   765   ~OpenRunnable()
   766   { }
   768   virtual nsresult
   769   MainThreadRun() MOZ_OVERRIDE
   770   {
   771     WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
   772     mProxy->mWorkerPrivate = mWorkerPrivate;
   774     nsresult rv = MainThreadRunInternal();
   776     mProxy->mWorkerPrivate = oldWorker;
   777     return rv;
   778   }
   780   nsresult
   781   MainThreadRunInternal();
   782 };
   784 class SendRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   785 {
   786   nsString mStringBody;
   787   JSAutoStructuredCloneBuffer mBody;
   788   nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
   789   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   790   bool mHasUploadListeners;
   792 public:
   793   SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   794                const nsAString& aStringBody, JSAutoStructuredCloneBuffer&& aBody,
   795                nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects,
   796                nsIEventTarget* aSyncLoopTarget, bool aHasUploadListeners)
   797   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
   798   , mStringBody(aStringBody)
   799   , mBody(Move(aBody))
   800   , mSyncLoopTarget(aSyncLoopTarget)
   801   , mHasUploadListeners(aHasUploadListeners)
   802   {
   803     mClonedObjects.SwapElements(aClonedObjects);
   804   }
   806 private:
   807   ~SendRunnable()
   808   { }
   810   virtual nsresult
   811   MainThreadRun() MOZ_OVERRIDE;
   812 };
   814 class SetRequestHeaderRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   815 {
   816   nsCString mHeader;
   817   nsCString mValue;
   819 public:
   820   SetRequestHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   821                            const nsACString& aHeader, const nsACString& aValue)
   822   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mHeader(aHeader),
   823     mValue(aValue)
   824   { }
   826 private:
   827   ~SetRequestHeaderRunnable()
   828   { }
   830   virtual nsresult
   831   MainThreadRun() MOZ_OVERRIDE
   832   {
   833     return mProxy->mXHR->SetRequestHeader(mHeader, mValue);
   834   }
   835 };
   837 class OverrideMimeTypeRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   838 {
   839   nsString mMimeType;
   841 public:
   842   OverrideMimeTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   843                            const nsAString& aMimeType)
   844   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mMimeType(aMimeType)
   845   { }
   847 private:
   848   ~OverrideMimeTypeRunnable()
   849   { }
   851   virtual nsresult
   852   MainThreadRun() MOZ_OVERRIDE
   853   {
   854     mProxy->mXHR->OverrideMimeType(mMimeType);
   855     return NS_OK;
   856   }
   857 };
   859 class AutoUnpinXHR
   860 {
   861   XMLHttpRequest* mXMLHttpRequestPrivate;
   863 public:
   864   AutoUnpinXHR(XMLHttpRequest* aXMLHttpRequestPrivate)
   865   : mXMLHttpRequestPrivate(aXMLHttpRequestPrivate)
   866   {
   867     MOZ_ASSERT(aXMLHttpRequestPrivate);
   868   }
   870   ~AutoUnpinXHR()
   871   {
   872     if (mXMLHttpRequestPrivate) {
   873       mXMLHttpRequestPrivate->Unpin();
   874     }
   875   }
   877   void Clear()
   878   {
   879     mXMLHttpRequestPrivate = nullptr;
   880   }
   881 };
   883 } // anonymous namespace
   885 bool
   886 Proxy::Init()
   887 {
   888   AssertIsOnMainThread();
   889   MOZ_ASSERT(mWorkerPrivate);
   891   if (mXHR) {
   892     return true;
   893   }
   895   nsPIDOMWindow* ownerWindow = mWorkerPrivate->GetWindow();
   896   if (ownerWindow) {
   897     ownerWindow = ownerWindow->GetOuterWindow();
   898     if (!ownerWindow) {
   899       NS_ERROR("No outer window?!");
   900       return false;
   901     }
   903     nsPIDOMWindow* innerWindow = ownerWindow->GetCurrentInnerWindow();
   904     if (mWorkerPrivate->GetWindow() != innerWindow) {
   905       NS_WARNING("Window has navigated, cannot create XHR here.");
   906       return false;
   907     }
   908   }
   910   mXHR = new nsXMLHttpRequest();
   912   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(ownerWindow);
   913   if (NS_FAILED(mXHR->Init(mWorkerPrivate->GetPrincipal(),
   914                            mWorkerPrivate->GetScriptContext(),
   915                            global, mWorkerPrivate->GetBaseURI()))) {
   916     mXHR = nullptr;
   917     return false;
   918   }
   920   mXHR->SetParameters(mMozAnon, mMozSystem);
   922   if (NS_FAILED(mXHR->GetUpload(getter_AddRefs(mXHRUpload)))) {
   923     mXHR = nullptr;
   924     return false;
   925   }
   927   if (!AddRemoveEventListeners(false, true)) {
   928     mXHRUpload = nullptr;
   929     mXHR = nullptr;
   930     return false;
   931   }
   933   return true;
   934 }
   936 void
   937 Proxy::Teardown()
   938 {
   939   AssertIsOnMainThread();
   941   if (mXHR) {
   942     Reset();
   944     // NB: We are intentionally dropping events coming from xhr.abort on the
   945     // floor.
   946     AddRemoveEventListeners(false, false);
   947     mXHR->Abort();
   949     if (mOutstandingSendCount) {
   950       nsRefPtr<XHRUnpinRunnable> runnable =
   951         new XHRUnpinRunnable(mWorkerPrivate, mXMLHttpRequestPrivate);
   952       if (!runnable->Dispatch(nullptr)) {
   953         NS_RUNTIMEABORT("We're going to hang at shutdown anyways.");
   954       }
   956       if (mSyncLoopTarget) {
   957         // We have an unclosed sync loop.  Fix that now.
   958         nsRefPtr<MainThreadStopSyncLoopRunnable> runnable =
   959           new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
   960                                              mSyncLoopTarget.forget(),
   961                                              false);
   962         if (!runnable->Dispatch(nullptr)) {
   963           NS_RUNTIMEABORT("We're going to hang at shutdown anyways.");
   964         }
   965       }
   967       mWorkerPrivate = nullptr;
   968       mOutstandingSendCount = 0;
   969     }
   971     mXHRUpload = nullptr;
   972     mXHR = nullptr;
   973   }
   975   MOZ_ASSERT(!mWorkerPrivate);
   976   MOZ_ASSERT(!mSyncLoopTarget);
   977 }
   979 bool
   980 Proxy::AddRemoveEventListeners(bool aUpload, bool aAdd)
   981 {
   982   AssertIsOnMainThread();
   984   NS_ASSERTION(!aUpload ||
   985                (mUploadEventListenersAttached && !aAdd) ||
   986                (!mUploadEventListenersAttached && aAdd),
   987                "Messed up logic for upload listeners!");
   989   nsCOMPtr<nsIDOMEventTarget> target =
   990     aUpload ?
   991     do_QueryInterface(mXHRUpload) :
   992     do_QueryInterface(static_cast<nsIXMLHttpRequest*>(mXHR.get()));
   993   NS_ASSERTION(target, "This should never fail!");
   995   uint32_t lastEventType = aUpload ? STRING_LAST_EVENTTARGET : STRING_LAST_XHR;
   997   nsAutoString eventType;
   998   for (uint32_t index = 0; index <= lastEventType; index++) {
   999     eventType = NS_ConvertASCIItoUTF16(sEventStrings[index]);
  1000     if (aAdd) {
  1001       if (NS_FAILED(target->AddEventListener(eventType, this, false))) {
  1002         return false;
  1005     else if (NS_FAILED(target->RemoveEventListener(eventType, this, false))) {
  1006       return false;
  1010   if (aUpload) {
  1011     mUploadEventListenersAttached = aAdd;
  1014   return true;
  1017 NS_IMPL_ISUPPORTS(Proxy, nsIDOMEventListener)
  1019 NS_IMETHODIMP
  1020 Proxy::HandleEvent(nsIDOMEvent* aEvent)
  1022   AssertIsOnMainThread();
  1024   if (!mWorkerPrivate || !mXMLHttpRequestPrivate) {
  1025     NS_ERROR("Shouldn't get here!");
  1026     return NS_OK;
  1029   nsString type;
  1030   if (NS_FAILED(aEvent->GetType(type))) {
  1031     NS_WARNING("Failed to get event type!");
  1032     return NS_ERROR_FAILURE;
  1035   nsCOMPtr<nsIDOMEventTarget> target;
  1036   if (NS_FAILED(aEvent->GetTarget(getter_AddRefs(target)))) {
  1037     NS_WARNING("Failed to get target!");
  1038     return NS_ERROR_FAILURE;
  1041   nsCOMPtr<nsIXMLHttpRequestUpload> uploadTarget = do_QueryInterface(target);
  1042   nsCOMPtr<nsIDOMProgressEvent> progressEvent = do_QueryInterface(aEvent);
  1044   nsRefPtr<EventRunnable> runnable;
  1046   if (mInOpen && type.EqualsASCII(sEventStrings[STRING_readystatechange])) {
  1047     uint16_t readyState = 0;
  1048     if (NS_SUCCEEDED(mXHR->GetReadyState(&readyState)) &&
  1049         readyState == nsIXMLHttpRequest::OPENED) {
  1050       mInnerEventStreamId++;
  1054   if (progressEvent) {
  1055     bool lengthComputable;
  1056     uint64_t loaded, total;
  1057     if (NS_FAILED(progressEvent->GetLengthComputable(&lengthComputable)) ||
  1058         NS_FAILED(progressEvent->GetLoaded(&loaded)) ||
  1059         NS_FAILED(progressEvent->GetTotal(&total))) {
  1060       NS_WARNING("Bad progress event!");
  1061       return NS_ERROR_FAILURE;
  1063     runnable = new EventRunnable(this, !!uploadTarget, type, lengthComputable,
  1064                                  loaded, total);
  1066   else {
  1067     runnable = new EventRunnable(this, !!uploadTarget, type);
  1071     AutoSafeJSContext cx;
  1072     JSAutoRequest ar(cx);
  1073     runnable->Dispatch(cx);
  1076   if (!uploadTarget) {
  1077     if (type.EqualsASCII(sEventStrings[STRING_loadstart])) {
  1078       NS_ASSERTION(!mMainThreadSeenLoadStart, "Huh?!");
  1079       mMainThreadSeenLoadStart = true;
  1081     else if (mMainThreadSeenLoadStart &&
  1082              type.EqualsASCII(sEventStrings[STRING_loadend])) {
  1083       mMainThreadSeenLoadStart = false;
  1085       nsRefPtr<LoadStartDetectionRunnable> runnable =
  1086         new LoadStartDetectionRunnable(this, mXMLHttpRequestPrivate);
  1087       if (!runnable->RegisterAndDispatch()) {
  1088         NS_WARNING("Failed to dispatch LoadStartDetectionRunnable!");
  1093   return NS_OK;
  1096 NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadProxySyncRunnable, nsRunnable)
  1098 NS_IMPL_ISUPPORTS_INHERITED0(AsyncTeardownRunnable, nsRunnable)
  1100 NS_IMPL_ISUPPORTS_INHERITED(LoadStartDetectionRunnable, nsRunnable,
  1101                                                         nsIDOMEventListener)
  1103 NS_IMETHODIMP
  1104 LoadStartDetectionRunnable::Run()
  1106   AssertIsOnMainThread();
  1108   if (NS_FAILED(mXHR->RemoveEventListener(mEventType, this, false))) {
  1109     NS_WARNING("Failed to remove event listener!");
  1112   if (!mReceivedLoadStart) {
  1113     if (mProxy->mOutstandingSendCount > 1) {
  1114       mProxy->mOutstandingSendCount--;
  1115     } else if (mProxy->mOutstandingSendCount == 1) {
  1116       mProxy->Reset();
  1118       nsRefPtr<ProxyCompleteRunnable> runnable =
  1119         new ProxyCompleteRunnable(mWorkerPrivate, mProxy,
  1120                                   mXMLHttpRequestPrivate, mChannelId);
  1121       if (runnable->Dispatch(nullptr)) {
  1122         mProxy->mWorkerPrivate = nullptr;
  1123         mProxy->mSyncLoopTarget = nullptr;
  1124         mProxy->mOutstandingSendCount--;
  1129   mProxy = nullptr;
  1130   mXHR = nullptr;
  1131   mXMLHttpRequestPrivate = nullptr;
  1132   return NS_OK;
  1135 NS_IMETHODIMP
  1136 LoadStartDetectionRunnable::HandleEvent(nsIDOMEvent* aEvent)
  1138   AssertIsOnMainThread();
  1140 #ifdef DEBUG
  1142     nsString type;
  1143     if (NS_SUCCEEDED(aEvent->GetType(type))) {
  1144       MOZ_ASSERT(type == mEventType);
  1146     else {
  1147       NS_WARNING("Failed to get event type!");
  1150 #endif
  1152   mReceivedLoadStart = true;
  1153   return NS_OK;
  1156 bool
  1157 EventRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
  1159   AssertIsOnMainThread();
  1161   nsRefPtr<nsXMLHttpRequest>& xhr = mProxy->mXHR;
  1162   MOZ_ASSERT(xhr);
  1164   if (NS_FAILED(xhr->GetResponseType(mResponseType))) {
  1165     MOZ_ASSERT(false, "This should never fail!");
  1168   mResponseTextResult = xhr->GetResponseText(mResponseText);
  1169   if (NS_SUCCEEDED(mResponseTextResult)) {
  1170     mResponseResult = mResponseTextResult;
  1171     if (mResponseText.IsVoid()) {
  1172       mResponse = JSVAL_NULL;
  1175   else {
  1176     JS::Rooted<JS::Value> response(aCx);
  1177     mResponseResult = xhr->GetResponse(aCx, &response);
  1178     if (NS_SUCCEEDED(mResponseResult)) {
  1179       if (JSVAL_IS_UNIVERSAL(response)) {
  1180         mResponse = response;
  1182       else {
  1183         // Anything subject to GC must be cloned.
  1184         JSStructuredCloneCallbacks* callbacks =
  1185           aWorkerPrivate->IsChromeWorker() ?
  1186           ChromeWorkerStructuredCloneCallbacks(true) :
  1187           WorkerStructuredCloneCallbacks(true);
  1189         nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
  1191         if (mResponseBuffer.write(aCx, response, callbacks, &clonedObjects)) {
  1192           mClonedObjects.SwapElements(clonedObjects);
  1194         else {
  1195           NS_WARNING("Failed to clone response!");
  1196           mResponseResult = NS_ERROR_DOM_DATA_CLONE_ERR;
  1202   mStatusResult = xhr->GetStatus(&mStatus);
  1204   xhr->GetStatusText(mStatusText);
  1206   mReadyState = xhr->ReadyState();
  1208   return true;
  1211 bool
  1212 EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
  1214   if (mEventStreamId != mProxy->mOuterEventStreamId) {
  1215     // Threads raced, this event is now obsolete.
  1216     return true;
  1219   if (!mProxy->mXMLHttpRequestPrivate) {
  1220     // Object was finalized, bail.
  1221     return true;
  1224   if (mType.EqualsASCII(sEventStrings[STRING_loadstart])) {
  1225     if (mUploadEvent) {
  1226       mProxy->mSeenUploadLoadStart = true;
  1228     else {
  1229       mProxy->mSeenLoadStart = true;
  1232   else if (mType.EqualsASCII(sEventStrings[STRING_loadend])) {
  1233     if (mUploadEvent) {
  1234       mProxy->mSeenUploadLoadStart = false;
  1236     else {
  1237       mProxy->mSeenLoadStart = false;
  1240   else if (mType.EqualsASCII(sEventStrings[STRING_abort])) {
  1241     if ((mUploadEvent && !mProxy->mSeenUploadLoadStart) ||
  1242         (!mUploadEvent && !mProxy->mSeenLoadStart)) {
  1243       // We've already dispatched premature abort events.
  1244       return true;
  1247   else if (mType.EqualsASCII(sEventStrings[STRING_readystatechange])) {
  1248     if (mReadyState == 4 && !mUploadEvent && !mProxy->mSeenLoadStart) {
  1249       // We've already dispatched premature abort events.
  1250       return true;
  1254   if (mProgressEvent) {
  1255     // Cache these for premature abort events.
  1256     if (mUploadEvent) {
  1257       mProxy->mLastUploadLengthComputable = mLengthComputable;
  1258       mProxy->mLastUploadLoaded = mLoaded;
  1259       mProxy->mLastUploadTotal = mTotal;
  1261     else {
  1262       mProxy->mLastLengthComputable = mLengthComputable;
  1263       mProxy->mLastLoaded = mLoaded;
  1264       mProxy->mLastTotal = mTotal;
  1268   nsAutoPtr<XMLHttpRequest::StateData> state(new XMLHttpRequest::StateData());
  1269   StateDataAutoRooter rooter(aCx, state);
  1271   state->mResponseTextResult = mResponseTextResult;
  1272   state->mResponseText = mResponseText;
  1274   if (NS_SUCCEEDED(mResponseTextResult)) {
  1275     MOZ_ASSERT(JSVAL_IS_VOID(mResponse) || JSVAL_IS_NULL(mResponse));
  1276     state->mResponseResult = mResponseTextResult;
  1277     state->mResponse = mResponse;
  1279   else {
  1280     state->mResponseResult = mResponseResult;
  1282     if (NS_SUCCEEDED(mResponseResult)) {
  1283       if (mResponseBuffer.data()) {
  1284         MOZ_ASSERT(JSVAL_IS_VOID(mResponse));
  1286         JSAutoStructuredCloneBuffer responseBuffer(Move(mResponseBuffer));
  1288         JSStructuredCloneCallbacks* callbacks =
  1289           aWorkerPrivate->IsChromeWorker() ?
  1290           ChromeWorkerStructuredCloneCallbacks(false) :
  1291           WorkerStructuredCloneCallbacks(false);
  1293         nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
  1294         clonedObjects.SwapElements(mClonedObjects);
  1296         JS::Rooted<JS::Value> response(aCx);
  1297         if (!responseBuffer.read(aCx, &response, callbacks, &clonedObjects)) {
  1298           return false;
  1301         state->mResponse = response;
  1303       else {
  1304         state->mResponse = mResponse;
  1309   state->mStatusResult = mStatusResult;
  1310   state->mStatus = mStatus;
  1312   state->mStatusText = mStatusText;
  1314   state->mReadyState = mReadyState;
  1316   XMLHttpRequest* xhr = mProxy->mXMLHttpRequestPrivate;
  1317   xhr->UpdateState(*state);
  1319   if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) {
  1320     return true;
  1323   JS::Rooted<JSString*> type(aCx,
  1324     JS_NewUCStringCopyN(aCx, mType.get(), mType.Length()));
  1325   if (!type) {
  1326     return false;
  1329   nsXHREventTarget* target;
  1330   if (mUploadEvent) {
  1331     target = xhr->GetUploadObjectNoCreate();
  1333   else {
  1334     target = xhr;
  1337   MOZ_ASSERT(target);
  1339   nsCOMPtr<nsIDOMEvent> event;
  1340   if (mProgressEvent) {
  1341     NS_NewDOMProgressEvent(getter_AddRefs(event), target, nullptr, nullptr);
  1342     nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
  1344     if (progress) {
  1345       progress->InitProgressEvent(mType, false, false, mLengthComputable,
  1346                                   mLoaded, mTotal);
  1349   else {
  1350     NS_NewDOMEvent(getter_AddRefs(event), target, nullptr, nullptr);
  1352     if (event) {
  1353       event->InitEvent(mType, false, false);
  1357   if (!event) {
  1358     return false;
  1361   event->SetTrusted(true);
  1363   target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
  1365   // After firing the event set mResponse to JSVAL_NULL for chunked response
  1366   // types.
  1367   if (StringBeginsWith(mResponseType, NS_LITERAL_STRING("moz-chunked-"))) {
  1368     xhr->NullResponseText();
  1371   return true;
  1374 NS_IMETHODIMP
  1375 WorkerThreadProxySyncRunnable::Run()
  1377   AssertIsOnMainThread();
  1379   nsCOMPtr<nsIEventTarget> tempTarget;
  1380   mSyncLoopTarget.swap(tempTarget);
  1382   mProxy->mSyncEventResponseTarget.swap(tempTarget);
  1384   nsresult rv = MainThreadRun();
  1386   nsRefPtr<ResponseRunnable> response =
  1387     new ResponseRunnable(mWorkerPrivate, mProxy, rv);
  1388   if (!response->Dispatch(nullptr)) {
  1389     MOZ_ASSERT(false, "Failed to dispatch response!");
  1392   mProxy->mSyncEventResponseTarget.swap(tempTarget);
  1394   return NS_OK;
  1397 nsresult
  1398 AbortRunnable::MainThreadRun()
  1400   mProxy->mInnerEventStreamId++;
  1402   WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
  1403   mProxy->mWorkerPrivate = mWorkerPrivate;
  1405   mProxy->mXHR->Abort();
  1407   mProxy->mWorkerPrivate = oldWorker;
  1409   mProxy->Reset();
  1411   return NS_OK;
  1414 nsresult
  1415 OpenRunnable::MainThreadRunInternal()
  1417   if (!mProxy->Init()) {
  1418     return NS_ERROR_DOM_INVALID_STATE_ERR;
  1421   nsresult rv;
  1423   if (mBackgroundRequest) {
  1424     rv = mProxy->mXHR->SetMozBackgroundRequest(mBackgroundRequest);
  1425     NS_ENSURE_SUCCESS(rv, rv);
  1428   if (mWithCredentials) {
  1429     rv = mProxy->mXHR->SetWithCredentials(mWithCredentials);
  1430     NS_ENSURE_SUCCESS(rv, rv);
  1433   if (mTimeout) {
  1434     rv = mProxy->mXHR->SetTimeout(mTimeout);
  1435     NS_ENSURE_SUCCESS(rv, rv);
  1438   MOZ_ASSERT(!mProxy->mInOpen);
  1439   mProxy->mInOpen = true;
  1441   ErrorResult rv2;
  1442   mProxy->mXHR->Open(mMethod, mURL, true, mUser, mPassword, rv2);
  1444   MOZ_ASSERT(mProxy->mInOpen);
  1445   mProxy->mInOpen = false;
  1447   if (rv2.Failed()) {
  1448     return rv2.ErrorCode();
  1451   return mProxy->mXHR->SetResponseType(NS_LITERAL_STRING("text"));
  1455 nsresult
  1456 SendRunnable::MainThreadRun()
  1458   nsCOMPtr<nsIVariant> variant;
  1460   if (mBody.data()) {
  1461     AutoSafeJSContext cx;
  1462     JSAutoRequest ar(cx);
  1464     nsIXPConnect* xpc = nsContentUtils::XPConnect();
  1465     MOZ_ASSERT(xpc);
  1467     nsresult rv = NS_OK;
  1469     JSStructuredCloneCallbacks* callbacks =
  1470       mWorkerPrivate->IsChromeWorker() ?
  1471       ChromeWorkerStructuredCloneCallbacks(true) :
  1472       WorkerStructuredCloneCallbacks(true);
  1474     JS::Rooted<JS::Value> body(cx);
  1475     if (mBody.read(cx, &body, callbacks, &mClonedObjects)) {
  1476       if (NS_FAILED(xpc->JSValToVariant(cx, body, getter_AddRefs(variant)))) {
  1477         rv = NS_ERROR_DOM_INVALID_STATE_ERR;
  1480     else {
  1481       rv = NS_ERROR_DOM_DATA_CLONE_ERR;
  1484     mBody.clear();
  1485     mClonedObjects.Clear();
  1487     NS_ENSURE_SUCCESS(rv, rv);
  1489   else {
  1490     nsCOMPtr<nsIWritableVariant> wvariant =
  1491       do_CreateInstance(NS_VARIANT_CONTRACTID);
  1492     NS_ENSURE_TRUE(wvariant, NS_ERROR_UNEXPECTED);
  1494     if (NS_FAILED(wvariant->SetAsAString(mStringBody))) {
  1495       MOZ_ASSERT(false, "This should never fail!");
  1498     variant = wvariant;
  1501   MOZ_ASSERT(!mProxy->mWorkerPrivate);
  1502   mProxy->mWorkerPrivate = mWorkerPrivate;
  1504   MOZ_ASSERT(!mProxy->mSyncLoopTarget);
  1505   mProxy->mSyncLoopTarget.swap(mSyncLoopTarget);
  1507   if (mHasUploadListeners) {
  1508     NS_ASSERTION(!mProxy->mUploadEventListenersAttached, "Huh?!");
  1509     if (!mProxy->AddRemoveEventListeners(true, true)) {
  1510       MOZ_ASSERT(false, "This should never fail!");
  1514   mProxy->mInnerChannelId++;
  1516   nsresult rv = mProxy->mXHR->Send(variant);
  1518   if (NS_SUCCEEDED(rv)) {
  1519     mProxy->mOutstandingSendCount++;
  1521     if (!mHasUploadListeners) {
  1522       NS_ASSERTION(!mProxy->mUploadEventListenersAttached, "Huh?!");
  1523       if (!mProxy->AddRemoveEventListeners(true, true)) {
  1524         MOZ_ASSERT(false, "This should never fail!");
  1529   return rv;
  1532 XMLHttpRequest::XMLHttpRequest(WorkerPrivate* aWorkerPrivate)
  1533 : mWorkerPrivate(aWorkerPrivate),
  1534   mResponseType(XMLHttpRequestResponseType::Text), mTimeout(0),
  1535   mRooted(false), mBackgroundRequest(false), mWithCredentials(false),
  1536   mCanceled(false), mMozAnon(false), mMozSystem(false)
  1538   mWorkerPrivate->AssertIsOnWorkerThread();
  1540   SetIsDOMBinding();
  1542   mozilla::HoldJSObjects(this);
  1545 XMLHttpRequest::~XMLHttpRequest()
  1547   mWorkerPrivate->AssertIsOnWorkerThread();
  1549   ReleaseProxy(XHRIsGoingAway);
  1551   MOZ_ASSERT(!mRooted);
  1553   mozilla::DropJSObjects(this);
  1556 NS_IMPL_ADDREF_INHERITED(XMLHttpRequest, nsXHREventTarget)
  1557 NS_IMPL_RELEASE_INHERITED(XMLHttpRequest, nsXHREventTarget)
  1559 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XMLHttpRequest)
  1560 NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
  1562 NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequest)
  1564 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequest,
  1565                                                   nsXHREventTarget)
  1566   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
  1567 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  1569 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequest,
  1570                                                 nsXHREventTarget)
  1571   tmp->ReleaseProxy(XHRIsGoingAway);
  1572   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
  1573   tmp->mStateData.mResponse.setUndefined();
  1574 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  1576 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequest,
  1577                                                nsXHREventTarget)
  1578   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mStateData.mResponse)
  1579 NS_IMPL_CYCLE_COLLECTION_TRACE_END
  1581 JSObject*
  1582 XMLHttpRequest::WrapObject(JSContext* aCx)
  1584   return XMLHttpRequestBinding_workers::Wrap(aCx, this);
  1587 // static
  1588 already_AddRefed<XMLHttpRequest>
  1589 XMLHttpRequest::Constructor(const GlobalObject& aGlobal,
  1590                             const MozXMLHttpRequestParameters& aParams,
  1591                             ErrorResult& aRv)
  1593   JSContext* cx = aGlobal.GetContext();
  1594   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
  1595   MOZ_ASSERT(workerPrivate);
  1597   nsRefPtr<XMLHttpRequest> xhr = new XMLHttpRequest(workerPrivate);
  1599   if (workerPrivate->XHRParamsAllowed()) {
  1600     if (aParams.mMozSystem)
  1601       xhr->mMozAnon = true;
  1602     else
  1603       xhr->mMozAnon = aParams.mMozAnon;
  1604     xhr->mMozSystem = aParams.mMozSystem;
  1607   return xhr.forget();
  1610 void
  1611 XMLHttpRequest::ReleaseProxy(ReleaseType aType)
  1613   // Can't assert that we're on the worker thread here because mWorkerPrivate
  1614   // may be gone.
  1616   if (mProxy) {
  1617     if (aType == XHRIsGoingAway) {
  1618       // We're in a GC finalizer, so we can't do a sync call here (and we don't
  1619       // need to).
  1620       nsRefPtr<AsyncTeardownRunnable> runnable =
  1621         new AsyncTeardownRunnable(mProxy);
  1622       mProxy = nullptr;
  1624       if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
  1625         NS_ERROR("Failed to dispatch teardown runnable!");
  1627     } else {
  1628       // This isn't necessary if the worker is going away or the XHR is going
  1629       // away.
  1630       if (aType == Default) {
  1631         // Don't let any more events run.
  1632         mProxy->mOuterEventStreamId++;
  1635       // We need to make a sync call here.
  1636       nsRefPtr<SyncTeardownRunnable> runnable =
  1637         new SyncTeardownRunnable(mWorkerPrivate, mProxy);
  1638       mProxy = nullptr;
  1640       if (!runnable->Dispatch(nullptr)) {
  1641         NS_ERROR("Failed to dispatch teardown runnable!");
  1647 void
  1648 XMLHttpRequest::MaybePin(ErrorResult& aRv)
  1650   mWorkerPrivate->AssertIsOnWorkerThread();
  1652   if (mRooted) {
  1653     return;
  1656   JSContext* cx = GetCurrentThreadJSContext();
  1658   if (!mWorkerPrivate->AddFeature(cx, this)) {
  1659     aRv.Throw(NS_ERROR_FAILURE);
  1660     return;
  1663   NS_ADDREF_THIS();
  1665   mRooted = true;
  1668 void
  1669 XMLHttpRequest::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv)
  1671   mWorkerPrivate->AssertIsOnWorkerThread();
  1672   MOZ_ASSERT(mProxy);
  1674   mStateData.mReadyState = 4;
  1676   if (mProxy->mSeenUploadLoadStart) {
  1677     MOZ_ASSERT(mUpload);
  1679     DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("abort"), true,
  1680                                 aRv);
  1681     if (aRv.Failed()) {
  1682       return;
  1685     DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("loadend"), true,
  1686                                 aRv);
  1687     if (aRv.Failed()) {
  1688       return;
  1691     mProxy->mSeenUploadLoadStart = false;
  1694   if (mProxy->mSeenLoadStart) {
  1695     DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("readystatechange"),
  1696                                 false, aRv);
  1697     if (aRv.Failed()) {
  1698       return;
  1701     DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("abort"), false, aRv);
  1702     if (aRv.Failed()) {
  1703       return;
  1706     DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("loadend"), false,
  1707                                 aRv);
  1708     if (aRv.Failed()) {
  1709       return;
  1712     mProxy->mSeenLoadStart = false;
  1716 void
  1717 XMLHttpRequest::DispatchPrematureAbortEvent(EventTarget* aTarget,
  1718                                             const nsAString& aEventType,
  1719                                             bool aUploadTarget,
  1720                                             ErrorResult& aRv)
  1722   mWorkerPrivate->AssertIsOnWorkerThread();
  1723   MOZ_ASSERT(aTarget);
  1725   if (!mProxy) {
  1726     aRv.Throw(NS_ERROR_FAILURE);
  1727     return;
  1730   nsCOMPtr<nsIDOMEvent> event;
  1731   if (aEventType.EqualsLiteral("readystatechange")) {
  1732     NS_NewDOMEvent(getter_AddRefs(event), aTarget, nullptr, nullptr);
  1734     if (event) {
  1735       event->InitEvent(aEventType, false, false);
  1738   else {
  1739     NS_NewDOMProgressEvent(getter_AddRefs(event), aTarget, nullptr, nullptr);
  1741     nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
  1742     if (progress) {
  1743       if (aUploadTarget) {
  1744         progress->InitProgressEvent(aEventType, false, false,
  1745                                     mProxy->mLastUploadLengthComputable,
  1746                                     mProxy->mLastUploadLoaded,
  1747                                     mProxy->mLastUploadTotal);
  1749       else {
  1750         progress->InitProgressEvent(aEventType, false, false,
  1751                                     mProxy->mLastLengthComputable,
  1752                                     mProxy->mLastLoaded,
  1753                                     mProxy->mLastTotal);
  1758   if (!event) {
  1759     aRv.Throw(NS_ERROR_FAILURE);
  1760     return;
  1763   event->SetTrusted(true);
  1765   aTarget->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
  1768 void
  1769 XMLHttpRequest::Unpin()
  1771   mWorkerPrivate->AssertIsOnWorkerThread();
  1773   MOZ_ASSERT(mRooted, "Mismatched calls to Unpin!");
  1775   JSContext* cx = GetCurrentThreadJSContext();
  1777   mWorkerPrivate->RemoveFeature(cx, this);
  1779   mRooted = false;
  1781   NS_RELEASE_THIS();
  1784 void
  1785 XMLHttpRequest::SendInternal(const nsAString& aStringBody,
  1786                              JSAutoStructuredCloneBuffer&& aBody,
  1787                              nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
  1788                              ErrorResult& aRv)
  1790   mWorkerPrivate->AssertIsOnWorkerThread();
  1792   bool hasUploadListeners = mUpload ? mUpload->HasListeners() : false;
  1794   MaybePin(aRv);
  1795   if (aRv.Failed()) {
  1796     return;
  1799   AutoUnpinXHR autoUnpin(this);
  1800   Maybe<AutoSyncLoopHolder> autoSyncLoop;
  1802   nsCOMPtr<nsIEventTarget> syncLoopTarget;
  1803   bool isSyncXHR = mProxy->mIsSyncXHR;
  1804   if (isSyncXHR) {
  1805     autoSyncLoop.construct(mWorkerPrivate);
  1806     syncLoopTarget = autoSyncLoop.ref().EventTarget();
  1809   mProxy->mOuterChannelId++;
  1811   JSContext* cx = mWorkerPrivate->GetJSContext();
  1813   nsRefPtr<SendRunnable> runnable =
  1814     new SendRunnable(mWorkerPrivate, mProxy, aStringBody, Move(aBody),
  1815                      aClonedObjects, syncLoopTarget, hasUploadListeners);
  1816   if (!runnable->Dispatch(cx)) {
  1817     aRv.Throw(NS_ERROR_FAILURE);
  1818     return;
  1821   if (!isSyncXHR)  {
  1822     autoUnpin.Clear();
  1823     MOZ_ASSERT(autoSyncLoop.empty());
  1824     return;
  1827   autoUnpin.Clear();
  1829   if (!autoSyncLoop.ref().Run()) {
  1830     aRv.Throw(NS_ERROR_FAILURE);
  1834 bool
  1835 XMLHttpRequest::Notify(JSContext* aCx, Status aStatus)
  1837   mWorkerPrivate->AssertIsOnWorkerThread();
  1838   MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
  1840   if (aStatus >= Canceling && !mCanceled) {
  1841     mCanceled = true;
  1842     ReleaseProxy(WorkerIsGoingAway);
  1845   return true;
  1848 void
  1849 XMLHttpRequest::Open(const nsACString& aMethod, const nsAString& aUrl,
  1850                      bool aAsync, const Optional<nsAString>& aUser,
  1851                      const Optional<nsAString>& aPassword, ErrorResult& aRv)
  1853   mWorkerPrivate->AssertIsOnWorkerThread();
  1855   if (mCanceled) {
  1856     aRv.Throw(UNCATCHABLE_EXCEPTION);
  1857     return;
  1860   if (mProxy) {
  1861     MaybeDispatchPrematureAbortEvents(aRv);
  1862     if (aRv.Failed()) {
  1863       return;
  1866   else {
  1867     mProxy = new Proxy(this, mMozAnon, mMozSystem);
  1870   mProxy->mOuterEventStreamId++;
  1872   nsRefPtr<OpenRunnable> runnable =
  1873     new OpenRunnable(mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
  1874                      mBackgroundRequest, mWithCredentials,
  1875                      mTimeout);
  1877   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1878     ReleaseProxy();
  1879     aRv.Throw(NS_ERROR_FAILURE);
  1880     return;
  1883   mProxy->mIsSyncXHR = !aAsync;
  1886 void
  1887 XMLHttpRequest::SetRequestHeader(const nsACString& aHeader,
  1888                                  const nsACString& aValue, ErrorResult& aRv)
  1890   mWorkerPrivate->AssertIsOnWorkerThread();
  1892   if (mCanceled) {
  1893     aRv.Throw(UNCATCHABLE_EXCEPTION);
  1894     return;
  1897   if (!mProxy) {
  1898     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1899     return;
  1902   nsRefPtr<SetRequestHeaderRunnable> runnable =
  1903     new SetRequestHeaderRunnable(mWorkerPrivate, mProxy, aHeader, aValue);
  1904   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1905     aRv.Throw(NS_ERROR_FAILURE);
  1906     return;
  1910 void
  1911 XMLHttpRequest::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
  1913   mWorkerPrivate->AssertIsOnWorkerThread();
  1915   if (mCanceled) {
  1916     aRv.Throw(UNCATCHABLE_EXCEPTION);
  1917     return;
  1920   mTimeout = aTimeout;
  1922   if (!mProxy) {
  1923     // Open may not have been called yet, in which case we'll handle the
  1924     // timeout in OpenRunnable.
  1925     return;
  1928   nsRefPtr<SetTimeoutRunnable> runnable =
  1929     new SetTimeoutRunnable(mWorkerPrivate, mProxy, aTimeout);
  1930   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1931     aRv.Throw(NS_ERROR_FAILURE);
  1932     return;
  1936 void
  1937 XMLHttpRequest::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
  1939   mWorkerPrivate->AssertIsOnWorkerThread();
  1941   if (mCanceled) {
  1942     aRv.Throw(UNCATCHABLE_EXCEPTION);
  1943     return;
  1946   mWithCredentials = aWithCredentials;
  1948   if (!mProxy) {
  1949     // Open may not have been called yet, in which case we'll handle the
  1950     // credentials in OpenRunnable.
  1951     return;
  1954   nsRefPtr<SetWithCredentialsRunnable> runnable =
  1955     new SetWithCredentialsRunnable(mWorkerPrivate, mProxy, aWithCredentials);
  1956   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1957     aRv.Throw(NS_ERROR_FAILURE);
  1958     return;
  1962 void
  1963 XMLHttpRequest::SetMozBackgroundRequest(bool aBackgroundRequest,
  1964                                         ErrorResult& aRv)
  1966   mWorkerPrivate->AssertIsOnWorkerThread();
  1968   if (mCanceled) {
  1969     aRv.Throw(UNCATCHABLE_EXCEPTION);
  1970     return;
  1973   mBackgroundRequest = aBackgroundRequest;
  1975   if (!mProxy) {
  1976     // Open may not have been called yet, in which case we'll handle the
  1977     // background request in OpenRunnable.
  1978     return;
  1981   nsRefPtr<SetBackgroundRequestRunnable> runnable =
  1982     new SetBackgroundRequestRunnable(mWorkerPrivate, mProxy,
  1983                                      aBackgroundRequest);
  1984   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1985     aRv.Throw(NS_ERROR_FAILURE);
  1986     return;
  1990 XMLHttpRequestUpload*
  1991 XMLHttpRequest::GetUpload(ErrorResult& aRv)
  1993   mWorkerPrivate->AssertIsOnWorkerThread();
  1995   if (mCanceled) {
  1996     aRv.Throw(UNCATCHABLE_EXCEPTION);
  1997     return nullptr;
  2000   if (!mUpload) {
  2001     mUpload = XMLHttpRequestUpload::Create(this);
  2003     if (!mUpload) {
  2004       aRv.Throw(NS_ERROR_FAILURE);
  2005       return nullptr;
  2009   return mUpload;
  2012 void
  2013 XMLHttpRequest::Send(ErrorResult& aRv)
  2015   mWorkerPrivate->AssertIsOnWorkerThread();
  2017   if (mCanceled) {
  2018     aRv.Throw(UNCATCHABLE_EXCEPTION);
  2019     return;
  2022   if (!mProxy) {
  2023     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  2024     return;
  2027   // Nothing to clone.
  2028   JSAutoStructuredCloneBuffer buffer;
  2029   nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
  2031   SendInternal(NullString(), Move(buffer), clonedObjects, aRv);
  2034 void
  2035 XMLHttpRequest::Send(const nsAString& aBody, ErrorResult& aRv)
  2037   mWorkerPrivate->AssertIsOnWorkerThread();
  2039   if (mCanceled) {
  2040     aRv.Throw(UNCATCHABLE_EXCEPTION);
  2041     return;
  2044   if (!mProxy) {
  2045     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  2046     return;
  2049   // Nothing to clone.
  2050   JSAutoStructuredCloneBuffer buffer;
  2051   nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
  2053   SendInternal(aBody, Move(buffer), clonedObjects, aRv);
  2056 void
  2057 XMLHttpRequest::Send(JS::Handle<JSObject*> aBody, ErrorResult& aRv)
  2059   JSContext* cx = mWorkerPrivate->GetJSContext();
  2061   MOZ_ASSERT(aBody);
  2063   mWorkerPrivate->AssertIsOnWorkerThread();
  2065   if (mCanceled) {
  2066     aRv.Throw(UNCATCHABLE_EXCEPTION);
  2067     return;
  2070   if (!mProxy) {
  2071     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  2072     return;
  2075   JS::Rooted<JS::Value> valToClone(cx);
  2076   if (JS_IsArrayBufferObject(aBody) || JS_IsArrayBufferViewObject(aBody) ||
  2077       file::GetDOMBlobFromJSObject(aBody)) {
  2078     valToClone.setObject(*aBody);
  2080   else {
  2081     JS::Rooted<JS::Value> obj(cx, JS::ObjectValue(*aBody));
  2082     JSString* bodyStr = JS::ToString(cx, obj);
  2083     if (!bodyStr) {
  2084       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
  2085       return;
  2087     valToClone.setString(bodyStr);
  2090   JSStructuredCloneCallbacks* callbacks =
  2091     mWorkerPrivate->IsChromeWorker() ?
  2092     ChromeWorkerStructuredCloneCallbacks(false) :
  2093     WorkerStructuredCloneCallbacks(false);
  2095   nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
  2097   JSAutoStructuredCloneBuffer buffer;
  2098   if (!buffer.write(cx, valToClone, callbacks, &clonedObjects)) {
  2099     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
  2100     return;
  2103   SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
  2106 void
  2107 XMLHttpRequest::Send(const ArrayBuffer& aBody, ErrorResult& aRv)
  2109   JS::Rooted<JSObject*> obj(mWorkerPrivate->GetJSContext(), aBody.Obj());
  2110   return Send(obj, aRv);
  2113 void
  2114 XMLHttpRequest::Send(const ArrayBufferView& aBody, ErrorResult& aRv)
  2116   JS::Rooted<JSObject*> obj(mWorkerPrivate->GetJSContext(), aBody.Obj());
  2117   return Send(obj, aRv);
  2120 void
  2121 XMLHttpRequest::SendAsBinary(const nsAString& aBody, ErrorResult& aRv)
  2123   NS_NOTYETIMPLEMENTED("Implement me!");
  2124   aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
  2125   return;
  2128 void
  2129 XMLHttpRequest::Abort(ErrorResult& aRv)
  2131   mWorkerPrivate->AssertIsOnWorkerThread();
  2133   if (mCanceled) {
  2134     aRv.Throw(UNCATCHABLE_EXCEPTION);
  2137   if (!mProxy) {
  2138     return;
  2141   MaybeDispatchPrematureAbortEvents(aRv);
  2142   if (aRv.Failed()) {
  2143     return;
  2146   mProxy->mOuterEventStreamId++;
  2148   nsRefPtr<AbortRunnable> runnable = new AbortRunnable(mWorkerPrivate, mProxy);
  2149   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  2150     aRv.Throw(NS_ERROR_FAILURE);
  2151     return;
  2155 void
  2156 XMLHttpRequest::GetResponseHeader(const nsACString& aHeader,
  2157                                   nsACString& aResponseHeader, ErrorResult& aRv)
  2159   mWorkerPrivate->AssertIsOnWorkerThread();
  2161   if (mCanceled) {
  2162     aRv.Throw(UNCATCHABLE_EXCEPTION);
  2163     return;
  2166   if (!mProxy) {
  2167     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  2168     return;
  2171   nsCString responseHeader;
  2172   nsRefPtr<GetResponseHeaderRunnable> runnable =
  2173     new GetResponseHeaderRunnable(mWorkerPrivate, mProxy, aHeader,
  2174                                   responseHeader);
  2175   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  2176     aRv.Throw(NS_ERROR_FAILURE);
  2177     return;
  2179   aResponseHeader = responseHeader;
  2182 void
  2183 XMLHttpRequest::GetAllResponseHeaders(nsACString& aResponseHeaders,
  2184                                       ErrorResult& aRv)
  2186   mWorkerPrivate->AssertIsOnWorkerThread();
  2188   if (mCanceled) {
  2189     aRv.Throw(UNCATCHABLE_EXCEPTION);
  2190     return;
  2193   if (!mProxy) {
  2194     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  2195     return;
  2198   nsCString responseHeaders;
  2199   nsRefPtr<GetAllResponseHeadersRunnable> runnable =
  2200     new GetAllResponseHeadersRunnable(mWorkerPrivate, mProxy, responseHeaders);
  2201   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  2202     aRv.Throw(NS_ERROR_FAILURE);
  2203     return;
  2206   aResponseHeaders = responseHeaders;
  2209 void
  2210 XMLHttpRequest::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
  2212   mWorkerPrivate->AssertIsOnWorkerThread();
  2214   if (mCanceled) {
  2215     aRv.Throw(UNCATCHABLE_EXCEPTION);
  2216     return;
  2219   // We're supposed to throw if the state is not OPENED or HEADERS_RECEIVED. We
  2220   // can detect OPENED really easily but we can't detect HEADERS_RECEIVED in a
  2221   // non-racy way until the XHR state machine actually runs on this thread
  2222   // (bug 671047). For now we're going to let this work only if the Send()
  2223   // method has not been called.
  2224   if (!mProxy || SendInProgress()) {
  2225     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  2226     return;
  2229   nsRefPtr<OverrideMimeTypeRunnable> runnable =
  2230     new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType);
  2231   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  2232     aRv.Throw(NS_ERROR_FAILURE);
  2233     return;
  2237 void
  2238 XMLHttpRequest::SetResponseType(XMLHttpRequestResponseType aResponseType,
  2239                                 ErrorResult& aRv)
  2241   mWorkerPrivate->AssertIsOnWorkerThread();
  2243   if (mCanceled) {
  2244     aRv.Throw(UNCATCHABLE_EXCEPTION);
  2245     return;
  2248   if (!mProxy || SendInProgress()) {
  2249     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  2250     return;
  2253   // "document" is fine for the main thread but not for a worker. Short-circuit
  2254   // that here.
  2255   if (aResponseType == XMLHttpRequestResponseType::Document) {
  2256     return;
  2259   nsString responseType;
  2260   ConvertResponseTypeToString(aResponseType, responseType);
  2262   nsRefPtr<SetResponseTypeRunnable> runnable =
  2263     new SetResponseTypeRunnable(mWorkerPrivate, mProxy, responseType);
  2264   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  2265     aRv.Throw(NS_ERROR_FAILURE);
  2266     return;
  2269   nsString acceptedResponseTypeString;
  2270   runnable->GetResponseType(acceptedResponseTypeString);
  2272   mResponseType = ConvertStringToResponseType(acceptedResponseTypeString);
  2275 void
  2276 XMLHttpRequest::GetResponse(JSContext* /* unused */,
  2277                             JS::MutableHandle<JS::Value> aResponse,
  2278                             ErrorResult& aRv)
  2280   if (NS_SUCCEEDED(mStateData.mResponseTextResult) &&
  2281       JSVAL_IS_VOID(mStateData.mResponse)) {
  2282     MOZ_ASSERT(mStateData.mResponseText.Length());
  2283     MOZ_ASSERT(NS_SUCCEEDED(mStateData.mResponseResult));
  2285     JSString* str =
  2286       JS_NewUCStringCopyN(mWorkerPrivate->GetJSContext(),
  2287                           mStateData.mResponseText.get(),
  2288                           mStateData.mResponseText.Length());
  2289     if (!str) {
  2290       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
  2291       return;
  2294     mStateData.mResponse = STRING_TO_JSVAL(str);
  2297   JS::ExposeValueToActiveJS(mStateData.mResponse);
  2298   aRv = mStateData.mResponseResult;
  2299   aResponse.set(mStateData.mResponse);
  2302 void
  2303 XMLHttpRequest::GetResponseText(nsAString& aResponseText, ErrorResult& aRv)
  2305   aRv = mStateData.mResponseTextResult;
  2306   aResponseText = mStateData.mResponseText;
  2309 void
  2310 XMLHttpRequest::UpdateState(const StateData& aStateData)
  2312   mStateData = aStateData;

mercurial