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