Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/xhr/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 136 kB image not shown  

Quelle  XMLHttpRequestMainThread.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "XMLHttpRequestMainThread.h"

#include <algorithm>
#ifndef XP_WIN
#  include <unistd.h>
#endif
#include "mozilla/AppShutdown.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/Components.h"
#include "mozilla/dom/AutoSuppressEventHandlingAndSuspend.h"
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/BlobURLProtocolHandler.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/DOMString.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileBinding.h"
#include "mozilla/dom/FileCreatorHelper.h"
#include "mozilla/dom/FetchUtil.h"
#include "mozilla/dom/FormData.h"
#include "mozilla/dom/quota/QuotaCommon.h"
#include "mozilla/dom/MutableBlobStorage.h"
#include "mozilla/dom/XMLDocument.h"
#include "mozilla/dom/URLSearchParams.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/ReferrerInfo.h"
#include "mozilla/dom/WorkerError.h"
#include "mozilla/Encoding.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/LoadContext.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/net/ContentRange.h"
#include "mozilla/PreloaderBase.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPrefs_privacy.h"
#include "mozilla/dom/ProgressEvent.h"
#include "nsDataChannel.h"
#include "nsIBaseChannel.h"
#include "nsIJARChannel.h"
#include "nsIJARURI.h"
#include "nsGlobalWindowInner.h"
#include "nsReadableUtils.h"
#include "nsSandboxFlags.h"

#include "nsIContentPolicy.h"
#include "nsIURI.h"
#include "nsIURIMutator.h"
#include "nsILoadGroup.h"
#include "nsNetUtil.h"
#include "nsStringStream.h"
#include "nsIAuthPrompt.h"
#include "nsIAuthPrompt2.h"
#include "nsIClassOfService.h"
#include "nsIHttpChannel.h"
#include "nsISupportsPriority.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsStreamUtils.h"
#include "nsThreadUtils.h"
#include "nsIUploadChannel.h"
#include "nsIUploadChannel2.h"
#include "nsXPCOM.h"
#include "nsIDOMEventListener.h"
#include "nsVariant.h"
#include "nsIScriptError.h"
#include "nsICachingChannel.h"
#include "nsICookieJarSettings.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsError.h"
#include "nsIPromptFactory.h"
#include "nsIWindowWatcher.h"
#include "nsIConsoleService.h"
#include "nsAsyncRedirectVerifyHelper.h"
#include "nsIFileChannel.h"
#include "mozilla/Telemetry.h"
#include "js/ArrayBuffer.h"  // JS::{Create,Release}MappedArrayBufferContents,New{,Mapped}ArrayBufferWithContents
#include "js/JSON.h"         // JS_ParseJSON
#include "js/MemoryFunctions.h"
#include "js/RootingAPI.h"  // JS::{{,Mutable}Handle,Rooted}
#include "js/Value.h"       // JS::{,Undefined}Value
#include "jsapi.h"          // JS_ClearPendingException
#include "GeckoProfiler.h"
#include "mozilla/dom/XMLHttpRequestBinding.h"
#include "mozilla/Attributes.h"
#include "MultipartBlobImpl.h"
#include "nsIPermissionManager.h"
#include "nsMimeTypes.h"
#include "nsIHttpChannelInternal.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsStreamListenerWrapper.h"
#include "nsITimedChannel.h"
#include "nsWrapperCacheInlines.h"
#include "nsZipArchive.h"
#include "mozilla/Preferences.h"
#include "private/pprio.h"
#include "XMLHttpRequestUpload.h"

// Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
// replaced by FileCreatorHelper#CreateFileW.
#ifdef CreateFile
#  undef CreateFile
#endif

extern mozilla::LazyLogModule gXMLHttpRequestLog;

using namespace mozilla::net;

namespace mozilla::dom {

using EventType = XMLHttpRequest::EventType;
using Events = XMLHttpRequest::Events;

// Maximum size that we'll grow an ArrayBuffer instead of doubling,
// once doubling reaches this threshold
const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH = 32 * 1024 * 1024;
// start at 32k to avoid lots of doubling right at the start
const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE = 32 * 1024;
// the maximum Content-Length that we'll preallocate.  1GB.  Must fit
// in an int32_t!
const int32_t XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE =
    1 * 1024 * 1024 * 1024LL;

constexpr nsLiteralString kLiteralString_readystatechange =
    u"readystatechange"_ns;
// constexpr nsLiteralString kLiteralString_xmlhttprequest =
// u"xmlhttprequest"_ns;
constexpr nsLiteralString kLiteralString_DOMContentLoaded =
    u"DOMContentLoaded"_ns;
constexpr nsLiteralCString kLiteralString_charset = "charset"_ns;
constexpr nsLiteralCString kLiteralString_UTF_8 = "UTF-8"_ns;

#define NS_PROGRESS_EVENT_INTERVAL 50
#define MAX_SYNC_TIMEOUT_WHEN_UNLOADING 10000 /* 10 secs */

NS_IMPL_ISUPPORTS(nsXHRParseEndListener, nsIDOMEventListener)

class nsResumeTimeoutsEvent : public Runnable {
 public:
  explicit nsResumeTimeoutsEvent(nsPIDOMWindowInner* aWindow)
      : Runnable("dom::nsResumeTimeoutsEvent"), mWindow(aWindow) {}

  NS_IMETHOD Run() override {
    mWindow->Resume();
    return NS_OK;
  }

 private:
  nsCOMPtr<nsPIDOMWindowInner> mWindow;
};

// This helper function adds the given load flags to the request's existing
// load flags.
static void AddLoadFlags(nsIRequest* request, nsLoadFlags newFlags) {
  nsLoadFlags flags;
  request->GetLoadFlags(&flags);
  flags |= newFlags;
  request->SetLoadFlags(flags);
}

// We are in a sync event loop.
#define NOT_CALLABLE_IN_SYNC_SEND_RV                               \
  if (mFlagSyncLooping || mEventDispatchingSuspended) {            \
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT); \
    return;                                                        \
  }

/////////////////////////////////////////////
//
//
/////////////////////////////////////////////

#ifdef DEBUG

// In debug mode, annotate WorkerRefs with the name of the function being
// invoked for increased scrutability.  Save the previous value on the stack.
namespace {
struct DebugWorkerRefs {
  Mutex& mMutex;
  RefPtr<ThreadSafeWorkerRef> mTSWorkerRef;
  nsCString mPrev;

  DebugWorkerRefs(XMLHttpRequestMainThread& aXHR, const std::string& aStatus)
      : mMutex(aXHR.mTSWorkerRefMutex) {
    MutexAutoLock lock(mMutex);

    mTSWorkerRef = aXHR.mTSWorkerRef;

    if (!mTSWorkerRef) {
      return;
    }

    MOZ_ASSERT(mTSWorkerRef->Private());

    nsCString status(aStatus.c_str());
    mPrev = GET_WORKERREF_DEBUG_STATUS(mTSWorkerRef->Ref());
    SET_WORKERREF_DEBUG_STATUS(mTSWorkerRef->Ref(), status);
  }

  ~DebugWorkerRefs() {
    MutexAutoLock lock(mMutex);

    if (!mTSWorkerRef) {
      return;
    }

    MOZ_ASSERT(mTSWorkerRef->Private());

    SET_WORKERREF_DEBUG_STATUS(mTSWorkerRef->Ref(), mPrev);

    mTSWorkerRef = nullptr;
  }
};
}  // namespace

#  define STREAM_STRING(stuff)                                    \
    (((const std::ostringstream&)(std::ostringstream() << stuff)) \
         .str())  // NOLINT

#  if 1  // Disabling because bug 1855699
#    define DEBUG_WORKERREFS void()
#    define DEBUG_WORKERREFS1(x) void()
#  else

#    define DEBUG_WORKERREFS \
      DebugWorkerRefs MOZ_UNIQUE_VAR(debugWR__)(*this, __func__)

#    define DEBUG_WORKERREFS1(x)                 \
      DebugWorkerRefs MOZ_UNIQUE_VAR(debugWR__)( \
          *this, STREAM_STRING(__func__ << ": " << x))  // NOLINT

#  endif

#else
#  define DEBUG_WORKERREFS void()
#  define DEBUG_WORKERREFS1(x) void()
#endif  // DEBUG

bool XMLHttpRequestMainThread::sDontWarnAboutSyncXHR = false;

XMLHttpRequestMainThread::XMLHttpRequestMainThread(
    nsIGlobalObject* aGlobalObject)
    : XMLHttpRequest(aGlobalObject),
#ifdef DEBUG
      mTSWorkerRefMutex("Debug WorkerRefs"),
#endif
      mResponseBodyDecodedPos(0),
      mResponseType(XMLHttpRequestResponseType::_empty),
      mState(XMLHttpRequest_Binding::UNSENT),
      mFlagSynchronous(false),
      mFlagAborted(false),
      mFlagParseBody(false),
      mFlagSyncLooping(false),
      mFlagBackgroundRequest(false),
      mFlagHadUploadListenersOnSend(false),
      mFlagACwithCredentials(false),
      mFlagTimedOut(false),
      mFlagDeleted(false),
      mFlagSend(false),
      mUploadTransferred(0),
      mUploadTotal(0),
      mUploadComplete(true),
      mProgressSinceLastProgressEvent(false),
      mRequestSentTime(0),
      mTimeoutMilliseconds(0),
      mErrorLoad(ErrorType::eOK),
      mErrorLoadDetail(NS_OK),
      mErrorParsingXML(false),
      mWaitingForOnStopRequest(false),
      mProgressTimerIsActive(false),
      mIsHtml(false),
      mWarnAboutSyncHtml(false),
      mLoadTotal(-1),
      mLoadTransferred(0),
      mIsSystem(false),
      mIsAnon(false),
      mResultJSON(JS::UndefinedValue()),
      mArrayBufferBuilder(new ArrayBufferBuilder()),
      mResultArrayBuffer(nullptr),
      mIsMappedArrayBuffer(false),
      mXPCOMifier(nullptr),
      mEventDispatchingSuspended(false),
      mEofDecoded(false),
      mDelayedDoneNotifier(nullptr) {
  DEBUG_WORKERREFS;
  mozilla::HoldJSObjects(this);
}

XMLHttpRequestMainThread::~XMLHttpRequestMainThread() {
  DEBUG_WORKERREFS;
  MOZ_ASSERT(
      !mDelayedDoneNotifier,
      "How can we have mDelayedDoneNotifier, which owns us, in destructor?");

  mFlagDeleted = true;

  if ((mState == XMLHttpRequest_Binding::OPENED && mFlagSend) ||
      mState == XMLHttpRequest_Binding::LOADING) {
    Abort();
  }

  if (mParseEndListener) {
    mParseEndListener->SetIsStale();
    mParseEndListener = nullptr;
  }

  MOZ_ASSERT(!mFlagSyncLooping, "we rather crash than hang");
  mFlagSyncLooping = false;

  mozilla::DropJSObjects(this);
}

void XMLHttpRequestMainThread::Construct(
    nsIPrincipal* aPrincipal, nsICookieJarSettings* aCookieJarSettings,
    bool aForWorker, nsIURI* aBaseURI /* = nullptr */,
    nsILoadGroup* aLoadGroup /* = nullptr */,
    PerformanceStorage* aPerformanceStorage /* = nullptr */,
    nsICSPEventListener* aCSPEventListener /* = nullptr */) {
  DEBUG_WORKERREFS;
  MOZ_ASSERT(aPrincipal);
  mPrincipal = aPrincipal;
  mBaseURI = aBaseURI;
  mLoadGroup = aLoadGroup;
  mCookieJarSettings = aCookieJarSettings;
  mForWorker = aForWorker;
  mPerformanceStorage = aPerformanceStorage;
  mCSPEventListener = aCSPEventListener;
}

void XMLHttpRequestMainThread::InitParameters(bool aAnon, bool aSystem) {
  DEBUG_WORKERREFS;
  if (!aAnon && !aSystem) {
    return;
  }

  // Check for permissions.
  // Chrome is always allowed access, so do the permission check only
  // for non-chrome pages.
  if (!IsSystemXHR() && aSystem) {
    nsIGlobalObject* global = GetOwnerGlobal();
    if (NS_WARN_IF(!global)) {
      SetParameters(aAnon, false);
      return;
    }

    nsIPrincipal* principal = global->PrincipalOrNull();
    if (NS_WARN_IF(!principal)) {
      SetParameters(aAnon, false);
      return;
    }

    nsCOMPtr<nsIPermissionManager> permMgr =
        components::PermissionManager::Service();
    if (NS_WARN_IF(!permMgr)) {
      SetParameters(aAnon, false);
      return;
    }

    uint32_t permission;
    nsresult rv = permMgr->TestPermissionFromPrincipal(
        principal, "systemXHR"_ns, &permission);
    if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
      SetParameters(aAnon, false);
      return;
    }
  }

  SetParameters(aAnon, aSystem);
}

void XMLHttpRequestMainThread::SetClientInfoAndController(
    const ClientInfo& aClientInfo,
    const Maybe<ServiceWorkerDescriptor>& aController) {
  mClientInfo.emplace(aClientInfo);
  mController = aController;
}

void XMLHttpRequestMainThread::ResetResponse() {
  mResponseXML = nullptr;
  mResponseBody.Truncate();
  TruncateResponseText();
  mResponseBlobImpl = nullptr;
  mResponseBlob = nullptr;
  mBlobStorage = nullptr;
  mResultArrayBuffer = nullptr;
  mArrayBufferBuilder = new ArrayBufferBuilder();
  mResultJSON.setUndefined();
  mLoadTransferred = 0;
  mResponseBodyDecodedPos = 0;
  mEofDecoded = false;
}

NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestMainThread)

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestMainThread,
                                                  XMLHttpRequestEventTarget)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestMainThread,
                                                XMLHttpRequestEventTarget)
  tmp->mResultArrayBuffer = nullptr;
  tmp->mArrayBufferBuilder = nullptr;
  tmp->mResultJSON.setUndefined();
  tmp->mResponseBlobImpl = nullptr;

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks)

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink)

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestMainThread,
                                               XMLHttpRequestEventTarget)
  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultJSON)
NS_IMPL_CYCLE_COLLECTION_TRACE_END

bool XMLHttpRequestMainThread::IsCertainlyAliveForCC() const {
  return mWaitingForOnStopRequest;
}

// QueryInterface implementation for XMLHttpRequestMainThread
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestMainThread)
  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
  NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
  NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
  NS_INTERFACE_MAP_ENTRY(nsINamed)
  NS_INTERFACE_MAP_ENTRY(nsISizeOfEventTarget)
NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget)

NS_IMPL_ADDREF_INHERITED(XMLHttpRequestMainThread, XMLHttpRequestEventTarget)
NS_IMPL_RELEASE_INHERITED(XMLHttpRequestMainThread, XMLHttpRequestEventTarget)

void XMLHttpRequestMainThread::DisconnectFromOwner() {
  XMLHttpRequestEventTarget::DisconnectFromOwner();
  // Worker-owned XHRs have their own complicated state machine that does not
  // expect Abort() to be called here.  The worker state machine cleanup will
  // take care of ensuring the XHR is aborted in a timely fashion since the
  // worker itself will inherently be canceled at the same time this is
  // happening.
  if (!mForWorker) {
    Abort();
  }
}

size_t XMLHttpRequestMainThread::SizeOfEventTargetIncludingThis(
    MallocSizeOf aMallocSizeOf) const {
  size_t n = aMallocSizeOf(this);
  n += mResponseBody.SizeOfExcludingThisIfUnshared(aMallocSizeOf);

  // Why is this safe?  Because no-one else will report this string.  The
  // other possible sharers of this string are as follows.
  //
  // - The JS engine could hold copies if the JS code holds references, e.g.
  //   |var text = XHR.responseText|.  However, those references will be via JS
  //   external strings, for which the JS memory reporter does *not* report the
  //   chars.
  //
  // - Binary extensions, but they're *extremely* unlikely to do any memory
  //   reporting.
  //
  n += mResponseText.SizeOfThis(aMallocSizeOf);

  return n;

  // Measurement of the following members may be added later if DMD finds it is
  // worthwhile:
  // - lots
}

static void LogMessage(
    const char* aWarning, nsPIDOMWindowInner* aWindow,
    const nsTArray<nsString>& aParams = nsTArray<nsString>()) {
  nsCOMPtr<Document> doc;
  if (aWindow) {
    doc = aWindow->GetExtantDoc();
  }
  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, doc,
                                  nsContentUtils::eDOM_PROPERTIES, aWarning,
                                  aParams);
}

Document* XMLHttpRequestMainThread::GetResponseXML(ErrorResult& aRv) {
  if (mResponseType != XMLHttpRequestResponseType::_empty &&
      mResponseType != XMLHttpRequestResponseType::Document) {
    aRv.ThrowInvalidStateError(
        "responseXML is only available if responseType is '' or 'document'.");
    return nullptr;
  }
  if (mWarnAboutSyncHtml) {
    mWarnAboutSyncHtml = false;
    LogMessage("HTMLSyncXHRWarning", GetOwnerWindow());
  }
  if (mState != XMLHttpRequest_Binding::DONE) {
    return nullptr;
  }
  return mResponseXML;
}

/*
 * This piece copied from XMLDocument, we try to get the charset
 * from HTTP headers.
 */

nsresult XMLHttpRequestMainThread::DetectCharset() {
  DEBUG_WORKERREFS;
  mDecoder = nullptr;

  if (mResponseType != XMLHttpRequestResponseType::_empty &&
      mResponseType != XMLHttpRequestResponseType::Text &&
      mResponseType != XMLHttpRequestResponseType::Json) {
    return NS_OK;
  }

  nsAutoCString charsetVal;
  const Encoding* encoding;
  bool ok = mChannel && NS_SUCCEEDED(mChannel->GetContentCharset(charsetVal)) &&
            (encoding = Encoding::ForLabel(charsetVal));
  if (!ok) {
    // MS documentation states UTF-8 is default for responseText
    encoding = UTF_8_ENCODING;
  }

  if (mResponseType == XMLHttpRequestResponseType::Json &&
      encoding != UTF_8_ENCODING) {
    // The XHR spec says only UTF-8 is supported for responseType == "json"
    LogMessage("JSONCharsetWarning", GetOwnerWindow());
    encoding = UTF_8_ENCODING;
  }

  // Only sniff the BOM for non-JSON responseTypes
  if (mResponseType == XMLHttpRequestResponseType::Json) {
    mDecoder = encoding->NewDecoderWithBOMRemoval();
  } else {
    mDecoder = encoding->NewDecoder();
  }

  return NS_OK;
}

nsresult XMLHttpRequestMainThread::AppendToResponseText(
    Span<const uint8_t> aBuffer, bool aLast) {
  // Call this with an empty buffer to send the decoder the signal
  // that we have hit the end of the stream.

  NS_ENSURE_STATE(mDecoder);

  CheckedInt<size_t> destBufferLen =
      mDecoder->MaxUTF16BufferLength(aBuffer.Length());

  {  // scope for holding the mutex that protects mResponseText
    XMLHttpRequestStringWriterHelper helper(mResponseText);

    uint32_t len = helper.Length();

    destBufferLen += len;
    if (!destBufferLen.isValid() || destBufferLen.value() > UINT32_MAX) {
      return NS_ERROR_OUT_OF_MEMORY;
    }

    auto handleOrErr = helper.BulkWrite(destBufferLen.value());
    if (handleOrErr.isErr()) {
      return handleOrErr.unwrapErr();
    }

    auto handle = handleOrErr.unwrap();

    uint32_t result;
    size_t read;
    size_t written;
    std::tie(result, read, written, std::ignore) =
        mDecoder->DecodeToUTF16(aBuffer, handle.AsSpan().From(len), aLast);
    MOZ_ASSERT(result == kInputEmpty);
    MOZ_ASSERT(read == aBuffer.Length());
    len += written;
    MOZ_ASSERT(len <= destBufferLen.value());
    handle.Finish(len, false);
  }  // release mutex

  if (aLast) {
    // Drop the finished decoder to avoid calling into a decoder
    // that has finished.
    mDecoder = nullptr;
    mEofDecoded = true;
  }
  return NS_OK;
}

void XMLHttpRequestMainThread::GetResponseText(DOMString& aResponseText,
                                               ErrorResult& aRv) {
  MOZ_DIAGNOSTIC_ASSERT(!mForWorker);

  XMLHttpRequestStringSnapshot snapshot;
  GetResponseText(snapshot, aRv);
  if (aRv.Failed()) {
    return;
  }

  if (!snapshot.GetAsString(aResponseText)) {
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    return;
  }
}

void XMLHttpRequestMainThread::GetResponseText(
    XMLHttpRequestStringSnapshot& aSnapshot, ErrorResult& aRv) {
  aSnapshot.Reset();

  if (mResponseType != XMLHttpRequestResponseType::_empty &&
      mResponseType != XMLHttpRequestResponseType::Text) {
    aRv.ThrowInvalidStateError(
        "responseText is only available if responseType is '' or 'text'.");
    return;
  }

  if (mState != XMLHttpRequest_Binding::LOADING &&
      mState != XMLHttpRequest_Binding::DONE) {
    return;
  }

  // Main Fetch step 18 requires to ignore body for head/connect methods.
  if (mRequestMethod.EqualsLiteral("HEAD") ||
      mRequestMethod.EqualsLiteral("CONNECT")) {
    return;
  }

  // We only decode text lazily if we're also parsing to a doc.
  // Also, if we've decoded all current data already, then no need to decode
  // more.
  if ((!mResponseXML && !mErrorParsingXML) ||
      (mResponseBodyDecodedPos == mResponseBody.Length() &&
       (mState != XMLHttpRequest_Binding::DONE || mEofDecoded))) {
    mResponseText.CreateSnapshot(aSnapshot);
    return;
  }

  MatchCharsetAndDecoderToResponseDocument();

  MOZ_ASSERT(mResponseBodyDecodedPos < mResponseBody.Length() ||
                 mState == XMLHttpRequest_Binding::DONE,
             "Unexpected mResponseBodyDecodedPos");
  Span<const uint8_t> span = mResponseBody;
  aRv = AppendToResponseText(span.From(mResponseBodyDecodedPos),
                             mState == XMLHttpRequest_Binding::DONE);
  if (aRv.Failed()) {
    return;
  }

  mResponseBodyDecodedPos = mResponseBody.Length();

  if (mEofDecoded) {
    // Free memory buffer which we no longer need
    mResponseBody.Truncate();
    mResponseBodyDecodedPos = 0;
  }

  mResponseText.CreateSnapshot(aSnapshot);
}

nsresult XMLHttpRequestMainThread::CreateResponseParsedJSON(JSContext* aCx) {
  if (!aCx) {
    return NS_ERROR_FAILURE;
  }

  nsAutoString string;
  nsresult rv = GetResponseTextForJSON(string);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  // The Unicode converter has already zapped the BOM if there was one
  JS::Rooted<JS::Value> value(aCx);
  if (!JS_ParseJSON(aCx, string.BeginReading(), string.Length(), &value)) {
    return NS_ERROR_FAILURE;
  }

  mResultJSON = value;
  return NS_OK;
}

void XMLHttpRequestMainThread::SetResponseType(
    XMLHttpRequestResponseType aResponseType, ErrorResult& aRv) {
  NOT_CALLABLE_IN_SYNC_SEND_RV

  if (mState == XMLHttpRequest_Binding::LOADING ||
      mState == XMLHttpRequest_Binding::DONE) {
    aRv.ThrowInvalidStateError(
        "Cannot set 'responseType' property on XMLHttpRequest after 'send()' "
        "(when its state is LOADING or DONE).");
    return;
  }

  // sync request is not allowed setting responseType in window context
  if (HasOrHasHadOwnerWindow() && mState != XMLHttpRequest_Binding::UNSENT &&
      mFlagSynchronous) {
    LogMessage("ResponseTypeSyncXHRWarning", GetOwnerWindow());
    aRv.ThrowInvalidAccessError(
        "synchronous XMLHttpRequests do not support timeout and responseType");
    return;
  }

  // Set the responseType attribute's value to the given value.
  SetResponseTypeRaw(aResponseType);
}

void XMLHttpRequestMainThread::GetResponse(
    JSContext* aCx, JS::MutableHandle<JS::Value> aResponse, ErrorResult& aRv) {
  MOZ_DIAGNOSTIC_ASSERT(!mForWorker);

  switch (mResponseType) {
    case XMLHttpRequestResponseType::_empty:
    case XMLHttpRequestResponseType::Text: {
      DOMString str;
      GetResponseText(str, aRv);
      if (aRv.Failed()) {
        return;
      }
      if (!xpc::StringToJsval(aCx, str, aResponse)) {
        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
      }
      return;
    }

    case XMLHttpRequestResponseType::Arraybuffer: {
      if (mState != XMLHttpRequest_Binding::DONE) {
        aResponse.setNull();
        return;
      }

      if (!mResultArrayBuffer) {
        mResultArrayBuffer = mArrayBufferBuilder->TakeArrayBuffer(aCx);
        if (!mResultArrayBuffer) {
          aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
          return;
        }
      }
      aResponse.setObject(*mResultArrayBuffer);
      return;
    }
    case XMLHttpRequestResponseType::Blob: {
      if (mState != XMLHttpRequest_Binding::DONE) {
        aResponse.setNull();
        return;
      }

      if (!mResponseBlobImpl) {
        aResponse.setNull();
        return;
      }

      if (!mResponseBlob) {
        mResponseBlob = Blob::Create(GetOwnerGlobal(), mResponseBlobImpl);
      }

      if (!GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse)) {
        aResponse.setNull();
      }

      return;
    }
    case XMLHttpRequestResponseType::Document: {
      if (!mResponseXML || mState != XMLHttpRequest_Binding::DONE) {
        aResponse.setNull();
        return;
      }

      aRv =
          nsContentUtils::WrapNative(aCx, ToSupports(mResponseXML), aResponse);
      return;
    }
    case XMLHttpRequestResponseType::Json: {
      if (mState != XMLHttpRequest_Binding::DONE) {
        aResponse.setNull();
        return;
      }

      if (mResultJSON.isUndefined()) {
        aRv = CreateResponseParsedJSON(aCx);
        TruncateResponseText();
        if (aRv.Failed()) {
          // Per spec, errors aren't propagated. null is returned instead.
          aRv = NS_OK;
          // It would be nice to log the error to the console. That's hard to
          // do without calling window.onerror as a side effect, though.
          JS_ClearPendingException(aCx);
          mResultJSON.setNull();
        }
      }
      aResponse.set(mResultJSON);
      return;
    }
    default:
      NS_ERROR("Should not happen");
  }

  aResponse.setNull();
}

already_AddRefed<BlobImpl> XMLHttpRequestMainThread::GetResponseBlobImpl() {
  MOZ_DIAGNOSTIC_ASSERT(mForWorker);
  MOZ_DIAGNOSTIC_ASSERT(mResponseType == XMLHttpRequestResponseType::Blob);

  if (mState != XMLHttpRequest_Binding::DONE) {
    return nullptr;
  }

  RefPtr<BlobImpl> blobImpl = mResponseBlobImpl;
  return blobImpl.forget();
}

already_AddRefed<ArrayBufferBuilder>
XMLHttpRequestMainThread::GetResponseArrayBufferBuilder() {
  MOZ_DIAGNOSTIC_ASSERT(mForWorker);
  MOZ_DIAGNOSTIC_ASSERT(mResponseType ==
                        XMLHttpRequestResponseType::Arraybuffer);

  if (mState != XMLHttpRequest_Binding::DONE) {
    return nullptr;
  }

  RefPtr<ArrayBufferBuilder> builder = mArrayBufferBuilder;
  return builder.forget();
}

nsresult XMLHttpRequestMainThread::GetResponseTextForJSON(nsAString& aString) {
  if (mState != XMLHttpRequest_Binding::DONE) {
    aString.SetIsVoid(true);
    return NS_OK;
  }

  if (!mResponseText.GetAsString(aString)) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  return NS_OK;
}

bool XMLHttpRequestMainThread::IsCrossSiteCORSRequest() const {
  if (!mChannel) {
    return false;
  }

  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
  return loadInfo->GetTainting() == LoadTainting::CORS;
}

bool XMLHttpRequestMainThread::IsDeniedCrossSiteCORSRequest() {
  if (IsCrossSiteCORSRequest()) {
    nsresult rv;
    mChannel->GetStatus(&rv);
    if (NS_FAILED(rv)) {
      return true;
    }
  }
  return false;
}

bool XMLHttpRequestMainThread::BadContentRangeRequested() {
  if (!mChannel) {
    return false;
  }
  // Only nsIBaseChannel supports this
  nsCOMPtr<nsIBaseChannel> baseChan = do_QueryInterface(mChannel);
  if (!baseChan) {
    return false;
  }
  // A bad range was requested if the channel has no content range
  // despite the request specifying a range header.
  return !baseChan->ContentRange() && mAuthorRequestHeaders.Has("range");
}

RefPtr<mozilla::net::ContentRange>
XMLHttpRequestMainThread::GetRequestedContentRange() const {
  MOZ_ASSERT(mChannel);
  nsCOMPtr<nsIBaseChannel> baseChan = do_QueryInterface(mChannel);
  if (!baseChan) {
    return nullptr;
  }
  return baseChan->ContentRange();
}

void XMLHttpRequestMainThread::GetContentRangeHeader(nsACString& out) const {
  if (!IsBlobURI(mRequestURL)) {
    out.SetIsVoid(true);
    return;
  }
  RefPtr<mozilla::net::ContentRange> range = GetRequestedContentRange();
  if (range) {
    range->AsHeader(out);
  } else {
    out.SetIsVoid(true);
  }
}

void XMLHttpRequestMainThread::GetResponseURL(nsAString& aUrl) {
  aUrl.Truncate();

  if ((mState == XMLHttpRequest_Binding::UNSENT ||
       mState == XMLHttpRequest_Binding::OPENED) ||
      !mChannel) {
    return;
  }

  // Make sure we don't leak responseURL information from denied cross-site
  // requests.
  if (IsDeniedCrossSiteCORSRequest()) {
    return;
  }

  nsCOMPtr<nsIURI> responseUrl;
  if (NS_FAILED(NS_GetFinalChannelURI(mChannel, getter_AddRefs(responseUrl)))) {
    return;
  }

  nsAutoCString temp;
  responseUrl->GetSpecIgnoringRef(temp);
  CopyUTF8toUTF16(temp, aUrl);
}

uint32_t XMLHttpRequestMainThread::GetStatus(ErrorResult& aRv) {
  // Make sure we don't leak status information from denied cross-site
  // requests.
  if (IsDeniedCrossSiteCORSRequest()) {
    return 0;
  }

  if (mState == XMLHttpRequest_Binding::UNSENT ||
      mState == XMLHttpRequest_Binding::OPENED) {
    return 0;
  }

  if (mErrorLoad != ErrorType::eOK) {
    // Let's simulate the http protocol for jar/app requests:
    nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
    if (jarChannel) {
      nsresult status;
      mChannel->GetStatus(&status);

      if (status == NS_ERROR_FILE_NOT_FOUND) {
        return 404;  // Not Found
      } else {
        return 500;  // Internal Error
      }
    }

    return 0;
  }

  nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
  if (!httpChannel) {
    // Pretend like we got a 200/206 response, since our load was successful
    return GetRequestedContentRange() ? 206 : 200;
  }

  uint32_t status;
  nsresult rv = httpChannel->GetResponseStatus(&status);
  if (NS_FAILED(rv)) {
    status = 0;
  }

  return status;
}

void XMLHttpRequestMainThread::GetStatusText(nsACString& aStatusText,
                                             ErrorResult& aRv) {
  // Return an empty status text on all error loads.
  aStatusText.Truncate();

  // Make sure we don't leak status information from denied cross-site
  // requests.
  if (IsDeniedCrossSiteCORSRequest()) {
    return;
  }

  // Check the current XHR state to see if it is valid to obtain the statusText
  // value.  This check is to prevent the status text for redirects from being
  // available before all the redirects have been followed and HTTP headers have
  // been received.
  if (mState == XMLHttpRequest_Binding::UNSENT ||
      mState == XMLHttpRequest_Binding::OPENED) {
    return;
  }

  if (mErrorLoad != ErrorType::eOK) {
    return;
  }

  nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
  if (httpChannel) {
    Unused << httpChannel->GetResponseStatusText(aStatusText);
  } else {
    aStatusText.AssignLiteral("OK");
  }
}

void XMLHttpRequestMainThread::TerminateOngoingFetch(nsresult detail) {
  DEBUG_WORKERREFS;
  if ((mState == XMLHttpRequest_Binding::OPENED && mFlagSend) ||
      mState == XMLHttpRequest_Binding::HEADERS_RECEIVED ||
      mState == XMLHttpRequest_Binding::LOADING) {
    MOZ_LOG(gXMLHttpRequestLog, LogLevel::Info,
            ("%p TerminateOngoingFetch(0x%" PRIx32 ")"this,
             static_cast<uint32_t>(detail)));
    CloseRequest(detail);
  }
}

void XMLHttpRequestMainThread::CloseRequest(nsresult detail) {
  DEBUG_WORKERREFS;
  mWaitingForOnStopRequest = false;
  mErrorLoad = ErrorType::eTerminated;
  mErrorLoadDetail = detail;
  if (mChannel) {
    mChannel->CancelWithReason(NS_BINDING_ABORTED,
                               "XMLHttpRequestMainThread::CloseRequest"_ns);
  }
  CancelTimeoutTimer();
}

void XMLHttpRequestMainThread::CloseRequestWithError(
    const ErrorProgressEventType& aType) {
  DEBUG_WORKERREFS;
  MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug,
          ("%p CloseRequestWithError(%s)"this, aType.cStr));

  CloseRequest(aType.errorCode);

  ResetResponse();

  // If we're in the destructor, don't risk dispatching an event.
  if (mFlagDeleted) {
    mFlagSyncLooping = false;
    return;
  }

  if (mState != XMLHttpRequest_Binding::UNSENT &&
      !(mState == XMLHttpRequest_Binding::OPENED && !mFlagSend) &&
      mState != XMLHttpRequest_Binding::DONE) {
    ChangeState(XMLHttpRequest_Binding::DONE, true);

    if (!mFlagSyncLooping) {
      if (mUpload && !mUploadComplete) {
        mUploadComplete = true;
        DispatchProgressEvent(mUpload, aType, 0, -1);
      }
      DispatchProgressEvent(this, aType, 0, -1);
    }
  }

  // The ChangeState call above calls onreadystatechange handlers which
  // if they load a new url will cause XMLHttpRequestMainThread::Open to clear
  // the abort state bit. If this occurs we're not uninitialized (bug 361773).
  if (mFlagAborted) {
    ChangeState(XMLHttpRequest_Binding::UNSENT, false);  // IE seems to do it
  }

  mFlagSyncLooping = false;
}

void XMLHttpRequestMainThread::RequestErrorSteps(
    const ProgressEventType aEventType, const nsresult aOptionalException,
    ErrorResult& aRv) {
  MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug,
          ("%p RequestErrorSteps(%s,0x%" PRIx32 ")"this, aEventType.cStr,
           static_cast<uint32_t>(aOptionalException)));

  // Cancel our timers first before setting our state to done, so we don't
  // trip any assertions if one fires and asserts that state != done.
  CancelTimeoutTimer();
  CancelSyncTimeoutTimer();
  StopProgressEventTimer();

  // Step 1
  mState = XMLHttpRequest_Binding::DONE;

  // Step 2
  mFlagSend = false;

  // Step 3
  ResetResponse();

  // If we're in the destructor, don't risk dispatching an event.
  if (mFlagDeleted) {
    mFlagSyncLooping = false;
    return;
  }

  // Step 4
  if (mFlagSynchronous && NS_FAILED(aOptionalException)) {
    aRv.Throw(aOptionalException);
    return;
  }

  // Step 5
  FireReadystatechangeEvent();

  // Step 6
  if (mUpload && !mUploadComplete) {
    // Step 6-1
    mUploadComplete = true;

    // Step 6-2
    if (mFlagHadUploadListenersOnSend) {
      // Steps 6-3, 6-4 (loadend is fired for us)
      DispatchProgressEvent(mUpload, aEventType, 0, -1);
    }
  }

  // Steps 7 and 8 (loadend is fired for us)
  DispatchProgressEvent(this, aEventType, 0, -1);
}

void XMLHttpRequestMainThread::Abort(ErrorResult& aRv) {
  NOT_CALLABLE_IN_SYNC_SEND_RV
  MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("%p Abort()"this));
  AbortInternal(aRv);
}

void XMLHttpRequestMainThread::AbortInternal(ErrorResult& aRv) {
  MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("%p AbortInternal()"this));
  mFlagAborted = true;
  DisconnectDoneNotifier();

  // Step 1
  TerminateOngoingFetch(NS_ERROR_DOM_ABORT_ERR);

  // Step 2
  if ((mState == XMLHttpRequest_Binding::OPENED && mFlagSend) ||
      mState == XMLHttpRequest_Binding::HEADERS_RECEIVED ||
      mState == XMLHttpRequest_Binding::LOADING) {
    RequestErrorSteps(Events::abort, NS_ERROR_DOM_ABORT_ERR, aRv);
  }

  // Step 3
  if (mState == XMLHttpRequest_Binding::DONE) {
    ChangeState(XMLHttpRequest_Binding::UNSENT,
                false);  // no ReadystateChange event
  }

  mFlagSyncLooping = false;
}

/*Method that checks if it is safe to expose a header value to the client.
It is used to check what headers are exposed for CORS requests.*/

bool XMLHttpRequestMainThread::IsSafeHeader(
    const nsACString& aHeader, NotNull<nsIHttpChannel*> aHttpChannel) const {
  // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
  if (!IsSystemXHR() && nsContentUtils::IsForbiddenResponseHeader(aHeader)) {
    NS_WARNING("blocked access to response header");
    return false;
  }
  // if this is not a CORS call all headers are safe
  if (!IsCrossSiteCORSRequest()) {
    return true;
  }
  // Check for dangerous headers
  // Make sure we don't leak header information from denied cross-site
  // requests.
  if (mChannel) {
    nsresult status;
    mChannel->GetStatus(&status);
    if (NS_FAILED(status)) {
      return false;
    }
  }
  const char* kCrossOriginSafeHeaders[] = {
      "cache-control""content-language""content-type""content-length",
      "expires",       "last-modified",    "pragma"};
  for (uint32_t i = 0; i < std::size(kCrossOriginSafeHeaders); ++i) {
    if (aHeader.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
      return true;
    }
  }
  nsAutoCString headerVal;
  // The "Access-Control-Expose-Headers" header contains a comma separated
  // list of method names.
  Unused << aHttpChannel->GetResponseHeader("Access-Control-Expose-Headers"_ns,
                                            headerVal);
  bool isSafe = false;
  for (const nsACString& token :
       nsCCharSeparatedTokenizer(headerVal, ',').ToRange()) {
    if (token.IsEmpty()) {
      continue;
    }
    if (!NS_IsValidHTTPToken(token)) {
      return false;
    }

    if (token.EqualsLiteral("*") && !mFlagACwithCredentials) {
      isSafe = true;
    } else if (aHeader.Equals(token, nsCaseInsensitiveCStringComparator)) {
      isSafe = true;
    }
  }

  return isSafe;
}

bool XMLHttpRequestMainThread::GetContentType(nsACString& aValue) const {
  MOZ_ASSERT(mChannel);
  nsCOMPtr<nsIBaseChannel> baseChan = do_QueryInterface(mChannel);
  if (baseChan) {
    RefPtr<CMimeType> fullMimeType(baseChan->FullMimeType());
    if (fullMimeType) {
      fullMimeType->Serialize(aValue);
      return true;
    }
  }
  if (NS_SUCCEEDED(mChannel->GetContentType(aValue))) {
    nsCString value;
    if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && !value.IsEmpty()) {
      aValue.AppendLiteral(";charset=");
      aValue.Append(value);
    }
    return true;
  }
  return false;
}
void XMLHttpRequestMainThread::GetAllResponseHeaders(
    nsACString& aResponseHeaders, ErrorResult& aRv) {
  NOT_CALLABLE_IN_SYNC_SEND_RV

  aResponseHeaders.Truncate();

  // If the state is UNSENT or OPENED,
  // return the empty string and terminate these steps.
  if (mState == XMLHttpRequest_Binding::UNSENT ||
      mState == XMLHttpRequest_Binding::OPENED) {
    return;
  }

  if (mErrorLoad != ErrorType::eOK) {
    return;
  }

  if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) {
    RefPtr<nsHeaderVisitor> visitor =
        new nsHeaderVisitor(*this, WrapNotNull(httpChannel));
    if (NS_SUCCEEDED(httpChannel->VisitResponseHeaders(visitor))) {
      aResponseHeaders = visitor->Headers();
    }
    return;
  }

  if (!mChannel) {
    return;
  }

  // Even non-http channels supply content type.
  nsAutoCString value;
  if (GetContentType(value)) {
    aResponseHeaders.AppendLiteral("Content-Type: ");
    aResponseHeaders.Append(value);
    aResponseHeaders.AppendLiteral("\r\n");
  }

  // Don't provide Content-Length for data URIs
  nsCOMPtr<nsIURI> uri;
  if (NS_FAILED(mChannel->GetURI(getter_AddRefs(uri))) ||
      !uri->SchemeIs("data")) {
    int64_t length;
    if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
      aResponseHeaders.AppendLiteral("Content-Length: ");
      aResponseHeaders.AppendInt(length);
      aResponseHeaders.AppendLiteral("\r\n");
    }
  }

  // Should set a Content-Range header for blob scheme.
  // From https://fetch.spec.whatwg.org/#scheme-fetch 3.blob.9.20:
  // "Set response’s header list to «(`Content-Length`, serializedSlicedLength),
  //  (`Content-Type`, type), (`Content-Range`, contentRange)»."
  GetContentRangeHeader(value);
  if (!value.IsVoid()) {
    aResponseHeaders.AppendLiteral("Content-Range: ");
    aResponseHeaders.Append(value);
    aResponseHeaders.AppendLiteral("\r\n");
  }
}

void XMLHttpRequestMainThread::GetResponseHeader(const nsACString& header,
                                                 nsACString& _retval,
                                                 ErrorResult& aRv) {
  NOT_CALLABLE_IN_SYNC_SEND_RV

  _retval.SetIsVoid(true);

  nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();

  if (!httpChannel) {
    // If the state is UNSENT or OPENED,
    // return null and terminate these steps.
    if (mState == XMLHttpRequest_Binding::UNSENT ||
        mState == XMLHttpRequest_Binding::OPENED) {
      return;
    }

    // Even non-http channels supply content type and content length.
    // Remember we don't leak header information from denied cross-site
    // requests. However, we handle file: and blob: URLs for blob response
    // types by canceling them with a specific error, so we have to allow
    // them to pass through this check.
    nsresult status;
    if (!mChannel || NS_FAILED(mChannel->GetStatus(&status)) ||
        (NS_FAILED(status) && status != NS_ERROR_FILE_ALREADY_EXISTS)) {
      return;
    }

    // Content Type:
    if (header.LowerCaseEqualsASCII("content-type")) {
      if (!GetContentType(_retval)) {
        // Means no content type
        _retval.SetIsVoid(true);
        return;
      }
    }

    // Content Length:
    else if (header.LowerCaseEqualsASCII("content-length")) {
      int64_t length;
      if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
        _retval.AppendInt(length);
      }
    }

    // Content Range:
    else if (header.LowerCaseEqualsASCII("content-range")) {
      GetContentRangeHeader(_retval);
    }

    return;
  }

  // Check for dangerous headers
  if (!IsSafeHeader(header, WrapNotNull(httpChannel))) {
    return;
  }

  aRv = httpChannel->GetResponseHeader(header, _retval);
  if (aRv.ErrorCodeIs(NS_ERROR_NOT_AVAILABLE)) {
    // Means no header
    _retval.SetIsVoid(true);
    aRv.SuppressException();
  }
}

already_AddRefed<nsILoadGroup> XMLHttpRequestMainThread::GetLoadGroup() const {
  if (mFlagBackgroundRequest) {
    return nullptr;
  }

  if (mLoadGroup) {
    nsCOMPtr<nsILoadGroup> ref = mLoadGroup;
    return ref.forget();
  }

  Document* doc = GetDocumentIfCurrent();
  if (doc) {
    return doc->GetDocumentLoadGroup();
  }

  return nullptr;
}

nsresult XMLHttpRequestMainThread::FireReadystatechangeEvent() {
  MOZ_ASSERT(mState != XMLHttpRequest_Binding::UNSENT);
  RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
  event->InitEvent(kLiteralString_readystatechange, falsefalse);
  // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
  event->SetTrusted(true);
  DispatchOrStoreEvent(this, event);
  return NS_OK;
}

void XMLHttpRequestMainThread::DispatchProgressEvent(
    DOMEventTargetHelper* aTarget, const ProgressEventType& aType,
    int64_t aLoaded, int64_t aTotal) {
  DEBUG_WORKERREFS;
  NS_ASSERTION(aTarget, "null target");

  if (NS_FAILED(CheckCurrentGlobalCorrectness()) ||
      (!AllowUploadProgress() && aTarget == mUpload)) {
    return;
  }

  // If blocked by CORS, zero-out the stats on progress events
  // and never fire "progress" or "load" events at all.
  if (IsDeniedCrossSiteCORSRequest()) {
    if (aType == Events::progress || aType == Events::load) {
      return;
    }
    aLoaded = 0;
    aTotal = -1;
  }

  ProgressEventInit init;
  init.mBubbles = false;
  init.mCancelable = false;
  init.mLengthComputable = aTotal != -1;  // XHR spec step 6.1
  init.mLoaded = aLoaded;
  init.mTotal = (aTotal == -1) ? 0 : aTotal;

  RefPtr<ProgressEvent> event =
      ProgressEvent::Constructor(aTarget, aType, init);
  event->SetTrusted(true);

  MOZ_LOG(
      gXMLHttpRequestLog, LogLevel::Debug,
      ("firing %s event (%u,%u,%" PRIu64 ",%" PRIu64 ")", aType.cStr,
       aTarget == mUpload, aTotal != -1, aLoaded, (aTotal == -1) ? 0 : aTotal));

  DispatchOrStoreEvent(aTarget, event);

  // If we're sending a load, error, timeout or abort event, then
  // also dispatch the subsequent loadend event.
  if (aType == Events::load || aType == Events::error ||
      aType == Events::timeout || aType == Events::abort) {
    DispatchProgressEvent(aTarget, Events::loadend, aLoaded, aTotal);
  }
}

void XMLHttpRequestMainThread::DispatchOrStoreEvent(
    DOMEventTargetHelper* aTarget, Event* aEvent) {
  DEBUG_WORKERREFS;
  MOZ_ASSERT(aTarget);
  MOZ_ASSERT(aEvent);

  if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
    return;
  }

  if (mEventDispatchingSuspended) {
    PendingEvent* event = mPendingEvents.AppendElement();
    event->mTarget = aTarget;
    event->mEvent = aEvent;
    return;
  }

  aTarget->DispatchEvent(*aEvent);
}

void XMLHttpRequestMainThread::SuspendEventDispatching() {
  MOZ_ASSERT(!mEventDispatchingSuspended);
  mEventDispatchingSuspended = true;
}

void XMLHttpRequestMainThread::ResumeEventDispatching() {
  MOZ_ASSERT(mEventDispatchingSuspended);
  mEventDispatchingSuspended = false;

  nsTArray<PendingEvent> pendingEvents = std::move(mPendingEvents);

  if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
    return;
  }

  for (uint32_t i = 0; i < pendingEvents.Length(); ++i) {
    pendingEvents[i].mTarget->DispatchEvent(*pendingEvents[i].mEvent);
  }
}

already_AddRefed<nsIHttpChannel>
XMLHttpRequestMainThread::GetCurrentHttpChannel() {
  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
  return httpChannel.forget();
}

already_AddRefed<nsIJARChannel>
XMLHttpRequestMainThread::GetCurrentJARChannel() {
  nsCOMPtr<nsIJARChannel> appChannel = do_QueryInterface(mChannel);
  return appChannel.forget();
}

bool XMLHttpRequestMainThread::IsSystemXHR() const {
  return mIsSystem || mPrincipal->IsSystemPrincipal();
}

bool XMLHttpRequestMainThread::InUploadPhase() const {
  // We're in the upload phase while our state is OPENED.
  return mState == XMLHttpRequest_Binding::OPENED;
}

// This case is hit when the async parameter is outright omitted, which
// should set it to true (and the username and password to null).
void XMLHttpRequestMainThread::Open(const nsACString& aMethod,
                                    const nsAString& aUrl, ErrorResult& aRv) {
  Open(aMethod, aUrl, true, VoidString(), VoidString(), aRv);
}

// This case is hit when the async parameter is specified, even if the
// JS value was "undefined" (which due to legacy reasons should be
// treated as true, which is how it will already be passed in here).
void XMLHttpRequestMainThread::Open(const nsACString& aMethod,
                                    const nsAString& aUrl, bool aAsync,
                                    const nsAString& aUsername,
                                    const nsAString& aPassword,
                                    ErrorResult& aRv) {
  Open(aMethod, NS_ConvertUTF16toUTF8(aUrl), aAsync, aUsername, aPassword, aRv);
}

void XMLHttpRequestMainThread::Open(const nsACString& aMethod,
                                    const nsACString& aUrl, bool aAsync,
                                    const nsAString& aUsername,
                                    const nsAString& aPassword,
                                    ErrorResult& aRv) {
  DEBUG_WORKERREFS1(aMethod << " " << aUrl);
  NOT_CALLABLE_IN_SYNC_SEND_RV

  // Gecko-specific
  if (!aAsync && !DontWarnAboutSyncXHR() && GetOwnerWindow() &&
      GetOwnerWindow()->GetExtantDoc()) {
    GetOwnerWindow()->GetExtantDoc()->WarnOnceAbout(
        DeprecatedOperations::eSyncXMLHttpRequestDeprecated);
  }

  Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC,
                        aAsync ? 0 : 1);

  // Step 1
  nsCOMPtr<Document> responsibleDocument = GetDocumentIfCurrent();
  if (!responsibleDocument) {
    // This could be because we're no longer current or because we're in some
    // non-window context...
    if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT);
      return;
    }
  }
  if (!mPrincipal) {
    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
    return;
  }

  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
    aRv.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
    return;
  }

  // Gecko-specific
  if (!aAsync && responsibleDocument && GetOwnerWindow()) {
    // We have no extant document during unload, so the above general
    // syncXHR warning will not display. But we do want to display a
    // recommendation to use sendBeacon instead of syncXHR during unload.
    nsCOMPtr<nsIDocShell> shell = responsibleDocument->GetDocShell();
    if (shell) {
      bool inUnload = false;
      shell->GetIsInUnload(&inUnload);
      if (inUnload) {
        LogMessage("UseSendBeaconDuringUnloadAndPagehideWarning",
                   GetOwnerWindow());
      }
    }
  }

  // Steps 2-4
  nsAutoCString method;
  aRv = FetchUtil::GetValidRequestMethod(aMethod, method);
  if (NS_WARN_IF(aRv.Failed())) {
    return;
  }

  // Steps 5-6
  nsIURI* baseURI = nullptr;
  if (mBaseURI) {
    baseURI = mBaseURI;
  } else if (responsibleDocument) {
    baseURI = responsibleDocument->GetBaseURI();
  }

  // Use the responsible document's encoding for the URL if we have one,
  // except for dedicated workers. Use UTF-8 otherwise.
  NotNull<const Encoding*> originCharset = UTF_8_ENCODING;
  if (responsibleDocument &&
      responsibleDocument->NodePrincipal() == mPrincipal) {
    originCharset = responsibleDocument->GetDocumentCharacterSet();
  }

  nsCOMPtr<nsIURI> parsedURL;
  nsresult rv =
      NS_NewURI(getter_AddRefs(parsedURL), aUrl, originCharset, baseURI);
  if (NS_FAILED(rv)) {
    aRv.ThrowSyntaxError("'"_ns + aUrl + "' is not a valid URL."_ns);
    return;
  }
  if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT);
    return;
  }

  // Step 7
  // This is already handled by the other Open() method, which passes
  // username and password in as NullStrings.

  // Step 8
  nsAutoCString host;
  parsedURL->GetHost(host);
  if (!host.IsEmpty() && (!aUsername.IsVoid() || !aPassword.IsVoid())) {
    auto mutator = NS_MutateURI(parsedURL);
    if (!aUsername.IsVoid()) {
      mutator.SetUsername(NS_ConvertUTF16toUTF8(aUsername));
    }
    if (!aPassword.IsVoid()) {
      mutator.SetPassword(NS_ConvertUTF16toUTF8(aPassword));
    }
    Unused << mutator.Finalize(parsedURL);
  }

  // Step 9
  if (!aAsync && HasOrHasHadOwnerWindow() &&
      (mTimeoutMilliseconds ||
       mResponseType != XMLHttpRequestResponseType::_empty)) {
    if (mTimeoutMilliseconds) {
      LogMessage("TimeoutSyncXHRWarning", GetOwnerWindow());
    }
    if (mResponseType != XMLHttpRequestResponseType::_empty) {
      LogMessage("ResponseTypeSyncXHRWarning", GetOwnerWindow());
    }
    aRv.ThrowInvalidAccessError(
        "synchronous XMLHttpRequests do not support timeout and responseType");
    return;
  }

  // Step 10
  TerminateOngoingFetch(NS_OK);

  // Step 11
  // timeouts are handled without a flag
  DisconnectDoneNotifier();
  mFlagSend = false;
  mRequestMethod.Assign(method);
  mRequestURL = parsedURL;
  mFlagSynchronous = !aAsync;
  mAuthorRequestHeaders.Clear();
  ResetResponse();

  // Gecko-specific
  mFlagHadUploadListenersOnSend = false;
  mFlagAborted = false;
  mFlagTimedOut = false;
  mDecoder = nullptr;

  // Per spec we should only create the channel on send(), but we have internal
  // code that relies on the channel being created now, and that code is not
  // always IsSystemXHR(). However, we're not supposed to throw channel-creation
  // errors during open(), so we silently ignore those here.
  CreateChannel();

  // Step 12
  if (mState != XMLHttpRequest_Binding::OPENED) {
    mState = XMLHttpRequest_Binding::OPENED;
    FireReadystatechangeEvent();
  }
}

void XMLHttpRequestMainThread::SetOriginAttributes(
    const OriginAttributesDictionary& aAttrs) {
  MOZ_ASSERT((mState == XMLHttpRequest_Binding::OPENED) && !mFlagSend);

  OriginAttributes attrs(aAttrs);

  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
  loadInfo->SetOriginAttributes(attrs);
}

/*
 * "Copy" from a stream.
 */

nsresult XMLHttpRequestMainThread::StreamReaderFunc(
    nsIInputStream* in, void* closure, const char* fromRawSegment,
    uint32_t toOffset, uint32_t count, uint32_t* writeCount) {
  XMLHttpRequestMainThread* xmlHttpRequest =
      static_cast<XMLHttpRequestMainThread*>(closure);
  if (!xmlHttpRequest || !writeCount) {
    NS_WARNING(
        "XMLHttpRequest cannot read from stream: no closure or writeCount");
    return NS_ERROR_FAILURE;
  }

  nsresult rv = NS_OK;

  if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Blob) {
    xmlHttpRequest->MaybeCreateBlobStorage();
    rv = xmlHttpRequest->mBlobStorage->Append(fromRawSegment, count);
  } else if (xmlHttpRequest->mResponseType ==
                 XMLHttpRequestResponseType::Arraybuffer &&
             !xmlHttpRequest->mIsMappedArrayBuffer) {
    // get the initial capacity to something reasonable to avoid a bunch of
    // reallocs right at the start
    if (xmlHttpRequest->mArrayBufferBuilder->Capacity() == 0)
      xmlHttpRequest->mArrayBufferBuilder->SetCapacity(
          std::max(count, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE));

    if (NS_WARN_IF(!xmlHttpRequest->mArrayBufferBuilder->Append(
            reinterpret_cast<const uint8_t*>(fromRawSegment), count,
            XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH))) {
      return NS_ERROR_OUT_OF_MEMORY;
    }

  } else if (xmlHttpRequest->mResponseType ==
                 XMLHttpRequestResponseType::_empty &&
             xmlHttpRequest->mResponseXML) {
    // Copy for our own use
    if (!xmlHttpRequest->mResponseBody.Append(fromRawSegment, count,
                                              fallible)) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
  } else if (xmlHttpRequest->mResponseType ==
                 XMLHttpRequestResponseType::_empty ||
             xmlHttpRequest->mResponseType ==
                 XMLHttpRequestResponseType::Text ||
             xmlHttpRequest->mResponseType ==
                 XMLHttpRequestResponseType::Json) {
    MOZ_ASSERT(!xmlHttpRequest->mResponseXML,
               "We shouldn't be parsing a doc here");
    rv = xmlHttpRequest->AppendToResponseText(
        AsBytes(Span(fromRawSegment, count)));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }
  }

  if (xmlHttpRequest->mFlagParseBody) {
    // Give the same data to the parser.

    // We need to wrap the data in a new lightweight stream and pass that
    // to the parser, because calling ReadSegments() recursively on the same
    // stream is not supported.
    nsCOMPtr<nsIInputStream> copyStream;
    rv = NS_NewByteInputStream(getter_AddRefs(copyStream),
                               Span(fromRawSegment, count),
                               NS_ASSIGNMENT_DEPEND);

    if (NS_SUCCEEDED(rv) && xmlHttpRequest->mXMLParserStreamListener) {
      NS_ASSERTION(copyStream, "NS_NewByteInputStream lied");
      nsresult parsingResult =
          xmlHttpRequest->mXMLParserStreamListener->OnDataAvailable(
              xmlHttpRequest->mChannel, copyStream, toOffset, count);

      // No use to continue parsing if we failed here, but we
      // should still finish reading the stream
      if (NS_FAILED(parsingResult)) {
        xmlHttpRequest->mFlagParseBody = false;
      }
    }
  }

  if (NS_SUCCEEDED(rv)) {
    *writeCount = count;
  } else {
    *writeCount = 0;
  }

  return rv;
}

namespace {

void GetBlobURIFromChannel(nsIRequest* aRequest, nsIURI** aURI) {
  MOZ_ASSERT(aRequest);
  MOZ_ASSERT(aURI);

  *aURI = nullptr;

  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
  if (!channel) {
    return;
  }

  nsCOMPtr<nsIURI> uri;
  nsresult rv = channel->GetURI(getter_AddRefs(uri));
  if (NS_FAILED(rv)) {
    return;
  }

  if (!dom::IsBlobURI(uri)) {
    return;
  }

  uri.forget(aURI);
}

nsresult GetLocalFileFromChannel(nsIRequest* aRequest, nsIFile** aFile) {
  MOZ_ASSERT(aRequest);
  MOZ_ASSERT(aFile);

  *aFile = nullptr;

  nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aRequest);
  if (!fc) {
    return NS_OK;
  }

  nsCOMPtr<nsIFile> file;
  nsresult rv = fc->GetFile(getter_AddRefs(file));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  file.forget(aFile);
  return NS_OK;
}

nsresult DummyStreamReaderFunc(nsIInputStream* aInputStream, void* aClosure,
                               const char* aFromRawSegment, uint32_t aToOffset,
                               uint32_t aCount, uint32_t* aWriteCount) {
  *aWriteCount = aCount;
  return NS_OK;
}

class FileCreationHandler final : public PromiseNativeHandler {
 public:
  NS_DECL_ISUPPORTS

  static void Create(Promise* aPromise, XMLHttpRequestMainThread* aXHR) {
    MOZ_ASSERT(aPromise);

    RefPtr<FileCreationHandler> handler = new FileCreationHandler(aXHR);
    aPromise->AppendNativeHandler(handler);
  }

  void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
                        ErrorResult& aRv) override {
    if (NS_WARN_IF(!aValue.isObject())) {
      mXHR->LocalFileToBlobCompleted(nullptr);
      return;
    }

    RefPtr<Blob> blob;
    if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob, &aValue.toObject(), blob)))) {
      mXHR->LocalFileToBlobCompleted(nullptr);
      return;
    }

    mXHR->LocalFileToBlobCompleted(blob->Impl());
  }

  void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
                        ErrorResult& aRv) override {
    mXHR->LocalFileToBlobCompleted(nullptr);
  }

 private:
  explicit FileCreationHandler(XMLHttpRequestMainThread* aXHR) : mXHR(aXHR) {
    MOZ_ASSERT(aXHR);
  }

  ~FileCreationHandler() = default;

  RefPtr<XMLHttpRequestMainThread> mXHR;
};

NS_IMPL_ISUPPORTS0(FileCreationHandler)

}  // namespace

void XMLHttpRequestMainThread::LocalFileToBlobCompleted(BlobImpl* aBlobImpl) {
  MOZ_ASSERT(mState != XMLHttpRequest_Binding::DONE);

  mResponseBlobImpl = aBlobImpl;
  mBlobStorage = nullptr;
  NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");

  ChangeStateToDone(mFlagSyncLooping);
}

NS_IMETHODIMP
XMLHttpRequestMainThread::OnDataAvailable(nsIRequest* request,
                                          nsIInputStream* inStr,
                                          uint64_t sourceOffset,
                                          uint32_t count) {
  DEBUG_WORKERREFS;
  NS_ENSURE_ARG_POINTER(inStr);

  mProgressSinceLastProgressEvent = true;
  XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);

  nsresult rv;

  if (mResponseType == XMLHttpRequestResponseType::Blob) {
    nsCOMPtr<nsIFile> localFile;
    nsCOMPtr<nsIURI> blobURI;
    GetBlobURIFromChannel(request, getter_AddRefs(blobURI));
    if (blobURI) {
      RefPtr<BlobImpl> blobImpl;
      rv = NS_GetBlobForBlobURI(blobURI, getter_AddRefs(blobImpl));
      if (NS_SUCCEEDED(rv)) {
        mResponseBlobImpl = blobImpl;
      }
    } else {
      rv = GetLocalFileFromChannel(request, getter_AddRefs(localFile));
    }
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    if (mResponseBlobImpl || localFile) {
      mBlobStorage = nullptr;
      NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");

      // The nsIStreamListener contract mandates us to read from the stream
      // before returning.
      uint32_t totalRead;
      rv = inStr->ReadSegments(DummyStreamReaderFunc, nullptr, count,
                               &totalRead);
      NS_ENSURE_SUCCESS(rv, rv);

      ChangeState(XMLHttpRequest_Binding::LOADING);

      // Cancel() must be called with an error. We use
      // NS_ERROR_FILE_ALREADY_EXISTS to know that we've aborted the operation
      // just because we can retrieve the File from the channel directly.
      return request->Cancel(NS_ERROR_FILE_ALREADY_EXISTS);
    }
  }

  uint32_t totalRead;
  rv = inStr->ReadSegments(XMLHttpRequestMainThread::StreamReaderFunc,
                           (void*)this, count, &totalRead);
  NS_ENSURE_SUCCESS(rv, rv);

  // Fire the first progress event/loading state change
  if (mState == XMLHttpRequest_Binding::HEADERS_RECEIVED) {
    ChangeState(XMLHttpRequest_Binding::LOADING);
    if (!mFlagSynchronous) {
      DispatchProgressEvent(this, Events::progress, mLoadTransferred,
                            mLoadTotal);
    }
    mProgressSinceLastProgressEvent = false;
  }

  if (!mFlagSynchronous && !mProgressTimerIsActive) {
    StartProgressEventTimer();
  }

  return NS_OK;
}

NS_IMETHODIMP
XMLHttpRequestMainThread::OnStartRequest(nsIRequest* request) {
  DEBUG_WORKERREFS;
  AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStartRequest", NETWORK);

  nsresult rv = NS_OK;

  if (request != mChannel) {
    // Can this still happen?
    return NS_OK;
  }

  // Don't do anything if we have been aborted
  if (mState == XMLHttpRequest_Binding::UNSENT) {
    return NS_OK;
  }

  // Don't do anything if we're in mid-abort, but let the request
  // know (this can happen due to race conditions in valid XHRs,
  // see bz1070763 for info).
  if (mFlagAborted) {
    return NS_BINDING_ABORTED;
  }

  // Don't do anything if we have timed out.
  if (mFlagTimedOut) {
    return NS_OK;
  }

  // If we were asked for a bad range on a blob URL, but we're async,
  // we should throw now in order to fire an error progress event.
  if (BadContentRangeRequested()) {
    return NS_ERROR_NET_PARTIAL_TRANSFER;
  }

  nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
  NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);

  nsresult status;
  request->GetStatus(&status);
  if (mErrorLoad == ErrorType::eOK && NS_FAILED(status)) {
    mErrorLoad = ErrorType::eRequest;
    mErrorLoadDetail = status;
  }

  // Upload phase is now over. If we were uploading anything,
  // stop the timer and fire any final progress events.
  if (mUpload && !mUploadComplete && mErrorLoad == ErrorType::eOK &&
      !mFlagSynchronous) {
    StopProgressEventTimer();

    mUploadTransferred = mUploadTotal;

    if (mProgressSinceLastProgressEvent) {
      DispatchProgressEvent(mUpload, Events::progress, mUploadTransferred,
                            mUploadTotal);
      mProgressSinceLastProgressEvent = false;
    }

    mUploadComplete = true;
    DispatchProgressEvent(mUpload, Events::load, mUploadTotal, mUploadTotal);
  }

  mFlagParseBody = true;
  if (mErrorLoad == ErrorType::eOK) {
    ChangeState(XMLHttpRequest_Binding::HEADERS_RECEIVED);
  }

  ResetResponse();

  if (!mOverrideMimeType.IsEmpty()) {
    channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
  }

  // Fallback to 'application/octet-stream' (leaving data URLs alone)
  if (!IsBlobURI(mRequestURL)) {
    nsAutoCString type;
    channel->GetContentType(type);
    if (type.IsEmpty() || type.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
      channel->SetContentType(nsLiteralCString(APPLICATION_OCTET_STREAM));
    }
  }

  DetectCharset();

  // Set up arraybuffer
  if (mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
      NS_SUCCEEDED(status)) {
    if (mIsMappedArrayBuffer) {
      nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
      if (jarChannel) {
        nsCOMPtr<nsIURI> uri;
        rv = channel->GetURI(getter_AddRefs(uri));
        if (NS_SUCCEEDED(rv)) {
          nsAutoCString file;
          nsAutoCString scheme;
          uri->GetScheme(scheme);
          if (scheme.LowerCaseEqualsLiteral("jar")) {
            nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri);
            if (jarURI) {
              jarURI->GetJAREntry(file);
            }
          }
          nsCOMPtr<nsIFile> jarFile;
          jarChannel->GetJarFile(getter_AddRefs(jarFile));
          if (!jarFile) {
            mIsMappedArrayBuffer = false;
          } else {
            rv = mArrayBufferBuilder->MapToFileInPackage(file, jarFile);
            // This can happen legitimately if there are compressed files
            // in the jarFile. See bug #1357219. No need to warn on the error.
            if (NS_FAILED(rv)) {
              mIsMappedArrayBuffer = false;
            } else {
              channel->SetContentType("application/mem-mapped"_ns);
            }
          }
        }
      }
    }
    // If memory mapping failed, mIsMappedArrayBuffer would be set to false,
    // and we want it fallback to the malloc way.
    if (!mIsMappedArrayBuffer) {
      int64_t contentLength;
      rv = channel->GetContentLength(&contentLength);
      if (NS_SUCCEEDED(rv) && contentLength > 0 &&
          contentLength < XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE) {
        mArrayBufferBuilder->SetCapacity(static_cast<int32_t>(contentLength));
      }
    }
  }

  // Set up responseXML
  // Fetch spec Main Fetch step 21: ignore body for head/connect methods.
  bool parseBody = (mResponseType == XMLHttpRequestResponseType::_empty ||
                    mResponseType == XMLHttpRequestResponseType::Document) &&
                   !(mRequestMethod.EqualsLiteral("HEAD") ||
                     mRequestMethod.EqualsLiteral("CONNECT"));

  if (parseBody) {
    // Do not try to parse documents if content-length = 0
    int64_t contentLength;
    if (NS_SUCCEEDED(mChannel->GetContentLength(&contentLength)) &&
        contentLength == 0) {
      parseBody = false;
    }
  }

  mIsHtml = false;
  mWarnAboutSyncHtml = false;
  if (parseBody && NS_SUCCEEDED(status)) {
    // We can gain a huge performance win by not even trying to
    // parse non-XML data. This also protects us from the situation
    // where we have an XML document and sink, but HTML (or other)
    // parser, which can produce unreliable results.
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=90 H=100 G=95

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.