tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

XMLHttpRequestWorker.cpp (73202B)


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