Bug 1285036 - Part 2: Split mState up into an enum and a set of bool flags. r=baku
authorThomas Wisniewski <wisniewskit@gmail.com>
Fri, 08 Jul 2016 12:46:12 -0400
changeset 304426 9c7274bdcd99e4bfb5f566975437e27d01b7cc53
parent 304425 e42827ec652175101256d38e0df27a96c867ec44
child 304427 bf25c8b6af9fe7cabb8421c99fb9845de2810fed
push id79321
push usercbook@mozilla.com
push dateMon, 11 Jul 2016 08:42:29 +0000
treeherdermozilla-inbound@21cd5236715f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1285036
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1285036 - Part 2: Split mState up into an enum and a set of bool flags. r=baku
dom/xhr/XMLHttpRequestMainThread.cpp
dom/xhr/XMLHttpRequestMainThread.h
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -110,47 +110,16 @@ namespace {
                 "Mismatched lengths for ProgressEventTypeStrings and ProgressEventType enums");
 
   const nsString kLiteralString_readystatechange = NS_LITERAL_STRING("readystatechange");
   const nsString kLiteralString_xmlhttprequest = NS_LITERAL_STRING("xmlhttprequest");
   const nsString kLiteralString_DOMContentLoaded = NS_LITERAL_STRING("DOMContentLoaded");
 }
 
 // CIDs
-
-// State
-#define XML_HTTP_REQUEST_UNSENT           (1 << 0) // 0 UNSENT
-#define XML_HTTP_REQUEST_OPENED           (1 << 1) // 1 OPENED
-#define XML_HTTP_REQUEST_HEADERS_RECEIVED (1 << 2) // 2 HEADERS_RECEIVED
-#define XML_HTTP_REQUEST_LOADING          (1 << 3) // 3 LOADING
-#define XML_HTTP_REQUEST_DONE             (1 << 4) // 4 DONE
-#define XML_HTTP_REQUEST_SENT             (1 << 5) // Internal, corresponds to
-                                                   // "OPENED and the send()
-                                                   // flag is set" in spec
-                                                   // terms.
-// The above states are mutually exclusive, change with ChangeState() only.
-// The states below can be combined.
-#define XML_HTTP_REQUEST_ABORTED        (1 << 7)  // Internal
-#define XML_HTTP_REQUEST_ASYNC          (1 << 8)  // Internal
-#define XML_HTTP_REQUEST_PARSEBODY      (1 << 9)  // Internal
-#define XML_HTTP_REQUEST_SYNCLOOPING    (1 << 10) // Internal
-#define XML_HTTP_REQUEST_BACKGROUND     (1 << 13) // Internal
-#define XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND (1 << 14) // Internal
-#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 15) // Internal
-#define XML_HTTP_REQUEST_TIMED_OUT (1 << 16) // Internal
-#define XML_HTTP_REQUEST_DELETED (1 << 17) // Internal
-
-#define XML_HTTP_REQUEST_LOADSTATES         \
-  (XML_HTTP_REQUEST_UNSENT |                \
-   XML_HTTP_REQUEST_OPENED |                \
-   XML_HTTP_REQUEST_HEADERS_RECEIVED |      \
-   XML_HTTP_REQUEST_LOADING |               \
-   XML_HTTP_REQUEST_DONE |                  \
-   XML_HTTP_REQUEST_SENT)
-
 #define NS_BADCERTHANDLER_CONTRACTID \
   "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
 
 #define NS_PROGRESS_EVENT_INTERVAL 50
 
 NS_IMPL_ISUPPORTS(nsXHRParseEndListener, nsIDOMEventListener)
 
 class nsResumeTimeoutsEvent : public Runnable
@@ -186,17 +155,21 @@ static void AddLoadFlags(nsIRequest *req
 
 bool
 XMLHttpRequestMainThread::sDontWarnAboutSyncXHR = false;
 
 XMLHttpRequestMainThread::XMLHttpRequestMainThread()
   : mResponseBodyDecodedPos(0),
     mResponseType(XMLHttpRequestResponseType::_empty),
     mRequestObserver(nullptr),
-    mState(XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC),
+    mState(State::unsent),
+    mFlagAsynchronous(true), mFlagAborted(false), mFlagParseBody(false),
+    mFlagSyncLooping(false), mFlagBackgroundRequest(false),
+    mFlagHadUploadListenersOnSend(false), mFlagACwithCredentials(false),
+    mFlagTimedOut(false), mFlagDeleted(false),
     mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
     mProgressSinceLastProgressEvent(false),
     mRequestSentTime(0), mTimeoutMilliseconds(0),
     mErrorLoad(false), mWaitingForOnStopRequest(false),
     mProgressTimerIsActive(false),
     mIsHtml(false),
     mWarnAboutSyncHtml(false),
     mLoadLengthComputable(false), mLoadTotal(0),
@@ -208,25 +181,24 @@ XMLHttpRequestMainThread::XMLHttpRequest
     mResultArrayBuffer(nullptr),
     mIsMappedArrayBuffer(false),
     mXPCOMifier(nullptr)
 {
 }
 
 XMLHttpRequestMainThread::~XMLHttpRequestMainThread()
 {
-  mState |= XML_HTTP_REQUEST_DELETED;
-
-  if (mState & (XML_HTTP_REQUEST_SENT |
-                XML_HTTP_REQUEST_LOADING)) {
+  mFlagDeleted = true;
+
+  if (mState == State::sent || mState == State::loading) {
     Abort();
   }
 
-  MOZ_ASSERT(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
-  mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
+  MOZ_ASSERT(!mFlagSyncLooping, "we rather crash than hang");
+  mFlagSyncLooping = false;
 
   mResultJSON.setUndefined();
   mResultArrayBuffer = nullptr;
   mozilla::DropJSObjects(this);
 }
 
 void
 XMLHttpRequestMainThread::RootJSResultObjects()
@@ -502,17 +474,17 @@ XMLHttpRequestMainThread::GetResponseXML
       mResponseType != XMLHttpRequestResponseType::Document) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
   if (mWarnAboutSyncHtml) {
     mWarnAboutSyncHtml = false;
     LogMessage("HTMLSyncXHRWarning", GetOwner());
   }
-  if (!(XML_HTTP_REQUEST_DONE & mState)) {
+  if (mState != State::done) {
     return nullptr;
   }
   return mResponseXML;
 }
 
 /*
  * This piece copied from XMLDocument, we try to get the charset
  * from HTTP headers.
@@ -618,17 +590,17 @@ XMLHttpRequestMainThread::GetResponseTex
   }
 
   if (mResponseType == XMLHttpRequestResponseType::Moz_chunked_text &&
       !mInLoadProgressEvent) {
     aResponseText.SetIsVoid(true);
     return;
   }
 
-  if (!(mState & (XML_HTTP_REQUEST_DONE | XML_HTTP_REQUEST_LOADING))) {
+  if (mState != State::loading && mState != State::done) {
     return;
   }
 
   // We only decode text lazily if we're also parsing to a doc.
   // Also, if we've decoded all current data already, then no need to decode
   // more.
   if (!mResponseXML ||
       mResponseBodyDecodedPos == mResponseBody.Length()) {
@@ -648,17 +620,17 @@ XMLHttpRequestMainThread::GetResponseTex
   aRv = AppendToResponseText(mResponseBody.get() + mResponseBodyDecodedPos,
                              mResponseBody.Length() - mResponseBodyDecodedPos);
   if (aRv.Failed()) {
     return;
   }
 
   mResponseBodyDecodedPos = mResponseBody.Length();
 
-  if (mState & XML_HTTP_REQUEST_DONE) {
+  if (mState == State::done) {
     // Free memory buffer which we no longer need
     mResponseBody.Truncate();
     mResponseBodyDecodedPos = 0;
   }
 
   aResponseText = mResponseText;
 }
 
@@ -736,30 +708,29 @@ NS_IMETHODIMP XMLHttpRequestMainThread::
 }
 
 void
 XMLHttpRequestMainThread::SetResponseType(XMLHttpRequestResponseType aResponseType,
                                           ErrorResult& aRv)
 {
   // If the state is LOADING or DONE raise an INVALID_STATE_ERR exception
   // and terminate these steps.
-  if ((mState & (XML_HTTP_REQUEST_LOADING | XML_HTTP_REQUEST_DONE))) {
+  if (mState == State::loading || mState == State::done) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // sync request is not allowed setting responseType in window context
-  if (HasOrHasHadOwner() &&
-      !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
+  if (HasOrHasHadOwner() && mState != State::unsent && !mFlagAsynchronous) {
     LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
 
-  if (!(mState & XML_HTTP_REQUEST_ASYNC) &&
+  if (!mFlagAsynchronous &&
       (aResponseType == XMLHttpRequestResponseType::Moz_chunked_text ||
        aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // Set the responseType attribute's value to the given value.
   mResponseType = aResponseType;
@@ -793,17 +764,17 @@ XMLHttpRequestMainThread::GetResponse(JS
     }
     return;
   }
 
   case XMLHttpRequestResponseType::Arraybuffer:
   case XMLHttpRequestResponseType::Moz_chunked_arraybuffer:
   {
     if (!(mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
-          mState & XML_HTTP_REQUEST_DONE) &&
+          mState == State::done) &&
         !(mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer &&
           mInLoadProgressEvent)) {
       aResponse.setNull();
       return;
     }
 
     if (!mResultArrayBuffer) {
       RootJSResultObjects();
@@ -816,17 +787,17 @@ XMLHttpRequestMainThread::GetResponse(JS
     }
     JS::ExposeObjectToActiveJS(mResultArrayBuffer);
     aResponse.setObject(*mResultArrayBuffer);
     return;
   }
   case XMLHttpRequestResponseType::Blob:
   case XMLHttpRequestResponseType::Moz_blob:
   {
-    if (!(mState & XML_HTTP_REQUEST_DONE)) {
+    if (mState != State::done) {
       if (mResponseType != XMLHttpRequestResponseType::Moz_blob) {
         aResponse.setNull();
         return;
       }
 
       if (!mResponseBlob) {
         CreatePartialBlob(aRv);
       }
@@ -837,27 +808,27 @@ XMLHttpRequestMainThread::GetResponse(JS
       return;
     }
 
     GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse);
     return;
   }
   case XMLHttpRequestResponseType::Document:
   {
-    if (!(mState & XML_HTTP_REQUEST_DONE) || !mResponseXML) {
+    if (!mResponseXML || mState != State::done) {
       aResponse.setNull();
       return;
     }
 
     aRv = nsContentUtils::WrapNative(aCx, mResponseXML, aResponse);
     return;
   }
   case XMLHttpRequestResponseType::Json:
   {
-    if (!(mState & XML_HTTP_REQUEST_DONE)) {
+    if (mState != State::done) {
       aResponse.setNull();
       return;
     }
 
     if (mResultJSON.isUndefined()) {
       aRv = CreateResponseParsedJSON(aCx);
       mResponseText.Truncate();
       if (aRv.Failed()) {
@@ -1026,65 +997,64 @@ XMLHttpRequestMainThread::GetStatusText(
   if (httpChannel) {
     httpChannel->GetResponseStatusText(aStatusText);
   } else {
     aStatusText.AssignLiteral("OK");
   }
 }
 
 void
-XMLHttpRequestMainThread::CloseRequestWithError(const ProgressEventType aType,
-                                                const uint32_t aFlag)
+XMLHttpRequestMainThread::CloseRequestWithError(const ProgressEventType aType)
 {
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
   }
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
   }
   uint32_t responseLength = mResponseBody.Length();
   ResetResponse();
-  mState |= aFlag;
 
   // If we're in the destructor, don't risk dispatching an event.
-  if (mState & XML_HTTP_REQUEST_DELETED) {
-    mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
+  if (mFlagDeleted) {
+    mFlagSyncLooping = false;
     return;
   }
 
-  if (!(mState & (XML_HTTP_REQUEST_UNSENT |
-                  XML_HTTP_REQUEST_OPENED |
-                  XML_HTTP_REQUEST_DONE))) {
-    ChangeState(XML_HTTP_REQUEST_DONE, true);
-
-    if (!(mState & XML_HTTP_REQUEST_SYNCLOOPING)) {
+  if (mState != State::unsent &&
+      mState != State::opened &&
+      mState != State::done) {
+    ChangeState(State::done, true);
+
+    if (!mFlagSyncLooping) {
       DispatchProgressEvent(this, aType, mLoadLengthComputable, responseLength,
                             mLoadTotal);
       if (mUpload && !mUploadComplete) {
         mUploadComplete = true;
         DispatchProgressEvent(mUpload, aType, true, mUploadTransferred,
                               mUploadTotal);
       }
     }
   }
 
   // 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 (mState & XML_HTTP_REQUEST_ABORTED) {
-    ChangeState(XML_HTTP_REQUEST_UNSENT, false);  // IE seems to do it
+  if (mFlagAborted) {
+    ChangeState(State::unsent, false);  // IE seems to do it
   }
 
-  mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
+  mFlagSyncLooping = false;
 }
 
 void
 XMLHttpRequestMainThread::Abort(ErrorResult& arv)
 {
-  CloseRequestWithError(ProgressEventType::abort, XML_HTTP_REQUEST_ABORTED);
+  mFlagAborted = true;
+  CloseRequestWithError(ProgressEventType::abort);
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::SlowAbort()
 {
   Abort();
   return NS_OK;
 }
@@ -1157,18 +1127,19 @@ XMLHttpRequestMainThread::GetAllResponse
 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 & (XML_HTTP_REQUEST_UNSENT |
-                XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
+  if (mState == State::unsent ||
+      mState == State::opened ||
+      mState == State::sent) {
     return;
   }
 
   if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) {
     RefPtr<nsHeaderVisitor> visitor =
       new nsHeaderVisitor(*this, WrapNotNull(httpChannel));
     if (NS_SUCCEEDED(httpChannel->VisitResponseHeaders(visitor))) {
       aResponseHeaders = visitor->Headers();
@@ -1222,18 +1193,19 @@ 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 & (XML_HTTP_REQUEST_UNSENT |
-                  XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
+    if (mState == State::unsent ||
+        mState == State::opened ||
+        mState == State::sent) {
       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 ||
@@ -1280,17 +1252,17 @@ XMLHttpRequestMainThread::GetResponseHea
     _retval.SetIsVoid(true);
     aRv.SuppressException();
   }
 }
 
 already_AddRefed<nsILoadGroup>
 XMLHttpRequestMainThread::GetLoadGroup() const
 {
-  if (mState & XML_HTTP_REQUEST_BACKGROUND) {
+  if (mFlagBackgroundRequest) {
     return nullptr;
   }
 
   if (mLoadGroup) {
     nsCOMPtr<nsILoadGroup> ref = mLoadGroup;
     return ref.forget();
   }
 
@@ -1428,38 +1400,33 @@ XMLHttpRequestMainThread::Open(const nsA
     if (mResponseType != XMLHttpRequestResponseType::_empty) {
       LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
     }
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
 
   nsCOMPtr<nsIURI> uri;
 
-  if (mState & (XML_HTTP_REQUEST_OPENED |
-                XML_HTTP_REQUEST_HEADERS_RECEIVED |
-                XML_HTTP_REQUEST_LOADING |
-                XML_HTTP_REQUEST_SENT)) {
+  if (mState == State::opened || mState == State::headers_received ||
+      mState == State::loading || mState == State::sent) {
     // IE aborts as well
     Abort();
 
     // XXX We should probably send a warning to the JS console
     //     that load was aborted and event listeners were cleared
     //     since this looks like a situation that could happen
     //     by accident and you could spend a lot of time wondering
     //     why things didn't work.
   }
 
-  // Unset any pre-existing aborted and timed-out states.
-  mState &= ~XML_HTTP_REQUEST_ABORTED & ~XML_HTTP_REQUEST_TIMED_OUT;
-
-  if (async) {
-    mState |= XML_HTTP_REQUEST_ASYNC;
-  } else {
-    mState &= ~XML_HTTP_REQUEST_ASYNC;
-  }
+  // Unset any pre-existing aborted and timed-out flags.
+  mFlagAborted = false;
+  mFlagTimedOut = false;
+
+  mFlagAsynchronous = async;
 
   nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
   if (!doc) {
     // This could be because we're no longer current or because we're in some
     // non-window context...
     nsresult rv = CheckInnerWindowCorrectness();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return NS_ERROR_DOM_INVALID_STATE_ERR;
@@ -1555,31 +1522,31 @@ XMLHttpRequestMainThread::Open(const nsA
                        nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
                        loadGroup,
                        nullptr,   // aCallbacks
                        loadFlags);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mState &= ~XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND;
+  mFlagHadUploadListenersOnSend = false;
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
   if (httpChannel) {
     rv = httpChannel->SetRequestMethod(method);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set the initiator type
     nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
     if (timedChannel) {
       timedChannel->SetInitiatorType(kLiteralString_xmlhttprequest);
     }
   }
 
-  ChangeState(XML_HTTP_REQUEST_OPENED);
+  ChangeState(State::opened);
 
   return NS_OK;
 }
 
 void
 XMLHttpRequestMainThread::PopulateNetworkInterfaceId()
 {
   if (mNetworkInterfaceId.IsEmpty()) {
@@ -1643,17 +1610,17 @@ XMLHttpRequestMainThread::StreamReaderFu
              xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Text ||
              xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Json ||
              xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_chunked_text) {
     NS_ASSERTION(!xmlHttpRequest->mResponseXML,
                  "We shouldn't be parsing a doc here");
     xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
   }
 
-  if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) {
+  if (xmlHttpRequest->mFlagParseBody) {
     // Give the same data to the parser.
 
     // We need to wrap the data in a new lightweight stream and pass that
     // to the parser, because calling ReadSegments() recursively on the same
     // stream is not supported.
     nsCOMPtr<nsIInputStream> copyStream;
     rv = NS_NewByteInputStream(getter_AddRefs(copyStream), fromRawSegment, count);
 
@@ -1662,17 +1629,17 @@ XMLHttpRequestMainThread::StreamReaderFu
       nsresult parsingResult = xmlHttpRequest->mXMLParserStreamListener
                                   ->OnDataAvailable(xmlHttpRequest->mChannel,
                                                     xmlHttpRequest->mContext,
                                                     copyStream, toOffset, count);
 
       // No use to continue parsing if we failed here, but we
       // should still finish reading the stream
       if (NS_FAILED(parsingResult)) {
-        xmlHttpRequest->mState &= ~XML_HTTP_REQUEST_PARSEBODY;
+        xmlHttpRequest->mFlagParseBody = false;
       }
     }
   }
 
   if (NS_SUCCEEDED(rv)) {
     *writeCount = count;
   } else {
     *writeCount = 0;
@@ -1732,23 +1699,23 @@ XMLHttpRequestMainThread::OnDataAvailabl
   if (cancelable) {
     // We don't have to read from the local file for the blob response
     ErrorResult error;
     mDataAvailable = mDOMBlob->GetSize(error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
 
-    ChangeState(XML_HTTP_REQUEST_LOADING);
+    ChangeState(State::loading);
     return request->Cancel(NS_OK);
   }
 
   mDataAvailable += totalRead;
 
-  ChangeState(XML_HTTP_REQUEST_LOADING);
+  ChangeState(State::loading);
 
   MaybeDispatchProgressEvents(false);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
@@ -1763,59 +1730,59 @@ XMLHttpRequestMainThread::OnStartRequest
   }
 
   if (request != mChannel) {
     // Can this still happen?
     return NS_OK;
   }
 
   // Don't do anything if we have been aborted
-  if (mState & XML_HTTP_REQUEST_UNSENT)
+  if (mState == State::unsent) {
     return NS_OK;
-
-  /* Apparently, Abort() should set XML_HTTP_REQUEST_UNSENT.  See bug 361773.
+  }
+
+  /* Apparently, Abort() should set State::unsent.  See bug 361773.
      XHR2 spec says this is correct. */
-  if (mState & XML_HTTP_REQUEST_ABORTED) {
+  if (mFlagAborted) {
     NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
 
     return NS_ERROR_UNEXPECTED;
   }
 
   // Don't do anything if we have timed out.
-  if (mState & XML_HTTP_REQUEST_TIMED_OUT) {
+  if (mFlagTimedOut) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
 
   nsresult status;
   request->GetStatus(&status);
   mErrorLoad = mErrorLoad || NS_FAILED(status);
 
-  if (mUpload && !mUploadComplete && !mErrorLoad &&
-      (mState & XML_HTTP_REQUEST_ASYNC)) {
+  if (mUpload && !mUploadComplete && !mErrorLoad && mFlagAsynchronous) {
     if (mProgressTimerIsActive) {
       mProgressTimerIsActive = false;
       mProgressNotifier->Cancel();
     }
     if (mUploadTransferred < mUploadTotal) {
       mUploadTransferred = mUploadTotal;
       mProgressSinceLastProgressEvent = true;
       mUploadLengthComputable = true;
       MaybeDispatchProgressEvents(true);
     }
     mUploadComplete = true;
     DispatchProgressEvent(mUpload, ProgressEventType::load,
                           true, mUploadTotal, mUploadTotal);
   }
 
   mContext = ctxt;
-  mState |= XML_HTTP_REQUEST_PARSEBODY;
-  ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED);
+  mFlagParseBody = true;
+  ChangeState(State::headers_received);
 
   ResetResponse();
 
   if (!mOverrideMimeType.IsEmpty()) {
     channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
   }
 
   DetectCharset();
@@ -1892,38 +1859,38 @@ XMLHttpRequestMainThread::OnStartRequest
 
     if ((mResponseType == XMLHttpRequestResponseType::Document) &&
         type.EqualsLiteral("text/html")) {
       // HTML parsing is only supported for responseType == "document" to
       // avoid running the parser and, worse, populating responseXML for
       // legacy users of XHR who use responseType == "" for retrieving the
       // responseText of text/html resources. This legacy case is so common
       // that it's not useful to emit a warning about it.
-      if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
+      if (!mFlagAsynchronous) {
         // We don't make cool new features available in the bad synchronous
         // mode. The synchronous mode is for legacy only.
         mWarnAboutSyncHtml = true;
-        mState &= ~XML_HTTP_REQUEST_PARSEBODY;
+        mFlagParseBody = false;
       } else {
         mIsHtml = true;
       }
     } else if (!(type.EqualsLiteral("text/xml") ||
                  type.EqualsLiteral("application/xml") ||
                  type.RFind("+xml", true, -1, 4) != kNotFound)) {
       // Follow https://xhr.spec.whatwg.org/
       // If final MIME type is not null, text/html, text/xml, application/xml,
       // or does not end in +xml, return null.
-      mState &= ~XML_HTTP_REQUEST_PARSEBODY;
+      mFlagParseBody = false;
     }
   } else {
     // The request failed, so we shouldn't be parsing anyway
-    mState &= ~XML_HTTP_REQUEST_PARSEBODY;
+    mFlagParseBody = false;
   }
 
-  if (mState & XML_HTTP_REQUEST_PARSEBODY) {
+  if (mFlagParseBody) {
     nsCOMPtr<nsIURI> baseURI, docURI;
     rv = mChannel->GetURI(getter_AddRefs(docURI));
     NS_ENSURE_SUCCESS(rv, rv);
     baseURI = docURI;
 
     nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
     nsCOMPtr<nsIURI> chromeXHRDocURI, chromeXHRDocBaseURI;
     if (doc) {
@@ -1986,17 +1953,17 @@ XMLHttpRequestMainThread::OnStartRequest
     mXMLParserStreamListener = listener;
     rv = mXMLParserStreamListener->OnStartRequest(request, ctxt);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // We won't get any progress events anyway if we didn't have progress
   // events when starting the request - so maybe no need to start timer here.
   if (NS_SUCCEEDED(rv) &&
-      (mState & XML_HTTP_REQUEST_ASYNC) &&
+      mFlagAsynchronous &&
       HasListenersFor(nsGkAtoms::onprogress)) {
     StartProgressEventTimer();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -2015,26 +1982,25 @@ XMLHttpRequestMainThread::OnStopRequest(
   if (mRequestObserver) {
     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??
-  // XML_HTTP_REQUEST_UNSENT is for abort calls.  See OnStartRequest above.
-  if ((mState & XML_HTTP_REQUEST_UNSENT) ||
-      (mState & XML_HTTP_REQUEST_TIMED_OUT)) {
+  // State::unsent is for abort calls.  See OnStartRequest above.
+  if (mState == State::unsent || mFlagTimedOut) {
     if (mXMLParserStreamListener)
       (void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
     return NS_OK;
   }
 
   // Is this good enough here?
-  if (mState & XML_HTTP_REQUEST_PARSEBODY && mXMLParserStreamListener) {
+  if (mXMLParserStreamListener && mFlagParseBody) {
     mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
   }
 
   mXMLParserStreamListener = nullptr;
   mContext = nullptr;
 
   // If we're received data since the last progress event, make sure to fire
   // an event for it, except in the HTML case, defer the last progress event
@@ -2088,40 +2054,39 @@ XMLHttpRequestMainThread::OnStopRequest(
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
 
   channel->SetNotificationCallbacks(nullptr);
   mNotificationCallbacks = nullptr;
   mChannelEventSink = nullptr;
   mProgressEventSink = nullptr;
 
-  mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
+  mFlagSyncLooping = false;
 
   if (NS_FAILED(status)) {
     // This can happen if the server is unreachable. Other possible
     // reasons are that the user leaves the page or hits the ESC key.
 
     mErrorLoad = true;
     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 & (XML_HTTP_REQUEST_UNSENT |
-                XML_HTTP_REQUEST_DONE)) {
+  if (mState == State::unsent || mState == State::done) {
     return NS_OK;
   }
 
   if (!mResponseXML) {
     ChangeStateToDone();
     return NS_OK;
   }
   if (mIsHtml) {
-    NS_ASSERTION(!(mState & XML_HTTP_REQUEST_SYNCLOOPING),
+    NS_ASSERTION(!mFlagSyncLooping,
       "We weren't supposed to support HTML parsing with XHR!");
     nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(mResponseXML);
     EventListenerManager* manager =
       eventTarget->GetOrCreateListenerManager();
     manager->AddEventListenerByType(new nsXHRParseEndListener(this),
                                     kLiteralString_DOMContentLoaded,
                                     TrustedEventsAtSystemGroupBubble());
     return NS_OK;
@@ -2141,17 +2106,18 @@ void
 XMLHttpRequestMainThread::ChangeStateToDone()
 {
   if (mIsHtml) {
     // In the HTML case, this has to be deferred, because the parser doesn't
     // do it's job synchronously.
     MaybeDispatchProgressEvents(true);
   }
 
-  ChangeState(XML_HTTP_REQUEST_DONE, true);
+  ChangeState(State::done, true);
+
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
   }
 
   DispatchProgressEvent(this,
                         mErrorLoad ? ProgressEventType::error : ProgressEventType::load,
                         !mErrorLoad,
                         mLoadTransferred,
@@ -2457,22 +2423,22 @@ XMLHttpRequestMainThread::Send(nsIVarian
   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
 
   PopulateNetworkInterfaceId();
 
   nsresult rv = CheckInnerWindowCorrectness();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Return error if we're already processing a request
-  if (XML_HTTP_REQUEST_SENT & mState) {
+  if (mState == State::sent) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   // Make sure we've been opened
-  if (!mChannel || !(XML_HTTP_REQUEST_OPENED & mState)) {
+  if (!mChannel || mState != State::opened) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
   // in turn keeps STOP button from becoming active.  If the consumer passed in
   // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
   // necko won't generate any progress notifications.
   if (HasListenersFor(nsGkAtoms::onprogress) ||
@@ -2651,18 +2617,17 @@ XMLHttpRequestMainThread::Send(nsIVarian
         // Reset the method to its original value
         httpChannel->SetRequestMethod(method);
       }
     }
   }
 
   ResetResponse();
 
-  if (!IsSystemXHR() && !mIsAnon &&
-      (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS)) {
+  if (!IsSystemXHR() && !mIsAnon && mFlagACwithCredentials) {
     // This is quite sad. We have to create the channel in .open(), since the
     // chrome-only xhr.channel API depends on that. However .withCredentials
     // can be modified after, so we don't know what to set the
     // SEC_COOKIES_INCLUDE flag to when the channel is
     // created. So set it here using a hacky internal API.
 
     // Not doing this for system XHR uses since those don't use CORS.
     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
@@ -2726,24 +2691,24 @@ XMLHttpRequestMainThread::Send(nsIVarian
   }
 
   // We're about to send the request.  Start our timeout.
   mRequestSentTime = PR_Now();
   StartTimeoutTimer();
 
   // Check if we should enabled cross-origin upload listeners.
   if (mUpload && mUpload->HasListeners()) {
-    mState |= XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND;
+    mFlagHadUploadListenersOnSend = true;
   }
 
   // Set up the preflight if needed
   if (!IsSystemXHR()) {
     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
     loadInfo->SetCorsPreflightInfo(mCORSUnsafeHeaders,
-                                   mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND);
+                                   mFlagHadUploadListenersOnSend);
   }
 
   mIsMappedArrayBuffer = false;
   if (mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
       Preferences::GetBool("dom.mapped_arraybuffer.enabled", true)) {
     nsCOMPtr<nsIURI> uri;
     nsAutoCString scheme;
 
@@ -2783,42 +2748,41 @@ XMLHttpRequestMainThread::Send(nsIVarian
     mChannel = nullptr;
 
     return rv;
   }
 
   mWaitingForOnStopRequest = true;
 
   // If we're synchronous, spin an event loop here and wait
-  if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
-    mState |= XML_HTTP_REQUEST_SYNCLOOPING;
+  if (!mFlagAsynchronous) {
+    mFlagSyncLooping = true;
 
     nsCOMPtr<nsIDocument> suspendedDoc;
     nsCOMPtr<nsIRunnable> resumeTimeoutRunnable;
     if (GetOwner()) {
       if (nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetOwner()->GetOuterWindow()->GetTop()) {
         if (nsCOMPtr<nsPIDOMWindowInner> topInner = topWindow->GetCurrentInnerWindow()) {
           suspendedDoc = topWindow->GetExtantDoc();
           if (suspendedDoc) {
             suspendedDoc->SuppressEventHandling(nsIDocument::eEvents);
           }
           topWindow->SuspendTimeouts(1, false);
           resumeTimeoutRunnable = new nsResumeTimeoutsEvent(topInner);
         }
       }
     }
 
-    ChangeState(XML_HTTP_REQUEST_SENT);
+    ChangeState(State::sent);
 
     {
       nsAutoSyncOperation sync(suspendedDoc);
-      // Note, calling ChangeState may have cleared
-      // XML_HTTP_REQUEST_SYNCLOOPING flag.
+      // Note, calling ChangeState may have cleared mFlagSyncLooping
       nsIThread *thread = NS_GetCurrentThread();
-      while (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
+      while (mFlagSyncLooping) {
         if (!NS_ProcessNextEvent(thread)) {
           rv = NS_ERROR_UNEXPECTED;
           break;
         }
       }
     }
 
     if (suspendedDoc) {
@@ -2829,17 +2793,17 @@ XMLHttpRequestMainThread::Send(nsIVarian
     if (resumeTimeoutRunnable) {
       NS_DispatchToCurrentThread(resumeTimeoutRunnable);
     }
   } else {
     // Now that we've successfully opened the channel, we can change state.  Note
     // that this needs to come after the AsyncOpen() and rv check, because this
     // can run script that would try to restart this request, and that could end
     // up doing our AsyncOpen on a null channel if the reentered AsyncOpen fails.
-    ChangeState(XML_HTTP_REQUEST_SENT);
+    ChangeState(State::sent);
     if (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress)) {
       StartProgressEventTimer();
     }
     DispatchProgressEvent(this, ProgressEventType::loadstart, false,
                           0, 0);
     if (mUpload && !mUploadComplete) {
       DispatchProgressEvent(mUpload, ProgressEventType::loadstart, true,
                             0, mUploadTotal);
@@ -2854,17 +2818,17 @@ XMLHttpRequestMainThread::Send(nsIVarian
 }
 
 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
 NS_IMETHODIMP
 XMLHttpRequestMainThread::SetRequestHeader(const nsACString& header,
                                            const nsACString& value)
 {
   // Step 1 and 2
-  if (!(mState & XML_HTTP_REQUEST_OPENED)) {
+  if (mState != State::opened) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
   NS_ASSERTION(mChannel, "mChannel must be valid if we're OPENED.");
 
   // Step 3
   // Make sure we don't store an invalid header name in mCORSUnsafeHeaders
   if (!NS_IsValidHTTPToken(header)) {
     return NS_ERROR_DOM_SYNTAX_ERR;
@@ -2963,18 +2927,17 @@ XMLHttpRequestMainThread::SetTimeout(uin
   ErrorResult rv;
   SetTimeout(aTimeout, rv);
   return rv.StealNSResult();
 }
 
 void
 XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
 {
-  if (!(mState & (XML_HTTP_REQUEST_ASYNC | XML_HTTP_REQUEST_UNSENT)) &&
-      HasOrHasHadOwner()) {
+  if (!mFlagAsynchronous && mState != 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_ERR);
     return;
   }
 
   mTimeoutMilliseconds = aTimeout;
@@ -2983,17 +2946,17 @@ XMLHttpRequestMainThread::SetTimeout(uin
   }
 }
 
 void
 XMLHttpRequestMainThread::StartTimeoutTimer()
 {
   MOZ_ASSERT(mRequestSentTime,
              "StartTimeoutTimer mustn't be called before the request was sent!");
-  if (mState & XML_HTTP_REQUEST_DONE) {
+  if (mState == State::done) {
     // do nothing!
     return;
   }
 
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
   }
 
@@ -3019,35 +2982,35 @@ XMLHttpRequestMainThread::GetReadyState(
   *aState = ReadyState();
   return NS_OK;
 }
 
 uint16_t
 XMLHttpRequestMainThread::ReadyState() const
 {
   // Translate some of our internal states for external consumers
-  if (mState & XML_HTTP_REQUEST_UNSENT) {
+  if (mState == State::unsent) {
     return UNSENT;
   }
-  if (mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
+  if (mState == State::opened || mState == State::sent) {
     return OPENED;
   }
-  if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
+  if (mState == State::headers_received) {
     return HEADERS_RECEIVED;
   }
-  if (mState & XML_HTTP_REQUEST_LOADING) {
+  if (mState == State::loading) {
     return LOADING;
   }
-  MOZ_ASSERT(mState & XML_HTTP_REQUEST_DONE);
+  MOZ_ASSERT(mState == State::done);
   return DONE;
 }
 
 void XMLHttpRequestMainThread::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
 {
-  if ((mState & XML_HTTP_REQUEST_LOADING) || (mState & XML_HTTP_REQUEST_DONE)) {
+  if (mState == State::loading || mState == State::done) {
     ResetResponse();
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   mOverrideMimeType = aMimeType;
 }
 
@@ -3064,36 +3027,32 @@ XMLHttpRequestMainThread::GetMozBackgrou
 {
   *_retval = MozBackgroundRequest();
   return NS_OK;
 }
 
 bool
 XMLHttpRequestMainThread::MozBackgroundRequest() const
 {
-  return !!(mState & XML_HTTP_REQUEST_BACKGROUND);
+  return mFlagBackgroundRequest;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::SetMozBackgroundRequest(bool aMozBackgroundRequest)
 {
   if (!IsSystemXHR()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  if (!(mState & XML_HTTP_REQUEST_UNSENT)) {
+  if (mState != State::unsent) {
     // Can't change this while we're in the middle of something.
      return NS_ERROR_IN_PROGRESS;
   }
 
-  if (aMozBackgroundRequest) {
-    mState |= XML_HTTP_REQUEST_BACKGROUND;
-  } else {
-    mState &= ~XML_HTTP_REQUEST_BACKGROUND;
-  }
+  mFlagBackgroundRequest = aMozBackgroundRequest;
 
   return NS_OK;
 }
 
 void
 XMLHttpRequestMainThread::SetMozBackgroundRequest(bool aMozBackgroundRequest,
                                                   ErrorResult& aRv)
 {
@@ -3106,70 +3065,60 @@ XMLHttpRequestMainThread::GetWithCredent
 {
   *_retval = WithCredentials();
   return NS_OK;
 }
 
 bool
 XMLHttpRequestMainThread::WithCredentials() const
 {
-  return !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
+  return mFlagACwithCredentials;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials)
 {
   ErrorResult rv;
   SetWithCredentials(aWithCredentials, rv);
   return rv.StealNSResult();
 }
 
 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 & XML_HTTP_REQUEST_UNSENT) &&
-       !(mState & XML_HTTP_REQUEST_OPENED)) ||
-      mIsAnon) {
+  if ((mState != State::unsent && mState != State::opened) || mIsAnon) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  if (aWithCredentials) {
-    mState |= XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
-  } else {
-    mState &= ~XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
-  }
+  mFlagACwithCredentials = aWithCredentials;
 }
 
 nsresult
-XMLHttpRequestMainThread::ChangeState(uint32_t aState, bool aBroadcast)
+XMLHttpRequestMainThread::ChangeState(State aState, bool aBroadcast)
 {
-  // If we are setting one of the mutually exclusive states,
-  // unset those state bits first.
-  if (aState & XML_HTTP_REQUEST_LOADSTATES) {
-    mState &= ~XML_HTTP_REQUEST_LOADSTATES;
-  }
-  mState |= aState;
+  bool droppingFromSENTtoOPENED = mState == State::sent &&
+                                  aState == State::opened;
+
+  mState = aState;
   nsresult rv = NS_OK;
 
   if (mProgressNotifier &&
-      !(aState & (XML_HTTP_REQUEST_HEADERS_RECEIVED | XML_HTTP_REQUEST_LOADING))) {
+      aState != State::headers_received && aState != State::loading) {
     mProgressTimerIsActive = false;
     mProgressNotifier->Cancel();
   }
 
-  if ((aState & XML_HTTP_REQUEST_LOADSTATES) &&  // Broadcast load states only
-      aState != XML_HTTP_REQUEST_SENT && // And not internal ones
+  if (aState != State::sent && // SENT is internal
+      !droppingFromSENTtoOPENED && // since SENT is essentially OPENED
       aBroadcast &&
-      (mState & XML_HTTP_REQUEST_ASYNC ||
-       aState & XML_HTTP_REQUEST_OPENED ||
-       aState & XML_HTTP_REQUEST_DONE)) {
+      (mFlagAsynchronous || aState == State::opened || aState == State::done)) {
     nsCOMPtr<nsIDOMEvent> event;
     rv = CreateReadystatechangeEvent(getter_AddRefs(event));
     NS_ENSURE_SUCCESS(rv, rv);
 
     DispatchDOMEvent(nullptr, event, nullptr, nullptr);
   }
 
   return rv;
@@ -3251,27 +3200,26 @@ XMLHttpRequestMainThread::MaybeDispatchP
   if (aFinalProgress && mProgressTimerIsActive) {
     mProgressTimerIsActive = false;
     mProgressNotifier->Cancel();
   }
 
   if (mProgressTimerIsActive ||
       !mProgressSinceLastProgressEvent ||
       mErrorLoad ||
-      !(mState & XML_HTTP_REQUEST_ASYNC)) {
+      !mFlagAsynchronous) {
     return;
   }
 
   if (!aFinalProgress) {
     StartProgressEventTimer();
   }
 
-  // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
-  // XML_HTTP_REQUEST_SENT
-  if ((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState) {
+  // We're uploading if our state is State::opened or State::sent
+  if (mState == State::opened || mState == State::sent) {
     if (mUpload && !mUploadComplete) {
       DispatchProgressEvent(mUpload, ProgressEventType::progress,
                             mUploadLengthComputable, mUploadTransferred,
                             mUploadTotal);
     }
   } else {
     if (aFinalProgress) {
       mLoadTotal = mLoadTransferred;
@@ -3291,19 +3239,18 @@ XMLHttpRequestMainThread::MaybeDispatchP
   }
 
   mProgressSinceLastProgressEvent = false;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::OnProgress(nsIRequest *aRequest, nsISupports *aContext, int64_t aProgress, int64_t aProgressMax)
 {
-  // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
-  // XML_HTTP_REQUEST_SENT
-  bool upload = !!((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState);
+  // We're uploading if our state is State::opened or State::sent
+  bool upload = mState == State::opened || mState == State::sent;
   // When uploading, OnProgress reports also headers in aProgress and aProgressMax.
   // So, try to remove the headers, if possible.
   bool lengthComputable = (aProgressMax != -1);
   if (upload) {
     int64_t loaded = aProgress;
     if (lengthComputable) {
       int64_t headerSize = aProgressMax - mUploadTotal;
       loaded -= headerSize;
@@ -3338,17 +3285,17 @@ XMLHttpRequestMainThread::OnStatus(nsIRe
 
   return NS_OK;
 }
 
 bool
 XMLHttpRequestMainThread::AllowUploadProgress()
 {
   return !IsCrossSiteCORSRequest() ||
-    (mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND);
+         mFlagHadUploadListenersOnSend;
 }
 
 /////////////////////////////////////////////////////
 // nsIInterfaceRequestor methods:
 //
 NS_IMETHODIMP
 XMLHttpRequestMainThread::GetInterface(const nsIID & aIID, void **aResult)
 {
@@ -3373,17 +3320,17 @@ XMLHttpRequestMainThread::GetInterface(c
   if (mNotificationCallbacks) {
     rv = mNotificationCallbacks->GetInterface(aIID, aResult);
     if (NS_SUCCEEDED(rv)) {
       NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
       return rv;
     }
   }
 
-  if (mState & XML_HTTP_REQUEST_BACKGROUND) {
+  if (mFlagBackgroundRequest) {
     nsCOMPtr<nsIInterfaceRequestor> badCertHandler(do_CreateInstance(NS_BADCERTHANDLER_CONTRACTID, &rv));
 
     // Ignore failure to get component, we may not have all its dependencies
     // available
     if (NS_SUCCEEDED(rv)) {
       rv = badCertHandler->GetInterface(aIID, aResult);
       if (NS_SUCCEEDED(rv))
         return rv;
@@ -3475,23 +3422,24 @@ XMLHttpRequestMainThread::GetMozSystem(b
 {
   *aSystem = MozSystem();
   return NS_OK;
 }
 
 void
 XMLHttpRequestMainThread::HandleTimeoutCallback()
 {
-  if (mState & XML_HTTP_REQUEST_DONE) {
+  if (mState == State::done) {
     NS_NOTREACHED("XMLHttpRequestMainThread::HandleTimeoutCallback with completed request");
     // do nothing!
     return;
   }
 
-  CloseRequestWithError(ProgressEventType::timeout, XML_HTTP_REQUEST_TIMED_OUT);
+  mFlagTimedOut = true;
+  CloseRequestWithError(ProgressEventType::timeout);
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::Notify(nsITimer* aTimer)
 {
   if (mProgressNotifier == aTimer) {
     HandleProgressTimerCallback();
     return NS_OK;
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_XMLHttpRequestMainThread_h
 #define mozilla_dom_XMLHttpRequestMainThread_h
 
+#include <bitset>
 #include "nsAutoPtr.h"
 #include "nsIXMLHttpRequest.h"
 #include "nsISupportsUtils.h"
 #include "nsIURI.h"
 #include "nsIHttpChannel.h"
 #include "nsIDocument.h"
 #include "nsIStreamListener.h"
 #include "nsWeakReference.h"
@@ -553,30 +554,42 @@ public:
   {
     sDontWarnAboutSyncXHR = aVal;
   }
   static bool DontWarnAboutSyncXHR()
   {
     return sDontWarnAboutSyncXHR;
   }
 protected:
+  // XHR states are meant to mirror the XHR2 spec:
+  //   https://xhr.spec.whatwg.org/#states
+  // Note that we currently have an extra pseudo-state called sent.
+  enum class State : uint8_t {
+    unsent,           // object has been constructed.
+    opened,           // open() has been successfully invoked.
+    sent,             // non-spec, corresponds with "opened and the send() flag is set".
+    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 NS_METHOD StreamReaderFunc(nsIInputStream* in,
                 void* closure,
                 const char* fromRawSegment,
                 uint32_t toOffset,
                 uint32_t count,
                 uint32_t *writeCount);
   nsresult CreateResponseParsedJSON(JSContext* aCx);
   void CreatePartialBlob(ErrorResult& aRv);
   bool CreateDOMBlob(nsIRequest *request);
   // Change the state of the object with this. The broadcast argument
   // determines if the onreadystatechange listener should be called.
-  nsresult ChangeState(uint32_t aState, bool aBroadcast = true);
+  nsresult ChangeState(State aState, bool aBroadcast = true);
   already_AddRefed<nsILoadGroup> GetLoadGroup() const;
   nsIURI *GetBaseURI();
 
   already_AddRefed<nsIHttpChannel> GetCurrentHttpChannel();
   already_AddRefed<nsIJARChannel> GetCurrentJARChannel();
 
   bool IsSystemXHR() const;
 
@@ -670,17 +683,27 @@ protected:
   nsCOMPtr<nsIChannelEventSink> mChannelEventSink;
   nsCOMPtr<nsIProgressEventSink> mProgressEventSink;
 
   nsIRequestObserver* mRequestObserver;
 
   nsCOMPtr<nsIURI> mBaseURI;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
 
-  uint32_t mState;
+  State mState;
+
+  bool mFlagAsynchronous;
+  bool mFlagAborted;
+  bool mFlagParseBody;
+  bool mFlagSyncLooping;
+  bool mFlagBackgroundRequest;
+  bool mFlagHadUploadListenersOnSend;
+  bool mFlagACwithCredentials;
+  bool mFlagTimedOut;
+  bool mFlagDeleted;
 
   RefPtr<XMLHttpRequestUpload> mUpload;
   int64_t mUploadTransferred;
   int64_t mUploadTotal;
   bool mUploadLengthComputable;
   bool mUploadComplete;
   bool mProgressSinceLastProgressEvent;
 
@@ -720,20 +743,18 @@ protected:
   // that this request is associated with.
   nsCString mNetworkInterfaceId;
 
   /**
    * Close the XMLHttpRequest's channels and dispatch appropriate progress
    * events.
    *
    * @param aType The progress event type.
-   * @param aFlag A XML_HTTP_REQUEST_* state flag defined in
-   *              XMLHttpRequestMainthread.cpp.
    */
-  void CloseRequestWithError(const ProgressEventType aType, const uint32_t aFlag);
+  void CloseRequestWithError(const ProgressEventType aType);
 
   bool mFirstStartRequestSeen;
   bool mInLoadProgressEvent;
 
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
   nsCOMPtr<nsIChannel> mNewRedirectChannel;
 
   JS::Heap<JS::Value> mResultJSON;