mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-30 20:37:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
124
dom/fetch/ChannelInfo.cpp
Normal file
124
dom/fetch/ChannelInfo.cpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/* -*- 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 "mozilla/dom/ChannelInfo.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsSerializationHelper.h"
|
||||
#include "mozilla/net/HttpBaseChannel.h"
|
||||
#include "mozilla/ipc/ChannelInfo.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
void
|
||||
ChannelInfo::InitFromDocument(nsIDocument* aDoc)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
|
||||
|
||||
nsCOMPtr<nsISupports> securityInfo = aDoc->GetSecurityInfo();
|
||||
if (securityInfo) {
|
||||
SetSecurityInfo(securityInfo);
|
||||
}
|
||||
|
||||
mInited = true;
|
||||
}
|
||||
|
||||
void
|
||||
ChannelInfo::InitFromChannel(nsIChannel* aChannel)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
|
||||
|
||||
nsCOMPtr<nsISupports> securityInfo;
|
||||
aChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
|
||||
if (securityInfo) {
|
||||
SetSecurityInfo(securityInfo);
|
||||
}
|
||||
|
||||
mInited = true;
|
||||
}
|
||||
|
||||
void
|
||||
ChannelInfo::InitFromChromeGlobal(nsIGlobalObject* aGlobal)
|
||||
{
|
||||
MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
|
||||
MOZ_ASSERT(aGlobal);
|
||||
|
||||
MOZ_RELEASE_ASSERT(
|
||||
nsContentUtils::IsSystemPrincipal(aGlobal->PrincipalOrNull()));
|
||||
|
||||
mSecurityInfo.Truncate();
|
||||
mInited = true;
|
||||
}
|
||||
|
||||
void
|
||||
ChannelInfo::InitFromIPCChannelInfo(const mozilla::ipc::IPCChannelInfo& aChannelInfo)
|
||||
{
|
||||
MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
|
||||
|
||||
mSecurityInfo = aChannelInfo.securityInfo();
|
||||
|
||||
mInited = true;
|
||||
}
|
||||
|
||||
void
|
||||
ChannelInfo::SetSecurityInfo(nsISupports* aSecurityInfo)
|
||||
{
|
||||
MOZ_ASSERT(mSecurityInfo.IsEmpty(), "security info should only be set once");
|
||||
nsCOMPtr<nsISerializable> serializable = do_QueryInterface(aSecurityInfo);
|
||||
if (!serializable) {
|
||||
NS_WARNING("A non-serializable object was passed to InternalResponse::SetSecurityInfo");
|
||||
return;
|
||||
}
|
||||
NS_SerializeToString(serializable, mSecurityInfo);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ChannelInfo::ResurrectInfoOnChannel(nsIChannel* aChannel)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mInited);
|
||||
|
||||
if (!mSecurityInfo.IsEmpty()) {
|
||||
nsCOMPtr<nsISupports> infoObj;
|
||||
nsresult rv = NS_DeserializeObject(mSecurityInfo, getter_AddRefs(infoObj));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel =
|
||||
do_QueryInterface(aChannel);
|
||||
MOZ_ASSERT(httpChannel);
|
||||
net::HttpBaseChannel* httpBaseChannel =
|
||||
static_cast<net::HttpBaseChannel*>(httpChannel.get());
|
||||
rv = httpBaseChannel->OverrideSecurityInfo(infoObj);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCChannelInfo
|
||||
ChannelInfo::AsIPCChannelInfo() const
|
||||
{
|
||||
// This may be called when mInited is false, for example if we try to store
|
||||
// a synthesized Response object into the Cache. Uninitialized and empty
|
||||
// ChannelInfo objects are indistinguishable at the IPC level, so this is
|
||||
// fine.
|
||||
|
||||
IPCChannelInfo ipcInfo;
|
||||
|
||||
ipcInfo.securityInfo() = mSecurityInfo;
|
||||
|
||||
return ipcInfo;
|
||||
}
|
||||
93
dom/fetch/ChannelInfo.h
Normal file
93
dom/fetch/ChannelInfo.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_ChannelInfo_h
|
||||
#define mozilla_dom_ChannelInfo_h
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsIChannel;
|
||||
class nsIDocument;
|
||||
class nsIGlobalObject;
|
||||
class nsIURI;
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
class IPCChannelInfo;
|
||||
} // namespace ipc
|
||||
|
||||
namespace dom {
|
||||
|
||||
// This class represents the information related to a Response that we
|
||||
// retrieve from the corresponding channel that is used to perform the fetch.
|
||||
//
|
||||
// When adding new members to this object, the following code needs to be
|
||||
// updated:
|
||||
// * IPCChannelInfo
|
||||
// * InitFromChannel and InitFromIPCChannelInfo members
|
||||
// * ResurrectInfoOnChannel member
|
||||
// * AsIPCChannelInfo member
|
||||
// * constructors and assignment operators for this class.
|
||||
// * DOM Cache schema code (in dom/cache/DBSchema.cpp) to ensure that the newly
|
||||
// added member is saved into the DB and loaded from it properly.
|
||||
//
|
||||
// Care must be taken when initializing this object, or when calling
|
||||
// ResurrectInfoOnChannel(). This object cannot be initialized twice, and
|
||||
// ResurrectInfoOnChannel() cannot be called on it before it has been
|
||||
// initialized. There are assertions ensuring these invariants.
|
||||
class ChannelInfo final
|
||||
{
|
||||
public:
|
||||
typedef mozilla::ipc::IPCChannelInfo IPCChannelInfo;
|
||||
|
||||
ChannelInfo()
|
||||
: mInited(false)
|
||||
{
|
||||
}
|
||||
|
||||
ChannelInfo(const ChannelInfo& aRHS)
|
||||
: mSecurityInfo(aRHS.mSecurityInfo)
|
||||
, mInited(aRHS.mInited)
|
||||
{
|
||||
}
|
||||
|
||||
ChannelInfo&
|
||||
operator=(const ChannelInfo& aRHS)
|
||||
{
|
||||
mSecurityInfo = aRHS.mSecurityInfo;
|
||||
mInited = aRHS.mInited;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void InitFromDocument(nsIDocument* aDoc);
|
||||
void InitFromChannel(nsIChannel* aChannel);
|
||||
void InitFromChromeGlobal(nsIGlobalObject* aGlobal);
|
||||
void InitFromIPCChannelInfo(const IPCChannelInfo& aChannelInfo);
|
||||
|
||||
// This restores every possible information stored from a previous channel
|
||||
// object on a new one.
|
||||
nsresult ResurrectInfoOnChannel(nsIChannel* aChannel);
|
||||
|
||||
bool IsInitialized() const
|
||||
{
|
||||
return mInited;
|
||||
}
|
||||
|
||||
IPCChannelInfo AsIPCChannelInfo() const;
|
||||
|
||||
private:
|
||||
void SetSecurityInfo(nsISupports* aSecurityInfo);
|
||||
|
||||
private:
|
||||
nsCString mSecurityInfo;
|
||||
bool mInited;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_ChannelInfo_h
|
||||
14
dom/fetch/ChannelInfo.ipdlh
Normal file
14
dom/fetch/ChannelInfo.ipdlh
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* 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/. */
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
struct IPCChannelInfo
|
||||
{
|
||||
nsCString securityInfo;
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
759
dom/fetch/Fetch.cpp
Normal file
759
dom/fetch/Fetch.cpp
Normal file
|
|
@ -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
|
||||
205
dom/fetch/Fetch.h
Normal file
205
dom/fetch/Fetch.h
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_Fetch_h
|
||||
#define mozilla_dom_Fetch_h
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIStreamLoader.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsError.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/RequestBinding.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
|
||||
class BlobImpl;
|
||||
class InternalRequest;
|
||||
class OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
|
||||
class RequestOrUSVString;
|
||||
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
} // namespace workers
|
||||
|
||||
already_AddRefed<Promise>
|
||||
FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
|
||||
const RequestInit& aInit, ErrorResult& aRv);
|
||||
|
||||
nsresult
|
||||
UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest);
|
||||
|
||||
/*
|
||||
* Creates an nsIInputStream based on the fetch specifications 'extract a byte
|
||||
* stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
|
||||
* Stores content type in out param aContentType.
|
||||
*/
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType,
|
||||
uint64_t& aContentLength);
|
||||
|
||||
/*
|
||||
* Non-owning version.
|
||||
*/
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType,
|
||||
uint64_t& aContentLength);
|
||||
|
||||
template <class Derived> class FetchBodyConsumer;
|
||||
|
||||
enum FetchConsumeType
|
||||
{
|
||||
CONSUME_ARRAYBUFFER,
|
||||
CONSUME_BLOB,
|
||||
CONSUME_FORMDATA,
|
||||
CONSUME_JSON,
|
||||
CONSUME_TEXT,
|
||||
};
|
||||
|
||||
/*
|
||||
* FetchBody's body consumption uses nsIInputStreamPump to read from the
|
||||
* underlying stream to a block of memory, which is then adopted by
|
||||
* ContinueConsumeBody() and converted to the right type based on the JS
|
||||
* function called.
|
||||
*
|
||||
* Use of the nsIInputStreamPump complicates things on the worker thread.
|
||||
* The solution used here is similar to WebSockets.
|
||||
* The difference is that we are only interested in completion and not data
|
||||
* events, and nsIInputStreamPump can only deliver completion on the main thread.
|
||||
*
|
||||
* Before starting the pump on the main thread, we addref the FetchBody to keep
|
||||
* it alive. Then we add a feature, to track the status of the worker.
|
||||
*
|
||||
* ContinueConsumeBody() is the function that cleans things up in both success
|
||||
* and error conditions and so all callers call it with the appropriate status.
|
||||
*
|
||||
* Once the read is initiated on the main thread there are two possibilities.
|
||||
*
|
||||
* 1) Pump finishes before worker has finished Running.
|
||||
* In this case we adopt the data and dispatch a runnable to the worker,
|
||||
* which derefs FetchBody and removes the feature and resolves the Promise.
|
||||
*
|
||||
* 2) Pump still working while worker has stopped Running.
|
||||
* The feature is Notify()ed and ContinueConsumeBody() is called with
|
||||
* NS_BINDING_ABORTED. We first Cancel() the pump using a sync runnable to
|
||||
* ensure that mFetchBody remains alive (since mConsumeBodyPump is strongly
|
||||
* held by it) until pump->Cancel() is called. OnStreamComplete() will not
|
||||
* do anything if the error code is NS_BINDING_ABORTED, so we don't have to
|
||||
* worry about keeping anything alive.
|
||||
*
|
||||
* The pump is always released on the main thread.
|
||||
*/
|
||||
template <class Derived>
|
||||
class FetchBody
|
||||
{
|
||||
public:
|
||||
friend class FetchBodyConsumer<Derived>;
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
|
||||
|
||||
bool
|
||||
BodyUsed() const { return mBodyUsed; }
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ArrayBuffer(ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_ARRAYBUFFER, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Blob(ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_BLOB, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
FormData(ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_FORMDATA, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Json(ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_JSON, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Text(ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_TEXT, aRv);
|
||||
}
|
||||
|
||||
// Utility public methods accessed by various runnables.
|
||||
|
||||
void
|
||||
SetBodyUsed()
|
||||
{
|
||||
mBodyUsed = true;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
MimeType() const
|
||||
{
|
||||
return mMimeType;
|
||||
}
|
||||
|
||||
protected:
|
||||
FetchBody();
|
||||
|
||||
// Always set whenever the FetchBody is created on the worker thread.
|
||||
workers::WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
virtual ~FetchBody();
|
||||
|
||||
void
|
||||
SetMimeType();
|
||||
private:
|
||||
Derived*
|
||||
DerivedClass() const
|
||||
{
|
||||
return static_cast<Derived*>(const_cast<FetchBody*>(this));
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
IsOnTargetThread()
|
||||
{
|
||||
return NS_IsMainThread() == !mWorkerPrivate;
|
||||
}
|
||||
|
||||
void
|
||||
AssertIsOnTargetThread()
|
||||
{
|
||||
MOZ_ASSERT(IsOnTargetThread());
|
||||
}
|
||||
|
||||
// Only ever set once, always on target thread.
|
||||
bool mBodyUsed;
|
||||
nsCString mMimeType;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_Fetch_h
|
||||
705
dom/fetch/FetchConsumer.cpp
Normal file
705
dom/fetch/FetchConsumer.cpp
Normal file
|
|
@ -0,0 +1,705 @@
|
|||
/* -*- 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 "nsIInputStreamPump.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "WorkerScope.h"
|
||||
#include "Workers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
using namespace workers;
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Derived>
|
||||
class FetchBodyWorkerHolder final : public workers::WorkerHolder
|
||||
{
|
||||
RefPtr<FetchBodyConsumer<Derived>> mConsumer;
|
||||
bool mWasNotified;
|
||||
|
||||
public:
|
||||
explicit FetchBodyWorkerHolder(FetchBodyConsumer<Derived>* aConsumer)
|
||||
: mConsumer(aConsumer)
|
||||
, mWasNotified(false)
|
||||
{
|
||||
MOZ_ASSERT(aConsumer);
|
||||
}
|
||||
|
||||
~FetchBodyWorkerHolder() = default;
|
||||
|
||||
bool Notify(workers::Status aStatus) override
|
||||
{
|
||||
MOZ_ASSERT(aStatus > workers::Running);
|
||||
if (!mWasNotified) {
|
||||
mWasNotified = true;
|
||||
mConsumer->ShutDownMainThreadConsuming();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
class BeginConsumeBodyRunnable final : public Runnable
|
||||
{
|
||||
RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
|
||||
|
||||
public:
|
||||
explicit BeginConsumeBodyRunnable(FetchBodyConsumer<Derived>* aConsumer)
|
||||
: mFetchBodyConsumer(aConsumer)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
mFetchBodyConsumer->BeginConsumeBodyMainThread();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Called on successfully reading the complete stream.
|
||||
*/
|
||||
template <class Derived>
|
||||
class ContinueConsumeBodyRunnable final : public MainThreadWorkerRunnable
|
||||
{
|
||||
RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
|
||||
nsresult mStatus;
|
||||
uint32_t mLength;
|
||||
uint8_t* mResult;
|
||||
|
||||
public:
|
||||
ContinueConsumeBodyRunnable(FetchBodyConsumer<Derived>* aFetchBodyConsumer,
|
||||
nsresult aStatus, uint32_t aLength,
|
||||
uint8_t* aResult)
|
||||
: MainThreadWorkerRunnable(aFetchBodyConsumer->GetWorkerPrivate())
|
||||
, mFetchBodyConsumer(aFetchBodyConsumer)
|
||||
, mStatus(aStatus)
|
||||
, mLength(aLength)
|
||||
, mResult(aResult)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
mFetchBodyConsumer->ContinueConsumeBody(mStatus, mLength, mResult);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
class FailConsumeBodyWorkerRunnable : public MainThreadWorkerControlRunnable
|
||||
{
|
||||
RefPtr<FetchBodyConsumer<Derived>> mBodyConsumer;
|
||||
|
||||
public:
|
||||
explicit FailConsumeBodyWorkerRunnable(FetchBodyConsumer<Derived>* aBodyConsumer)
|
||||
: MainThreadWorkerControlRunnable(aBodyConsumer->GetWorkerPrivate())
|
||||
, mBodyConsumer(aBodyConsumer)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
mBodyConsumer->ContinueConsumeBody(NS_ERROR_FAILURE, 0, nullptr);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* In case of failure to create a stream pump or dispatch stream completion to
|
||||
* worker, ensure we cleanup properly. Thread agnostic.
|
||||
*/
|
||||
template <class Derived>
|
||||
class MOZ_STACK_CLASS AutoFailConsumeBody final
|
||||
{
|
||||
RefPtr<FetchBodyConsumer<Derived>> mBodyConsumer;
|
||||
|
||||
public:
|
||||
explicit AutoFailConsumeBody(FetchBodyConsumer<Derived>* aBodyConsumer)
|
||||
: mBodyConsumer(aBodyConsumer)
|
||||
{}
|
||||
|
||||
~AutoFailConsumeBody()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (mBodyConsumer) {
|
||||
if (mBodyConsumer->GetWorkerPrivate()) {
|
||||
RefPtr<FailConsumeBodyWorkerRunnable<Derived>> r =
|
||||
new FailConsumeBodyWorkerRunnable<Derived>(mBodyConsumer);
|
||||
if (!r->Dispatch()) {
|
||||
MOZ_CRASH("We are going to leak");
|
||||
}
|
||||
} else {
|
||||
mBodyConsumer->ContinueConsumeBody(NS_ERROR_FAILURE, 0, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DontFail()
|
||||
{
|
||||
mBodyConsumer = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Called on successfully reading the complete stream for Blob.
|
||||
*/
|
||||
template <class Derived>
|
||||
class ContinueConsumeBlobBodyRunnable final : public MainThreadWorkerRunnable
|
||||
{
|
||||
RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
|
||||
RefPtr<BlobImpl> mBlobImpl;
|
||||
|
||||
public:
|
||||
ContinueConsumeBlobBodyRunnable(FetchBodyConsumer<Derived>* aFetchBodyConsumer,
|
||||
BlobImpl* aBlobImpl)
|
||||
: MainThreadWorkerRunnable(aFetchBodyConsumer->GetWorkerPrivate())
|
||||
, mFetchBodyConsumer(aFetchBodyConsumer)
|
||||
, mBlobImpl(aBlobImpl)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mBlobImpl);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
mFetchBodyConsumer->ContinueConsumeBlobBody(mBlobImpl);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
class ConsumeBodyDoneObserver : public nsIStreamLoaderObserver
|
||||
, public MutableBlobStorageCallback
|
||||
{
|
||||
RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
explicit ConsumeBodyDoneObserver(FetchBodyConsumer<Derived>* aFetchBodyConsumer)
|
||||
: mFetchBodyConsumer(aFetchBodyConsumer)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
OnStreamComplete(nsIStreamLoader* aLoader,
|
||||
nsISupports* aCtxt,
|
||||
nsresult aStatus,
|
||||
uint32_t aResultLength,
|
||||
const uint8_t* aResult) override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// The loading is completed. Let's nullify the pump before continuing the
|
||||
// consuming of the body.
|
||||
mFetchBodyConsumer->NullifyConsumeBodyPump();
|
||||
|
||||
uint8_t* nonconstResult = const_cast<uint8_t*>(aResult);
|
||||
if (mFetchBodyConsumer->GetWorkerPrivate()) {
|
||||
RefPtr<ContinueConsumeBodyRunnable<Derived>> r =
|
||||
new ContinueConsumeBodyRunnable<Derived>(mFetchBodyConsumer,
|
||||
aStatus,
|
||||
aResultLength,
|
||||
nonconstResult);
|
||||
if (!r->Dispatch()) {
|
||||
NS_WARNING("Could not dispatch ConsumeBodyRunnable");
|
||||
// Return failure so that aResult is freed.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else {
|
||||
mFetchBodyConsumer->ContinueConsumeBody(aStatus, aResultLength,
|
||||
nonconstResult);
|
||||
}
|
||||
|
||||
// FetchBody is responsible for data.
|
||||
return NS_SUCCESS_ADOPTED_DATA;
|
||||
}
|
||||
|
||||
virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
|
||||
Blob* aBlob,
|
||||
nsresult aRv) override
|
||||
{
|
||||
// On error.
|
||||
if (NS_FAILED(aRv)) {
|
||||
OnStreamComplete(nullptr, nullptr, aRv, 0, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// The loading is completed. Let's nullify the pump before continuing the
|
||||
// consuming of the body.
|
||||
mFetchBodyConsumer->NullifyConsumeBodyPump();
|
||||
|
||||
MOZ_ASSERT(aBlob);
|
||||
|
||||
if (mFetchBodyConsumer->GetWorkerPrivate()) {
|
||||
RefPtr<ContinueConsumeBlobBodyRunnable<Derived>> r =
|
||||
new ContinueConsumeBlobBodyRunnable<Derived>(mFetchBodyConsumer,
|
||||
aBlob->Impl());
|
||||
|
||||
if (!r->Dispatch()) {
|
||||
NS_WARNING("Could not dispatch ConsumeBlobBodyRunnable");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mFetchBodyConsumer->ContinueConsumeBlobBody(aBlob->Impl());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~ConsumeBodyDoneObserver()
|
||||
{ }
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_ADDREF(ConsumeBodyDoneObserver<Derived>)
|
||||
template <class Derived>
|
||||
NS_IMPL_RELEASE(ConsumeBodyDoneObserver<Derived>)
|
||||
template <class Derived>
|
||||
NS_INTERFACE_MAP_BEGIN(ConsumeBodyDoneObserver<Derived>)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamLoaderObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
} // anonymous
|
||||
|
||||
template <class Derived>
|
||||
/* static */ already_AddRefed<Promise>
|
||||
FetchBodyConsumer<Derived>::Create(nsIGlobalObject* aGlobal,
|
||||
FetchBody<Derived>* aBody,
|
||||
FetchConsumeType aType,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(aBody);
|
||||
|
||||
nsCOMPtr<nsIInputStream> bodyStream;
|
||||
aBody->DerivedClass()->GetBody(getter_AddRefs(bodyStream));
|
||||
if (!bodyStream) {
|
||||
aRv = NS_NewCStringInputStream(getter_AddRefs(bodyStream), EmptyCString());
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = nullptr;
|
||||
if (!NS_IsMainThread()) {
|
||||
workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
}
|
||||
|
||||
RefPtr<FetchBodyConsumer<Derived>> consumer =
|
||||
new FetchBodyConsumer<Derived>(aGlobal, workerPrivate, aBody, bodyStream,
|
||||
promise, aType);
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
if (NS_WARN_IF(!consumer->RegisterWorkerHolder())) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (NS_WARN_IF(!os)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aRv = os->AddObserver(consumer, DOM_WINDOW_DESTROYED_TOPIC, true);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aRv = os->AddObserver(consumer, DOM_WINDOW_FROZEN_TOPIC, true);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> r = new BeginConsumeBodyRunnable<Derived>(consumer);
|
||||
|
||||
if (workerPrivate) {
|
||||
aRv = workerPrivate->DispatchToMainThread(r.forget());
|
||||
} else {
|
||||
aRv = NS_DispatchToMainThread(r.forget());
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBodyConsumer<Derived>::ReleaseObject()
|
||||
{
|
||||
AssertIsOnTargetThread();
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (os) {
|
||||
os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
|
||||
os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
|
||||
}
|
||||
}
|
||||
|
||||
mGlobal = nullptr;
|
||||
mWorkerHolder = nullptr;
|
||||
|
||||
#ifdef DEBUG
|
||||
mBody = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
FetchBodyConsumer<Derived>::FetchBodyConsumer(nsIGlobalObject* aGlobalObject,
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
FetchBody<Derived>* aBody,
|
||||
nsIInputStream* aBodyStream,
|
||||
Promise* aPromise,
|
||||
FetchConsumeType aType)
|
||||
: mTargetThread(NS_GetCurrentThread())
|
||||
#ifdef DEBUG
|
||||
, mBody(aBody)
|
||||
#endif
|
||||
, mBodyStream(aBodyStream)
|
||||
, mBlobStorageType(MutableBlobStorage::eOnlyInMemory)
|
||||
, mGlobal(aGlobalObject)
|
||||
, mWorkerPrivate(aWorkerPrivate)
|
||||
, mConsumeType(aType)
|
||||
, mConsumePromise(aPromise)
|
||||
, mBodyConsumed(false)
|
||||
, mShuttingDown(false)
|
||||
{
|
||||
MOZ_ASSERT(aBody);
|
||||
MOZ_ASSERT(aBodyStream);
|
||||
MOZ_ASSERT(aPromise);
|
||||
|
||||
const mozilla::UniquePtr<mozilla::ipc::PrincipalInfo>& principalInfo =
|
||||
aBody->DerivedClass()->GetPrincipalInfo();
|
||||
// We support temporary file for blobs only if the principal is known and
|
||||
// it's system or content not in private Browsing.
|
||||
if (principalInfo &&
|
||||
(principalInfo->type() == mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo ||
|
||||
(principalInfo->type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
|
||||
principalInfo->get_ContentPrincipalInfo().attrs().mPrivateBrowsingId == 0))) {
|
||||
mBlobStorageType = MutableBlobStorage::eCouldBeInTemporaryFile;
|
||||
}
|
||||
|
||||
mBodyMimeType = aBody->MimeType();
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
FetchBodyConsumer<Derived>::~FetchBodyConsumer()
|
||||
{
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBodyConsumer<Derived>::AssertIsOnTargetThread() const
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mTargetThread);
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
bool
|
||||
FetchBodyConsumer<Derived>::RegisterWorkerHolder()
|
||||
{
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
MOZ_ASSERT(!mWorkerHolder);
|
||||
mWorkerHolder.reset(new FetchBodyWorkerHolder<Derived>(this));
|
||||
|
||||
if (!mWorkerHolder->HoldWorker(mWorkerPrivate, Closing)) {
|
||||
NS_WARNING("Failed to add workerHolder");
|
||||
mWorkerHolder = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* BeginConsumeBodyMainThread() will automatically reject the consume promise
|
||||
* and clean up on any failures, so there is no need for callers to do so,
|
||||
* reflected in a lack of error return code.
|
||||
*/
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBodyConsumer<Derived>::BeginConsumeBodyMainThread()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
AutoFailConsumeBody<Derived> autoReject(this);
|
||||
|
||||
if (mShuttingDown) {
|
||||
// We haven't started yet, but we have been terminated. AutoFailConsumeBody
|
||||
// will dispatch a runnable to release resources.
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStreamPump> pump;
|
||||
nsresult rv = NS_NewInputStreamPump(getter_AddRefs(pump),
|
||||
mBodyStream, -1, -1, 0, 0, false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ConsumeBodyDoneObserver<Derived>> p =
|
||||
new ConsumeBodyDoneObserver<Derived>(this);
|
||||
|
||||
nsCOMPtr<nsIStreamListener> listener;
|
||||
if (mConsumeType == CONSUME_BLOB) {
|
||||
listener = new MutableBlobStreamListener(mBlobStorageType, nullptr,
|
||||
mBodyMimeType, p);
|
||||
} else {
|
||||
nsCOMPtr<nsIStreamLoader> loader;
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(loader), p);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
listener = loader;
|
||||
}
|
||||
|
||||
rv = pump->AsyncRead(listener, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now that everything succeeded, we can assign the pump to a pointer that
|
||||
// stays alive for the lifetime of the FetchConsumer.
|
||||
mConsumeBodyPump = pump;
|
||||
|
||||
// It is ok for retargeting to fail and reads to happen on the main thread.
|
||||
autoReject.DontFail();
|
||||
|
||||
// Try to retarget, otherwise fall back to main thread.
|
||||
nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(pump);
|
||||
if (rr) {
|
||||
nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
||||
rv = rr->RetargetDeliveryTo(sts);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
NS_WARNING("Retargeting failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBodyConsumer<Derived>::ContinueConsumeBody(nsresult aStatus,
|
||||
uint32_t aResultLength,
|
||||
uint8_t* aResult)
|
||||
{
|
||||
AssertIsOnTargetThread();
|
||||
|
||||
if (mBodyConsumed) {
|
||||
return;
|
||||
}
|
||||
mBodyConsumed = true;
|
||||
|
||||
// Just a precaution to ensure ContinueConsumeBody is not called out of
|
||||
// sync with a body read.
|
||||
MOZ_ASSERT(mBody->BodyUsed());
|
||||
|
||||
auto autoFree = mozilla::MakeScopeExit([&] {
|
||||
free(aResult);
|
||||
});
|
||||
|
||||
MOZ_ASSERT(mConsumePromise);
|
||||
RefPtr<Promise> localPromise = mConsumePromise.forget();
|
||||
|
||||
RefPtr<FetchBodyConsumer<Derived>> self = this;
|
||||
auto autoReleaseObject = mozilla::MakeScopeExit([&] {
|
||||
self->ReleaseObject();
|
||||
});
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(aStatus))) {
|
||||
localPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
// Don't warn here since we warned above.
|
||||
if (NS_FAILED(aStatus)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Finish successfully consuming body according to type.
|
||||
MOZ_ASSERT(aResult);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (!jsapi.Init(mGlobal)) {
|
||||
localPromise->MaybeReject(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
ErrorResult error;
|
||||
|
||||
switch (mConsumeType) {
|
||||
case CONSUME_ARRAYBUFFER: {
|
||||
JS::Rooted<JSObject*> arrayBuffer(cx);
|
||||
BodyUtil::ConsumeArrayBuffer(cx, &arrayBuffer, aResultLength, aResult,
|
||||
error);
|
||||
|
||||
if (!error.Failed()) {
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
val.setObjectOrNull(arrayBuffer);
|
||||
|
||||
localPromise->MaybeResolve(cx, val);
|
||||
// ArrayBuffer takes over ownership.
|
||||
aResult = nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONSUME_BLOB: {
|
||||
MOZ_CRASH("This should not happen.");
|
||||
break;
|
||||
}
|
||||
case CONSUME_FORMDATA: {
|
||||
nsCString data;
|
||||
data.Adopt(reinterpret_cast<char*>(aResult), aResultLength);
|
||||
aResult = nullptr;
|
||||
|
||||
RefPtr<dom::FormData> fd =
|
||||
BodyUtil::ConsumeFormData(mGlobal, mBodyMimeType, data, error);
|
||||
if (!error.Failed()) {
|
||||
localPromise->MaybeResolve(fd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONSUME_TEXT:
|
||||
// fall through handles early exit.
|
||||
case CONSUME_JSON: {
|
||||
nsString decoded;
|
||||
if (NS_SUCCEEDED(BodyUtil::ConsumeText(aResultLength, aResult, decoded))) {
|
||||
if (mConsumeType == CONSUME_TEXT) {
|
||||
localPromise->MaybeResolve(decoded);
|
||||
} else {
|
||||
JS::Rooted<JS::Value> json(cx);
|
||||
BodyUtil::ConsumeJson(cx, &json, decoded, error);
|
||||
if (!error.Failed()) {
|
||||
localPromise->MaybeResolve(cx, json);
|
||||
}
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NS_NOTREACHED("Unexpected consume body type");
|
||||
}
|
||||
|
||||
error.WouldReportJSException();
|
||||
if (error.Failed()) {
|
||||
localPromise->MaybeReject(error);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBodyConsumer<Derived>::ContinueConsumeBlobBody(BlobImpl* aBlobImpl)
|
||||
{
|
||||
AssertIsOnTargetThread();
|
||||
MOZ_ASSERT(mConsumeType == CONSUME_BLOB);
|
||||
|
||||
if (mBodyConsumed) {
|
||||
return;
|
||||
}
|
||||
mBodyConsumed = true;
|
||||
|
||||
// Just a precaution to ensure ContinueConsumeBody is not called out of
|
||||
// sync with a body read.
|
||||
MOZ_ASSERT(mBody->BodyUsed());
|
||||
|
||||
MOZ_ASSERT(mConsumePromise);
|
||||
RefPtr<Promise> localPromise = mConsumePromise.forget();
|
||||
|
||||
RefPtr<dom::Blob> blob = dom::Blob::Create(mGlobal, aBlobImpl);
|
||||
MOZ_ASSERT(blob);
|
||||
|
||||
localPromise->MaybeResolve(blob);
|
||||
|
||||
ReleaseObject();
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBodyConsumer<Derived>::ShutDownMainThreadConsuming()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
RefPtr<FetchBodyConsumer<Derived>> self = this;
|
||||
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||
[self] () { self->ShutDownMainThreadConsuming(); });
|
||||
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
workerPrivate->DispatchToMainThread(r.forget());
|
||||
return;
|
||||
}
|
||||
|
||||
// We need this because maybe, mConsumeBodyPump has not been created yet. We
|
||||
// must be sure that we don't try to do it.
|
||||
mShuttingDown = true;
|
||||
|
||||
if (mConsumeBodyPump) {
|
||||
mConsumeBodyPump->Cancel(NS_BINDING_ABORTED);
|
||||
mConsumeBodyPump = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
NS_IMETHODIMP
|
||||
FetchBodyConsumer<Derived>::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MOZ_ASSERT((strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) ||
|
||||
(strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0));
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
|
||||
if (SameCOMIdentity(aSubject, window)) {
|
||||
ContinueConsumeBody(NS_BINDING_ABORTED, 0, nullptr);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_ADDREF(FetchBodyConsumer<Derived>)
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_RELEASE(FetchBodyConsumer<Derived>)
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_QUERY_INTERFACE(FetchBodyConsumer<Derived>,
|
||||
nsIObserver,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
128
dom/fetch/FetchConsumer.h
Normal file
128
dom/fetch/FetchConsumer.h
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_FetchConsumer_h
|
||||
#define mozilla_dom_FetchConsumer_h
|
||||
|
||||
#include "Fetch.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "mozilla/dom/MutableBlobStorage.h"
|
||||
|
||||
class nsIThread;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
class WorkerHolder;
|
||||
}
|
||||
|
||||
template <class Derived> class FetchBody;
|
||||
|
||||
// FetchBody is not thread-safe but we need to move it around threads.
|
||||
// In order to keep it alive all the time, we use a WorkerHolder, if created on
|
||||
// workers, plus a this consumer.
|
||||
template <class Derived>
|
||||
class FetchBodyConsumer final : public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static already_AddRefed<Promise>
|
||||
Create(nsIGlobalObject* aGlobal,
|
||||
FetchBody<Derived>* aBody,
|
||||
FetchConsumeType aType,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
ReleaseObject();
|
||||
|
||||
void
|
||||
BeginConsumeBodyMainThread();
|
||||
|
||||
void
|
||||
ContinueConsumeBody(nsresult aStatus, uint32_t aLength, uint8_t* aResult);
|
||||
|
||||
void
|
||||
ContinueConsumeBlobBody(BlobImpl* aBlobImpl);
|
||||
|
||||
void
|
||||
ShutDownMainThreadConsuming();
|
||||
|
||||
workers::WorkerPrivate*
|
||||
GetWorkerPrivate() const
|
||||
{
|
||||
return mWorkerPrivate;
|
||||
}
|
||||
|
||||
void
|
||||
NullifyConsumeBodyPump()
|
||||
{
|
||||
mShuttingDown = true;
|
||||
mConsumeBodyPump = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
FetchBodyConsumer(nsIGlobalObject* aGlobalObject,
|
||||
workers::WorkerPrivate* aWorkerPrivate,
|
||||
FetchBody<Derived>* aBody,
|
||||
nsIInputStream* aBodyStream,
|
||||
Promise* aPromise,
|
||||
FetchConsumeType aType);
|
||||
|
||||
~FetchBodyConsumer();
|
||||
|
||||
void
|
||||
AssertIsOnTargetThread() const;
|
||||
|
||||
bool
|
||||
RegisterWorkerHolder();
|
||||
|
||||
nsCOMPtr<nsIThread> mTargetThread;
|
||||
|
||||
#ifdef DEBUG
|
||||
// This is used only to check if the body has been correctly consumed.
|
||||
RefPtr<FetchBody<Derived>> mBody;
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIInputStream> mBodyStream;
|
||||
MutableBlobStorage::MutableBlobStorageType mBlobStorageType;
|
||||
nsCString mBodyMimeType;
|
||||
|
||||
// Set when consuming the body is attempted on a worker.
|
||||
// Unset when consumption is done/aborted.
|
||||
// This WorkerHolder keeps alive the consumer via a cycle.
|
||||
UniquePtr<workers::WorkerHolder> mWorkerHolder;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
|
||||
// Always set whenever the FetchBodyConsumer is created on the worker thread.
|
||||
workers::WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
// Touched on the main-thread only.
|
||||
nsCOMPtr<nsIInputStreamPump> mConsumeBodyPump;
|
||||
|
||||
// Only ever set once, always on target thread.
|
||||
FetchConsumeType mConsumeType;
|
||||
RefPtr<Promise> mConsumePromise;
|
||||
|
||||
// touched only on the target thread.
|
||||
bool mBodyConsumed;
|
||||
|
||||
// touched only on the main-thread.
|
||||
bool mShuttingDown;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FetchConsumer_h
|
||||
938
dom/fetch/FetchDriver.cpp
Normal file
938
dom/fetch/FetchDriver.cpp
Normal file
|
|
@ -0,0 +1,938 @@
|
|||
/* -*- 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 "mozilla/DebugOnly.h"
|
||||
#include "mozilla/dom/FetchDriver.h"
|
||||
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsIHttpHeaderVisitor.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIThreadRetargetableRequest.h"
|
||||
#include "nsIUploadChannel2.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIPipe.h"
|
||||
|
||||
#include "nsContentPolicyUtils.h"
|
||||
#include "nsDataHandler.h"
|
||||
#include "nsHostObjectProtocolHandler.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "nsHttpChannel.h"
|
||||
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/workers/Workers.h"
|
||||
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include "Fetch.h"
|
||||
#include "InternalRequest.h"
|
||||
#include "InternalResponse.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS(FetchDriver,
|
||||
nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor,
|
||||
nsIThreadRetargetableStreamListener)
|
||||
|
||||
FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
|
||||
nsILoadGroup* aLoadGroup)
|
||||
: mPrincipal(aPrincipal)
|
||||
, mLoadGroup(aLoadGroup)
|
||||
, mRequest(aRequest)
|
||||
#ifdef DEBUG
|
||||
, mResponseAvailableCalled(false)
|
||||
, mFetchCalled(false)
|
||||
#endif
|
||||
{
|
||||
MOZ_ASSERT(aRequest);
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
}
|
||||
|
||||
FetchDriver::~FetchDriver()
|
||||
{
|
||||
// We assert this since even on failures, we should call
|
||||
// FailWithNetworkError().
|
||||
MOZ_ASSERT(mResponseAvailableCalled);
|
||||
}
|
||||
|
||||
nsresult
|
||||
FetchDriver::Fetch(FetchDriverObserver* aObserver)
|
||||
{
|
||||
workers::AssertIsOnMainThread();
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(!mFetchCalled);
|
||||
mFetchCalled = true;
|
||||
#endif
|
||||
|
||||
mObserver = aObserver;
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REQUEST_PASSTHROUGH,
|
||||
mRequest->WasCreatedByFetchEvent());
|
||||
|
||||
// FIXME(nsm): Deal with HSTS.
|
||||
|
||||
MOZ_RELEASE_ASSERT(!mRequest->IsSynchronous(),
|
||||
"Synchronous fetch not supported");
|
||||
|
||||
|
||||
UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo(new mozilla::ipc::PrincipalInfo());
|
||||
nsresult rv = PrincipalToPrincipalInfo(mPrincipal, principalInfo.get());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mRequest->SetPrincipalInfo(Move(principalInfo));
|
||||
if (NS_FAILED(HttpFetch())) {
|
||||
FailWithNetworkError();
|
||||
}
|
||||
|
||||
// Any failure is handled by FailWithNetworkError notifying the aObserver.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// This function implements the "HTTP Fetch" algorithm from the Fetch spec.
|
||||
// Functionality is often split between here, the CORS listener proxy and the
|
||||
// Necko HTTP implementation.
|
||||
nsresult
|
||||
FetchDriver::HttpFetch()
|
||||
{
|
||||
// Step 1. "Let response be null."
|
||||
mResponse = nullptr;
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString url;
|
||||
mRequest->GetURL(url);
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_NewURI(getter_AddRefs(uri),
|
||||
url,
|
||||
nullptr,
|
||||
nullptr,
|
||||
ios);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Unsafe requests aren't allowed with when using no-core mode.
|
||||
if (mRequest->Mode() == RequestMode::No_cors &&
|
||||
mRequest->UnsafeRequest() &&
|
||||
(!mRequest->HasSimpleMethod() ||
|
||||
!mRequest->Headers()->HasOnlySimpleHeaders())) {
|
||||
MOZ_ASSERT(false, "The API should have caught this");
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
// non-GET requests aren't allowed for blob.
|
||||
if (IsBlobURI(uri)) {
|
||||
nsAutoCString method;
|
||||
mRequest->GetMethod(method);
|
||||
if (!method.EqualsLiteral("GET")) {
|
||||
return NS_ERROR_DOM_NETWORK_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2 deals with letting ServiceWorkers intercept requests. This is
|
||||
// handled by Necko after the channel is opened.
|
||||
// FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
|
||||
// set based on the Request's flag.
|
||||
|
||||
// Step 3.1 "If the CORS preflight flag is set and one of these conditions is
|
||||
// true..." is handled by the CORS proxy.
|
||||
//
|
||||
// Step 3.2 "Set request's skip service worker flag." This isn't required
|
||||
// since Necko will fall back to the network if the ServiceWorker does not
|
||||
// respond with a valid Response.
|
||||
//
|
||||
// NS_StartCORSPreflight() will automatically kick off the original request
|
||||
// if it succeeds, so we need to have everything setup for the original
|
||||
// request too.
|
||||
|
||||
// Step 3.3 "Let credentials flag be set if one of
|
||||
// - request's credentials mode is "include"
|
||||
// - request's credentials mode is "same-origin" and either the CORS flag
|
||||
// is unset or response tainting is "opaque"
|
||||
// is true, and unset otherwise."
|
||||
|
||||
// Set skip serviceworker flag.
|
||||
// While the spec also gates on the client being a ServiceWorker, we can't
|
||||
// infer that here. Instead we rely on callers to set the flag correctly.
|
||||
const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker() ?
|
||||
nsIChannel::LOAD_BYPASS_SERVICE_WORKER : 0;
|
||||
|
||||
nsSecurityFlags secFlags = nsILoadInfo::SEC_ABOUT_BLANK_INHERITS;
|
||||
if (mRequest->Mode() == RequestMode::Cors) {
|
||||
secFlags |= nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
|
||||
} else if (mRequest->Mode() == RequestMode::Same_origin ||
|
||||
mRequest->Mode() == RequestMode::Navigate) {
|
||||
secFlags |= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
|
||||
} else if (mRequest->Mode() == RequestMode::No_cors) {
|
||||
secFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
|
||||
} else {
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected request mode!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
|
||||
secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS;
|
||||
}
|
||||
|
||||
// This is handles the use credentials flag in "HTTP
|
||||
// network or cache fetch" in the spec and decides whether to transmit
|
||||
// cookies and other identifying information.
|
||||
if (mRequest->GetCredentialsMode() == RequestCredentials::Include) {
|
||||
secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
|
||||
} else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) {
|
||||
secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
|
||||
} else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin) {
|
||||
secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
|
||||
} else {
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// From here on we create a channel and set its properties with the
|
||||
// information from the InternalRequest. This is an implementation detail.
|
||||
MOZ_ASSERT(mLoadGroup);
|
||||
nsCOMPtr<nsIChannel> chan;
|
||||
|
||||
nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
|
||||
bypassFlag | nsIChannel::LOAD_CLASSIFY_URI;
|
||||
if (mDocument) {
|
||||
MOZ_ASSERT(mDocument->NodePrincipal() == mPrincipal);
|
||||
rv = NS_NewChannel(getter_AddRefs(chan),
|
||||
uri,
|
||||
mDocument,
|
||||
secFlags,
|
||||
mRequest->ContentPolicyType(),
|
||||
mLoadGroup,
|
||||
nullptr, /* aCallbacks */
|
||||
loadFlags,
|
||||
ios);
|
||||
} else {
|
||||
rv = NS_NewChannel(getter_AddRefs(chan),
|
||||
uri,
|
||||
mPrincipal,
|
||||
secFlags,
|
||||
mRequest->ContentPolicyType(),
|
||||
mLoadGroup,
|
||||
nullptr, /* aCallbacks */
|
||||
loadFlags,
|
||||
ios);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mLoadGroup = nullptr;
|
||||
|
||||
// Insert ourselves into the notification callbacks chain so we can set
|
||||
// headers on redirects.
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
|
||||
chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
|
||||
MOZ_ASSERT(!notificationCallbacks);
|
||||
}
|
||||
#endif
|
||||
chan->SetNotificationCallbacks(this);
|
||||
|
||||
// Step 3.5 begins "HTTP network or cache fetch".
|
||||
// HTTP network or cache fetch
|
||||
// ---------------------------
|
||||
// Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
|
||||
nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
|
||||
if (httpChan) {
|
||||
// Copy the method.
|
||||
nsAutoCString method;
|
||||
mRequest->GetMethod(method);
|
||||
rv = httpChan->SetRequestMethod(method);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set the same headers.
|
||||
SetRequestHeaders(httpChan);
|
||||
|
||||
// Step 2. Set the referrer.
|
||||
nsAutoString referrer;
|
||||
mRequest->GetReferrer(referrer);
|
||||
|
||||
// The Referrer Policy in Request can be used to override a referrer policy
|
||||
// associated with an environment settings object.
|
||||
// If there's no Referrer Policy in the request, it should be inherited
|
||||
// from environment.
|
||||
ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
|
||||
net::ReferrerPolicy net_referrerPolicy = mRequest->GetEnvironmentReferrerPolicy();
|
||||
switch (referrerPolicy) {
|
||||
case ReferrerPolicy::_empty:
|
||||
break;
|
||||
case ReferrerPolicy::No_referrer:
|
||||
net_referrerPolicy = net::RP_No_Referrer;
|
||||
break;
|
||||
case ReferrerPolicy::No_referrer_when_downgrade:
|
||||
net_referrerPolicy = net::RP_No_Referrer_When_Downgrade;
|
||||
break;
|
||||
case ReferrerPolicy::Origin:
|
||||
net_referrerPolicy = net::RP_Origin;
|
||||
break;
|
||||
case ReferrerPolicy::Origin_when_cross_origin:
|
||||
net_referrerPolicy = net::RP_Origin_When_Crossorigin;
|
||||
break;
|
||||
case ReferrerPolicy::Unsafe_url:
|
||||
net_referrerPolicy = net::RP_Unsafe_URL;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy enum value?");
|
||||
break;
|
||||
}
|
||||
if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
|
||||
rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal,
|
||||
mDocument,
|
||||
httpChan,
|
||||
net_referrerPolicy);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (referrer.IsEmpty()) {
|
||||
rv = httpChan->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
// From "Determine request's Referrer" step 3
|
||||
// "If request's referrer is a URL, let referrerSource be request's
|
||||
// referrer."
|
||||
nsCOMPtr<nsIURI> referrerURI;
|
||||
rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv =
|
||||
httpChan->SetReferrerWithPolicy(referrerURI,
|
||||
referrerPolicy == ReferrerPolicy::_empty ?
|
||||
mRequest->GetEnvironmentReferrerPolicy() :
|
||||
net_referrerPolicy);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Bug 1120722 - Authorization will be handled later.
|
||||
// Auth may require prompting, we don't support it yet.
|
||||
// The next patch in this same bug prevents this from aborting the request.
|
||||
// Credentials checks for CORS are handled by nsCORSListenerProxy,
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
|
||||
|
||||
// Conversion between enumerations is safe due to static asserts in
|
||||
// dom/workers/ServiceWorkerManager.cpp
|
||||
internalChan->SetCorsMode(static_cast<uint32_t>(mRequest->Mode()));
|
||||
internalChan->SetRedirectMode(static_cast<uint32_t>(mRequest->GetRedirectMode()));
|
||||
mRequest->MaybeSkipCacheIfPerformingRevalidation();
|
||||
internalChan->SetFetchCacheMode(static_cast<uint32_t>(mRequest->GetCacheMode()));
|
||||
internalChan->SetIntegrityMetadata(mRequest->GetIntegrity());
|
||||
}
|
||||
|
||||
// Step 5. Proxy authentication will be handled by Necko.
|
||||
|
||||
// Continue setting up 'HTTPRequest'. Content-Type and body data.
|
||||
nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan);
|
||||
if (uploadChan) {
|
||||
nsAutoCString contentType;
|
||||
ErrorResult result;
|
||||
mRequest->Headers()->GetFirst(NS_LITERAL_CSTRING("content-type"), contentType, result);
|
||||
// We don't actually expect "result" to have failed here: that only happens
|
||||
// for invalid header names. But if for some reason it did, just propagate
|
||||
// it out.
|
||||
if (result.Failed()) {
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
// Now contentType is the header that was set in mRequest->Headers(), or a
|
||||
// void string if no header was set.
|
||||
#ifdef DEBUG
|
||||
bool hasContentTypeHeader =
|
||||
mRequest->Headers()->Has(NS_LITERAL_CSTRING("content-type"), result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
MOZ_ASSERT_IF(!hasContentTypeHeader, contentType.IsVoid());
|
||||
#endif // DEBUG
|
||||
|
||||
nsCOMPtr<nsIInputStream> bodyStream;
|
||||
mRequest->GetBody(getter_AddRefs(bodyStream));
|
||||
if (bodyStream) {
|
||||
nsAutoCString method;
|
||||
mRequest->GetMethod(method);
|
||||
rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType, -1, method, false /* aStreamHasHeaders */);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
// If preflight is required, start a "CORS preflight fetch"
|
||||
// https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
|
||||
// implementation is handled by the http channel calling into
|
||||
// nsCORSListenerProxy. We just inform it which unsafe headers are included
|
||||
// in the request.
|
||||
if (mRequest->Mode() == RequestMode::Cors) {
|
||||
AutoTArray<nsCString, 5> unsafeHeaders;
|
||||
mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo();
|
||||
loadInfo->SetCorsPreflightInfo(unsafeHeaders, false);
|
||||
}
|
||||
|
||||
rv = chan->AsyncOpen2(this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
|
||||
return NS_OK;
|
||||
}
|
||||
already_AddRefed<InternalResponse>
|
||||
FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse,
|
||||
bool aFoundOpaqueRedirect)
|
||||
{
|
||||
MOZ_ASSERT(aResponse);
|
||||
AutoTArray<nsCString, 4> reqURLList;
|
||||
mRequest->GetURLListWithoutFragment(reqURLList);
|
||||
MOZ_ASSERT(!reqURLList.IsEmpty());
|
||||
aResponse->SetURLList(reqURLList);
|
||||
RefPtr<InternalResponse> filteredResponse;
|
||||
if (aFoundOpaqueRedirect) {
|
||||
filteredResponse = aResponse->OpaqueRedirectResponse();
|
||||
} else {
|
||||
switch (mRequest->GetResponseTainting()) {
|
||||
case LoadTainting::Basic:
|
||||
filteredResponse = aResponse->BasicResponse();
|
||||
break;
|
||||
case LoadTainting::CORS:
|
||||
filteredResponse = aResponse->CORSResponse();
|
||||
break;
|
||||
case LoadTainting::Opaque:
|
||||
filteredResponse = aResponse->OpaqueResponse();
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected case");
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(filteredResponse);
|
||||
MOZ_ASSERT(mObserver);
|
||||
if (filteredResponse->Type() == ResponseType::Error ||
|
||||
mRequest->GetIntegrity().IsEmpty()) {
|
||||
mObserver->OnResponseAvailable(filteredResponse);
|
||||
#ifdef DEBUG
|
||||
mResponseAvailableCalled = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
return filteredResponse.forget();
|
||||
}
|
||||
|
||||
void
|
||||
FetchDriver::FailWithNetworkError()
|
||||
{
|
||||
workers::AssertIsOnMainThread();
|
||||
RefPtr<InternalResponse> error = InternalResponse::NetworkError();
|
||||
if (mObserver) {
|
||||
mObserver->OnResponseAvailable(error);
|
||||
#ifdef DEBUG
|
||||
mResponseAvailableCalled = true;
|
||||
#endif
|
||||
mObserver->OnResponseEnd();
|
||||
mObserver = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
class FillResponseHeaders final : public nsIHttpHeaderVisitor {
|
||||
InternalResponse* mResponse;
|
||||
|
||||
~FillResponseHeaders()
|
||||
{ }
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
explicit FillResponseHeaders(InternalResponse* aResponse)
|
||||
: mResponse(aResponse)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
VisitHeader(const nsACString & aHeader, const nsACString & aValue) override
|
||||
{
|
||||
ErrorResult result;
|
||||
mResponse->Headers()->Append(aHeader, aValue, result);
|
||||
if (result.Failed()) {
|
||||
NS_WARNING(nsPrintfCString("Fetch ignoring illegal header - '%s': '%s'",
|
||||
PromiseFlatCString(aHeader).get(),
|
||||
PromiseFlatCString(aValue).get()).get());
|
||||
result.SuppressException();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(FillResponseHeaders, nsIHttpHeaderVisitor)
|
||||
} // namespace
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchDriver::OnStartRequest(nsIRequest* aRequest,
|
||||
nsISupports* aContext)
|
||||
{
|
||||
workers::AssertIsOnMainThread();
|
||||
|
||||
// Note, this can be called multiple times if we are doing an opaqueredirect.
|
||||
// In that case we will get a simulated OnStartRequest() and then the real
|
||||
// channel will call in with an errored OnStartRequest().
|
||||
|
||||
nsresult rv;
|
||||
aRequest->GetStatus(&rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
FailWithNetworkError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
// We should only get to the following code once.
|
||||
MOZ_ASSERT(!mPipeOutputStream);
|
||||
MOZ_ASSERT(mObserver);
|
||||
|
||||
RefPtr<InternalResponse> response;
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
|
||||
|
||||
// On a successful redirect we perform the following substeps of HTTP Fetch,
|
||||
// step 5, "redirect status", step 11.
|
||||
|
||||
bool foundOpaqueRedirect = false;
|
||||
|
||||
int64_t contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
|
||||
rv = channel->GetContentLength(&contentLength);
|
||||
MOZ_ASSERT_IF(NS_FAILED(rv), contentLength == InternalResponse::UNKNOWN_BODY_SIZE);
|
||||
|
||||
if (httpChannel) {
|
||||
uint32_t responseStatus;
|
||||
httpChannel->GetResponseStatus(&responseStatus);
|
||||
|
||||
if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
|
||||
if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
|
||||
FailWithNetworkError();
|
||||
return NS_BINDING_FAILED;
|
||||
}
|
||||
if (mRequest->GetRedirectMode() == RequestRedirect::Manual) {
|
||||
foundOpaqueRedirect = true;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoCString statusText;
|
||||
httpChannel->GetResponseStatusText(statusText);
|
||||
|
||||
response = new InternalResponse(responseStatus, statusText);
|
||||
|
||||
RefPtr<FillResponseHeaders> visitor = new FillResponseHeaders(response);
|
||||
rv = httpChannel->VisitResponseHeaders(visitor);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
NS_WARNING("Failed to visit all headers.");
|
||||
}
|
||||
|
||||
// If Content-Encoding or Transfer-Encoding headers are set, then the actual
|
||||
// Content-Length (which refer to the decoded data) is obscured behind the encodings.
|
||||
ErrorResult result;
|
||||
if (response->Headers()->Has(NS_LITERAL_CSTRING("content-encoding"), result) ||
|
||||
response->Headers()->Has(NS_LITERAL_CSTRING("transfer-encoding"), result)) {
|
||||
NS_WARNING("Cannot know response Content-Length due to presence of Content-Encoding "
|
||||
"or Transfer-Encoding headers.");
|
||||
contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
|
||||
}
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
} else {
|
||||
response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
|
||||
|
||||
ErrorResult result;
|
||||
nsAutoCString contentType;
|
||||
rv = channel->GetContentType(contentType);
|
||||
if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
|
||||
nsAutoCString contentCharset;
|
||||
channel->GetContentCharset(contentCharset);
|
||||
if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
|
||||
contentType += NS_LITERAL_CSTRING(";charset=") + contentCharset;
|
||||
}
|
||||
|
||||
response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
|
||||
contentType,
|
||||
result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
}
|
||||
|
||||
if (contentLength > 0) {
|
||||
nsAutoCString contentLenStr;
|
||||
contentLenStr.AppendInt(contentLength);
|
||||
response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"),
|
||||
contentLenStr,
|
||||
result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
}
|
||||
}
|
||||
|
||||
// We open a pipe so that we can immediately set the pipe's read end as the
|
||||
// response's body. Setting the segment size to UINT32_MAX means that the
|
||||
// pipe has infinite space. The nsIChannel will continue to buffer data in
|
||||
// xpcom events even if we block on a fixed size pipe. It might be possible
|
||||
// to suspend the channel and then resume when there is space available, but
|
||||
// for now use an infinite pipe to avoid blocking.
|
||||
nsCOMPtr<nsIInputStream> pipeInputStream;
|
||||
rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
|
||||
getter_AddRefs(mPipeOutputStream),
|
||||
0, /* default segment size */
|
||||
UINT32_MAX /* infinite pipe */,
|
||||
true /* non-blocking input, otherwise you deadlock */,
|
||||
false /* blocking output, since the pipe is 'in'finite */ );
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
// Cancel request.
|
||||
return rv;
|
||||
}
|
||||
response->SetBody(pipeInputStream, contentLength);
|
||||
|
||||
response->InitChannelInfo(channel);
|
||||
|
||||
nsCOMPtr<nsIURI> channelURI;
|
||||
rv = channel->GetURI(getter_AddRefs(channelURI));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
// Cancel request.
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Propagate any tainting from the channel back to our response here. This
|
||||
// step is not reflected in the spec because the spec is written such that
|
||||
// FetchEvent.respondWith() just passes the already-tainted Response back to
|
||||
// the outer fetch(). In gecko, however, we serialize the Response through
|
||||
// the channel and must regenerate the tainting from the channel in the
|
||||
// interception case.
|
||||
mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());
|
||||
|
||||
// Resolves fetch() promise which may trigger code running in a worker. Make
|
||||
// sure the Response is fully initialized before calling this.
|
||||
mResponse = BeginAndGetFilteredResponse(response, foundOpaqueRedirect);
|
||||
|
||||
// From "Main Fetch" step 17: SRI-part1.
|
||||
if (mResponse->Type() != ResponseType::Error &&
|
||||
!mRequest->GetIntegrity().IsEmpty() &&
|
||||
mSRIMetadata.IsEmpty()) {
|
||||
nsIConsoleReportCollector* aReporter = nullptr;
|
||||
if (mObserver) {
|
||||
aReporter = mObserver->GetReporter();
|
||||
}
|
||||
|
||||
nsAutoCString sourceUri;
|
||||
if (mDocument && mDocument->GetDocumentURI()) {
|
||||
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
|
||||
} else if (!mWorkerScript.IsEmpty()) {
|
||||
sourceUri.Assign(mWorkerScript);
|
||||
}
|
||||
SRICheck::IntegrityMetadata(mRequest->GetIntegrity(), sourceUri,
|
||||
aReporter, &mSRIMetadata);
|
||||
mSRIDataVerifier = new SRICheckDataVerifier(mSRIMetadata, sourceUri,
|
||||
aReporter);
|
||||
|
||||
// Do not retarget off main thread when using SRI API.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
// Cancel request.
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Try to retarget off main thread.
|
||||
if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
|
||||
Unused << NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts)));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchDriver::OnDataAvailable(nsIRequest* aRequest,
|
||||
nsISupports* aContext,
|
||||
nsIInputStream* aInputStream,
|
||||
uint64_t aOffset,
|
||||
uint32_t aCount)
|
||||
{
|
||||
// NB: This can be called on any thread! But we're guaranteed that it is
|
||||
// called between OnStartRequest and OnStopRequest, so we don't need to worry
|
||||
// about races.
|
||||
|
||||
uint32_t aRead;
|
||||
MOZ_ASSERT(mResponse);
|
||||
MOZ_ASSERT(mPipeOutputStream);
|
||||
|
||||
// From "Main Fetch" step 17: SRI-part2.
|
||||
if (mResponse->Type() != ResponseType::Error &&
|
||||
!mRequest->GetIntegrity().IsEmpty()) {
|
||||
MOZ_ASSERT(mSRIDataVerifier);
|
||||
|
||||
uint32_t aWrite;
|
||||
nsTArray<uint8_t> buffer;
|
||||
nsresult rv;
|
||||
buffer.SetCapacity(aCount);
|
||||
while (aCount > 0) {
|
||||
rv = aInputStream->Read(reinterpret_cast<char*>(buffer.Elements()),
|
||||
aCount, &aRead);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mSRIDataVerifier->Update(aRead, (uint8_t*)buffer.Elements());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
while (aRead > 0) {
|
||||
rv = mPipeOutputStream->Write(reinterpret_cast<char*>(buffer.Elements()),
|
||||
aRead, &aWrite);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (aRead < aWrite) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
aRead -= aWrite;
|
||||
}
|
||||
|
||||
|
||||
aCount -= aWrite;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = aInputStream->ReadSegments(NS_CopySegmentToStream,
|
||||
mPipeOutputStream,
|
||||
aCount, &aRead);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchDriver::OnStopRequest(nsIRequest* aRequest,
|
||||
nsISupports* aContext,
|
||||
nsresult aStatusCode)
|
||||
{
|
||||
workers::AssertIsOnMainThread();
|
||||
if (NS_FAILED(aStatusCode)) {
|
||||
nsCOMPtr<nsIAsyncOutputStream> outputStream = do_QueryInterface(mPipeOutputStream);
|
||||
if (outputStream) {
|
||||
outputStream->CloseWithStatus(NS_BINDING_FAILED);
|
||||
}
|
||||
|
||||
// We proceed as usual here, since we've already created a successful response
|
||||
// from OnStartRequest.
|
||||
} else {
|
||||
MOZ_ASSERT(mResponse);
|
||||
MOZ_ASSERT(!mResponse->IsError());
|
||||
|
||||
// From "Main Fetch" step 17: SRI-part3.
|
||||
if (mResponse->Type() != ResponseType::Error &&
|
||||
!mRequest->GetIntegrity().IsEmpty()) {
|
||||
MOZ_ASSERT(mSRIDataVerifier);
|
||||
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
||||
|
||||
nsIConsoleReportCollector* aReporter = nullptr;
|
||||
if (mObserver) {
|
||||
aReporter = mObserver->GetReporter();
|
||||
}
|
||||
|
||||
nsAutoCString sourceUri;
|
||||
if (mDocument && mDocument->GetDocumentURI()) {
|
||||
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
|
||||
} else if (!mWorkerScript.IsEmpty()) {
|
||||
sourceUri.Assign(mWorkerScript);
|
||||
}
|
||||
nsresult rv = mSRIDataVerifier->Verify(mSRIMetadata, channel, sourceUri,
|
||||
aReporter);
|
||||
if (NS_FAILED(rv)) {
|
||||
FailWithNetworkError();
|
||||
// Cancel request.
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (mPipeOutputStream) {
|
||||
mPipeOutputStream->Close();
|
||||
}
|
||||
}
|
||||
|
||||
if (mObserver) {
|
||||
if (mResponse->Type() != ResponseType::Error &&
|
||||
!mRequest->GetIntegrity().IsEmpty()) {
|
||||
//From "Main Fetch" step 23: Process response.
|
||||
MOZ_ASSERT(mResponse);
|
||||
mObserver->OnResponseAvailable(mResponse);
|
||||
#ifdef DEBUG
|
||||
mResponseAvailableCalled = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
mObserver->OnResponseEnd();
|
||||
mObserver = nullptr;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
|
||||
nsIChannel* aNewChannel,
|
||||
uint32_t aFlags,
|
||||
nsIAsyncVerifyRedirectCallback *aCallback)
|
||||
{
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
|
||||
if (httpChannel) {
|
||||
SetRequestHeaders(httpChannel);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(aOldChannel);
|
||||
nsAutoCString tRPHeaderCValue;
|
||||
if (oldHttpChannel) {
|
||||
oldHttpChannel->GetResponseHeader(NS_LITERAL_CSTRING("referrer-policy"),
|
||||
tRPHeaderCValue);
|
||||
}
|
||||
|
||||
// "HTTP-redirect fetch": step 14 "Append locationURL to request's URL list."
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
MOZ_ALWAYS_SUCCEEDS(aNewChannel->GetURI(getter_AddRefs(uri)));
|
||||
|
||||
nsCOMPtr<nsIURI> uriClone;
|
||||
nsresult rv = uri->CloneIgnoringRef(getter_AddRefs(uriClone));
|
||||
if(NS_WARN_IF(NS_FAILED(rv))){
|
||||
return rv;
|
||||
}
|
||||
nsCString spec;
|
||||
rv = uriClone->GetSpec(spec);
|
||||
if(NS_WARN_IF(NS_FAILED(rv))){
|
||||
return rv;
|
||||
}
|
||||
nsCString fragment;
|
||||
rv = uri->GetRef(fragment);
|
||||
if(NS_WARN_IF(NS_FAILED(rv))){
|
||||
return rv;
|
||||
}
|
||||
|
||||
mRequest->AddURL(spec, fragment);
|
||||
NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue);
|
||||
// updates request’s associated referrer policy according to the
|
||||
// Referrer-Policy header (if any).
|
||||
if (!tRPHeaderValue.IsEmpty()) {
|
||||
net::ReferrerPolicy net_referrerPolicy =
|
||||
nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue);
|
||||
if (net_referrerPolicy != net::RP_Unset) {
|
||||
ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
|
||||
switch (net_referrerPolicy) {
|
||||
case net::RP_No_Referrer:
|
||||
referrerPolicy = ReferrerPolicy::No_referrer;
|
||||
break;
|
||||
case net::RP_No_Referrer_When_Downgrade:
|
||||
referrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
|
||||
break;
|
||||
case net::RP_Origin:
|
||||
referrerPolicy = ReferrerPolicy::Origin;
|
||||
break;
|
||||
case net::RP_Origin_When_Crossorigin:
|
||||
referrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
|
||||
break;
|
||||
case net::RP_Unsafe_URL:
|
||||
referrerPolicy = ReferrerPolicy::Unsafe_url;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy value");
|
||||
break;
|
||||
}
|
||||
|
||||
mRequest->SetReferrerPolicy(referrerPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
aCallback->OnRedirectVerifyCallback(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchDriver::CheckListenerChain()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchDriver::GetInterface(const nsIID& aIID, void **aResult)
|
||||
{
|
||||
if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
|
||||
*aResult = static_cast<nsIChannelEventSink*>(this);
|
||||
NS_ADDREF_THIS();
|
||||
return NS_OK;
|
||||
}
|
||||
if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
|
||||
*aResult = static_cast<nsIStreamListener*>(this);
|
||||
NS_ADDREF_THIS();
|
||||
return NS_OK;
|
||||
}
|
||||
if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
|
||||
*aResult = static_cast<nsIRequestObserver*>(this);
|
||||
NS_ADDREF_THIS();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
void
|
||||
FetchDriver::SetDocument(nsIDocument* aDocument)
|
||||
{
|
||||
// Cannot set document after Fetch() has been called.
|
||||
MOZ_ASSERT(!mFetchCalled);
|
||||
mDocument = aDocument;
|
||||
}
|
||||
|
||||
void
|
||||
FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel) const
|
||||
{
|
||||
MOZ_ASSERT(aChannel);
|
||||
|
||||
AutoTArray<InternalHeaders::Entry, 5> headers;
|
||||
mRequest->Headers()->GetEntries(headers);
|
||||
bool hasAccept = false;
|
||||
for (uint32_t i = 0; i < headers.Length(); ++i) {
|
||||
if (!hasAccept && headers[i].mName.EqualsLiteral("accept")) {
|
||||
hasAccept = true;
|
||||
}
|
||||
if (headers[i].mValue.IsEmpty()) {
|
||||
aChannel->SetEmptyRequestHeader(headers[i].mName);
|
||||
} else {
|
||||
aChannel->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasAccept) {
|
||||
aChannel->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
|
||||
NS_LITERAL_CSTRING("*/*"),
|
||||
false /* merge */);
|
||||
}
|
||||
|
||||
if (mRequest->ForceOriginHeader()) {
|
||||
nsAutoString origin;
|
||||
if (NS_SUCCEEDED(nsContentUtils::GetUTFOrigin(mPrincipal, origin))) {
|
||||
aChannel->SetRequestHeader(NS_LITERAL_CSTRING("origin"),
|
||||
NS_ConvertUTF16toUTF8(origin),
|
||||
false /* merge */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
136
dom/fetch/FetchDriver.h
Normal file
136
dom/fetch/FetchDriver.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_FetchDriver_h
|
||||
#define mozilla_dom_FetchDriver_h
|
||||
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIThreadRetargetableStreamListener.h"
|
||||
#include "mozilla/ConsoleReportCollector.h"
|
||||
#include "mozilla/dom/SRIMetadata.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
|
||||
class nsIConsoleReportCollector;
|
||||
class nsIDocument;
|
||||
class nsIOutputStream;
|
||||
class nsILoadGroup;
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class InternalRequest;
|
||||
class InternalResponse;
|
||||
|
||||
/**
|
||||
* Provides callbacks to be called when response is available or on error.
|
||||
* Implemenations usually resolve or reject the promise returned from fetch().
|
||||
* The callbacks can be called synchronously or asynchronously from FetchDriver::Fetch.
|
||||
*/
|
||||
class FetchDriverObserver
|
||||
{
|
||||
public:
|
||||
FetchDriverObserver() : mReporter(new ConsoleReportCollector())
|
||||
, mGotResponseAvailable(false)
|
||||
{ }
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FetchDriverObserver);
|
||||
void OnResponseAvailable(InternalResponse* aResponse)
|
||||
{
|
||||
MOZ_ASSERT(!mGotResponseAvailable);
|
||||
mGotResponseAvailable = true;
|
||||
OnResponseAvailableInternal(aResponse);
|
||||
}
|
||||
virtual void OnResponseEnd()
|
||||
{ };
|
||||
|
||||
nsIConsoleReportCollector* GetReporter() const
|
||||
{
|
||||
return mReporter;
|
||||
}
|
||||
|
||||
virtual void FlushConsoleReport() = 0;
|
||||
protected:
|
||||
virtual ~FetchDriverObserver()
|
||||
{ };
|
||||
|
||||
virtual void OnResponseAvailableInternal(InternalResponse* aResponse) = 0;
|
||||
|
||||
nsCOMPtr<nsIConsoleReportCollector> mReporter;
|
||||
private:
|
||||
bool mGotResponseAvailable;
|
||||
};
|
||||
|
||||
class FetchDriver final : public nsIStreamListener,
|
||||
public nsIChannelEventSink,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsIThreadRetargetableStreamListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
|
||||
|
||||
explicit FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
|
||||
nsILoadGroup* aLoadGroup);
|
||||
NS_IMETHOD Fetch(FetchDriverObserver* aObserver);
|
||||
|
||||
void
|
||||
SetDocument(nsIDocument* aDocument);
|
||||
|
||||
void
|
||||
SetWorkerScript(const nsACString& aWorkerScirpt)
|
||||
{
|
||||
MOZ_ASSERT(!aWorkerScirpt.IsEmpty());
|
||||
mWorkerScript = aWorkerScirpt;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
RefPtr<InternalRequest> mRequest;
|
||||
RefPtr<InternalResponse> mResponse;
|
||||
nsCOMPtr<nsIOutputStream> mPipeOutputStream;
|
||||
RefPtr<FetchDriverObserver> mObserver;
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
nsAutoPtr<SRICheckDataVerifier> mSRIDataVerifier;
|
||||
SRIMetadata mSRIMetadata;
|
||||
nsCString mWorkerScript;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mResponseAvailableCalled;
|
||||
bool mFetchCalled;
|
||||
#endif
|
||||
|
||||
FetchDriver() = delete;
|
||||
FetchDriver(const FetchDriver&) = delete;
|
||||
FetchDriver& operator=(const FetchDriver&) = delete;
|
||||
~FetchDriver();
|
||||
|
||||
nsresult HttpFetch();
|
||||
// Returns the filtered response sent to the observer.
|
||||
already_AddRefed<InternalResponse>
|
||||
BeginAndGetFilteredResponse(InternalResponse* aResponse,
|
||||
bool aFoundOpaqueRedirect);
|
||||
// Utility since not all cases need to do any post processing of the filtered
|
||||
// response.
|
||||
void FailWithNetworkError();
|
||||
|
||||
void SetRequestHeaders(nsIHttpChannel* aChannel) const;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FetchDriver_h
|
||||
57
dom/fetch/FetchIPCTypes.h
Normal file
57
dom/fetch/FetchIPCTypes.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_fetch_IPCUtils_h
|
||||
#define mozilla_dom_fetch_IPCUtils_h
|
||||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
|
||||
// Fix X11 header brain damage that conflicts with HeadersGuardEnum::None
|
||||
#undef None
|
||||
|
||||
#include "mozilla/dom/HeadersBinding.h"
|
||||
#include "mozilla/dom/RequestBinding.h"
|
||||
#include "mozilla/dom/ResponseBinding.h"
|
||||
|
||||
namespace IPC {
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::HeadersGuardEnum> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::HeadersGuardEnum,
|
||||
mozilla::dom::HeadersGuardEnum::None,
|
||||
mozilla::dom::HeadersGuardEnum::EndGuard_> {};
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::ReferrerPolicy> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::ReferrerPolicy,
|
||||
mozilla::dom::ReferrerPolicy::_empty,
|
||||
mozilla::dom::ReferrerPolicy::EndGuard_> {};
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::RequestMode> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::RequestMode,
|
||||
mozilla::dom::RequestMode::Same_origin,
|
||||
mozilla::dom::RequestMode::EndGuard_> {};
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::RequestCredentials> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::RequestCredentials,
|
||||
mozilla::dom::RequestCredentials::Omit,
|
||||
mozilla::dom::RequestCredentials::EndGuard_> {};
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::RequestCache> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::RequestCache,
|
||||
mozilla::dom::RequestCache::Default,
|
||||
mozilla::dom::RequestCache::EndGuard_> {};
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::RequestRedirect> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::RequestRedirect,
|
||||
mozilla::dom::RequestRedirect::Follow,
|
||||
mozilla::dom::RequestRedirect::EndGuard_> {};
|
||||
template<>
|
||||
struct ParamTraits<mozilla::dom::ResponseType> :
|
||||
public ContiguousEnumSerializer<mozilla::dom::ResponseType,
|
||||
mozilla::dom::ResponseType::Basic,
|
||||
mozilla::dom::ResponseType::EndGuard_> {};
|
||||
} // namespace IPC
|
||||
|
||||
#endif // mozilla_dom_fetch_IPCUtils_h
|
||||
61
dom/fetch/FetchTypes.ipdlh
Normal file
61
dom/fetch/FetchTypes.ipdlh
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* 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 IPCStream;
|
||||
include ChannelInfo;
|
||||
include PBackgroundSharedTypes;
|
||||
|
||||
using HeadersGuardEnum from "mozilla/dom/FetchIPCTypes.h";
|
||||
using ReferrerPolicy from "mozilla/dom/FetchIPCTypes.h";
|
||||
using RequestCredentials from "mozilla/dom/FetchIPCTypes.h";
|
||||
using RequestMode from "mozilla/dom/FetchIPCTypes.h";
|
||||
using ResponseType from "mozilla/dom/FetchIPCTypes.h";
|
||||
using RequestRedirect from "mozilla/dom/FetchIPCTypes.h";
|
||||
using RequestCache from "mozilla/dom/FetchIPCTypes.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct HeadersEntry
|
||||
{
|
||||
nsCString name;
|
||||
nsCString value;
|
||||
};
|
||||
|
||||
// Note, this does not yet serialize *all* of InternalRequest
|
||||
// Make sure that it contains the fields that you care about
|
||||
struct IPCInternalRequest
|
||||
{
|
||||
nsCString[] urls;
|
||||
nsCString method;
|
||||
HeadersEntry[] headers;
|
||||
HeadersGuardEnum headersGuard;
|
||||
nsString referrer;
|
||||
ReferrerPolicy referrerPolicy;
|
||||
RequestMode mode;
|
||||
RequestCredentials credentials;
|
||||
uint32_t contentPolicyType;
|
||||
RequestCache requestCache;
|
||||
RequestRedirect requestRedirect;
|
||||
};
|
||||
|
||||
// Note, this does not yet serialize *all* of InternalResponse
|
||||
// Make sure that it contains the fields that you care about
|
||||
struct IPCInternalResponse
|
||||
{
|
||||
ResponseType type;
|
||||
nsCString[] urlList;
|
||||
uint32_t status;
|
||||
nsCString statusText;
|
||||
HeadersEntry[] headers;
|
||||
HeadersGuardEnum headersGuard;
|
||||
IPCChannelInfo channelInfo;
|
||||
OptionalPrincipalInfo principalInfo;
|
||||
OptionalIPCStream body;
|
||||
int64_t bodySize;
|
||||
};
|
||||
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
114
dom/fetch/FetchUtil.cpp
Normal file
114
dom/fetch/FetchUtil.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#include "FetchUtil.h"
|
||||
|
||||
#include "nsError.h"
|
||||
#include "nsIUnicodeDecoder.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// static
|
||||
nsresult
|
||||
FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod)
|
||||
{
|
||||
nsAutoCString upperCaseMethod(aMethod);
|
||||
ToUpperCase(upperCaseMethod);
|
||||
if (!NS_IsValidHTTPToken(aMethod)) {
|
||||
outMethod.SetIsVoid(true);
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
}
|
||||
|
||||
if (upperCaseMethod.EqualsLiteral("CONNECT") ||
|
||||
upperCaseMethod.EqualsLiteral("TRACE") ||
|
||||
upperCaseMethod.EqualsLiteral("TRACK")) {
|
||||
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;
|
||||
}
|
||||
|
||||
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.
|
||||
static bool
|
||||
PushOverLine(nsACString::const_iterator& aStart,
|
||||
const nsACString::const_iterator& aEnd)
|
||||
{
|
||||
if (*aStart == nsCRT::CR && (aEnd - aStart > 1) && *(++aStart) == nsCRT::LF) {
|
||||
++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);
|
||||
|
||||
nsACString::const_iterator headerStart, iter, headerEnd;
|
||||
header.BeginReading(headerStart);
|
||||
header.EndReading(headerEnd);
|
||||
iter = headerStart;
|
||||
if (!FindCharInReadable(':', iter, headerEnd)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aHeaderName.Assign(StringHead(header, iter - headerStart));
|
||||
aHeaderName.CompressWhitespace();
|
||||
if (!NS_IsValidHTTPToken(aHeaderName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aHeaderValue.Assign(Substring(++iter, headerEnd));
|
||||
if (!NS_IsReasonableHTTPHeaderValue(aHeaderValue)) {
|
||||
return false;
|
||||
}
|
||||
aHeaderValue.CompressWhitespace();
|
||||
|
||||
return PushOverLine(aStart, aEnd);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
42
dom/fetch/FetchUtil.h
Normal file
42
dom/fetch/FetchUtil.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef mozilla_dom_FetchUtil_h
|
||||
#define mozilla_dom_FetchUtil_h
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsError.h"
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/FormData.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FetchUtil final
|
||||
{
|
||||
private:
|
||||
FetchUtil() = delete;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Sets outMethod to a valid HTTP request method string based on an input method.
|
||||
* Implements checks and normalization as specified by the Fetch specification.
|
||||
* Returns NS_ERROR_DOM_SECURITY_ERR if the method is invalid.
|
||||
* Otherwise returns NS_OK and the normalized method via outMethod.
|
||||
*/
|
||||
static nsresult
|
||||
GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod);
|
||||
|
||||
/**
|
||||
* Extracts an HTTP header from a substring range.
|
||||
*/
|
||||
static bool
|
||||
ExtractHeader(nsACString::const_iterator& aStart,
|
||||
nsACString::const_iterator& aEnd,
|
||||
nsCString& aHeaderName,
|
||||
nsCString& aHeaderValue,
|
||||
bool* aWasEmptyHeader);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
#endif
|
||||
97
dom/fetch/Headers.cpp
Normal file
97
dom/fetch/Headers.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/* -*- 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 "mozilla/dom/Headers.h"
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Headers)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Headers)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Headers, mOwner)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Headers)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
// static
|
||||
already_AddRefed<Headers>
|
||||
Headers::Constructor(const GlobalObject& aGlobal,
|
||||
const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<InternalHeaders> ih = new InternalHeaders();
|
||||
RefPtr<Headers> headers = new Headers(aGlobal.GetAsSupports(), ih);
|
||||
|
||||
if (!aInit.WasPassed()) {
|
||||
return headers.forget();
|
||||
}
|
||||
|
||||
if (aInit.Value().IsHeaders()) {
|
||||
ih->Fill(*aInit.Value().GetAsHeaders().mInternalHeaders, aRv);
|
||||
} else if (aInit.Value().IsByteStringSequenceSequence()) {
|
||||
ih->Fill(aInit.Value().GetAsByteStringSequenceSequence(), aRv);
|
||||
} else if (aInit.Value().IsByteStringMozMap()) {
|
||||
ih->Fill(aInit.Value().GetAsByteStringMozMap(), aRv);
|
||||
}
|
||||
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return headers.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Headers>
|
||||
Headers::Constructor(const GlobalObject& aGlobal,
|
||||
const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
return Create(global, aInit, aRv);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<Headers>
|
||||
Headers::Create(nsIGlobalObject* aGlobal,
|
||||
const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<InternalHeaders> ih = new InternalHeaders();
|
||||
RefPtr<Headers> headers = new Headers(aGlobal, ih);
|
||||
|
||||
if (aInit.IsHeaders()) {
|
||||
ih->Fill(*(aInit.GetAsHeaders().get()->mInternalHeaders), aRv);
|
||||
} else if (aInit.IsByteStringSequenceSequence()) {
|
||||
ih->Fill(aInit.GetAsByteStringSequenceSequence(), aRv);
|
||||
} else if (aInit.IsByteStringMozMap()) {
|
||||
ih->Fill(aInit.GetAsByteStringMozMap(), aRv);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return headers.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
Headers::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return mozilla::dom::HeadersBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
Headers::~Headers()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
144
dom/fetch/Headers.h
Normal file
144
dom/fetch/Headers.h
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_Headers_h
|
||||
#define mozilla_dom_Headers_h
|
||||
|
||||
#include "mozilla/dom/HeadersBinding.h"
|
||||
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
#include "InternalHeaders.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
template<typename T> class MozMap;
|
||||
class HeadersOrByteStringSequenceSequenceOrByteStringMozMap;
|
||||
class OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap;
|
||||
|
||||
/**
|
||||
* This Headers class is only used to represent the content facing Headers
|
||||
* object. It is actually backed by an InternalHeaders implementation. Gecko
|
||||
* code should NEVER use this, except in the Request and Response
|
||||
* implementations, where they must always be created from the backing
|
||||
* InternalHeaders object.
|
||||
*/
|
||||
class Headers final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Headers)
|
||||
|
||||
friend class Request;
|
||||
friend class Response;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
RefPtr<InternalHeaders> mInternalHeaders;
|
||||
|
||||
public:
|
||||
explicit Headers(nsISupports* aOwner, InternalHeaders* aInternalHeaders)
|
||||
: mOwner(aOwner)
|
||||
, mInternalHeaders(aInternalHeaders)
|
||||
{
|
||||
}
|
||||
|
||||
explicit Headers(const Headers& aOther) = delete;
|
||||
|
||||
static bool PrefEnabled(JSContext* cx, JSObject* obj);
|
||||
|
||||
static already_AddRefed<Headers>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<Headers>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<Headers>
|
||||
Create(nsIGlobalObject* aGlobalObject,
|
||||
const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void Append(const nsACString& aName, const nsACString& aValue,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
mInternalHeaders->Append(aName, aValue, aRv);
|
||||
}
|
||||
|
||||
void Delete(const nsACString& aName, ErrorResult& aRv)
|
||||
{
|
||||
mInternalHeaders->Delete(aName, aRv);
|
||||
}
|
||||
|
||||
void Get(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const
|
||||
{
|
||||
mInternalHeaders->Get(aName, aValue, aRv);
|
||||
}
|
||||
|
||||
void GetFirst(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const
|
||||
{
|
||||
mInternalHeaders->GetFirst(aName, aValue, aRv);
|
||||
}
|
||||
|
||||
bool Has(const nsACString& aName, ErrorResult& aRv) const
|
||||
{
|
||||
return mInternalHeaders->Has(aName, aRv);
|
||||
}
|
||||
|
||||
void Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv)
|
||||
{
|
||||
mInternalHeaders->Set(aName, aValue, aRv);
|
||||
}
|
||||
|
||||
uint32_t GetIterableLength() const
|
||||
{
|
||||
return mInternalHeaders->GetIterableLength();
|
||||
}
|
||||
const nsString GetKeyAtIndex(unsigned aIndex) const
|
||||
{
|
||||
return mInternalHeaders->GetKeyAtIndex(aIndex);
|
||||
}
|
||||
const nsString GetValueAtIndex(unsigned aIndex) const
|
||||
{
|
||||
return mInternalHeaders->GetValueAtIndex(aIndex);
|
||||
}
|
||||
|
||||
// ChromeOnly
|
||||
HeadersGuardEnum Guard() const
|
||||
{
|
||||
return mInternalHeaders->Guard();
|
||||
}
|
||||
|
||||
void SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv)
|
||||
{
|
||||
mInternalHeaders->SetGuard(aGuard, aRv);
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
|
||||
private:
|
||||
virtual ~Headers();
|
||||
|
||||
InternalHeaders*
|
||||
GetInternalHeaders() const
|
||||
{
|
||||
return mInternalHeaders;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_Headers_h
|
||||
423
dom/fetch/InternalHeaders.cpp
Normal file
423
dom/fetch/InternalHeaders.cpp
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
/* -*- 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 "mozilla/dom/InternalHeaders.h"
|
||||
|
||||
#include "mozilla/dom/FetchTypes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsReadableUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
InternalHeaders::InternalHeaders(const nsTArray<Entry>&& aHeaders,
|
||||
HeadersGuardEnum aGuard)
|
||||
: mGuard(aGuard)
|
||||
, mList(aHeaders)
|
||||
{
|
||||
}
|
||||
|
||||
InternalHeaders::InternalHeaders(const nsTArray<HeadersEntry>& aHeadersEntryList,
|
||||
HeadersGuardEnum aGuard)
|
||||
: mGuard(aGuard)
|
||||
{
|
||||
for (const HeadersEntry& headersEntry : aHeadersEntryList) {
|
||||
mList.AppendElement(Entry(headersEntry.name(), headersEntry.value()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::ToIPC(nsTArray<HeadersEntry>& aIPCHeaders,
|
||||
HeadersGuardEnum& aGuard)
|
||||
{
|
||||
aGuard = mGuard;
|
||||
|
||||
aIPCHeaders.Clear();
|
||||
for (Entry& entry : mList) {
|
||||
aIPCHeaders.AppendElement(HeadersEntry(entry.mName, entry.mValue));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::Append(const nsACString& aName, const nsACString& aValue,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsAutoCString lowerName;
|
||||
ToLowerCase(aName, lowerName);
|
||||
|
||||
if (IsInvalidMutableHeader(lowerName, aValue, aRv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mList.AppendElement(Entry(lowerName, aValue));
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::Delete(const nsACString& aName, ErrorResult& aRv)
|
||||
{
|
||||
nsAutoCString lowerName;
|
||||
ToLowerCase(aName, lowerName);
|
||||
|
||||
if (IsInvalidMutableHeader(lowerName, aRv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// remove in reverse order to minimize copying
|
||||
for (int32_t i = mList.Length() - 1; i >= 0; --i) {
|
||||
if (lowerName == mList[i].mName) {
|
||||
mList.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::Get(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const
|
||||
{
|
||||
nsAutoCString lowerName;
|
||||
ToLowerCase(aName, lowerName);
|
||||
|
||||
if (IsInvalidName(lowerName, aRv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char* delimiter = ",";
|
||||
bool firstValueFound = false;
|
||||
|
||||
for (uint32_t i = 0; i < mList.Length(); ++i) {
|
||||
if (lowerName == mList[i].mName) {
|
||||
if (firstValueFound) {
|
||||
aValue += delimiter;
|
||||
}
|
||||
aValue += mList[i].mValue;
|
||||
firstValueFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
// No value found, so return null to content
|
||||
if (!firstValueFound) {
|
||||
aValue.SetIsVoid(true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::GetFirst(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const
|
||||
{
|
||||
nsAutoCString lowerName;
|
||||
ToLowerCase(aName, lowerName);
|
||||
|
||||
if (IsInvalidName(lowerName, aRv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mList.Length(); ++i) {
|
||||
if (lowerName == mList[i].mName) {
|
||||
aValue = mList[i].mValue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No value found, so return null to content
|
||||
aValue.SetIsVoid(true);
|
||||
}
|
||||
|
||||
bool
|
||||
InternalHeaders::Has(const nsACString& aName, ErrorResult& aRv) const
|
||||
{
|
||||
nsAutoCString lowerName;
|
||||
ToLowerCase(aName, lowerName);
|
||||
|
||||
if (IsInvalidName(lowerName, aRv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mList.Length(); ++i) {
|
||||
if (lowerName == mList[i].mName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv)
|
||||
{
|
||||
nsAutoCString lowerName;
|
||||
ToLowerCase(aName, lowerName);
|
||||
|
||||
if (IsInvalidMutableHeader(lowerName, aValue, aRv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t firstIndex = INT32_MAX;
|
||||
|
||||
// remove in reverse order to minimize copying
|
||||
for (int32_t i = mList.Length() - 1; i >= 0; --i) {
|
||||
if (lowerName == mList[i].mName) {
|
||||
firstIndex = std::min(firstIndex, i);
|
||||
mList.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (firstIndex < INT32_MAX) {
|
||||
Entry* entry = mList.InsertElementAt(firstIndex);
|
||||
entry->mName = lowerName;
|
||||
entry->mValue = aValue;
|
||||
} else {
|
||||
mList.AppendElement(Entry(lowerName, aValue));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::Clear()
|
||||
{
|
||||
mList.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv)
|
||||
{
|
||||
// The guard is only checked during ::Set() and ::Append() in the spec. It
|
||||
// does not require revalidating headers already set.
|
||||
mGuard = aGuard;
|
||||
}
|
||||
|
||||
InternalHeaders::~InternalHeaders()
|
||||
{
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
InternalHeaders::IsSimpleHeader(const nsACString& aName, const nsACString& aValue)
|
||||
{
|
||||
// Note, we must allow a null content-type value here to support
|
||||
// get("content-type"), but the IsInvalidValue() check will prevent null
|
||||
// from being set or appended.
|
||||
return aName.EqualsLiteral("accept") ||
|
||||
aName.EqualsLiteral("accept-language") ||
|
||||
aName.EqualsLiteral("content-language") ||
|
||||
(aName.EqualsLiteral("content-type") &&
|
||||
nsContentUtils::IsAllowedNonCorsContentType(aValue));
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
InternalHeaders::IsRevalidationHeader(const nsACString& aName)
|
||||
{
|
||||
return aName.EqualsLiteral("if-modified-since") ||
|
||||
aName.EqualsLiteral("if-none-match") ||
|
||||
aName.EqualsLiteral("if-unmodified-since") ||
|
||||
aName.EqualsLiteral("if-match") ||
|
||||
aName.EqualsLiteral("if-range");
|
||||
}
|
||||
|
||||
//static
|
||||
bool
|
||||
InternalHeaders::IsInvalidName(const nsACString& aName, ErrorResult& aRv)
|
||||
{
|
||||
if (!NS_IsValidHTTPToken(aName)) {
|
||||
NS_ConvertUTF8toUTF16 label(aName);
|
||||
aRv.ThrowTypeError<MSG_INVALID_HEADER_NAME>(label);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
InternalHeaders::IsInvalidValue(const nsACString& aValue, ErrorResult& aRv)
|
||||
{
|
||||
if (!NS_IsReasonableHTTPHeaderValue(aValue)) {
|
||||
NS_ConvertUTF8toUTF16 label(aValue);
|
||||
aRv.ThrowTypeError<MSG_INVALID_HEADER_VALUE>(label);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
InternalHeaders::IsImmutable(ErrorResult& aRv) const
|
||||
{
|
||||
if (mGuard == HeadersGuardEnum::Immutable) {
|
||||
aRv.ThrowTypeError<MSG_HEADERS_IMMUTABLE>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
InternalHeaders::IsForbiddenRequestHeader(const nsACString& aName) const
|
||||
{
|
||||
return mGuard == HeadersGuardEnum::Request &&
|
||||
nsContentUtils::IsForbiddenRequestHeader(aName);
|
||||
}
|
||||
|
||||
bool
|
||||
InternalHeaders::IsForbiddenRequestNoCorsHeader(const nsACString& aName) const
|
||||
{
|
||||
return mGuard == HeadersGuardEnum::Request_no_cors &&
|
||||
!IsSimpleHeader(aName, EmptyCString());
|
||||
}
|
||||
|
||||
bool
|
||||
InternalHeaders::IsForbiddenRequestNoCorsHeader(const nsACString& aName,
|
||||
const nsACString& aValue) const
|
||||
{
|
||||
return mGuard == HeadersGuardEnum::Request_no_cors &&
|
||||
!IsSimpleHeader(aName, aValue);
|
||||
}
|
||||
|
||||
bool
|
||||
InternalHeaders::IsForbiddenResponseHeader(const nsACString& aName) const
|
||||
{
|
||||
return mGuard == HeadersGuardEnum::Response &&
|
||||
nsContentUtils::IsForbiddenResponseHeader(aName);
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::Fill(const InternalHeaders& aInit, ErrorResult& aRv)
|
||||
{
|
||||
const nsTArray<Entry>& list = aInit.mList;
|
||||
for (uint32_t i = 0; i < list.Length() && !aRv.Failed(); ++i) {
|
||||
const Entry& entry = list[i];
|
||||
Append(entry.mName, entry.mValue, aRv);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv)
|
||||
{
|
||||
for (uint32_t i = 0; i < aInit.Length() && !aRv.Failed(); ++i) {
|
||||
const Sequence<nsCString>& tuple = aInit[i];
|
||||
if (tuple.Length() != 2) {
|
||||
aRv.ThrowTypeError<MSG_INVALID_HEADER_SEQUENCE>();
|
||||
return;
|
||||
}
|
||||
Append(tuple[0], tuple[1], aRv);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv)
|
||||
{
|
||||
nsTArray<nsString> keys;
|
||||
aInit.GetKeys(keys);
|
||||
for (uint32_t i = 0; i < keys.Length() && !aRv.Failed(); ++i) {
|
||||
Append(NS_ConvertUTF16toUTF8(keys[i]), aInit.Get(keys[i]), aRv);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
InternalHeaders::HasOnlySimpleHeaders() const
|
||||
{
|
||||
for (uint32_t i = 0; i < mList.Length(); ++i) {
|
||||
if (!IsSimpleHeader(mList[i].mName, mList[i].mValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InternalHeaders::HasRevalidationHeaders() const
|
||||
{
|
||||
for (uint32_t i = 0; i < mList.Length(); ++i) {
|
||||
if (IsRevalidationHeader(mList[i].mName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<InternalHeaders>
|
||||
InternalHeaders::BasicHeaders(InternalHeaders* aHeaders)
|
||||
{
|
||||
RefPtr<InternalHeaders> basic = new InternalHeaders(*aHeaders);
|
||||
ErrorResult result;
|
||||
// The Set-Cookie headers cannot be invalid mutable headers, so the Delete
|
||||
// must succeed.
|
||||
basic->Delete(NS_LITERAL_CSTRING("Set-Cookie"), result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
basic->Delete(NS_LITERAL_CSTRING("Set-Cookie2"), result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
return basic.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<InternalHeaders>
|
||||
InternalHeaders::CORSHeaders(InternalHeaders* aHeaders)
|
||||
{
|
||||
RefPtr<InternalHeaders> cors = new InternalHeaders(aHeaders->mGuard);
|
||||
ErrorResult result;
|
||||
|
||||
nsAutoCString acExposedNames;
|
||||
aHeaders->GetFirst(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"), acExposedNames, result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
|
||||
AutoTArray<nsCString, 5> exposeNamesArray;
|
||||
nsCCharSeparatedTokenizer exposeTokens(acExposedNames, ',');
|
||||
while (exposeTokens.hasMoreTokens()) {
|
||||
const nsDependentCSubstring& token = exposeTokens.nextToken();
|
||||
if (token.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!NS_IsValidHTTPToken(token)) {
|
||||
NS_WARNING("Got invalid HTTP token in Access-Control-Expose-Headers. Header value is:");
|
||||
NS_WARNING(acExposedNames.get());
|
||||
exposeNamesArray.Clear();
|
||||
break;
|
||||
}
|
||||
|
||||
exposeNamesArray.AppendElement(token);
|
||||
}
|
||||
|
||||
nsCaseInsensitiveCStringArrayComparator comp;
|
||||
for (uint32_t i = 0; i < aHeaders->mList.Length(); ++i) {
|
||||
const Entry& entry = aHeaders->mList[i];
|
||||
if (entry.mName.EqualsASCII("cache-control") ||
|
||||
entry.mName.EqualsASCII("content-language") ||
|
||||
entry.mName.EqualsASCII("content-type") ||
|
||||
entry.mName.EqualsASCII("expires") ||
|
||||
entry.mName.EqualsASCII("last-modified") ||
|
||||
entry.mName.EqualsASCII("pragma") ||
|
||||
exposeNamesArray.Contains(entry.mName, comp)) {
|
||||
cors->Append(entry.mName, entry.mValue, result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
}
|
||||
}
|
||||
|
||||
return cors.forget();
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::GetEntries(nsTArray<InternalHeaders::Entry>& aEntries) const
|
||||
{
|
||||
MOZ_ASSERT(aEntries.IsEmpty());
|
||||
aEntries.AppendElements(mList);
|
||||
}
|
||||
|
||||
void
|
||||
InternalHeaders::GetUnsafeHeaders(nsTArray<nsCString>& aNames) const
|
||||
{
|
||||
MOZ_ASSERT(aNames.IsEmpty());
|
||||
for (uint32_t i = 0; i < mList.Length(); ++i) {
|
||||
const Entry& header = mList[i];
|
||||
if (!InternalHeaders::IsSimpleHeader(header.mName, header.mValue)) {
|
||||
aNames.AppendElement(header.mName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
160
dom/fetch/InternalHeaders.h
Normal file
160
dom/fetch/InternalHeaders.h
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_InternalHeaders_h
|
||||
#define mozilla_dom_InternalHeaders_h
|
||||
|
||||
// needed for HeadersGuardEnum.
|
||||
#include "mozilla/dom/HeadersBinding.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
template<typename T> class MozMap;
|
||||
class HeadersEntry;
|
||||
|
||||
class InternalHeaders final
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalHeaders)
|
||||
|
||||
public:
|
||||
struct Entry
|
||||
{
|
||||
Entry(const nsACString& aName, const nsACString& aValue)
|
||||
: mName(aName)
|
||||
, mValue(aValue)
|
||||
{ }
|
||||
|
||||
Entry() { }
|
||||
|
||||
nsCString mName;
|
||||
nsCString mValue;
|
||||
};
|
||||
|
||||
private:
|
||||
HeadersGuardEnum mGuard;
|
||||
nsTArray<Entry> mList;
|
||||
|
||||
public:
|
||||
explicit InternalHeaders(HeadersGuardEnum aGuard = HeadersGuardEnum::None)
|
||||
: mGuard(aGuard)
|
||||
{
|
||||
}
|
||||
|
||||
explicit InternalHeaders(const InternalHeaders& aOther)
|
||||
: mGuard(HeadersGuardEnum::None)
|
||||
{
|
||||
ErrorResult result;
|
||||
Fill(aOther, result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
// Note that it's important to set the guard after Fill(), to make sure
|
||||
// that Fill() doesn't fail if aOther is immutable.
|
||||
mGuard = aOther.mGuard;
|
||||
}
|
||||
|
||||
explicit InternalHeaders(const nsTArray<Entry>&& aHeaders,
|
||||
HeadersGuardEnum aGuard = HeadersGuardEnum::None);
|
||||
|
||||
InternalHeaders(const nsTArray<HeadersEntry>& aHeadersEntryList,
|
||||
HeadersGuardEnum aGuard);
|
||||
|
||||
void ToIPC(nsTArray<HeadersEntry>& aIPCHeaders,
|
||||
HeadersGuardEnum& aGuard);
|
||||
|
||||
void Append(const nsACString& aName, const nsACString& aValue,
|
||||
ErrorResult& aRv);
|
||||
void Delete(const nsACString& aName, ErrorResult& aRv);
|
||||
void Get(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const;
|
||||
void GetFirst(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const;
|
||||
bool Has(const nsACString& aName, ErrorResult& aRv) const;
|
||||
void Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv);
|
||||
|
||||
uint32_t GetIterableLength() const
|
||||
{
|
||||
return mList.Length();
|
||||
}
|
||||
const NS_ConvertASCIItoUTF16 GetKeyAtIndex(unsigned aIndex) const
|
||||
{
|
||||
MOZ_ASSERT(aIndex < mList.Length());
|
||||
return NS_ConvertASCIItoUTF16(mList[aIndex].mName);
|
||||
}
|
||||
const NS_ConvertASCIItoUTF16 GetValueAtIndex(unsigned aIndex) const
|
||||
{
|
||||
MOZ_ASSERT(aIndex < mList.Length());
|
||||
return NS_ConvertASCIItoUTF16(mList[aIndex].mValue);
|
||||
}
|
||||
|
||||
void Clear();
|
||||
|
||||
HeadersGuardEnum Guard() const { return mGuard; }
|
||||
void SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv);
|
||||
|
||||
void Fill(const InternalHeaders& aInit, ErrorResult& aRv);
|
||||
void Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv);
|
||||
void Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv);
|
||||
|
||||
bool HasOnlySimpleHeaders() const;
|
||||
|
||||
bool HasRevalidationHeaders() const;
|
||||
|
||||
static already_AddRefed<InternalHeaders>
|
||||
BasicHeaders(InternalHeaders* aHeaders);
|
||||
|
||||
static already_AddRefed<InternalHeaders>
|
||||
CORSHeaders(InternalHeaders* aHeaders);
|
||||
|
||||
void
|
||||
GetEntries(nsTArray<InternalHeaders::Entry>& aEntries) const;
|
||||
|
||||
void
|
||||
GetUnsafeHeaders(nsTArray<nsCString>& aNames) const;
|
||||
private:
|
||||
virtual ~InternalHeaders();
|
||||
|
||||
static bool IsInvalidName(const nsACString& aName, ErrorResult& aRv);
|
||||
static bool IsInvalidValue(const nsACString& aValue, ErrorResult& aRv);
|
||||
bool IsImmutable(ErrorResult& aRv) const;
|
||||
bool IsForbiddenRequestHeader(const nsACString& aName) const;
|
||||
bool IsForbiddenRequestNoCorsHeader(const nsACString& aName) const;
|
||||
bool IsForbiddenRequestNoCorsHeader(const nsACString& aName,
|
||||
const nsACString& aValue) const;
|
||||
bool IsForbiddenResponseHeader(const nsACString& aName) const;
|
||||
|
||||
bool IsInvalidMutableHeader(const nsACString& aName,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
return IsInvalidMutableHeader(aName, EmptyCString(), aRv);
|
||||
}
|
||||
|
||||
bool IsInvalidMutableHeader(const nsACString& aName,
|
||||
const nsACString& aValue,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
return IsInvalidName(aName, aRv) ||
|
||||
IsInvalidValue(aValue, aRv) ||
|
||||
IsImmutable(aRv) ||
|
||||
IsForbiddenRequestHeader(aName) ||
|
||||
IsForbiddenRequestNoCorsHeader(aName, aValue) ||
|
||||
IsForbiddenResponseHeader(aName);
|
||||
}
|
||||
|
||||
static bool IsSimpleHeader(const nsACString& aName,
|
||||
const nsACString& aValue);
|
||||
|
||||
static bool IsRevalidationHeader(const nsACString& aName);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_InternalHeaders_h
|
||||
495
dom/fetch/InternalRequest.cpp
Normal file
495
dom/fetch/InternalRequest.cpp
Normal file
|
|
@ -0,0 +1,495 @@
|
|||
/* -*- 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 "InternalRequest.h"
|
||||
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsStreamUtils.h"
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/FetchTypes.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/workers/Workers.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
// The global is used to extract the principal.
|
||||
already_AddRefed<InternalRequest>
|
||||
InternalRequest::GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mURLList.IsEmpty(), "Internal Request's urlList should not be empty when copied from constructor.");
|
||||
RefPtr<InternalRequest> copy = new InternalRequest(mURLList.LastElement(),
|
||||
mFragment);
|
||||
copy->SetMethod(mMethod);
|
||||
copy->mHeaders = new InternalHeaders(*mHeaders);
|
||||
copy->SetUnsafeRequest();
|
||||
copy->mBodyStream = mBodyStream;
|
||||
copy->mForceOriginHeader = true;
|
||||
// The "client" is not stored in our implementation. Fetch API users should
|
||||
// use the appropriate window/document/principal and other Gecko security
|
||||
// mechanisms as appropriate.
|
||||
copy->mSameOriginDataURL = true;
|
||||
copy->mPreserveContentCodings = true;
|
||||
copy->mReferrer = mReferrer;
|
||||
copy->mReferrerPolicy = mReferrerPolicy;
|
||||
copy->mEnvironmentReferrerPolicy = mEnvironmentReferrerPolicy;
|
||||
copy->mIntegrity = mIntegrity;
|
||||
|
||||
copy->mContentPolicyType = mContentPolicyTypeOverridden ?
|
||||
mContentPolicyType :
|
||||
nsIContentPolicy::TYPE_FETCH;
|
||||
copy->mMode = mMode;
|
||||
copy->mCredentialsMode = mCredentialsMode;
|
||||
copy->mCacheMode = mCacheMode;
|
||||
copy->mRedirectMode = mRedirectMode;
|
||||
copy->mCreatedByFetchEvent = mCreatedByFetchEvent;
|
||||
copy->mContentPolicyTypeOverridden = mContentPolicyTypeOverridden;
|
||||
return copy.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<InternalRequest>
|
||||
InternalRequest::Clone()
|
||||
{
|
||||
RefPtr<InternalRequest> clone = new InternalRequest(*this);
|
||||
|
||||
if (!mBodyStream) {
|
||||
return clone.forget();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> clonedBody;
|
||||
nsCOMPtr<nsIInputStream> replacementBody;
|
||||
|
||||
nsresult rv = NS_CloneInputStream(mBodyStream, getter_AddRefs(clonedBody),
|
||||
getter_AddRefs(replacementBody));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; }
|
||||
|
||||
clone->mBodyStream.swap(clonedBody);
|
||||
if (replacementBody) {
|
||||
mBodyStream.swap(replacementBody);
|
||||
}
|
||||
return clone.forget();
|
||||
}
|
||||
InternalRequest::InternalRequest(const nsACString& aURL,
|
||||
const nsACString& aFragment)
|
||||
: mMethod("GET")
|
||||
, mHeaders(new InternalHeaders(HeadersGuardEnum::None))
|
||||
, mContentPolicyType(nsIContentPolicy::TYPE_FETCH)
|
||||
, mReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR))
|
||||
, mReferrerPolicy(ReferrerPolicy::_empty)
|
||||
, mEnvironmentReferrerPolicy(net::RP_Default)
|
||||
, mMode(RequestMode::No_cors)
|
||||
, mCredentialsMode(RequestCredentials::Omit)
|
||||
, mResponseTainting(LoadTainting::Basic)
|
||||
, mCacheMode(RequestCache::Default)
|
||||
, mRedirectMode(RequestRedirect::Follow)
|
||||
, mAuthenticationFlag(false)
|
||||
, mForceOriginHeader(false)
|
||||
, mPreserveContentCodings(false)
|
||||
// FIXME(nsm): This should be false by default, but will lead to the
|
||||
// algorithm never loading data: URLs right now. See Bug 1018872 about
|
||||
// how certain contexts will override it to set it to true. Fetch
|
||||
// specification does not handle this yet.
|
||||
, mSameOriginDataURL(true)
|
||||
, mSkipServiceWorker(false)
|
||||
, mSynchronous(false)
|
||||
, mUnsafeRequest(false)
|
||||
, mUseURLCredentials(false)
|
||||
{
|
||||
MOZ_ASSERT(!aURL.IsEmpty());
|
||||
AddURL(aURL, aFragment);
|
||||
}
|
||||
InternalRequest::InternalRequest(const nsACString& aURL,
|
||||
const nsACString& aFragment,
|
||||
const nsACString& aMethod,
|
||||
already_AddRefed<InternalHeaders> aHeaders,
|
||||
RequestCache aCacheMode,
|
||||
RequestMode aMode,
|
||||
RequestRedirect aRequestRedirect,
|
||||
RequestCredentials aRequestCredentials,
|
||||
const nsAString& aReferrer,
|
||||
ReferrerPolicy aReferrerPolicy,
|
||||
nsContentPolicyType aContentPolicyType,
|
||||
const nsAString& aIntegrity)
|
||||
: mMethod(aMethod)
|
||||
, mHeaders(aHeaders)
|
||||
, mContentPolicyType(aContentPolicyType)
|
||||
, mReferrer(aReferrer)
|
||||
, mReferrerPolicy(aReferrerPolicy)
|
||||
, mEnvironmentReferrerPolicy(net::RP_Default)
|
||||
, mMode(aMode)
|
||||
, mCredentialsMode(aRequestCredentials)
|
||||
, mResponseTainting(LoadTainting::Basic)
|
||||
, mCacheMode(aCacheMode)
|
||||
, mRedirectMode(aRequestRedirect)
|
||||
, mIntegrity(aIntegrity)
|
||||
, mAuthenticationFlag(false)
|
||||
, mForceOriginHeader(false)
|
||||
, mPreserveContentCodings(false)
|
||||
// FIXME See the above comment in the default constructor.
|
||||
, mSameOriginDataURL(true)
|
||||
, mSkipServiceWorker(false)
|
||||
, mSynchronous(false)
|
||||
, mUnsafeRequest(false)
|
||||
, mUseURLCredentials(false)
|
||||
{
|
||||
MOZ_ASSERT(!aURL.IsEmpty());
|
||||
AddURL(aURL, aFragment);
|
||||
}
|
||||
InternalRequest::InternalRequest(const InternalRequest& aOther)
|
||||
: mMethod(aOther.mMethod)
|
||||
, mURLList(aOther.mURLList)
|
||||
, mHeaders(new InternalHeaders(*aOther.mHeaders))
|
||||
, mContentPolicyType(aOther.mContentPolicyType)
|
||||
, mReferrer(aOther.mReferrer)
|
||||
, mReferrerPolicy(aOther.mReferrerPolicy)
|
||||
, mEnvironmentReferrerPolicy(aOther.mEnvironmentReferrerPolicy)
|
||||
, mMode(aOther.mMode)
|
||||
, mCredentialsMode(aOther.mCredentialsMode)
|
||||
, mResponseTainting(aOther.mResponseTainting)
|
||||
, mCacheMode(aOther.mCacheMode)
|
||||
, mRedirectMode(aOther.mRedirectMode)
|
||||
, mIntegrity(aOther.mIntegrity)
|
||||
, mFragment(aOther.mFragment)
|
||||
, mAuthenticationFlag(aOther.mAuthenticationFlag)
|
||||
, mForceOriginHeader(aOther.mForceOriginHeader)
|
||||
, mPreserveContentCodings(aOther.mPreserveContentCodings)
|
||||
, mSameOriginDataURL(aOther.mSameOriginDataURL)
|
||||
, mSkipServiceWorker(aOther.mSkipServiceWorker)
|
||||
, mSynchronous(aOther.mSynchronous)
|
||||
, mUnsafeRequest(aOther.mUnsafeRequest)
|
||||
, mUseURLCredentials(aOther.mUseURLCredentials)
|
||||
, mCreatedByFetchEvent(aOther.mCreatedByFetchEvent)
|
||||
, mContentPolicyTypeOverridden(aOther.mContentPolicyTypeOverridden)
|
||||
{
|
||||
// NOTE: does not copy body stream... use the fallible Clone() for that
|
||||
}
|
||||
|
||||
InternalRequest::InternalRequest(const IPCInternalRequest& aIPCRequest)
|
||||
: mMethod(aIPCRequest.method())
|
||||
, mURLList(aIPCRequest.urls())
|
||||
, mHeaders(new InternalHeaders(aIPCRequest.headers(),
|
||||
aIPCRequest.headersGuard()))
|
||||
, mContentPolicyType(aIPCRequest.contentPolicyType())
|
||||
, mReferrer(aIPCRequest.referrer())
|
||||
, mReferrerPolicy(aIPCRequest.referrerPolicy())
|
||||
, mMode(aIPCRequest.mode())
|
||||
, mCredentialsMode(aIPCRequest.credentials())
|
||||
, mCacheMode(aIPCRequest.requestCache())
|
||||
, mRedirectMode(aIPCRequest.requestRedirect())
|
||||
{
|
||||
MOZ_ASSERT(!mURLList.IsEmpty());
|
||||
}
|
||||
|
||||
InternalRequest::~InternalRequest()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
InternalRequest::ToIPC(IPCInternalRequest* aIPCRequest)
|
||||
{
|
||||
MOZ_ASSERT(aIPCRequest);
|
||||
MOZ_ASSERT(!mURLList.IsEmpty());
|
||||
aIPCRequest->urls() = mURLList;
|
||||
aIPCRequest->method() = mMethod;
|
||||
|
||||
mHeaders->ToIPC(aIPCRequest->headers(), aIPCRequest->headersGuard());
|
||||
|
||||
aIPCRequest->referrer() = mReferrer;
|
||||
aIPCRequest->referrerPolicy() = mReferrerPolicy;
|
||||
aIPCRequest->mode() = mMode;
|
||||
aIPCRequest->credentials() = mCredentialsMode;
|
||||
aIPCRequest->contentPolicyType() = mContentPolicyType;
|
||||
aIPCRequest->requestCache() = mCacheMode;
|
||||
aIPCRequest->requestRedirect() = mRedirectMode;
|
||||
}
|
||||
|
||||
void
|
||||
InternalRequest::SetContentPolicyType(nsContentPolicyType aContentPolicyType)
|
||||
{
|
||||
mContentPolicyType = aContentPolicyType;
|
||||
}
|
||||
|
||||
void
|
||||
InternalRequest::OverrideContentPolicyType(nsContentPolicyType aContentPolicyType)
|
||||
{
|
||||
SetContentPolicyType(aContentPolicyType);
|
||||
mContentPolicyTypeOverridden = true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
RequestContext
|
||||
InternalRequest::MapContentPolicyTypeToRequestContext(nsContentPolicyType aContentPolicyType)
|
||||
{
|
||||
RequestContext context = RequestContext::Internal;
|
||||
switch (aContentPolicyType) {
|
||||
case nsIContentPolicy::TYPE_OTHER:
|
||||
context = RequestContext::Internal;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
|
||||
context = RequestContext::Script;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_WORKER:
|
||||
context = RequestContext::Worker;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
|
||||
context = RequestContext::Sharedworker;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_IMAGE:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON:
|
||||
context = RequestContext::Image;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD:
|
||||
context = RequestContext::Style;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
|
||||
context = RequestContext::Object;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_EMBED:
|
||||
context = RequestContext::Embed;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_DOCUMENT:
|
||||
context = RequestContext::Internal;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_IFRAME:
|
||||
context = RequestContext::Iframe;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_FRAME:
|
||||
context = RequestContext::Frame;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_REFRESH:
|
||||
context = RequestContext::Internal;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_XBL:
|
||||
context = RequestContext::Internal;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_PING:
|
||||
context = RequestContext::Ping;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST:
|
||||
context = RequestContext::Xmlhttprequest;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE:
|
||||
context = RequestContext::Eventsource;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST:
|
||||
context = RequestContext::Plugin;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_DTD:
|
||||
context = RequestContext::Internal;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_FONT:
|
||||
context = RequestContext::Font;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_AUDIO:
|
||||
context = RequestContext::Audio;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_VIDEO:
|
||||
context = RequestContext::Video;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_TRACK:
|
||||
context = RequestContext::Track;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_WEBSOCKET:
|
||||
context = RequestContext::Internal;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_CSP_REPORT:
|
||||
context = RequestContext::Cspreport;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_XSLT:
|
||||
context = RequestContext::Xslt;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_BEACON:
|
||||
context = RequestContext::Beacon;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_FETCH:
|
||||
context = RequestContext::Fetch;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_IMAGESET:
|
||||
context = RequestContext::Imageset;
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_WEB_MANIFEST:
|
||||
context = RequestContext::Manifest;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false, "Unhandled nsContentPolicyType value");
|
||||
break;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
InternalRequest::IsNavigationContentPolicy(nsContentPolicyType aContentPolicyType)
|
||||
{
|
||||
// https://fetch.spec.whatwg.org/#navigation-request-context
|
||||
//
|
||||
// A navigation request context is one of "form", "frame", "hyperlink",
|
||||
// "iframe", "internal" (as long as context frame type is not "none"),
|
||||
// "location", "metarefresh", and "prerender".
|
||||
//
|
||||
// Note, all of these request types are effectively initiated by nsDocShell.
|
||||
//
|
||||
// The TYPE_REFRESH is used in some code paths for metarefresh, but will not
|
||||
// be seen during the actual load. Instead the new load gets a normal
|
||||
// nsDocShell policy type. We include it here in case this utility method
|
||||
// is called before the load starts.
|
||||
return aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
|
||||
aContentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT ||
|
||||
aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
|
||||
aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
|
||||
aContentPolicyType == nsIContentPolicy::TYPE_REFRESH;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
InternalRequest::IsWorkerContentPolicy(nsContentPolicyType aContentPolicyType)
|
||||
{
|
||||
// https://fetch.spec.whatwg.org/#worker-request-context
|
||||
//
|
||||
// A worker request context is one of "serviceworker", "sharedworker", and
|
||||
// "worker".
|
||||
//
|
||||
// Note, service workers are not included here because currently there is
|
||||
// no way to generate a Request with a "serviceworker" RequestContext.
|
||||
// ServiceWorker scripts cannot be intercepted.
|
||||
return aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
|
||||
aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
|
||||
}
|
||||
|
||||
bool
|
||||
InternalRequest::IsNavigationRequest() const
|
||||
{
|
||||
return IsNavigationContentPolicy(mContentPolicyType);
|
||||
}
|
||||
|
||||
bool
|
||||
InternalRequest::IsWorkerRequest() const
|
||||
{
|
||||
return IsWorkerContentPolicy(mContentPolicyType);
|
||||
}
|
||||
|
||||
bool
|
||||
InternalRequest::IsClientRequest() const
|
||||
{
|
||||
return IsNavigationRequest() || IsWorkerRequest();
|
||||
}
|
||||
|
||||
// static
|
||||
RequestMode
|
||||
InternalRequest::MapChannelToRequestMode(nsIChannel* aChannel)
|
||||
{
|
||||
MOZ_ASSERT(aChannel);
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
MOZ_ALWAYS_SUCCEEDS(aChannel->GetLoadInfo(getter_AddRefs(loadInfo)));
|
||||
|
||||
nsContentPolicyType contentPolicy = loadInfo->InternalContentPolicyType();
|
||||
if (IsNavigationContentPolicy(contentPolicy)) {
|
||||
return RequestMode::Navigate;
|
||||
}
|
||||
|
||||
// TODO: remove the worker override once securityMode is fully implemented (bug 1189945)
|
||||
if (IsWorkerContentPolicy(contentPolicy)) {
|
||||
return RequestMode::Same_origin;
|
||||
}
|
||||
|
||||
uint32_t securityMode = loadInfo->GetSecurityMode();
|
||||
|
||||
switch(securityMode) {
|
||||
case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS:
|
||||
case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED:
|
||||
return RequestMode::Same_origin;
|
||||
case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS:
|
||||
case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL:
|
||||
return RequestMode::No_cors;
|
||||
case nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS:
|
||||
// TODO: Check additional flag force-preflight after bug 1199693 (bug 1189945)
|
||||
return RequestMode::Cors;
|
||||
default:
|
||||
// TODO: assert never reached after CorsMode flag removed (bug 1189945)
|
||||
MOZ_ASSERT(securityMode == nsILoadInfo::SEC_NORMAL);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: remove following code once securityMode is fully implemented (bug 1189945)
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aChannel);
|
||||
|
||||
uint32_t corsMode;
|
||||
MOZ_ALWAYS_SUCCEEDS(httpChannel->GetCorsMode(&corsMode));
|
||||
MOZ_ASSERT(corsMode != nsIHttpChannelInternal::CORS_MODE_NAVIGATE);
|
||||
|
||||
// This cast is valid due to static asserts in ServiceWorkerManager.cpp.
|
||||
return static_cast<RequestMode>(corsMode);
|
||||
}
|
||||
|
||||
// static
|
||||
RequestCredentials
|
||||
InternalRequest::MapChannelToRequestCredentials(nsIChannel* aChannel)
|
||||
{
|
||||
MOZ_ASSERT(aChannel);
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
MOZ_ALWAYS_SUCCEEDS(aChannel->GetLoadInfo(getter_AddRefs(loadInfo)));
|
||||
|
||||
|
||||
// TODO: Remove following code after stylesheet and image support cookie policy
|
||||
if (loadInfo->GetSecurityMode() == nsILoadInfo::SEC_NORMAL) {
|
||||
uint32_t loadFlags;
|
||||
aChannel->GetLoadFlags(&loadFlags);
|
||||
|
||||
if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
|
||||
return RequestCredentials::Omit;
|
||||
} else {
|
||||
bool includeCrossOrigin;
|
||||
nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(aChannel);
|
||||
|
||||
internalChannel->GetCorsIncludeCredentials(&includeCrossOrigin);
|
||||
if (includeCrossOrigin) {
|
||||
return RequestCredentials::Include;
|
||||
}
|
||||
}
|
||||
return RequestCredentials::Same_origin;
|
||||
}
|
||||
|
||||
uint32_t cookiePolicy = loadInfo->GetCookiePolicy();
|
||||
|
||||
if (cookiePolicy == nsILoadInfo::SEC_COOKIES_INCLUDE) {
|
||||
return RequestCredentials::Include;
|
||||
} else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_OMIT) {
|
||||
return RequestCredentials::Omit;
|
||||
} else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) {
|
||||
return RequestCredentials::Same_origin;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected cookie policy!");
|
||||
return RequestCredentials::Same_origin;
|
||||
}
|
||||
|
||||
void
|
||||
InternalRequest::MaybeSkipCacheIfPerformingRevalidation()
|
||||
{
|
||||
if (mCacheMode == RequestCache::Default &&
|
||||
mHeaders->HasRevalidationHeaders()) {
|
||||
mCacheMode = RequestCache::No_store;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InternalRequest::SetPrincipalInfo(UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo)
|
||||
{
|
||||
mPrincipalInfo = Move(aPrincipalInfo);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
536
dom/fetch/InternalRequest.h
Normal file
536
dom/fetch/InternalRequest.h
Normal file
|
|
@ -0,0 +1,536 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_InternalRequest_h
|
||||
#define mozilla_dom_InternalRequest_h
|
||||
|
||||
#include "mozilla/dom/HeadersBinding.h"
|
||||
#include "mozilla/dom/InternalHeaders.h"
|
||||
#include "mozilla/dom/RequestBinding.h"
|
||||
#include "mozilla/LoadTainting.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#ifdef DEBUG
|
||||
#include "nsIURLParser.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace ipc {
|
||||
class PrincipalInfo;
|
||||
} // namespace ipc
|
||||
|
||||
namespace dom {
|
||||
|
||||
/*
|
||||
* The mapping of RequestContext and nsContentPolicyType is currently as the
|
||||
* following. Note that this mapping is not perfect yet (see the TODO comments
|
||||
* below for examples).
|
||||
*
|
||||
* RequestContext | nsContentPolicyType
|
||||
* ------------------+--------------------
|
||||
* audio | TYPE_INTERNAL_AUDIO
|
||||
* beacon | TYPE_BEACON
|
||||
* cspreport | TYPE_CSP_REPORT
|
||||
* download |
|
||||
* embed | TYPE_INTERNAL_EMBED
|
||||
* eventsource |
|
||||
* favicon |
|
||||
* fetch | TYPE_FETCH
|
||||
* font | TYPE_FONT
|
||||
* form |
|
||||
* frame | TYPE_INTERNAL_FRAME
|
||||
* hyperlink |
|
||||
* iframe | TYPE_INTERNAL_IFRAME
|
||||
* image | TYPE_INTERNAL_IMAGE, TYPE_INTERNAL_IMAGE_PRELOAD, TYPE_INTERNAL_IMAGE_FAVICON
|
||||
* imageset | TYPE_IMAGESET
|
||||
* import | Not supported by Gecko
|
||||
* internal | TYPE_DOCUMENT, TYPE_XBL, TYPE_OTHER
|
||||
* location |
|
||||
* manifest | TYPE_WEB_MANIFEST
|
||||
* object | TYPE_INTERNAL_OBJECT
|
||||
* ping | TYPE_PING
|
||||
* plugin | TYPE_OBJECT_SUBREQUEST
|
||||
* prefetch |
|
||||
* script | TYPE_INTERNAL_SCRIPT, TYPE_INTERNAL_SCRIPT_PRELOAD
|
||||
* sharedworker | TYPE_INTERNAL_SHARED_WORKER
|
||||
* subresource | Not supported by Gecko
|
||||
* style | TYPE_INTERNAL_STYLESHEET, TYPE_INTERNAL_STYLESHEET_PRELOAD
|
||||
* track | TYPE_INTERNAL_TRACK
|
||||
* video | TYPE_INTERNAL_VIDEO
|
||||
* worker | TYPE_INTERNAL_WORKER
|
||||
* xmlhttprequest | TYPE_INTERNAL_XMLHTTPREQUEST
|
||||
* eventsource | TYPE_INTERNAL_EVENTSOURCE
|
||||
* xslt | TYPE_XSLT
|
||||
*
|
||||
* TODO: Figure out if TYPE_REFRESH maps to anything useful
|
||||
* TODO: Figure out if TYPE_DTD maps to anything useful
|
||||
* TODO: Figure out if TYPE_WEBSOCKET maps to anything useful
|
||||
* TODO: Add a content type for prefetch
|
||||
* TODO: Use the content type for manifest when it becomes available
|
||||
* TODO: Add a content type for location
|
||||
* TODO: Add a content type for hyperlink
|
||||
* TODO: Add a content type for form
|
||||
* TODO: Add a content type for favicon
|
||||
* TODO: Add a content type for download
|
||||
*/
|
||||
|
||||
class Request;
|
||||
class IPCInternalRequest;
|
||||
|
||||
#define kFETCH_CLIENT_REFERRER_STR "about:client"
|
||||
class InternalRequest final
|
||||
{
|
||||
friend class Request;
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalRequest)
|
||||
InternalRequest(const nsACString& aURL, const nsACString& aFragment);
|
||||
InternalRequest(const nsACString& aURL,
|
||||
const nsACString& aFragment,
|
||||
const nsACString& aMethod,
|
||||
already_AddRefed<InternalHeaders> aHeaders,
|
||||
RequestCache aCacheMode,
|
||||
RequestMode aMode,
|
||||
RequestRedirect aRequestRedirect,
|
||||
RequestCredentials aRequestCredentials,
|
||||
const nsAString& aReferrer,
|
||||
ReferrerPolicy aReferrerPolicy,
|
||||
nsContentPolicyType aContentPolicyType,
|
||||
const nsAString& aIntegrity);
|
||||
|
||||
explicit InternalRequest(const IPCInternalRequest& aIPCRequest);
|
||||
|
||||
void ToIPC(IPCInternalRequest* aIPCRequest);
|
||||
|
||||
already_AddRefed<InternalRequest> Clone();
|
||||
|
||||
void
|
||||
GetMethod(nsCString& aMethod) const
|
||||
{
|
||||
aMethod.Assign(mMethod);
|
||||
}
|
||||
|
||||
void
|
||||
SetMethod(const nsACString& aMethod)
|
||||
{
|
||||
mMethod.Assign(aMethod);
|
||||
}
|
||||
|
||||
bool
|
||||
HasSimpleMethod() const
|
||||
{
|
||||
return mMethod.LowerCaseEqualsASCII("get") ||
|
||||
mMethod.LowerCaseEqualsASCII("post") ||
|
||||
mMethod.LowerCaseEqualsASCII("head");
|
||||
}
|
||||
// GetURL should get the request's current url with fragment. A request has
|
||||
// an associated current url. It is a pointer to the last fetch URL in
|
||||
// request's url list.
|
||||
void
|
||||
GetURL(nsACString& aURL) const
|
||||
{
|
||||
aURL.Assign(GetURLWithoutFragment());
|
||||
if (GetFragment().IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
aURL.Append(NS_LITERAL_CSTRING("#"));
|
||||
aURL.Append(GetFragment());
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
GetURLWithoutFragment() const
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mURLList.IsEmpty(),
|
||||
"Internal Request's urlList should not be empty.");
|
||||
|
||||
return mURLList.LastElement();
|
||||
}
|
||||
// AddURL should append the url into url list.
|
||||
// Normally we strip the fragment from the URL in Request::Constructor and
|
||||
// pass the fragment as the second argument into it.
|
||||
// If a fragment is present in the URL it must be stripped and passed in
|
||||
// separately.
|
||||
void
|
||||
AddURL(const nsACString& aURL, const nsACString& aFragment)
|
||||
{
|
||||
MOZ_ASSERT(!aURL.IsEmpty());
|
||||
MOZ_ASSERT(!aURL.Contains('#'));
|
||||
|
||||
mURLList.AppendElement(aURL);
|
||||
|
||||
mFragment.Assign(aFragment);
|
||||
}
|
||||
// Get the URL list without their fragments.
|
||||
void
|
||||
GetURLListWithoutFragment(nsTArray<nsCString>& aURLList)
|
||||
{
|
||||
aURLList.Assign(mURLList);
|
||||
}
|
||||
void
|
||||
GetReferrer(nsAString& aReferrer) const
|
||||
{
|
||||
aReferrer.Assign(mReferrer);
|
||||
}
|
||||
|
||||
void
|
||||
SetReferrer(const nsAString& aReferrer)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
bool validReferrer = false;
|
||||
if (aReferrer.IsEmpty() ||
|
||||
aReferrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
|
||||
validReferrer = true;
|
||||
} else {
|
||||
nsCOMPtr<nsIURLParser> parser = do_GetService(NS_STDURLPARSER_CONTRACTID);
|
||||
if (!parser) {
|
||||
NS_WARNING("Could not get parser to validate URL!");
|
||||
} else {
|
||||
uint32_t schemePos;
|
||||
int32_t schemeLen;
|
||||
uint32_t authorityPos;
|
||||
int32_t authorityLen;
|
||||
uint32_t pathPos;
|
||||
int32_t pathLen;
|
||||
|
||||
NS_ConvertUTF16toUTF8 ref(aReferrer);
|
||||
nsresult rv = parser->ParseURL(ref.get(), ref.Length(),
|
||||
&schemePos, &schemeLen,
|
||||
&authorityPos, &authorityLen,
|
||||
&pathPos, &pathLen);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Invalid referrer URL!");
|
||||
} else if (schemeLen < 0 || authorityLen < 0) {
|
||||
NS_WARNING("Invalid referrer URL!");
|
||||
} else {
|
||||
validReferrer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(validReferrer);
|
||||
#endif
|
||||
|
||||
mReferrer.Assign(aReferrer);
|
||||
}
|
||||
|
||||
ReferrerPolicy
|
||||
ReferrerPolicy_() const
|
||||
{
|
||||
return mReferrerPolicy;
|
||||
}
|
||||
|
||||
void
|
||||
SetReferrerPolicy(ReferrerPolicy aReferrerPolicy)
|
||||
{
|
||||
mReferrerPolicy = aReferrerPolicy;
|
||||
}
|
||||
|
||||
net::ReferrerPolicy
|
||||
GetEnvironmentReferrerPolicy() const
|
||||
{
|
||||
return mEnvironmentReferrerPolicy;
|
||||
}
|
||||
|
||||
void
|
||||
SetEnvironmentReferrerPolicy(net::ReferrerPolicy aReferrerPolicy)
|
||||
{
|
||||
mEnvironmentReferrerPolicy = aReferrerPolicy;
|
||||
}
|
||||
|
||||
bool
|
||||
SkipServiceWorker() const
|
||||
{
|
||||
return mSkipServiceWorker;
|
||||
}
|
||||
|
||||
void
|
||||
SetSkipServiceWorker()
|
||||
{
|
||||
mSkipServiceWorker = true;
|
||||
}
|
||||
|
||||
bool
|
||||
IsSynchronous() const
|
||||
{
|
||||
return mSynchronous;
|
||||
}
|
||||
|
||||
RequestMode
|
||||
Mode() const
|
||||
{
|
||||
return mMode;
|
||||
}
|
||||
|
||||
void
|
||||
SetMode(RequestMode aMode)
|
||||
{
|
||||
mMode = aMode;
|
||||
}
|
||||
|
||||
RequestCredentials
|
||||
GetCredentialsMode() const
|
||||
{
|
||||
return mCredentialsMode;
|
||||
}
|
||||
|
||||
void
|
||||
SetCredentialsMode(RequestCredentials aCredentialsMode)
|
||||
{
|
||||
mCredentialsMode = aCredentialsMode;
|
||||
}
|
||||
|
||||
LoadTainting
|
||||
GetResponseTainting() const
|
||||
{
|
||||
return mResponseTainting;
|
||||
}
|
||||
|
||||
void
|
||||
MaybeIncreaseResponseTainting(LoadTainting aTainting)
|
||||
{
|
||||
if (aTainting > mResponseTainting) {
|
||||
mResponseTainting = aTainting;
|
||||
}
|
||||
}
|
||||
|
||||
RequestCache
|
||||
GetCacheMode() const
|
||||
{
|
||||
return mCacheMode;
|
||||
}
|
||||
|
||||
void
|
||||
SetCacheMode(RequestCache aCacheMode)
|
||||
{
|
||||
mCacheMode = aCacheMode;
|
||||
}
|
||||
|
||||
RequestRedirect
|
||||
GetRedirectMode() const
|
||||
{
|
||||
return mRedirectMode;
|
||||
}
|
||||
|
||||
void
|
||||
SetRedirectMode(RequestRedirect aRedirectMode)
|
||||
{
|
||||
mRedirectMode = aRedirectMode;
|
||||
}
|
||||
|
||||
const nsString&
|
||||
GetIntegrity() const
|
||||
{
|
||||
return mIntegrity;
|
||||
}
|
||||
void
|
||||
SetIntegrity(const nsAString& aIntegrity)
|
||||
{
|
||||
MOZ_ASSERT(mIntegrity.IsEmpty());
|
||||
mIntegrity.Assign(aIntegrity);
|
||||
}
|
||||
const nsCString&
|
||||
GetFragment() const
|
||||
{
|
||||
return mFragment;
|
||||
}
|
||||
|
||||
nsContentPolicyType
|
||||
ContentPolicyType() const
|
||||
{
|
||||
return mContentPolicyType;
|
||||
}
|
||||
void
|
||||
SetContentPolicyType(nsContentPolicyType aContentPolicyType);
|
||||
|
||||
void
|
||||
OverrideContentPolicyType(nsContentPolicyType aContentPolicyType);
|
||||
|
||||
RequestContext
|
||||
Context() const
|
||||
{
|
||||
return MapContentPolicyTypeToRequestContext(mContentPolicyType);
|
||||
}
|
||||
|
||||
bool
|
||||
UnsafeRequest() const
|
||||
{
|
||||
return mUnsafeRequest;
|
||||
}
|
||||
|
||||
void
|
||||
SetUnsafeRequest()
|
||||
{
|
||||
mUnsafeRequest = true;
|
||||
}
|
||||
|
||||
InternalHeaders*
|
||||
Headers()
|
||||
{
|
||||
return mHeaders;
|
||||
}
|
||||
|
||||
bool
|
||||
ForceOriginHeader()
|
||||
{
|
||||
return mForceOriginHeader;
|
||||
}
|
||||
|
||||
bool
|
||||
SameOriginDataURL() const
|
||||
{
|
||||
return mSameOriginDataURL;
|
||||
}
|
||||
|
||||
void
|
||||
UnsetSameOriginDataURL()
|
||||
{
|
||||
mSameOriginDataURL = false;
|
||||
}
|
||||
|
||||
void
|
||||
SetBody(nsIInputStream* aStream)
|
||||
{
|
||||
// A request's body may not be reset once set.
|
||||
MOZ_ASSERT_IF(aStream, !mBodyStream);
|
||||
mBodyStream = aStream;
|
||||
}
|
||||
|
||||
// Will return the original stream!
|
||||
// Use a tee or copy if you don't want to erase the original.
|
||||
void
|
||||
GetBody(nsIInputStream** aStream)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> s = mBodyStream;
|
||||
s.forget(aStream);
|
||||
}
|
||||
|
||||
// The global is used as the client for the new object.
|
||||
already_AddRefed<InternalRequest>
|
||||
GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const;
|
||||
|
||||
bool
|
||||
WasCreatedByFetchEvent() const
|
||||
{
|
||||
return mCreatedByFetchEvent;
|
||||
}
|
||||
|
||||
void
|
||||
SetCreatedByFetchEvent()
|
||||
{
|
||||
mCreatedByFetchEvent = true;
|
||||
}
|
||||
|
||||
void
|
||||
ClearCreatedByFetchEvent()
|
||||
{
|
||||
mCreatedByFetchEvent = false;
|
||||
}
|
||||
|
||||
bool
|
||||
IsNavigationRequest() const;
|
||||
|
||||
bool
|
||||
IsWorkerRequest() const;
|
||||
|
||||
bool
|
||||
IsClientRequest() const;
|
||||
|
||||
void
|
||||
MaybeSkipCacheIfPerformingRevalidation();
|
||||
|
||||
bool
|
||||
IsContentPolicyTypeOverridden() const
|
||||
{
|
||||
return mContentPolicyTypeOverridden;
|
||||
}
|
||||
|
||||
static RequestMode
|
||||
MapChannelToRequestMode(nsIChannel* aChannel);
|
||||
|
||||
static RequestCredentials
|
||||
MapChannelToRequestCredentials(nsIChannel* aChannel);
|
||||
|
||||
// Takes ownership of the principal info.
|
||||
void
|
||||
SetPrincipalInfo(UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo);
|
||||
|
||||
const UniquePtr<mozilla::ipc::PrincipalInfo>&
|
||||
GetPrincipalInfo() const
|
||||
{
|
||||
return mPrincipalInfo;
|
||||
}
|
||||
|
||||
private:
|
||||
// Does not copy mBodyStream. Use fallible Clone() for complete copy.
|
||||
explicit InternalRequest(const InternalRequest& aOther);
|
||||
|
||||
~InternalRequest();
|
||||
|
||||
static RequestContext
|
||||
MapContentPolicyTypeToRequestContext(nsContentPolicyType aContentPolicyType);
|
||||
|
||||
static bool
|
||||
IsNavigationContentPolicy(nsContentPolicyType aContentPolicyType);
|
||||
|
||||
static bool
|
||||
IsWorkerContentPolicy(nsContentPolicyType aContentPolicyType);
|
||||
|
||||
nsCString mMethod;
|
||||
// mURLList: a list of one or more fetch URLs
|
||||
nsTArray<nsCString> mURLList;
|
||||
RefPtr<InternalHeaders> mHeaders;
|
||||
nsCOMPtr<nsIInputStream> mBodyStream;
|
||||
|
||||
nsContentPolicyType mContentPolicyType;
|
||||
|
||||
// Empty string: no-referrer
|
||||
// "about:client": client (default)
|
||||
// URL: an URL
|
||||
nsString mReferrer;
|
||||
ReferrerPolicy mReferrerPolicy;
|
||||
|
||||
// This will be used for request created from Window or Worker contexts
|
||||
// In case there's no Referrer Policy in Request, this will be passed to
|
||||
// channel.
|
||||
// The Environment Referrer Policy should be net::ReferrerPolicy so that it
|
||||
// could be associated with nsIHttpChannel.
|
||||
net::ReferrerPolicy mEnvironmentReferrerPolicy;
|
||||
RequestMode mMode;
|
||||
RequestCredentials mCredentialsMode;
|
||||
MOZ_INIT_OUTSIDE_CTOR LoadTainting mResponseTainting;
|
||||
RequestCache mCacheMode;
|
||||
RequestRedirect mRedirectMode;
|
||||
nsString mIntegrity;
|
||||
nsCString mFragment;
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mAuthenticationFlag;
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mForceOriginHeader;
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mPreserveContentCodings;
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mSameOriginDataURL;
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mSkipServiceWorker;
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mSynchronous;
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mUnsafeRequest;
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mUseURLCredentials;
|
||||
// This is only set when a Request object is created by a fetch event. We
|
||||
// use it to check if Service Workers are simply fetching intercepted Request
|
||||
// objects without modifying them.
|
||||
bool mCreatedByFetchEvent = false;
|
||||
// This is only set when Request.overrideContentPolicyType() has been set.
|
||||
// It is illegal to pass such a Request object to a fetch() method unless
|
||||
// if the caller has chrome privileges.
|
||||
bool mContentPolicyTypeOverridden = false;
|
||||
|
||||
UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_InternalRequest_h
|
||||
261
dom/fetch/InternalResponse.cpp
Normal file
261
dom/fetch/InternalResponse.cpp
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
/* -*- 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 "InternalResponse.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/dom/InternalHeaders.h"
|
||||
#include "mozilla/dom/cache/CacheTypes.h"
|
||||
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
||||
#include "mozilla/ipc/IPCStreamUtils.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsStreamUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusText)
|
||||
: mType(ResponseType::Default)
|
||||
, mStatus(aStatus)
|
||||
, mStatusText(aStatusText)
|
||||
, mHeaders(new InternalHeaders(HeadersGuardEnum::Response))
|
||||
, mBodySize(UNKNOWN_BODY_SIZE)
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
InternalResponse::FromIPC(const IPCInternalResponse& aIPCResponse)
|
||||
{
|
||||
if (aIPCResponse.type() == ResponseType::Error) {
|
||||
return InternalResponse::NetworkError();
|
||||
}
|
||||
|
||||
RefPtr<InternalResponse> response =
|
||||
new InternalResponse(aIPCResponse.status(),
|
||||
aIPCResponse.statusText());
|
||||
|
||||
response->SetURLList(aIPCResponse.urlList());
|
||||
|
||||
response->mHeaders = new InternalHeaders(aIPCResponse.headers(),
|
||||
aIPCResponse.headersGuard());
|
||||
|
||||
response->InitChannelInfo(aIPCResponse.channelInfo());
|
||||
if (aIPCResponse.principalInfo().type() == mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
|
||||
UniquePtr<mozilla::ipc::PrincipalInfo> info(new mozilla::ipc::PrincipalInfo(aIPCResponse.principalInfo().get_PrincipalInfo()));
|
||||
response->SetPrincipalInfo(Move(info));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aIPCResponse.body());
|
||||
response->SetBody(stream, aIPCResponse.bodySize());
|
||||
|
||||
switch (aIPCResponse.type())
|
||||
{
|
||||
case ResponseType::Basic:
|
||||
response = response->BasicResponse();
|
||||
break;
|
||||
case ResponseType::Cors:
|
||||
response = response->CORSResponse();
|
||||
break;
|
||||
case ResponseType::Default:
|
||||
break;
|
||||
case ResponseType::Opaque:
|
||||
response = response->OpaqueResponse();
|
||||
break;
|
||||
case ResponseType::Opaqueredirect:
|
||||
response = response->OpaqueRedirectResponse();
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected ResponseType!");
|
||||
}
|
||||
MOZ_ASSERT(response);
|
||||
|
||||
return response.forget();
|
||||
}
|
||||
|
||||
InternalResponse::~InternalResponse()
|
||||
{
|
||||
}
|
||||
|
||||
template void
|
||||
InternalResponse::ToIPC<PContentParent>
|
||||
(IPCInternalResponse* aIPCResponse,
|
||||
PContentParent* aManager,
|
||||
UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
|
||||
template void
|
||||
InternalResponse::ToIPC<nsIContentChild>
|
||||
(IPCInternalResponse* aIPCResponse,
|
||||
nsIContentChild* aManager,
|
||||
UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
|
||||
template void
|
||||
InternalResponse::ToIPC<mozilla::ipc::PBackgroundParent>
|
||||
(IPCInternalResponse* aIPCResponse,
|
||||
mozilla::ipc::PBackgroundParent* aManager,
|
||||
UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
|
||||
template void
|
||||
InternalResponse::ToIPC<mozilla::ipc::PBackgroundChild>
|
||||
(IPCInternalResponse* aIPCResponse,
|
||||
mozilla::ipc::PBackgroundChild* aManager,
|
||||
UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
|
||||
|
||||
template<typename M>
|
||||
void
|
||||
InternalResponse::ToIPC(IPCInternalResponse* aIPCResponse,
|
||||
M* aManager,
|
||||
UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream)
|
||||
{
|
||||
MOZ_ASSERT(aIPCResponse);
|
||||
aIPCResponse->type() = mType;
|
||||
aIPCResponse->urlList() = mURLList;
|
||||
aIPCResponse->status() = GetUnfilteredStatus();
|
||||
aIPCResponse->statusText() = GetUnfilteredStatusText();
|
||||
|
||||
mHeaders->ToIPC(aIPCResponse->headers(), aIPCResponse->headersGuard());
|
||||
|
||||
aIPCResponse->channelInfo() = mChannelInfo.AsIPCChannelInfo();
|
||||
if (mPrincipalInfo) {
|
||||
aIPCResponse->principalInfo() = *mPrincipalInfo;
|
||||
} else {
|
||||
aIPCResponse->principalInfo() = void_t();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> body;
|
||||
int64_t bodySize;
|
||||
GetUnfilteredBody(getter_AddRefs(body), &bodySize);
|
||||
|
||||
if (body) {
|
||||
aAutoStream.reset(new mozilla::ipc::AutoIPCStream(aIPCResponse->body()));
|
||||
aAutoStream->Serialize(body, aManager);
|
||||
} else {
|
||||
aIPCResponse->body() = void_t();
|
||||
}
|
||||
|
||||
aIPCResponse->bodySize() = bodySize;
|
||||
}
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
InternalResponse::Clone()
|
||||
{
|
||||
RefPtr<InternalResponse> clone = CreateIncompleteCopy();
|
||||
|
||||
clone->mHeaders = new InternalHeaders(*mHeaders);
|
||||
if (mWrappedResponse) {
|
||||
clone->mWrappedResponse = mWrappedResponse->Clone();
|
||||
MOZ_ASSERT(!mBody);
|
||||
return clone.forget();
|
||||
}
|
||||
|
||||
if (!mBody) {
|
||||
return clone.forget();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> clonedBody;
|
||||
nsCOMPtr<nsIInputStream> replacementBody;
|
||||
|
||||
nsresult rv = NS_CloneInputStream(mBody, getter_AddRefs(clonedBody),
|
||||
getter_AddRefs(replacementBody));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; }
|
||||
|
||||
clone->mBody.swap(clonedBody);
|
||||
if (replacementBody) {
|
||||
mBody.swap(replacementBody);
|
||||
}
|
||||
|
||||
return clone.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
InternalResponse::BasicResponse()
|
||||
{
|
||||
MOZ_ASSERT(!mWrappedResponse, "Can't BasicResponse a already wrapped response");
|
||||
RefPtr<InternalResponse> basic = CreateIncompleteCopy();
|
||||
basic->mType = ResponseType::Basic;
|
||||
basic->mHeaders = InternalHeaders::BasicHeaders(Headers());
|
||||
basic->mWrappedResponse = this;
|
||||
return basic.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
InternalResponse::CORSResponse()
|
||||
{
|
||||
MOZ_ASSERT(!mWrappedResponse, "Can't CORSResponse a already wrapped response");
|
||||
RefPtr<InternalResponse> cors = CreateIncompleteCopy();
|
||||
cors->mType = ResponseType::Cors;
|
||||
cors->mHeaders = InternalHeaders::CORSHeaders(Headers());
|
||||
cors->mWrappedResponse = this;
|
||||
return cors.forget();
|
||||
}
|
||||
|
||||
void
|
||||
InternalResponse::SetPrincipalInfo(UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo)
|
||||
{
|
||||
mPrincipalInfo = Move(aPrincipalInfo);
|
||||
}
|
||||
|
||||
LoadTainting
|
||||
InternalResponse::GetTainting() const
|
||||
{
|
||||
switch (mType) {
|
||||
case ResponseType::Cors:
|
||||
return LoadTainting::CORS;
|
||||
case ResponseType::Opaque:
|
||||
return LoadTainting::Opaque;
|
||||
default:
|
||||
return LoadTainting::Basic;
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
InternalResponse::Unfiltered()
|
||||
{
|
||||
RefPtr<InternalResponse> ref = mWrappedResponse;
|
||||
if (!ref) {
|
||||
ref = this;
|
||||
}
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
InternalResponse::OpaqueResponse()
|
||||
{
|
||||
MOZ_ASSERT(!mWrappedResponse, "Can't OpaqueResponse a already wrapped response");
|
||||
RefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
|
||||
response->mType = ResponseType::Opaque;
|
||||
response->mTerminationReason = mTerminationReason;
|
||||
response->mChannelInfo = mChannelInfo;
|
||||
if (mPrincipalInfo) {
|
||||
response->mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
|
||||
}
|
||||
response->mWrappedResponse = this;
|
||||
return response.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
InternalResponse::OpaqueRedirectResponse()
|
||||
{
|
||||
MOZ_ASSERT(!mWrappedResponse, "Can't OpaqueRedirectResponse a already wrapped response");
|
||||
MOZ_ASSERT(!mURLList.IsEmpty(), "URLList should not be emtpy for internalResponse");
|
||||
RefPtr<InternalResponse> response = OpaqueResponse();
|
||||
response->mType = ResponseType::Opaqueredirect;
|
||||
response->mURLList = mURLList;
|
||||
return response.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
InternalResponse::CreateIncompleteCopy()
|
||||
{
|
||||
RefPtr<InternalResponse> copy = new InternalResponse(mStatus, mStatusText);
|
||||
copy->mType = mType;
|
||||
copy->mTerminationReason = mTerminationReason;
|
||||
copy->mURLList = mURLList;
|
||||
copy->mChannelInfo = mChannelInfo;
|
||||
if (mPrincipalInfo) {
|
||||
copy->mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>(*mPrincipalInfo);
|
||||
}
|
||||
return copy.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
313
dom/fetch/InternalResponse.h
Normal file
313
dom/fetch/InternalResponse.h
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_InternalResponse_h
|
||||
#define mozilla_dom_InternalResponse_h
|
||||
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
#include "mozilla/dom/ResponseBinding.h"
|
||||
#include "mozilla/dom/ChannelInfo.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
class PrincipalInfo;
|
||||
class AutoIPCStream;
|
||||
} // namespace ipc
|
||||
|
||||
namespace dom {
|
||||
|
||||
class InternalHeaders;
|
||||
class IPCInternalResponse;
|
||||
|
||||
class InternalResponse final
|
||||
{
|
||||
friend class FetchDriver;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalResponse)
|
||||
|
||||
InternalResponse(uint16_t aStatus, const nsACString& aStatusText);
|
||||
|
||||
static already_AddRefed<InternalResponse>
|
||||
FromIPC(const IPCInternalResponse& aIPCResponse);
|
||||
|
||||
template<typename M>
|
||||
void
|
||||
ToIPC(IPCInternalResponse* aIPCResponse,
|
||||
M* aManager,
|
||||
UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
|
||||
|
||||
already_AddRefed<InternalResponse> Clone();
|
||||
|
||||
static already_AddRefed<InternalResponse>
|
||||
NetworkError()
|
||||
{
|
||||
RefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
|
||||
ErrorResult result;
|
||||
response->Headers()->SetGuard(HeadersGuardEnum::Immutable, result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
response->mType = ResponseType::Error;
|
||||
return response.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
OpaqueResponse();
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
OpaqueRedirectResponse();
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
BasicResponse();
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
CORSResponse();
|
||||
|
||||
ResponseType
|
||||
Type() const
|
||||
{
|
||||
MOZ_ASSERT_IF(mType == ResponseType::Error, !mWrappedResponse);
|
||||
MOZ_ASSERT_IF(mType == ResponseType::Default, !mWrappedResponse);
|
||||
MOZ_ASSERT_IF(mType == ResponseType::Basic, mWrappedResponse);
|
||||
MOZ_ASSERT_IF(mType == ResponseType::Cors, mWrappedResponse);
|
||||
MOZ_ASSERT_IF(mType == ResponseType::Opaque, mWrappedResponse);
|
||||
MOZ_ASSERT_IF(mType == ResponseType::Opaqueredirect, mWrappedResponse);
|
||||
return mType;
|
||||
}
|
||||
|
||||
bool
|
||||
IsError() const
|
||||
{
|
||||
return Type() == ResponseType::Error;
|
||||
}
|
||||
// GetUrl should return last fetch URL in response's url list and null if
|
||||
// response's url list is the empty list.
|
||||
const nsCString&
|
||||
GetURL() const
|
||||
{
|
||||
// Empty urlList when response is a synthetic response.
|
||||
if (mURLList.IsEmpty()) {
|
||||
return EmptyCString();
|
||||
}
|
||||
return mURLList.LastElement();
|
||||
}
|
||||
void
|
||||
GetURLList(nsTArray<nsCString>& aURLList) const
|
||||
{
|
||||
aURLList.Assign(mURLList);
|
||||
}
|
||||
const nsCString&
|
||||
GetUnfilteredURL() const
|
||||
{
|
||||
if (mWrappedResponse) {
|
||||
return mWrappedResponse->GetURL();
|
||||
}
|
||||
return GetURL();
|
||||
}
|
||||
void
|
||||
GetUnfilteredURLList(nsTArray<nsCString>& aURLList) const
|
||||
{
|
||||
if (mWrappedResponse) {
|
||||
return mWrappedResponse->GetURLList(aURLList);
|
||||
}
|
||||
|
||||
return GetURLList(aURLList);
|
||||
}
|
||||
|
||||
void
|
||||
SetURLList(const nsTArray<nsCString>& aURLList)
|
||||
{
|
||||
mURLList.Assign(aURLList);
|
||||
|
||||
#ifdef DEBUG
|
||||
for(uint32_t i = 0; i < mURLList.Length(); ++i) {
|
||||
MOZ_ASSERT(mURLList[i].Find(NS_LITERAL_CSTRING("#")) == kNotFound);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t
|
||||
GetStatus() const
|
||||
{
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
uint16_t
|
||||
GetUnfilteredStatus() const
|
||||
{
|
||||
if (mWrappedResponse) {
|
||||
return mWrappedResponse->GetStatus();
|
||||
}
|
||||
|
||||
return GetStatus();
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
GetStatusText() const
|
||||
{
|
||||
return mStatusText;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
GetUnfilteredStatusText() const
|
||||
{
|
||||
if (mWrappedResponse) {
|
||||
return mWrappedResponse->GetStatusText();
|
||||
}
|
||||
|
||||
return GetStatusText();
|
||||
}
|
||||
|
||||
InternalHeaders*
|
||||
Headers()
|
||||
{
|
||||
return mHeaders;
|
||||
}
|
||||
|
||||
InternalHeaders*
|
||||
UnfilteredHeaders()
|
||||
{
|
||||
if (mWrappedResponse) {
|
||||
return mWrappedResponse->Headers();
|
||||
};
|
||||
|
||||
return Headers();
|
||||
}
|
||||
|
||||
void
|
||||
GetUnfilteredBody(nsIInputStream** aStream, int64_t* aBodySize = nullptr)
|
||||
{
|
||||
if (mWrappedResponse) {
|
||||
MOZ_ASSERT(!mBody);
|
||||
return mWrappedResponse->GetBody(aStream, aBodySize);
|
||||
}
|
||||
nsCOMPtr<nsIInputStream> stream = mBody;
|
||||
stream.forget(aStream);
|
||||
if (aBodySize) {
|
||||
*aBodySize = mBodySize;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GetBody(nsIInputStream** aStream, int64_t* aBodySize = nullptr)
|
||||
{
|
||||
if (Type() == ResponseType::Opaque ||
|
||||
Type() == ResponseType::Opaqueredirect) {
|
||||
*aStream = nullptr;
|
||||
if (aBodySize) {
|
||||
*aBodySize = UNKNOWN_BODY_SIZE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return GetUnfilteredBody(aStream, aBodySize);
|
||||
}
|
||||
|
||||
void
|
||||
SetBody(nsIInputStream* aBody, int64_t aBodySize)
|
||||
{
|
||||
if (mWrappedResponse) {
|
||||
return mWrappedResponse->SetBody(aBody, aBodySize);
|
||||
}
|
||||
// A request's body may not be reset once set.
|
||||
MOZ_ASSERT(!mBody);
|
||||
MOZ_ASSERT(mBodySize == UNKNOWN_BODY_SIZE);
|
||||
// Check arguments.
|
||||
MOZ_ASSERT(aBodySize == UNKNOWN_BODY_SIZE || aBodySize >= 0);
|
||||
// If body is not given, then size must be unknown.
|
||||
MOZ_ASSERT_IF(!aBody, aBodySize == UNKNOWN_BODY_SIZE);
|
||||
|
||||
mBody = aBody;
|
||||
mBodySize = aBodySize;
|
||||
}
|
||||
|
||||
void
|
||||
InitChannelInfo(nsIChannel* aChannel)
|
||||
{
|
||||
mChannelInfo.InitFromChannel(aChannel);
|
||||
}
|
||||
|
||||
void
|
||||
InitChannelInfo(const mozilla::ipc::IPCChannelInfo& aChannelInfo)
|
||||
{
|
||||
mChannelInfo.InitFromIPCChannelInfo(aChannelInfo);
|
||||
}
|
||||
|
||||
void
|
||||
InitChannelInfo(const ChannelInfo& aChannelInfo)
|
||||
{
|
||||
mChannelInfo = aChannelInfo;
|
||||
}
|
||||
|
||||
const ChannelInfo&
|
||||
GetChannelInfo() const
|
||||
{
|
||||
return mChannelInfo;
|
||||
}
|
||||
|
||||
const UniquePtr<mozilla::ipc::PrincipalInfo>&
|
||||
GetPrincipalInfo() const
|
||||
{
|
||||
return mPrincipalInfo;
|
||||
}
|
||||
|
||||
bool
|
||||
IsRedirected() const
|
||||
{
|
||||
return mURLList.Length() > 1;
|
||||
}
|
||||
|
||||
// Takes ownership of the principal info.
|
||||
void
|
||||
SetPrincipalInfo(UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo);
|
||||
|
||||
LoadTainting
|
||||
GetTainting() const;
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
Unfiltered();
|
||||
|
||||
private:
|
||||
~InternalResponse();
|
||||
|
||||
explicit InternalResponse(const InternalResponse& aOther) = delete;
|
||||
InternalResponse& operator=(const InternalResponse&) = delete;
|
||||
|
||||
// Returns an instance of InternalResponse which is a copy of this
|
||||
// InternalResponse, except headers, body and wrapped response (if any) which
|
||||
// are left uninitialized. Used for cloning and filtering.
|
||||
already_AddRefed<InternalResponse> CreateIncompleteCopy();
|
||||
|
||||
ResponseType mType;
|
||||
nsCString mTerminationReason;
|
||||
// A response has an associated url list (a list of zero or more fetch URLs).
|
||||
// Unless stated otherwise, it is the empty list. The current url is the last
|
||||
// element in mURLlist
|
||||
nsTArray<nsCString> mURLList;
|
||||
const uint16_t mStatus;
|
||||
const nsCString mStatusText;
|
||||
RefPtr<InternalHeaders> mHeaders;
|
||||
nsCOMPtr<nsIInputStream> mBody;
|
||||
int64_t mBodySize;
|
||||
public:
|
||||
static const int64_t UNKNOWN_BODY_SIZE = -1;
|
||||
private:
|
||||
ChannelInfo mChannelInfo;
|
||||
UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
|
||||
|
||||
// For filtered responses.
|
||||
// Cache, and SW interception should always serialize/access the underlying
|
||||
// unfiltered headers and when deserializing, create an InternalResponse
|
||||
// with the unfiltered headers followed by wrapping it.
|
||||
RefPtr<InternalResponse> mWrappedResponse;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_InternalResponse_h
|
||||
627
dom/fetch/Request.cpp
Normal file
627
dom/fetch/Request.cpp
Normal file
|
|
@ -0,0 +1,627 @@
|
|||
/* -*- 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 "Request.h"
|
||||
|
||||
#include "nsIURI.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/Headers.h"
|
||||
#include "mozilla/dom/Fetch.h"
|
||||
#include "mozilla/dom/FetchUtil.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/URL.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Request)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Request)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner, mHeaders)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
Request::Request(nsIGlobalObject* aOwner, InternalRequest* aRequest)
|
||||
: FetchBody<Request>()
|
||||
, mOwner(aOwner)
|
||||
, mRequest(aRequest)
|
||||
{
|
||||
MOZ_ASSERT(aRequest->Headers()->Guard() == HeadersGuardEnum::Immutable ||
|
||||
aRequest->Headers()->Guard() == HeadersGuardEnum::Request ||
|
||||
aRequest->Headers()->Guard() == HeadersGuardEnum::Request_no_cors);
|
||||
SetMimeType();
|
||||
}
|
||||
|
||||
Request::~Request()
|
||||
{
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
Request::RequestContextEnabled(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return Preferences::GetBool("dom.requestcontext.enabled", false);
|
||||
}
|
||||
|
||||
using namespace workers;
|
||||
|
||||
// Otherwise, check the pref via the WorkerPrivate
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
if (!workerPrivate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return workerPrivate->RequestContextEnabled();
|
||||
}
|
||||
|
||||
already_AddRefed<InternalRequest>
|
||||
Request::GetInternalRequest()
|
||||
{
|
||||
RefPtr<InternalRequest> r = mRequest;
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
namespace {
|
||||
already_AddRefed<nsIURI>
|
||||
ParseURLFromDocument(nsIDocument* aDocument, const nsAString& aInput,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(aDocument);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIURI> baseURI = aDocument->GetBaseURI();
|
||||
nsCOMPtr<nsIURI> resolvedURI;
|
||||
aRv = NS_NewURI(getter_AddRefs(resolvedURI), aInput, nullptr, baseURI);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
aRv.ThrowTypeError<MSG_INVALID_URL>(aInput);
|
||||
}
|
||||
return resolvedURI.forget();
|
||||
}
|
||||
void
|
||||
GetRequestURLFromDocument(nsIDocument* aDocument, const nsAString& aInput,
|
||||
nsAString& aRequestURL, nsACString& aURLfragment,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIURI> resolvedURI = ParseURLFromDocument(aDocument, aInput, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
// This fails with URIs with weird protocols, even when they are valid,
|
||||
// so we ignore the failure
|
||||
nsAutoCString credentials;
|
||||
Unused << resolvedURI->GetUserPass(credentials);
|
||||
if (!credentials.IsEmpty()) {
|
||||
aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(aInput);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> resolvedURIClone;
|
||||
// We use CloneIgnoringRef to strip away the fragment even if the original URI
|
||||
// is immutable.
|
||||
aRv = resolvedURI->CloneIgnoringRef(getter_AddRefs(resolvedURIClone));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
nsAutoCString spec;
|
||||
aRv = resolvedURIClone->GetSpec(spec);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
CopyUTF8toUTF16(spec, aRequestURL);
|
||||
|
||||
// Get the fragment from nsIURI.
|
||||
aRv = resolvedURI->GetRef(aURLfragment);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
already_AddRefed<nsIURI>
|
||||
ParseURLFromChrome(const nsAString& aInput, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
aRv = NS_NewURI(getter_AddRefs(uri), aInput, nullptr, nullptr);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
aRv.ThrowTypeError<MSG_INVALID_URL>(aInput);
|
||||
}
|
||||
return uri.forget();
|
||||
}
|
||||
void
|
||||
GetRequestURLFromChrome(const nsAString& aInput, nsAString& aRequestURL,
|
||||
nsACString& aURLfragment, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri = ParseURLFromChrome(aInput, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
// This fails with URIs with weird protocols, even when they are valid,
|
||||
// so we ignore the failure
|
||||
nsAutoCString credentials;
|
||||
Unused << uri->GetUserPass(credentials);
|
||||
if (!credentials.IsEmpty()) {
|
||||
aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(aInput);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uriClone;
|
||||
// We use CloneIgnoringRef to strip away the fragment even if the original URI
|
||||
// is immutable.
|
||||
aRv = uri->CloneIgnoringRef(getter_AddRefs(uriClone));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
nsAutoCString spec;
|
||||
aRv = uriClone->GetSpec(spec);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
CopyUTF8toUTF16(spec, aRequestURL);
|
||||
|
||||
// Get the fragment from nsIURI.
|
||||
aRv = uri->GetRef(aURLfragment);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
already_AddRefed<URL>
|
||||
ParseURLFromWorker(const GlobalObject& aGlobal, const nsAString& aInput,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
|
||||
NS_ConvertUTF8toUTF16 baseURL(worker->GetLocationInfo().mHref);
|
||||
RefPtr<URL> url = URL::WorkerConstructor(aGlobal, aInput, baseURL, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
aRv.ThrowTypeError<MSG_INVALID_URL>(aInput);
|
||||
}
|
||||
return url.forget();
|
||||
}
|
||||
void
|
||||
GetRequestURLFromWorker(const GlobalObject& aGlobal, const nsAString& aInput,
|
||||
nsAString& aRequestURL, nsACString& aURLfragment,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<URL> url = ParseURLFromWorker(aGlobal, aInput, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
nsString username;
|
||||
url->GetUsername(username, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsString password;
|
||||
url->GetPassword(password, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
if (!username.IsEmpty() || !password.IsEmpty()) {
|
||||
aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(aInput);
|
||||
return;
|
||||
}
|
||||
// Get the fragment from URL.
|
||||
nsAutoString fragment;
|
||||
url->GetHash(fragment, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: URL::GetHash() includes the "#" and we want the fragment with out
|
||||
// the hash symbol.
|
||||
if (!fragment.IsEmpty()) {
|
||||
CopyUTF16toUTF8(Substring(fragment, 1), aURLfragment);
|
||||
}
|
||||
|
||||
url->SetHash(EmptyString(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
url->Stringify(aRequestURL, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
class ReferrerSameOriginChecker final : public workers::WorkerMainThreadRunnable
|
||||
{
|
||||
public:
|
||||
ReferrerSameOriginChecker(workers::WorkerPrivate* aWorkerPrivate,
|
||||
const nsAString& aReferrerURL,
|
||||
nsresult& aResult)
|
||||
: workers::WorkerMainThreadRunnable(aWorkerPrivate,
|
||||
NS_LITERAL_CSTRING("Fetch :: Referrer same origin check")),
|
||||
mReferrerURL(aReferrerURL),
|
||||
mResult(aResult)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
bool
|
||||
MainThreadRun() override
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), mReferrerURL))) {
|
||||
nsCOMPtr<nsIPrincipal> principal = mWorkerPrivate->GetPrincipal();
|
||||
if (principal) {
|
||||
mResult = principal->CheckMayLoad(uri, /* report */ false,
|
||||
/* allowIfInheritsPrincipal */ false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const nsString mReferrerURL;
|
||||
nsresult& mResult;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/*static*/ already_AddRefed<Request>
|
||||
Request::Constructor(const GlobalObject& aGlobal,
|
||||
const RequestOrUSVString& aInput,
|
||||
const RequestInit& aInit, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> temporaryBody;
|
||||
RefPtr<InternalRequest> request;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
|
||||
if (aInput.IsRequest()) {
|
||||
RefPtr<Request> inputReq = &aInput.GetAsRequest();
|
||||
nsCOMPtr<nsIInputStream> body;
|
||||
inputReq->GetBody(getter_AddRefs(body));
|
||||
if (inputReq->BodyUsed()) {
|
||||
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
if (body) {
|
||||
temporaryBody = body;
|
||||
}
|
||||
|
||||
request = inputReq->GetInternalRequest();
|
||||
} else {
|
||||
// aInput is USVString.
|
||||
// We need to get url before we create a InternalRequest.
|
||||
nsAutoString input;
|
||||
input.Assign(aInput.GetAsUSVString());
|
||||
nsAutoString requestURL;
|
||||
nsCString fragment;
|
||||
if (NS_IsMainThread()) {
|
||||
nsIDocument* doc = GetEntryDocument();
|
||||
if (doc) {
|
||||
GetRequestURLFromDocument(doc, input, requestURL, fragment, aRv);
|
||||
} else {
|
||||
// If we don't have a document, we must assume that this is a full URL.
|
||||
GetRequestURLFromChrome(input, requestURL, fragment, aRv);
|
||||
}
|
||||
} else {
|
||||
GetRequestURLFromWorker(aGlobal, input, requestURL, fragment, aRv);
|
||||
}
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
request = new InternalRequest(NS_ConvertUTF16toUTF8(requestURL), fragment);
|
||||
}
|
||||
request = request->GetRequestConstructorCopy(global, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
RequestMode fallbackMode = RequestMode::EndGuard_;
|
||||
RequestCredentials fallbackCredentials = RequestCredentials::EndGuard_;
|
||||
RequestCache fallbackCache = RequestCache::EndGuard_;
|
||||
if (aInput.IsUSVString()) {
|
||||
fallbackMode = RequestMode::Cors;
|
||||
fallbackCredentials = RequestCredentials::Omit;
|
||||
fallbackCache = RequestCache::Default;
|
||||
}
|
||||
|
||||
RequestMode mode = aInit.mMode.WasPassed() ? aInit.mMode.Value() : fallbackMode;
|
||||
RequestCredentials credentials =
|
||||
aInit.mCredentials.WasPassed() ? aInit.mCredentials.Value()
|
||||
: fallbackCredentials;
|
||||
|
||||
if (mode == RequestMode::Navigate ||
|
||||
(aInit.IsAnyMemberPresent() && request->Mode() == RequestMode::Navigate)) {
|
||||
aRv.ThrowTypeError<MSG_INVALID_REQUEST_MODE>(NS_LITERAL_STRING("navigate"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aInit.IsAnyMemberPresent()) {
|
||||
request->SetReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR));
|
||||
request->SetReferrerPolicy(ReferrerPolicy::_empty);
|
||||
}
|
||||
if (aInit.mReferrer.WasPassed()) {
|
||||
const nsString& referrer = aInit.mReferrer.Value();
|
||||
if (referrer.IsEmpty()) {
|
||||
request->SetReferrer(NS_LITERAL_STRING(""));
|
||||
} else {
|
||||
nsAutoString referrerURL;
|
||||
if (NS_IsMainThread()) {
|
||||
nsIDocument* doc = GetEntryDocument();
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (doc) {
|
||||
uri = ParseURLFromDocument(doc, referrer, aRv);
|
||||
} else {
|
||||
// If we don't have a document, we must assume that this is a full URL.
|
||||
uri = ParseURLFromChrome(referrer, aRv);
|
||||
}
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
aRv.ThrowTypeError<MSG_INVALID_REFERRER_URL>(referrer);
|
||||
return nullptr;
|
||||
}
|
||||
nsAutoCString spec;
|
||||
uri->GetSpec(spec);
|
||||
CopyUTF8toUTF16(spec, referrerURL);
|
||||
if (!referrerURL.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
|
||||
nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull();
|
||||
if (principal) {
|
||||
nsresult rv = principal->CheckMayLoad(uri, /* report */ false,
|
||||
/* allowIfInheritsPrincipal */ false);
|
||||
if (NS_FAILED(rv)) {
|
||||
nsAutoCString globalOrigin;
|
||||
principal->GetOrigin(globalOrigin);
|
||||
aRv.ThrowTypeError<MSG_CROSS_ORIGIN_REFERRER_URL>(referrer,
|
||||
NS_ConvertUTF8toUTF16(globalOrigin));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RefPtr<URL> url = ParseURLFromWorker(aGlobal, referrer, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
aRv.ThrowTypeError<MSG_INVALID_REFERRER_URL>(referrer);
|
||||
return nullptr;
|
||||
}
|
||||
url->Stringify(referrerURL, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
aRv.ThrowTypeError<MSG_INVALID_REFERRER_URL>(referrer);
|
||||
return nullptr;
|
||||
}
|
||||
if (!referrerURL.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
|
||||
workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
|
||||
nsresult rv = NS_OK;
|
||||
// ReferrerSameOriginChecker uses a sync loop to get the main thread
|
||||
// to perform the same-origin check. Overall, on Workers this method
|
||||
// can create 3 sync loops (two for constructing URLs and one here) so
|
||||
// in the future we may want to optimize it all by off-loading all of
|
||||
// this work in a single sync loop.
|
||||
RefPtr<ReferrerSameOriginChecker> checker =
|
||||
new ReferrerSameOriginChecker(worker, referrerURL, rv);
|
||||
checker->Dispatch(aRv);
|
||||
if (aRv.Failed() || NS_FAILED(rv)) {
|
||||
aRv.ThrowTypeError<MSG_CROSS_ORIGIN_REFERRER_URL>(referrer,
|
||||
worker->GetLocationInfo().mOrigin);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
request->SetReferrer(referrerURL);
|
||||
}
|
||||
}
|
||||
|
||||
if (aInit.mReferrerPolicy.WasPassed()) {
|
||||
request->SetReferrerPolicy(aInit.mReferrerPolicy.Value());
|
||||
}
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
|
||||
if (window) {
|
||||
nsCOMPtr<nsIDocument> doc;
|
||||
doc = window->GetExtantDoc();
|
||||
if (doc) {
|
||||
request->SetEnvironmentReferrerPolicy(doc->GetReferrerPolicy());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
|
||||
if (worker) {
|
||||
worker->AssertIsOnWorkerThread();
|
||||
request->SetEnvironmentReferrerPolicy(worker->GetReferrerPolicy());
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != RequestMode::EndGuard_) {
|
||||
request->ClearCreatedByFetchEvent();
|
||||
request->SetMode(mode);
|
||||
}
|
||||
|
||||
if (credentials != RequestCredentials::EndGuard_) {
|
||||
request->ClearCreatedByFetchEvent();
|
||||
request->SetCredentialsMode(credentials);
|
||||
}
|
||||
|
||||
RequestCache cache = aInit.mCache.WasPassed() ?
|
||||
aInit.mCache.Value() : fallbackCache;
|
||||
if (cache != RequestCache::EndGuard_) {
|
||||
if (cache == RequestCache::Only_if_cached &&
|
||||
request->Mode() != RequestMode::Same_origin) {
|
||||
uint32_t t = static_cast<uint32_t>(request->Mode());
|
||||
NS_ConvertASCIItoUTF16 modeString(RequestModeValues::strings[t].value,
|
||||
RequestModeValues::strings[t].length);
|
||||
aRv.ThrowTypeError<MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN>(modeString);
|
||||
return nullptr;
|
||||
}
|
||||
request->ClearCreatedByFetchEvent();
|
||||
request->SetCacheMode(cache);
|
||||
}
|
||||
|
||||
if (aInit.mRedirect.WasPassed()) {
|
||||
request->SetRedirectMode(aInit.mRedirect.Value());
|
||||
}
|
||||
|
||||
if (aInit.mIntegrity.WasPassed()) {
|
||||
request->SetIntegrity(aInit.mIntegrity.Value());
|
||||
}
|
||||
|
||||
// Request constructor step 14.
|
||||
if (aInit.mMethod.WasPassed()) {
|
||||
nsAutoCString method(aInit.mMethod.Value());
|
||||
|
||||
// Step 14.1. Disallow forbidden methods, and anything that is not a HTTP
|
||||
// token, since HTTP states that Method may be any of the defined values or
|
||||
// a token (extension method).
|
||||
nsAutoCString outMethod;
|
||||
nsresult rv = FetchUtil::GetValidRequestMethod(method, outMethod);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ConvertUTF8toUTF16 label(method);
|
||||
aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(label);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 14.2
|
||||
request->ClearCreatedByFetchEvent();
|
||||
request->SetMethod(outMethod);
|
||||
}
|
||||
|
||||
RefPtr<InternalHeaders> requestHeaders = request->Headers();
|
||||
|
||||
RefPtr<InternalHeaders> headers;
|
||||
if (aInit.mHeaders.WasPassed()) {
|
||||
RefPtr<Headers> h = Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
request->ClearCreatedByFetchEvent();
|
||||
headers = h->GetInternalHeaders();
|
||||
} else {
|
||||
headers = new InternalHeaders(*requestHeaders);
|
||||
}
|
||||
|
||||
requestHeaders->Clear();
|
||||
// From "Let r be a new Request object associated with request and a new
|
||||
// Headers object whose guard is "request"."
|
||||
requestHeaders->SetGuard(HeadersGuardEnum::Request, aRv);
|
||||
MOZ_ASSERT(!aRv.Failed());
|
||||
|
||||
if (request->Mode() == RequestMode::No_cors) {
|
||||
if (!request->HasSimpleMethod()) {
|
||||
nsAutoCString method;
|
||||
request->GetMethod(method);
|
||||
NS_ConvertUTF8toUTF16 label(method);
|
||||
aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(label);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!request->GetIntegrity().IsEmpty()) {
|
||||
aRv.ThrowTypeError<MSG_REQUEST_INTEGRITY_METADATA_NOT_EMPTY>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
requestHeaders->SetGuard(HeadersGuardEnum::Request_no_cors, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
requestHeaders->Fill(*headers, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((aInit.mBody.WasPassed() && !aInit.mBody.Value().IsNull()) ||
|
||||
temporaryBody) {
|
||||
// HEAD and GET are not allowed to have a body.
|
||||
nsAutoCString method;
|
||||
request->GetMethod(method);
|
||||
// method is guaranteed to be uppercase due to step 14.2 above.
|
||||
if (method.EqualsLiteral("HEAD") || method.EqualsLiteral("GET")) {
|
||||
aRv.ThrowTypeError<MSG_NO_BODY_ALLOWED_FOR_GET_AND_HEAD>();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (aInit.mBody.WasPassed()) {
|
||||
const Nullable<OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& bodyInitNullable =
|
||||
aInit.mBody.Value();
|
||||
if (!bodyInitNullable.IsNull()) {
|
||||
const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit =
|
||||
bodyInitNullable.Value();
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
nsAutoCString contentType;
|
||||
uint64_t contentLengthUnused;
|
||||
aRv = ExtractByteStreamFromBody(bodyInit,
|
||||
getter_AddRefs(stream),
|
||||
contentType,
|
||||
contentLengthUnused);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
temporaryBody = stream;
|
||||
|
||||
if (!contentType.IsVoid() &&
|
||||
!requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
|
||||
requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
|
||||
contentType, aRv);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
request->ClearCreatedByFetchEvent();
|
||||
request->SetBody(temporaryBody);
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<Request> domRequest = new Request(global, request);
|
||||
domRequest->SetMimeType();
|
||||
|
||||
if (aInput.IsRequest()) {
|
||||
RefPtr<Request> inputReq = &aInput.GetAsRequest();
|
||||
nsCOMPtr<nsIInputStream> body;
|
||||
inputReq->GetBody(getter_AddRefs(body));
|
||||
if (body) {
|
||||
inputReq->SetBody(nullptr);
|
||||
inputReq->SetBodyUsed();
|
||||
}
|
||||
}
|
||||
return domRequest.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Request>
|
||||
Request::Clone(ErrorResult& aRv) const
|
||||
{
|
||||
if (BodyUsed()) {
|
||||
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<InternalRequest> ir = mRequest->Clone();
|
||||
if (!ir) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Request> request = new Request(mOwner, ir);
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
Headers*
|
||||
Request::Headers_()
|
||||
{
|
||||
if (!mHeaders) {
|
||||
mHeaders = new Headers(mOwner, mRequest->Headers());
|
||||
}
|
||||
|
||||
return mHeaders;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
167
dom/fetch/Request.h
Normal file
167
dom/fetch/Request.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_Request_h
|
||||
#define mozilla_dom_Request_h
|
||||
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
#include "mozilla/dom/Fetch.h"
|
||||
#include "mozilla/dom/InternalRequest.h"
|
||||
// Required here due to certain WebIDL enums/classes being declared in both
|
||||
// files.
|
||||
#include "mozilla/dom/RequestBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Headers;
|
||||
class InternalHeaders;
|
||||
class RequestOrUSVString;
|
||||
|
||||
class Request final : public nsISupports
|
||||
, public FetchBody<Request>
|
||||
, public nsWrapperCache
|
||||
{
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Request)
|
||||
|
||||
public:
|
||||
Request(nsIGlobalObject* aOwner, InternalRequest* aRequest);
|
||||
|
||||
static bool
|
||||
RequestContextEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
return RequestBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
GetUrl(nsAString& aUrl) const
|
||||
{
|
||||
nsAutoCString url;
|
||||
mRequest->GetURL(url);
|
||||
CopyUTF8toUTF16(url, aUrl);
|
||||
}
|
||||
|
||||
void
|
||||
GetMethod(nsCString& aMethod) const
|
||||
{
|
||||
aMethod = mRequest->mMethod;
|
||||
}
|
||||
|
||||
RequestMode
|
||||
Mode() const
|
||||
{
|
||||
return mRequest->mMode;
|
||||
}
|
||||
|
||||
RequestCredentials
|
||||
Credentials() const
|
||||
{
|
||||
return mRequest->mCredentialsMode;
|
||||
}
|
||||
|
||||
RequestCache
|
||||
Cache() const
|
||||
{
|
||||
return mRequest->GetCacheMode();
|
||||
}
|
||||
|
||||
RequestRedirect
|
||||
Redirect() const
|
||||
{
|
||||
return mRequest->GetRedirectMode();
|
||||
}
|
||||
|
||||
void
|
||||
GetIntegrity(nsAString& aIntegrity) const
|
||||
{
|
||||
aIntegrity = mRequest->GetIntegrity();
|
||||
}
|
||||
|
||||
RequestContext
|
||||
Context() const
|
||||
{
|
||||
return mRequest->Context();
|
||||
}
|
||||
|
||||
void
|
||||
OverrideContentPolicyType(nsContentPolicyType aContentPolicyType)
|
||||
{
|
||||
mRequest->OverrideContentPolicyType(aContentPolicyType);
|
||||
}
|
||||
|
||||
bool
|
||||
IsContentPolicyTypeOverridden() const
|
||||
{
|
||||
return mRequest->IsContentPolicyTypeOverridden();
|
||||
}
|
||||
|
||||
void
|
||||
GetReferrer(nsAString& aReferrer) const
|
||||
{
|
||||
mRequest->GetReferrer(aReferrer);
|
||||
}
|
||||
|
||||
ReferrerPolicy
|
||||
ReferrerPolicy_() const
|
||||
{
|
||||
return mRequest->ReferrerPolicy_();
|
||||
}
|
||||
|
||||
InternalHeaders*
|
||||
GetInternalHeaders() const
|
||||
{
|
||||
return mRequest->Headers();
|
||||
}
|
||||
|
||||
Headers* Headers_();
|
||||
|
||||
void
|
||||
GetBody(nsIInputStream** aStream) { return mRequest->GetBody(aStream); }
|
||||
|
||||
void
|
||||
SetBody(nsIInputStream* aStream) { return mRequest->SetBody(aStream); }
|
||||
|
||||
static already_AddRefed<Request>
|
||||
Constructor(const GlobalObject& aGlobal, const RequestOrUSVString& aInput,
|
||||
const RequestInit& aInit, ErrorResult& rv);
|
||||
|
||||
nsIGlobalObject* GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
already_AddRefed<Request>
|
||||
Clone(ErrorResult& aRv) const;
|
||||
|
||||
already_AddRefed<InternalRequest>
|
||||
GetInternalRequest();
|
||||
|
||||
const UniquePtr<mozilla::ipc::PrincipalInfo>&
|
||||
GetPrincipalInfo() const
|
||||
{
|
||||
return mRequest->GetPrincipalInfo();
|
||||
}
|
||||
|
||||
private:
|
||||
~Request();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mOwner;
|
||||
RefPtr<InternalRequest> mRequest;
|
||||
// Lazily created.
|
||||
RefPtr<Headers> mHeaders;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_Request_h
|
||||
281
dom/fetch/Response.cpp
Normal file
281
dom/fetch/Response.cpp
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
/* -*- 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 "Response.h"
|
||||
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/FetchBinding.h"
|
||||
#include "mozilla/dom/Headers.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/URL.h"
|
||||
|
||||
#include "nsDOMString.h"
|
||||
|
||||
#include "InternalResponse.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Response)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Response)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Response, mOwner, mHeaders)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Response)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse)
|
||||
: FetchBody<Response>()
|
||||
, mOwner(aGlobal)
|
||||
, mInternalResponse(aInternalResponse)
|
||||
{
|
||||
MOZ_ASSERT(aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Immutable ||
|
||||
aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Response);
|
||||
SetMimeType();
|
||||
}
|
||||
|
||||
Response::~Response()
|
||||
{
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<Response>
|
||||
Response::Error(const GlobalObject& aGlobal)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
RefPtr<InternalResponse> error = InternalResponse::NetworkError();
|
||||
RefPtr<Response> r = new Response(global, error);
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<Response>
|
||||
Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
uint16_t aStatus, ErrorResult& aRv)
|
||||
{
|
||||
nsAutoString parsedURL;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIURI> baseURI;
|
||||
nsIDocument* doc = GetEntryDocument();
|
||||
if (doc) {
|
||||
baseURI = doc->GetBaseURI();
|
||||
}
|
||||
nsCOMPtr<nsIURI> resolvedURI;
|
||||
aRv = NS_NewURI(getter_AddRefs(resolvedURI), aUrl, nullptr, baseURI);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoCString spec;
|
||||
aRv = resolvedURI->GetSpec(spec);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CopyUTF8toUTF16(spec, parsedURL);
|
||||
} else {
|
||||
workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
|
||||
NS_ConvertUTF8toUTF16 baseURL(worker->GetLocationInfo().mHref);
|
||||
RefPtr<URL> url = URL::WorkerConstructor(aGlobal, aUrl, baseURL, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
url->Stringify(parsedURL, aRv);
|
||||
}
|
||||
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aStatus != 301 && aStatus != 302 && aStatus != 303 && aStatus != 307 && aStatus != 308) {
|
||||
aRv.ThrowRangeError<MSG_INVALID_REDIRECT_STATUSCODE_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Optional<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams> body;
|
||||
ResponseInit init;
|
||||
init.mStatus = aStatus;
|
||||
RefPtr<Response> r = Response::Constructor(aGlobal, body, init, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
r->GetInternalHeaders()->Set(NS_LITERAL_CSTRING("Location"),
|
||||
NS_ConvertUTF16toUTF8(parsedURL), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
r->GetInternalHeaders()->SetGuard(HeadersGuardEnum::Immutable, aRv);
|
||||
MOZ_ASSERT(!aRv.Failed());
|
||||
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
/*static*/ already_AddRefed<Response>
|
||||
Response::Constructor(const GlobalObject& aGlobal,
|
||||
const Optional<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& aBody,
|
||||
const ResponseInit& aInit, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
|
||||
if (aInit.mStatus < 200 || aInit.mStatus > 599) {
|
||||
aRv.ThrowRangeError<MSG_INVALID_RESPONSE_STATUSCODE_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the status text contains illegal characters
|
||||
nsACString::const_iterator start, end;
|
||||
aInit.mStatusText.BeginReading(start);
|
||||
aInit.mStatusText.EndReading(end);
|
||||
if (FindCharInReadable('\r', start, end)) {
|
||||
aRv.ThrowTypeError<MSG_RESPONSE_INVALID_STATUSTEXT_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
// Reset iterator since FindCharInReadable advances it.
|
||||
aInit.mStatusText.BeginReading(start);
|
||||
if (FindCharInReadable('\n', start, end)) {
|
||||
aRv.ThrowTypeError<MSG_RESPONSE_INVALID_STATUSTEXT_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<InternalResponse> internalResponse =
|
||||
new InternalResponse(aInit.mStatus, aInit.mStatusText);
|
||||
|
||||
// Grab a valid channel info from the global so this response is 'valid' for
|
||||
// interception.
|
||||
if (NS_IsMainThread()) {
|
||||
ChannelInfo info;
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
|
||||
if (window) {
|
||||
nsIDocument* doc = window->GetExtantDoc();
|
||||
MOZ_ASSERT(doc);
|
||||
info.InitFromDocument(doc);
|
||||
} else {
|
||||
info.InitFromChromeGlobal(global);
|
||||
}
|
||||
internalResponse->InitChannelInfo(info);
|
||||
} else {
|
||||
workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
internalResponse->InitChannelInfo(worker->GetChannelInfo());
|
||||
}
|
||||
|
||||
RefPtr<Response> r = new Response(global, internalResponse);
|
||||
|
||||
if (aInit.mHeaders.WasPassed()) {
|
||||
internalResponse->Headers()->Clear();
|
||||
|
||||
// Instead of using Fill, create an object to allow the constructor to
|
||||
// unwrap the HeadersInit.
|
||||
RefPtr<Headers> headers =
|
||||
Headers::Create(global, aInit.mHeaders.Value(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
internalResponse->Headers()->Fill(*headers->GetInternalHeaders(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (aBody.WasPassed()) {
|
||||
if (aInit.mStatus == 204 || aInit.mStatus == 205 || aInit.mStatus == 304) {
|
||||
aRv.ThrowTypeError<MSG_RESPONSE_NULL_STATUS_WITH_BODY>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> bodyStream;
|
||||
nsCString contentType;
|
||||
uint64_t bodySize = 0;
|
||||
aRv = ExtractByteStreamFromBody(aBody.Value(),
|
||||
getter_AddRefs(bodyStream),
|
||||
contentType,
|
||||
bodySize);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
internalResponse->SetBody(bodyStream, bodySize);
|
||||
|
||||
if (!contentType.IsVoid() &&
|
||||
!internalResponse->Headers()->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
|
||||
// Ignore Append() failing here.
|
||||
ErrorResult error;
|
||||
internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, error);
|
||||
error.SuppressException();
|
||||
}
|
||||
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
r->SetMimeType();
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Response>
|
||||
Response::Clone(ErrorResult& aRv) const
|
||||
{
|
||||
if (BodyUsed()) {
|
||||
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<InternalResponse> ir = mInternalResponse->Clone();
|
||||
RefPtr<Response> response = new Response(mOwner, ir);
|
||||
return response.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Response>
|
||||
Response::CloneUnfiltered(ErrorResult& aRv) const
|
||||
{
|
||||
if (BodyUsed()) {
|
||||
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<InternalResponse> clone = mInternalResponse->Clone();
|
||||
RefPtr<InternalResponse> ir = clone->Unfiltered();
|
||||
RefPtr<Response> ref = new Response(mOwner, ir);
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
void
|
||||
Response::SetBody(nsIInputStream* aBody, int64_t aBodySize)
|
||||
{
|
||||
MOZ_ASSERT(!BodyUsed());
|
||||
mInternalResponse->SetBody(aBody, aBodySize);
|
||||
}
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
Response::GetInternalResponse() const
|
||||
{
|
||||
RefPtr<InternalResponse> ref = mInternalResponse;
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
Headers*
|
||||
Response::Headers_()
|
||||
{
|
||||
if (!mHeaders) {
|
||||
mHeaders = new Headers(mOwner, mInternalResponse->Headers());
|
||||
}
|
||||
|
||||
return mHeaders;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
149
dom/fetch/Response.h
Normal file
149
dom/fetch/Response.h
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_Response_h
|
||||
#define mozilla_dom_Response_h
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
#include "mozilla/dom/Fetch.h"
|
||||
#include "mozilla/dom/ResponseBinding.h"
|
||||
|
||||
#include "InternalHeaders.h"
|
||||
#include "InternalResponse.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
class PrincipalInfo;
|
||||
} // namespace ipc
|
||||
|
||||
namespace dom {
|
||||
|
||||
class Headers;
|
||||
|
||||
class Response final : public nsISupports
|
||||
, public FetchBody<Response>
|
||||
, public nsWrapperCache
|
||||
{
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Response)
|
||||
|
||||
public:
|
||||
Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse);
|
||||
|
||||
Response(const Response& aOther) = delete;
|
||||
|
||||
JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
return ResponseBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
ResponseType
|
||||
Type() const
|
||||
{
|
||||
return mInternalResponse->Type();
|
||||
}
|
||||
void
|
||||
GetUrl(nsAString& aUrl) const
|
||||
{
|
||||
CopyUTF8toUTF16(mInternalResponse->GetURL(), aUrl);
|
||||
}
|
||||
bool
|
||||
Redirected() const
|
||||
{
|
||||
return mInternalResponse->IsRedirected();
|
||||
}
|
||||
uint16_t
|
||||
Status() const
|
||||
{
|
||||
return mInternalResponse->GetStatus();
|
||||
}
|
||||
|
||||
bool
|
||||
Ok() const
|
||||
{
|
||||
return mInternalResponse->GetStatus() >= 200 &&
|
||||
mInternalResponse->GetStatus() <= 299;
|
||||
}
|
||||
|
||||
void
|
||||
GetStatusText(nsCString& aStatusText) const
|
||||
{
|
||||
aStatusText = mInternalResponse->GetStatusText();
|
||||
}
|
||||
|
||||
InternalHeaders*
|
||||
GetInternalHeaders() const
|
||||
{
|
||||
return mInternalResponse->Headers();
|
||||
}
|
||||
|
||||
void
|
||||
InitChannelInfo(nsIChannel* aChannel)
|
||||
{
|
||||
mInternalResponse->InitChannelInfo(aChannel);
|
||||
}
|
||||
|
||||
const ChannelInfo&
|
||||
GetChannelInfo() const
|
||||
{
|
||||
return mInternalResponse->GetChannelInfo();
|
||||
}
|
||||
|
||||
const UniquePtr<mozilla::ipc::PrincipalInfo>&
|
||||
GetPrincipalInfo() const
|
||||
{
|
||||
return mInternalResponse->GetPrincipalInfo();
|
||||
}
|
||||
|
||||
Headers* Headers_();
|
||||
|
||||
void
|
||||
GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); }
|
||||
|
||||
static already_AddRefed<Response>
|
||||
Error(const GlobalObject& aGlobal);
|
||||
|
||||
static already_AddRefed<Response>
|
||||
Redirect(const GlobalObject& aGlobal, const nsAString& aUrl, uint16_t aStatus, ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<Response>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const Optional<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& aBody,
|
||||
const ResponseInit& aInit, ErrorResult& rv);
|
||||
|
||||
nsIGlobalObject* GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
already_AddRefed<Response>
|
||||
Clone(ErrorResult& aRv) const;
|
||||
|
||||
already_AddRefed<Response>
|
||||
CloneUnfiltered(ErrorResult& aRv) const;
|
||||
|
||||
void
|
||||
SetBody(nsIInputStream* aBody, int64_t aBodySize);
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
GetInternalResponse() const;
|
||||
|
||||
private:
|
||||
~Response();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mOwner;
|
||||
RefPtr<InternalResponse> mInternalResponse;
|
||||
// Lazily created
|
||||
RefPtr<Headers> mHeaders;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_Response_h
|
||||
52
dom/fetch/moz.build
Normal file
52
dom/fetch/moz.build
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'ChannelInfo.h',
|
||||
'Fetch.h',
|
||||
'FetchDriver.h',
|
||||
'FetchIPCTypes.h',
|
||||
'FetchUtil.h',
|
||||
'Headers.h',
|
||||
'InternalHeaders.h',
|
||||
'InternalRequest.h',
|
||||
'InternalResponse.h',
|
||||
'Request.h',
|
||||
'Response.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ChannelInfo.cpp',
|
||||
'Fetch.cpp',
|
||||
'FetchConsumer.cpp',
|
||||
'FetchDriver.cpp',
|
||||
'FetchUtil.cpp',
|
||||
'Headers.cpp',
|
||||
'InternalHeaders.cpp',
|
||||
'InternalRequest.cpp',
|
||||
'InternalResponse.cpp',
|
||||
'Request.cpp',
|
||||
'Response.cpp',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'ChannelInfo.ipdlh',
|
||||
'FetchTypes.ipdlh',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../workers',
|
||||
# For HttpBaseChannel.h dependencies
|
||||
'/netwerk/base',
|
||||
# For nsDataHandler.h
|
||||
'/netwerk/protocol/data',
|
||||
# For HttpBaseChannel.h
|
||||
'/netwerk/protocol/http',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
Loading…
Add table
Add a link
Reference in a new issue