Bug 792808 - expose XHR state constants as XMLHttpRequest::State enum, and use them fully-qualified in the subclasses; r?baku draft
authorThomas Wisniewski <wisniewskit@gmail.com>
Thu, 28 Sep 2017 11:01:21 -0400
changeset 672337 3a220a3fe327be7631c05bdd5dace8ad0572126d
parent 672336 1ea2e667faf0cbf257f1a739d032f61b3664ff96
child 733772 a28b2f65a436bddc1f5bf1db59268c6491bbe53d
push id82215
push userwisniewskit@gmail.com
push dateFri, 29 Sep 2017 02:06:27 +0000
reviewersbaku
bugs792808
milestone58.0a1
Bug 792808 - expose XHR state constants as XMLHttpRequest::State enum, and use them fully-qualified in the subclasses; r?baku MozReview-Commit-ID: LcVPQ35Ivw6
dom/xhr/XMLHttpRequest.h
dom/xhr/XMLHttpRequestMainThread.cpp
dom/xhr/XMLHttpRequestMainThread.h
dom/xhr/XMLHttpRequestWorker.cpp
dom/xhr/XMLHttpRequestWorker.h
--- a/dom/xhr/XMLHttpRequest.h
+++ b/dom/xhr/XMLHttpRequest.h
@@ -20,16 +20,26 @@ class Blob;
 class DOMString;
 class FormData;
 class URLSearchParams;
 class XMLHttpRequestUpload;
 
 class XMLHttpRequest : public XMLHttpRequestEventTarget
 {
 public:
+  // XHR states are meant to mirror the XHR2 spec:
+  //   https://xhr.spec.whatwg.org/#states
+  enum class State : uint8_t {
+    unsent,           // object has been constructed.
+    opened,           // open() has been successfully invoked.
+    headers_received, // redirects followed and response headers received.
+    loading,          // response body is being received.
+    done,             // data transfer concluded, whether success or error.
+  };
+
   static already_AddRefed<XMLHttpRequest>
   Constructor(const GlobalObject& aGlobal,
               const MozXMLHttpRequestParameters& aParams,
               ErrorResult& aRv);
 
   static already_AddRefed<XMLHttpRequest>
   Constructor(const GlobalObject& aGlobal, const nsAString& ignored,
               ErrorResult& aRv)
@@ -44,16 +54,19 @@ public:
     return Constructor(aGlobal, params, aRv);
   }
 
   IMPL_EVENT_HANDLER(readystatechange)
 
   virtual uint16_t
   ReadyState() const = 0;
 
+  virtual State
+  GetReadyState() const = 0;
+
   virtual void
   Open(const nsACString& aMethod, const nsAString& aUrl, ErrorResult& aRv) = 0;
 
   virtual void
   Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync,
        const nsAString& aUser, const nsAString& aPassword, ErrorResult& aRv) = 0;
 
   virtual void
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -181,17 +181,17 @@ static void AddLoadFlags(nsIRequest *req
 bool
 XMLHttpRequestMainThread::sDontWarnAboutSyncXHR = false;
 
 XMLHttpRequestMainThread::XMLHttpRequestMainThread()
   : mResponseBodyDecodedPos(0),
     mResponseCharset(nullptr),
     mResponseType(XMLHttpRequestResponseType::_empty),
     mRequestObserver(nullptr),
-    mState(State::unsent),
+    mState(XMLHttpRequest::State::unsent),
     mStyleBackend(StyleBackendType::None),
     mFlagSynchronous(false), mFlagAborted(false), mFlagParseBody(false),
     mFlagSyncLooping(false), mFlagBackgroundRequest(false),
     mFlagHadUploadListenersOnSend(false), mFlagACwithCredentials(false),
     mFlagTimedOut(false), mFlagDeleted(false), mFlagSend(false),
     mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
     mProgressSinceLastProgressEvent(false),
     mRequestSentTime(0), mTimeoutMilliseconds(0),
@@ -213,18 +213,18 @@ XMLHttpRequestMainThread::XMLHttpRequest
 {
   mozilla::HoldJSObjects(this);
 }
 
 XMLHttpRequestMainThread::~XMLHttpRequestMainThread()
 {
   mFlagDeleted = true;
 
-  if ((mState == State::opened && mFlagSend) ||
-      mState == State::loading) {
+  if ((mState == XMLHttpRequest::State::opened && mFlagSend) ||
+      mState == XMLHttpRequest::State::loading) {
     Abort();
   }
 
   mParseEndListener = nullptr;
 
   MOZ_ASSERT(!mFlagSyncLooping, "we rather crash than hang");
   mFlagSyncLooping = false;
 
@@ -430,17 +430,17 @@ XMLHttpRequestMainThread::GetResponseXML
       mResponseType != XMLHttpRequestResponseType::Document) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSEXML);
     return nullptr;
   }
   if (mWarnAboutSyncHtml) {
     mWarnAboutSyncHtml = false;
     LogMessage("HTMLSyncXHRWarning", GetOwner());
   }
-  if (mState != State::done) {
+  if (mState != XMLHttpRequest::State::done) {
     return nullptr;
   }
   return mResponseXML;
 }
 
 /*
  * This piece copied from XMLDocument, we try to get the charset
  * from HTTP headers.
@@ -544,17 +544,18 @@ XMLHttpRequestMainThread::GetResponseTex
   aSnapshot.Reset();
 
   if (mResponseType != XMLHttpRequestResponseType::_empty &&
       mResponseType != XMLHttpRequestResponseType::Text) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT);
     return;
   }
 
-  if (mState != State::loading && mState != State::done) {
+  if (mState != XMLHttpRequest::State::loading &&
+      mState != XMLHttpRequest::State::done) {
     return;
   }
 
   // Main Fetch step 18 requires to ignore body for head/connect methods.
   if (mRequestMethod.EqualsLiteral("HEAD") ||
       mRequestMethod.EqualsLiteral("CONNECT")) {
     return;
   }
@@ -575,17 +576,17 @@ XMLHttpRequestMainThread::GetResponseTex
   aRv = AppendToResponseText(mResponseBody.get() + mResponseBodyDecodedPos,
                              mResponseBody.Length() - mResponseBodyDecodedPos);
   if (aRv.Failed()) {
     return;
   }
 
   mResponseBodyDecodedPos = mResponseBody.Length();
 
-  if (mState == State::done) {
+  if (mState == XMLHttpRequest::State::done) {
     // Free memory buffer which we no longer need
     mResponseBody.Truncate();
     mResponseBodyDecodedPos = 0;
   }
 
   mResponseText.CreateSnapshot(aSnapshot);
 }
 
@@ -610,23 +611,25 @@ XMLHttpRequestMainThread::CreateResponse
   mResultJSON = value;
   return NS_OK;
 }
 
 void
 XMLHttpRequestMainThread::SetResponseType(XMLHttpRequestResponseType aResponseType,
                                           ErrorResult& aRv)
 {
-  if (mState == State::loading || mState == State::done) {
+  if (mState == XMLHttpRequest::State::loading ||
+      mState == XMLHttpRequest::State::done) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
     return;
   }
 
   // sync request is not allowed setting responseType in window context
-  if (HasOrHasHadOwner() && mState != State::unsent && mFlagSynchronous) {
+  if (HasOrHasHadOwner() &&
+      mState != XMLHttpRequest::State::unsent && mFlagSynchronous) {
     LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC);
     return;
   }
 
   if (mFlagSynchronous &&
       aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC);
@@ -661,17 +664,17 @@ XMLHttpRequestMainThread::GetResponse(JS
     }
     return;
   }
 
   case XMLHttpRequestResponseType::Arraybuffer:
   case XMLHttpRequestResponseType::Moz_chunked_arraybuffer:
   {
     if (!(mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
-          mState == State::done) &&
+          mState == XMLHttpRequest::State::done) &&
         !(mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer &&
           mInLoadProgressEvent)) {
       aResponse.setNull();
       return;
     }
 
     if (!mResultArrayBuffer) {
       mResultArrayBuffer = mArrayBufferBuilder.getArrayBuffer(aCx);
@@ -680,42 +683,42 @@ XMLHttpRequestMainThread::GetResponse(JS
         return;
       }
     }
     aResponse.setObject(*mResultArrayBuffer);
     return;
   }
   case XMLHttpRequestResponseType::Blob:
   {
-    if (mState != State::done) {
+    if (mState != XMLHttpRequest::State::done) {
       aResponse.setNull();
       return;
     }
 
     if (!mResponseBlob) {
       aResponse.setNull();
       return;
     }
 
     GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse);
     return;
   }
   case XMLHttpRequestResponseType::Document:
   {
-    if (!mResponseXML || mState != State::done) {
+    if (!mResponseXML || mState != XMLHttpRequest::State::done) {
       aResponse.setNull();
       return;
     }
 
     aRv = nsContentUtils::WrapNative(aCx, mResponseXML, aResponse);
     return;
   }
   case XMLHttpRequestResponseType::Json:
   {
-    if (mState != State::done) {
+    if (mState != XMLHttpRequest::State::done) {
       aResponse.setNull();
       return;
     }
 
     if (mResultJSON.isUndefined()) {
       aRv = CreateResponseParsedJSON(aCx);
       TruncateResponseText();
       if (aRv.Failed()) {
@@ -767,17 +770,18 @@ XMLHttpRequestMainThread::IsDeniedCrossS
   return false;
 }
 
 void
 XMLHttpRequestMainThread::GetResponseURL(nsAString& aUrl)
 {
   aUrl.Truncate();
 
-  if ((mState == State::unsent || mState == State::opened) || !mChannel) {
+  if ((mState == XMLHttpRequest::State::unsent ||
+       mState == XMLHttpRequest::State::opened) || !mChannel) {
     return;
   }
 
   // Make sure we don't leak responseURL information from denied cross-site
   // requests.
   if (IsDeniedCrossSiteCORSRequest()) {
     return;
   }
@@ -798,17 +802,18 @@ uint32_t
 XMLHttpRequestMainThread::GetStatus(ErrorResult& aRv)
 {
   // Make sure we don't leak status information from denied cross-site
   // requests.
   if (IsDeniedCrossSiteCORSRequest()) {
     return 0;
   }
 
-  if (mState == State::unsent || mState == State::opened) {
+  if (mState == XMLHttpRequest::State::unsent ||
+      mState == XMLHttpRequest::State::opened) {
     return 0;
   }
 
   if (mErrorLoad != ErrorType::eOK) {
     // Let's simulate the http protocol for jar/app requests:
     nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
     if (jarChannel) {
       nsresult status;
@@ -851,17 +856,18 @@ XMLHttpRequestMainThread::GetStatusText(
   if (IsDeniedCrossSiteCORSRequest()) {
     return;
   }
 
   // Check the current XHR state to see if it is valid to obtain the statusText
   // value.  This check is to prevent the status text for redirects from being
   // available before all the redirects have been followed and HTTP headers have
   // been received.
-  if (mState == State::unsent || mState == State::opened) {
+  if (mState == XMLHttpRequest::State::unsent ||
+      mState == XMLHttpRequest::State::opened) {
     return;
   }
 
   if (mErrorLoad != ErrorType::eOK) {
     return;
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
@@ -869,18 +875,19 @@ XMLHttpRequestMainThread::GetStatusText(
     Unused << httpChannel->GetResponseStatusText(aStatusText);
   } else {
     aStatusText.AssignLiteral("OK");
   }
 }
 
 void
 XMLHttpRequestMainThread::TerminateOngoingFetch() {
-  if ((mState == State::opened && mFlagSend) ||
-      mState == State::headers_received || mState == State::loading) {
+  if ((mState == XMLHttpRequest::State::opened && mFlagSend) ||
+      mState == XMLHttpRequest::State::headers_received ||
+      mState == XMLHttpRequest::State::loading) {
     CloseRequest();
   }
 }
 
 void
 XMLHttpRequestMainThread::CloseRequest()
 {
   mWaitingForOnStopRequest = false;
@@ -901,47 +908,47 @@ XMLHttpRequestMainThread::CloseRequestWi
   ResetResponse();
 
   // If we're in the destructor, don't risk dispatching an event.
   if (mFlagDeleted) {
     mFlagSyncLooping = false;
     return;
   }
 
-  if (mState != State::unsent &&
-      !(mState == State::opened && !mFlagSend) &&
-      mState != State::done) {
-    ChangeState(State::done, true);
+  if (mState != XMLHttpRequest::State::unsent &&
+      !(mState == XMLHttpRequest::State::opened && !mFlagSend) &&
+      mState != XMLHttpRequest::State::done) {
+    ChangeState(XMLHttpRequest::State::done, true);
 
     if (!mFlagSyncLooping) {
       if (mUpload && !mUploadComplete) {
         mUploadComplete = true;
         DispatchProgressEvent(mUpload, aType, 0, -1);
       }
       DispatchProgressEvent(this, aType, 0, -1);
     }
   }
 
   // The ChangeState call above calls onreadystatechange handlers which
   // if they load a new url will cause XMLHttpRequestMainThread::Open to clear
   // the abort state bit. If this occurs we're not uninitialized (bug 361773).
   if (mFlagAborted) {
-    ChangeState(State::unsent, false);  // IE seems to do it
+    ChangeState(XMLHttpRequest::State::unsent, false);  // IE seems to do it
   }
 
   mFlagSyncLooping = false;
 }
 
 void
 XMLHttpRequestMainThread::RequestErrorSteps(const ProgressEventType aEventType,
                                             const nsresult aOptionalException,
                                             ErrorResult& aRv)
 {
   // Step 1
-  mState = State::done;
+  mState = XMLHttpRequest::State::done;
 
   StopProgressEventTimer();
 
   // Step 2
   mFlagSend = false;
 
   // Step 3
   ResetResponse();
@@ -983,25 +990,25 @@ void
 XMLHttpRequestMainThread::Abort(ErrorResult& aRv)
 {
   mFlagAborted = true;
 
   // Step 1
   TerminateOngoingFetch();
 
   // Step 2
-  if ((mState == State::opened && mFlagSend) ||
-       mState == State::headers_received ||
-       mState == State::loading) {
+  if ((mState == XMLHttpRequest::State::opened && mFlagSend) ||
+       mState == XMLHttpRequest::State::headers_received ||
+       mState == XMLHttpRequest::State::loading) {
     RequestErrorSteps(ProgressEventType::abort, NS_OK, aRv);
   }
 
   // Step 3
-  if (mState == State::done) {
-    ChangeState(State::unsent, false); // no ReadystateChange event
+  if (mState == XMLHttpRequest::State::done) {
+    ChangeState(XMLHttpRequest::State::unsent, false); // no ReadystateChange event
   }
 
   mFlagSyncLooping = false;
 }
 
 /*Method that checks if it is safe to expose a header value to the client.
 It is used to check what headers are exposed for CORS requests.*/
 bool
@@ -1062,17 +1069,18 @@ XMLHttpRequestMainThread::IsSafeHeader(c
 void
 XMLHttpRequestMainThread::GetAllResponseHeaders(nsACString& aResponseHeaders,
                                                 ErrorResult& aRv)
 {
   aResponseHeaders.Truncate();
 
   // If the state is UNSENT or OPENED,
   // return the empty string and terminate these steps.
-  if (mState == State::unsent || mState == State::opened) {
+  if (mState == XMLHttpRequest::State::unsent ||
+      mState == XMLHttpRequest::State::opened) {
     return;
   }
 
   if (mErrorLoad != ErrorType::eOK) {
     return;
   }
 
   if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) {
@@ -1121,17 +1129,18 @@ XMLHttpRequestMainThread::GetResponseHea
 {
   _retval.SetIsVoid(true);
 
   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
 
   if (!httpChannel) {
     // If the state is UNSENT or OPENED,
     // return null and terminate these steps.
-    if (mState == State::unsent || mState == State::opened) {
+    if (mState == XMLHttpRequest::State::unsent ||
+        mState == XMLHttpRequest::State::opened) {
       return;
     }
 
     // Even non-http channels supply content type and content length.
     // Remember we don't leak header information from denied cross-site
     // requests.
     nsresult status;
     if (!mChannel ||
@@ -1198,17 +1207,17 @@ XMLHttpRequestMainThread::GetLoadGroup()
   }
 
   return nullptr;
 }
 
 nsresult
 XMLHttpRequestMainThread::FireReadystatechangeEvent()
 {
-  MOZ_ASSERT(mState != State::unsent);
+  MOZ_ASSERT(mState != XMLHttpRequest::State::unsent);
   RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
   event->InitEvent(kLiteralString_readystatechange, false, false);
   // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
   event->SetTrusted(true);
   DispatchOrStoreEvent(this, event);
   return NS_OK;
 }
 
@@ -1332,17 +1341,17 @@ XMLHttpRequestMainThread::IsSystemXHR() 
 {
   return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal);
 }
 
 bool
 XMLHttpRequestMainThread::InUploadPhase() const
 {
   // We're in the upload phase while our state is State::opened.
-  return mState == State::opened;
+  return mState == XMLHttpRequest::State::opened;
 }
 
 // This case is hit when the async parameter is outright omitted, which
 // should set it to true (and the username and password to null).
 void
 XMLHttpRequestMainThread::Open(const nsACString& aMethod, const nsAString& aUrl,
                                ErrorResult& aRv)
 {
@@ -1468,28 +1477,28 @@ XMLHttpRequestMainThread::Open(const nsA
 
   // Per spec we should only create the channel on send(), but we have internal
   // code that relies on the channel being created now, and that code is not
   // always IsSystemXHR(). However, we're not supposed to throw channel-creation
   // errors during open(), so we silently ignore those here.
   CreateChannel();
 
   // Step 12
-  if (mState != State::opened) {
-    mState = State::opened;
+  if (mState != XMLHttpRequest::State::opened) {
+    mState = XMLHttpRequest::State::opened;
     FireReadystatechangeEvent();
   }
 
   return NS_OK;
 }
 
 void
 XMLHttpRequestMainThread::SetOriginAttributes(const OriginAttributesDictionary& aAttrs)
 {
-  MOZ_ASSERT((mState == State::opened) && !mFlagSend);
+  MOZ_ASSERT((mState == XMLHttpRequest::State::opened) && !mFlagSend);
 
   OriginAttributes attrs(aAttrs);
 
   nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
   MOZ_ASSERT(loadInfo);
   if (loadInfo) {
     loadInfo->SetOriginAttributes(attrs);
   }
@@ -1684,17 +1693,17 @@ private:
 
 NS_IMPL_ISUPPORTS0(FileCreationHandler)
 
 } // namespace
 
 void
 XMLHttpRequestMainThread::LocalFileToBlobCompleted(Blob* aBlob)
 {
-  MOZ_ASSERT(mState != State::done);
+  MOZ_ASSERT(mState != XMLHttpRequest::State::done);
 
   mResponseBlob = aBlob;
   mBlobStorage = nullptr;
   NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
 
   ChangeStateToDone();
 }
 
@@ -1727,33 +1736,33 @@ XMLHttpRequestMainThread::OnDataAvailabl
 
       // The nsIStreamListener contract mandates us to read from the stream
       // before returning.
       uint32_t totalRead;
       rv =
         inStr->ReadSegments(DummyStreamReaderFunc, nullptr, count, &totalRead);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      ChangeState(State::loading);
+      ChangeState(XMLHttpRequest::State::loading);
 
       // Cancel() must be called with an error. We use
       // NS_ERROR_FILE_ALREADY_EXISTS to know that we've aborted the operation
       // just because we can retrieve the File from the channel directly.
       return request->Cancel(NS_ERROR_FILE_ALREADY_EXISTS);
     }
   }
 
   uint32_t totalRead;
   rv = inStr->ReadSegments(XMLHttpRequestMainThread::StreamReaderFunc,
                            (void*)this, count, &totalRead);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Fire the first progress event/loading state change
-  if (mState == State::headers_received) {
-    ChangeState(State::loading);
+  if (mState == XMLHttpRequest::State::headers_received) {
+    ChangeState(XMLHttpRequest::State::loading);
     if (!mFlagSynchronous) {
       DispatchProgressEvent(this, ProgressEventType::progress,
                             mLoadTransferred, mLoadTotal);
     }
     mProgressSinceLastProgressEvent = false;
   }
 
   if (!mFlagSynchronous && !mProgressTimerIsActive) {
@@ -1775,17 +1784,17 @@ XMLHttpRequestMainThread::OnStartRequest
   }
 
   if (request != mChannel) {
     // Can this still happen?
     return NS_OK;
   }
 
   // Don't do anything if we have been aborted
-  if (mState == State::unsent) {
+  if (mState == XMLHttpRequest::State::unsent) {
     return NS_OK;
   }
 
   /* Apparently, Abort() should set State::unsent.  See bug 361773.
      XHR2 spec says this is correct. */
   if (mFlagAborted) {
     NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
 
@@ -1821,17 +1830,17 @@ XMLHttpRequestMainThread::OnStartRequest
 
     mUploadComplete = true;
     DispatchProgressEvent(mUpload, ProgressEventType::load,
                           mUploadTotal, mUploadTotal);
   }
 
   mContext = ctxt;
   mFlagParseBody = true;
-  ChangeState(State::headers_received);
+  ChangeState(XMLHttpRequest::State::headers_received);
 
   ResetResponse();
 
   if (!mOverrideMimeType.IsEmpty()) {
     channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
   }
 
   // Fallback to 'application/octet-stream'
@@ -2052,17 +2061,17 @@ XMLHttpRequestMainThread::OnStopRequest(
     NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!");
     mFirstStartRequestSeen = false;
     mRequestObserver->OnStopRequest(request, ctxt, status);
   }
 
   // make sure to notify the listener if we were aborted
   // XXX in fact, why don't we do the cleanup below in this case??
   // State::unsent is for abort calls.  See OnStartRequest above.
-  if (mState == State::unsent || mFlagTimedOut) {
+  if (mState == XMLHttpRequest::State::unsent || mFlagTimedOut) {
     if (mXMLParserStreamListener)
       (void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
     return NS_OK;
   }
 
   // Is this good enough here?
   if (mXMLParserStreamListener && mFlagParseBody) {
     mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
@@ -2161,17 +2170,18 @@ XMLHttpRequestMainThread::OnStopRequest(
 
     mErrorLoad = ErrorType::eUnreachable;
     mResponseXML = nullptr;
   }
 
   // If we're uninitialized at this point, we encountered an error
   // earlier and listeners have already been notified. Also we do
   // not want to do this if we already completed.
-  if (mState == State::unsent || mState == State::done) {
+  if (mState == XMLHttpRequest::State::unsent ||
+      mState == XMLHttpRequest::State::done) {
     return NS_OK;
   }
 
   if (!mResponseXML) {
     mFlagParseBody = false;
 
     //We postpone the 'done' until the creation of the Blob is completed.
     if (!waitingForBlobCreation) {
@@ -2247,17 +2257,17 @@ XMLHttpRequestMainThread::ChangeStateToD
   if (!mFlagSynchronous &&
       (!mLoadTransferred || mProgressSinceLastProgressEvent)) {
     DispatchProgressEvent(this, ProgressEventType::progress,
                           mLoadTransferred, mLoadTotal);
     mProgressSinceLastProgressEvent = false;
   }
 
   // Per spec, fire readystatechange=4/done before final error events.
-  ChangeState(State::done, true);
+  ChangeState(XMLHttpRequest::State::done, true);
 
   // Per spec, if we failed in the upload phase, fire a final error
   // and loadend events for the upload after readystatechange=4/done.
   if (!mFlagSynchronous && mUpload && !mUploadComplete) {
     DispatchProgressEvent(mUpload, ProgressEventType::error, 0, -1);
   }
 
   // Per spec, fire download's load/error and loadend events after
@@ -2626,17 +2636,17 @@ XMLHttpRequestMainThread::InitiateFetch(
     // ref to us to be extra safe.
     mChannel->SetNotificationCallbacks(mNotificationCallbacks);
     mChannel = nullptr;
 
     mErrorLoad = ErrorType::eChannelOpen;
 
     // Per spec, we throw on sync errors, but not async.
     if (mFlagSynchronous) {
-      mState = State::done;
+      mState = XMLHttpRequest::State::done;
       return NS_ERROR_DOM_NETWORK_ERR;
     }
   }
 
   return NS_OK;
 }
 
 void
@@ -2714,17 +2724,17 @@ XMLHttpRequestMainThread::Send(JSContext
 nsresult
 XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase* aBody)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
 
   // Step 1
-  if (mState != State::opened) {
+  if (mState != XMLHttpRequest::State::opened) {
     return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED;
   }
 
   // Step 2
   if (mFlagSend) {
     return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
   }
 
@@ -2887,17 +2897,17 @@ XMLHttpRequestMainThread::SendInternal(c
       DispatchProgressEvent(mUpload, ProgressEventType::loadstart,
                             0, mUploadTotal);
     }
   }
 
   if (!mChannel) {
     // Per spec, silently fail on async request failures; throw for sync.
     if (mFlagSynchronous) {
-      mState = State::done;
+      mState = XMLHttpRequest::State::done;
       return NS_ERROR_DOM_NETWORK_ERR;
     } else {
       // Defer the actual sending of async events just in case listeners
       // are attached after the send() method is called.
       return DispatchToMainThread(NewRunnableMethod<ProgressEventType>(
         "dom::XMLHttpRequestMainThread::CloseRequestWithError",
         this,
         &XMLHttpRequestMainThread::CloseRequestWithError,
@@ -2944,17 +2954,17 @@ XMLHttpRequestMainThread::IsLowercaseRes
 
 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
 void
 XMLHttpRequestMainThread::SetRequestHeader(const nsACString& aName,
                                            const nsACString& aValue,
                                            ErrorResult& aRv)
 {
   // Step 1
-  if (mState != State::opened) {
+  if (mState != XMLHttpRequest::State::opened) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED);
     return;
   }
 
   // Step 2
   if (mFlagSend) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING);
     return;
@@ -2992,17 +3002,18 @@ XMLHttpRequestMainThread::SetRequestHead
   } else {
     mAuthorRequestHeaders.MergeOrSet(aName, value);
   }
 }
 
 void
 XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
 {
-  if (mFlagSynchronous && mState != State::unsent && HasOrHasHadOwner()) {
+  if (mFlagSynchronous && mState != XMLHttpRequest::State::unsent &&
+      HasOrHasHadOwner()) {
     /* Timeout is not supported for synchronous requests with an owning window,
        per XHR2 spec. */
     LogMessage("TimeoutSyncXHRWarning", GetOwner());
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC);
     return;
   }
 
   mTimeoutMilliseconds = aTimeout;
@@ -3033,17 +3044,17 @@ XMLHttpRequestMainThread::DispatchToMain
   return NS_DispatchToMainThread(Move(aRunnable));
 }
 
 void
 XMLHttpRequestMainThread::StartTimeoutTimer()
 {
   MOZ_ASSERT(mRequestSentTime,
              "StartTimeoutTimer mustn't be called before the request was sent!");
-  if (mState == State::done) {
+  if (mState == XMLHttpRequest::State::done) {
     // do nothing!
     return;
   }
 
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
   }
 
@@ -3059,25 +3070,32 @@ XMLHttpRequestMainThread::StartTimeoutTi
     (uint32_t)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC);
   mTimeoutTimer->InitWithCallback(
     this,
     mTimeoutMilliseconds > elapsed ? mTimeoutMilliseconds - elapsed : 0,
     nsITimer::TYPE_ONE_SHOT
   );
 }
 
+XMLHttpRequest::State
+XMLHttpRequestMainThread::GetReadyState() const
+{
+  return mState;
+}
+
 uint16_t
 XMLHttpRequestMainThread::ReadyState() const
 {
   return static_cast<uint16_t>(mState);
 }
 
 void XMLHttpRequestMainThread::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
 {
-  if (mState == State::loading || mState == State::done) {
+  if (mState == XMLHttpRequest::State::loading ||
+      mState == XMLHttpRequest::State::done) {
     ResetResponse();
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
     return;
   }
 
   mOverrideMimeType = aMimeType;
 }
 
@@ -3089,17 +3107,17 @@ XMLHttpRequestMainThread::MozBackgroundR
 
 nsresult
 XMLHttpRequestMainThread::SetMozBackgroundRequest(bool aMozBackgroundRequest)
 {
   if (!IsSystemXHR()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  if (mState != State::unsent) {
+  if (mState != XMLHttpRequest::State::unsent) {
     // Can't change this while we're in the middle of something.
     return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
   }
 
   mFlagBackgroundRequest = aMozBackgroundRequest;
 
   return NS_OK;
 }
@@ -3120,39 +3138,41 @@ XMLHttpRequestMainThread::WithCredential
 
 void
 XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
 {
   // Return error if we're already processing a request.  Note that we can't use
   // ReadyState() here, because it can't differentiate between "opened" and
   // "sent", so we use mState directly.
 
-  if ((mState != State::unsent && mState != State::opened) ||
+  if ((mState != XMLHttpRequest::State::unsent &&
+       mState != XMLHttpRequest::State::opened) ||
       mFlagSend || mIsAnon) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING);
     return;
   }
 
   mFlagACwithCredentials = aWithCredentials;
 }
 
 nsresult
 XMLHttpRequestMainThread::ChangeState(State aState, bool aBroadcast)
 {
   mState = aState;
   nsresult rv = NS_OK;
 
-  if (aState != State::headers_received && aState != State::loading) {
+  if (aState != XMLHttpRequest::State::headers_received &&
+      aState != XMLHttpRequest::State::loading) {
     StopProgressEventTimer();
   }
 
 
   if (aBroadcast && (!mFlagSynchronous ||
-                     aState == State::opened ||
-                     aState == State::done)) {
+                     aState == XMLHttpRequest::State::opened ||
+                     aState == XMLHttpRequest::State::done)) {
     rv = FireReadystatechangeEvent();
   }
 
   return rv;
 }
 
 /////////////////////////////////////////////////////
 // nsIChannelEventSink methods:
@@ -3375,17 +3395,17 @@ bool
 XMLHttpRequestMainThread::MozSystem() const
 {
   return IsSystemXHR();
 }
 
 void
 XMLHttpRequestMainThread::HandleTimeoutCallback()
 {
-  if (mState == State::done) {
+  if (mState == XMLHttpRequest::State::done) {
     NS_NOTREACHED("XMLHttpRequestMainThread::HandleTimeoutCallback with completed request");
     // do nothing!
     return;
   }
 
   mFlagTimedOut = true;
   CloseRequestWithError(ProgressEventType::timeout);
 }
@@ -3617,17 +3637,17 @@ void
 XMLHttpRequestMainThread::BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
                                              Blob* aBlob, nsresult aRv)
 {
   // Ok, the state is changed...
   if (mBlobStorage != aBlobStorage || NS_FAILED(aRv)) {
     return;
   }
 
-  MOZ_ASSERT(mState != State::done);
+  MOZ_ASSERT(mState != XMLHttpRequest::State::done);
 
   mResponseBlob = aBlob;
   mBlobStorage = nullptr;
 
   ChangeStateToDone();
 }
 
 NS_IMETHODIMP
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -246,16 +246,17 @@ public:
   // nsISizeOfEventTarget
   virtual size_t
     SizeOfEventTargetIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(XMLHttpRequestEventTarget)
 
   // states
   virtual uint16_t ReadyState() const override;
+  virtual State GetReadyState() const override;
 
   // request
   nsresult CreateChannel();
   nsresult InitiateFetch(nsIInputStream* aUploadStream,
                          int64_t aUploadLength,
                          nsACString& aUploadContentType);
 
   virtual void
@@ -495,26 +496,16 @@ public:
   void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
                           Blob* aBlob,
                           nsresult aResult) override;
 
   void
   LocalFileToBlobCompleted(Blob* aBlob);
 
 protected:
-  // XHR states are meant to mirror the XHR2 spec:
-  //   https://xhr.spec.whatwg.org/#states
-  enum class State : uint8_t {
-    unsent,           // object has been constructed.
-    opened,           // open() has been successfully invoked.
-    headers_received, // redirects followed and response headers received.
-    loading,          // response body is being received.
-    done,             // data transfer concluded, whether success or error.
-  };
-
   nsresult DetectCharset();
   nsresult AppendToResponseText(const char * aBuffer, uint32_t aBufferLen);
   static nsresult StreamReaderFunc(nsIInputStream* in,
                                    void* closure,
                                    const char* fromRawSegment,
                                    uint32_t toOffset,
                                    uint32_t count,
                                    uint32_t *writeCount);
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -482,17 +482,17 @@ class EventRunnable final : public MainT
   JS::Heap<JS::Value> mResponse;
   XMLHttpRequestStringSnapshot mResponseText;
   nsString mResponseURL;
   nsCString mStatusText;
   uint64_t mLoaded;
   uint64_t mTotal;
   uint32_t mEventStreamId;
   uint32_t mStatus;
-  uint16_t mReadyState;
+  XMLHttpRequest::State mReadyState;
   bool mUploadEvent;
   bool mProgressEvent;
   bool mLengthComputable;
   bool mUseCachedArrayBufferResponse;
   nsresult mResponseTextResult;
   nsresult mStatusResult;
   nsresult mResponseResult;
   // mScopeObj is used in PreDispatch only.  We init it in our constructor, and
@@ -504,30 +504,32 @@ public:
   EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
                 bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal,
                 JS::Handle<JSObject*> aScopeObj)
   : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
     StructuredCloneHolder(CloningSupported, TransferringNotSupported,
                           StructuredCloneScope::SameProcessDifferentThread),
     mType(aType), mResponse(JS::UndefinedValue()), mLoaded(aLoaded),
     mTotal(aTotal), mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0),
-    mReadyState(0), mUploadEvent(aUploadEvent), mProgressEvent(true),
-    mLengthComputable(aLengthComputable), mUseCachedArrayBufferResponse(false),
-    mResponseTextResult(NS_OK), mStatusResult(NS_OK), mResponseResult(NS_OK),
+    mReadyState(XMLHttpRequest::State::unsent), mUploadEvent(aUploadEvent),
+    mProgressEvent(true), mLengthComputable(aLengthComputable),
+    mUseCachedArrayBufferResponse(false), mResponseTextResult(NS_OK),
+    mStatusResult(NS_OK), mResponseResult(NS_OK),
     mScopeObj(RootingCx(), aScopeObj)
   { }
 
   EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
                 JS::Handle<JSObject*> aScopeObj)
   : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
     StructuredCloneHolder(CloningSupported, TransferringNotSupported,
                           StructuredCloneScope::SameProcessDifferentThread),
     mType(aType), mResponse(JS::UndefinedValue()), mLoaded(0), mTotal(0),
-    mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
-    mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
+    mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0),
+    mReadyState(XMLHttpRequest::State::unsent), mUploadEvent(aUploadEvent),
+    mProgressEvent(false), mLengthComputable(0),
     mUseCachedArrayBufferResponse(false), mResponseTextResult(NS_OK),
     mStatusResult(NS_OK), mResponseResult(NS_OK),
     mScopeObj(RootingCx(), aScopeObj)
   { }
 
 private:
   ~EventRunnable()
   { }
@@ -999,17 +1001,17 @@ Proxy::HandleEvent(nsIDOMEvent* aEvent)
     NS_WARNING("Failed to get target!");
     return NS_ERROR_FAILURE;
   }
 
   bool isUploadTarget = mXHR != target;
   ProgressEvent* progressEvent = aEvent->InternalDOMEvent()->AsProgressEvent();
 
   if (mInOpen && type.EqualsASCII(sEventStrings[STRING_readystatechange])) {
-    if (mXHR->ReadyState() == 1) {
+    if (mXHR->GetReadyState() == XMLHttpRequest::State::opened) {
       mInnerEventStreamId++;
     }
   }
 
   {
     AutoSafeJSContext cx;
     JSAutoRequest ar(cx);
 
@@ -1163,17 +1165,17 @@ EventRunnable::PreDispatch(WorkerPrivate
           } else {
             MOZ_ASSERT(!JS_IsDetachedArrayBufferObject(obj));
             JS::AutoValueArray<1> argv(cx);
             argv[0].set(response);
             obj = JS_NewArrayObject(cx, argv);
             if (obj) {
               transferable.setObject(*obj);
               // Only cache the response when the readyState is DONE.
-              if (xhr->ReadyState() == 4) {
+              if (xhr->GetReadyState() == XMLHttpRequest::State::done) {
                 mProxy->mArrayBufferResponseWasTransferred = true;
               }
             } else {
               mResponseResult = NS_ERROR_OUT_OF_MEMORY;
               doClone = false;
             }
           }
         }
@@ -1191,17 +1193,17 @@ EventRunnable::PreDispatch(WorkerPrivate
   }
 
   mStatus = xhr->GetStatus(rv);
   mStatusResult = rv.StealNSResult();
 
   xhr->GetStatusText(mStatusText, rv);
   MOZ_ASSERT(!rv.Failed());
 
-  mReadyState = xhr->ReadyState();
+  mReadyState = xhr->GetReadyState();
 
   xhr->GetResponseURL(mResponseURL);
 
   return true;
 }
 
 bool
 EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
@@ -1305,17 +1307,18 @@ EventRunnable::WorkerRun(JSContext* aCx,
   state->mReadyState = mReadyState;
 
   state->mResponseURL = mResponseURL;
 
   XMLHttpRequestWorker* xhr = mProxy->mXMLHttpRequestPrivate;
   xhr->UpdateState(*state.get(), mUseCachedArrayBufferResponse);
 
   if (mType.EqualsASCII(sEventStrings[STRING_readystatechange])) {
-    if (mReadyState == 4 && !mUploadEvent && !mProxy->mSeenLoadStart) {
+    if (mReadyState == XMLHttpRequest::State::done && !mUploadEvent &&
+        !mProxy->mSeenLoadStart) {
       // We've already dispatched premature abort events.
       return true;
     }
   }
 
   if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) {
     return true;
   }
@@ -1672,21 +1675,22 @@ XMLHttpRequestWorker::MaybePin(ErrorResu
 void
 XMLHttpRequestWorker::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(mProxy);
 
   // Only send readystatechange event when state changed.
   bool isStateChanged = false;
-  if ((mStateData.mReadyState == 1 && mStateData.mFlagSend) ||
-      mStateData.mReadyState == 2 ||
-      mStateData.mReadyState == 3) {
+  if ((mStateData.mReadyState == XMLHttpRequest::State::opened &&
+       mStateData.mFlagSend) ||
+      mStateData.mReadyState == XMLHttpRequest::State::headers_received ||
+      mStateData.mReadyState == XMLHttpRequest::State::loading) {
     isStateChanged = true;
-    mStateData.mReadyState = 4;
+    mStateData.mReadyState = XMLHttpRequest::State::done;
   }
 
   if (mProxy->mSeenUploadLoadStart) {
     MOZ_ASSERT(mUpload);
 
     DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("abort"), true,
                                 aRv);
     if (aRv.Failed()) {
@@ -2178,33 +2182,34 @@ XMLHttpRequestWorker::Abort(ErrorResult&
 
   if (!mProxy) {
     return;
   }
 
   // Set our status to 0 and statusText to "" if we
   // will be aborting an ongoing fetch, so the upcoming
   // abort events we dispatch have the correct info.
-  if ((mStateData.mReadyState == State::opened && mStateData.mFlagSend) ||
-      mStateData.mReadyState == State::headers_received ||
-      mStateData.mReadyState == State::loading ||
-      mStateData.mReadyState == State::done) {
+  if ((mStateData.mReadyState == XMLHttpRequest::State::opened &&
+       mStateData.mFlagSend) ||
+      mStateData.mReadyState == XMLHttpRequest::State::headers_received ||
+      mStateData.mReadyState == XMLHttpRequest::State::loading ||
+      mStateData.mReadyState == XMLHttpRequest::State::done) {
     mStateData.mStatus = 0;
     mStateData.mStatusText.Truncate();
   }
 
   MaybeDispatchPrematureAbortEvents(aRv);
   if (aRv.Failed()) {
     return;
   }
 
-  if (mStateData.mReadyState == 4) {
+  if (mStateData.mReadyState == XMLHttpRequest::State::done) {
     // No one did anything to us while we fired abort events, so reset our state
     // to "unsent"
-    mStateData.mReadyState = 0;
+    mStateData.mReadyState = XMLHttpRequest::State::unsent;
   }
 
   mProxy->mOuterEventStreamId++;
 
   RefPtr<AbortRunnable> runnable = new AbortRunnable(mWorkerPrivate, mProxy);
   runnable->Dispatch(Terminating, aRv);
 }
 
@@ -2274,17 +2279,17 @@ XMLHttpRequestWorker::OverrideMimeType(c
 
   // We're supposed to throw if the state is not OPENED or HEADERS_RECEIVED. We
   // can detect OPENED really easily but we can't detect HEADERS_RECEIVED in a
   // non-racy way until the XHR state machine actually runs on this thread
   // (bug 671047). For now we're going to let this work only if the Send()
   // method has not been called, unless the send has been aborted.
   if (!mProxy || (SendInProgress() &&
                   (mProxy->mSeenLoadStart ||
-                   mStateData.mReadyState > 1))) {
+                   mStateData.mReadyState > XMLHttpRequest::State::opened))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   RefPtr<OverrideMimeTypeRunnable> runnable =
     new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType);
   runnable->Dispatch(Terminating, aRv);
 }
@@ -2310,17 +2315,17 @@ XMLHttpRequestWorker::SetResponseType(XM
     // Open() has not been called yet. We store the responseType and we will use
     // it later in Open().
     mResponseType = aResponseType;
     return;
   }
 
   if (SendInProgress() &&
       (mProxy->mSeenLoadStart ||
-       mStateData.mReadyState > 1)) {
+       mStateData.mReadyState > XMLHttpRequest::State::opened)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   RefPtr<SetResponseTypeRunnable> runnable =
     new SetResponseTypeRunnable(mWorkerPrivate, mProxy, aResponseType);
   runnable->Dispatch(Terminating, aRv);
   if (aRv.Failed()) {
--- a/dom/xhr/XMLHttpRequestWorker.h
+++ b/dom/xhr/XMLHttpRequestWorker.h
@@ -28,25 +28,25 @@ class XMLHttpRequestWorker final : publi
 {
 public:
   struct StateData
   {
     XMLHttpRequestStringSnapshot mResponseText;
     nsString mResponseURL;
     uint32_t mStatus;
     nsCString mStatusText;
-    uint16_t mReadyState;
+    State mReadyState;
     bool mFlagSend;
     JS::Heap<JS::Value> mResponse;
     nsresult mResponseTextResult;
     nsresult mStatusResult;
     nsresult mResponseResult;
 
     StateData()
-    : mStatus(0), mReadyState(0), mFlagSend(false),
+    : mStatus(0), mReadyState(State::unsent), mFlagSend(false),
       mResponse(JS::UndefinedValue()), mResponseTextResult(NS_OK),
       mStatusResult(NS_OK), mResponseResult(NS_OK)
     { }
 
     void trace(JSTracer* trc);
   };
 
 private:
@@ -81,16 +81,22 @@ public:
   Unpin();
 
   bool
   Notify(workers::Status aStatus) override;
 
   virtual uint16_t
   ReadyState() const override
   {
+    return static_cast<uint16_t>(mStateData.mReadyState);
+  }
+
+  virtual State
+  GetReadyState() const override
+  {
     return mStateData.mReadyState;
   }
 
   virtual void
   Open(const nsACString& aMethod, const nsAString& aUrl,
        ErrorResult& aRv) override
   {
     Open(aMethod, aUrl, true, Optional<nsAString>(),