summaryrefslogtreecommitdiff
path: root/dom/fetch/Fetch.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/fetch/Fetch.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloaduxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/fetch/Fetch.cpp')
-rw-r--r--dom/fetch/Fetch.cpp759
1 files changed, 759 insertions, 0 deletions
diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp
new file mode 100644
index 0000000000..5c05f36024
--- /dev/null
+++ b/dom/fetch/Fetch.cpp
@@ -0,0 +1,759 @@
+/* -*- 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 "Fetch.h"
+#include "FetchConsumer.h"
+
+#include "nsIDocument.h"
+#include "nsIGlobalObject.h"
+#include "nsIStreamLoader.h"
+#include "nsIThreadRetargetableRequest.h"
+#include "nsIUnicodeDecoder.h"
+#include "nsIUnicodeEncoder.h"
+
+#include "nsCharSeparatedTokenizer.h"
+#include "nsDOMString.h"
+#include "nsNetUtil.h"
+#include "nsReadableUtils.h"
+#include "nsStreamUtils.h"
+#include "nsStringStream.h"
+#include "nsProxyRelease.h"
+
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BodyUtil.h"
+#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/Exceptions.h"
+#include "mozilla/dom/FetchDriver.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/FormData.h"
+#include "mozilla/dom/Headers.h"
+#include "mozilla/dom/MutableBlobStreamListener.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseWorkerProxy.h"
+#include "mozilla/dom/Request.h"
+#include "mozilla/dom/Response.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/URLSearchParams.h"
+#include "mozilla/dom/workers/ServiceWorkerManager.h"
+#include "mozilla/Telemetry.h"
+
+#include "InternalRequest.h"
+#include "InternalResponse.h"
+
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
+#include "WorkerScope.h"
+#include "Workers.h"
+
+namespace mozilla {
+namespace dom {
+
+using namespace workers;
+
+class WorkerFetchResolver final : public FetchDriverObserver
+{
+ friend class MainThreadFetchRunnable;
+ friend class WorkerFetchResponseEndRunnable;
+ friend class WorkerFetchResponseRunnable;
+
+ RefPtr<PromiseWorkerProxy> mPromiseProxy;
+public:
+ // Returns null if worker is shutting down.
+ static already_AddRefed<WorkerFetchResolver>
+ Create(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise)
+ {
+ MOZ_ASSERT(aWorkerPrivate);
+ aWorkerPrivate->AssertIsOnWorkerThread();
+ RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(aWorkerPrivate, aPromise);
+ if (!proxy) {
+ return nullptr;
+ }
+
+ RefPtr<WorkerFetchResolver> r = new WorkerFetchResolver(proxy);
+ return r.forget();
+ }
+
+ void
+ OnResponseAvailableInternal(InternalResponse* aResponse) override;
+
+ void
+ OnResponseEnd() override;
+
+private:
+ explicit WorkerFetchResolver(PromiseWorkerProxy* aProxy)
+ : mPromiseProxy(aProxy)
+ {
+ MOZ_ASSERT(!NS_IsMainThread());
+ MOZ_ASSERT(mPromiseProxy);
+ }
+
+ ~WorkerFetchResolver()
+ {}
+
+ virtual void
+ FlushConsoleReport() override;
+};
+
+class MainThreadFetchResolver final : public FetchDriverObserver
+{
+ RefPtr<Promise> mPromise;
+ RefPtr<Response> mResponse;
+
+ nsCOMPtr<nsIDocument> mDocument;
+
+ NS_DECL_OWNINGTHREAD
+public:
+ explicit MainThreadFetchResolver(Promise* aPromise);
+
+ void
+ OnResponseAvailableInternal(InternalResponse* aResponse) override;
+
+ void SetDocument(nsIDocument* aDocument)
+ {
+ mDocument = aDocument;
+ }
+
+ virtual void OnResponseEnd() override
+ {
+ FlushConsoleReport();
+ }
+
+private:
+ ~MainThreadFetchResolver();
+
+ void FlushConsoleReport() override
+ {
+ mReporter->FlushConsoleReports(mDocument);
+ }
+};
+
+class MainThreadFetchRunnable : public Runnable
+{
+ RefPtr<WorkerFetchResolver> mResolver;
+ RefPtr<InternalRequest> mRequest;
+
+public:
+ MainThreadFetchRunnable(WorkerFetchResolver* aResolver,
+ InternalRequest* aRequest)
+ : mResolver(aResolver)
+ , mRequest(aRequest)
+ {
+ MOZ_ASSERT(mResolver);
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ AssertIsOnMainThread();
+ RefPtr<FetchDriver> fetch;
+ RefPtr<PromiseWorkerProxy> proxy = mResolver->mPromiseProxy;
+
+ {
+ // Acquire the proxy mutex while getting data from the WorkerPrivate...
+ MutexAutoLock lock(proxy->Lock());
+ if (proxy->CleanedUp()) {
+ NS_WARNING("Aborting Fetch because worker already shut down");
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = proxy->GetWorkerPrivate()->GetPrincipal();
+ MOZ_ASSERT(principal);
+ nsCOMPtr<nsILoadGroup> loadGroup = proxy->GetWorkerPrivate()->GetLoadGroup();
+ MOZ_ASSERT(loadGroup);
+ fetch = new FetchDriver(mRequest, principal, loadGroup);
+ nsAutoCString spec;
+ if (proxy->GetWorkerPrivate()->GetBaseURI()) {
+ proxy->GetWorkerPrivate()->GetBaseURI()->GetAsciiSpec(spec);
+ }
+ fetch->SetWorkerScript(spec);
+ }
+
+ // ...but release it before calling Fetch, because mResolver's callback can
+ // be called synchronously and they want the mutex, too.
+ return fetch->Fetch(mResolver);
+ }
+};
+
+already_AddRefed<Promise>
+FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
+ const RequestInit& aInit, ErrorResult& aRv)
+{
+ RefPtr<Promise> p = Promise::Create(aGlobal, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ // Double check that we have chrome privileges if the Request's content
+ // policy type has been overridden. Note, we must do this before
+ // entering the global below. Otherwise the IsCallerChrome() will
+ // always fail.
+ MOZ_ASSERT_IF(aInput.IsRequest() &&
+ aInput.GetAsRequest().IsContentPolicyTypeOverridden(),
+ nsContentUtils::IsCallerChrome());
+
+ AutoJSAPI jsapi;
+ if (!jsapi.Init(aGlobal)) {
+ aRv.Throw(NS_ERROR_NOT_AVAILABLE);
+ return nullptr;
+ }
+
+ JSContext* cx = jsapi.cx();
+ JS::Rooted<JSObject*> jsGlobal(cx, aGlobal->GetGlobalJSObject());
+ GlobalObject global(cx, jsGlobal);
+
+ RefPtr<Request> request = Request::Constructor(global, aInput, aInit, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ RefPtr<InternalRequest> r = request->GetInternalRequest();
+
+ if (NS_IsMainThread()) {
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
+ nsCOMPtr<nsIDocument> doc;
+ nsCOMPtr<nsILoadGroup> loadGroup;
+ nsIPrincipal* principal;
+ if (window) {
+ doc = window->GetExtantDoc();
+ if (!doc) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+ principal = doc->NodePrincipal();
+ loadGroup = doc->GetDocumentLoadGroup();
+ } else {
+ principal = aGlobal->PrincipalOrNull();
+ if (NS_WARN_IF(!principal)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+ nsresult rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), principal);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aRv.Throw(rv);
+ return nullptr;
+ }
+ }
+
+ Telemetry::Accumulate(Telemetry::FETCH_IS_MAINTHREAD, 1);
+
+ RefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(p);
+ RefPtr<FetchDriver> fetch = new FetchDriver(r, principal, loadGroup);
+ fetch->SetDocument(doc);
+ resolver->SetDocument(doc);
+ aRv = fetch->Fetch(resolver);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+ } else {
+ WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(worker);
+
+ Telemetry::Accumulate(Telemetry::FETCH_IS_MAINTHREAD, 0);
+
+ if (worker->IsServiceWorker()) {
+ r->SetSkipServiceWorker();
+ }
+
+ RefPtr<WorkerFetchResolver> resolver = WorkerFetchResolver::Create(worker, p);
+ if (!resolver) {
+ NS_WARNING("Could not add WorkerFetchResolver workerHolder to worker");
+ aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
+ return nullptr;
+ }
+
+ RefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(resolver, r);
+ worker->DispatchToMainThread(run.forget());
+ }
+
+ return p.forget();
+}
+
+MainThreadFetchResolver::MainThreadFetchResolver(Promise* aPromise)
+ : mPromise(aPromise)
+{
+}
+
+void
+MainThreadFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse)
+{
+ NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
+ AssertIsOnMainThread();
+
+ if (aResponse->Type() != ResponseType::Error) {
+ nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
+ mResponse = new Response(go, aResponse);
+ mPromise->MaybeResolve(mResponse);
+ } else {
+ ErrorResult result;
+ result.ThrowTypeError<MSG_FETCH_FAILED>();
+ mPromise->MaybeReject(result);
+ }
+}
+
+MainThreadFetchResolver::~MainThreadFetchResolver()
+{
+ NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
+}
+
+class WorkerFetchResponseRunnable final : public MainThreadWorkerRunnable
+{
+ RefPtr<WorkerFetchResolver> mResolver;
+ // Passed from main thread to worker thread after being initialized.
+ RefPtr<InternalResponse> mInternalResponse;
+public:
+ WorkerFetchResponseRunnable(WorkerPrivate* aWorkerPrivate,
+ WorkerFetchResolver* aResolver,
+ InternalResponse* aResponse)
+ : MainThreadWorkerRunnable(aWorkerPrivate)
+ , mResolver(aResolver)
+ , mInternalResponse(aResponse)
+ {
+ }
+
+ bool
+ WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+ {
+ MOZ_ASSERT(aWorkerPrivate);
+ aWorkerPrivate->AssertIsOnWorkerThread();
+
+ RefPtr<Promise> promise = mResolver->mPromiseProxy->WorkerPromise();
+
+ if (mInternalResponse->Type() != ResponseType::Error) {
+ RefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
+ RefPtr<Response> response = new Response(global, mInternalResponse);
+ promise->MaybeResolve(response);
+ } else {
+ ErrorResult result;
+ result.ThrowTypeError<MSG_FETCH_FAILED>();
+ promise->MaybeReject(result);
+ }
+ return true;
+ }
+};
+
+class WorkerFetchResponseEndBase
+{
+ RefPtr<PromiseWorkerProxy> mPromiseProxy;
+public:
+ explicit WorkerFetchResponseEndBase(PromiseWorkerProxy* aPromiseProxy)
+ : mPromiseProxy(aPromiseProxy)
+ {
+ MOZ_ASSERT(mPromiseProxy);
+ }
+
+ void
+ WorkerRunInternal(WorkerPrivate* aWorkerPrivate)
+ {
+ MOZ_ASSERT(aWorkerPrivate);
+ aWorkerPrivate->AssertIsOnWorkerThread();
+ mPromiseProxy->CleanUp();
+ }
+};
+
+class WorkerFetchResponseEndRunnable final : public MainThreadWorkerRunnable
+ , public WorkerFetchResponseEndBase
+{
+public:
+ explicit WorkerFetchResponseEndRunnable(PromiseWorkerProxy* aPromiseProxy)
+ : MainThreadWorkerRunnable(aPromiseProxy->GetWorkerPrivate())
+ , WorkerFetchResponseEndBase(aPromiseProxy)
+ {
+ }
+
+ bool
+ WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+ {
+ WorkerRunInternal(aWorkerPrivate);
+ return true;
+ }
+
+ nsresult
+ Cancel() override
+ {
+ // Execute Run anyway to make sure we cleanup our promise proxy to avoid
+ // leaking the worker thread
+ Run();
+ return WorkerRunnable::Cancel();
+ }
+};
+
+class WorkerFetchResponseEndControlRunnable final : public MainThreadWorkerControlRunnable
+ , public WorkerFetchResponseEndBase
+{
+public:
+ explicit WorkerFetchResponseEndControlRunnable(PromiseWorkerProxy* aPromiseProxy)
+ : MainThreadWorkerControlRunnable(aPromiseProxy->GetWorkerPrivate())
+ , WorkerFetchResponseEndBase(aPromiseProxy)
+ {
+ }
+
+ bool
+ WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+ {
+ WorkerRunInternal(aWorkerPrivate);
+ return true;
+ }
+
+ // Control runnable cancel already calls Run().
+};
+
+void
+WorkerFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse)
+{
+ AssertIsOnMainThread();
+
+ MutexAutoLock lock(mPromiseProxy->Lock());
+ if (mPromiseProxy->CleanedUp()) {
+ return;
+ }
+
+ RefPtr<WorkerFetchResponseRunnable> r =
+ new WorkerFetchResponseRunnable(mPromiseProxy->GetWorkerPrivate(), this,
+ aResponse);
+
+ if (!r->Dispatch()) {
+ NS_WARNING("Could not dispatch fetch response");
+ }
+}
+
+void
+WorkerFetchResolver::OnResponseEnd()
+{
+ AssertIsOnMainThread();
+ MutexAutoLock lock(mPromiseProxy->Lock());
+ if (mPromiseProxy->CleanedUp()) {
+ return;
+ }
+
+ FlushConsoleReport();
+
+ RefPtr<WorkerFetchResponseEndRunnable> r =
+ new WorkerFetchResponseEndRunnable(mPromiseProxy);
+
+ if (!r->Dispatch()) {
+ RefPtr<WorkerFetchResponseEndControlRunnable> cr =
+ new WorkerFetchResponseEndControlRunnable(mPromiseProxy);
+ // This can fail if the worker thread is canceled or killed causing
+ // the PromiseWorkerProxy to give up its WorkerHolder immediately,
+ // allowing the worker thread to become Dead.
+ if (!cr->Dispatch()) {
+ NS_WARNING("Failed to dispatch WorkerFetchResponseEndControlRunnable");
+ }
+ }
+}
+
+void
+WorkerFetchResolver::FlushConsoleReport()
+{
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mPromiseProxy);
+
+ if(!mReporter) {
+ return;
+ }
+
+ workers::WorkerPrivate* worker = mPromiseProxy->GetWorkerPrivate();
+ if (!worker) {
+ mReporter->FlushConsoleReports((nsIDocument*)nullptr);
+ return;
+ }
+
+ if (worker->IsServiceWorker()) {
+ // Flush to service worker
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ if (!swm) {
+ mReporter->FlushConsoleReports((nsIDocument*)nullptr);
+ return;
+ }
+
+ swm->FlushReportsToAllClients(worker->WorkerName(), mReporter);
+ return;
+ }
+
+ if (worker->IsSharedWorker()) {
+ // Flush to shared worker
+ worker->FlushReportsToSharedWorkers(mReporter);
+ return;
+ }
+
+ // Flush to dedicated worker
+ mReporter->FlushConsoleReports(worker->GetDocument());
+}
+
+namespace {
+
+nsresult
+ExtractFromArrayBuffer(const ArrayBuffer& aBuffer,
+ nsIInputStream** aStream,
+ uint64_t& aContentLength)
+{
+ aBuffer.ComputeLengthAndData();
+ aContentLength = aBuffer.Length();
+ //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
+ return NS_NewByteInputStream(aStream,
+ reinterpret_cast<char*>(aBuffer.Data()),
+ aBuffer.Length(), NS_ASSIGNMENT_COPY);
+}
+
+nsresult
+ExtractFromArrayBufferView(const ArrayBufferView& aBuffer,
+ nsIInputStream** aStream,
+ uint64_t& aContentLength)
+{
+ aBuffer.ComputeLengthAndData();
+ aContentLength = aBuffer.Length();
+ //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
+ return NS_NewByteInputStream(aStream,
+ reinterpret_cast<char*>(aBuffer.Data()),
+ aBuffer.Length(), NS_ASSIGNMENT_COPY);
+}
+
+nsresult
+ExtractFromBlob(const Blob& aBlob,
+ nsIInputStream** aStream,
+ nsCString& aContentType,
+ uint64_t& aContentLength)
+{
+ RefPtr<BlobImpl> impl = aBlob.Impl();
+ ErrorResult rv;
+ aContentLength = impl->GetSize(rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return rv.StealNSResult();
+ }
+
+ impl->GetInternalStream(aStream, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return rv.StealNSResult();
+ }
+
+ nsAutoString type;
+ impl->GetType(type);
+ aContentType = NS_ConvertUTF16toUTF8(type);
+ return NS_OK;
+}
+
+nsresult
+ExtractFromFormData(FormData& aFormData,
+ nsIInputStream** aStream,
+ nsCString& aContentType,
+ uint64_t& aContentLength)
+{
+ nsAutoCString unusedCharset;
+ return aFormData.GetSendInfo(aStream, &aContentLength,
+ aContentType, unusedCharset);
+}
+
+nsresult
+ExtractFromUSVString(const nsString& aStr,
+ nsIInputStream** aStream,
+ nsCString& aContentType,
+ uint64_t& aContentLength)
+{
+ nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
+ if (!encoder) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ int32_t destBufferLen;
+ nsresult rv = encoder->GetMaxLength(aStr.get(), aStr.Length(), &destBufferLen);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCString encoded;
+ if (!encoded.SetCapacity(destBufferLen, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ char* destBuffer = encoded.BeginWriting();
+ int32_t srcLen = (int32_t) aStr.Length();
+ int32_t outLen = destBufferLen;
+ rv = encoder->Convert(aStr.get(), &srcLen, destBuffer, &outLen);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MOZ_ASSERT(outLen <= destBufferLen);
+ encoded.SetLength(outLen);
+
+ aContentType = NS_LITERAL_CSTRING("text/plain;charset=UTF-8");
+ aContentLength = outLen;
+
+ return NS_NewCStringInputStream(aStream, encoded);
+}
+
+nsresult
+ExtractFromURLSearchParams(const URLSearchParams& aParams,
+ nsIInputStream** aStream,
+ nsCString& aContentType,
+ uint64_t& aContentLength)
+{
+ nsAutoString serialized;
+ aParams.Stringify(serialized);
+ aContentType = NS_LITERAL_CSTRING("application/x-www-form-urlencoded;charset=UTF-8");
+ aContentLength = serialized.Length();
+ return NS_NewCStringInputStream(aStream, NS_ConvertUTF16toUTF8(serialized));
+}
+} // namespace
+
+nsresult
+ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
+ nsIInputStream** aStream,
+ nsCString& aContentType,
+ uint64_t& aContentLength)
+{
+ MOZ_ASSERT(aStream);
+
+ if (aBodyInit.IsArrayBuffer()) {
+ const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
+ return ExtractFromArrayBuffer(buf, aStream, aContentLength);
+ }
+ if (aBodyInit.IsArrayBufferView()) {
+ const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
+ return ExtractFromArrayBufferView(buf, aStream, aContentLength);
+ }
+ if (aBodyInit.IsBlob()) {
+ const Blob& blob = aBodyInit.GetAsBlob();
+ return ExtractFromBlob(blob, aStream, aContentType, aContentLength);
+ }
+ if (aBodyInit.IsFormData()) {
+ FormData& form = aBodyInit.GetAsFormData();
+ return ExtractFromFormData(form, aStream, aContentType, aContentLength);
+ }
+ if (aBodyInit.IsUSVString()) {
+ nsAutoString str;
+ str.Assign(aBodyInit.GetAsUSVString());
+ return ExtractFromUSVString(str, aStream, aContentType, aContentLength);
+ }
+ if (aBodyInit.IsURLSearchParams()) {
+ URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
+ return ExtractFromURLSearchParams(params, aStream, aContentType, aContentLength);
+ }
+
+ NS_NOTREACHED("Should never reach here");
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
+ nsIInputStream** aStream,
+ nsCString& aContentType,
+ uint64_t& aContentLength)
+{
+ MOZ_ASSERT(aStream);
+ MOZ_ASSERT(!*aStream);
+
+ if (aBodyInit.IsArrayBuffer()) {
+ const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
+ return ExtractFromArrayBuffer(buf, aStream, aContentLength);
+ }
+ if (aBodyInit.IsArrayBufferView()) {
+ const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
+ return ExtractFromArrayBufferView(buf, aStream, aContentLength);
+ }
+ if (aBodyInit.IsBlob()) {
+ const Blob& blob = aBodyInit.GetAsBlob();
+ return ExtractFromBlob(blob, aStream, aContentType, aContentLength);
+ }
+ if (aBodyInit.IsFormData()) {
+ FormData& form = aBodyInit.GetAsFormData();
+ return ExtractFromFormData(form, aStream, aContentType, aContentLength);
+ }
+ if (aBodyInit.IsUSVString()) {
+ nsAutoString str;
+ str.Assign(aBodyInit.GetAsUSVString());
+ return ExtractFromUSVString(str, aStream, aContentType, aContentLength);
+ }
+ if (aBodyInit.IsURLSearchParams()) {
+ URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
+ return ExtractFromURLSearchParams(params, aStream, aContentType, aContentLength);
+ }
+
+ NS_NOTREACHED("Should never reach here");
+ return NS_ERROR_FAILURE;
+}
+
+template <class Derived>
+FetchBody<Derived>::FetchBody()
+ : mWorkerPrivate(nullptr)
+ , mBodyUsed(false)
+{
+ if (!NS_IsMainThread()) {
+ mWorkerPrivate = GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(mWorkerPrivate);
+ }
+}
+
+template
+FetchBody<Request>::FetchBody();
+
+template
+FetchBody<Response>::FetchBody();
+
+template <class Derived>
+FetchBody<Derived>::~FetchBody()
+{
+}
+
+template <class Derived>
+already_AddRefed<Promise>
+FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
+{
+ if (BodyUsed()) {
+ aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
+ return nullptr;
+ }
+
+ SetBodyUsed();
+
+ RefPtr<Promise> promise =
+ FetchBodyConsumer<Derived>::Create(DerivedClass()->GetParentObject(),
+ this, aType, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ return promise.forget();
+}
+
+template
+already_AddRefed<Promise>
+FetchBody<Request>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
+
+template
+already_AddRefed<Promise>
+FetchBody<Response>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
+
+template <class Derived>
+void
+FetchBody<Derived>::SetMimeType()
+{
+ // Extract mime type.
+ ErrorResult result;
+ nsCString contentTypeValues;
+ MOZ_ASSERT(DerivedClass()->GetInternalHeaders());
+ DerivedClass()->GetInternalHeaders()->Get(NS_LITERAL_CSTRING("Content-Type"),
+ contentTypeValues, result);
+ MOZ_ALWAYS_TRUE(!result.Failed());
+
+ // HTTP ABNF states Content-Type may have only one value.
+ // This is from the "parse a header value" of the fetch spec.
+ if (!contentTypeValues.IsVoid() && contentTypeValues.Find(",") == -1) {
+ mMimeType = contentTypeValues;
+ ToLowerCase(mMimeType);
+ }
+}
+
+template
+void
+FetchBody<Request>::SetMimeType();
+
+template
+void
+FetchBody<Response>::SetMimeType();
+
+} // namespace dom
+} // namespace mozilla