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