2017-10-27 00:54:59 +03:00
|
|
|
|
/* -*- 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/. */
|
|
|
|
|
|
2015-09-11 21:26:33 +03:00
|
|
|
|
#include "FetchUtil.h"
|
2015-09-17 15:10:42 +03:00
|
|
|
|
|
2015-09-11 21:26:33 +03:00
|
|
|
|
#include "nsError.h"
|
|
|
|
|
#include "nsString.h"
|
2019-01-02 16:05:23 +03:00
|
|
|
|
#include "mozilla/dom/Document.h"
|
2015-09-11 21:26:33 +03:00
|
|
|
|
|
2016-11-14 10:15:32 +03:00
|
|
|
|
#include "mozilla/dom/InternalRequest.h"
|
2018-03-14 17:27:52 +03:00
|
|
|
|
#include "mozilla/dom/WorkerRef.h"
|
2015-09-17 15:10:42 +03:00
|
|
|
|
|
2015-09-11 21:26:33 +03:00
|
|
|
|
namespace mozilla {
|
|
|
|
|
namespace dom {
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
nsresult FetchUtil::GetValidRequestMethod(const nsACString& aMethod,
|
|
|
|
|
nsCString& outMethod) {
|
|
|
|
|
nsAutoCString upperCaseMethod(aMethod);
|
|
|
|
|
ToUpperCase(upperCaseMethod);
|
2016-03-17 10:29:42 +03:00
|
|
|
|
if (!NS_IsValidHTTPToken(aMethod)) {
|
|
|
|
|
outMethod.SetIsVoid(true);
|
|
|
|
|
return NS_ERROR_DOM_SYNTAX_ERR;
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-11 21:26:33 +03:00
|
|
|
|
if (upperCaseMethod.EqualsLiteral("CONNECT") ||
|
|
|
|
|
upperCaseMethod.EqualsLiteral("TRACE") ||
|
2016-03-17 10:29:42 +03:00
|
|
|
|
upperCaseMethod.EqualsLiteral("TRACK")) {
|
2015-09-11 21:26:33 +03:00
|
|
|
|
outMethod.SetIsVoid(true);
|
|
|
|
|
return NS_ERROR_DOM_SECURITY_ERR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (upperCaseMethod.EqualsLiteral("DELETE") ||
|
|
|
|
|
upperCaseMethod.EqualsLiteral("GET") ||
|
|
|
|
|
upperCaseMethod.EqualsLiteral("HEAD") ||
|
|
|
|
|
upperCaseMethod.EqualsLiteral("OPTIONS") ||
|
|
|
|
|
upperCaseMethod.EqualsLiteral("POST") ||
|
|
|
|
|
upperCaseMethod.EqualsLiteral("PUT")) {
|
|
|
|
|
outMethod = upperCaseMethod;
|
|
|
|
|
} else {
|
|
|
|
|
outMethod = aMethod; // Case unchanged for non-standard methods
|
|
|
|
|
}
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-25 00:42:35 +03:00
|
|
|
|
static bool FindCRLF(nsACString::const_iterator& aStart,
|
|
|
|
|
nsACString::const_iterator& aEnd) {
|
|
|
|
|
nsACString::const_iterator end(aEnd);
|
|
|
|
|
return FindInReadable(NS_LITERAL_CSTRING("\r\n"), aStart, end);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reads over a CRLF and positions start after it.
|
2016-09-30 05:33:57 +03:00
|
|
|
|
static bool PushOverLine(nsACString::const_iterator& aStart,
|
|
|
|
|
const nsACString::const_iterator& aEnd) {
|
|
|
|
|
if (*aStart == nsCRT::CR && (aEnd - aStart > 1) && *(++aStart) == nsCRT::LF) {
|
2015-10-25 00:42:35 +03:00
|
|
|
|
++aStart; // advance to after CRLF
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
bool FetchUtil::ExtractHeader(nsACString::const_iterator& aStart,
|
|
|
|
|
nsACString::const_iterator& aEnd,
|
|
|
|
|
nsCString& aHeaderName, nsCString& aHeaderValue,
|
|
|
|
|
bool* aWasEmptyHeader) {
|
|
|
|
|
MOZ_ASSERT(aWasEmptyHeader);
|
|
|
|
|
// Set it to a valid value here so we don't forget later.
|
|
|
|
|
*aWasEmptyHeader = false;
|
|
|
|
|
|
|
|
|
|
const char* beginning = aStart.get();
|
|
|
|
|
nsACString::const_iterator end(aEnd);
|
|
|
|
|
if (!FindCRLF(aStart, end)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (aStart.get() == beginning) {
|
|
|
|
|
*aWasEmptyHeader = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsAutoCString header(beginning, aStart.get() - beginning);
|
|
|
|
|
|
2016-09-30 05:33:57 +03:00
|
|
|
|
nsACString::const_iterator headerStart, iter, headerEnd;
|
2015-10-25 00:42:35 +03:00
|
|
|
|
header.BeginReading(headerStart);
|
|
|
|
|
header.EndReading(headerEnd);
|
2016-09-30 05:33:57 +03:00
|
|
|
|
iter = headerStart;
|
|
|
|
|
if (!FindCharInReadable(':', iter, headerEnd)) {
|
2015-10-25 00:42:35 +03:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-30 05:33:57 +03:00
|
|
|
|
aHeaderName.Assign(StringHead(header, iter - headerStart));
|
2015-10-25 00:42:35 +03:00
|
|
|
|
aHeaderName.CompressWhitespace();
|
|
|
|
|
if (!NS_IsValidHTTPToken(aHeaderName)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-30 05:33:57 +03:00
|
|
|
|
aHeaderValue.Assign(Substring(++iter, headerEnd));
|
2015-10-25 00:42:35 +03:00
|
|
|
|
if (!NS_IsReasonableHTTPHeaderValue(aHeaderValue)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
aHeaderValue.CompressWhitespace();
|
|
|
|
|
|
2016-09-30 05:33:57 +03:00
|
|
|
|
return PushOverLine(aStart, aEnd);
|
2015-10-25 00:42:35 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-14 10:15:32 +03:00
|
|
|
|
// static
|
2019-01-02 16:05:23 +03:00
|
|
|
|
nsresult FetchUtil::SetRequestReferrer(nsIPrincipal* aPrincipal, Document* aDoc,
|
2016-11-14 10:15:32 +03:00
|
|
|
|
nsIHttpChannel* aChannel,
|
|
|
|
|
InternalRequest* aRequest) {
|
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
2019-05-02 15:33:55 +03:00
|
|
|
|
nsresult rv = NS_OK;
|
2016-11-14 10:15:32 +03:00
|
|
|
|
nsAutoString referrer;
|
|
|
|
|
aRequest->GetReferrer(referrer);
|
|
|
|
|
|
2019-08-21 16:24:45 +03:00
|
|
|
|
ReferrerPolicy policy = aRequest->ReferrerPolicy_();
|
2019-05-02 15:33:55 +03:00
|
|
|
|
nsCOMPtr<nsIReferrerInfo> referrerInfo;
|
2016-11-14 10:15:32 +03:00
|
|
|
|
if (referrer.IsEmpty()) {
|
|
|
|
|
// This is the case request’s referrer is "no-referrer"
|
2019-08-21 16:24:45 +03:00
|
|
|
|
referrerInfo = new ReferrerInfo(nullptr, ReferrerPolicy::No_referrer);
|
2016-11-14 10:15:32 +03:00
|
|
|
|
} else if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
|
2019-06-13 23:37:34 +03:00
|
|
|
|
referrerInfo = ReferrerInfo::CreateForFetch(aPrincipal, aDoc);
|
|
|
|
|
// In the first step, we should use referrer info from requetInit
|
|
|
|
|
referrerInfo = static_cast<ReferrerInfo*>(referrerInfo.get())
|
|
|
|
|
->CloneWithNewPolicy(policy);
|
2016-11-14 10:15:32 +03:00
|
|
|
|
} else {
|
|
|
|
|
// From "Determine request's Referrer" step 3
|
|
|
|
|
// "If request's referrer is a URL, let referrerSource be request's
|
|
|
|
|
// referrer."
|
|
|
|
|
nsCOMPtr<nsIURI> referrerURI;
|
2019-07-15 16:39:51 +03:00
|
|
|
|
rv = NS_NewURI(getter_AddRefs(referrerURI), referrer);
|
2016-11-14 10:15:32 +03:00
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2019-05-02 15:33:55 +03:00
|
|
|
|
referrerInfo = new ReferrerInfo(referrerURI, policy);
|
2016-11-14 10:15:32 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-13 23:37:34 +03:00
|
|
|
|
rv = aChannel->SetReferrerInfoWithoutClone(referrerInfo);
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
2019-08-20 19:22:44 +03:00
|
|
|
|
nsAutoString computedReferrerSpec;
|
2019-05-02 15:33:55 +03:00
|
|
|
|
referrerInfo = aChannel->GetReferrerInfo();
|
|
|
|
|
if (referrerInfo) {
|
2019-08-20 19:22:44 +03:00
|
|
|
|
Unused << referrerInfo->GetComputedReferrerSpec(computedReferrerSpec);
|
2019-05-02 15:33:55 +03:00
|
|
|
|
}
|
2016-11-14 10:15:32 +03:00
|
|
|
|
|
|
|
|
|
// Step 8 https://fetch.spec.whatwg.org/#main-fetch
|
|
|
|
|
// If request’s referrer is not "no-referrer", set request’s referrer to
|
|
|
|
|
// the result of invoking determine request’s referrer.
|
2019-08-20 19:22:44 +03:00
|
|
|
|
aRequest->SetReferrer(computedReferrerSpec);
|
2016-11-14 10:15:32 +03:00
|
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-10 22:41:24 +03:00
|
|
|
|
class WindowStreamOwner final : public nsIObserver,
|
|
|
|
|
public nsSupportsWeakReference {
|
|
|
|
|
// Read from any thread but only set/cleared on the main thread. The lifecycle
|
|
|
|
|
// of WindowStreamOwner prevents concurrent read/clear.
|
|
|
|
|
nsCOMPtr<nsIAsyncInputStream> mStream;
|
|
|
|
|
|
|
|
|
|
nsCOMPtr<nsIGlobalObject> mGlobal;
|
|
|
|
|
|
|
|
|
|
~WindowStreamOwner() {
|
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
|
|
|
if (obs) {
|
|
|
|
|
obs->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
|
|
|
|
|
|
WindowStreamOwner(nsIAsyncInputStream* aStream, nsIGlobalObject* aGlobal)
|
|
|
|
|
: mStream(aStream), mGlobal(aGlobal) {
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mGlobal);
|
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static already_AddRefed<WindowStreamOwner> Create(
|
|
|
|
|
nsIAsyncInputStream* aStream, nsIGlobalObject* aGlobal) {
|
|
|
|
|
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
|
|
|
|
if (NS_WARN_IF(!os)) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RefPtr<WindowStreamOwner> self = new WindowStreamOwner(aStream, aGlobal);
|
|
|
|
|
|
|
|
|
|
// Holds nsIWeakReference to self.
|
|
|
|
|
nsresult rv = os->AddObserver(self, DOM_WINDOW_DESTROYED_TOPIC, true);
|
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return self.forget();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Destroyer final : Runnable {
|
|
|
|
|
RefPtr<WindowStreamOwner> mDoomed;
|
|
|
|
|
|
|
|
|
|
explicit Destroyer(already_AddRefed<WindowStreamOwner> aDoomed)
|
|
|
|
|
: Runnable("WindowStreamOwner::Destroyer"), mDoomed(aDoomed) {}
|
|
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
|
Run() override {
|
|
|
|
|
mDoomed = nullptr;
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// nsIObserver:
|
|
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
|
Observe(nsISupports* aSubject, const char* aTopic,
|
|
|
|
|
const char16_t* aData) override {
|
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0);
|
|
|
|
|
|
|
|
|
|
if (!mStream) {
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
|
|
|
|
|
if (!SameCOMIdentity(aSubject, window)) {
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// mStream->Close() will call JSStreamConsumer::OnInputStreamReady which may
|
|
|
|
|
// then destory itself, dropping the last reference to 'this'.
|
|
|
|
|
RefPtr<WindowStreamOwner> keepAlive(this);
|
|
|
|
|
|
|
|
|
|
mStream->Close();
|
|
|
|
|
mStream = nullptr;
|
|
|
|
|
mGlobal = nullptr;
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(WindowStreamOwner, nsIObserver, nsISupportsWeakReference)
|
|
|
|
|
|
2018-03-14 17:27:52 +03:00
|
|
|
|
class WorkerStreamOwner final {
|
2017-10-10 22:41:24 +03:00
|
|
|
|
public:
|
2018-03-14 17:27:52 +03:00
|
|
|
|
NS_INLINE_DECL_REFCOUNTING(WorkerStreamOwner)
|
|
|
|
|
|
2017-10-10 22:41:24 +03:00
|
|
|
|
explicit WorkerStreamOwner(nsIAsyncInputStream* aStream) : mStream(aStream) {}
|
|
|
|
|
|
2018-03-14 17:27:52 +03:00
|
|
|
|
static already_AddRefed<WorkerStreamOwner> Create(
|
2017-10-10 22:41:24 +03:00
|
|
|
|
nsIAsyncInputStream* aStream, WorkerPrivate* aWorker) {
|
2018-03-14 17:27:52 +03:00
|
|
|
|
RefPtr<WorkerStreamOwner> self = new WorkerStreamOwner(aStream);
|
|
|
|
|
|
|
|
|
|
self->mWorkerRef = WeakWorkerRef::Create(aWorker, [self]() {
|
|
|
|
|
if (self->mStream) {
|
|
|
|
|
// If this Close() calls JSStreamConsumer::OnInputStreamReady and drops
|
|
|
|
|
// the last reference to the JSStreamConsumer, 'this' will not be
|
|
|
|
|
// destroyed since ~JSStreamConsumer() only enqueues a Destroyer.
|
|
|
|
|
self->mStream->Close();
|
|
|
|
|
self->mStream = nullptr;
|
|
|
|
|
self->mWorkerRef = nullptr;
|
|
|
|
|
}
|
|
|
|
|
});
|
2017-10-10 22:41:24 +03:00
|
|
|
|
|
2018-03-14 17:27:52 +03:00
|
|
|
|
if (!self->mWorkerRef) {
|
2017-10-10 22:41:24 +03:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:27:52 +03:00
|
|
|
|
return self.forget();
|
2017-10-10 22:41:24 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Destroyer final : CancelableRunnable {
|
2018-03-14 17:27:52 +03:00
|
|
|
|
RefPtr<WorkerStreamOwner> mDoomed;
|
2017-10-10 22:41:24 +03:00
|
|
|
|
|
2018-03-14 17:27:52 +03:00
|
|
|
|
explicit Destroyer(already_AddRefed<WorkerStreamOwner>&& aDoomed)
|
2017-10-10 22:41:24 +03:00
|
|
|
|
: CancelableRunnable("WorkerStreamOwner::Destroyer"),
|
2018-05-30 22:15:35 +03:00
|
|
|
|
mDoomed(std::move(aDoomed)) {}
|
2017-10-10 22:41:24 +03:00
|
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
|
Run() override {
|
|
|
|
|
mDoomed = nullptr;
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsresult Cancel() override { return Run(); }
|
|
|
|
|
};
|
|
|
|
|
|
2018-03-14 17:27:52 +03:00
|
|
|
|
private:
|
|
|
|
|
~WorkerStreamOwner() = default;
|
2017-10-10 22:41:24 +03:00
|
|
|
|
|
2018-03-14 17:27:52 +03:00
|
|
|
|
// Read from any thread but only set/cleared on the worker thread. The
|
|
|
|
|
// lifecycle of WorkerStreamOwner prevents concurrent read/clear.
|
|
|
|
|
nsCOMPtr<nsIAsyncInputStream> mStream;
|
|
|
|
|
RefPtr<WeakWorkerRef> mWorkerRef;
|
2017-10-10 22:41:24 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class JSStreamConsumer final : public nsIInputStreamCallback {
|
|
|
|
|
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
|
|
|
|
RefPtr<WindowStreamOwner> mWindowStreamOwner;
|
2018-03-14 17:27:52 +03:00
|
|
|
|
RefPtr<WorkerStreamOwner> mWorkerStreamOwner;
|
2017-10-10 22:41:24 +03:00
|
|
|
|
JS::StreamConsumer* mConsumer;
|
|
|
|
|
bool mConsumerAborted;
|
|
|
|
|
|
|
|
|
|
JSStreamConsumer(already_AddRefed<WindowStreamOwner> aWindowStreamOwner,
|
|
|
|
|
nsIGlobalObject* aGlobal, JS::StreamConsumer* aConsumer)
|
|
|
|
|
: mOwningEventTarget(aGlobal->EventTargetFor(TaskCategory::Other)),
|
|
|
|
|
mWindowStreamOwner(aWindowStreamOwner),
|
|
|
|
|
mConsumer(aConsumer),
|
|
|
|
|
mConsumerAborted(false) {
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mWindowStreamOwner);
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mConsumer);
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:27:52 +03:00
|
|
|
|
JSStreamConsumer(RefPtr<WorkerStreamOwner> aWorkerStreamOwner,
|
2017-10-10 22:41:24 +03:00
|
|
|
|
nsIGlobalObject* aGlobal, JS::StreamConsumer* aConsumer)
|
|
|
|
|
: mOwningEventTarget(aGlobal->EventTargetFor(TaskCategory::Other)),
|
2018-05-30 22:15:35 +03:00
|
|
|
|
mWorkerStreamOwner(std::move(aWorkerStreamOwner)),
|
2017-10-10 22:41:24 +03:00
|
|
|
|
mConsumer(aConsumer),
|
|
|
|
|
mConsumerAborted(false) {
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mWorkerStreamOwner);
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mConsumer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~JSStreamConsumer() {
|
|
|
|
|
// Both WindowStreamOwner and WorkerStreamOwner need to be destroyed on
|
|
|
|
|
// their global's event target thread.
|
|
|
|
|
|
|
|
|
|
RefPtr<Runnable> destroyer;
|
|
|
|
|
if (mWindowStreamOwner) {
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mWorkerStreamOwner);
|
|
|
|
|
destroyer = new WindowStreamOwner::Destroyer(mWindowStreamOwner.forget());
|
|
|
|
|
} else {
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mWorkerStreamOwner);
|
2018-03-14 17:27:52 +03:00
|
|
|
|
destroyer = new WorkerStreamOwner::Destroyer(mWorkerStreamOwner.forget());
|
2017-10-10 22:41:24 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(destroyer.forget()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static nsresult WriteSegment(nsIInputStream* aStream, void* aClosure,
|
|
|
|
|
const char* aFromSegment, uint32_t aToOffset,
|
|
|
|
|
uint32_t aCount, uint32_t* aWriteCount) {
|
|
|
|
|
JSStreamConsumer* self = reinterpret_cast<JSStreamConsumer*>(aClosure);
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!self->mConsumerAborted);
|
|
|
|
|
|
|
|
|
|
// This callback can be called on any thread which is explicitly allowed by
|
|
|
|
|
// this particular JS API call.
|
|
|
|
|
if (!self->mConsumer->consumeChunk((const uint8_t*)aFromSegment, aCount)) {
|
|
|
|
|
self->mConsumerAborted = true;
|
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*aWriteCount = aCount;
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
|
|
2019-03-25 22:39:17 +03:00
|
|
|
|
static bool Start(nsCOMPtr<nsIInputStream>&& aStream,
|
|
|
|
|
JS::StreamConsumer* aConsumer, nsIGlobalObject* aGlobal,
|
|
|
|
|
WorkerPrivate* aMaybeWorker) {
|
|
|
|
|
nsCOMPtr<nsIAsyncInputStream> asyncStream;
|
|
|
|
|
nsresult rv = NS_MakeAsyncNonBlockingInputStream(
|
|
|
|
|
aStream.forget(), getter_AddRefs(asyncStream));
|
2017-10-10 22:41:24 +03:00
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RefPtr<JSStreamConsumer> consumer;
|
|
|
|
|
if (aMaybeWorker) {
|
2018-03-14 17:27:52 +03:00
|
|
|
|
RefPtr<WorkerStreamOwner> owner =
|
2017-10-10 22:41:24 +03:00
|
|
|
|
WorkerStreamOwner::Create(asyncStream, aMaybeWorker);
|
|
|
|
|
if (!owner) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-30 22:15:35 +03:00
|
|
|
|
consumer = new JSStreamConsumer(std::move(owner), aGlobal, aConsumer);
|
2017-10-10 22:41:24 +03:00
|
|
|
|
} else {
|
|
|
|
|
RefPtr<WindowStreamOwner> owner =
|
|
|
|
|
WindowStreamOwner::Create(asyncStream, aGlobal);
|
|
|
|
|
if (!owner) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
consumer = new JSStreamConsumer(owner.forget(), aGlobal, aConsumer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This AsyncWait() creates a ref-cycle between asyncStream and consumer:
|
|
|
|
|
//
|
|
|
|
|
// asyncStream -> consumer -> (Window|Worker)StreamOwner -> asyncStream
|
|
|
|
|
//
|
|
|
|
|
// The cycle is broken when the stream completes or errors out and
|
|
|
|
|
// asyncStream drops its reference to consumer.
|
|
|
|
|
return NS_SUCCEEDED(asyncStream->AsyncWait(consumer, 0, 0, nullptr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// nsIInputStreamCallback:
|
|
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
|
OnInputStreamReady(nsIAsyncInputStream* aStream) override {
|
|
|
|
|
// Can be called on any stream. The JS API calls made below explicitly
|
|
|
|
|
// support being called from any thread.
|
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mConsumerAborted);
|
|
|
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
|
|
uint64_t available = 0;
|
|
|
|
|
rv = aStream->Available(&available);
|
|
|
|
|
if (NS_SUCCEEDED(rv) && available == 0) {
|
|
|
|
|
rv = NS_BASE_STREAM_CLOSED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rv == NS_BASE_STREAM_CLOSED) {
|
2018-11-16 19:32:20 +03:00
|
|
|
|
mConsumer->streamEnd();
|
2017-10-10 22:41:24 +03:00
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NS_FAILED(rv)) {
|
2018-11-16 19:32:20 +03:00
|
|
|
|
mConsumer->streamError(size_t(rv));
|
2017-10-10 22:41:24 +03:00
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-16 19:32:20 +03:00
|
|
|
|
// Check mConsumerAborted before NS_FAILED to avoid calling streamError()
|
2017-10-10 22:41:24 +03:00
|
|
|
|
// if consumeChunk() returned false per JS API contract.
|
|
|
|
|
uint32_t written = 0;
|
|
|
|
|
rv = aStream->ReadSegments(WriteSegment, this, available, &written);
|
|
|
|
|
if (mConsumerAborted) {
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
2018-11-16 19:32:20 +03:00
|
|
|
|
mConsumer->streamError(size_t(rv));
|
2017-10-10 22:41:24 +03:00
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rv = aStream->AsyncWait(this, 0, 0, nullptr);
|
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
2018-11-16 19:32:20 +03:00
|
|
|
|
mConsumer->streamError(size_t(rv));
|
2017-10-10 22:41:24 +03:00
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(JSStreamConsumer, nsIInputStreamCallback)
|
|
|
|
|
|
|
|
|
|
static bool ThrowException(JSContext* aCx, unsigned errorNumber) {
|
|
|
|
|
JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr, errorNumber);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
bool FetchUtil::StreamResponseToJS(JSContext* aCx, JS::HandleObject aObj,
|
|
|
|
|
JS::MimeType aMimeType,
|
|
|
|
|
JS::StreamConsumer* aConsumer,
|
2018-01-31 10:24:08 +03:00
|
|
|
|
WorkerPrivate* aMaybeWorker) {
|
2017-10-10 22:41:24 +03:00
|
|
|
|
MOZ_ASSERT(!aMaybeWorker == NS_IsMainThread());
|
|
|
|
|
|
|
|
|
|
RefPtr<Response> response;
|
|
|
|
|
nsresult rv = UNWRAP_OBJECT(Response, aObj, response);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
return ThrowException(aCx, JSMSG_BAD_RESPONSE_VALUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char* requiredMimeType = nullptr;
|
|
|
|
|
switch (aMimeType) {
|
|
|
|
|
case JS::MimeType::Wasm:
|
2019-02-20 13:05:35 +03:00
|
|
|
|
requiredMimeType = WASM_CONTENT_TYPE;
|
2017-10-10 22:41:24 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(requiredMimeType, response->MimeType().Data())) {
|
|
|
|
|
return ThrowException(aCx, JSMSG_BAD_RESPONSE_MIME_TYPE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response->Type() != ResponseType::Basic &&
|
|
|
|
|
response->Type() != ResponseType::Cors &&
|
|
|
|
|
response->Type() != ResponseType::Default) {
|
|
|
|
|
return ThrowException(aCx, JSMSG_BAD_RESPONSE_CORS_SAME_ORIGIN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!response->Ok()) {
|
|
|
|
|
return ThrowException(aCx, JSMSG_BAD_RESPONSE_STATUS);
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-11 22:18:43 +03:00
|
|
|
|
IgnoredErrorResult result;
|
|
|
|
|
bool used = response->GetBodyUsed(result);
|
|
|
|
|
if (NS_WARN_IF(result.Failed())) {
|
|
|
|
|
return ThrowException(aCx, JSMSG_ERROR_CONSUMING_RESPONSE);
|
|
|
|
|
}
|
|
|
|
|
if (used) {
|
2017-10-10 22:41:24 +03:00
|
|
|
|
return ThrowException(aCx, JSMSG_RESPONSE_ALREADY_CONSUMED);
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 23:55:30 +03:00
|
|
|
|
switch (aMimeType) {
|
|
|
|
|
case JS::MimeType::Wasm:
|
|
|
|
|
nsAutoString url;
|
|
|
|
|
response->GetUrl(url);
|
|
|
|
|
|
|
|
|
|
nsCString sourceMapUrl;
|
|
|
|
|
response->GetInternalHeaders()->Get(NS_LITERAL_CSTRING("SourceMap"),
|
|
|
|
|
sourceMapUrl, result);
|
|
|
|
|
if (NS_WARN_IF(result.Failed())) {
|
|
|
|
|
return ThrowException(aCx, JSMSG_ERROR_CONSUMING_RESPONSE);
|
|
|
|
|
}
|
|
|
|
|
NS_ConvertUTF16toUTF8 urlUTF8(url);
|
|
|
|
|
aConsumer->noteResponseURLs(
|
|
|
|
|
urlUTF8.get(), sourceMapUrl.IsVoid() ? nullptr : sourceMapUrl.get());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-10 22:41:24 +03:00
|
|
|
|
RefPtr<InternalResponse> ir = response->GetInternalResponse();
|
|
|
|
|
if (NS_WARN_IF(!ir)) {
|
|
|
|
|
return ThrowException(aCx, JSMSG_OUT_OF_MEMORY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsCOMPtr<nsIInputStream> body;
|
|
|
|
|
ir->GetUnfilteredBody(getter_AddRefs(body));
|
|
|
|
|
if (!body) {
|
2018-11-16 19:32:20 +03:00
|
|
|
|
aConsumer->streamEnd();
|
2017-10-10 22:41:24 +03:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IgnoredErrorResult error;
|
|
|
|
|
response->SetBodyUsed(aCx, error);
|
|
|
|
|
if (NS_WARN_IF(error.Failed())) {
|
|
|
|
|
return ThrowException(aCx, JSMSG_ERROR_CONSUMING_RESPONSE);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-26 11:52:01 +03:00
|
|
|
|
nsIGlobalObject* global = xpc::NativeGlobal(js::UncheckedUnwrap(aObj));
|
2017-10-10 22:41:24 +03:00
|
|
|
|
|
2019-03-25 22:39:17 +03:00
|
|
|
|
if (!JSStreamConsumer::Start(std::move(body), aConsumer, global,
|
|
|
|
|
aMaybeWorker)) {
|
2017-10-10 22:41:24 +03:00
|
|
|
|
return ThrowException(aCx, JSMSG_OUT_OF_MEMORY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-16 19:32:20 +03:00
|
|
|
|
// static
|
|
|
|
|
void FetchUtil::ReportJSStreamError(JSContext* aCx, size_t aErrorCode) {
|
|
|
|
|
// For now, convert *all* errors into AbortError.
|
|
|
|
|
|
|
|
|
|
RefPtr<DOMException> e = DOMException::Create(NS_ERROR_DOM_ABORT_ERR);
|
|
|
|
|
|
|
|
|
|
JS::Rooted<JS::Value> value(aCx);
|
|
|
|
|
if (!GetOrCreateDOMReflector(aCx, e, &value)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JS_SetPendingException(aCx, value);
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-11 21:26:33 +03:00
|
|
|
|
} // namespace dom
|
|
|
|
|
} // namespace mozilla
|