tor-browser

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

XMLHttpRequestMainThread.cpp (140107B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "XMLHttpRequestMainThread.h"
      8 
      9 #include <algorithm>
     10 #ifndef XP_WIN
     11 #  include <unistd.h>
     12 #endif
     13 #include "GeckoProfiler.h"
     14 #include "MultipartBlobImpl.h"
     15 #include "XMLHttpRequestUpload.h"
     16 #include "js/ArrayBuffer.h"  // JS::{Create,Release}MappedArrayBufferContents,New{,Mapped}ArrayBufferWithContents
     17 #include "js/JSON.h"         // JS_ParseJSON
     18 #include "js/MemoryFunctions.h"
     19 #include "js/RootingAPI.h"  // JS::{{,Mutable}Handle,Rooted}
     20 #include "js/Value.h"       // JS::{,Undefined}Value
     21 #include "jsapi.h"          // JS_ClearPendingException
     22 #include "mozilla/AppShutdown.h"
     23 #include "mozilla/BasePrincipal.h"
     24 #include "mozilla/CheckedInt.h"
     25 #include "mozilla/Components.h"
     26 #include "mozilla/Encoding.h"
     27 #include "mozilla/EventDispatcher.h"
     28 #include "mozilla/EventListenerManager.h"
     29 #include "mozilla/HoldDropJSObjects.h"
     30 #include "mozilla/LoadContext.h"
     31 #include "mozilla/LoadInfo.h"
     32 #include "mozilla/MemoryReporting.h"
     33 #include "mozilla/Preferences.h"
     34 #include "mozilla/PreloaderBase.h"
     35 #include "mozilla/ScopeExit.h"
     36 #include "mozilla/SpinEventLoopUntil.h"
     37 #include "mozilla/StaticPrefs_dom.h"
     38 #include "mozilla/StaticPrefs_network.h"
     39 #include "mozilla/StaticPrefs_privacy.h"
     40 #include "mozilla/dom/AutoSuppressEventHandlingAndSuspend.h"
     41 #include "mozilla/dom/BlobBinding.h"
     42 #include "mozilla/dom/BlobURLProtocolHandler.h"
     43 #include "mozilla/dom/DOMString.h"
     44 #include "mozilla/dom/DocGroup.h"
     45 #include "mozilla/dom/FetchUtil.h"
     46 #include "mozilla/dom/File.h"
     47 #include "mozilla/dom/FileBinding.h"
     48 #include "mozilla/dom/FileCreatorHelper.h"
     49 #include "mozilla/dom/FormData.h"
     50 #include "mozilla/dom/MutableBlobStorage.h"
     51 #include "mozilla/dom/ProgressEvent.h"
     52 #include "mozilla/dom/Promise.h"
     53 #include "mozilla/dom/PromiseNativeHandler.h"
     54 #include "mozilla/dom/ReferrerInfo.h"
     55 #include "mozilla/dom/URLSearchParams.h"
     56 #include "mozilla/dom/UserActivation.h"
     57 #include "mozilla/dom/WorkerError.h"
     58 #include "mozilla/dom/XMLDocument.h"
     59 #include "mozilla/dom/XMLHttpRequestBinding.h"
     60 #include "mozilla/dom/quota/QuotaCommon.h"
     61 #include "mozilla/glean/DomMetrics.h"
     62 #include "mozilla/net/ContentRange.h"
     63 #include "nsAsyncRedirectVerifyHelper.h"
     64 #include "nsCharSeparatedTokenizer.h"
     65 #include "nsContentUtils.h"
     66 #include "nsCycleCollectionParticipant.h"
     67 #include "nsDataChannel.h"
     68 #include "nsError.h"
     69 #include "nsGlobalWindowInner.h"
     70 #include "nsIAuthPrompt.h"
     71 #include "nsIAuthPrompt2.h"
     72 #include "nsIBaseChannel.h"
     73 #include "nsICachingChannel.h"
     74 #include "nsIClassOfService.h"
     75 #include "nsIClassifiedChannel.h"
     76 #include "nsIContentPolicy.h"
     77 #include "nsICookieJarSettings.h"
     78 #include "nsIDOMEventListener.h"
     79 #include "nsIFileChannel.h"
     80 #include "nsIHttpChannel.h"
     81 #include "nsIHttpChannelInternal.h"
     82 #include "nsIInterfaceRequestorUtils.h"
     83 #include "nsIJARChannel.h"
     84 #include "nsIJARURI.h"
     85 #include "nsILoadGroup.h"
     86 #include "nsIPermissionManager.h"
     87 #include "nsIPromptFactory.h"
     88 #include "nsIScriptError.h"
     89 #include "nsISupportsPriority.h"
     90 #include "nsITimedChannel.h"
     91 #include "nsIURI.h"
     92 #include "nsIURIMutator.h"
     93 #include "nsIUploadChannel.h"
     94 #include "nsIUploadChannel2.h"
     95 #include "nsIWindowWatcher.h"
     96 #include "nsMimeTypes.h"
     97 #include "nsNetUtil.h"
     98 #include "nsReadableUtils.h"
     99 #include "nsSandboxFlags.h"
    100 #include "nsStreamListenerWrapper.h"
    101 #include "nsStreamUtils.h"
    102 #include "nsStringStream.h"
    103 #include "nsThreadUtils.h"
    104 #include "nsVariant.h"
    105 #include "nsWrapperCacheInlines.h"
    106 #include "nsXPCOM.h"
    107 #include "nsZipArchive.h"
    108 #include "private/pprio.h"
    109 
    110 // Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
    111 // replaced by FileCreatorHelper#CreateFileW.
    112 #ifdef CreateFile
    113 #  undef CreateFile
    114 #endif
    115 
    116 extern mozilla::LazyLogModule gXMLHttpRequestLog;
    117 
    118 using namespace mozilla::net;
    119 
    120 namespace mozilla::dom {
    121 
    122 using EventType = XMLHttpRequest::EventType;
    123 using Events = XMLHttpRequest::Events;
    124 
    125 // Maximum size that we'll grow an ArrayBuffer instead of doubling,
    126 // once doubling reaches this threshold
    127 const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH = 32 * 1024 * 1024;
    128 // start at 32k to avoid lots of doubling right at the start
    129 const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE = 32 * 1024;
    130 // the maximum Content-Length that we'll preallocate.  1GB.  Must fit
    131 // in an int32_t!
    132 const int32_t XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE =
    133    1 * 1024 * 1024 * 1024LL;
    134 
    135 constexpr nsLiteralString kLiteralString_readystatechange =
    136    u"readystatechange"_ns;
    137 // constexpr nsLiteralString kLiteralString_xmlhttprequest =
    138 // u"xmlhttprequest"_ns;
    139 constexpr nsLiteralString kLiteralString_DOMContentLoaded =
    140    u"DOMContentLoaded"_ns;
    141 constexpr nsLiteralCString kLiteralString_charset = "charset"_ns;
    142 constexpr nsLiteralCString kLiteralString_UTF_8 = "UTF-8"_ns;
    143 
    144 #define NS_PROGRESS_EVENT_INTERVAL 50
    145 #define MAX_SYNC_TIMEOUT_WHEN_UNLOADING 10000 /* 10 secs */
    146 
    147 NS_IMPL_ISUPPORTS(nsXHRParseEndListener, nsIDOMEventListener)
    148 
    149 class nsResumeTimeoutsEvent : public Runnable {
    150 public:
    151  explicit nsResumeTimeoutsEvent(nsPIDOMWindowInner* aWindow)
    152      : Runnable("dom::nsResumeTimeoutsEvent"), mWindow(aWindow) {}
    153 
    154  NS_IMETHOD Run() override {
    155    mWindow->Resume();
    156    return NS_OK;
    157  }
    158 
    159 private:
    160  nsCOMPtr<nsPIDOMWindowInner> mWindow;
    161 };
    162 
    163 // This helper function adds the given load flags to the request's existing
    164 // load flags.
    165 static void AddLoadFlags(nsIRequest* request, nsLoadFlags newFlags) {
    166  nsLoadFlags flags;
    167  request->GetLoadFlags(&flags);
    168  flags |= newFlags;
    169  request->SetLoadFlags(flags);
    170 }
    171 
    172 // We are in a sync event loop.
    173 #define NOT_CALLABLE_IN_SYNC_SEND_RV                               \
    174  if (mFlagSyncLooping || mEventDispatchingSuspended) {            \
    175    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT); \
    176    return;                                                        \
    177  }
    178 
    179 /////////////////////////////////////////////
    180 //
    181 //
    182 /////////////////////////////////////////////
    183 
    184 #ifdef DEBUG
    185 
    186 // In debug mode, annotate WorkerRefs with the name of the function being
    187 // invoked for increased scrutability.  Save the previous value on the stack.
    188 namespace {
    189 struct DebugWorkerRefs {
    190  Mutex& mMutex;
    191  RefPtr<ThreadSafeWorkerRef> mTSWorkerRef;
    192  nsCString mPrev;
    193 
    194  DebugWorkerRefs(XMLHttpRequestMainThread& aXHR, const std::string& aStatus)
    195      : mMutex(aXHR.mTSWorkerRefMutex) {
    196    MutexAutoLock lock(mMutex);
    197 
    198    mTSWorkerRef = aXHR.mTSWorkerRef;
    199 
    200    if (!mTSWorkerRef) {
    201      return;
    202    }
    203 
    204    MOZ_ASSERT(mTSWorkerRef->Private());
    205 
    206    nsCString status(aStatus.c_str());
    207    mPrev = GET_WORKERREF_DEBUG_STATUS(mTSWorkerRef->Ref());
    208    SET_WORKERREF_DEBUG_STATUS(mTSWorkerRef->Ref(), status);
    209  }
    210 
    211  ~DebugWorkerRefs() {
    212    MutexAutoLock lock(mMutex);
    213 
    214    if (!mTSWorkerRef) {
    215      return;
    216    }
    217 
    218    MOZ_ASSERT(mTSWorkerRef->Private());
    219 
    220    SET_WORKERREF_DEBUG_STATUS(mTSWorkerRef->Ref(), mPrev);
    221 
    222    mTSWorkerRef = nullptr;
    223  }
    224 };
    225 }  // namespace
    226 
    227 #  define STREAM_STRING(stuff)                                    \
    228    (((const std::ostringstream&)(std::ostringstream() << stuff)) \
    229         .str())  // NOLINT
    230 
    231 #  if 1  // Disabling because bug 1855699
    232 #    define DEBUG_WORKERREFS void()
    233 #    define DEBUG_WORKERREFS1(x) void()
    234 #  else
    235 
    236 #    define DEBUG_WORKERREFS \
    237      DebugWorkerRefs MOZ_UNIQUE_VAR(debugWR__)(*this, __func__)
    238 
    239 #    define DEBUG_WORKERREFS1(x)                 \
    240      DebugWorkerRefs MOZ_UNIQUE_VAR(debugWR__)( \
    241          *this, STREAM_STRING(__func__ << ": " << x))  // NOLINT
    242 
    243 #  endif
    244 
    245 #else
    246 #  define DEBUG_WORKERREFS void()
    247 #  define DEBUG_WORKERREFS1(x) void()
    248 #endif  // DEBUG
    249 
    250 bool XMLHttpRequestMainThread::sDontWarnAboutSyncXHR = false;
    251 
    252 XMLHttpRequestMainThread::XMLHttpRequestMainThread(
    253    nsIGlobalObject* aGlobalObject)
    254    : XMLHttpRequest(aGlobalObject),
    255 #ifdef DEBUG
    256      mTSWorkerRefMutex("Debug WorkerRefs"),
    257 #endif
    258      mResponseBodyDecodedPos(0),
    259      mResponseType(XMLHttpRequestResponseType::_empty),
    260      mState(XMLHttpRequest_Binding::UNSENT),
    261      mFlagSynchronous(false),
    262      mFlagAborted(false),
    263      mFlagParseBody(false),
    264      mFlagSyncLooping(false),
    265      mFlagBackgroundRequest(false),
    266      mFlagHadUploadListenersOnSend(false),
    267      mFlagACwithCredentials(false),
    268      mFlagTimedOut(false),
    269      mFlagDeleted(false),
    270      mFlagSend(false),
    271      mUploadTransferred(0),
    272      mUploadTotal(0),
    273      mUploadComplete(true),
    274      mProgressSinceLastProgressEvent(false),
    275      mRequestSentTime(0),
    276      mTimeoutMilliseconds(0),
    277      mErrorLoad(ErrorType::eOK),
    278      mErrorLoadDetail(NS_OK),
    279      mErrorParsingXML(false),
    280      mWaitingForOnStopRequest(false),
    281      mProgressTimerIsActive(false),
    282      mIsHtml(false),
    283      mWarnAboutSyncHtml(false),
    284      mLoadTotal(-1),
    285      mLoadTransferred(0),
    286      mIsSystem(false),
    287      mIsAnon(false),
    288      mAlreadyGotStopRequest(false),
    289      mResultJSON(JS::UndefinedValue()),
    290      mArrayBufferBuilder(new ArrayBufferBuilder()),
    291      mResultArrayBuffer(nullptr),
    292      mIsMappedArrayBuffer(false),
    293      mXPCOMifier(nullptr),
    294      mEventDispatchingSuspended(false),
    295      mEofDecoded(false),
    296      mDelayedDoneNotifier(nullptr) {
    297  DEBUG_WORKERREFS;
    298  mozilla::HoldJSObjects(this);
    299 }
    300 
    301 XMLHttpRequestMainThread::~XMLHttpRequestMainThread() {
    302  DEBUG_WORKERREFS;
    303  MOZ_ASSERT(
    304      !mDelayedDoneNotifier,
    305      "How can we have mDelayedDoneNotifier, which owns us, in destructor?");
    306 
    307  mFlagDeleted = true;
    308 
    309  if ((mState == XMLHttpRequest_Binding::OPENED && mFlagSend) ||
    310      mState == XMLHttpRequest_Binding::LOADING) {
    311    Abort();
    312  }
    313 
    314  if (mParseEndListener) {
    315    mParseEndListener->SetIsStale();
    316    mParseEndListener = nullptr;
    317  }
    318 
    319  MOZ_ASSERT(!mFlagSyncLooping, "we rather crash than hang");
    320  mFlagSyncLooping = false;
    321 
    322  mozilla::DropJSObjects(this);
    323 }
    324 
    325 void XMLHttpRequestMainThread::Construct(
    326    nsIPrincipal* aPrincipal, nsICookieJarSettings* aCookieJarSettings,
    327    bool aForWorker, nsIURI* aBaseURI /* = nullptr */,
    328    nsILoadGroup* aLoadGroup /* = nullptr */,
    329    PerformanceStorage* aPerformanceStorage /* = nullptr */,
    330    nsICSPEventListener* aCSPEventListener /* = nullptr */) {
    331  DEBUG_WORKERREFS;
    332  MOZ_ASSERT(aPrincipal);
    333  mPrincipal = aPrincipal;
    334  mBaseURI = aBaseURI;
    335  mLoadGroup = aLoadGroup;
    336  mCookieJarSettings = aCookieJarSettings;
    337  mForWorker = aForWorker;
    338  mPerformanceStorage = aPerformanceStorage;
    339  mCSPEventListener = aCSPEventListener;
    340 }
    341 
    342 void XMLHttpRequestMainThread::InitParameters(bool aAnon, bool aSystem) {
    343  DEBUG_WORKERREFS;
    344  if (!aAnon && !aSystem) {
    345    return;
    346  }
    347 
    348  // Check for permissions.
    349  // Chrome is always allowed access, so do the permission check only
    350  // for non-chrome pages.
    351  if (!IsSystemXHR() && aSystem) {
    352    nsIGlobalObject* global = GetOwnerGlobal();
    353    if (NS_WARN_IF(!global)) {
    354      SetParameters(aAnon, false);
    355      return;
    356    }
    357 
    358    nsIPrincipal* principal = global->PrincipalOrNull();
    359    if (NS_WARN_IF(!principal)) {
    360      SetParameters(aAnon, false);
    361      return;
    362    }
    363 
    364    nsCOMPtr<nsIPermissionManager> permMgr =
    365        components::PermissionManager::Service();
    366    if (NS_WARN_IF(!permMgr)) {
    367      SetParameters(aAnon, false);
    368      return;
    369    }
    370 
    371    uint32_t permission;
    372    nsresult rv = permMgr->TestPermissionFromPrincipal(
    373        principal, "systemXHR"_ns, &permission);
    374    if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
    375      SetParameters(aAnon, false);
    376      return;
    377    }
    378  }
    379 
    380  SetParameters(aAnon, aSystem);
    381 }
    382 
    383 void XMLHttpRequestMainThread::SetClientInfoAndController(
    384    const ClientInfo& aClientInfo,
    385    const Maybe<ServiceWorkerDescriptor>& aController) {
    386  mClientInfo.emplace(aClientInfo);
    387  mController = aController;
    388 }
    389 
    390 void XMLHttpRequestMainThread::ResetResponse() {
    391  mResponseXML = nullptr;
    392  mResponseBody.Truncate();
    393  TruncateResponseText();
    394  mResponseBlobImpl = nullptr;
    395  mResponseBlob = nullptr;
    396  mBlobStorage = nullptr;
    397  mResultArrayBuffer = nullptr;
    398  mArrayBufferBuilder = new ArrayBufferBuilder();
    399  mResultJSON.setUndefined();
    400  mLoadTransferred = 0;
    401  mResponseBodyDecodedPos = 0;
    402  mEofDecoded = false;
    403 }
    404 
    405 NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestMainThread)
    406 
    407 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestMainThread,
    408                                                  XMLHttpRequestEventTarget)
    409  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
    410  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML)
    411 
    412  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener)
    413 
    414  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob)
    415  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks)
    416 
    417  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
    418  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink)
    419 
    420  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
    421 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    422 
    423 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestMainThread,
    424                                                XMLHttpRequestEventTarget)
    425  tmp->mResultArrayBuffer = nullptr;
    426  tmp->mArrayBufferBuilder = nullptr;
    427  tmp->mResultJSON.setUndefined();
    428  tmp->mResponseBlobImpl = nullptr;
    429 
    430  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
    431  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
    432 
    433  NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)
    434 
    435  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob)
    436  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks)
    437 
    438  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink)
    439  NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink)
    440 
    441  NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
    442 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    443 
    444 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestMainThread,
    445                                               XMLHttpRequestEventTarget)
    446  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
    447  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultJSON)
    448 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    449 
    450 bool XMLHttpRequestMainThread::IsCertainlyAliveForCC() const {
    451  return mWaitingForOnStopRequest;
    452 }
    453 
    454 // QueryInterface implementation for XMLHttpRequestMainThread
    455 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestMainThread)
    456  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
    457  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
    458  NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
    459  NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
    460  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
    461  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
    462  NS_INTERFACE_MAP_ENTRY(nsINamed)
    463  NS_INTERFACE_MAP_ENTRY(nsISizeOfEventTarget)
    464 NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget)
    465 
    466 NS_IMPL_ADDREF_INHERITED(XMLHttpRequestMainThread, XMLHttpRequestEventTarget)
    467 NS_IMPL_RELEASE_INHERITED(XMLHttpRequestMainThread, XMLHttpRequestEventTarget)
    468 
    469 void XMLHttpRequestMainThread::DisconnectFromOwner() {
    470  XMLHttpRequestEventTarget::DisconnectFromOwner();
    471  // Worker-owned XHRs have their own complicated state machine that does not
    472  // expect Abort() to be called here.  The worker state machine cleanup will
    473  // take care of ensuring the XHR is aborted in a timely fashion since the
    474  // worker itself will inherently be canceled at the same time this is
    475  // happening.
    476  if (!mForWorker) {
    477    Abort();
    478  }
    479 }
    480 
    481 size_t XMLHttpRequestMainThread::SizeOfEventTargetIncludingThis(
    482    MallocSizeOf aMallocSizeOf) const {
    483  size_t n = aMallocSizeOf(this);
    484  n += mResponseBody.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
    485 
    486  // Why is this safe?  Because no-one else will report this string.  The
    487  // other possible sharers of this string are as follows.
    488  //
    489  // - The JS engine could hold copies if the JS code holds references, e.g.
    490  //   |var text = XHR.responseText|.  However, those references will be via JS
    491  //   external strings, for which the JS memory reporter does *not* report the
    492  //   chars.
    493  //
    494  // - Binary extensions, but they're *extremely* unlikely to do any memory
    495  //   reporting.
    496  //
    497  n += mResponseText.SizeOfThis(aMallocSizeOf);
    498 
    499  return n;
    500 
    501  // Measurement of the following members may be added later if DMD finds it is
    502  // worthwhile:
    503  // - lots
    504 }
    505 
    506 static void LogMessage(
    507    const char* aWarning, nsPIDOMWindowInner* aWindow,
    508    const nsTArray<nsString>& aParams = nsTArray<nsString>()) {
    509  nsCOMPtr<Document> doc;
    510  if (aWindow) {
    511    doc = aWindow->GetExtantDoc();
    512  }
    513  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, doc,
    514                                  nsContentUtils::eDOM_PROPERTIES, aWarning,
    515                                  aParams);
    516 }
    517 
    518 Document* XMLHttpRequestMainThread::GetResponseXML(ErrorResult& aRv) {
    519  if (mResponseType != XMLHttpRequestResponseType::_empty &&
    520      mResponseType != XMLHttpRequestResponseType::Document) {
    521    aRv.ThrowInvalidStateError(
    522        "responseXML is only available if responseType is '' or 'document'.");
    523    return nullptr;
    524  }
    525  if (mWarnAboutSyncHtml) {
    526    mWarnAboutSyncHtml = false;
    527    LogMessage("HTMLSyncXHRWarning", GetOwnerWindow());
    528  }
    529  if (mState != XMLHttpRequest_Binding::DONE) {
    530    return nullptr;
    531  }
    532  return mResponseXML;
    533 }
    534 
    535 /*
    536 * This piece copied from XMLDocument, we try to get the charset
    537 * from HTTP headers.
    538 */
    539 nsresult XMLHttpRequestMainThread::DetectCharset() {
    540  DEBUG_WORKERREFS;
    541  mDecoder = nullptr;
    542 
    543  if (mResponseType != XMLHttpRequestResponseType::_empty &&
    544      mResponseType != XMLHttpRequestResponseType::Text &&
    545      mResponseType != XMLHttpRequestResponseType::Json) {
    546    return NS_OK;
    547  }
    548 
    549  nsAutoCString charsetVal;
    550  const Encoding* encoding;
    551  bool ok = mChannel && NS_SUCCEEDED(mChannel->GetContentCharset(charsetVal)) &&
    552            (encoding = Encoding::ForLabel(charsetVal));
    553  if (!ok) {
    554    // MS documentation states UTF-8 is default for responseText
    555    encoding = UTF_8_ENCODING;
    556  }
    557 
    558  if (mResponseType == XMLHttpRequestResponseType::Json &&
    559      encoding != UTF_8_ENCODING) {
    560    // The XHR spec says only UTF-8 is supported for responseType == "json"
    561    LogMessage("JSONCharsetWarning", GetOwnerWindow());
    562    encoding = UTF_8_ENCODING;
    563  }
    564 
    565  // Only sniff the BOM for non-JSON responseTypes
    566  if (mResponseType == XMLHttpRequestResponseType::Json) {
    567    mDecoder = encoding->NewDecoderWithBOMRemoval();
    568  } else {
    569    mDecoder = encoding->NewDecoder();
    570  }
    571 
    572  return NS_OK;
    573 }
    574 
    575 nsresult XMLHttpRequestMainThread::AppendToResponseText(
    576    Span<const uint8_t> aBuffer, bool aLast) {
    577  // Call this with an empty buffer to send the decoder the signal
    578  // that we have hit the end of the stream.
    579 
    580  NS_ENSURE_STATE(mDecoder);
    581 
    582  CheckedInt<size_t> destBufferLen =
    583      mDecoder->MaxUTF16BufferLength(aBuffer.Length());
    584 
    585  {  // scope for holding the mutex that protects mResponseText
    586    XMLHttpRequestStringWriterHelper helper(mResponseText);
    587 
    588    uint32_t len = helper.Length();
    589 
    590    destBufferLen += len;
    591    if (!destBufferLen.isValid() || destBufferLen.value() > UINT32_MAX) {
    592      return NS_ERROR_OUT_OF_MEMORY;
    593    }
    594 
    595    auto handleOrErr = helper.BulkWrite(destBufferLen.value());
    596    if (handleOrErr.isErr()) {
    597      return handleOrErr.unwrapErr();
    598    }
    599 
    600    auto handle = handleOrErr.unwrap();
    601 
    602    uint32_t result;
    603    size_t read;
    604    size_t written;
    605    std::tie(result, read, written, std::ignore) =
    606        mDecoder->DecodeToUTF16(aBuffer, handle.AsSpan().From(len), aLast);
    607    MOZ_ASSERT(result == kInputEmpty);
    608    MOZ_ASSERT(read == aBuffer.Length());
    609    len += written;
    610    MOZ_ASSERT(len <= destBufferLen.value());
    611    handle.Finish(len, false);
    612  }  // release mutex
    613 
    614  if (aLast) {
    615    // Drop the finished decoder to avoid calling into a decoder
    616    // that has finished.
    617    mDecoder = nullptr;
    618    mEofDecoded = true;
    619  }
    620  return NS_OK;
    621 }
    622 
    623 void XMLHttpRequestMainThread::GetResponseText(DOMString& aResponseText,
    624                                               ErrorResult& aRv) {
    625  MOZ_DIAGNOSTIC_ASSERT(!mForWorker);
    626 
    627  XMLHttpRequestStringSnapshot snapshot;
    628  GetResponseText(snapshot, aRv);
    629  if (aRv.Failed()) {
    630    return;
    631  }
    632 
    633  if (!snapshot.GetAsString(aResponseText)) {
    634    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    635    return;
    636  }
    637 }
    638 
    639 void XMLHttpRequestMainThread::GetResponseText(
    640    XMLHttpRequestStringSnapshot& aSnapshot, ErrorResult& aRv) {
    641  aSnapshot.Reset();
    642 
    643  if (mResponseType != XMLHttpRequestResponseType::_empty &&
    644      mResponseType != XMLHttpRequestResponseType::Text) {
    645    aRv.ThrowInvalidStateError(
    646        "responseText is only available if responseType is '' or 'text'.");
    647    return;
    648  }
    649 
    650  if (mState != XMLHttpRequest_Binding::LOADING &&
    651      mState != XMLHttpRequest_Binding::DONE) {
    652    return;
    653  }
    654 
    655  // Main Fetch step 18 requires to ignore body for head/connect methods.
    656  if (mRequestMethod.EqualsLiteral("HEAD") ||
    657      mRequestMethod.EqualsLiteral("CONNECT")) {
    658    return;
    659  }
    660 
    661  // We only decode text lazily if we're also parsing to a doc.
    662  // Also, if we've decoded all current data already, then no need to decode
    663  // more.
    664  if ((!mResponseXML && !mErrorParsingXML) ||
    665      (mResponseBodyDecodedPos == mResponseBody.Length() &&
    666       (mState != XMLHttpRequest_Binding::DONE || mEofDecoded))) {
    667    mResponseText.CreateSnapshot(aSnapshot);
    668    return;
    669  }
    670 
    671  MatchCharsetAndDecoderToResponseDocument();
    672 
    673  MOZ_ASSERT(mResponseBodyDecodedPos < mResponseBody.Length() ||
    674                 mState == XMLHttpRequest_Binding::DONE,
    675             "Unexpected mResponseBodyDecodedPos");
    676  Span<const uint8_t> span = mResponseBody;
    677  aRv = AppendToResponseText(span.From(mResponseBodyDecodedPos),
    678                             mState == XMLHttpRequest_Binding::DONE);
    679  if (aRv.Failed()) {
    680    return;
    681  }
    682 
    683  mResponseBodyDecodedPos = mResponseBody.Length();
    684 
    685  if (mEofDecoded) {
    686    // Free memory buffer which we no longer need
    687    mResponseBody.Truncate();
    688    mResponseBodyDecodedPos = 0;
    689  }
    690 
    691  mResponseText.CreateSnapshot(aSnapshot);
    692 }
    693 
    694 nsresult XMLHttpRequestMainThread::CreateResponseParsedJSON(JSContext* aCx) {
    695  if (!aCx) {
    696    return NS_ERROR_FAILURE;
    697  }
    698 
    699  nsAutoString string;
    700  nsresult rv = GetResponseTextForJSON(string);
    701  if (NS_WARN_IF(NS_FAILED(rv))) {
    702    return rv;
    703  }
    704 
    705  // The Unicode converter has already zapped the BOM if there was one
    706  JS::Rooted<JS::Value> value(aCx);
    707  if (!JS_ParseJSON(aCx, string.BeginReading(), string.Length(), &value)) {
    708    return NS_ERROR_FAILURE;
    709  }
    710 
    711  mResultJSON = value;
    712  return NS_OK;
    713 }
    714 
    715 void XMLHttpRequestMainThread::SetResponseType(
    716    XMLHttpRequestResponseType aResponseType, ErrorResult& aRv) {
    717  NOT_CALLABLE_IN_SYNC_SEND_RV
    718 
    719  if (mState == XMLHttpRequest_Binding::LOADING ||
    720      mState == XMLHttpRequest_Binding::DONE) {
    721    aRv.ThrowInvalidStateError(
    722        "Cannot set 'responseType' property on XMLHttpRequest after 'send()' "
    723        "(when its state is LOADING or DONE).");
    724    return;
    725  }
    726 
    727  // sync request is not allowed setting responseType in window context
    728  if (HasOrHasHadOwnerWindow() && mState != XMLHttpRequest_Binding::UNSENT &&
    729      mFlagSynchronous) {
    730    LogMessage("ResponseTypeSyncXHRWarning", GetOwnerWindow());
    731    aRv.ThrowInvalidAccessError(
    732        "synchronous XMLHttpRequests do not support timeout and responseType");
    733    return;
    734  }
    735 
    736  // Set the responseType attribute's value to the given value.
    737  SetResponseTypeRaw(aResponseType);
    738 }
    739 
    740 void XMLHttpRequestMainThread::GetResponse(
    741    JSContext* aCx, JS::MutableHandle<JS::Value> aResponse, ErrorResult& aRv) {
    742  MOZ_DIAGNOSTIC_ASSERT(!mForWorker);
    743 
    744  switch (mResponseType) {
    745    case XMLHttpRequestResponseType::_empty:
    746    case XMLHttpRequestResponseType::Text: {
    747      DOMString str;
    748      GetResponseText(str, aRv);
    749      if (aRv.Failed()) {
    750        return;
    751      }
    752      if (!xpc::StringToJsval(aCx, str, aResponse)) {
    753        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    754      }
    755      return;
    756    }
    757 
    758    case XMLHttpRequestResponseType::Arraybuffer: {
    759      if (mState != XMLHttpRequest_Binding::DONE) {
    760        aResponse.setNull();
    761        return;
    762      }
    763 
    764      if (!mResultArrayBuffer) {
    765        mResultArrayBuffer = mArrayBufferBuilder->TakeArrayBuffer(aCx);
    766        if (!mResultArrayBuffer) {
    767          aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    768          return;
    769        }
    770      }
    771      aResponse.setObject(*mResultArrayBuffer);
    772      return;
    773    }
    774    case XMLHttpRequestResponseType::Blob: {
    775      if (mState != XMLHttpRequest_Binding::DONE) {
    776        aResponse.setNull();
    777        return;
    778      }
    779 
    780      if (!mResponseBlobImpl) {
    781        aResponse.setNull();
    782        return;
    783      }
    784 
    785      if (!mResponseBlob) {
    786        mResponseBlob = Blob::Create(GetOwnerGlobal(), mResponseBlobImpl);
    787      }
    788 
    789      if (!GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse)) {
    790        aResponse.setNull();
    791      }
    792 
    793      return;
    794    }
    795    case XMLHttpRequestResponseType::Document: {
    796      if (!mResponseXML || mState != XMLHttpRequest_Binding::DONE) {
    797        aResponse.setNull();
    798        return;
    799      }
    800 
    801      aRv =
    802          nsContentUtils::WrapNative(aCx, ToSupports(mResponseXML), aResponse);
    803      return;
    804    }
    805    case XMLHttpRequestResponseType::Json: {
    806      if (mState != XMLHttpRequest_Binding::DONE) {
    807        aResponse.setNull();
    808        return;
    809      }
    810 
    811      if (mResultJSON.isUndefined()) {
    812        aRv = CreateResponseParsedJSON(aCx);
    813        TruncateResponseText();
    814        if (aRv.Failed()) {
    815          // Per spec, errors aren't propagated. null is returned instead.
    816          aRv = NS_OK;
    817          // It would be nice to log the error to the console. That's hard to
    818          // do without calling window.onerror as a side effect, though.
    819          JS_ClearPendingException(aCx);
    820          mResultJSON.setNull();
    821        }
    822      }
    823      aResponse.set(mResultJSON);
    824      return;
    825    }
    826    default:
    827      NS_ERROR("Should not happen");
    828  }
    829 
    830  aResponse.setNull();
    831 }
    832 
    833 already_AddRefed<BlobImpl> XMLHttpRequestMainThread::GetResponseBlobImpl() {
    834  MOZ_DIAGNOSTIC_ASSERT(mForWorker);
    835  MOZ_DIAGNOSTIC_ASSERT(mResponseType == XMLHttpRequestResponseType::Blob);
    836 
    837  if (mState != XMLHttpRequest_Binding::DONE) {
    838    return nullptr;
    839  }
    840 
    841  RefPtr<BlobImpl> blobImpl = mResponseBlobImpl;
    842  return blobImpl.forget();
    843 }
    844 
    845 already_AddRefed<ArrayBufferBuilder>
    846 XMLHttpRequestMainThread::GetResponseArrayBufferBuilder() {
    847  MOZ_DIAGNOSTIC_ASSERT(mForWorker);
    848  MOZ_DIAGNOSTIC_ASSERT(mResponseType ==
    849                        XMLHttpRequestResponseType::Arraybuffer);
    850 
    851  if (mState != XMLHttpRequest_Binding::DONE) {
    852    return nullptr;
    853  }
    854 
    855  RefPtr<ArrayBufferBuilder> builder = mArrayBufferBuilder;
    856  return builder.forget();
    857 }
    858 
    859 nsresult XMLHttpRequestMainThread::GetResponseTextForJSON(nsAString& aString) {
    860  if (mState != XMLHttpRequest_Binding::DONE) {
    861    aString.SetIsVoid(true);
    862    return NS_OK;
    863  }
    864 
    865  if (!mResponseText.GetAsString(aString)) {
    866    return NS_ERROR_OUT_OF_MEMORY;
    867  }
    868 
    869  return NS_OK;
    870 }
    871 
    872 bool XMLHttpRequestMainThread::IsCrossSiteCORSRequest() const {
    873  if (!mChannel) {
    874    return false;
    875  }
    876 
    877  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
    878  return loadInfo->GetTainting() == LoadTainting::CORS;
    879 }
    880 
    881 bool XMLHttpRequestMainThread::IsDeniedCrossSiteCORSRequest() {
    882  if (IsCrossSiteCORSRequest()) {
    883    nsresult rv;
    884    mChannel->GetStatus(&rv);
    885    if (NS_FAILED(rv)) {
    886      return true;
    887    }
    888  }
    889  return false;
    890 }
    891 
    892 bool XMLHttpRequestMainThread::BadContentRangeRequested() {
    893  if (!mChannel) {
    894    return false;
    895  }
    896  // Only nsIBaseChannel supports this
    897  nsCOMPtr<nsIBaseChannel> baseChan = do_QueryInterface(mChannel);
    898  if (!baseChan) {
    899    return false;
    900  }
    901  // A bad range was requested if the channel has no content range
    902  // despite the request specifying a range header.
    903  return !baseChan->ContentRange() && mAuthorRequestHeaders.Has("range");
    904 }
    905 
    906 RefPtr<mozilla::net::ContentRange>
    907 XMLHttpRequestMainThread::GetRequestedContentRange() const {
    908  MOZ_ASSERT(mChannel);
    909  nsCOMPtr<nsIBaseChannel> baseChan = do_QueryInterface(mChannel);
    910  if (!baseChan) {
    911    return nullptr;
    912  }
    913  return baseChan->ContentRange();
    914 }
    915 
    916 void XMLHttpRequestMainThread::GetContentRangeHeader(nsACString& out) const {
    917  if (!IsBlobURI(mRequestURL)) {
    918    out.SetIsVoid(true);
    919    return;
    920  }
    921  RefPtr<mozilla::net::ContentRange> range = GetRequestedContentRange();
    922  if (range) {
    923    range->AsHeader(out);
    924  } else {
    925    out.SetIsVoid(true);
    926  }
    927 }
    928 
    929 void XMLHttpRequestMainThread::GetResponseURL(nsACString& aUrl) {
    930  aUrl.Truncate();
    931 
    932  if ((mState == XMLHttpRequest_Binding::UNSENT ||
    933       mState == XMLHttpRequest_Binding::OPENED) ||
    934      !mChannel) {
    935    return;
    936  }
    937 
    938  // Make sure we don't leak responseURL information from denied cross-site
    939  // requests.
    940  if (IsDeniedCrossSiteCORSRequest()) {
    941    return;
    942  }
    943 
    944  nsCOMPtr<nsIURI> responseUrl;
    945  if (NS_FAILED(NS_GetFinalChannelURI(mChannel, getter_AddRefs(responseUrl)))) {
    946    return;
    947  }
    948 
    949  responseUrl->GetSpecIgnoringRef(aUrl);
    950 }
    951 
    952 uint32_t XMLHttpRequestMainThread::GetStatus(ErrorResult& aRv) {
    953  // Make sure we don't leak status information from denied cross-site
    954  // requests.
    955  if (IsDeniedCrossSiteCORSRequest()) {
    956    return 0;
    957  }
    958 
    959  if (mState == XMLHttpRequest_Binding::UNSENT ||
    960      mState == XMLHttpRequest_Binding::OPENED) {
    961    return 0;
    962  }
    963 
    964  if (mErrorLoad != ErrorType::eOK) {
    965    // Let's simulate the http protocol for jar/app requests:
    966    nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
    967    if (jarChannel) {
    968      nsresult status;
    969      mChannel->GetStatus(&status);
    970 
    971      if (status == NS_ERROR_FILE_NOT_FOUND) {
    972        return 404;  // Not Found
    973      } else {
    974        return 500;  // Internal Error
    975      }
    976    }
    977 
    978    return 0;
    979  }
    980 
    981  nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
    982  if (!httpChannel) {
    983    // Pretend like we got a 200/206 response, since our load was successful
    984    return GetRequestedContentRange() ? 206 : 200;
    985  }
    986 
    987  uint32_t status;
    988  nsresult rv = httpChannel->GetResponseStatus(&status);
    989  if (NS_FAILED(rv)) {
    990    status = 0;
    991  }
    992 
    993  return status;
    994 }
    995 
    996 void XMLHttpRequestMainThread::GetStatusText(nsACString& aStatusText,
    997                                             ErrorResult& aRv) {
    998  // Return an empty status text on all error loads.
    999  aStatusText.Truncate();
   1000 
   1001  // Make sure we don't leak status information from denied cross-site
   1002  // requests.
   1003  if (IsDeniedCrossSiteCORSRequest()) {
   1004    return;
   1005  }
   1006 
   1007  // Check the current XHR state to see if it is valid to obtain the statusText
   1008  // value.  This check is to prevent the status text for redirects from being
   1009  // available before all the redirects have been followed and HTTP headers have
   1010  // been received.
   1011  if (mState == XMLHttpRequest_Binding::UNSENT ||
   1012      mState == XMLHttpRequest_Binding::OPENED) {
   1013    return;
   1014  }
   1015 
   1016  if (mErrorLoad != ErrorType::eOK) {
   1017    return;
   1018  }
   1019 
   1020  nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
   1021  if (httpChannel) {
   1022    (void)httpChannel->GetResponseStatusText(aStatusText);
   1023  } else {
   1024    aStatusText.AssignLiteral("OK");
   1025  }
   1026 }
   1027 
   1028 void XMLHttpRequestMainThread::TerminateOngoingFetch(nsresult detail) {
   1029  DEBUG_WORKERREFS;
   1030  if ((mState == XMLHttpRequest_Binding::OPENED && mFlagSend) ||
   1031      mState == XMLHttpRequest_Binding::HEADERS_RECEIVED ||
   1032      mState == XMLHttpRequest_Binding::LOADING) {
   1033    MOZ_LOG(gXMLHttpRequestLog, LogLevel::Info,
   1034            ("%p TerminateOngoingFetch(0x%" PRIx32 ")", this,
   1035             static_cast<uint32_t>(detail)));
   1036    CloseRequest(detail);
   1037  }
   1038 }
   1039 
   1040 void XMLHttpRequestMainThread::CloseRequest(nsresult detail) {
   1041  DEBUG_WORKERREFS;
   1042  mWaitingForOnStopRequest = false;
   1043  mErrorLoad = ErrorType::eTerminated;
   1044  mErrorLoadDetail = detail;
   1045  if (mChannel) {
   1046    mChannel->CancelWithReason(NS_BINDING_ABORTED,
   1047                               "XMLHttpRequestMainThread::CloseRequest"_ns);
   1048  }
   1049  CancelTimeoutTimer();
   1050 }
   1051 
   1052 void XMLHttpRequestMainThread::CloseRequestWithError(
   1053    const ErrorProgressEventType& aType) {
   1054  DEBUG_WORKERREFS;
   1055  MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug,
   1056          ("%p CloseRequestWithError(%s)", this, aType.cStr));
   1057 
   1058  CloseRequest(aType.errorCode);
   1059 
   1060  ResetResponse();
   1061 
   1062  // If we're in the destructor, don't risk dispatching an event.
   1063  if (mFlagDeleted) {
   1064    mFlagSyncLooping = false;
   1065    return;
   1066  }
   1067 
   1068  if (mState != XMLHttpRequest_Binding::UNSENT &&
   1069      !(mState == XMLHttpRequest_Binding::OPENED && !mFlagSend) &&
   1070      mState != XMLHttpRequest_Binding::DONE) {
   1071    ChangeState(XMLHttpRequest_Binding::DONE, true);
   1072 
   1073    if (!mFlagSyncLooping) {
   1074      if (mUpload && !mUploadComplete) {
   1075        mUploadComplete = true;
   1076        DispatchProgressEvent(mUpload, aType, 0, -1);
   1077      }
   1078      DispatchProgressEvent(this, aType, 0, -1);
   1079    }
   1080  }
   1081 
   1082  // The ChangeState call above calls onreadystatechange handlers which
   1083  // if they load a new url will cause XMLHttpRequestMainThread::Open to clear
   1084  // the abort state bit. If this occurs we're not uninitialized (bug 361773).
   1085  if (mFlagAborted) {
   1086    ChangeState(XMLHttpRequest_Binding::UNSENT, false);  // IE seems to do it
   1087  }
   1088 
   1089  mFlagSyncLooping = false;
   1090 }
   1091 
   1092 void XMLHttpRequestMainThread::RequestErrorSteps(
   1093    const ProgressEventType aEventType, const nsresult aOptionalException,
   1094    ErrorResult& aRv) {
   1095  MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug,
   1096          ("%p RequestErrorSteps(%s,0x%" PRIx32 ")", this, aEventType.cStr,
   1097           static_cast<uint32_t>(aOptionalException)));
   1098 
   1099  // Cancel our timers first before setting our state to done, so we don't
   1100  // trip any assertions if one fires and asserts that state != done.
   1101  CancelTimeoutTimer();
   1102  CancelSyncTimeoutTimer();
   1103  StopProgressEventTimer();
   1104 
   1105  // Step 1
   1106  mState = XMLHttpRequest_Binding::DONE;
   1107 
   1108  // Step 2
   1109  mFlagSend = false;
   1110 
   1111  // Step 3
   1112  ResetResponse();
   1113 
   1114  // If we're in the destructor, don't risk dispatching an event.
   1115  if (mFlagDeleted) {
   1116    mFlagSyncLooping = false;
   1117    return;
   1118  }
   1119 
   1120  // Step 4
   1121  if (mFlagSynchronous && NS_FAILED(aOptionalException)) {
   1122    aRv.Throw(aOptionalException);
   1123    return;
   1124  }
   1125 
   1126  // Step 5
   1127  FireReadystatechangeEvent();
   1128 
   1129  // Step 6
   1130  if (mUpload && !mUploadComplete) {
   1131    // Step 6-1
   1132    mUploadComplete = true;
   1133 
   1134    // Step 6-2
   1135    if (mFlagHadUploadListenersOnSend) {
   1136      // Steps 6-3, 6-4 (loadend is fired for us)
   1137      DispatchProgressEvent(mUpload, aEventType, 0, -1);
   1138    }
   1139  }
   1140 
   1141  // Steps 7 and 8 (loadend is fired for us)
   1142  DispatchProgressEvent(this, aEventType, 0, -1);
   1143 }
   1144 
   1145 void XMLHttpRequestMainThread::Abort(ErrorResult& aRv) {
   1146  NOT_CALLABLE_IN_SYNC_SEND_RV
   1147  MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("%p Abort()", this));
   1148  AbortInternal(aRv);
   1149 }
   1150 
   1151 void XMLHttpRequestMainThread::AbortInternal(ErrorResult& aRv) {
   1152  MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("%p AbortInternal()", this));
   1153  mFlagAborted = true;
   1154  DisconnectDoneNotifier();
   1155 
   1156  // Step 1
   1157  TerminateOngoingFetch(NS_ERROR_DOM_ABORT_ERR);
   1158 
   1159  // Step 2
   1160  if ((mState == XMLHttpRequest_Binding::OPENED && mFlagSend) ||
   1161      mState == XMLHttpRequest_Binding::HEADERS_RECEIVED ||
   1162      mState == XMLHttpRequest_Binding::LOADING) {
   1163    RequestErrorSteps(Events::abort, NS_ERROR_DOM_ABORT_ERR, aRv);
   1164  }
   1165 
   1166  // Step 3
   1167  if (mState == XMLHttpRequest_Binding::DONE) {
   1168    ChangeState(XMLHttpRequest_Binding::UNSENT,
   1169                false);  // no ReadystateChange event
   1170  }
   1171 
   1172  mFlagSyncLooping = false;
   1173 }
   1174 
   1175 /*Method that checks if it is safe to expose a header value to the client.
   1176 It is used to check what headers are exposed for CORS requests.*/
   1177 bool XMLHttpRequestMainThread::IsSafeHeader(
   1178    const nsACString& aHeader, NotNull<nsIHttpChannel*> aHttpChannel) const {
   1179  // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
   1180  if (!IsSystemXHR() && nsContentUtils::IsForbiddenResponseHeader(aHeader)) {
   1181    NS_WARNING("blocked access to response header");
   1182    return false;
   1183  }
   1184  // if this is not a CORS call all headers are safe
   1185  if (!IsCrossSiteCORSRequest()) {
   1186    return true;
   1187  }
   1188  // Check for dangerous headers
   1189  // Make sure we don't leak header information from denied cross-site
   1190  // requests.
   1191  if (mChannel) {
   1192    nsresult status;
   1193    mChannel->GetStatus(&status);
   1194    if (NS_FAILED(status)) {
   1195      return false;
   1196    }
   1197  }
   1198  const char* kCrossOriginSafeHeaders[] = {
   1199      "cache-control", "content-language", "content-type", "content-length",
   1200      "expires",       "last-modified",    "pragma"};
   1201  for (uint32_t i = 0; i < std::size(kCrossOriginSafeHeaders); ++i) {
   1202    if (aHeader.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
   1203      return true;
   1204    }
   1205  }
   1206  nsAutoCString headerVal;
   1207  // The "Access-Control-Expose-Headers" header contains a comma separated
   1208  // list of method names.
   1209  (void)aHttpChannel->GetResponseHeader("Access-Control-Expose-Headers"_ns,
   1210                                        headerVal);
   1211  bool isSafe = false;
   1212  for (const nsACString& token :
   1213       nsCCharSeparatedTokenizer(headerVal, ',').ToRange()) {
   1214    if (token.IsEmpty()) {
   1215      continue;
   1216    }
   1217    if (!NS_IsValidHTTPToken(token)) {
   1218      return false;
   1219    }
   1220 
   1221    if (token.EqualsLiteral("*") && !mFlagACwithCredentials) {
   1222      isSafe = true;
   1223    } else if (aHeader.Equals(token, nsCaseInsensitiveCStringComparator)) {
   1224      isSafe = true;
   1225    }
   1226  }
   1227 
   1228  return isSafe;
   1229 }
   1230 
   1231 bool XMLHttpRequestMainThread::GetContentType(nsACString& aValue) const {
   1232  MOZ_ASSERT(mChannel);
   1233  nsCOMPtr<nsIBaseChannel> baseChan = do_QueryInterface(mChannel);
   1234  if (baseChan) {
   1235    RefPtr<CMimeType> fullMimeType(baseChan->FullMimeType());
   1236    if (fullMimeType) {
   1237      fullMimeType->Serialize(aValue);
   1238      return true;
   1239    }
   1240  }
   1241  if (NS_SUCCEEDED(mChannel->GetContentType(aValue))) {
   1242    nsCString value;
   1243    if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && !value.IsEmpty()) {
   1244      aValue.AppendLiteral(";charset=");
   1245      aValue.Append(value);
   1246    }
   1247    return true;
   1248  }
   1249  return false;
   1250 }
   1251 void XMLHttpRequestMainThread::GetAllResponseHeaders(
   1252    nsACString& aResponseHeaders, ErrorResult& aRv) {
   1253  NOT_CALLABLE_IN_SYNC_SEND_RV
   1254 
   1255  aResponseHeaders.Truncate();
   1256 
   1257  // If the state is UNSENT or OPENED,
   1258  // return the empty string and terminate these steps.
   1259  if (mState == XMLHttpRequest_Binding::UNSENT ||
   1260      mState == XMLHttpRequest_Binding::OPENED) {
   1261    return;
   1262  }
   1263 
   1264  if (mErrorLoad != ErrorType::eOK) {
   1265    return;
   1266  }
   1267 
   1268  if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) {
   1269    RefPtr<nsHeaderVisitor> visitor =
   1270        new nsHeaderVisitor(*this, WrapNotNull(httpChannel));
   1271    if (NS_SUCCEEDED(httpChannel->VisitResponseHeaders(visitor))) {
   1272      aResponseHeaders = visitor->Headers();
   1273    }
   1274    return;
   1275  }
   1276 
   1277  if (!mChannel) {
   1278    return;
   1279  }
   1280 
   1281  // Even non-http channels supply content type.
   1282  nsAutoCString value;
   1283  if (GetContentType(value)) {
   1284    aResponseHeaders.AppendLiteral("Content-Type: ");
   1285    aResponseHeaders.Append(value);
   1286    aResponseHeaders.AppendLiteral("\r\n");
   1287  }
   1288 
   1289  // Don't provide Content-Length for data URIs
   1290  nsCOMPtr<nsIURI> uri;
   1291  if (NS_FAILED(mChannel->GetURI(getter_AddRefs(uri))) ||
   1292      !uri->SchemeIs("data")) {
   1293    int64_t length;
   1294    if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
   1295      aResponseHeaders.AppendLiteral("Content-Length: ");
   1296      aResponseHeaders.AppendInt(length);
   1297      aResponseHeaders.AppendLiteral("\r\n");
   1298    }
   1299  }
   1300 
   1301  // Should set a Content-Range header for blob scheme.
   1302  // From https://fetch.spec.whatwg.org/#scheme-fetch 3.blob.9.20:
   1303  // "Set response’s header list to «(`Content-Length`, serializedSlicedLength),
   1304  //  (`Content-Type`, type), (`Content-Range`, contentRange)»."
   1305  GetContentRangeHeader(value);
   1306  if (!value.IsVoid()) {
   1307    aResponseHeaders.AppendLiteral("Content-Range: ");
   1308    aResponseHeaders.Append(value);
   1309    aResponseHeaders.AppendLiteral("\r\n");
   1310  }
   1311 }
   1312 
   1313 void XMLHttpRequestMainThread::GetResponseHeader(const nsACString& header,
   1314                                                 nsACString& _retval,
   1315                                                 ErrorResult& aRv) {
   1316  NOT_CALLABLE_IN_SYNC_SEND_RV
   1317 
   1318  _retval.SetIsVoid(true);
   1319 
   1320  nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
   1321 
   1322  if (!httpChannel) {
   1323    // If the state is UNSENT or OPENED,
   1324    // return null and terminate these steps.
   1325    if (mState == XMLHttpRequest_Binding::UNSENT ||
   1326        mState == XMLHttpRequest_Binding::OPENED) {
   1327      return;
   1328    }
   1329 
   1330    // Even non-http channels supply content type and content length.
   1331    // Remember we don't leak header information from denied cross-site
   1332    // requests. However, we handle file: and blob: URLs for blob response
   1333    // types by canceling them with a specific error, so we have to allow
   1334    // them to pass through this check.
   1335    nsresult status;
   1336    if (!mChannel || NS_FAILED(mChannel->GetStatus(&status)) ||
   1337        (NS_FAILED(status) && status != NS_ERROR_FILE_ALREADY_EXISTS)) {
   1338      return;
   1339    }
   1340 
   1341    // Content Type:
   1342    if (header.LowerCaseEqualsASCII("content-type")) {
   1343      if (!GetContentType(_retval)) {
   1344        // Means no content type
   1345        _retval.SetIsVoid(true);
   1346        return;
   1347      }
   1348    }
   1349 
   1350    // Content Length:
   1351    else if (header.LowerCaseEqualsASCII("content-length")) {
   1352      int64_t length;
   1353      if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
   1354        _retval.AppendInt(length);
   1355      }
   1356    }
   1357 
   1358    // Content Range:
   1359    else if (header.LowerCaseEqualsASCII("content-range")) {
   1360      GetContentRangeHeader(_retval);
   1361    }
   1362 
   1363    return;
   1364  }
   1365 
   1366  // Check for dangerous headers
   1367  if (!IsSafeHeader(header, WrapNotNull(httpChannel))) {
   1368    return;
   1369  }
   1370 
   1371  aRv = httpChannel->GetResponseHeader(header, _retval);
   1372  if (aRv.ErrorCodeIs(NS_ERROR_NOT_AVAILABLE)) {
   1373    // Means no header
   1374    _retval.SetIsVoid(true);
   1375    aRv.SuppressException();
   1376  }
   1377 }
   1378 
   1379 already_AddRefed<nsILoadGroup> XMLHttpRequestMainThread::GetLoadGroup() const {
   1380  if (mFlagBackgroundRequest) {
   1381    return nullptr;
   1382  }
   1383 
   1384  if (mLoadGroup) {
   1385    nsCOMPtr<nsILoadGroup> ref = mLoadGroup;
   1386    return ref.forget();
   1387  }
   1388 
   1389  Document* doc = GetDocumentIfCurrent();
   1390  if (doc) {
   1391    return doc->GetDocumentLoadGroup();
   1392  }
   1393 
   1394  return nullptr;
   1395 }
   1396 
   1397 nsresult XMLHttpRequestMainThread::FireReadystatechangeEvent() {
   1398  MOZ_ASSERT(mState != XMLHttpRequest_Binding::UNSENT);
   1399  RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
   1400  event->InitEvent(kLiteralString_readystatechange, false, false);
   1401  // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
   1402  event->SetTrusted(true);
   1403  DispatchOrStoreEvent(this, event);
   1404  return NS_OK;
   1405 }
   1406 
   1407 void XMLHttpRequestMainThread::DispatchProgressEvent(
   1408    DOMEventTargetHelper* aTarget, const ProgressEventType& aType,
   1409    int64_t aLoaded, int64_t aTotal) {
   1410  DEBUG_WORKERREFS;
   1411  NS_ASSERTION(aTarget, "null target");
   1412 
   1413  if (NS_FAILED(CheckCurrentGlobalCorrectness()) ||
   1414      (!AllowUploadProgress() && aTarget == mUpload)) {
   1415    return;
   1416  }
   1417 
   1418  // If blocked by CORS, zero-out the stats on progress events
   1419  // and never fire "progress" or "load" events at all.
   1420  if (IsDeniedCrossSiteCORSRequest()) {
   1421    if (aType == Events::progress || aType == Events::load) {
   1422      return;
   1423    }
   1424    aLoaded = 0;
   1425    aTotal = -1;
   1426  }
   1427 
   1428  ProgressEventInit init;
   1429  init.mBubbles = false;
   1430  init.mCancelable = false;
   1431  init.mLengthComputable = aTotal != -1;  // XHR spec step 6.1
   1432  init.mLoaded = aLoaded;
   1433  init.mTotal = (aTotal == -1) ? 0 : aTotal;
   1434 
   1435  RefPtr<ProgressEvent> event =
   1436      ProgressEvent::Constructor(aTarget, aType, init);
   1437  event->SetTrusted(true);
   1438 
   1439  MOZ_LOG(
   1440      gXMLHttpRequestLog, LogLevel::Debug,
   1441      ("firing %s event (%u,%u,%" PRIu64 ",%" PRIu64 ")", aType.cStr,
   1442       aTarget == mUpload, aTotal != -1, aLoaded, (aTotal == -1) ? 0 : aTotal));
   1443 
   1444  DispatchOrStoreEvent(aTarget, event);
   1445 
   1446  // If we're sending a load, error, timeout or abort event, then
   1447  // also dispatch the subsequent loadend event.
   1448  if (aType == Events::load || aType == Events::error ||
   1449      aType == Events::timeout || aType == Events::abort) {
   1450    DispatchProgressEvent(aTarget, Events::loadend, aLoaded, aTotal);
   1451  }
   1452 }
   1453 
   1454 void XMLHttpRequestMainThread::DispatchOrStoreEvent(
   1455    DOMEventTargetHelper* aTarget, Event* aEvent) {
   1456  DEBUG_WORKERREFS;
   1457  MOZ_ASSERT(aTarget);
   1458  MOZ_ASSERT(aEvent);
   1459 
   1460  if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
   1461    return;
   1462  }
   1463 
   1464  if (mEventDispatchingSuspended) {
   1465    PendingEvent* event = mPendingEvents.AppendElement();
   1466    event->mTarget = aTarget;
   1467    event->mEvent = aEvent;
   1468    return;
   1469  }
   1470 
   1471  aTarget->DispatchEvent(*aEvent);
   1472 }
   1473 
   1474 void XMLHttpRequestMainThread::SuspendEventDispatching() {
   1475  MOZ_ASSERT(!mEventDispatchingSuspended);
   1476  mEventDispatchingSuspended = true;
   1477 }
   1478 
   1479 void XMLHttpRequestMainThread::ResumeEventDispatching() {
   1480  MOZ_ASSERT(mEventDispatchingSuspended);
   1481  mEventDispatchingSuspended = false;
   1482 
   1483  nsTArray<PendingEvent> pendingEvents = std::move(mPendingEvents);
   1484 
   1485  if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
   1486    return;
   1487  }
   1488 
   1489  for (uint32_t i = 0; i < pendingEvents.Length(); ++i) {
   1490    pendingEvents[i].mTarget->DispatchEvent(*pendingEvents[i].mEvent);
   1491  }
   1492 }
   1493 
   1494 already_AddRefed<nsIHttpChannel>
   1495 XMLHttpRequestMainThread::GetCurrentHttpChannel() {
   1496  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
   1497  return httpChannel.forget();
   1498 }
   1499 
   1500 already_AddRefed<nsIJARChannel>
   1501 XMLHttpRequestMainThread::GetCurrentJARChannel() {
   1502  nsCOMPtr<nsIJARChannel> appChannel = do_QueryInterface(mChannel);
   1503  return appChannel.forget();
   1504 }
   1505 
   1506 bool XMLHttpRequestMainThread::IsSystemXHR() const {
   1507  return mIsSystem || mPrincipal->IsSystemPrincipal();
   1508 }
   1509 
   1510 bool XMLHttpRequestMainThread::InUploadPhase() const {
   1511  // We're in the upload phase while our state is OPENED.
   1512  return mState == XMLHttpRequest_Binding::OPENED;
   1513 }
   1514 
   1515 // This case is hit when the async parameter is outright omitted, which
   1516 // should set it to true (and the username and password to null).
   1517 void XMLHttpRequestMainThread::Open(const nsACString& aMethod,
   1518                                    const nsACString& aUrl, ErrorResult& aRv) {
   1519  Open(aMethod, aUrl, true, VoidCString(), VoidCString(), aRv);
   1520 }
   1521 
   1522 // This case is hit when the async parameter is specified, even if the
   1523 // JS value was "undefined" (which due to legacy reasons should be
   1524 // treated as true, which is how it will already be passed in here).
   1525 void XMLHttpRequestMainThread::Open(const nsACString& aMethod,
   1526                                    const nsACString& aUrl, bool aAsync,
   1527                                    const nsACString& aUsername,
   1528                                    const nsACString& aPassword,
   1529                                    ErrorResult& aRv) {
   1530  DEBUG_WORKERREFS1(aMethod << " " << aUrl);
   1531  NOT_CALLABLE_IN_SYNC_SEND_RV
   1532 
   1533  // Gecko-specific
   1534  if (!aAsync && !DontWarnAboutSyncXHR() && GetOwnerWindow() &&
   1535      GetOwnerWindow()->GetExtantDoc()) {
   1536    GetOwnerWindow()->GetExtantDoc()->WarnOnceAbout(
   1537        DeprecatedOperations::eSyncXMLHttpRequestDeprecated);
   1538  }
   1539 
   1540  glean::dom::xmlhttprequest_async_or_sync
   1541      .EnumGet(static_cast<glean::dom::XmlhttprequestAsyncOrSyncLabel>(
   1542          aAsync ? 0 : 1))
   1543      .Add();
   1544 
   1545  // Step 1
   1546  nsCOMPtr<Document> responsibleDocument = GetDocumentIfCurrent();
   1547  if (!responsibleDocument) {
   1548    // This could be because we're no longer current or because we're in some
   1549    // non-window context...
   1550    if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
   1551      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT);
   1552      return;
   1553    }
   1554  }
   1555  if (!mPrincipal) {
   1556    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
   1557    return;
   1558  }
   1559 
   1560  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
   1561    aRv.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
   1562    return;
   1563  }
   1564 
   1565  // Gecko-specific
   1566  if (!aAsync && responsibleDocument && GetOwnerWindow()) {
   1567    // We have no extant document during unload, so the above general
   1568    // syncXHR warning will not display. But we do want to display a
   1569    // recommendation to use sendBeacon instead of syncXHR during unload.
   1570    nsCOMPtr<nsIDocShell> shell = responsibleDocument->GetDocShell();
   1571    if (shell) {
   1572      bool inUnload = false;
   1573      shell->GetIsInUnload(&inUnload);
   1574      if (inUnload) {
   1575        LogMessage("UseSendBeaconDuringUnloadAndPagehideWarning",
   1576                   GetOwnerWindow());
   1577      }
   1578    }
   1579  }
   1580 
   1581  // Steps 2-4
   1582  nsAutoCString method;
   1583  aRv = FetchUtil::GetValidRequestMethod(aMethod, method);
   1584  if (NS_WARN_IF(aRv.Failed())) {
   1585    return;
   1586  }
   1587 
   1588  // Steps 5-6
   1589  nsIURI* baseURI = nullptr;
   1590  if (mBaseURI) {
   1591    baseURI = mBaseURI;
   1592  } else if (responsibleDocument) {
   1593    baseURI = responsibleDocument->GetBaseURI();
   1594  }
   1595 
   1596  // Use the responsible document's encoding for the URL if we have one,
   1597  // except for dedicated workers. Use UTF-8 otherwise.
   1598  NotNull<const Encoding*> originCharset = UTF_8_ENCODING;
   1599  if (responsibleDocument &&
   1600      responsibleDocument->NodePrincipal() == mPrincipal) {
   1601    originCharset = responsibleDocument->GetDocumentCharacterSet();
   1602  }
   1603 
   1604  nsCOMPtr<nsIURI> parsedURL;
   1605  nsresult rv =
   1606      NS_NewURI(getter_AddRefs(parsedURL), aUrl, originCharset, baseURI);
   1607  if (NS_FAILED(rv)) {
   1608    aRv.ThrowSyntaxError("'"_ns + aUrl + "' is not a valid URL."_ns);
   1609    return;
   1610  }
   1611  if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
   1612    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT);
   1613    return;
   1614  }
   1615 
   1616  // Step 7
   1617  // This is already handled by the other Open() method, which passes
   1618  // username and password in as NullStrings.
   1619 
   1620  // Step 8
   1621  nsAutoCString host;
   1622  parsedURL->GetHost(host);
   1623  if (!host.IsEmpty() && (!aUsername.IsVoid() || !aPassword.IsVoid())) {
   1624    auto mutator = NS_MutateURI(parsedURL);
   1625    if (!aUsername.IsVoid()) {
   1626      mutator.SetUsername(aUsername);
   1627    }
   1628    if (!aPassword.IsVoid()) {
   1629      mutator.SetPassword(aPassword);
   1630    }
   1631    (void)mutator.Finalize(parsedURL);
   1632  }
   1633 
   1634  // Step 9
   1635  if (!aAsync && HasOrHasHadOwnerWindow() &&
   1636      (mTimeoutMilliseconds ||
   1637       mResponseType != XMLHttpRequestResponseType::_empty)) {
   1638    if (mTimeoutMilliseconds) {
   1639      LogMessage("TimeoutSyncXHRWarning", GetOwnerWindow());
   1640    }
   1641    if (mResponseType != XMLHttpRequestResponseType::_empty) {
   1642      LogMessage("ResponseTypeSyncXHRWarning", GetOwnerWindow());
   1643    }
   1644    aRv.ThrowInvalidAccessError(
   1645        "synchronous XMLHttpRequests do not support timeout and responseType");
   1646    return;
   1647  }
   1648 
   1649  // Step 10
   1650  TerminateOngoingFetch(NS_OK);
   1651 
   1652  // Step 11
   1653  // timeouts are handled without a flag
   1654  DisconnectDoneNotifier();
   1655  mFlagSend = false;
   1656  mRequestMethod.Assign(method);
   1657  mRequestURL = parsedURL;
   1658  mFlagSynchronous = !aAsync;
   1659  mAuthorRequestHeaders.Clear();
   1660  ResetResponse();
   1661 
   1662  // Gecko-specific
   1663  mFlagHadUploadListenersOnSend = false;
   1664  mFlagAborted = false;
   1665  mFlagTimedOut = false;
   1666  mDecoder = nullptr;
   1667 
   1668  // Per spec we should only create the channel on send(), but we have internal
   1669  // code that relies on the channel being created now, and that code is not
   1670  // always IsSystemXHR(). However, we're not supposed to throw channel-creation
   1671  // errors during open(), so we silently ignore those here.
   1672  CreateChannel();
   1673 
   1674  // Step 12
   1675  if (mState != XMLHttpRequest_Binding::OPENED) {
   1676    mState = XMLHttpRequest_Binding::OPENED;
   1677    FireReadystatechangeEvent();
   1678  }
   1679 }
   1680 
   1681 void XMLHttpRequestMainThread::SetOriginAttributes(
   1682    const OriginAttributesDictionary& aAttrs) {
   1683  MOZ_ASSERT((mState == XMLHttpRequest_Binding::OPENED) && !mFlagSend);
   1684 
   1685  OriginAttributes attrs(aAttrs);
   1686 
   1687  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
   1688  loadInfo->SetOriginAttributes(attrs);
   1689 }
   1690 
   1691 /*
   1692 * "Copy" from a stream.
   1693 */
   1694 nsresult XMLHttpRequestMainThread::StreamReaderFunc(
   1695    nsIInputStream* in, void* closure, const char* fromRawSegment,
   1696    uint32_t toOffset, uint32_t count, uint32_t* writeCount) {
   1697  XMLHttpRequestMainThread* xmlHttpRequest =
   1698      static_cast<XMLHttpRequestMainThread*>(closure);
   1699  if (!xmlHttpRequest || !writeCount) {
   1700    NS_WARNING(
   1701        "XMLHttpRequest cannot read from stream: no closure or writeCount");
   1702    return NS_ERROR_FAILURE;
   1703  }
   1704 
   1705  nsresult rv = NS_OK;
   1706 
   1707  if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Blob) {
   1708    xmlHttpRequest->MaybeCreateBlobStorage();
   1709    rv = xmlHttpRequest->mBlobStorage->Append(fromRawSegment, count);
   1710  } else if (xmlHttpRequest->mResponseType ==
   1711                 XMLHttpRequestResponseType::Arraybuffer &&
   1712             !xmlHttpRequest->mIsMappedArrayBuffer) {
   1713    // get the initial capacity to something reasonable to avoid a bunch of
   1714    // reallocs right at the start
   1715    if (xmlHttpRequest->mArrayBufferBuilder->Capacity() == 0)
   1716      xmlHttpRequest->mArrayBufferBuilder->SetCapacity(
   1717          std::max(count, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE));
   1718 
   1719    if (NS_WARN_IF(!xmlHttpRequest->mArrayBufferBuilder->Append(
   1720            reinterpret_cast<const uint8_t*>(fromRawSegment), count,
   1721            XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH))) {
   1722      return NS_ERROR_OUT_OF_MEMORY;
   1723    }
   1724 
   1725  } else if (xmlHttpRequest->mResponseType ==
   1726                 XMLHttpRequestResponseType::_empty &&
   1727             xmlHttpRequest->mResponseXML) {
   1728    // Copy for our own use
   1729    if (!xmlHttpRequest->mResponseBody.Append(fromRawSegment, count,
   1730                                              fallible)) {
   1731      return NS_ERROR_OUT_OF_MEMORY;
   1732    }
   1733  } else if (xmlHttpRequest->mResponseType ==
   1734                 XMLHttpRequestResponseType::_empty ||
   1735             xmlHttpRequest->mResponseType ==
   1736                 XMLHttpRequestResponseType::Text ||
   1737             xmlHttpRequest->mResponseType ==
   1738                 XMLHttpRequestResponseType::Json) {
   1739    MOZ_ASSERT(!xmlHttpRequest->mResponseXML,
   1740               "We shouldn't be parsing a doc here");
   1741    rv = xmlHttpRequest->AppendToResponseText(
   1742        AsBytes(Span(fromRawSegment, count)));
   1743    if (NS_WARN_IF(NS_FAILED(rv))) {
   1744      return rv;
   1745    }
   1746  }
   1747 
   1748  if (xmlHttpRequest->mFlagParseBody) {
   1749    // Give the same data to the parser.
   1750 
   1751    // We need to wrap the data in a new lightweight stream and pass that
   1752    // to the parser, because calling ReadSegments() recursively on the same
   1753    // stream is not supported.
   1754    nsCOMPtr<nsIInputStream> copyStream;
   1755    rv = NS_NewByteInputStream(getter_AddRefs(copyStream),
   1756                               Span(fromRawSegment, count),
   1757                               NS_ASSIGNMENT_DEPEND);
   1758 
   1759    if (NS_SUCCEEDED(rv) && xmlHttpRequest->mXMLParserStreamListener) {
   1760      NS_ASSERTION(copyStream, "NS_NewByteInputStream lied");
   1761      nsresult parsingResult =
   1762          xmlHttpRequest->mXMLParserStreamListener->OnDataAvailable(
   1763              xmlHttpRequest->mChannel, copyStream, toOffset, count);
   1764 
   1765      // No use to continue parsing if we failed here, but we
   1766      // should still finish reading the stream
   1767      if (NS_FAILED(parsingResult)) {
   1768        xmlHttpRequest->mFlagParseBody = false;
   1769      }
   1770    }
   1771  }
   1772 
   1773  if (NS_SUCCEEDED(rv)) {
   1774    *writeCount = count;
   1775  } else {
   1776    *writeCount = 0;
   1777  }
   1778 
   1779  return rv;
   1780 }
   1781 
   1782 namespace {
   1783 
   1784 void GetBlobURIFromChannel(nsIRequest* aRequest, nsIURI** aURI) {
   1785  MOZ_ASSERT(aRequest);
   1786  MOZ_ASSERT(aURI);
   1787 
   1788  *aURI = nullptr;
   1789 
   1790  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   1791  if (!channel) {
   1792    return;
   1793  }
   1794 
   1795  nsCOMPtr<nsIURI> uri;
   1796  nsresult rv = channel->GetURI(getter_AddRefs(uri));
   1797  if (NS_FAILED(rv)) {
   1798    return;
   1799  }
   1800 
   1801  if (!dom::IsBlobURI(uri)) {
   1802    return;
   1803  }
   1804 
   1805  uri.forget(aURI);
   1806 }
   1807 
   1808 nsresult GetLocalFileFromChannel(nsIRequest* aRequest, nsIFile** aFile) {
   1809  MOZ_ASSERT(aRequest);
   1810  MOZ_ASSERT(aFile);
   1811 
   1812  *aFile = nullptr;
   1813 
   1814  nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aRequest);
   1815  if (!fc) {
   1816    return NS_OK;
   1817  }
   1818 
   1819  nsCOMPtr<nsIFile> file;
   1820  nsresult rv = fc->GetFile(getter_AddRefs(file));
   1821  if (NS_WARN_IF(NS_FAILED(rv))) {
   1822    return rv;
   1823  }
   1824 
   1825  file.forget(aFile);
   1826  return NS_OK;
   1827 }
   1828 
   1829 nsresult DummyStreamReaderFunc(nsIInputStream* aInputStream, void* aClosure,
   1830                               const char* aFromRawSegment, uint32_t aToOffset,
   1831                               uint32_t aCount, uint32_t* aWriteCount) {
   1832  *aWriteCount = aCount;
   1833  return NS_OK;
   1834 }
   1835 
   1836 class FileCreationHandler final : public PromiseNativeHandler {
   1837 public:
   1838  NS_DECL_ISUPPORTS
   1839 
   1840  static void Create(Promise* aPromise, XMLHttpRequestMainThread* aXHR) {
   1841    MOZ_ASSERT(aPromise);
   1842 
   1843    RefPtr<FileCreationHandler> handler = new FileCreationHandler(aXHR);
   1844    aPromise->AppendNativeHandler(handler);
   1845  }
   1846 
   1847  void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
   1848                        ErrorResult& aRv) override {
   1849    if (NS_WARN_IF(!aValue.isObject())) {
   1850      mXHR->LocalFileToBlobCompleted(nullptr);
   1851      return;
   1852    }
   1853 
   1854    RefPtr<Blob> blob;
   1855    if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob, &aValue.toObject(), blob)))) {
   1856      mXHR->LocalFileToBlobCompleted(nullptr);
   1857      return;
   1858    }
   1859 
   1860    mXHR->LocalFileToBlobCompleted(blob->Impl());
   1861  }
   1862 
   1863  void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
   1864                        ErrorResult& aRv) override {
   1865    mXHR->LocalFileToBlobCompleted(nullptr);
   1866  }
   1867 
   1868 private:
   1869  explicit FileCreationHandler(XMLHttpRequestMainThread* aXHR) : mXHR(aXHR) {
   1870    MOZ_ASSERT(aXHR);
   1871  }
   1872 
   1873  ~FileCreationHandler() = default;
   1874 
   1875  RefPtr<XMLHttpRequestMainThread> mXHR;
   1876 };
   1877 
   1878 NS_IMPL_ISUPPORTS0(FileCreationHandler)
   1879 
   1880 }  // namespace
   1881 
   1882 void XMLHttpRequestMainThread::LocalFileToBlobCompleted(BlobImpl* aBlobImpl) {
   1883  MOZ_ASSERT(mState != XMLHttpRequest_Binding::DONE);
   1884 
   1885  mResponseBlobImpl = aBlobImpl;
   1886  mBlobStorage = nullptr;
   1887  NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
   1888 
   1889  ChangeStateToDone(mFlagSyncLooping);
   1890 }
   1891 
   1892 NS_IMETHODIMP
   1893 XMLHttpRequestMainThread::OnDataAvailable(nsIRequest* request,
   1894                                          nsIInputStream* inStr,
   1895                                          uint64_t sourceOffset,
   1896                                          uint32_t count) {
   1897  DEBUG_WORKERREFS;
   1898  NS_ENSURE_ARG_POINTER(inStr);
   1899 
   1900  mProgressSinceLastProgressEvent = true;
   1901  XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
   1902 
   1903  nsresult rv;
   1904 
   1905  if (mResponseType == XMLHttpRequestResponseType::Blob) {
   1906    nsCOMPtr<nsIFile> localFile;
   1907    nsCOMPtr<nsIURI> blobURI;
   1908    GetBlobURIFromChannel(request, getter_AddRefs(blobURI));
   1909    if (blobURI) {
   1910      RefPtr<BlobImpl> blobImpl;
   1911      rv = NS_GetBlobForBlobURI(blobURI, getter_AddRefs(blobImpl));
   1912      if (NS_SUCCEEDED(rv)) {
   1913        mResponseBlobImpl = blobImpl;
   1914      }
   1915    } else {
   1916      rv = GetLocalFileFromChannel(request, getter_AddRefs(localFile));
   1917    }
   1918    if (NS_WARN_IF(NS_FAILED(rv))) {
   1919      return rv;
   1920    }
   1921 
   1922    if (mResponseBlobImpl || localFile) {
   1923      mBlobStorage = nullptr;
   1924      NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
   1925 
   1926      // The nsIStreamListener contract mandates us to read from the stream
   1927      // before returning.
   1928      uint32_t totalRead;
   1929      rv = inStr->ReadSegments(DummyStreamReaderFunc, nullptr, count,
   1930                               &totalRead);
   1931      NS_ENSURE_SUCCESS(rv, rv);
   1932 
   1933      ChangeState(XMLHttpRequest_Binding::LOADING);
   1934 
   1935      // Cancel() must be called with an error. We use
   1936      // NS_ERROR_FILE_ALREADY_EXISTS to know that we've aborted the operation
   1937      // just because we can retrieve the File from the channel directly.
   1938      return request->Cancel(NS_ERROR_FILE_ALREADY_EXISTS);
   1939    }
   1940  }
   1941 
   1942  uint32_t totalRead;
   1943  rv = inStr->ReadSegments(XMLHttpRequestMainThread::StreamReaderFunc,
   1944                           (void*)this, count, &totalRead);
   1945  NS_ENSURE_SUCCESS(rv, rv);
   1946 
   1947  // Fire the first progress event/loading state change
   1948  if (mState == XMLHttpRequest_Binding::HEADERS_RECEIVED) {
   1949    ChangeState(XMLHttpRequest_Binding::LOADING);
   1950    if (!mFlagSynchronous) {
   1951      DispatchProgressEvent(this, Events::progress, mLoadTransferred,
   1952                            mLoadTotal);
   1953    }
   1954    mProgressSinceLastProgressEvent = false;
   1955  }
   1956 
   1957  if (!mFlagSynchronous && !mProgressTimerIsActive) {
   1958    StartProgressEventTimer();
   1959  }
   1960 
   1961  return NS_OK;
   1962 }
   1963 
   1964 NS_IMETHODIMP
   1965 XMLHttpRequestMainThread::OnStartRequest(nsIRequest* request) {
   1966  DEBUG_WORKERREFS;
   1967  AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStartRequest", NETWORK);
   1968 
   1969  nsresult rv = NS_OK;
   1970 
   1971  if (request != mChannel) {
   1972    // Can this still happen?
   1973    return NS_OK;
   1974  }
   1975 
   1976  // Don't do anything if we have been aborted
   1977  if (mState == XMLHttpRequest_Binding::UNSENT) {
   1978    return NS_OK;
   1979  }
   1980 
   1981  // Don't do anything if we're in mid-abort, but let the request
   1982  // know (this can happen due to race conditions in valid XHRs,
   1983  // see bz1070763 for info).
   1984  if (mFlagAborted) {
   1985    return NS_BINDING_ABORTED;
   1986  }
   1987 
   1988  // Don't do anything if we have timed out.
   1989  if (mFlagTimedOut) {
   1990    return NS_OK;
   1991  }
   1992 
   1993  // If we were asked for a bad range on a blob URL, but we're async,
   1994  // we should throw now in order to fire an error progress event.
   1995  if (BadContentRangeRequested()) {
   1996    return NS_ERROR_NET_PARTIAL_TRANSFER;
   1997  }
   1998 
   1999  nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
   2000  NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
   2001 
   2002  nsresult status;
   2003  request->GetStatus(&status);
   2004  if (mErrorLoad == ErrorType::eOK && NS_FAILED(status)) {
   2005    mErrorLoad = ErrorType::eRequest;
   2006    mErrorLoadDetail = status;
   2007  }
   2008 
   2009  // Upload phase is now over. If we were uploading anything,
   2010  // stop the timer and fire any final progress events.
   2011  if (mUpload && !mUploadComplete && mErrorLoad == ErrorType::eOK &&
   2012      !mFlagSynchronous) {
   2013    StopProgressEventTimer();
   2014 
   2015    mUploadTransferred = mUploadTotal;
   2016 
   2017    if (mProgressSinceLastProgressEvent) {
   2018      DispatchProgressEvent(mUpload, Events::progress, mUploadTransferred,
   2019                            mUploadTotal);
   2020      mProgressSinceLastProgressEvent = false;
   2021    }
   2022 
   2023    mUploadComplete = true;
   2024    DispatchProgressEvent(mUpload, Events::load, mUploadTotal, mUploadTotal);
   2025  }
   2026 
   2027  mFlagParseBody = true;
   2028  if (mErrorLoad == ErrorType::eOK) {
   2029    ChangeState(XMLHttpRequest_Binding::HEADERS_RECEIVED);
   2030  }
   2031 
   2032  ResetResponse();
   2033 
   2034  if (!mOverrideMimeType.IsEmpty()) {
   2035    channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
   2036  }
   2037 
   2038  // Fallback to 'application/octet-stream' (leaving data URLs alone)
   2039  if (!IsBlobURI(mRequestURL)) {
   2040    nsAutoCString type;
   2041    channel->GetContentType(type);
   2042    if (type.IsEmpty() || type.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
   2043      channel->SetContentType(nsLiteralCString(APPLICATION_OCTET_STREAM));
   2044    }
   2045  }
   2046 
   2047  DetectCharset();
   2048 
   2049  // Set up arraybuffer
   2050  if (mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
   2051      NS_SUCCEEDED(status)) {
   2052    if (mIsMappedArrayBuffer) {
   2053      nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
   2054      if (jarChannel) {
   2055        nsCOMPtr<nsIURI> uri;
   2056        rv = channel->GetURI(getter_AddRefs(uri));
   2057        if (NS_SUCCEEDED(rv)) {
   2058          nsAutoCString file;
   2059          nsAutoCString scheme;
   2060          uri->GetScheme(scheme);
   2061          if (scheme.LowerCaseEqualsLiteral("jar")) {
   2062            nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri);
   2063            if (jarURI) {
   2064              jarURI->GetJAREntry(file);
   2065            }
   2066          }
   2067          nsCOMPtr<nsIFile> jarFile;
   2068          jarChannel->GetJarFile(getter_AddRefs(jarFile));
   2069          if (!jarFile) {
   2070            mIsMappedArrayBuffer = false;
   2071          } else {
   2072            rv = mArrayBufferBuilder->MapToFileInPackage(file, jarFile);
   2073            // This can happen legitimately if there are compressed files
   2074            // in the jarFile. See bug #1357219. No need to warn on the error.
   2075            if (NS_FAILED(rv)) {
   2076              mIsMappedArrayBuffer = false;
   2077            } else {
   2078              channel->SetContentType("application/mem-mapped"_ns);
   2079            }
   2080          }
   2081        }
   2082      }
   2083    }
   2084    // If memory mapping failed, mIsMappedArrayBuffer would be set to false,
   2085    // and we want it fallback to the malloc way.
   2086    if (!mIsMappedArrayBuffer) {
   2087      int64_t contentLength;
   2088      rv = channel->GetContentLength(&contentLength);
   2089      if (NS_SUCCEEDED(rv) && contentLength > 0 &&
   2090          contentLength < XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE) {
   2091        mArrayBufferBuilder->SetCapacity(static_cast<int32_t>(contentLength));
   2092      }
   2093    }
   2094  }
   2095 
   2096  // Set up responseXML
   2097  // Fetch spec Main Fetch step 21: ignore body for head/connect methods.
   2098  bool parseBody = (mResponseType == XMLHttpRequestResponseType::_empty ||
   2099                    mResponseType == XMLHttpRequestResponseType::Document) &&
   2100                   !(mRequestMethod.EqualsLiteral("HEAD") ||
   2101                     mRequestMethod.EqualsLiteral("CONNECT"));
   2102 
   2103  if (parseBody) {
   2104    // Do not try to parse documents if content-length = 0
   2105    int64_t contentLength;
   2106    if (NS_SUCCEEDED(mChannel->GetContentLength(&contentLength)) &&
   2107        contentLength == 0) {
   2108      parseBody = false;
   2109    }
   2110  }
   2111 
   2112  mIsHtml = false;
   2113  mWarnAboutSyncHtml = false;
   2114  if (parseBody && NS_SUCCEEDED(status)) {
   2115    // We can gain a huge performance win by not even trying to
   2116    // parse non-XML data. This also protects us from the situation
   2117    // where we have an XML document and sink, but HTML (or other)
   2118    // parser, which can produce unreliable results.
   2119    nsAutoCString type;
   2120    channel->GetContentType(type);
   2121 
   2122    if ((mResponseType == XMLHttpRequestResponseType::Document) &&
   2123        type.EqualsLiteral("text/html")) {
   2124      // HTML parsing is only supported for responseType == "document" to
   2125      // avoid running the parser and, worse, populating responseXML for
   2126      // legacy users of XHR who use responseType == "" for retrieving the
   2127      // responseText of text/html resources. This legacy case is so common
   2128      // that it's not useful to emit a warning about it.
   2129      if (mFlagSynchronous) {
   2130        // We don't make cool new features available in the bad synchronous
   2131        // mode. The synchronous mode is for legacy only.
   2132        mWarnAboutSyncHtml = true;
   2133        mFlagParseBody = false;
   2134      } else {
   2135        mIsHtml = true;
   2136      }
   2137    } else if (!type.IsEmpty() && (!(type.EqualsLiteral("text/xml") ||
   2138                                     type.EqualsLiteral("application/xml") ||
   2139                                     StringEndsWith(type, "+xml"_ns)))) {
   2140      // Follow https://xhr.spec.whatwg.org/
   2141      // If final MIME type is not null, text/html, text/xml, application/xml,
   2142      // or does not end in +xml, return null.
   2143      mFlagParseBody = false;
   2144    }
   2145  } else {
   2146    // The request failed, so we shouldn't be parsing anyway
   2147    mFlagParseBody = false;
   2148  }
   2149 
   2150  if (mFlagParseBody) {
   2151    nsCOMPtr<nsIURI> baseURI, docURI;
   2152    rv = mChannel->GetURI(getter_AddRefs(docURI));
   2153    NS_ENSURE_SUCCESS(rv, rv);
   2154    baseURI = docURI;
   2155 
   2156    nsCOMPtr<Document> doc = GetDocumentIfCurrent();
   2157    nsCOMPtr<nsIURI> chromeXHRDocURI, chromeXHRDocBaseURI;
   2158    if (doc) {
   2159      chromeXHRDocURI = doc->GetDocumentURI();
   2160      chromeXHRDocBaseURI = doc->GetBaseURI();
   2161    } else {
   2162      // If we're no longer current, just kill the load, though it really should
   2163      // have been killed already.
   2164      if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
   2165        return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
   2166      }
   2167    }
   2168 
   2169    // Create an empty document from it.
   2170    const auto& emptyStr = u""_ns;
   2171    nsIGlobalObject* global = DOMEventTargetHelper::GetParentObject();
   2172 
   2173    nsCOMPtr<nsIPrincipal> requestingPrincipal;
   2174    rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
   2175        channel, getter_AddRefs(requestingPrincipal));
   2176    NS_ENSURE_SUCCESS(rv, rv);
   2177 
   2178    rv = NS_NewDOMDocument(
   2179        getter_AddRefs(mResponseXML), emptyStr, emptyStr, nullptr, docURI,
   2180        baseURI, requestingPrincipal, LoadedAsData::AsData, global,
   2181        mIsHtml ? DocumentFlavor::HTML : DocumentFlavor::LegacyGuess);
   2182    NS_ENSURE_SUCCESS(rv, rv);
   2183    mResponseXML->SetChromeXHRDocURI(chromeXHRDocURI);
   2184    mResponseXML->SetChromeXHRDocBaseURI(chromeXHRDocBaseURI);
   2185 
   2186    // suppress parsing failure messages to console for statuses which
   2187    // can have empty bodies (see bug 884693).
   2188    IgnoredErrorResult rv2;
   2189    uint32_t responseStatus = GetStatus(rv2);
   2190    if (!rv2.Failed() && (responseStatus == 201 || responseStatus == 202 ||
   2191                          responseStatus == 204 || responseStatus == 205 ||
   2192                          responseStatus == 304)) {
   2193      mResponseXML->SetSuppressParserErrorConsoleMessages(true);
   2194    }
   2195 
   2196    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
   2197    bool isCrossSite = false;
   2198    isCrossSite = loadInfo->GetTainting() != LoadTainting::Basic;
   2199 
   2200    if (isCrossSite) {
   2201      mResponseXML->DisableCookieAccess();
   2202    }
   2203 
   2204    nsCOMPtr<nsIStreamListener> listener;
   2205    nsCOMPtr<nsILoadGroup> loadGroup;
   2206    channel->GetLoadGroup(getter_AddRefs(loadGroup));
   2207 
   2208    // suppress <parsererror> nodes on XML document parse failure, but only
   2209    // for non-privileged code (including Web Extensions). See bug 289714.
   2210    if (!IsSystemXHR()) {
   2211      mResponseXML->SetSuppressParserErrorElement(true);
   2212    }
   2213 
   2214    rv = mResponseXML->StartDocumentLoad(kLoadAsData, channel, loadGroup,
   2215                                         nullptr, getter_AddRefs(listener),
   2216                                         !isCrossSite);
   2217    NS_ENSURE_SUCCESS(rv, rv);
   2218 
   2219    // the spec requires the response document.referrer to be the empty string
   2220    nsCOMPtr<nsIReferrerInfo> referrerInfo =
   2221        new ReferrerInfo(nullptr, mResponseXML->ReferrerPolicy());
   2222    mResponseXML->SetReferrerInfo(referrerInfo);
   2223 
   2224    mXMLParserStreamListener = listener;
   2225    rv = mXMLParserStreamListener->OnStartRequest(request);
   2226    NS_ENSURE_SUCCESS(rv, rv);
   2227  }
   2228 
   2229  // Download phase beginning; start the progress event timer if necessary.
   2230  if (NS_SUCCEEDED(rv) && HasListenersFor(nsGkAtoms::onprogress)) {
   2231    StartProgressEventTimer();
   2232  }
   2233 
   2234  return NS_OK;
   2235 }
   2236 
   2237 NS_IMETHODIMP
   2238 XMLHttpRequestMainThread::OnStopRequest(nsIRequest* request, nsresult status) {
   2239  DEBUG_WORKERREFS;
   2240  AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStopRequest", NETWORK);
   2241 
   2242  if (request != mChannel) {
   2243    // This can happen when we have already faked an OnStopRequest earlier
   2244    // when synchronously canceling a sync XHR.
   2245    return NS_OK;
   2246  }
   2247 
   2248  if (mAlreadyGotStopRequest) {
   2249    // This is needed to filter out the real, second call after faking one in
   2250    // SendInternal.
   2251    return NS_OK;
   2252  }
   2253  mAlreadyGotStopRequest = true;
   2254 
   2255  // Send the decoder the signal that we've hit the end of the stream,
   2256  // but only when decoding text eagerly.
   2257  if (mDecoder && ((mResponseType == XMLHttpRequestResponseType::Text) ||
   2258                   (mResponseType == XMLHttpRequestResponseType::Json) ||
   2259                   (mResponseType == XMLHttpRequestResponseType::_empty &&
   2260                    !mResponseXML))) {
   2261    AppendToResponseText(Span<const uint8_t>(), true);
   2262  }
   2263 
   2264  mWaitingForOnStopRequest = false;
   2265 
   2266  // make sure to notify the listener if we were aborted
   2267  // XXX in fact, why don't we do the cleanup below in this case??
   2268  // UNSENT is for abort calls.  See OnStartRequest above.
   2269  if (mState == XMLHttpRequest_Binding::UNSENT || mFlagTimedOut) {
   2270    if (mXMLParserStreamListener)
   2271      (void)mXMLParserStreamListener->OnStopRequest(request, status);
   2272    return NS_OK;
   2273  }
   2274 
   2275  // Is this good enough here?
   2276  if (mXMLParserStreamListener && mFlagParseBody) {
   2277    mXMLParserStreamListener->OnStopRequest(request, status);
   2278  }
   2279 
   2280  mXMLParserStreamListener = nullptr;
   2281 
   2282  // If window.stop() or other aborts were issued, handle as an abort
   2283  if (status == NS_BINDING_ABORTED) {
   2284    mFlagParseBody = false;
   2285    IgnoredErrorResult rv;
   2286    RequestErrorSteps(Events::abort, NS_ERROR_DOM_ABORT_ERR, rv);
   2287    ChangeState(XMLHttpRequest_Binding::UNSENT, false);
   2288    return NS_OK;
   2289  }
   2290 
   2291  // If we were just reading a blob URL, we're already done
   2292  if (status == NS_ERROR_FILE_ALREADY_EXISTS && mResponseBlobImpl) {
   2293    ChangeStateToDone(mFlagSyncLooping);
   2294    return NS_OK;
   2295  }
   2296 
   2297  bool waitingForBlobCreation = false;
   2298 
   2299  // If we have this error, we have to deal with a file: URL + responseType =
   2300  // blob. We have this error because we canceled the channel. The status will
   2301  // be set to NS_OK.
   2302  if (!mResponseBlobImpl && status == NS_ERROR_FILE_ALREADY_EXISTS &&
   2303      mResponseType == XMLHttpRequestResponseType::Blob) {
   2304    nsCOMPtr<nsIFile> file;
   2305    nsresult rv = GetLocalFileFromChannel(request, getter_AddRefs(file));
   2306    if (NS_WARN_IF(NS_FAILED(rv))) {
   2307      return rv;
   2308    }
   2309 
   2310    if (file) {
   2311      nsAutoCString contentType;
   2312      rv = mChannel->GetContentType(contentType);
   2313      if (NS_WARN_IF(NS_FAILED(rv))) {
   2314        return rv;
   2315      }
   2316 
   2317      ChromeFilePropertyBag bag;
   2318      CopyUTF8toUTF16(contentType, bag.mType);
   2319 
   2320      nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
   2321 
   2322      ErrorResult error;
   2323      RefPtr<Promise> promise =
   2324          FileCreatorHelper::CreateFile(global, file, bag, true, error);
   2325      if (NS_WARN_IF(error.Failed())) {
   2326        return error.StealNSResult();
   2327      }
   2328 
   2329      FileCreationHandler::Create(promise, this);
   2330      waitingForBlobCreation = true;
   2331      status = NS_OK;
   2332 
   2333      NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
   2334      NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
   2335    }
   2336  }
   2337 
   2338  if (NS_SUCCEEDED(status) &&
   2339      mResponseType == XMLHttpRequestResponseType::Blob &&
   2340      !waitingForBlobCreation) {
   2341    // Smaller files may be written in cache map instead of separate files.
   2342    // Also, no-store response cannot be written in persistent cache.
   2343    nsAutoCString contentType;
   2344    if (!mOverrideMimeType.IsEmpty()) {
   2345      contentType.Assign(NS_ConvertUTF16toUTF8(mOverrideMimeType));
   2346    } else {
   2347      mChannel->GetContentType(contentType);
   2348    }
   2349 
   2350    // mBlobStorage can be null if the channel is non-file non-cacheable
   2351    // and if the response length is zero.
   2352    MaybeCreateBlobStorage();
   2353    mBlobStorage->GetBlobImplWhenReady(contentType, this);
   2354    waitingForBlobCreation = true;
   2355 
   2356    NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
   2357    NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
   2358  } else if (NS_SUCCEEDED(status) && !mIsMappedArrayBuffer &&
   2359             mResponseType == XMLHttpRequestResponseType::Arraybuffer) {
   2360    // set the capacity down to the actual length, to realloc back
   2361    // down to the actual size
   2362    if (!mArrayBufferBuilder->SetCapacity(mArrayBufferBuilder->Length())) {
   2363      // this should never happen!
   2364      status = NS_ERROR_UNEXPECTED;
   2365    }
   2366  }
   2367 
   2368  nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
   2369  NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
   2370 
   2371  channel->SetNotificationCallbacks(nullptr);
   2372  mNotificationCallbacks = nullptr;
   2373  mChannelEventSink = nullptr;
   2374  mProgressEventSink = nullptr;
   2375 
   2376  bool wasSync = mFlagSyncLooping;
   2377  mFlagSyncLooping = false;
   2378  mRequestSentTime = 0;
   2379 
   2380  // update our charset and decoder to match mResponseXML,
   2381  // before it is possibly nulled out
   2382  MatchCharsetAndDecoderToResponseDocument();
   2383 
   2384  if (NS_FAILED(status)) {
   2385    // This can happen if the server is unreachable. Other possible
   2386    // reasons are that the user leaves the page or hits the ESC key.
   2387 
   2388    mErrorLoad = ErrorType::eUnreachable;
   2389    mErrorLoadDetail = status;
   2390    mResponseXML = nullptr;
   2391 
   2392    // Handle network errors specifically per spec.
   2393    if (NS_ERROR_GET_MODULE(status) == NS_ERROR_MODULE_NETWORK) {
   2394      MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug,
   2395              ("%p detected networking error 0x%" PRIx32 "\n", this,
   2396               static_cast<uint32_t>(status)));
   2397      IgnoredErrorResult rv;
   2398      mFlagParseBody = false;
   2399      RequestErrorSteps(Events::error, NS_ERROR_DOM_NETWORK_ERR, rv);
   2400      // RequestErrorSteps will not call ChangeStateToDone for sync XHRs, so we
   2401      // do so here to ensure progress events are sent and our state is sane.
   2402      if (mFlagSynchronous) {
   2403        ChangeStateToDone(wasSync);
   2404      }
   2405      return NS_OK;
   2406    }
   2407 
   2408    MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug,
   2409            ("%p detected unreachable error 0x%" PRIx32 "\n", this,
   2410             static_cast<uint32_t>(status)));
   2411  }
   2412 
   2413  // If we're uninitialized at this point, we encountered an error
   2414  // earlier and listeners have already been notified. Also we do
   2415  // not want to do this if we already completed.
   2416  if (mState == XMLHttpRequest_Binding::UNSENT ||
   2417      mState == XMLHttpRequest_Binding::DONE) {
   2418    return NS_OK;
   2419  }
   2420 
   2421  if (!mResponseXML) {
   2422    mFlagParseBody = false;
   2423 
   2424    // We postpone the 'done' until the creation of the Blob is completed.
   2425    if (!waitingForBlobCreation) {
   2426      ChangeStateToDone(wasSync);
   2427    }
   2428 
   2429    return NS_OK;
   2430  }
   2431 
   2432  if (mIsHtml) {
   2433    NS_ASSERTION(!mFlagSyncLooping,
   2434                 "We weren't supposed to support HTML parsing with XHR!");
   2435    mParseEndListener = new nsXHRParseEndListener(this);
   2436    RefPtr<EventTarget> eventTarget = mResponseXML;
   2437    EventListenerManager* manager = eventTarget->GetOrCreateListenerManager();
   2438    manager->AddEventListenerByType(mParseEndListener,
   2439                                    kLiteralString_DOMContentLoaded,
   2440                                    TrustedEventsAtSystemGroupBubble());
   2441    return NS_OK;
   2442  } else {
   2443    mFlagParseBody = false;
   2444  }
   2445 
   2446  // We might have been sent non-XML data. If that was the case,
   2447  // we should null out the document member. The idea in this
   2448  // check here is that if there is no document element it is not
   2449  // an XML document. We might need a fancier check...
   2450  if (!mResponseXML->GetRootElement()) {
   2451    mErrorParsingXML = true;
   2452    mResponseXML = nullptr;
   2453  }
   2454  ChangeStateToDone(wasSync);
   2455  return NS_OK;
   2456 }
   2457 
   2458 void XMLHttpRequestMainThread::OnBodyParseEnd() {
   2459  mFlagParseBody = false;
   2460  mParseEndListener = nullptr;
   2461  ChangeStateToDone(mFlagSyncLooping);
   2462 }
   2463 
   2464 void XMLHttpRequestMainThread::MatchCharsetAndDecoderToResponseDocument() {
   2465  if (mResponseXML &&
   2466      (!mDecoder ||
   2467       mDecoder->Encoding() != mResponseXML->GetDocumentCharacterSet())) {
   2468    TruncateResponseText();
   2469    mResponseBodyDecodedPos = 0;
   2470    mEofDecoded = false;
   2471    mDecoder = mResponseXML->GetDocumentCharacterSet()->NewDecoder();
   2472  }
   2473 }
   2474 
   2475 void XMLHttpRequestMainThread::DisconnectDoneNotifier() {
   2476  if (mDelayedDoneNotifier) {
   2477    // Disconnect may release the last reference to 'this'.
   2478    RefPtr<XMLHttpRequestMainThread> kungfuDeathGrip = this;
   2479    mDelayedDoneNotifier->Disconnect();
   2480    mDelayedDoneNotifier = nullptr;
   2481  }
   2482 }
   2483 
   2484 void XMLHttpRequestMainThread::ChangeStateToDone(bool aWasSync) {
   2485  DEBUG_WORKERREFS;
   2486  DisconnectDoneNotifier();
   2487 
   2488  if (!mForWorker && !aWasSync && mChannel) {
   2489    // If the top level page is loading, try to postpone the handling of the
   2490    // final events.
   2491    nsLoadFlags loadFlags = 0;
   2492    mChannel->GetLoadFlags(&loadFlags);
   2493    if (loadFlags & nsIRequest::LOAD_BACKGROUND) {
   2494      nsPIDOMWindowInner* owner = GetOwnerWindow();
   2495      BrowsingContext* bc = owner ? owner->GetBrowsingContext() : nullptr;
   2496      bc = bc ? bc->Top() : nullptr;
   2497      if (bc && bc->IsLoading()) {
   2498        MOZ_ASSERT(!mDelayedDoneNotifier);
   2499        RefPtr<XMLHttpRequestDoneNotifier> notifier =
   2500            new XMLHttpRequestDoneNotifier(this);
   2501        mDelayedDoneNotifier = notifier;
   2502        bc->AddDeprioritizedLoadRunner(notifier);
   2503        return;
   2504      }
   2505    }
   2506  }
   2507 
   2508  ChangeStateToDoneInternal();
   2509 }
   2510 
   2511 void XMLHttpRequestMainThread::ChangeStateToDoneInternal() {
   2512  DEBUG_WORKERREFS;
   2513  DisconnectDoneNotifier();
   2514  StopProgressEventTimer();
   2515 
   2516  MOZ_ASSERT(!mFlagParseBody,
   2517             "ChangeStateToDone() called before async HTML parsing is done.");
   2518 
   2519  mFlagSend = false;
   2520 
   2521  CancelTimeoutTimer();
   2522 
   2523  // Per spec, fire the last download progress event, if any,
   2524  // before readystatechange=4/done. (Note that 0-sized responses
   2525  // will have not sent a progress event yet, so one must be sent here).
   2526  if (!mFlagSynchronous &&
   2527      (!mLoadTransferred || mProgressSinceLastProgressEvent)) {
   2528    DispatchProgressEvent(this, Events::progress, mLoadTransferred, mLoadTotal);
   2529    mProgressSinceLastProgressEvent = false;
   2530  }
   2531 
   2532  // Notify the document when an XHR request completes successfully.
   2533  // This is used by the password manager as a hint to observe DOM mutations.
   2534  // Call this prior to changing state to DONE to ensure we set up the
   2535  // observer before mutations occur.
   2536  if (mErrorLoad == ErrorType::eOK) {
   2537    Document* doc = GetDocumentIfCurrent();
   2538    if (doc) {
   2539      doc->NotifyFetchOrXHRSuccess();
   2540    }
   2541  }
   2542 
   2543  // Per spec, fire readystatechange=4/done before final error events.
   2544  ChangeState(XMLHttpRequest_Binding::DONE, true);
   2545 
   2546  // Per spec, if we failed in the upload phase, fire a final error
   2547  // and loadend events for the upload after readystatechange=4/done.
   2548  if (!mFlagSynchronous && mUpload && !mUploadComplete) {
   2549    DispatchProgressEvent(mUpload, Events::error, 0, -1);
   2550  }
   2551 
   2552  // Per spec, fire download's load/error and loadend events after
   2553  // readystatechange=4/done (and of course all upload events).
   2554  if (mErrorLoad != ErrorType::eOK) {
   2555    DispatchProgressEvent(this, Events::error, 0, -1);
   2556  } else {
   2557    DispatchProgressEvent(this, Events::load, mLoadTransferred, mLoadTotal);
   2558  }
   2559 
   2560  if (mErrorLoad != ErrorType::eOK) {
   2561    // By nulling out channel here we make it so that Send() can test
   2562    // for that and throw. Also calling the various status
   2563    // methods/members will not throw.
   2564    // This matches what IE does.
   2565    mChannel = nullptr;
   2566  }
   2567 }
   2568 
   2569 nsresult XMLHttpRequestMainThread::CreateChannel() {
   2570  DEBUG_WORKERREFS;
   2571  // When we are called from JS we can find the load group for the page,
   2572  // and add ourselves to it. This way any pending requests
   2573  // will be automatically aborted if the user leaves the page.
   2574  nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
   2575 
   2576  nsSecurityFlags secFlags;
   2577  nsLoadFlags loadFlags = nsIRequest::LOAD_BACKGROUND;
   2578  uint32_t sandboxFlags = 0;
   2579  if (mPrincipal->IsSystemPrincipal()) {
   2580    // When chrome is loading we want to make sure to sandbox any potential
   2581    // result document. We also want to allow cross-origin loads.
   2582    secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
   2583    sandboxFlags = SANDBOXED_ORIGIN;
   2584  } else if (IsSystemXHR()) {
   2585    // For pages that have appropriate permissions, we want to still allow
   2586    // cross-origin loads, but make sure that the any potential result
   2587    // documents get the same principal as the loader.
   2588    secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT |
   2589               nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   2590    loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
   2591  } else {
   2592    // Otherwise use CORS. Again, make sure that potential result documents
   2593    // use the same principal as the loader.
   2594    secFlags = nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT |
   2595               nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   2596  }
   2597 
   2598  if (mIsAnon) {
   2599    secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
   2600  }
   2601 
   2602  // Use the responsibleDocument if we have it, except for dedicated workers
   2603  // where it will be the parent document, which is not the one we want to use.
   2604  nsresult rv;
   2605  nsCOMPtr<Document> responsibleDocument = GetDocumentIfCurrent();
   2606  auto contentPolicyType =
   2607      mFlagSynchronous ? nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_SYNC
   2608                       : nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC;
   2609  if (responsibleDocument &&
   2610      responsibleDocument->NodePrincipal() == mPrincipal) {
   2611    rv = NS_NewChannel(getter_AddRefs(mChannel), mRequestURL,
   2612                       responsibleDocument, secFlags, contentPolicyType,
   2613                       nullptr,  // aPerformanceStorage
   2614                       loadGroup,
   2615                       nullptr,  // aCallbacks
   2616                       loadFlags, nullptr, sandboxFlags);
   2617  } else if (mClientInfo.isSome()) {
   2618    rv = NS_NewChannel(getter_AddRefs(mChannel), mRequestURL, mPrincipal,
   2619                       mClientInfo.ref(), mController, secFlags,
   2620                       contentPolicyType, mCookieJarSettings,
   2621                       mPerformanceStorage,  // aPerformanceStorage
   2622                       loadGroup,
   2623                       nullptr,  // aCallbacks
   2624                       loadFlags, nullptr, sandboxFlags);
   2625  } else {
   2626    // Otherwise use the principal.
   2627    rv = NS_NewChannel(getter_AddRefs(mChannel), mRequestURL, mPrincipal,
   2628                       secFlags, contentPolicyType, mCookieJarSettings,
   2629                       mPerformanceStorage,  // aPerformanceStorage
   2630                       loadGroup,
   2631                       nullptr,  // aCallbacks
   2632                       loadFlags, nullptr, sandboxFlags);
   2633  }
   2634  NS_ENSURE_SUCCESS(rv, rv);
   2635 
   2636  mAlreadyGotStopRequest = false;
   2637 
   2638  if (mCSPEventListener) {
   2639    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
   2640    rv = loadInfo->SetCspEventListener(mCSPEventListener);
   2641    NS_ENSURE_SUCCESS(rv, rv);
   2642  }
   2643 
   2644  if (nsCOMPtr<Document> doc = GetDocumentIfCurrent()) {
   2645    net::ClassificationFlags flags = doc->GetScriptTrackingFlags();
   2646    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
   2647 
   2648    loadInfo->SetTriggeringFirstPartyClassificationFlags(flags.firstPartyFlags);
   2649    loadInfo->SetTriggeringThirdPartyClassificationFlags(flags.thirdPartyFlags);
   2650  }
   2651 
   2652  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
   2653  if (httpChannel) {
   2654    rv = httpChannel->SetRequestMethod(mRequestMethod);
   2655    NS_ENSURE_SUCCESS(rv, rv);
   2656 
   2657    httpChannel->SetSource(profiler_capture_backtrace());
   2658 
   2659    // Set the initiator type
   2660    nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
   2661    if (timedChannel) {
   2662      timedChannel->SetInitiatorType(u"xmlhttprequest"_ns);
   2663    }
   2664  }
   2665 
   2666  return NS_OK;
   2667 }
   2668 
   2669 void XMLHttpRequestMainThread::MaybeLowerChannelPriority() {
   2670  nsCOMPtr<Document> doc = GetDocumentIfCurrent();
   2671  if (!doc) {
   2672    return;
   2673  }
   2674 
   2675  AutoJSAPI jsapi;
   2676  if (!jsapi.Init(GetOwnerGlobal())) {
   2677    return;
   2678  }
   2679 
   2680  JSContext* cx = jsapi.cx();
   2681 
   2682  if (!doc->IsScriptTracking(cx)) {
   2683    return;
   2684  }
   2685 
   2686  if (StaticPrefs::network_http_tailing_enabled()) {
   2687    nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(mChannel);
   2688    if (cos) {
   2689      // Adding TailAllowed to overrule the Unblocked flag, but to preserve
   2690      // the effect of Unblocked when tailing is off.
   2691      cos->AddClassFlags(nsIClassOfService::Throttleable |
   2692                         nsIClassOfService::Tail |
   2693                         nsIClassOfService::TailAllowed);
   2694    }
   2695  }
   2696 
   2697  nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mChannel);
   2698  if (p) {
   2699    p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
   2700  }
   2701 }
   2702 
   2703 nsresult XMLHttpRequestMainThread::InitiateFetch(
   2704    already_AddRefed<nsIInputStream> aUploadStream, int64_t aUploadLength,
   2705    nsACString& aUploadContentType) {
   2706  DEBUG_WORKERREFS;
   2707  nsresult rv;
   2708  nsCOMPtr<nsIInputStream> uploadStream = std::move(aUploadStream);
   2709 
   2710  if (IsSystemXHR() && mFlagSynchronous &&
   2711      StaticPrefs::network_xhr_block_sync_system_requests()) {
   2712    mState = XMLHttpRequest_Binding::DONE;
   2713    return NS_ERROR_DOM_NETWORK_ERR;
   2714  }
   2715 
   2716  if (!uploadStream) {
   2717    RefPtr<PreloaderBase> preload = FindPreload();
   2718    if (preload) {
   2719      // Because of bug 682305, we can't let listener be the XHR object itself
   2720      // because JS wouldn't be able to use it. So create a listener around
   2721      // 'this'. Make sure to hold a strong reference so that we don't leak the
   2722      // wrapper.
   2723      nsCOMPtr<nsIStreamListener> listener =
   2724          new net::nsStreamListenerWrapper(this);
   2725      rv = preload->AsyncConsume(listener);
   2726      if (NS_SUCCEEDED(rv)) {
   2727        mFromPreload = true;
   2728 
   2729        // May be null when the preload has already finished, but the XHR code
   2730        // is safe to live with it.
   2731        mChannel = preload->Channel();
   2732        MOZ_ASSERT(mChannel);
   2733        EnsureChannelContentType();
   2734        return NS_OK;
   2735      }
   2736 
   2737      preload = nullptr;
   2738    }
   2739  }
   2740 
   2741  // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
   2742  // in turn keeps STOP button from becoming active.  If the consumer passed in
   2743  // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
   2744  // necko won't generate any progress notifications.
   2745  if (HasListenersFor(nsGkAtoms::onprogress) ||
   2746      (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress))) {
   2747    nsLoadFlags loadFlags;
   2748    mChannel->GetLoadFlags(&loadFlags);
   2749    loadFlags &= ~nsIRequest::LOAD_BACKGROUND;
   2750    loadFlags |= nsIRequest::LOAD_NORMAL;
   2751    mChannel->SetLoadFlags(loadFlags);
   2752  }
   2753 
   2754  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
   2755  if (httpChannel) {
   2756    // If the user hasn't overridden the Accept header, set it to */* per spec.
   2757    if (!mAuthorRequestHeaders.Has("accept")) {
   2758      mAuthorRequestHeaders.Set("accept", "*/*"_ns);
   2759    }
   2760 
   2761    mAuthorRequestHeaders.ApplyToChannel(httpChannel, false, false);
   2762 
   2763    if (!IsSystemXHR()) {
   2764      nsCOMPtr<nsPIDOMWindowInner> owner = GetOwnerWindow();
   2765      nsCOMPtr<Document> doc = owner ? owner->GetExtantDoc() : nullptr;
   2766      nsCOMPtr<nsIReferrerInfo> referrerInfo =
   2767          ReferrerInfo::CreateForFetch(mPrincipal, doc);
   2768      (void)httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
   2769    }
   2770 
   2771    if (uploadStream) {
   2772      // If necessary, wrap the stream in a buffered stream so as to guarantee
   2773      // support for our upload when calling ExplicitSetUploadStream.
   2774      if (!NS_InputStreamIsBuffered(uploadStream)) {
   2775        nsCOMPtr<nsIInputStream> bufferedStream;
   2776        rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
   2777                                       uploadStream.forget(), 4096);
   2778        NS_ENSURE_SUCCESS(rv, rv);
   2779 
   2780        uploadStream = bufferedStream;
   2781      }
   2782 
   2783      // We want to use a newer version of the upload channel that won't
   2784      // ignore the necessary headers for an empty Content-Type.
   2785      nsCOMPtr<nsIUploadChannel2> uploadChannel(do_QueryInterface(httpChannel));
   2786      NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
   2787      rv = uploadChannel->ExplicitSetUploadStream(
   2788          uploadStream, aUploadContentType, mUploadTotal, mRequestMethod,
   2789          PR_FALSE);
   2790    }
   2791  }
   2792 
   2793  // Should set a Content-Range header for blob scheme, and also slice the
   2794  // blob appropriately, so we process the Range header here for later use.
   2795  if (IsBlobURI(mRequestURL)) {
   2796    nsAutoCString range;
   2797    mAuthorRequestHeaders.Get("range", range);
   2798    if (!range.IsVoid()) {
   2799      rv = NS_SetChannelContentRangeForBlobURI(mChannel, mRequestURL, range);
   2800      if (mFlagSynchronous && NS_FAILED(rv)) {
   2801        // We later fire an error progress event for non-sync
   2802        mState = XMLHttpRequest_Binding::DONE;
   2803        return NS_ERROR_DOM_NETWORK_ERR;
   2804      }
   2805    }
   2806  }
   2807 
   2808  // Due to the chrome-only XHR.channel API, we need a hacky way to set the
   2809  // SEC_COOKIES_INCLUDE *after* the channel has been has been created, since
   2810  // .withCredentials can be called after open() is called.
   2811  // Not doing this for privileged system XHRs since those don't use CORS.
   2812  if (!IsSystemXHR() && !mIsAnon && mFlagACwithCredentials) {
   2813    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
   2814    static_cast<net::LoadInfo*>(loadInfo.get())->SetIncludeCookiesSecFlag();
   2815  }
   2816 
   2817  // We never let XHR be blocked by head CSS/JS loads to avoid potential
   2818  // deadlock where server generation of CSS/JS requires an XHR signal.
   2819  nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(mChannel));
   2820  if (cos) {
   2821    cos->AddClassFlags(nsIClassOfService::Unblocked);
   2822 
   2823    // Mark channel as urgent-start if the XHR is triggered by user input
   2824    // events.
   2825    if (UserActivation::IsHandlingUserInput()) {
   2826      cos->AddClassFlags(nsIClassOfService::UrgentStart);
   2827    }
   2828  }
   2829 
   2830  // Disable Necko-internal response timeouts.
   2831  nsCOMPtr<nsIHttpChannelInternal> internalHttpChannel(
   2832      do_QueryInterface(mChannel));
   2833  if (internalHttpChannel) {
   2834    rv = internalHttpChannel->SetResponseTimeoutEnabled(false);
   2835    MOZ_ASSERT(NS_SUCCEEDED(rv));
   2836  }
   2837 
   2838  if (!mIsAnon) {
   2839    AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
   2840  }
   2841 
   2842  // Bypass the network cache in cases where it makes no sense:
   2843  // POST responses are always unique, and we provide no API that would
   2844  // allow our consumers to specify a "cache key" to access old POST
   2845  // responses, so they are not worth caching.
   2846  if (mRequestMethod.EqualsLiteral("POST")) {
   2847    AddLoadFlags(mChannel, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE |
   2848                               nsIRequest::INHIBIT_CACHING);
   2849  } else {
   2850    // When we are sync loading, we need to bypass the local cache when it would
   2851    // otherwise block us waiting for exclusive access to the cache.  If we
   2852    // don't do this, then we could dead lock in some cases (see bug 309424).
   2853    //
   2854    // Also don't block on the cache entry on async if it is busy - favoring
   2855    // parallelism over cache hit rate for xhr. This does not disable the cache
   2856    // everywhere - only in cases where more than one channel for the same URI
   2857    // is accessed simultanously.
   2858    AddLoadFlags(mChannel, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
   2859  }
   2860 
   2861  EnsureChannelContentType();
   2862 
   2863  // Set up the preflight if needed
   2864  if (!IsSystemXHR()) {
   2865    nsTArray<nsCString> CORSUnsafeHeaders;
   2866    mAuthorRequestHeaders.GetCORSUnsafeHeaders(CORSUnsafeHeaders);
   2867    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
   2868    loadInfo->SetCorsPreflightInfo(CORSUnsafeHeaders,
   2869                                   mFlagHadUploadListenersOnSend);
   2870  }
   2871 
   2872  // Hook us up to listen to redirects and the like. Only do this very late
   2873  // since this creates a cycle between the channel and us. This cycle has
   2874  // to be manually broken if anything below fails.
   2875  mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
   2876  mChannel->SetNotificationCallbacks(this);
   2877 
   2878  if (internalHttpChannel) {
   2879    internalHttpChannel->SetBlockAuthPrompt(ShouldBlockAuthPrompt());
   2880  }
   2881 
   2882  // Because of bug 682305, we can't let listener be the XHR object itself
   2883  // because JS wouldn't be able to use it. So create a listener around 'this'.
   2884  // Make sure to hold a strong reference so that we don't leak the wrapper.
   2885  nsCOMPtr<nsIStreamListener> listener = new net::nsStreamListenerWrapper(this);
   2886 
   2887  // Check if this XHR is created from a tracking script.
   2888  // If yes, lower the channel's priority.
   2889  if (StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
   2890    MaybeLowerChannelPriority();
   2891  }
   2892 
   2893  // Associate any originating stack with the channel.
   2894  NotifyNetworkMonitorAlternateStack(mChannel, std::move(mOriginStack));
   2895 
   2896  // Start reading from the channel
   2897  rv = mChannel->AsyncOpen(listener);
   2898  listener = nullptr;
   2899  if (NS_WARN_IF(NS_FAILED(rv))) {
   2900    // Drop our ref to the channel to avoid cycles. Also drop channel's
   2901    // ref to us to be extra safe.
   2902    mChannel->SetNotificationCallbacks(mNotificationCallbacks);
   2903    mChannel = nullptr;
   2904 
   2905    mErrorLoad = ErrorType::eChannelOpen;
   2906    mErrorLoadDetail = rv;
   2907 
   2908    // Per spec, we throw on sync errors, but not async.
   2909    if (mFlagSynchronous) {
   2910      mState = XMLHttpRequest_Binding::DONE;
   2911      return NS_ERROR_DOM_NETWORK_ERR;
   2912    }
   2913  }
   2914 
   2915  return NS_OK;
   2916 }
   2917 
   2918 already_AddRefed<PreloaderBase> XMLHttpRequestMainThread::FindPreload() {
   2919  Document* doc = GetDocumentIfCurrent();
   2920  if (!doc) {
   2921    return nullptr;
   2922  }
   2923  if (mPrincipal->IsSystemPrincipal() || IsSystemXHR()) {
   2924    return nullptr;
   2925  }
   2926  if (!mRequestMethod.EqualsLiteral("GET")) {
   2927    // Preload can only do GET.
   2928    return nullptr;
   2929  }
   2930  if (!mAuthorRequestHeaders.IsEmpty()) {
   2931    // Preload can't set headers.
   2932    return nullptr;
   2933  }
   2934 
   2935  // mIsAnon overrules mFlagACwithCredentials.
   2936  CORSMode cors = (mIsAnon || !mFlagACwithCredentials)
   2937                      ? CORSMode::CORS_ANONYMOUS
   2938                      : CORSMode::CORS_USE_CREDENTIALS;
   2939  nsCOMPtr<nsIReferrerInfo> referrerInfo =
   2940      ReferrerInfo::CreateForFetch(mPrincipal, doc);
   2941  auto key = PreloadHashKey::CreateAsFetch(mRequestURL, cors);
   2942  RefPtr<PreloaderBase> preload = doc->Preloads().LookupPreload(key);
   2943  if (!preload) {
   2944    return nullptr;
   2945  }
   2946 
   2947  preload->RemoveSelf(doc);
   2948  preload->NotifyUsage(doc, PreloaderBase::LoadBackground::Keep);
   2949 
   2950  return preload.forget();
   2951 }
   2952 
   2953 void XMLHttpRequestMainThread::EnsureChannelContentType() {
   2954  MOZ_ASSERT(mChannel);
   2955 
   2956  // We don't mess with the content type of a blob URL.
   2957  if (IsBlobURI(mRequestURL)) {
   2958    return;
   2959  }
   2960 
   2961  // Since we expect XML data, set the type hint accordingly
   2962  // if the channel doesn't know any content type.
   2963  // This means that we always try to parse local files as XML
   2964  // ignoring return value, as this is not critical. Use text/xml as fallback
   2965  // MIME type.
   2966  nsAutoCString contentType;
   2967  if (NS_FAILED(mChannel->GetContentType(contentType)) ||
   2968      contentType.IsEmpty() ||
   2969      contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
   2970    mChannel->SetContentType("text/xml"_ns);
   2971  }
   2972 }
   2973 
   2974 void XMLHttpRequestMainThread::ResumeTimeout() {
   2975  DEBUG_WORKERREFS;
   2976  MOZ_ASSERT(NS_IsMainThread());
   2977  MOZ_ASSERT(mFlagSynchronous);
   2978 
   2979  if (mResumeTimeoutRunnable) {
   2980    DispatchToMainThread(mResumeTimeoutRunnable.forget());
   2981    mResumeTimeoutRunnable = nullptr;
   2982  }
   2983 }
   2984 
   2985 void XMLHttpRequestMainThread::Send(
   2986    const Nullable<
   2987        DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString>&
   2988        aData,
   2989    ErrorResult& aRv) {
   2990  DEBUG_WORKERREFS1(mRequestURL);
   2991  NOT_CALLABLE_IN_SYNC_SEND_RV
   2992 
   2993  if (!CanSend(aRv)) {
   2994    return;
   2995  }
   2996 
   2997  if (aData.IsNull()) {
   2998    SendInternal(nullptr, false, aRv);
   2999    return;
   3000  }
   3001 
   3002  if (aData.Value().IsDocument()) {
   3003    BodyExtractor<Document> body(&aData.Value().GetAsDocument());
   3004    SendInternal(&body, true, aRv);
   3005    return;
   3006  }
   3007 
   3008  if (aData.Value().IsBlob()) {
   3009    BodyExtractor<const Blob> body(&aData.Value().GetAsBlob());
   3010    SendInternal(&body, false, aRv);
   3011    return;
   3012  }
   3013 
   3014  if (aData.Value().IsArrayBuffer()) {
   3015    BodyExtractor<const ArrayBuffer> body(&aData.Value().GetAsArrayBuffer());
   3016    SendInternal(&body, false, aRv);
   3017    return;
   3018  }
   3019 
   3020  if (aData.Value().IsArrayBufferView()) {
   3021    BodyExtractor<const ArrayBufferView> body(
   3022        &aData.Value().GetAsArrayBufferView());
   3023    SendInternal(&body, false, aRv);
   3024    return;
   3025  }
   3026 
   3027  if (aData.Value().IsFormData()) {
   3028    BodyExtractor<const FormData> body(&aData.Value().GetAsFormData());
   3029    SendInternal(&body, false, aRv);
   3030    return;
   3031  }
   3032 
   3033  if (aData.Value().IsURLSearchParams()) {
   3034    BodyExtractor<const URLSearchParams> body(
   3035        &aData.Value().GetAsURLSearchParams());
   3036    SendInternal(&body, false, aRv);
   3037    return;
   3038  }
   3039 
   3040  if (aData.Value().IsUSVString()) {
   3041    BodyExtractor<const nsAString> body(&aData.Value().GetAsUSVString());
   3042    SendInternal(&body, true, aRv);
   3043    return;
   3044  }
   3045 }
   3046 
   3047 nsresult XMLHttpRequestMainThread::MaybeSilentSendFailure(nsresult aRv) {
   3048  // Per spec, silently fail on async request failures; throw for sync.
   3049  if (mFlagSynchronous) {
   3050    mState = XMLHttpRequest_Binding::DONE;
   3051    return NS_ERROR_DOM_NETWORK_ERR;
   3052  }
   3053 
   3054  // Defer the actual sending of async events just in case listeners
   3055  // are attached after the send() method is called.
   3056  (void)NS_WARN_IF(
   3057      NS_FAILED(DispatchToMainThread(NewRunnableMethod<ErrorProgressEventType>(
   3058          "dom::XMLHttpRequestMainThread::CloseRequestWithError", this,
   3059          &XMLHttpRequestMainThread::CloseRequestWithError, Events::error))));
   3060  return NS_OK;
   3061 }
   3062 
   3063 bool XMLHttpRequestMainThread::CanSend(ErrorResult& aRv) {
   3064  if (!mPrincipal) {
   3065    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
   3066    return false;
   3067  }
   3068 
   3069  // Step 1
   3070  if (mState != XMLHttpRequest_Binding::OPENED) {
   3071    aRv.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
   3072    return false;
   3073  }
   3074 
   3075  // Step 2
   3076  if (mFlagSend) {
   3077    aRv.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
   3078    return false;
   3079  }
   3080 
   3081  if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
   3082    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT);
   3083    return false;
   3084  }
   3085 
   3086  // Backstop against late workers.
   3087  if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads)) {
   3088    aRv.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
   3089    return false;
   3090  }
   3091 
   3092  return true;
   3093 }
   3094 
   3095 void XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase* aBody,
   3096                                            bool aBodyIsDocumentOrString,
   3097                                            ErrorResult& aRv) {
   3098  DEBUG_WORKERREFS;
   3099  MOZ_ASSERT(NS_IsMainThread());
   3100 
   3101  // We expect that CanSend has been called before we get here!
   3102  // We cannot move the remaining two checks below there because
   3103  // MaybeSilentSendFailure can cause unexpected side effects if called
   3104  // too early.
   3105 
   3106  // If open() failed to create the channel, then throw a network error
   3107  // as per spec. We really should create the channel here in send(), but
   3108  // we have internal code relying on the channel being created in open().
   3109  if (!mChannel) {
   3110    mErrorLoad = ErrorType::eChannelOpen;
   3111    mErrorLoadDetail = NS_ERROR_DOM_NETWORK_ERR;
   3112    mFlagSend = true;  // so CloseRequestWithError sets us to DONE.
   3113    aRv = MaybeSilentSendFailure(mErrorLoadDetail);
   3114    return;
   3115  }
   3116 
   3117  // non-GET requests aren't allowed for blob.
   3118  if (IsBlobURI(mRequestURL) && !mRequestMethod.EqualsLiteral("GET")) {
   3119    mErrorLoad = ErrorType::eChannelOpen;
   3120    mErrorLoadDetail = NS_ERROR_DOM_NETWORK_ERR;
   3121    mFlagSend = true;  // so CloseRequestWithError sets us to DONE.
   3122    aRv = MaybeSilentSendFailure(mErrorLoadDetail);
   3123    return;
   3124  }
   3125 
   3126  // XXX We should probably send a warning to the JS console
   3127  //     if there are no event listeners set and we are doing
   3128  //     an asynchronous call.
   3129 
   3130  mUploadTransferred = 0;
   3131  mUploadTotal = 0;
   3132  // By default we don't have any upload, so mark upload complete.
   3133  mUploadComplete = true;
   3134  mErrorLoad = ErrorType::eOK;
   3135  mErrorLoadDetail = NS_OK;
   3136  mLoadTotal = -1;
   3137  nsCOMPtr<nsIInputStream> uploadStream;
   3138  nsAutoCString uploadContentType;
   3139  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
   3140  if (aBody && httpChannel && !mRequestMethod.EqualsLiteral("GET") &&
   3141      !mRequestMethod.EqualsLiteral("HEAD")) {
   3142    nsAutoCString charset;
   3143    nsAutoCString defaultContentType;
   3144    uint64_t size_u64;
   3145    aRv = aBody->GetAsStream(getter_AddRefs(uploadStream), &size_u64,
   3146                             defaultContentType, charset);
   3147    if (aRv.Failed()) {
   3148      return;
   3149    }
   3150 
   3151    // make sure it fits within js MAX_SAFE_INTEGER
   3152    mUploadTotal =
   3153        net::InScriptableRange(size_u64) ? static_cast<int64_t>(size_u64) : -1;
   3154 
   3155    if (uploadStream) {
   3156      // If author set no Content-Type, use the default from GetAsStream().
   3157      mAuthorRequestHeaders.Get("content-type", uploadContentType);
   3158      if (uploadContentType.IsVoid()) {
   3159        uploadContentType = defaultContentType;
   3160      } else if (aBodyIsDocumentOrString) {
   3161        RefPtr<CMimeType> contentTypeRecord =
   3162            CMimeType::Parse(uploadContentType);
   3163        nsAutoCString charset;
   3164        if (contentTypeRecord &&
   3165            contentTypeRecord->GetParameterValue(kLiteralString_charset,
   3166                                                 charset) &&
   3167            !charset.EqualsIgnoreCase("utf-8")) {
   3168          contentTypeRecord->SetParameterValue(kLiteralString_charset,
   3169                                               kLiteralString_UTF_8);
   3170          contentTypeRecord->Serialize(uploadContentType);
   3171        }
   3172      } else if (!charset.IsEmpty()) {
   3173        // We don't want to set a charset for streams.
   3174        // Replace all case-insensitive matches of the charset in the
   3175        // content-type with the correct case.
   3176        RequestHeaders::CharsetIterator iter(uploadContentType);
   3177        while (iter.Next()) {
   3178          if (!iter.Equals(charset, nsCaseInsensitiveCStringComparator)) {
   3179            iter.Replace(charset);
   3180          }
   3181        }
   3182      }
   3183 
   3184      mUploadComplete = false;
   3185    }
   3186  }
   3187 
   3188  ResetResponse();
   3189 
   3190  // Check if we should enable cross-origin upload listeners.
   3191  if (mUpload && mUpload->HasListeners()) {
   3192    mFlagHadUploadListenersOnSend = true;
   3193  }
   3194 
   3195  mIsMappedArrayBuffer = false;
   3196  if (mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
   3197      StaticPrefs::dom_mapped_arraybuffer_enabled()) {
   3198    nsCOMPtr<nsIURI> uri;
   3199    nsAutoCString scheme;
   3200 
   3201    aRv = mChannel->GetURI(getter_AddRefs(uri));
   3202    if (!aRv.Failed()) {
   3203      uri->GetScheme(scheme);
   3204      if (scheme.LowerCaseEqualsLiteral("jar")) {
   3205        mIsMappedArrayBuffer = true;
   3206      }
   3207    }
   3208  }
   3209 
   3210  aRv = InitiateFetch(uploadStream.forget(), mUploadTotal, uploadContentType);
   3211  if (aRv.Failed()) {
   3212    return;
   3213  }
   3214 
   3215  // Start our timeout
   3216  mRequestSentTime = PR_Now();
   3217  StartTimeoutTimer();
   3218 
   3219  mWaitingForOnStopRequest = true;
   3220 
   3221  // Step 8
   3222  mFlagSend = true;
   3223 
   3224  // If we're synchronous, spin an event loop here and wait
   3225  RefPtr<Document> suspendedDoc;
   3226  if (mFlagSynchronous) {
   3227    auto scopeExit = MakeScopeExit([&] {
   3228      CancelSyncTimeoutTimer();
   3229      ResumeTimeout();
   3230      ResumeEventDispatching();
   3231    });
   3232    Maybe<AutoSuppressEventHandling> autoSuppress;
   3233 
   3234    mFlagSyncLooping = true;
   3235 
   3236    if (GetOwnerWindow()) {
   3237      if (nsCOMPtr<nsPIDOMWindowOuter> topWindow =
   3238              GetOwnerWindow()->GetOuterWindow()->GetInProcessTop()) {
   3239        if (nsCOMPtr<nsPIDOMWindowInner> topInner =
   3240                topWindow->GetCurrentInnerWindow()) {
   3241          suspendedDoc = topWindow->GetExtantDoc();
   3242          autoSuppress.emplace(topWindow->GetBrowsingContext());
   3243          topInner->Suspend();
   3244          mResumeTimeoutRunnable = new nsResumeTimeoutsEvent(topInner);
   3245        }
   3246      }
   3247    }
   3248 
   3249    SuspendEventDispatching();
   3250    StopProgressEventTimer();
   3251 
   3252    SyncTimeoutType syncTimeoutType = MaybeStartSyncTimeoutTimer();
   3253    if (syncTimeoutType == eErrorOrExpired) {
   3254      Abort();
   3255      aRv.Throw(NS_ERROR_DOM_NETWORK_ERR);
   3256      return;
   3257    }
   3258 
   3259    nsresult channelStatus = NS_OK;
   3260    nsAutoSyncOperation sync(suspendedDoc,
   3261                             SyncOperationBehavior::eSuspendInput);
   3262    if (!SpinEventLoopUntil("XMLHttpRequestMainThread::SendInternal"_ns, [&]() {
   3263          if (mFlagSyncLooping && mChannel) {
   3264            // The purpose of this check is to enable XHR channel cancelation
   3265            // upon navigating away from the page that is doing sync XHR
   3266            // to genuinely make the sync XHR go away within the same task.
   3267            mChannel->GetStatus(&channelStatus);
   3268            // Can't change mFlagSyncLooping to false, because other
   3269            // end-of-request code expects to be able to see it as true
   3270            // still even if we exit the loop early due to the channel
   3271            // getting canceled.
   3272          }
   3273          return !mFlagSyncLooping || NS_FAILED(channelStatus);
   3274        })) {
   3275      aRv.Throw(NS_ERROR_UNEXPECTED);
   3276      return;
   3277    }
   3278    if (NS_FAILED(channelStatus)) {
   3279      MOZ_ASSERT(mFlagSyncLooping);
   3280      // As mentioned above, when navigating away, we want channel cancelation
   3281      // to make the sync XHR go away within the same task. This also requires
   3282      // us to set the correct error result and dispatch events. So call
   3283      // OnStopRequest explicitly instead of the channel calling it after
   3284      // SendInternal has already completed.
   3285      // Consecutive OnStopRequests are blocked due to mAlreadyGotStopRequest
   3286      OnStopRequest(mChannel, channelStatus);
   3287    }
   3288 
   3289    // Time expired... We should throw.
   3290    if (syncTimeoutType == eTimerStarted && !mSyncTimeoutTimer) {
   3291      aRv.Throw(NS_ERROR_DOM_NETWORK_ERR);
   3292      return;
   3293    }
   3294  } else {
   3295    // Now that we've successfully opened the channel, we can change state. Note
   3296    // that this needs to come after the AsyncOpen() and rv check, because this
   3297    // can run script that would try to restart this request, and that could end
   3298    // up doing our AsyncOpen on a null channel if the reentered AsyncOpen
   3299    // fails.
   3300    StopProgressEventTimer();
   3301 
   3302    // Upload phase beginning; start the progress event timer if necessary.
   3303    if (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress)) {
   3304      StartProgressEventTimer();
   3305    }
   3306    // Dispatch loadstart events
   3307    DispatchProgressEvent(this, Events::loadstart, 0, -1);
   3308    if (mUpload && !mUploadComplete) {
   3309      DispatchProgressEvent(mUpload, Events::loadstart, 0, mUploadTotal);
   3310    }
   3311  }
   3312 
   3313  if (!mChannel) {
   3314    aRv = MaybeSilentSendFailure(NS_ERROR_DOM_NETWORK_ERR);
   3315  }
   3316 }
   3317 
   3318 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
   3319 void XMLHttpRequestMainThread::SetRequestHeader(const nsACString& aName,
   3320                                                const nsACString& aValue,
   3321                                                ErrorResult& aRv) {
   3322  NOT_CALLABLE_IN_SYNC_SEND_RV
   3323 
   3324  // Step 1
   3325  if (mState != XMLHttpRequest_Binding::OPENED) {
   3326    aRv.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
   3327    return;
   3328  }
   3329 
   3330  // Step 2
   3331  if (mFlagSend) {
   3332    aRv.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
   3333    return;
   3334  }
   3335 
   3336  // Step 3
   3337  nsAutoCString value;
   3338  NS_TrimHTTPWhitespace(aValue, value);
   3339 
   3340  // Step 4
   3341  if (!NS_IsValidHTTPToken(aName) || !NS_IsReasonableHTTPHeaderValue(value)) {
   3342    aRv.Throw(NS_ERROR_DOM_INVALID_HEADER_NAME);
   3343    return;
   3344  }
   3345 
   3346  // Step 5
   3347  bool isPrivilegedCaller = IsSystemXHR();
   3348  bool isForbiddenHeader =
   3349      nsContentUtils::IsForbiddenRequestHeader(aName, aValue);
   3350  if (!isPrivilegedCaller && isForbiddenHeader) {
   3351    AutoTArray<nsString, 1> params;
   3352    CopyUTF8toUTF16(aName, *params.AppendElement());
   3353    LogMessage("ForbiddenHeaderWarning", GetOwnerWindow(), params);
   3354    return;
   3355  }
   3356 
   3357  // Step 6.1
   3358  // Skipping for now, as normalizing the case of header names may not be
   3359  // web-compatible. See bug 1285036.
   3360 
   3361  // Step 6.2-6.3
   3362  // Gecko-specific: invalid headers can be set by privileged
   3363  //                 callers, but will not merge.
   3364  if (isPrivilegedCaller && isForbiddenHeader) {
   3365    mAuthorRequestHeaders.Set(aName, value);
   3366  } else {
   3367    mAuthorRequestHeaders.MergeOrSet(aName, value);
   3368  }
   3369 }
   3370 
   3371 void XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout, ErrorResult& aRv) {
   3372  NOT_CALLABLE_IN_SYNC_SEND_RV
   3373 
   3374  if (mFlagSynchronous && mState != XMLHttpRequest_Binding::UNSENT &&
   3375      HasOrHasHadOwnerWindow()) {
   3376    /* Timeout is not supported for synchronous requests with an owning window,
   3377       per XHR2 spec. */
   3378    LogMessage("TimeoutSyncXHRWarning", GetOwnerWindow());
   3379    aRv.ThrowInvalidAccessError(
   3380        "synchronous XMLHttpRequests do not support timeout and responseType");
   3381    return;
   3382  }
   3383 
   3384  mTimeoutMilliseconds = aTimeout;
   3385  if (mRequestSentTime) {
   3386    StartTimeoutTimer();
   3387  }
   3388 }
   3389 
   3390 nsIEventTarget* XMLHttpRequestMainThread::GetTimerEventTarget() {
   3391  if (nsIGlobalObject* global = GetOwnerGlobal()) {
   3392    return global->SerialEventTarget();
   3393  }
   3394  return nullptr;
   3395 }
   3396 
   3397 nsresult XMLHttpRequestMainThread::DispatchToMainThread(
   3398    already_AddRefed<nsIRunnable> aRunnable) {
   3399  DEBUG_WORKERREFS;
   3400  if (nsIGlobalObject* global = GetOwnerGlobal()) {
   3401    return global->Dispatch(std::move(aRunnable));
   3402  }
   3403  return NS_DispatchToMainThread(std::move(aRunnable));
   3404 }
   3405 
   3406 void XMLHttpRequestMainThread::StartTimeoutTimer() {
   3407  DEBUG_WORKERREFS;
   3408  MOZ_ASSERT(
   3409      mRequestSentTime,
   3410      "StartTimeoutTimer mustn't be called before the request was sent!");
   3411  if (mState == XMLHttpRequest_Binding::DONE) {
   3412    // do nothing!
   3413    return;
   3414  }
   3415 
   3416  CancelTimeoutTimer();
   3417 
   3418  if (!mTimeoutMilliseconds) {
   3419    return;
   3420  }
   3421 
   3422  if (!mTimeoutTimer) {
   3423    mTimeoutTimer = NS_NewTimer(GetTimerEventTarget());
   3424  }
   3425  uint32_t elapsed =
   3426      (uint32_t)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC);
   3427  mTimeoutTimer->InitWithCallback(
   3428      this, mTimeoutMilliseconds > elapsed ? mTimeoutMilliseconds - elapsed : 0,
   3429      nsITimer::TYPE_ONE_SHOT);
   3430 }
   3431 
   3432 uint16_t XMLHttpRequestMainThread::ReadyState() const { return mState; }
   3433 
   3434 void XMLHttpRequestMainThread::OverrideMimeType(const nsAString& aMimeType,
   3435                                                ErrorResult& aRv) {
   3436  NOT_CALLABLE_IN_SYNC_SEND_RV
   3437 
   3438  if (mState == XMLHttpRequest_Binding::LOADING ||
   3439      mState == XMLHttpRequest_Binding::DONE) {
   3440    aRv.ThrowInvalidStateError(
   3441        "Cannot call 'overrideMimeType()' on XMLHttpRequest after 'send()' "
   3442        "(when its state is LOADING or DONE).");
   3443    return;
   3444  }
   3445 
   3446  RefPtr<MimeType> parsed = MimeType::Parse(aMimeType);
   3447  if (parsed) {
   3448    parsed->Serialize(mOverrideMimeType);
   3449  } else {
   3450    mOverrideMimeType.AssignLiteral(APPLICATION_OCTET_STREAM);
   3451  }
   3452 }
   3453 
   3454 bool XMLHttpRequestMainThread::MozBackgroundRequest() const {
   3455  return mFlagBackgroundRequest;
   3456 }
   3457 
   3458 void XMLHttpRequestMainThread::SetMozBackgroundRequestExternal(
   3459    bool aMozBackgroundRequest, ErrorResult& aRv) {
   3460  if (!IsSystemXHR()) {
   3461    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   3462    return;
   3463  }
   3464 
   3465  if (mState != XMLHttpRequest_Binding::UNSENT) {
   3466    // Can't change this while we're in the middle of something.
   3467    aRv.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
   3468    return;
   3469  }
   3470 
   3471  mFlagBackgroundRequest = aMozBackgroundRequest;
   3472 }
   3473 
   3474 void XMLHttpRequestMainThread::SetMozBackgroundRequest(
   3475    bool aMozBackgroundRequest, ErrorResult& aRv) {
   3476  // No errors for this webIDL method on main-thread.
   3477  SetMozBackgroundRequestExternal(aMozBackgroundRequest, IgnoreErrors());
   3478 }
   3479 
   3480 void XMLHttpRequestMainThread::SetOriginStack(
   3481    UniquePtr<SerializedStackHolder> aOriginStack) {
   3482  mOriginStack = std::move(aOriginStack);
   3483 }
   3484 
   3485 void XMLHttpRequestMainThread::SetSource(
   3486    UniquePtr<ProfileChunkedBuffer> aSource) {
   3487  if (!mChannel) {
   3488    return;
   3489  }
   3490  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
   3491 
   3492  if (httpChannel) {
   3493    httpChannel->SetSource(std::move(aSource));
   3494  }
   3495 }
   3496 
   3497 bool XMLHttpRequestMainThread::WithCredentials() const {
   3498  return mFlagACwithCredentials;
   3499 }
   3500 
   3501 void XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials,
   3502                                                  ErrorResult& aRv) {
   3503  NOT_CALLABLE_IN_SYNC_SEND_RV
   3504 
   3505  // Return error if we're already processing a request.  Note that we can't use
   3506  // ReadyState() here, because it can't differentiate between "opened" and
   3507  // "sent", so we use mState directly.
   3508 
   3509  if ((mState != XMLHttpRequest_Binding::UNSENT &&
   3510       mState != XMLHttpRequest_Binding::OPENED) ||
   3511      mFlagSend || mIsAnon) {
   3512    aRv.ThrowInvalidStateError("XMLHttpRequest must not be sending.");
   3513    return;
   3514  }
   3515 
   3516  mFlagACwithCredentials = aWithCredentials;
   3517 }
   3518 
   3519 nsresult XMLHttpRequestMainThread::ChangeState(uint16_t aState,
   3520                                               bool aBroadcast) {
   3521  mState = aState;
   3522  nsresult rv = NS_OK;
   3523 
   3524  if (aState != XMLHttpRequest_Binding::HEADERS_RECEIVED &&
   3525      aState != XMLHttpRequest_Binding::LOADING) {
   3526    StopProgressEventTimer();
   3527  }
   3528 
   3529  if (aBroadcast &&
   3530      (!mFlagSynchronous || aState == XMLHttpRequest_Binding::OPENED ||
   3531       aState == XMLHttpRequest_Binding::DONE)) {
   3532    rv = FireReadystatechangeEvent();
   3533  }
   3534 
   3535  return rv;
   3536 }
   3537 
   3538 /////////////////////////////////////////////////////
   3539 // nsIChannelEventSink methods:
   3540 //
   3541 NS_IMETHODIMP
   3542 XMLHttpRequestMainThread::AsyncOnChannelRedirect(
   3543    nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
   3544    nsIAsyncVerifyRedirectCallback* callback) {
   3545  DEBUG_WORKERREFS;
   3546  MOZ_ASSERT(aNewChannel, "Redirect without a channel?");
   3547 
   3548  // Prepare to receive callback
   3549  mRedirectCallback = callback;
   3550  mNewRedirectChannel = aNewChannel;
   3551 
   3552  if (mChannelEventSink) {
   3553    nsCOMPtr<nsIAsyncVerifyRedirectCallback> fwd = EnsureXPCOMifier();
   3554 
   3555    nsresult rv = mChannelEventSink->AsyncOnChannelRedirect(
   3556        aOldChannel, aNewChannel, aFlags, fwd);
   3557    if (NS_FAILED(rv)) {
   3558      mRedirectCallback = nullptr;
   3559      mNewRedirectChannel = nullptr;
   3560    }
   3561    return rv;
   3562  }
   3563 
   3564  // we need to strip Authentication headers for cross-origin requests
   3565  // Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch
   3566  bool stripAuth =
   3567      NS_ShouldRemoveAuthHeaderOnRedirect(aOldChannel, aNewChannel, aFlags);
   3568 
   3569  OnRedirectVerifyCallback(NS_OK, stripAuth);
   3570 
   3571  return NS_OK;
   3572 }
   3573 
   3574 nsresult XMLHttpRequestMainThread::OnRedirectVerifyCallback(nsresult result,
   3575                                                            bool aStripAuth) {
   3576  DEBUG_WORKERREFS;
   3577  NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
   3578  NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
   3579 
   3580  if (NS_SUCCEEDED(result)) {
   3581    bool rewriteToGET = false;
   3582    nsCOMPtr<nsIHttpChannel> oldHttpChannel = GetCurrentHttpChannel();
   3583    // Fetch 4.4.11
   3584    (void)oldHttpChannel->ShouldStripRequestBodyHeader(mRequestMethod,
   3585                                                       &rewriteToGET);
   3586 
   3587    mChannel = mNewRedirectChannel;
   3588 
   3589    nsCOMPtr<nsIHttpChannel> newHttpChannel(do_QueryInterface(mChannel));
   3590    if (newHttpChannel) {
   3591      // Ensure all original headers are duplicated for the new channel (bug
   3592      // #553888)
   3593      mAuthorRequestHeaders.ApplyToChannel(newHttpChannel, rewriteToGET,
   3594                                           aStripAuth);
   3595    }
   3596  } else {
   3597    mErrorLoad = ErrorType::eRedirect;
   3598    mErrorLoadDetail = result;
   3599  }
   3600 
   3601  mNewRedirectChannel = nullptr;
   3602 
   3603  mRedirectCallback->OnRedirectVerifyCallback(result);
   3604  mRedirectCallback = nullptr;
   3605 
   3606  // It's important that we return success here. If we return the result code
   3607  // that we were passed, JavaScript callers who cancel the redirect will wind
   3608  // up throwing an exception in the process.
   3609  return NS_OK;
   3610 }
   3611 
   3612 /////////////////////////////////////////////////////
   3613 // nsIProgressEventSink methods:
   3614 //
   3615 
   3616 NS_IMETHODIMP
   3617 XMLHttpRequestMainThread::OnProgress(nsIRequest* aRequest, int64_t aProgress,
   3618                                     int64_t aProgressMax) {
   3619  DEBUG_WORKERREFS;
   3620  // When uploading, OnProgress reports also headers in aProgress and
   3621  // aProgressMax. So, try to remove the headers, if possible.
   3622  bool lengthComputable = (aProgressMax != -1);
   3623  if (InUploadPhase()) {
   3624    int64_t loaded = aProgress;
   3625    if (lengthComputable) {
   3626      int64_t headerSize = aProgressMax - mUploadTotal;
   3627      loaded -= headerSize;
   3628    }
   3629    mUploadTransferred = loaded;
   3630    mProgressSinceLastProgressEvent = true;
   3631 
   3632    if (!mFlagSynchronous && !mProgressTimerIsActive) {
   3633      StartProgressEventTimer();
   3634    }
   3635  } else {
   3636    mLoadTotal = aProgressMax;
   3637    mLoadTransferred = aProgress;
   3638    // OnDataAvailable() handles mProgressSinceLastProgressEvent
   3639    // for the download phase.
   3640  }
   3641 
   3642  if (mProgressEventSink) {
   3643    mProgressEventSink->OnProgress(aRequest, aProgress, aProgressMax);
   3644  }
   3645 
   3646  return NS_OK;
   3647 }
   3648 
   3649 NS_IMETHODIMP
   3650 XMLHttpRequestMainThread::OnStatus(nsIRequest* aRequest, nsresult aStatus,
   3651                                   const char16_t* aStatusArg) {
   3652  DEBUG_WORKERREFS;
   3653  if (mProgressEventSink) {
   3654    mProgressEventSink->OnStatus(aRequest, aStatus, aStatusArg);
   3655  }
   3656 
   3657  return NS_OK;
   3658 }
   3659 
   3660 bool XMLHttpRequestMainThread::AllowUploadProgress() {
   3661  return !IsCrossSiteCORSRequest() || mFlagHadUploadListenersOnSend;
   3662 }
   3663 
   3664 /////////////////////////////////////////////////////
   3665 // nsIInterfaceRequestor methods:
   3666 //
   3667 NS_IMETHODIMP
   3668 XMLHttpRequestMainThread::GetInterface(const nsIID& aIID, void** aResult) {
   3669  nsresult rv;
   3670 
   3671  // Make sure to return ourselves for the channel event sink interface and
   3672  // progress event sink interface, no matter what.  We can forward these to
   3673  // mNotificationCallbacks if it wants to get notifications for them.  But we
   3674  // need to see these notifications for proper functioning.
   3675  if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
   3676    mChannelEventSink = do_GetInterface(mNotificationCallbacks);
   3677    *aResult = static_cast<nsIChannelEventSink*>(EnsureXPCOMifier().take());
   3678    return NS_OK;
   3679  } else if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
   3680    mProgressEventSink = do_GetInterface(mNotificationCallbacks);
   3681    *aResult = static_cast<nsIProgressEventSink*>(EnsureXPCOMifier().take());
   3682    return NS_OK;
   3683  }
   3684 
   3685  // Now give mNotificationCallbacks (if non-null) a chance to return the
   3686  // desired interface.
   3687  if (mNotificationCallbacks) {
   3688    rv = mNotificationCallbacks->GetInterface(aIID, aResult);
   3689    if (NS_SUCCEEDED(rv)) {
   3690      NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
   3691      return rv;
   3692    }
   3693  }
   3694 
   3695  if (!mFlagBackgroundRequest && (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
   3696                                  aIID.Equals(NS_GET_IID(nsIAuthPrompt2)))) {
   3697    nsCOMPtr<nsIPromptFactory> wwatch =
   3698        do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
   3699    NS_ENSURE_SUCCESS(rv, rv);
   3700 
   3701    // Get the an auth prompter for our window so that the parenting
   3702    // of the dialogs works as it should when using tabs.
   3703    nsCOMPtr<nsPIDOMWindowOuter> window;
   3704    if (nsGlobalWindowInner* inner = GetOwnerWindow()) {
   3705      window = inner->GetOuterWindow();
   3706    }
   3707    return wwatch->GetPrompt(window, aIID, reinterpret_cast<void**>(aResult));
   3708  }
   3709 
   3710  // Now check for the various XHR non-DOM interfaces, except
   3711  // nsIProgressEventSink and nsIChannelEventSink which we already
   3712  // handled above.
   3713  if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
   3714    *aResult = static_cast<nsIStreamListener*>(EnsureXPCOMifier().take());
   3715    return NS_OK;
   3716  }
   3717  if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
   3718    *aResult = static_cast<nsIRequestObserver*>(EnsureXPCOMifier().take());
   3719    return NS_OK;
   3720  }
   3721  if (aIID.Equals(NS_GET_IID(nsITimerCallback))) {
   3722    *aResult = static_cast<nsITimerCallback*>(EnsureXPCOMifier().take());
   3723    return NS_OK;
   3724  }
   3725 
   3726  return QueryInterface(aIID, aResult);
   3727 }
   3728 
   3729 void XMLHttpRequestMainThread::GetInterface(
   3730    JSContext* aCx, JS::Handle<JS::Value> aIID,
   3731    JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv) {
   3732  dom::GetInterface(aCx, this, aIID, aRetval, aRv);
   3733 }
   3734 
   3735 XMLHttpRequestUpload* XMLHttpRequestMainThread::GetUpload(ErrorResult& aRv) {
   3736  if (!mUpload) {
   3737    mUpload = new XMLHttpRequestUpload(this);
   3738  }
   3739  return mUpload;
   3740 }
   3741 
   3742 bool XMLHttpRequestMainThread::MozAnon() const { return mIsAnon; }
   3743 
   3744 bool XMLHttpRequestMainThread::MozSystem() const { return IsSystemXHR(); }
   3745 
   3746 void XMLHttpRequestMainThread::HandleTimeoutCallback() {
   3747  DEBUG_WORKERREFS;
   3748  if (mState == XMLHttpRequest_Binding::DONE) {
   3749    MOZ_ASSERT_UNREACHABLE(
   3750        "XMLHttpRequestMainThread::HandleTimeoutCallback "
   3751        "with completed request");
   3752    // do nothing!
   3753    return;
   3754  }
   3755 
   3756  mFlagTimedOut = true;
   3757  CloseRequestWithError(Events::timeout);
   3758 }
   3759 
   3760 void XMLHttpRequestMainThread::CancelTimeoutTimer() {
   3761  DEBUG_WORKERREFS;
   3762  if (mTimeoutTimer) {
   3763    mTimeoutTimer->Cancel();
   3764    mTimeoutTimer = nullptr;
   3765  }
   3766 }
   3767 
   3768 NS_IMETHODIMP
   3769 XMLHttpRequestMainThread::Notify(nsITimer* aTimer) {
   3770  DEBUG_WORKERREFS;
   3771  if (mProgressNotifier == aTimer) {
   3772    HandleProgressTimerCallback();
   3773    return NS_OK;
   3774  }
   3775 
   3776  if (mTimeoutTimer == aTimer) {
   3777    HandleTimeoutCallback();
   3778    return NS_OK;
   3779  }
   3780 
   3781  if (mSyncTimeoutTimer == aTimer) {
   3782    HandleSyncTimeoutTimer();
   3783    return NS_OK;
   3784  }
   3785 
   3786  // Just in case some JS user wants to QI to nsITimerCallback and play with
   3787  // us...
   3788  NS_WARNING("Unexpected timer!");
   3789  return NS_ERROR_INVALID_POINTER;
   3790 }
   3791 
   3792 void XMLHttpRequestMainThread::HandleProgressTimerCallback() {
   3793  DEBUG_WORKERREFS;
   3794  // Don't fire the progress event if mLoadTotal is 0, see XHR spec step 6.1
   3795  if (!mLoadTotal && mLoadTransferred) {
   3796    return;
   3797  }
   3798 
   3799  mProgressTimerIsActive = false;
   3800 
   3801  if (!mProgressSinceLastProgressEvent || mErrorLoad != ErrorType::eOK) {
   3802    return;
   3803  }
   3804 
   3805  if (InUploadPhase()) {
   3806    if (mUpload && !mUploadComplete && mFlagHadUploadListenersOnSend) {
   3807      DispatchProgressEvent(mUpload, Events::progress, mUploadTransferred,
   3808                            mUploadTotal);
   3809    }
   3810  } else {
   3811    FireReadystatechangeEvent();
   3812    DispatchProgressEvent(this, Events::progress, mLoadTransferred, mLoadTotal);
   3813  }
   3814 
   3815  mProgressSinceLastProgressEvent = false;
   3816 
   3817  StartProgressEventTimer();
   3818 }
   3819 
   3820 void XMLHttpRequestMainThread::StopProgressEventTimer() {
   3821  if (mProgressNotifier) {
   3822    mProgressTimerIsActive = false;
   3823    mProgressNotifier->Cancel();
   3824  }
   3825 }
   3826 
   3827 void XMLHttpRequestMainThread::StartProgressEventTimer() {
   3828  if (!mProgressNotifier) {
   3829    mProgressNotifier = NS_NewTimer(GetTimerEventTarget());
   3830  }
   3831  if (mProgressNotifier) {
   3832    mProgressTimerIsActive = true;
   3833    mProgressNotifier->Cancel();
   3834    mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
   3835                                        nsITimer::TYPE_ONE_SHOT);
   3836  }
   3837 }
   3838 
   3839 XMLHttpRequestMainThread::SyncTimeoutType
   3840 XMLHttpRequestMainThread::MaybeStartSyncTimeoutTimer() {
   3841  MOZ_ASSERT(mFlagSynchronous);
   3842 
   3843  Document* doc = GetDocumentIfCurrent();
   3844  if (!doc || !doc->GetPageUnloadingEventTimeStamp()) {
   3845    return eNoTimerNeeded;
   3846  }
   3847 
   3848  // If we are in a beforeunload or a unload event, we must force a timeout.
   3849  TimeDuration diff =
   3850      (TimeStamp::NowLoRes() - doc->GetPageUnloadingEventTimeStamp());
   3851  if (diff.ToMilliseconds() > MAX_SYNC_TIMEOUT_WHEN_UNLOADING) {
   3852    return eErrorOrExpired;
   3853  }
   3854 
   3855  mSyncTimeoutTimer = NS_NewTimer(GetTimerEventTarget());
   3856  if (!mSyncTimeoutTimer) {
   3857    return eErrorOrExpired;
   3858  }
   3859 
   3860  uint32_t timeout = MAX_SYNC_TIMEOUT_WHEN_UNLOADING - diff.ToMilliseconds();
   3861  nsresult rv = mSyncTimeoutTimer->InitWithCallback(this, timeout,
   3862                                                    nsITimer::TYPE_ONE_SHOT);
   3863  return NS_FAILED(rv) ? eErrorOrExpired : eTimerStarted;
   3864 }
   3865 
   3866 void XMLHttpRequestMainThread::HandleSyncTimeoutTimer() {
   3867  MOZ_ASSERT(mSyncTimeoutTimer);
   3868  MOZ_ASSERT(mFlagSyncLooping);
   3869 
   3870  CancelSyncTimeoutTimer();
   3871  Abort();
   3872  mErrorLoadDetail = NS_ERROR_DOM_TIMEOUT_ERR;
   3873 }
   3874 
   3875 void XMLHttpRequestMainThread::CancelSyncTimeoutTimer() {
   3876  if (mSyncTimeoutTimer) {
   3877    mSyncTimeoutTimer->Cancel();
   3878    mSyncTimeoutTimer = nullptr;
   3879  }
   3880 }
   3881 
   3882 already_AddRefed<nsXMLHttpRequestXPCOMifier>
   3883 XMLHttpRequestMainThread::EnsureXPCOMifier() {
   3884  if (!mXPCOMifier) {
   3885    mXPCOMifier = new nsXMLHttpRequestXPCOMifier(this);
   3886  }
   3887  RefPtr<nsXMLHttpRequestXPCOMifier> newRef(mXPCOMifier);
   3888  return newRef.forget();
   3889 }
   3890 
   3891 bool XMLHttpRequestMainThread::ShouldBlockAuthPrompt() {
   3892  // Verify that it's ok to prompt for credentials here, per spec
   3893  // http://xhr.spec.whatwg.org/#the-send%28%29-method
   3894 
   3895  if (mAuthorRequestHeaders.Has("authorization")) {
   3896    return true;
   3897  }
   3898 
   3899  nsCOMPtr<nsIURI> uri;
   3900  nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
   3901  if (NS_WARN_IF(NS_FAILED(rv))) {
   3902    return false;
   3903  }
   3904 
   3905  // Also skip if a username and/or password is provided in the URI.
   3906  bool hasUserPass;
   3907  return NS_SUCCEEDED(uri->GetHasUserPass(&hasUserPass)) && hasUserPass;
   3908 }
   3909 
   3910 void XMLHttpRequestMainThread::TruncateResponseText() {
   3911  mResponseText.Truncate();
   3912  XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
   3913 }
   3914 
   3915 NS_IMPL_ISUPPORTS(XMLHttpRequestMainThread::nsHeaderVisitor,
   3916                  nsIHttpHeaderVisitor)
   3917 
   3918 NS_IMETHODIMP XMLHttpRequestMainThread::nsHeaderVisitor::VisitHeader(
   3919    const nsACString& header, const nsACString& value) {
   3920  if (mXHR.IsSafeHeader(header, mHttpChannel)) {
   3921    nsAutoCString lowerHeader(header);
   3922    ToLowerCase(lowerHeader);
   3923    if (!mHeaderList.InsertElementSorted(HeaderEntry(lowerHeader, value),
   3924                                         fallible)) {
   3925      return NS_ERROR_OUT_OF_MEMORY;
   3926    }
   3927  }
   3928  return NS_OK;
   3929 }
   3930 
   3931 XMLHttpRequestMainThread::nsHeaderVisitor::nsHeaderVisitor(
   3932    const XMLHttpRequestMainThread& aXMLHttpRequest,
   3933    NotNull<nsIHttpChannel*> aHttpChannel)
   3934    : mXHR(aXMLHttpRequest), mHttpChannel(aHttpChannel) {}
   3935 
   3936 XMLHttpRequestMainThread::nsHeaderVisitor::~nsHeaderVisitor() = default;
   3937 
   3938 void XMLHttpRequestMainThread::MaybeCreateBlobStorage() {
   3939  DEBUG_WORKERREFS;
   3940  MOZ_ASSERT(mResponseType == XMLHttpRequestResponseType::Blob);
   3941 
   3942  if (mBlobStorage) {
   3943    return;
   3944  }
   3945 
   3946  MutableBlobStorage::MutableBlobStorageType storageType =
   3947      BasePrincipal::Cast(mPrincipal)->PrivateBrowsingId() == 0
   3948          ? MutableBlobStorage::eCouldBeInTemporaryFile
   3949          : MutableBlobStorage::eOnlyInMemory;
   3950 
   3951  nsCOMPtr<nsIEventTarget> eventTarget;
   3952  if (nsIGlobalObject* global = GetOwnerGlobal()) {
   3953    eventTarget = global->SerialEventTarget();
   3954  }
   3955 
   3956  mBlobStorage = new MutableBlobStorage(storageType, eventTarget);
   3957 }
   3958 
   3959 void XMLHttpRequestMainThread::BlobStoreCompleted(
   3960    MutableBlobStorage* aBlobStorage, BlobImpl* aBlobImpl, nsresult aRv) {
   3961  DEBUG_WORKERREFS;
   3962  // Ok, the state is changed...
   3963  if (mBlobStorage != aBlobStorage || NS_FAILED(aRv)) {
   3964    return;
   3965  }
   3966 
   3967  MOZ_ASSERT(mState != XMLHttpRequest_Binding::DONE);
   3968 
   3969  mResponseBlobImpl = aBlobImpl;
   3970  mBlobStorage = nullptr;
   3971 
   3972  ChangeStateToDone(mFlagSyncLooping);
   3973 }
   3974 
   3975 NS_IMETHODIMP
   3976 XMLHttpRequestMainThread::GetName(nsACString& aName) {
   3977  aName.AssignLiteral("XMLHttpRequest");
   3978  return NS_OK;
   3979 }
   3980 
   3981 // nsXMLHttpRequestXPCOMifier implementation
   3982 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier)
   3983  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   3984  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   3985  NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
   3986  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
   3987  NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
   3988  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   3989  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
   3990  NS_INTERFACE_MAP_ENTRY(nsINamed)
   3991  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
   3992 NS_INTERFACE_MAP_END
   3993 
   3994 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier)
   3995 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier)
   3996 
   3997 // Can't NS_IMPL_CYCLE_COLLECTION( because mXHR has ambiguous
   3998 // inheritance from nsISupports.
   3999 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier)
   4000 
   4001 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequestXPCOMifier)
   4002  if (tmp->mXHR) {
   4003    tmp->mXHR->mXPCOMifier = nullptr;
   4004  }
   4005  NS_IMPL_CYCLE_COLLECTION_UNLINK(mXHR)
   4006 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   4007 
   4008 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier)
   4009  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR)
   4010 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   4011 
   4012 NS_IMETHODIMP
   4013 nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID& aIID, void** aResult) {
   4014  // Return ourselves for the things we implement (except
   4015  // nsIInterfaceRequestor) and the XHR for the rest.
   4016  if (!aIID.Equals(NS_GET_IID(nsIInterfaceRequestor))) {
   4017    nsresult rv = QueryInterface(aIID, aResult);
   4018    if (NS_SUCCEEDED(rv)) {
   4019      return rv;
   4020    }
   4021  }
   4022 
   4023  return mXHR->GetInterface(aIID, aResult);
   4024 }
   4025 
   4026 ArrayBufferBuilder::ArrayBufferBuilder()
   4027    : mMutex("ArrayBufferBuilder"),
   4028      mDataPtr(nullptr),
   4029      mCapacity(0),
   4030      mLength(0),
   4031      mMapPtr(nullptr),
   4032      mNeutered(false) {}
   4033 
   4034 ArrayBufferBuilder::~ArrayBufferBuilder() {
   4035  if (mDataPtr) {
   4036    JS_free(nullptr, mDataPtr);
   4037  }
   4038 
   4039  if (mMapPtr) {
   4040    JS::ReleaseMappedArrayBufferContents(mMapPtr, mLength);
   4041    mMapPtr = nullptr;
   4042  }
   4043 
   4044  mDataPtr = nullptr;
   4045  mCapacity = mLength = 0;
   4046 }
   4047 
   4048 bool ArrayBufferBuilder::SetCapacity(uint32_t aNewCap) {
   4049  MutexAutoLock lock(mMutex);
   4050  return SetCapacityInternal(aNewCap, lock);
   4051 }
   4052 
   4053 bool ArrayBufferBuilder::SetCapacityInternal(
   4054    uint32_t aNewCap, const MutexAutoLock& aProofOfLock) {
   4055  MOZ_ASSERT(!mMapPtr);
   4056  MOZ_ASSERT(!mNeutered);
   4057 
   4058  // To ensure that realloc won't free mDataPtr, use a size of 1
   4059  // instead of 0.
   4060  uint8_t* newdata = (uint8_t*)js_realloc(mDataPtr, aNewCap ? aNewCap : 1);
   4061 
   4062  if (!newdata) {
   4063    return false;
   4064  }
   4065 
   4066  if (aNewCap > mCapacity) {
   4067    memset(newdata + mCapacity, 0, aNewCap - mCapacity);
   4068  }
   4069 
   4070  mDataPtr = newdata;
   4071  mCapacity = aNewCap;
   4072  if (mLength > aNewCap) {
   4073    mLength = aNewCap;
   4074  }
   4075 
   4076  return true;
   4077 }
   4078 
   4079 bool ArrayBufferBuilder::Append(const uint8_t* aNewData, uint32_t aDataLen,
   4080                                uint32_t aMaxGrowth) {
   4081  MutexAutoLock lock(mMutex);
   4082  MOZ_ASSERT(!mMapPtr);
   4083  MOZ_ASSERT(!mNeutered);
   4084 
   4085  CheckedUint32 neededCapacity = mLength;
   4086  neededCapacity += aDataLen;
   4087  if (!neededCapacity.isValid()) {
   4088    return false;
   4089  }
   4090  if (mLength + aDataLen > mCapacity) {
   4091    CheckedUint32 newcap = mCapacity;
   4092    // Double while under aMaxGrowth or if not specified.
   4093    if (!aMaxGrowth || mCapacity < aMaxGrowth) {
   4094      newcap *= 2;
   4095    } else {
   4096      newcap += aMaxGrowth;
   4097    }
   4098 
   4099    if (!newcap.isValid()) {
   4100      return false;
   4101    }
   4102 
   4103    // But make sure there's always enough to satisfy our request.
   4104    if (newcap.value() < neededCapacity.value()) {
   4105      newcap = neededCapacity;
   4106    }
   4107 
   4108    if (!SetCapacityInternal(newcap.value(), lock)) {
   4109      return false;
   4110    }
   4111  }
   4112 
   4113  // Assert that the region isn't overlapping so we can memcpy.
   4114  MOZ_ASSERT(
   4115      !AreOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength, aDataLen));
   4116 
   4117  memcpy(mDataPtr + mLength, aNewData, aDataLen);
   4118  mLength += aDataLen;
   4119 
   4120  return true;
   4121 }
   4122 
   4123 uint32_t ArrayBufferBuilder::Length() {
   4124  MutexAutoLock lock(mMutex);
   4125  MOZ_ASSERT(!mNeutered);
   4126  return mLength;
   4127 }
   4128 
   4129 uint32_t ArrayBufferBuilder::Capacity() {
   4130  MutexAutoLock lock(mMutex);
   4131  MOZ_ASSERT(!mNeutered);
   4132  return mCapacity;
   4133 }
   4134 
   4135 JSObject* ArrayBufferBuilder::TakeArrayBuffer(JSContext* aCx) {
   4136  MutexAutoLock lock(mMutex);
   4137  MOZ_DIAGNOSTIC_ASSERT(!mNeutered);
   4138 
   4139  if (mMapPtr) {
   4140    JSObject* obj = JS::NewMappedArrayBufferWithContents(aCx, mLength, mMapPtr);
   4141    if (!obj) {
   4142      JS::ReleaseMappedArrayBufferContents(mMapPtr, mLength);
   4143    }
   4144 
   4145    mMapPtr = nullptr;
   4146    mNeutered = true;
   4147 
   4148    // The memory-mapped contents will be released when the ArrayBuffer becomes
   4149    // detached or is GC'd.
   4150    return obj;
   4151  }
   4152 
   4153  // we need to check for mLength == 0, because nothing may have been
   4154  // added
   4155  if (mCapacity > mLength || mLength == 0) {
   4156    if (!SetCapacityInternal(mLength, lock)) {
   4157      return nullptr;
   4158    }
   4159  }
   4160 
   4161  // |mDataPtr| will be deallocated in ArrayBufferBuilder's destructor when this
   4162  // ArrayBuffer allocation failed.
   4163  JSObject* obj = JS::NewArrayBufferWithContents(
   4164      aCx, mLength, mDataPtr,
   4165      JS::NewArrayBufferOutOfMemory::CallerMustFreeMemory);
   4166  if (!obj) {
   4167    return nullptr;
   4168  }
   4169 
   4170  mDataPtr = nullptr;
   4171  mCapacity = mLength = 0;
   4172 
   4173  mNeutered = true;
   4174  return obj;
   4175 }
   4176 
   4177 nsresult ArrayBufferBuilder::MapToFileInPackage(const nsCString& aFile,
   4178                                                nsIFile* aJarFile) {
   4179  MutexAutoLock lock(mMutex);
   4180  MOZ_ASSERT(NS_IsMainThread());
   4181  MOZ_ASSERT(!mNeutered);
   4182 
   4183  nsresult rv;
   4184 
   4185  // Open Jar file to get related attributes of target file.
   4186  RefPtr<nsZipArchive> zip = nsZipArchive::OpenArchive(aJarFile);
   4187  if (!zip) {
   4188    return NS_ERROR_FAILURE;
   4189  }
   4190  nsZipItem* zipItem = zip->GetItem(aFile);
   4191  if (!zipItem) {
   4192    return NS_ERROR_FILE_NOT_FOUND;
   4193  }
   4194 
   4195  // If file was added to the package as stored(uncompressed), map to the
   4196  // offset of file in zip package.
   4197  if (!zipItem->Compression()) {
   4198    uint32_t offset = zip->GetDataOffset(zipItem);
   4199    uint32_t size = zipItem->RealSize();
   4200    mozilla::AutoFDClose pr_fd;
   4201    rv = aJarFile->OpenNSPRFileDesc(PR_RDONLY, 0, getter_Transfers(pr_fd));
   4202    if (NS_FAILED(rv)) {
   4203      return rv;
   4204    }
   4205    mMapPtr = JS::CreateMappedArrayBufferContents(
   4206        PR_FileDesc2NativeHandle(pr_fd.get()), offset, size);
   4207    if (mMapPtr) {
   4208      mLength = size;
   4209      return NS_OK;
   4210    }
   4211  }
   4212  return NS_ERROR_FAILURE;
   4213 }
   4214 
   4215 /* static */
   4216 bool ArrayBufferBuilder::AreOverlappingRegions(const uint8_t* aStart1,
   4217                                               uint32_t aLength1,
   4218                                               const uint8_t* aStart2,
   4219                                               uint32_t aLength2) {
   4220  const uint8_t* end1 = aStart1 + aLength1;
   4221  const uint8_t* end2 = aStart2 + aLength2;
   4222 
   4223  const uint8_t* max_start = aStart1 > aStart2 ? aStart1 : aStart2;
   4224  const uint8_t* min_end = end1 < end2 ? end1 : end2;
   4225 
   4226  return max_start < min_end;
   4227 }
   4228 
   4229 RequestHeaders::RequestHeader* RequestHeaders::Find(const nsACString& aName) {
   4230  for (RequestHeaders::RequestHeader& header : mHeaders) {
   4231    if (header.mName.Equals(aName, nsCaseInsensitiveCStringComparator)) {
   4232      return &header;
   4233    }
   4234  }
   4235  return nullptr;
   4236 }
   4237 
   4238 bool RequestHeaders::IsEmpty() const { return mHeaders.IsEmpty(); }
   4239 
   4240 bool RequestHeaders::Has(const char* aName) {
   4241  return Has(nsDependentCString(aName));
   4242 }
   4243 
   4244 bool RequestHeaders::Has(const nsACString& aName) { return !!Find(aName); }
   4245 
   4246 void RequestHeaders::Get(const char* aName, nsACString& aValue) {
   4247  Get(nsDependentCString(aName), aValue);
   4248 }
   4249 
   4250 void RequestHeaders::Get(const nsACString& aName, nsACString& aValue) {
   4251  RequestHeader* header = Find(aName);
   4252  if (header) {
   4253    aValue = header->mValue;
   4254  } else {
   4255    aValue.SetIsVoid(true);
   4256  }
   4257 }
   4258 
   4259 void RequestHeaders::Set(const char* aName, const nsACString& aValue) {
   4260  Set(nsDependentCString(aName), aValue);
   4261 }
   4262 
   4263 void RequestHeaders::Set(const nsACString& aName, const nsACString& aValue) {
   4264  RequestHeader* header = Find(aName);
   4265  if (header) {
   4266    header->mValue.Assign(aValue);
   4267  } else {
   4268    RequestHeader newHeader = {nsCString(aName), nsCString(aValue)};
   4269    mHeaders.AppendElement(newHeader);
   4270  }
   4271 }
   4272 
   4273 void RequestHeaders::MergeOrSet(const char* aName, const nsACString& aValue) {
   4274  MergeOrSet(nsDependentCString(aName), aValue);
   4275 }
   4276 
   4277 void RequestHeaders::MergeOrSet(const nsACString& aName,
   4278                                const nsACString& aValue) {
   4279  RequestHeader* header = Find(aName);
   4280  if (header) {
   4281    header->mValue.AppendLiteral(", ");
   4282    header->mValue.Append(aValue);
   4283  } else {
   4284    RequestHeader newHeader = {nsCString(aName), nsCString(aValue)};
   4285    mHeaders.AppendElement(newHeader);
   4286  }
   4287 }
   4288 
   4289 void RequestHeaders::Clear() { mHeaders.Clear(); }
   4290 
   4291 void RequestHeaders::ApplyToChannel(nsIHttpChannel* aChannel,
   4292                                    bool aStripRequestBodyHeader,
   4293                                    bool aStripAuthHeader) const {
   4294  for (const RequestHeader& header : mHeaders) {
   4295    if (aStripRequestBodyHeader &&
   4296        (header.mName.LowerCaseEqualsASCII("content-type") ||
   4297         header.mName.LowerCaseEqualsASCII("content-encoding") ||
   4298         header.mName.LowerCaseEqualsASCII("content-language") ||
   4299         header.mName.LowerCaseEqualsASCII("content-location"))) {
   4300      continue;
   4301    }
   4302 
   4303    if (aStripAuthHeader &&
   4304        header.mName.LowerCaseEqualsASCII("authorization")) {
   4305      continue;
   4306    }
   4307 
   4308    // Update referrerInfo to override referrer header in system privileged.
   4309    if (header.mName.LowerCaseEqualsASCII("referer")) {
   4310      DebugOnly<nsresult> rv = aChannel->SetNewReferrerInfo(
   4311          header.mValue, nsIReferrerInfo::ReferrerPolicyIDL::UNSAFE_URL, true);
   4312      MOZ_ASSERT(NS_SUCCEEDED(rv));
   4313    }
   4314    if (header.mValue.IsEmpty()) {
   4315      DebugOnly<nsresult> rv = aChannel->SetEmptyRequestHeader(header.mName);
   4316      MOZ_ASSERT(NS_SUCCEEDED(rv));
   4317    } else {
   4318      DebugOnly<nsresult> rv =
   4319          aChannel->SetRequestHeader(header.mName, header.mValue, false);
   4320      MOZ_ASSERT(NS_SUCCEEDED(rv));
   4321    }
   4322  }
   4323 }
   4324 
   4325 void RequestHeaders::GetCORSUnsafeHeaders(nsTArray<nsCString>& aArray) const {
   4326  for (const RequestHeader& header : mHeaders) {
   4327    if (!nsContentUtils::IsCORSSafelistedRequestHeader(header.mName,
   4328                                                       header.mValue)) {
   4329      aArray.AppendElement(header.mName);
   4330    }
   4331  }
   4332 }
   4333 
   4334 RequestHeaders::CharsetIterator::CharsetIterator(nsACString& aSource)
   4335    : mValid(false),
   4336      mCurPos(-1),
   4337      mCurLen(-1),
   4338      mCutoff(aSource.Length()),
   4339      mSource(aSource) {}
   4340 
   4341 bool RequestHeaders::CharsetIterator::Equals(
   4342    const nsACString& aOther, const nsCStringComparator& aCmp) const {
   4343  if (mValid) {
   4344    return Substring(mSource, mCurPos, mCurLen).Equals(aOther, aCmp);
   4345  } else {
   4346    return false;
   4347  }
   4348 }
   4349 
   4350 void RequestHeaders::CharsetIterator::Replace(const nsACString& aReplacement) {
   4351  if (mValid) {
   4352    mSource.Replace(mCurPos, mCurLen, aReplacement);
   4353    mCurLen = aReplacement.Length();
   4354  }
   4355 }
   4356 
   4357 bool RequestHeaders::CharsetIterator::Next() {
   4358  int32_t start, end;
   4359  nsAutoCString charset;
   4360 
   4361  // Look for another charset declaration in the string, limiting the
   4362  // search to only the characters before the parts we've already searched
   4363  // (before mCutoff), so that we don't find the same charset twice.
   4364  NS_ExtractCharsetFromContentType(Substring(mSource, 0, mCutoff), charset,
   4365                                   &mValid, &start, &end);
   4366 
   4367  if (!mValid) {
   4368    return false;
   4369  }
   4370 
   4371  // Everything after the = sign is the part of the charset we want.
   4372  mCurPos = mSource.FindChar('=', start) + 1;
   4373  mCurLen = end - mCurPos;
   4374 
   4375  // Special case: the extracted charset is quoted with single quotes.
   4376  // For the purpose of preserving what was set we want to handle them
   4377  // as delimiters (although they aren't really).
   4378  if (charset.Length() >= 2 && charset.First() == '\'' &&
   4379      charset.Last() == '\'') {
   4380    ++mCurPos;
   4381    mCurLen -= 2;
   4382  }
   4383 
   4384  mCutoff = start;
   4385 
   4386  return true;
   4387 }
   4388 
   4389 }  // namespace mozilla::dom