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