1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "XMLHttpRequestWorker.h"
8 
9 #include "nsIDOMEvent.h"
10 #include "nsIDOMEventListener.h"
11 #include "nsIRunnable.h"
12 #include "nsIXPConnect.h"
13 
14 #include "jsfriendapi.h"
15 #include "js/TracingAPI.h"
16 #include "js/GCPolicyAPI.h"
17 #include "mozilla/ArrayUtils.h"
18 #include "mozilla/dom/Exceptions.h"
19 #include "mozilla/dom/File.h"
20 #include "mozilla/dom/FormData.h"
21 #include "mozilla/dom/ProgressEvent.h"
22 #include "mozilla/dom/StructuredCloneHolder.h"
23 #include "mozilla/dom/UnionConversions.h"
24 #include "mozilla/dom/URLSearchParams.h"
25 #include "mozilla/dom/WorkerScope.h"
26 #include "mozilla/dom/WorkerPrivate.h"
27 #include "mozilla/dom/WorkerRunnable.h"
28 #include "mozilla/Telemetry.h"
29 #include "nsComponentManagerUtils.h"
30 #include "nsContentUtils.h"
31 #include "nsJSUtils.h"
32 #include "nsThreadUtils.h"
33 
34 #include "XMLHttpRequestUpload.h"
35 
36 #include "mozilla/UniquePtr.h"
37 
38 namespace mozilla {
39 namespace dom {
40 
trace(JSTracer * aTrc)41 /* static */ void XMLHttpRequestWorker::StateData::trace(JSTracer* aTrc) {
42   JS::TraceEdge(aTrc, &mResponse, "XMLHttpRequestWorker::StateData::mResponse");
43 }
44 
45 /**
46  *  XMLHttpRequest in workers
47  *
48  *  XHR in workers is implemented by proxying calls/events/etc between the
49  *  worker thread and an XMLHttpRequest on the main thread.  The glue
50  *  object here is the Proxy, which lives on both threads.  All other objects
51  *  live on either the main thread (the XMLHttpRequest) or the worker thread
52  *  (the worker and XHR private objects).
53  *
54  *  The main thread XHR is always operated in async mode, even for sync XHR
55  *  in workers.  Calls made on the worker thread are proxied to the main thread
56  *  synchronously (meaning the worker thread is blocked until the call
57  *  returns).  Each proxied call spins up a sync queue, which captures any
58  *  synchronously dispatched events and ensures that they run synchronously
59  *  on the worker as well.  Asynchronously dispatched events are posted to the
60  *  worker thread to run asynchronously.  Some of the XHR state is mirrored on
61  *  the worker thread to avoid needing a cross-thread call on every property
62  *  access.
63  *
64  *  The XHR private is stored in the private slot of the XHR JSObject on the
65  *  worker thread.  It is destroyed when that JSObject is GCd.  The private
66  *  roots its JSObject while network activity is in progress.  It also
67  *  adds itself as a feature to the worker to give itself a chance to clean up
68  *  if the worker goes away during an XHR call.  It is important that the
69  *  rooting and feature registration (collectively called pinning) happens at
70  *  the proper times.  If we pin for too long we can cause memory leaks or even
71  *  shutdown hangs.  If we don't pin for long enough we introduce a GC hazard.
72  *
73  *  The XHR is pinned from the time Send is called to roughly the time loadend
74  *  is received.  There are some complications involved with Abort and XHR
75  *  reuse.  We maintain a counter on the main thread of how many times Send was
76  *  called on this XHR, and we decrement the counter every time we receive a
77  *  loadend event.  When the counter reaches zero we dispatch a runnable to the
78  *  worker thread to unpin the XHR.  We only decrement the counter if the
79  *  dispatch was successful, because the worker may no longer be accepting
80  *  regular runnables.  In the event that we reach Proxy::Teardown and there
81  *  the outstanding Send count is still non-zero, we dispatch a control
82  *  runnable which is guaranteed to run.
83  *
84  *  NB: Some of this could probably be simplified now that we have the
85  *  inner/outer channel ids.
86  */
87 
88 class Proxy final : public nsIDOMEventListener {
89  public:
90   // Read on multiple threads.
91   WorkerPrivate* mWorkerPrivate;
92   XMLHttpRequestWorker* mXMLHttpRequestPrivate;
93   const ClientInfo mClientInfo;
94   const Maybe<ServiceWorkerDescriptor> mController;
95 
96   // XHR Params:
97   bool mMozAnon;
98   bool mMozSystem;
99 
100   // Only touched on the main thread.
101   RefPtr<XMLHttpRequestMainThread> mXHR;
102   RefPtr<XMLHttpRequestUpload> mXHRUpload;
103   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
104   nsCOMPtr<nsIEventTarget> mSyncEventResponseTarget;
105   uint32_t mInnerEventStreamId;
106   uint32_t mInnerChannelId;
107   uint32_t mOutstandingSendCount;
108 
109   // Only touched on the worker thread.
110   uint32_t mOuterEventStreamId;
111   uint32_t mOuterChannelId;
112   uint32_t mOpenCount;
113   uint64_t mLastLoaded;
114   uint64_t mLastTotal;
115   uint64_t mLastUploadLoaded;
116   uint64_t mLastUploadTotal;
117   bool mIsSyncXHR;
118   bool mLastLengthComputable;
119   bool mLastUploadLengthComputable;
120   bool mSeenLoadStart;
121   bool mSeenUploadLoadStart;
122 
123   // Only touched on the main thread.
124   bool mUploadEventListenersAttached;
125   bool mMainThreadSeenLoadStart;
126   bool mInOpen;
127   bool mArrayBufferResponseWasTransferred;
128 
129  public:
Proxy(XMLHttpRequestWorker * aXHRPrivate,const ClientInfo & aClientInfo,const Maybe<ServiceWorkerDescriptor> & aController,bool aMozAnon,bool aMozSystem)130   Proxy(XMLHttpRequestWorker* aXHRPrivate, const ClientInfo& aClientInfo,
131         const Maybe<ServiceWorkerDescriptor>& aController, bool aMozAnon,
132         bool aMozSystem)
133       : mWorkerPrivate(nullptr),
134         mXMLHttpRequestPrivate(aXHRPrivate),
135         mClientInfo(aClientInfo),
136         mController(aController),
137         mMozAnon(aMozAnon),
138         mMozSystem(aMozSystem),
139         mInnerEventStreamId(0),
140         mInnerChannelId(0),
141         mOutstandingSendCount(0),
142         mOuterEventStreamId(0),
143         mOuterChannelId(0),
144         mOpenCount(0),
145         mLastLoaded(0),
146         mLastTotal(0),
147         mLastUploadLoaded(0),
148         mLastUploadTotal(0),
149         mIsSyncXHR(false),
150         mLastLengthComputable(false),
151         mLastUploadLengthComputable(false),
152         mSeenLoadStart(false),
153         mSeenUploadLoadStart(false),
154         mUploadEventListenersAttached(false),
155         mMainThreadSeenLoadStart(false),
156         mInOpen(false),
157         mArrayBufferResponseWasTransferred(false) {}
158 
159   NS_DECL_THREADSAFE_ISUPPORTS
160   NS_DECL_NSIDOMEVENTLISTENER
161 
162   bool Init();
163 
164   void Teardown(bool aSendUnpin);
165 
166   bool AddRemoveEventListeners(bool aUpload, bool aAdd);
167 
Reset()168   void Reset() {
169     AssertIsOnMainThread();
170 
171     if (mUploadEventListenersAttached) {
172       AddRemoveEventListeners(true, false);
173     }
174   }
175 
GetEventTarget()176   already_AddRefed<nsIEventTarget> GetEventTarget() {
177     AssertIsOnMainThread();
178 
179     nsCOMPtr<nsIEventTarget> target =
180         mSyncEventResponseTarget ? mSyncEventResponseTarget : mSyncLoopTarget;
181     return target.forget();
182   }
183 
184  private:
~Proxy()185   ~Proxy() {
186     MOZ_ASSERT(!mXHR);
187     MOZ_ASSERT(!mXHRUpload);
188     MOZ_ASSERT(!mOutstandingSendCount);
189   }
190 };
191 
192 class WorkerThreadProxySyncRunnable : public WorkerMainThreadRunnable {
193  protected:
194   RefPtr<Proxy> mProxy;
195 
196  private:
197   // mErrorCode is set on the main thread by MainThreadRun and it's used to at
198   // the end of the Dispatch() to return the error code.
199   nsresult mErrorCode;
200 
201  public:
WorkerThreadProxySyncRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy)202   WorkerThreadProxySyncRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
203       : WorkerMainThreadRunnable(aWorkerPrivate, NS_LITERAL_CSTRING("XHR")),
204         mProxy(aProxy),
205         mErrorCode(NS_OK) {
206     MOZ_ASSERT(aWorkerPrivate);
207     MOZ_ASSERT(aProxy);
208     aWorkerPrivate->AssertIsOnWorkerThread();
209   }
210 
Dispatch(WorkerStatus aFailStatus,ErrorResult & aRv)211   void Dispatch(WorkerStatus aFailStatus, ErrorResult& aRv) {
212     WorkerMainThreadRunnable::Dispatch(aFailStatus, aRv);
213     if (NS_WARN_IF(aRv.Failed())) {
214       return;
215     }
216 
217     if (NS_FAILED(mErrorCode)) {
218       aRv.Throw(mErrorCode);
219     }
220   }
221 
222  protected:
~WorkerThreadProxySyncRunnable()223   virtual ~WorkerThreadProxySyncRunnable() {}
224 
225   virtual void RunOnMainThread(ErrorResult& aRv) = 0;
226 
227  private:
228   virtual bool MainThreadRun() override;
229 };
230 
231 class SendRunnable final : public WorkerThreadProxySyncRunnable,
232                            public StructuredCloneHolder {
233   nsString mStringBody;
234   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
235   bool mHasUploadListeners;
236 
237  public:
SendRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy,const nsAString & aStringBody)238   SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
239                const nsAString& aStringBody)
240       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
241         StructuredCloneHolder(CloningSupported, TransferringNotSupported,
242                               StructuredCloneScope::SameProcessDifferentThread),
243         mStringBody(aStringBody),
244         mHasUploadListeners(false) {}
245 
SetHaveUploadListeners(bool aHasUploadListeners)246   void SetHaveUploadListeners(bool aHasUploadListeners) {
247     mHasUploadListeners = aHasUploadListeners;
248   }
249 
SetSyncLoopTarget(nsIEventTarget * aSyncLoopTarget)250   void SetSyncLoopTarget(nsIEventTarget* aSyncLoopTarget) {
251     mSyncLoopTarget = aSyncLoopTarget;
252   }
253 
254  private:
~SendRunnable()255   ~SendRunnable() {}
256 
257   virtual void RunOnMainThread(ErrorResult& aRv) override;
258 };
259 
260 namespace {
261 
262 enum {
263   STRING_abort = 0,
264   STRING_error,
265   STRING_load,
266   STRING_loadstart,
267   STRING_progress,
268   STRING_timeout,
269   STRING_readystatechange,
270   STRING_loadend,
271 
272   STRING_COUNT,
273 
274   STRING_LAST_XHR = STRING_loadend,
275   STRING_LAST_EVENTTARGET = STRING_timeout
276 };
277 
278 static_assert(STRING_LAST_XHR >= STRING_LAST_EVENTTARGET, "Bad string setup!");
279 static_assert(STRING_LAST_XHR == STRING_COUNT - 1, "Bad string setup!");
280 
281 const char* const sEventStrings[] = {
282     // XMLHttpRequestEventTarget event types, supported by both XHR and Upload.
283     "abort",
284     "error",
285     "load",
286     "loadstart",
287     "progress",
288     "timeout",
289 
290     // XMLHttpRequest event types, supported only by XHR.
291     "readystatechange",
292     "loadend",
293 };
294 
295 static_assert(MOZ_ARRAY_LENGTH(sEventStrings) == STRING_COUNT,
296               "Bad string count!");
297 
298 class MainThreadProxyRunnable : public MainThreadWorkerSyncRunnable {
299  protected:
300   RefPtr<Proxy> mProxy;
301 
MainThreadProxyRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy)302   MainThreadProxyRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
303       : MainThreadWorkerSyncRunnable(aWorkerPrivate, aProxy->GetEventTarget()),
304         mProxy(aProxy) {
305     MOZ_ASSERT(aProxy);
306   }
307 
~MainThreadProxyRunnable()308   virtual ~MainThreadProxyRunnable() {}
309 };
310 
311 class XHRUnpinRunnable final : public MainThreadWorkerControlRunnable {
312   XMLHttpRequestWorker* mXMLHttpRequestPrivate;
313 
314  public:
XHRUnpinRunnable(WorkerPrivate * aWorkerPrivate,XMLHttpRequestWorker * aXHRPrivate)315   XHRUnpinRunnable(WorkerPrivate* aWorkerPrivate,
316                    XMLHttpRequestWorker* aXHRPrivate)
317       : MainThreadWorkerControlRunnable(aWorkerPrivate),
318         mXMLHttpRequestPrivate(aXHRPrivate) {
319     MOZ_ASSERT(aXHRPrivate);
320   }
321 
322  private:
~XHRUnpinRunnable()323   ~XHRUnpinRunnable() {}
324 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)325   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
326     if (mXMLHttpRequestPrivate->SendInProgress()) {
327       mXMLHttpRequestPrivate->Unpin();
328     }
329 
330     return true;
331   }
332 };
333 
334 class AsyncTeardownRunnable final : public Runnable {
335   RefPtr<Proxy> mProxy;
336 
337  public:
AsyncTeardownRunnable(Proxy * aProxy)338   explicit AsyncTeardownRunnable(Proxy* aProxy)
339       : Runnable("dom::AsyncTeardownRunnable"), mProxy(aProxy) {
340     MOZ_ASSERT(aProxy);
341   }
342 
343  private:
~AsyncTeardownRunnable()344   ~AsyncTeardownRunnable() {}
345 
346   NS_IMETHOD
Run()347   Run() override {
348     AssertIsOnMainThread();
349 
350     // This means the XHR was GC'd, so we can't be pinned, and we don't need to
351     // try to unpin.
352     mProxy->Teardown(/* aSendUnpin */ false);
353     mProxy = nullptr;
354 
355     return NS_OK;
356   }
357 };
358 
359 class LoadStartDetectionRunnable final : public Runnable,
360                                          public nsIDOMEventListener {
361   WorkerPrivate* mWorkerPrivate;
362   RefPtr<Proxy> mProxy;
363   RefPtr<XMLHttpRequest> mXHR;
364   XMLHttpRequestWorker* mXMLHttpRequestPrivate;
365   nsString mEventType;
366   uint32_t mChannelId;
367   bool mReceivedLoadStart;
368 
369   class ProxyCompleteRunnable final : public MainThreadProxyRunnable {
370     XMLHttpRequestWorker* mXMLHttpRequestPrivate;
371     uint32_t mChannelId;
372 
373    public:
ProxyCompleteRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy,XMLHttpRequestWorker * aXHRPrivate,uint32_t aChannelId)374     ProxyCompleteRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
375                           XMLHttpRequestWorker* aXHRPrivate,
376                           uint32_t aChannelId)
377         : MainThreadProxyRunnable(aWorkerPrivate, aProxy),
378           mXMLHttpRequestPrivate(aXHRPrivate),
379           mChannelId(aChannelId) {}
380 
381    private:
~ProxyCompleteRunnable()382     ~ProxyCompleteRunnable() {}
383 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)384     virtual bool WorkerRun(JSContext* aCx,
385                            WorkerPrivate* aWorkerPrivate) override {
386       if (mChannelId != mProxy->mOuterChannelId) {
387         // Threads raced, this event is now obsolete.
388         return true;
389       }
390 
391       if (mSyncLoopTarget) {
392         aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, true);
393       }
394 
395       if (mXMLHttpRequestPrivate->SendInProgress()) {
396         mXMLHttpRequestPrivate->Unpin();
397       }
398 
399       return true;
400     }
401 
Cancel()402     nsresult Cancel() override {
403       // This must run!
404       nsresult rv = MainThreadProxyRunnable::Cancel();
405       nsresult rv2 = Run();
406       return NS_FAILED(rv) ? rv : rv2;
407     }
408   };
409 
410  public:
LoadStartDetectionRunnable(Proxy * aProxy,XMLHttpRequestWorker * aXHRPrivate)411   LoadStartDetectionRunnable(Proxy* aProxy, XMLHttpRequestWorker* aXHRPrivate)
412       : Runnable("dom::LoadStartDetectionRunnable"),
413         mWorkerPrivate(aProxy->mWorkerPrivate),
414         mProxy(aProxy),
415         mXHR(aProxy->mXHR),
416         mXMLHttpRequestPrivate(aXHRPrivate),
417         mChannelId(mProxy->mInnerChannelId),
418         mReceivedLoadStart(false) {
419     AssertIsOnMainThread();
420     CopyASCIItoUTF16(sEventStrings[STRING_loadstart], mEventType);
421   }
422 
423   NS_DECL_ISUPPORTS_INHERITED
424   NS_DECL_NSIRUNNABLE
425   NS_DECL_NSIDOMEVENTLISTENER
426 
RegisterAndDispatch()427   bool RegisterAndDispatch() {
428     AssertIsOnMainThread();
429 
430     if (NS_FAILED(mXHR->AddEventListener(mEventType, this, false, false, 2))) {
431       NS_WARNING("Failed to add event listener!");
432       return false;
433     }
434 
435     return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(this));
436   }
437 
438  private:
~LoadStartDetectionRunnable()439   ~LoadStartDetectionRunnable() { AssertIsOnMainThread(); }
440 };
441 
442 class EventRunnable final : public MainThreadProxyRunnable,
443                             public StructuredCloneHolder {
444   nsString mType;
445   nsString mResponseType;
446   JS::Heap<JS::Value> mResponse;
447   XMLHttpRequestStringSnapshot mResponseText;
448   nsString mResponseURL;
449   nsCString mStatusText;
450   uint64_t mLoaded;
451   uint64_t mTotal;
452   uint32_t mEventStreamId;
453   uint32_t mStatus;
454   uint16_t mReadyState;
455   bool mUploadEvent;
456   bool mProgressEvent;
457   bool mLengthComputable;
458   bool mUseCachedArrayBufferResponse;
459   nsresult mResponseTextResult;
460   nsresult mStatusResult;
461   nsresult mResponseResult;
462   // mScopeObj is used in PreDispatch only.  We init it in our constructor, and
463   // reset() in PreDispatch, to ensure that it's not still linked into the
464   // runtime once we go off-thread.
465   JS::PersistentRooted<JSObject*> mScopeObj;
466 
467  public:
EventRunnable(Proxy * aProxy,bool aUploadEvent,const nsString & aType,bool aLengthComputable,uint64_t aLoaded,uint64_t aTotal,JS::Handle<JSObject * > aScopeObj)468   EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
469                 bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal,
470                 JS::Handle<JSObject*> aScopeObj)
471       : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
472         StructuredCloneHolder(CloningSupported, TransferringNotSupported,
473                               StructuredCloneScope::SameProcessDifferentThread),
474         mType(aType),
475         mResponse(JS::UndefinedValue()),
476         mLoaded(aLoaded),
477         mTotal(aTotal),
478         mEventStreamId(aProxy->mInnerEventStreamId),
479         mStatus(0),
480         mReadyState(0),
481         mUploadEvent(aUploadEvent),
482         mProgressEvent(true),
483         mLengthComputable(aLengthComputable),
484         mUseCachedArrayBufferResponse(false),
485         mResponseTextResult(NS_OK),
486         mStatusResult(NS_OK),
487         mResponseResult(NS_OK),
488         mScopeObj(RootingCx(), aScopeObj) {}
489 
EventRunnable(Proxy * aProxy,bool aUploadEvent,const nsString & aType,JS::Handle<JSObject * > aScopeObj)490   EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
491                 JS::Handle<JSObject*> aScopeObj)
492       : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
493         StructuredCloneHolder(CloningSupported, TransferringNotSupported,
494                               StructuredCloneScope::SameProcessDifferentThread),
495         mType(aType),
496         mResponse(JS::UndefinedValue()),
497         mLoaded(0),
498         mTotal(0),
499         mEventStreamId(aProxy->mInnerEventStreamId),
500         mStatus(0),
501         mReadyState(0),
502         mUploadEvent(aUploadEvent),
503         mProgressEvent(false),
504         mLengthComputable(0),
505         mUseCachedArrayBufferResponse(false),
506         mResponseTextResult(NS_OK),
507         mStatusResult(NS_OK),
508         mResponseResult(NS_OK),
509         mScopeObj(RootingCx(), aScopeObj) {}
510 
511  private:
~EventRunnable()512   ~EventRunnable() {}
513 
514   bool PreDispatch(WorkerPrivate* /* unused */) final;
515   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
516 };
517 
518 class SyncTeardownRunnable final : public WorkerThreadProxySyncRunnable {
519  public:
SyncTeardownRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy)520   SyncTeardownRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
521       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy) {}
522 
523  private:
~SyncTeardownRunnable()524   ~SyncTeardownRunnable() {}
525 
RunOnMainThread(ErrorResult & aRv)526   virtual void RunOnMainThread(ErrorResult& aRv) override {
527     mProxy->Teardown(/* aSendUnpin */ true);
528     MOZ_ASSERT(!mProxy->mSyncLoopTarget);
529   }
530 };
531 
532 class SetBackgroundRequestRunnable final
533     : public WorkerThreadProxySyncRunnable {
534   bool mValue;
535 
536  public:
SetBackgroundRequestRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy,bool aValue)537   SetBackgroundRequestRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
538                                bool aValue)
539       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue) {}
540 
541  private:
~SetBackgroundRequestRunnable()542   ~SetBackgroundRequestRunnable() {}
543 
RunOnMainThread(ErrorResult & aRv)544   virtual void RunOnMainThread(ErrorResult& aRv) override {
545     mProxy->mXHR->SetMozBackgroundRequest(mValue, aRv);
546   }
547 };
548 
549 class SetWithCredentialsRunnable final : public WorkerThreadProxySyncRunnable {
550   bool mValue;
551 
552  public:
SetWithCredentialsRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy,bool aValue)553   SetWithCredentialsRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
554                              bool aValue)
555       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue) {}
556 
557  private:
~SetWithCredentialsRunnable()558   ~SetWithCredentialsRunnable() {}
559 
RunOnMainThread(ErrorResult & aRv)560   virtual void RunOnMainThread(ErrorResult& aRv) override {
561     mProxy->mXHR->SetWithCredentials(mValue, aRv);
562   }
563 };
564 
565 class SetResponseTypeRunnable final : public WorkerThreadProxySyncRunnable {
566   XMLHttpRequestResponseType mResponseType;
567 
568  public:
SetResponseTypeRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy,XMLHttpRequestResponseType aResponseType)569   SetResponseTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
570                           XMLHttpRequestResponseType aResponseType)
571       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
572         mResponseType(aResponseType) {}
573 
ResponseType()574   XMLHttpRequestResponseType ResponseType() { return mResponseType; }
575 
576  private:
~SetResponseTypeRunnable()577   ~SetResponseTypeRunnable() {}
578 
RunOnMainThread(ErrorResult & aRv)579   virtual void RunOnMainThread(ErrorResult& aRv) override {
580     mProxy->mXHR->SetResponseType(mResponseType, aRv);
581     if (!aRv.Failed()) {
582       mResponseType = mProxy->mXHR->ResponseType();
583     }
584   }
585 };
586 
587 class SetTimeoutRunnable final : public WorkerThreadProxySyncRunnable {
588   uint32_t mTimeout;
589 
590  public:
SetTimeoutRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy,uint32_t aTimeout)591   SetTimeoutRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
592                      uint32_t aTimeout)
593       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
594         mTimeout(aTimeout) {}
595 
596  private:
~SetTimeoutRunnable()597   ~SetTimeoutRunnable() {}
598 
RunOnMainThread(ErrorResult & aRv)599   virtual void RunOnMainThread(ErrorResult& aRv) override {
600     mProxy->mXHR->SetTimeout(mTimeout, aRv);
601   }
602 };
603 
604 class AbortRunnable final : public WorkerThreadProxySyncRunnable {
605  public:
AbortRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy)606   AbortRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
607       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy) {}
608 
609  private:
~AbortRunnable()610   ~AbortRunnable() {}
611 
612   virtual void RunOnMainThread(ErrorResult& aRv) override;
613 };
614 
615 class GetAllResponseHeadersRunnable final
616     : public WorkerThreadProxySyncRunnable {
617   nsCString& mResponseHeaders;
618 
619  public:
GetAllResponseHeadersRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy,nsCString & aResponseHeaders)620   GetAllResponseHeadersRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
621                                 nsCString& aResponseHeaders)
622       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
623         mResponseHeaders(aResponseHeaders) {}
624 
625  private:
~GetAllResponseHeadersRunnable()626   ~GetAllResponseHeadersRunnable() {}
627 
RunOnMainThread(ErrorResult & aRv)628   virtual void RunOnMainThread(ErrorResult& aRv) override {
629     mProxy->mXHR->GetAllResponseHeaders(mResponseHeaders, aRv);
630   }
631 };
632 
633 class GetResponseHeaderRunnable final : public WorkerThreadProxySyncRunnable {
634   const nsCString mHeader;
635   nsCString& mValue;
636 
637  public:
GetResponseHeaderRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy,const nsACString & aHeader,nsCString & aValue)638   GetResponseHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
639                             const nsACString& aHeader, nsCString& aValue)
640       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
641         mHeader(aHeader),
642         mValue(aValue) {}
643 
644  private:
~GetResponseHeaderRunnable()645   ~GetResponseHeaderRunnable() {}
646 
RunOnMainThread(ErrorResult & aRv)647   virtual void RunOnMainThread(ErrorResult& aRv) override {
648     mProxy->mXHR->GetResponseHeader(mHeader, mValue, aRv);
649   }
650 };
651 
652 class OpenRunnable final : public WorkerThreadProxySyncRunnable {
653   nsCString mMethod;
654   nsString mURL;
655   Optional<nsAString> mUser;
656   nsString mUserStr;
657   Optional<nsAString> mPassword;
658   nsString mPasswordStr;
659   bool mBackgroundRequest;
660   bool mWithCredentials;
661   uint32_t mTimeout;
662   XMLHttpRequestResponseType mResponseType;
663 
664  public:
OpenRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy,const nsACString & aMethod,const nsAString & aURL,const Optional<nsAString> & aUser,const Optional<nsAString> & aPassword,bool aBackgroundRequest,bool aWithCredentials,uint32_t aTimeout,XMLHttpRequestResponseType aResponseType)665   OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
666                const nsACString& aMethod, const nsAString& aURL,
667                const Optional<nsAString>& aUser,
668                const Optional<nsAString>& aPassword, bool aBackgroundRequest,
669                bool aWithCredentials, uint32_t aTimeout,
670                XMLHttpRequestResponseType aResponseType)
671       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
672         mMethod(aMethod),
673         mURL(aURL),
674         mBackgroundRequest(aBackgroundRequest),
675         mWithCredentials(aWithCredentials),
676         mTimeout(aTimeout),
677         mResponseType(aResponseType) {
678     if (aUser.WasPassed()) {
679       mUserStr = aUser.Value();
680       mUser = &mUserStr;
681     }
682     if (aPassword.WasPassed()) {
683       mPasswordStr = aPassword.Value();
684       mPassword = &mPasswordStr;
685     }
686   }
687 
688  private:
~OpenRunnable()689   ~OpenRunnable() {}
690 
RunOnMainThread(ErrorResult & aRv)691   virtual void RunOnMainThread(ErrorResult& aRv) override {
692     WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
693     mProxy->mWorkerPrivate = mWorkerPrivate;
694 
695     aRv = MainThreadRunInternal();
696 
697     mProxy->mWorkerPrivate = oldWorker;
698   }
699 
700   nsresult MainThreadRunInternal();
701 };
702 
703 class SetRequestHeaderRunnable final : public WorkerThreadProxySyncRunnable {
704   nsCString mHeader;
705   nsCString mValue;
706 
707  public:
SetRequestHeaderRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy,const nsACString & aHeader,const nsACString & aValue)708   SetRequestHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
709                            const nsACString& aHeader, const nsACString& aValue)
710       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
711         mHeader(aHeader),
712         mValue(aValue) {}
713 
714  private:
~SetRequestHeaderRunnable()715   ~SetRequestHeaderRunnable() {}
716 
RunOnMainThread(ErrorResult & aRv)717   virtual void RunOnMainThread(ErrorResult& aRv) override {
718     mProxy->mXHR->SetRequestHeader(mHeader, mValue, aRv);
719   }
720 };
721 
722 class OverrideMimeTypeRunnable final : public WorkerThreadProxySyncRunnable {
723   nsString mMimeType;
724 
725  public:
OverrideMimeTypeRunnable(WorkerPrivate * aWorkerPrivate,Proxy * aProxy,const nsAString & aMimeType)726   OverrideMimeTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
727                            const nsAString& aMimeType)
728       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
729         mMimeType(aMimeType) {}
730 
731  private:
~OverrideMimeTypeRunnable()732   ~OverrideMimeTypeRunnable() {}
733 
RunOnMainThread(ErrorResult & aRv)734   virtual void RunOnMainThread(ErrorResult& aRv) override {
735     mProxy->mXHR->OverrideMimeType(mMimeType, aRv);
736   }
737 };
738 
739 class AutoUnpinXHR {
740   XMLHttpRequestWorker* mXMLHttpRequestPrivate;
741 
742  public:
AutoUnpinXHR(XMLHttpRequestWorker * aXMLHttpRequestPrivate)743   explicit AutoUnpinXHR(XMLHttpRequestWorker* aXMLHttpRequestPrivate)
744       : mXMLHttpRequestPrivate(aXMLHttpRequestPrivate) {
745     MOZ_ASSERT(aXMLHttpRequestPrivate);
746   }
747 
~AutoUnpinXHR()748   ~AutoUnpinXHR() {
749     if (mXMLHttpRequestPrivate) {
750       mXMLHttpRequestPrivate->Unpin();
751     }
752   }
753 
Clear()754   void Clear() { mXMLHttpRequestPrivate = nullptr; }
755 };
756 
757 }  // namespace
758 
Init()759 bool Proxy::Init() {
760   AssertIsOnMainThread();
761   MOZ_ASSERT(mWorkerPrivate);
762 
763   if (mXHR) {
764     return true;
765   }
766 
767   nsPIDOMWindowInner* ownerWindow = mWorkerPrivate->GetWindow();
768   if (ownerWindow && !ownerWindow->IsCurrentInnerWindow()) {
769     NS_WARNING("Window has navigated, cannot create XHR here.");
770     return false;
771   }
772 
773   mXHR = new XMLHttpRequestMainThread();
774   mXHR->Construct(mWorkerPrivate->GetPrincipal(),
775                   ownerWindow ? ownerWindow->AsGlobal() : nullptr,
776                   mWorkerPrivate->GetBaseURI(), mWorkerPrivate->GetLoadGroup(),
777                   mWorkerPrivate->GetPerformanceStorage());
778 
779   mXHR->SetParameters(mMozAnon, mMozSystem);
780   mXHR->SetClientInfoAndController(mClientInfo, mController);
781 
782   ErrorResult rv;
783   mXHRUpload = mXHR->GetUpload(rv);
784   if (NS_WARN_IF(rv.Failed())) {
785     mXHR = nullptr;
786     return false;
787   }
788 
789   if (!AddRemoveEventListeners(false, true)) {
790     mXHR = nullptr;
791     mXHRUpload = nullptr;
792     return false;
793   }
794 
795   return true;
796 }
797 
Teardown(bool aSendUnpin)798 void Proxy::Teardown(bool aSendUnpin) {
799   AssertIsOnMainThread();
800 
801   if (mXHR) {
802     Reset();
803 
804     // NB: We are intentionally dropping events coming from xhr.abort on the
805     // floor.
806     AddRemoveEventListeners(false, false);
807 
808     ErrorResult rv;
809     mXHR->Abort(rv);
810     if (NS_WARN_IF(rv.Failed())) {
811       rv.SuppressException();
812     }
813 
814     if (mOutstandingSendCount) {
815       if (aSendUnpin) {
816         RefPtr<XHRUnpinRunnable> runnable =
817             new XHRUnpinRunnable(mWorkerPrivate, mXMLHttpRequestPrivate);
818         if (!runnable->Dispatch()) {
819           MOZ_CRASH("We're going to hang at shutdown anyways.");
820         }
821       }
822 
823       if (mSyncLoopTarget) {
824         // We have an unclosed sync loop.  Fix that now.
825         RefPtr<MainThreadStopSyncLoopRunnable> runnable =
826             new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
827                                                mSyncLoopTarget.forget(), false);
828         if (!runnable->Dispatch()) {
829           MOZ_CRASH("We're going to hang at shutdown anyways.");
830         }
831       }
832 
833       mOutstandingSendCount = 0;
834     }
835 
836     mWorkerPrivate = nullptr;
837     mXHRUpload = nullptr;
838     mXHR = nullptr;
839   }
840 
841   MOZ_ASSERT(!mWorkerPrivate);
842   MOZ_ASSERT(!mSyncLoopTarget);
843 }
844 
AddRemoveEventListeners(bool aUpload,bool aAdd)845 bool Proxy::AddRemoveEventListeners(bool aUpload, bool aAdd) {
846   AssertIsOnMainThread();
847 
848   NS_ASSERTION(!aUpload || (mUploadEventListenersAttached && !aAdd) ||
849                    (!mUploadEventListenersAttached && aAdd),
850                "Messed up logic for upload listeners!");
851 
852   DOMEventTargetHelper* targetHelper =
853       aUpload ? static_cast<XMLHttpRequestUpload*>(mXHRUpload.get())
854               : static_cast<XMLHttpRequestEventTarget*>(mXHR.get());
855   MOZ_ASSERT(targetHelper, "This should never fail!");
856   nsCOMPtr<nsIDOMEventTarget> target = targetHelper;
857 
858   uint32_t lastEventType = aUpload ? STRING_LAST_EVENTTARGET : STRING_LAST_XHR;
859 
860   nsAutoString eventType;
861   for (uint32_t index = 0; index <= lastEventType; index++) {
862     eventType = NS_ConvertASCIItoUTF16(sEventStrings[index]);
863     if (aAdd) {
864       if (NS_FAILED(target->AddEventListener(eventType, this, false))) {
865         return false;
866       }
867     } else if (NS_FAILED(target->RemoveEventListener(eventType, this, false))) {
868       return false;
869     }
870   }
871 
872   if (aUpload) {
873     mUploadEventListenersAttached = aAdd;
874   }
875 
876   return true;
877 }
878 
NS_IMPL_ISUPPORTS(Proxy,nsIDOMEventListener)879 NS_IMPL_ISUPPORTS(Proxy, nsIDOMEventListener)
880 
881 NS_IMETHODIMP
882 Proxy::HandleEvent(nsIDOMEvent* aEvent) {
883   AssertIsOnMainThread();
884 
885   if (!mWorkerPrivate || !mXMLHttpRequestPrivate) {
886     NS_ERROR("Shouldn't get here!");
887     return NS_OK;
888   }
889 
890   nsString type;
891   if (NS_FAILED(aEvent->GetType(type))) {
892     NS_WARNING("Failed to get event type!");
893     return NS_ERROR_FAILURE;
894   }
895 
896   nsCOMPtr<nsIDOMEventTarget> target;
897   if (NS_FAILED(aEvent->GetTarget(getter_AddRefs(target)))) {
898     NS_WARNING("Failed to get target!");
899     return NS_ERROR_FAILURE;
900   }
901 
902   bool isUploadTarget = mXHR != target;
903   ProgressEvent* progressEvent = aEvent->InternalDOMEvent()->AsProgressEvent();
904 
905   if (mInOpen && type.EqualsASCII(sEventStrings[STRING_readystatechange])) {
906     if (mXHR->ReadyState() == 1) {
907       mInnerEventStreamId++;
908     }
909   }
910 
911   {
912     AutoSafeJSContext cx;
913     JSAutoRequest ar(cx);
914 
915     JS::Rooted<JS::Value> value(cx);
916     if (!GetOrCreateDOMReflectorNoWrap(cx, mXHR, &value)) {
917       return NS_ERROR_FAILURE;
918     }
919 
920     JS::Rooted<JSObject*> scope(cx, &value.toObject());
921 
922     RefPtr<EventRunnable> runnable;
923     if (progressEvent) {
924       runnable = new EventRunnable(
925           this, isUploadTarget, type, progressEvent->LengthComputable(),
926           progressEvent->Loaded(), progressEvent->Total(), scope);
927     } else {
928       runnable = new EventRunnable(this, isUploadTarget, type, scope);
929     }
930 
931     runnable->Dispatch();
932   }
933 
934   if (!isUploadTarget) {
935     if (type.EqualsASCII(sEventStrings[STRING_loadstart])) {
936       mMainThreadSeenLoadStart = true;
937     } else if (mMainThreadSeenLoadStart &&
938                type.EqualsASCII(sEventStrings[STRING_loadend])) {
939       mMainThreadSeenLoadStart = false;
940 
941       RefPtr<LoadStartDetectionRunnable> runnable =
942           new LoadStartDetectionRunnable(this, mXMLHttpRequestPrivate);
943       if (!runnable->RegisterAndDispatch()) {
944         NS_WARNING("Failed to dispatch LoadStartDetectionRunnable!");
945       }
946     }
947   }
948 
949   return NS_OK;
950 }
951 
NS_IMPL_ISUPPORTS_INHERITED(LoadStartDetectionRunnable,Runnable,nsIDOMEventListener)952 NS_IMPL_ISUPPORTS_INHERITED(LoadStartDetectionRunnable, Runnable,
953                             nsIDOMEventListener)
954 
955 NS_IMETHODIMP
956 LoadStartDetectionRunnable::Run() {
957   AssertIsOnMainThread();
958 
959   if (NS_FAILED(mXHR->RemoveEventListener(mEventType, this, false))) {
960     NS_WARNING("Failed to remove event listener!");
961   }
962 
963   if (!mReceivedLoadStart) {
964     if (mProxy->mOutstandingSendCount > 1) {
965       mProxy->mOutstandingSendCount--;
966     } else if (mProxy->mOutstandingSendCount == 1) {
967       mProxy->Reset();
968 
969       RefPtr<ProxyCompleteRunnable> runnable = new ProxyCompleteRunnable(
970           mWorkerPrivate, mProxy, mXMLHttpRequestPrivate, mChannelId);
971       if (runnable->Dispatch()) {
972         mProxy->mWorkerPrivate = nullptr;
973         mProxy->mSyncLoopTarget = nullptr;
974         mProxy->mOutstandingSendCount--;
975       }
976     }
977   }
978 
979   mProxy = nullptr;
980   mXHR = nullptr;
981   mXMLHttpRequestPrivate = nullptr;
982   return NS_OK;
983 }
984 
985 NS_IMETHODIMP
HandleEvent(nsIDOMEvent * aEvent)986 LoadStartDetectionRunnable::HandleEvent(nsIDOMEvent* aEvent) {
987   AssertIsOnMainThread();
988 
989 #ifdef DEBUG
990   {
991     nsString type;
992     if (NS_SUCCEEDED(aEvent->GetType(type))) {
993       MOZ_ASSERT(type == mEventType);
994     } else {
995       NS_WARNING("Failed to get event type!");
996     }
997   }
998 #endif
999 
1000   mReceivedLoadStart = true;
1001   return NS_OK;
1002 }
1003 
PreDispatch(WorkerPrivate *)1004 bool EventRunnable::PreDispatch(WorkerPrivate* /* unused */) {
1005   AssertIsOnMainThread();
1006 
1007   AutoJSAPI jsapi;
1008   DebugOnly<bool> ok = jsapi.Init(xpc::NativeGlobal(mScopeObj));
1009   MOZ_ASSERT(ok);
1010   JSContext* cx = jsapi.cx();
1011   // Now keep the mScopeObj alive for the duration
1012   JS::Rooted<JSObject*> scopeObj(cx, mScopeObj);
1013   // And reset mScopeObj now, before we have a chance to run its destructor on
1014   // some background thread.
1015   mScopeObj.reset();
1016 
1017   RefPtr<XMLHttpRequestMainThread>& xhr = mProxy->mXHR;
1018   MOZ_ASSERT(xhr);
1019 
1020   const EnumEntry& entry =
1021       XMLHttpRequestResponseTypeValues::strings[static_cast<uint32_t>(
1022           xhr->ResponseType())];
1023   mResponseType.AssignASCII(entry.value, entry.length);
1024 
1025   ErrorResult rv;
1026   xhr->GetResponseText(mResponseText, rv);
1027   mResponseTextResult = rv.StealNSResult();
1028 
1029   if (NS_SUCCEEDED(mResponseTextResult)) {
1030     mResponseResult = mResponseTextResult;
1031     if (mResponseText.IsVoid()) {
1032       mResponse.setNull();
1033     }
1034   } else {
1035     JS::Rooted<JS::Value> response(cx);
1036     xhr->GetResponse(cx, &response, rv);
1037     mResponseResult = rv.StealNSResult();
1038     if (NS_SUCCEEDED(mResponseResult)) {
1039       if (!response.isGCThing()) {
1040         mResponse = response;
1041       } else {
1042         bool doClone = true;
1043         JS::Rooted<JS::Value> transferable(cx);
1044         JS::Rooted<JSObject*> obj(
1045             cx, response.isObject() ? &response.toObject() : nullptr);
1046         if (obj && JS_IsArrayBufferObject(obj)) {
1047           // Use cached response if the arraybuffer has been transfered.
1048           if (mProxy->mArrayBufferResponseWasTransferred) {
1049             MOZ_ASSERT(JS_IsDetachedArrayBufferObject(obj));
1050             mUseCachedArrayBufferResponse = true;
1051             doClone = false;
1052           } else {
1053             MOZ_ASSERT(!JS_IsDetachedArrayBufferObject(obj));
1054             JS::AutoValueArray<1> argv(cx);
1055             argv[0].set(response);
1056             obj = JS_NewArrayObject(cx, argv);
1057             if (obj) {
1058               transferable.setObject(*obj);
1059               // Only cache the response when the readyState is DONE.
1060               if (xhr->ReadyState() == 4) {
1061                 mProxy->mArrayBufferResponseWasTransferred = true;
1062               }
1063             } else {
1064               mResponseResult = NS_ERROR_OUT_OF_MEMORY;
1065               doClone = false;
1066             }
1067           }
1068         }
1069 
1070         if (doClone) {
1071           Write(cx, response, transferable, JS::CloneDataPolicy(), rv);
1072           if (NS_WARN_IF(rv.Failed())) {
1073             NS_WARNING("Failed to clone response!");
1074             mResponseResult = rv.StealNSResult();
1075             mProxy->mArrayBufferResponseWasTransferred = false;
1076           }
1077         }
1078       }
1079     }
1080   }
1081 
1082   mStatus = xhr->GetStatus(rv);
1083   mStatusResult = rv.StealNSResult();
1084 
1085   xhr->GetStatusText(mStatusText, rv);
1086   MOZ_ASSERT(!rv.Failed());
1087 
1088   mReadyState = xhr->ReadyState();
1089 
1090   xhr->GetResponseURL(mResponseURL);
1091 
1092   return true;
1093 }
1094 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1095 bool EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
1096   if (mEventStreamId != mProxy->mOuterEventStreamId) {
1097     // Threads raced, this event is now obsolete.
1098     return true;
1099   }
1100 
1101   if (!mProxy->mXMLHttpRequestPrivate) {
1102     // Object was finalized, bail.
1103     return true;
1104   }
1105 
1106   if (mType.EqualsASCII(sEventStrings[STRING_loadstart])) {
1107     if (mUploadEvent) {
1108       mProxy->mSeenUploadLoadStart = true;
1109     } else {
1110       mProxy->mSeenLoadStart = true;
1111     }
1112   } else if (mType.EqualsASCII(sEventStrings[STRING_loadend])) {
1113     if (mUploadEvent) {
1114       mProxy->mSeenUploadLoadStart = false;
1115     } else {
1116       if (!mProxy->mSeenLoadStart) {
1117         // We've already dispatched premature abort events.
1118         return true;
1119       }
1120       mProxy->mSeenLoadStart = false;
1121     }
1122   } else if (mType.EqualsASCII(sEventStrings[STRING_abort])) {
1123     if ((mUploadEvent && !mProxy->mSeenUploadLoadStart) ||
1124         (!mUploadEvent && !mProxy->mSeenLoadStart)) {
1125       // We've already dispatched premature abort events.
1126       return true;
1127     }
1128   }
1129 
1130   if (mProgressEvent) {
1131     // Cache these for premature abort events.
1132     if (mUploadEvent) {
1133       mProxy->mLastUploadLengthComputable = mLengthComputable;
1134       mProxy->mLastUploadLoaded = mLoaded;
1135       mProxy->mLastUploadTotal = mTotal;
1136     } else {
1137       mProxy->mLastLengthComputable = mLengthComputable;
1138       mProxy->mLastLoaded = mLoaded;
1139       mProxy->mLastTotal = mTotal;
1140     }
1141   }
1142 
1143   JS::Rooted<UniquePtr<XMLHttpRequestWorker::StateData>> state(
1144       aCx, new XMLHttpRequestWorker::StateData());
1145 
1146   state->mResponseTextResult = mResponseTextResult;
1147 
1148   state->mResponseText = mResponseText;
1149 
1150   if (NS_SUCCEEDED(mResponseTextResult)) {
1151     MOZ_ASSERT(mResponse.isUndefined() || mResponse.isNull());
1152     state->mResponseResult = mResponseTextResult;
1153     state->mResponse = mResponse;
1154   } else {
1155     state->mResponseResult = mResponseResult;
1156 
1157     if (NS_SUCCEEDED(mResponseResult)) {
1158       if (HasData()) {
1159         MOZ_ASSERT(mResponse.isUndefined());
1160 
1161         ErrorResult rv;
1162         JS::Rooted<JS::Value> response(aCx);
1163 
1164         GlobalObject globalObj(aCx,
1165                                aWorkerPrivate->GlobalScope()->GetWrapper());
1166         nsCOMPtr<nsIGlobalObject> global =
1167             do_QueryInterface(globalObj.GetAsSupports());
1168 
1169         Read(global, aCx, &response, rv);
1170         if (NS_WARN_IF(rv.Failed())) {
1171           rv.SuppressException();
1172           return false;
1173         }
1174 
1175         state->mResponse = response;
1176       } else {
1177         state->mResponse = mResponse;
1178       }
1179     }
1180   }
1181 
1182   state->mStatusResult = mStatusResult;
1183   state->mStatus = mStatus;
1184 
1185   state->mStatusText = mStatusText;
1186 
1187   state->mReadyState = mReadyState;
1188 
1189   state->mResponseURL = mResponseURL;
1190 
1191   XMLHttpRequestWorker* xhr = mProxy->mXMLHttpRequestPrivate;
1192   xhr->UpdateState(*state.get(), mUseCachedArrayBufferResponse);
1193 
1194   if (mType.EqualsASCII(sEventStrings[STRING_readystatechange])) {
1195     if (mReadyState == 4 && !mUploadEvent && !mProxy->mSeenLoadStart) {
1196       // We've already dispatched premature abort events.
1197       return true;
1198     }
1199   }
1200 
1201   if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) {
1202     return true;
1203   }
1204 
1205   XMLHttpRequestEventTarget* target;
1206   if (mUploadEvent) {
1207     target = xhr->GetUploadObjectNoCreate();
1208   } else {
1209     target = xhr;
1210   }
1211 
1212   MOZ_ASSERT(target);
1213 
1214   RefPtr<Event> event;
1215   if (mProgressEvent) {
1216     ProgressEventInit init;
1217     init.mBubbles = false;
1218     init.mCancelable = false;
1219     init.mLengthComputable = mLengthComputable;
1220     init.mLoaded = mLoaded;
1221     init.mTotal = mTotal;
1222 
1223     event = ProgressEvent::Constructor(target, mType, init);
1224   } else {
1225     event = NS_NewDOMEvent(target, nullptr, nullptr);
1226 
1227     if (event) {
1228       event->InitEvent(mType, false, false);
1229     }
1230   }
1231 
1232   if (!event) {
1233     return false;
1234   }
1235 
1236   event->SetTrusted(true);
1237 
1238   bool dummy;
1239   target->DispatchEvent(event, &dummy);
1240 
1241   // After firing the event set mResponse to JSVAL_NULL for chunked response
1242   // types.
1243   if (StringBeginsWith(mResponseType, NS_LITERAL_STRING("moz-chunked-"))) {
1244     xhr->NullResponseText();
1245   }
1246 
1247   return true;
1248 }
1249 
MainThreadRun()1250 bool WorkerThreadProxySyncRunnable::MainThreadRun() {
1251   AssertIsOnMainThread();
1252 
1253   nsCOMPtr<nsIEventTarget> tempTarget = mSyncLoopTarget;
1254 
1255   mProxy->mSyncEventResponseTarget.swap(tempTarget);
1256 
1257   ErrorResult rv;
1258   RunOnMainThread(rv);
1259   mErrorCode = rv.StealNSResult();
1260 
1261   mProxy->mSyncEventResponseTarget.swap(tempTarget);
1262 
1263   return true;
1264 }
1265 
RunOnMainThread(ErrorResult & aRv)1266 void AbortRunnable::RunOnMainThread(ErrorResult& aRv) {
1267   mProxy->mInnerEventStreamId++;
1268 
1269   WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
1270   mProxy->mWorkerPrivate = mWorkerPrivate;
1271 
1272   mProxy->mXHR->Abort(aRv);
1273 
1274   mProxy->mWorkerPrivate = oldWorker;
1275 
1276   mProxy->Reset();
1277 }
1278 
MainThreadRunInternal()1279 nsresult OpenRunnable::MainThreadRunInternal() {
1280   if (!mProxy->Init()) {
1281     return NS_ERROR_DOM_INVALID_STATE_ERR;
1282   }
1283 
1284   if (mBackgroundRequest) {
1285     nsresult rv = mProxy->mXHR->SetMozBackgroundRequest(mBackgroundRequest);
1286     NS_ENSURE_SUCCESS(rv, rv);
1287   }
1288 
1289   ErrorResult rv;
1290 
1291   if (mWithCredentials) {
1292     mProxy->mXHR->SetWithCredentials(mWithCredentials, rv);
1293     if (NS_WARN_IF(rv.Failed())) {
1294       return rv.StealNSResult();
1295     }
1296   }
1297 
1298   if (mTimeout) {
1299     mProxy->mXHR->SetTimeout(mTimeout, rv);
1300     if (NS_WARN_IF(rv.Failed())) {
1301       return rv.StealNSResult();
1302     }
1303   }
1304 
1305   MOZ_ASSERT(!mProxy->mInOpen);
1306   mProxy->mInOpen = true;
1307 
1308   mProxy->mXHR->Open(
1309       mMethod, mURL, true, mUser.WasPassed() ? mUser.Value() : VoidString(),
1310       mPassword.WasPassed() ? mPassword.Value() : VoidString(), rv);
1311 
1312   MOZ_ASSERT(mProxy->mInOpen);
1313   mProxy->mInOpen = false;
1314 
1315   if (NS_WARN_IF(rv.Failed())) {
1316     return rv.StealNSResult();
1317   }
1318 
1319   mProxy->mXHR->SetResponseType(mResponseType, rv);
1320   if (NS_WARN_IF(rv.Failed())) {
1321     return rv.StealNSResult();
1322   }
1323 
1324   return NS_OK;
1325 }
1326 
RunOnMainThread(ErrorResult & aRv)1327 void SendRunnable::RunOnMainThread(ErrorResult& aRv) {
1328   Nullable<
1329       DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString>
1330       payload;
1331 
1332   if (HasData()) {
1333     AutoSafeJSContext cx;
1334     JSAutoRequest ar(cx);
1335 
1336     JS::Rooted<JSObject*> globalObject(cx, JS::CurrentGlobalOrNull(cx));
1337     if (NS_WARN_IF(!globalObject)) {
1338       aRv.Throw(NS_ERROR_FAILURE);
1339       return;
1340     }
1341 
1342     nsCOMPtr<nsIGlobalObject> parent = xpc::NativeGlobal(globalObject);
1343     if (NS_WARN_IF(!parent)) {
1344       aRv.Throw(NS_ERROR_FAILURE);
1345       return;
1346     }
1347 
1348     JS::Rooted<JS::Value> body(cx);
1349     Read(parent, cx, &body, aRv);
1350     if (NS_WARN_IF(aRv.Failed())) {
1351       return;
1352     }
1353 
1354     Maybe<
1355         DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVStringArgument>
1356         holder;
1357     holder.emplace(payload.SetValue());
1358     bool done = false, failed = false, tryNext;
1359 
1360     if (body.isObject()) {
1361       done = (failed =
1362                   !holder.ref().TrySetToDocument(cx, &body, tryNext, false)) ||
1363              !tryNext ||
1364              (failed = !holder.ref().TrySetToBlob(cx, &body, tryNext, false)) ||
1365              !tryNext ||
1366              (failed = !holder.ref().TrySetToArrayBufferView(cx, &body, tryNext,
1367                                                              false)) ||
1368              !tryNext ||
1369              (failed = !holder.ref().TrySetToArrayBuffer(cx, &body, tryNext,
1370                                                          false)) ||
1371              !tryNext ||
1372              (failed =
1373                   !holder.ref().TrySetToFormData(cx, &body, tryNext, false)) ||
1374              !tryNext ||
1375              (failed = !holder.ref().TrySetToURLSearchParams(cx, &body, tryNext,
1376                                                              false)) ||
1377              !tryNext;
1378     }
1379 
1380     if (!done) {
1381       done = (failed = !holder.ref().TrySetToUSVString(cx, &body, tryNext)) ||
1382              !tryNext;
1383     }
1384     if (failed || !done) {
1385       aRv.Throw(NS_ERROR_FAILURE);
1386       return;
1387     }
1388   } else {
1389     DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString&
1390         ref = payload.SetValue();
1391     ref.SetAsUSVString().Rebind(mStringBody.Data(), mStringBody.Length());
1392   }
1393 
1394   // Send() has been already called, reset the proxy.
1395   if (mProxy->mWorkerPrivate) {
1396     mProxy->Reset();
1397   }
1398 
1399   mProxy->mWorkerPrivate = mWorkerPrivate;
1400 
1401   MOZ_ASSERT(!mProxy->mSyncLoopTarget);
1402   mProxy->mSyncLoopTarget.swap(mSyncLoopTarget);
1403 
1404   if (mHasUploadListeners) {
1405     // Send() can be called more than once before failure,
1406     // so don't attach the upload listeners more than once.
1407     if (!mProxy->mUploadEventListenersAttached &&
1408         !mProxy->AddRemoveEventListeners(true, true)) {
1409       MOZ_ASSERT(false, "This should never fail!");
1410     }
1411   }
1412 
1413   mProxy->mArrayBufferResponseWasTransferred = false;
1414 
1415   mProxy->mInnerChannelId++;
1416 
1417   mProxy->mXHR->Send(nullptr, payload, aRv);
1418 
1419   if (!aRv.Failed()) {
1420     mProxy->mOutstandingSendCount++;
1421 
1422     if (!mHasUploadListeners) {
1423       // Send() can be called more than once before failure,
1424       // so don't attach the upload listeners more than once.
1425       if (!mProxy->mUploadEventListenersAttached &&
1426           !mProxy->AddRemoveEventListeners(true, true)) {
1427         MOZ_ASSERT(false, "This should never fail!");
1428       }
1429     }
1430   }
1431 }
1432 
XMLHttpRequestWorker(WorkerPrivate * aWorkerPrivate)1433 XMLHttpRequestWorker::XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate)
1434     : WorkerHolder("XMLHttpRequestWorker"),
1435       mWorkerPrivate(aWorkerPrivate),
1436       mResponseType(XMLHttpRequestResponseType::Text),
1437       mTimeout(0),
1438       mRooted(false),
1439       mBackgroundRequest(false),
1440       mWithCredentials(false),
1441       mCanceled(false),
1442       mMozAnon(false),
1443       mMozSystem(false) {
1444   mWorkerPrivate->AssertIsOnWorkerThread();
1445 
1446   mozilla::HoldJSObjects(this);
1447 }
1448 
~XMLHttpRequestWorker()1449 XMLHttpRequestWorker::~XMLHttpRequestWorker() {
1450   mWorkerPrivate->AssertIsOnWorkerThread();
1451 
1452   ReleaseProxy(XHRIsGoingAway);
1453 
1454   MOZ_ASSERT(!mRooted);
1455 
1456   mozilla::DropJSObjects(this);
1457 }
1458 
1459 NS_IMPL_ADDREF_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget)
1460 NS_IMPL_RELEASE_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget)
1461 
1462 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestWorker)
1463 NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget)
1464 
1465 NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestWorker)
1466 
1467 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestWorker,
1468                                                   XMLHttpRequestEventTarget)
1469   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
1470 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1471 
1472 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestWorker,
1473                                                 XMLHttpRequestEventTarget)
1474   tmp->ReleaseProxy(XHRIsGoingAway);
1475   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
1476   tmp->mStateData.mResponse.setUndefined();
1477 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1478 
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestWorker,XMLHttpRequestEventTarget)1479 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestWorker,
1480                                                XMLHttpRequestEventTarget)
1481   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStateData.mResponse)
1482 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1483 
1484 /* static */ already_AddRefed<XMLHttpRequest> XMLHttpRequestWorker::Construct(
1485     const GlobalObject& aGlobal, const MozXMLHttpRequestParameters& aParams,
1486     ErrorResult& aRv) {
1487   JSContext* cx = aGlobal.Context();
1488   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
1489   MOZ_ASSERT(workerPrivate);
1490 
1491   RefPtr<XMLHttpRequestWorker> xhr = new XMLHttpRequestWorker(workerPrivate);
1492 
1493   if (workerPrivate->XHRParamsAllowed()) {
1494     if (aParams.mMozSystem)
1495       xhr->mMozAnon = true;
1496     else
1497       xhr->mMozAnon = aParams.mMozAnon;
1498     xhr->mMozSystem = aParams.mMozSystem;
1499   }
1500 
1501   return xhr.forget();
1502 }
1503 
ReleaseProxy(ReleaseType aType)1504 void XMLHttpRequestWorker::ReleaseProxy(ReleaseType aType) {
1505   // Can't assert that we're on the worker thread here because mWorkerPrivate
1506   // may be gone.
1507 
1508   if (mProxy) {
1509     if (aType == XHRIsGoingAway) {
1510       // We're in a GC finalizer, so we can't do a sync call here (and we don't
1511       // need to).
1512       RefPtr<AsyncTeardownRunnable> runnable =
1513           new AsyncTeardownRunnable(mProxy);
1514       mProxy = nullptr;
1515 
1516       if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
1517         NS_ERROR("Failed to dispatch teardown runnable!");
1518       }
1519     } else {
1520       // This isn't necessary if the worker is going away or the XHR is going
1521       // away.
1522       if (aType == Default) {
1523         // Don't let any more events run.
1524         mProxy->mOuterEventStreamId++;
1525       }
1526 
1527       // We need to make a sync call here.
1528       RefPtr<SyncTeardownRunnable> runnable =
1529           new SyncTeardownRunnable(mWorkerPrivate, mProxy);
1530       mProxy = nullptr;
1531 
1532       IgnoredErrorResult forAssertionsOnly;
1533       // This runnable _must_ be executed.
1534       runnable->Dispatch(Dead, forAssertionsOnly);
1535       MOZ_DIAGNOSTIC_ASSERT(!forAssertionsOnly.Failed());
1536     }
1537   }
1538 }
1539 
MaybePin(ErrorResult & aRv)1540 void XMLHttpRequestWorker::MaybePin(ErrorResult& aRv) {
1541   mWorkerPrivate->AssertIsOnWorkerThread();
1542 
1543   if (mRooted) {
1544     return;
1545   }
1546 
1547   if (!HoldWorker(mWorkerPrivate, Canceling)) {
1548     aRv.Throw(NS_ERROR_FAILURE);
1549     return;
1550   }
1551 
1552   NS_ADDREF_THIS();
1553 
1554   mRooted = true;
1555 }
1556 
MaybeDispatchPrematureAbortEvents(ErrorResult & aRv)1557 void XMLHttpRequestWorker::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv) {
1558   mWorkerPrivate->AssertIsOnWorkerThread();
1559   MOZ_ASSERT(mProxy);
1560 
1561   // Only send readystatechange event when state changed.
1562   bool isStateChanged = false;
1563   if ((mStateData.mReadyState == 1 && mStateData.mFlagSend) ||
1564       mStateData.mReadyState == 2 || mStateData.mReadyState == 3) {
1565     isStateChanged = true;
1566     mStateData.mReadyState = 4;
1567   }
1568 
1569   if (mProxy->mSeenUploadLoadStart) {
1570     MOZ_ASSERT(mUpload);
1571 
1572     DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("abort"), true, aRv);
1573     if (aRv.Failed()) {
1574       return;
1575     }
1576 
1577     DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("loadend"), true,
1578                                 aRv);
1579     if (aRv.Failed()) {
1580       return;
1581     }
1582 
1583     mProxy->mSeenUploadLoadStart = false;
1584   }
1585 
1586   if (mProxy->mSeenLoadStart) {
1587     if (isStateChanged) {
1588       DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("readystatechange"),
1589                                   false, aRv);
1590       if (aRv.Failed()) {
1591         return;
1592       }
1593     }
1594 
1595     DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("abort"), false, aRv);
1596     if (aRv.Failed()) {
1597       return;
1598     }
1599 
1600     DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("loadend"), false, aRv);
1601     if (aRv.Failed()) {
1602       return;
1603     }
1604 
1605     mProxy->mSeenLoadStart = false;
1606   }
1607 }
1608 
DispatchPrematureAbortEvent(EventTarget * aTarget,const nsAString & aEventType,bool aUploadTarget,ErrorResult & aRv)1609 void XMLHttpRequestWorker::DispatchPrematureAbortEvent(
1610     EventTarget* aTarget, const nsAString& aEventType, bool aUploadTarget,
1611     ErrorResult& aRv) {
1612   mWorkerPrivate->AssertIsOnWorkerThread();
1613   MOZ_ASSERT(aTarget);
1614 
1615   if (!mProxy) {
1616     aRv.Throw(NS_ERROR_FAILURE);
1617     return;
1618   }
1619 
1620   RefPtr<Event> event;
1621   if (aEventType.EqualsLiteral("readystatechange")) {
1622     event = NS_NewDOMEvent(aTarget, nullptr, nullptr);
1623     event->InitEvent(aEventType, false, false);
1624   } else {
1625     ProgressEventInit init;
1626     init.mBubbles = false;
1627     init.mCancelable = false;
1628     if (aUploadTarget) {
1629       init.mLengthComputable = mProxy->mLastUploadLengthComputable;
1630       init.mLoaded = mProxy->mLastUploadLoaded;
1631       init.mTotal = mProxy->mLastUploadTotal;
1632     } else {
1633       init.mLengthComputable = mProxy->mLastLengthComputable;
1634       init.mLoaded = mProxy->mLastLoaded;
1635       init.mTotal = mProxy->mLastTotal;
1636     }
1637     event = ProgressEvent::Constructor(aTarget, aEventType, init);
1638   }
1639 
1640   if (!event) {
1641     aRv.Throw(NS_ERROR_FAILURE);
1642     return;
1643   }
1644 
1645   event->SetTrusted(true);
1646 
1647   bool dummy;
1648   aTarget->DispatchEvent(event, &dummy);
1649 }
1650 
Unpin()1651 void XMLHttpRequestWorker::Unpin() {
1652   mWorkerPrivate->AssertIsOnWorkerThread();
1653 
1654   MOZ_ASSERT(mRooted, "Mismatched calls to Unpin!");
1655 
1656   ReleaseWorker();
1657 
1658   mRooted = false;
1659 
1660   NS_RELEASE_THIS();
1661 }
1662 
SendInternal(SendRunnable * aRunnable,ErrorResult & aRv)1663 void XMLHttpRequestWorker::SendInternal(SendRunnable* aRunnable,
1664                                         ErrorResult& aRv) {
1665   MOZ_ASSERT(aRunnable);
1666   mWorkerPrivate->AssertIsOnWorkerThread();
1667 
1668   // No send() calls when open is running.
1669   if (mProxy->mOpenCount) {
1670     aRv.Throw(NS_ERROR_FAILURE);
1671     return;
1672   }
1673 
1674   bool hasUploadListeners = mUpload ? mUpload->HasListeners() : false;
1675 
1676   MaybePin(aRv);
1677   if (aRv.Failed()) {
1678     return;
1679   }
1680 
1681   AutoUnpinXHR autoUnpin(this);
1682   Maybe<AutoSyncLoopHolder> autoSyncLoop;
1683 
1684   nsCOMPtr<nsIEventTarget> syncLoopTarget;
1685   bool isSyncXHR = mProxy->mIsSyncXHR;
1686   if (isSyncXHR) {
1687     autoSyncLoop.emplace(mWorkerPrivate, Terminating);
1688     syncLoopTarget = autoSyncLoop->GetEventTarget();
1689     if (!syncLoopTarget) {
1690       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1691       return;
1692     }
1693   }
1694 
1695   mProxy->mOuterChannelId++;
1696 
1697   aRunnable->SetSyncLoopTarget(syncLoopTarget);
1698   aRunnable->SetHaveUploadListeners(hasUploadListeners);
1699 
1700   mStateData.mFlagSend = true;
1701 
1702   aRunnable->Dispatch(Terminating, aRv);
1703   if (aRv.Failed()) {
1704     // Dispatch() may have spun the event loop and we may have already unrooted.
1705     // If so we don't want autoUnpin to try again.
1706     if (!mRooted) {
1707       autoUnpin.Clear();
1708     }
1709     return;
1710   }
1711 
1712   if (!isSyncXHR) {
1713     autoUnpin.Clear();
1714     MOZ_ASSERT(!autoSyncLoop);
1715     return;
1716   }
1717 
1718   autoUnpin.Clear();
1719 
1720   bool succeeded = autoSyncLoop->Run();
1721   mStateData.mFlagSend = false;
1722 
1723   // Don't clobber an existing exception that we may have thrown on aRv
1724   // already... though can there really be one?  In any case, it seems to me
1725   // that this autoSyncLoop->Run() can never fail, since the StopSyncLoop call
1726   // for it will come from ProxyCompleteRunnable and that always passes true for
1727   // the second arg.
1728   if (!succeeded && !aRv.Failed()) {
1729     aRv.Throw(NS_ERROR_FAILURE);
1730   }
1731 }
1732 
Notify(WorkerStatus aStatus)1733 bool XMLHttpRequestWorker::Notify(WorkerStatus aStatus) {
1734   mWorkerPrivate->AssertIsOnWorkerThread();
1735 
1736   if (aStatus >= Canceling && !mCanceled) {
1737     mCanceled = true;
1738     ReleaseProxy(WorkerIsGoingAway);
1739   }
1740 
1741   return true;
1742 }
1743 
Open(const nsACString & aMethod,const nsAString & aUrl,bool aAsync,const Optional<nsAString> & aUser,const Optional<nsAString> & aPassword,ErrorResult & aRv)1744 void XMLHttpRequestWorker::Open(const nsACString& aMethod,
1745                                 const nsAString& aUrl, bool aAsync,
1746                                 const Optional<nsAString>& aUser,
1747                                 const Optional<nsAString>& aPassword,
1748                                 ErrorResult& aRv) {
1749   mWorkerPrivate->AssertIsOnWorkerThread();
1750 
1751   if (mCanceled) {
1752     aRv.ThrowUncatchableException();
1753     return;
1754   }
1755 
1756   if (mProxy) {
1757     MaybeDispatchPrematureAbortEvents(aRv);
1758     if (aRv.Failed()) {
1759       return;
1760     }
1761   } else {
1762     mProxy = new Proxy(this, mWorkerPrivate->GetClientInfo(),
1763                        mWorkerPrivate->GetController(), mMozAnon, mMozSystem);
1764   }
1765 
1766   mProxy->mOuterEventStreamId++;
1767 
1768   RefPtr<OpenRunnable> runnable = new OpenRunnable(
1769       mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
1770       mBackgroundRequest, mWithCredentials, mTimeout, mResponseType);
1771 
1772   ++mProxy->mOpenCount;
1773   runnable->Dispatch(Terminating, aRv);
1774   if (aRv.Failed()) {
1775     if (mProxy && !--mProxy->mOpenCount) {
1776       ReleaseProxy();
1777     }
1778 
1779     return;
1780   }
1781 
1782   // We have been released in one of the nested Open() calls.
1783   if (!mProxy) {
1784     aRv.Throw(NS_ERROR_FAILURE);
1785     return;
1786   }
1787 
1788   --mProxy->mOpenCount;
1789   mProxy->mIsSyncXHR = !aAsync;
1790 }
1791 
SetRequestHeader(const nsACString & aHeader,const nsACString & aValue,ErrorResult & aRv)1792 void XMLHttpRequestWorker::SetRequestHeader(const nsACString& aHeader,
1793                                             const nsACString& aValue,
1794                                             ErrorResult& aRv) {
1795   mWorkerPrivate->AssertIsOnWorkerThread();
1796 
1797   if (mCanceled) {
1798     aRv.ThrowUncatchableException();
1799     return;
1800   }
1801 
1802   if (!mProxy) {
1803     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1804     return;
1805   }
1806 
1807   RefPtr<SetRequestHeaderRunnable> runnable =
1808       new SetRequestHeaderRunnable(mWorkerPrivate, mProxy, aHeader, aValue);
1809   runnable->Dispatch(Terminating, aRv);
1810 }
1811 
SetTimeout(uint32_t aTimeout,ErrorResult & aRv)1812 void XMLHttpRequestWorker::SetTimeout(uint32_t aTimeout, ErrorResult& aRv) {
1813   mWorkerPrivate->AssertIsOnWorkerThread();
1814 
1815   if (mCanceled) {
1816     aRv.ThrowUncatchableException();
1817     return;
1818   }
1819 
1820   mTimeout = aTimeout;
1821 
1822   if (!mProxy) {
1823     // Open may not have been called yet, in which case we'll handle the
1824     // timeout in OpenRunnable.
1825     return;
1826   }
1827 
1828   RefPtr<SetTimeoutRunnable> runnable =
1829       new SetTimeoutRunnable(mWorkerPrivate, mProxy, aTimeout);
1830   runnable->Dispatch(Terminating, aRv);
1831 }
1832 
SetWithCredentials(bool aWithCredentials,ErrorResult & aRv)1833 void XMLHttpRequestWorker::SetWithCredentials(bool aWithCredentials,
1834                                               ErrorResult& aRv) {
1835   mWorkerPrivate->AssertIsOnWorkerThread();
1836 
1837   if (mCanceled) {
1838     aRv.ThrowUncatchableException();
1839     return;
1840   }
1841 
1842   mWithCredentials = aWithCredentials;
1843 
1844   if (!mProxy) {
1845     // Open may not have been called yet, in which case we'll handle the
1846     // credentials in OpenRunnable.
1847     return;
1848   }
1849 
1850   RefPtr<SetWithCredentialsRunnable> runnable =
1851       new SetWithCredentialsRunnable(mWorkerPrivate, mProxy, aWithCredentials);
1852   runnable->Dispatch(Terminating, aRv);
1853 }
1854 
SetMozBackgroundRequest(bool aBackgroundRequest,ErrorResult & aRv)1855 void XMLHttpRequestWorker::SetMozBackgroundRequest(bool aBackgroundRequest,
1856                                                    ErrorResult& aRv) {
1857   mWorkerPrivate->AssertIsOnWorkerThread();
1858 
1859   if (mCanceled) {
1860     aRv.ThrowUncatchableException();
1861     return;
1862   }
1863 
1864   mBackgroundRequest = aBackgroundRequest;
1865 
1866   if (!mProxy) {
1867     // Open may not have been called yet, in which case we'll handle the
1868     // background request in OpenRunnable.
1869     return;
1870   }
1871 
1872   RefPtr<SetBackgroundRequestRunnable> runnable =
1873       new SetBackgroundRequestRunnable(mWorkerPrivate, mProxy,
1874                                        aBackgroundRequest);
1875   runnable->Dispatch(Terminating, aRv);
1876 }
1877 
GetUpload(ErrorResult & aRv)1878 XMLHttpRequestUpload* XMLHttpRequestWorker::GetUpload(ErrorResult& aRv) {
1879   mWorkerPrivate->AssertIsOnWorkerThread();
1880 
1881   if (mCanceled) {
1882     aRv.ThrowUncatchableException();
1883     return nullptr;
1884   }
1885 
1886   if (!mUpload) {
1887     mUpload = new XMLHttpRequestUpload();
1888 
1889     if (!mUpload) {
1890       aRv.Throw(NS_ERROR_FAILURE);
1891       return nullptr;
1892     }
1893   }
1894 
1895   return mUpload;
1896 }
1897 
Send(JSContext * aCx,const Nullable<DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString> & aData,ErrorResult & aRv)1898 void XMLHttpRequestWorker::Send(
1899     JSContext* aCx,
1900     const Nullable<
1901         DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString>&
1902         aData,
1903     ErrorResult& aRv) {
1904   mWorkerPrivate->AssertIsOnWorkerThread();
1905 
1906   if (mCanceled) {
1907     aRv.ThrowUncatchableException();
1908     return;
1909   }
1910 
1911   if (!mProxy) {
1912     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1913     return;
1914   }
1915 
1916   RefPtr<SendRunnable> sendRunnable;
1917 
1918   if (aData.IsNull()) {
1919     sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, VoidString());
1920     // Nothing to clone.
1921   }
1922 
1923   else if (aData.Value().IsDocument()) {
1924     MOZ_CRASH("Documents are not exposed to workers.");
1925   }
1926 
1927   else if (aData.Value().IsBlob()) {
1928     RefPtr<Blob> blob = &aData.Value().GetAsBlob();
1929     MOZ_ASSERT(blob);
1930 
1931     RefPtr<BlobImpl> blobImpl = blob->Impl();
1932     MOZ_ASSERT(blobImpl);
1933 
1934     aRv = blobImpl->SetMutable(false);
1935     if (NS_WARN_IF(aRv.Failed())) {
1936       return;
1937     }
1938 
1939     JS::Rooted<JS::Value> value(aCx);
1940     if (!GetOrCreateDOMReflector(aCx, blob, &value)) {
1941       aRv.Throw(NS_ERROR_FAILURE);
1942       return;
1943     }
1944 
1945     sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
1946 
1947     sendRunnable->Write(aCx, value, aRv);
1948     if (NS_WARN_IF(aRv.Failed())) {
1949       return;
1950     }
1951   }
1952 
1953   else if (aData.Value().IsArrayBuffer()) {
1954     sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
1955 
1956     JS::Rooted<JS::Value> value(aCx);
1957     value.setObject(*aData.Value().GetAsArrayBuffer().Obj());
1958 
1959     sendRunnable->Write(aCx, value, aRv);
1960     if (NS_WARN_IF(aRv.Failed())) {
1961       return;
1962     }
1963   }
1964 
1965   else if (aData.Value().IsArrayBufferView()) {
1966     const ArrayBufferView& body = aData.Value().GetAsArrayBufferView();
1967 
1968     if (JS_IsTypedArrayObject(body.Obj()) &&
1969         JS_GetTypedArraySharedness(body.Obj())) {
1970       // Throw if the object is mapping shared memory (must opt in).
1971       aRv.ThrowTypeError<MSG_TYPEDARRAY_IS_SHARED>(
1972           NS_LITERAL_STRING("Argument of XMLHttpRequest.send"));
1973       return;
1974     }
1975 
1976     sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
1977 
1978     JS::Rooted<JS::Value> value(aCx);
1979     value.setObject(*body.Obj());
1980 
1981     sendRunnable->Write(aCx, value, aRv);
1982     if (NS_WARN_IF(aRv.Failed())) {
1983       return;
1984     }
1985   }
1986 
1987   else if (aData.Value().IsFormData()) {
1988     JS::Rooted<JS::Value> value(aCx);
1989     if (!GetOrCreateDOMReflector(aCx, &aData.Value().GetAsFormData(), &value)) {
1990       aRv.Throw(NS_ERROR_FAILURE);
1991       return;
1992     }
1993 
1994     sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
1995 
1996     sendRunnable->Write(aCx, value, aRv);
1997     if (NS_WARN_IF(aRv.Failed())) {
1998       return;
1999     }
2000   }
2001 
2002   else if (aData.Value().IsURLSearchParams()) {
2003     JS::Rooted<JS::Value> value(aCx);
2004     if (!GetOrCreateDOMReflector(aCx, &aData.Value().GetAsURLSearchParams(),
2005                                  &value)) {
2006       aRv.Throw(NS_ERROR_FAILURE);
2007       return;
2008     }
2009 
2010     sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
2011 
2012     sendRunnable->Write(aCx, value, aRv);
2013     if (NS_WARN_IF(aRv.Failed())) {
2014       return;
2015     }
2016   }
2017 
2018   else if (aData.Value().IsUSVString()) {
2019     sendRunnable = new SendRunnable(mWorkerPrivate, mProxy,
2020                                     aData.Value().GetAsUSVString());
2021     // Nothing to clone.
2022   }
2023 
2024   MOZ_ASSERT(sendRunnable);
2025   SendInternal(sendRunnable, aRv);
2026 }
2027 
Abort(ErrorResult & aRv)2028 void XMLHttpRequestWorker::Abort(ErrorResult& aRv) {
2029   mWorkerPrivate->AssertIsOnWorkerThread();
2030 
2031   if (mCanceled) {
2032     aRv.ThrowUncatchableException();
2033     return;
2034   }
2035 
2036   if (!mProxy) {
2037     return;
2038   }
2039 
2040   // Set our status to 0 and statusText to "" if we
2041   // will be aborting an ongoing fetch, so the upcoming
2042   // abort events we dispatch have the correct info.
2043   if ((mStateData.mReadyState == XMLHttpRequestBinding::OPENED &&
2044        mStateData.mFlagSend) ||
2045       mStateData.mReadyState == XMLHttpRequestBinding::HEADERS_RECEIVED ||
2046       mStateData.mReadyState == XMLHttpRequestBinding::LOADING ||
2047       mStateData.mReadyState == XMLHttpRequestBinding::DONE) {
2048     mStateData.mStatus = 0;
2049     mStateData.mStatusText.Truncate();
2050   }
2051 
2052   MaybeDispatchPrematureAbortEvents(aRv);
2053   if (aRv.Failed()) {
2054     return;
2055   }
2056 
2057   if (mStateData.mReadyState == 4) {
2058     // No one did anything to us while we fired abort events, so reset our state
2059     // to "unsent"
2060     mStateData.mReadyState = 0;
2061   }
2062 
2063   mProxy->mOuterEventStreamId++;
2064 
2065   RefPtr<AbortRunnable> runnable = new AbortRunnable(mWorkerPrivate, mProxy);
2066   runnable->Dispatch(Terminating, aRv);
2067 }
2068 
GetResponseHeader(const nsACString & aHeader,nsACString & aResponseHeader,ErrorResult & aRv)2069 void XMLHttpRequestWorker::GetResponseHeader(const nsACString& aHeader,
2070                                              nsACString& aResponseHeader,
2071                                              ErrorResult& aRv) {
2072   mWorkerPrivate->AssertIsOnWorkerThread();
2073 
2074   if (mCanceled) {
2075     aRv.ThrowUncatchableException();
2076     return;
2077   }
2078 
2079   if (!mProxy) {
2080     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2081     return;
2082   }
2083 
2084   nsCString responseHeader;
2085   RefPtr<GetResponseHeaderRunnable> runnable = new GetResponseHeaderRunnable(
2086       mWorkerPrivate, mProxy, aHeader, responseHeader);
2087   runnable->Dispatch(Terminating, aRv);
2088   if (aRv.Failed()) {
2089     return;
2090   }
2091   aResponseHeader = responseHeader;
2092 }
2093 
GetAllResponseHeaders(nsACString & aResponseHeaders,ErrorResult & aRv)2094 void XMLHttpRequestWorker::GetAllResponseHeaders(nsACString& aResponseHeaders,
2095                                                  ErrorResult& aRv) {
2096   mWorkerPrivate->AssertIsOnWorkerThread();
2097 
2098   if (mCanceled) {
2099     aRv.ThrowUncatchableException();
2100     return;
2101   }
2102 
2103   if (!mProxy) {
2104     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2105     return;
2106   }
2107 
2108   nsCString responseHeaders;
2109   RefPtr<GetAllResponseHeadersRunnable> runnable =
2110       new GetAllResponseHeadersRunnable(mWorkerPrivate, mProxy,
2111                                         responseHeaders);
2112   runnable->Dispatch(Terminating, aRv);
2113   if (aRv.Failed()) {
2114     return;
2115   }
2116 
2117   aResponseHeaders = responseHeaders;
2118 }
2119 
OverrideMimeType(const nsAString & aMimeType,ErrorResult & aRv)2120 void XMLHttpRequestWorker::OverrideMimeType(const nsAString& aMimeType,
2121                                             ErrorResult& aRv) {
2122   mWorkerPrivate->AssertIsOnWorkerThread();
2123 
2124   if (mCanceled) {
2125     aRv.ThrowUncatchableException();
2126     return;
2127   }
2128 
2129   // We're supposed to throw if the state is not OPENED or HEADERS_RECEIVED. We
2130   // can detect OPENED really easily but we can't detect HEADERS_RECEIVED in a
2131   // non-racy way until the XHR state machine actually runs on this thread
2132   // (bug 671047). For now we're going to let this work only if the Send()
2133   // method has not been called, unless the send has been aborted.
2134   if (!mProxy || (SendInProgress() &&
2135                   (mProxy->mSeenLoadStart || mStateData.mReadyState > 1))) {
2136     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2137     return;
2138   }
2139 
2140   RefPtr<OverrideMimeTypeRunnable> runnable =
2141       new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType);
2142   runnable->Dispatch(Terminating, aRv);
2143 }
2144 
SetResponseType(XMLHttpRequestResponseType aResponseType,ErrorResult & aRv)2145 void XMLHttpRequestWorker::SetResponseType(
2146     XMLHttpRequestResponseType aResponseType, ErrorResult& aRv) {
2147   mWorkerPrivate->AssertIsOnWorkerThread();
2148 
2149   if (mCanceled) {
2150     aRv.ThrowUncatchableException();
2151     return;
2152   }
2153 
2154   // "document" is fine for the main thread but not for a worker. Short-circuit
2155   // that here.
2156   if (aResponseType == XMLHttpRequestResponseType::Document) {
2157     return;
2158   }
2159 
2160   if (!mProxy) {
2161     // Open() has not been called yet. We store the responseType and we will use
2162     // it later in Open().
2163     mResponseType = aResponseType;
2164     return;
2165   }
2166 
2167   if (SendInProgress() &&
2168       (mProxy->mSeenLoadStart || mStateData.mReadyState > 1)) {
2169     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2170     return;
2171   }
2172 
2173   RefPtr<SetResponseTypeRunnable> runnable =
2174       new SetResponseTypeRunnable(mWorkerPrivate, mProxy, aResponseType);
2175   runnable->Dispatch(Terminating, aRv);
2176   if (aRv.Failed()) {
2177     return;
2178   }
2179 
2180   mResponseType = runnable->ResponseType();
2181 }
2182 
GetResponse(JSContext *,JS::MutableHandle<JS::Value> aResponse,ErrorResult & aRv)2183 void XMLHttpRequestWorker::GetResponse(JSContext* /* unused */,
2184                                        JS::MutableHandle<JS::Value> aResponse,
2185                                        ErrorResult& aRv) {
2186   if (NS_SUCCEEDED(mStateData.mResponseTextResult) &&
2187       mStateData.mResponse.isUndefined()) {
2188     MOZ_ASSERT(NS_SUCCEEDED(mStateData.mResponseResult));
2189 
2190     if (mStateData.mResponseText.IsEmpty()) {
2191       mStateData.mResponse =
2192           JS_GetEmptyStringValue(mWorkerPrivate->GetJSContext());
2193     } else {
2194       XMLHttpRequestStringSnapshotReaderHelper helper(mStateData.mResponseText);
2195 
2196       JSString* str = JS_NewUCStringCopyN(mWorkerPrivate->GetJSContext(),
2197                                           helper.Buffer(), helper.Length());
2198 
2199       if (!str) {
2200         aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2201         return;
2202       }
2203 
2204       mStateData.mResponse.setString(str);
2205     }
2206   }
2207 
2208   aRv = mStateData.mResponseResult;
2209   aResponse.set(mStateData.mResponse);
2210 }
2211 
GetResponseText(DOMString & aResponseText,ErrorResult & aRv)2212 void XMLHttpRequestWorker::GetResponseText(DOMString& aResponseText,
2213                                            ErrorResult& aRv) {
2214   aRv = mStateData.mResponseTextResult;
2215   if (aRv.Failed()) {
2216     return;
2217   }
2218 
2219   if (!mStateData.mResponseText.GetAsString(aResponseText)) {
2220     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2221     return;
2222   }
2223 }
2224 
UpdateState(const StateData & aStateData,bool aUseCachedArrayBufferResponse)2225 void XMLHttpRequestWorker::UpdateState(const StateData& aStateData,
2226                                        bool aUseCachedArrayBufferResponse) {
2227   if (aUseCachedArrayBufferResponse) {
2228     MOZ_ASSERT(mStateData.mResponse.isObject() &&
2229                JS_IsArrayBufferObject(&mStateData.mResponse.toObject()));
2230 
2231     JS::Rooted<JS::Value> response(mWorkerPrivate->GetJSContext(),
2232                                    mStateData.mResponse);
2233     mStateData = aStateData;
2234     mStateData.mResponse = response;
2235   } else {
2236     mStateData = aStateData;
2237   }
2238 
2239   XMLHttpRequestBinding::ClearCachedResponseTextValue(this);
2240 }
2241 
2242 }  // namespace dom
2243 }  // namespace mozilla
2244