diff options
Diffstat (limited to '')
-rw-r--r-- | dom/fetch/InternalRequest.cpp | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/dom/fetch/InternalRequest.cpp b/dom/fetch/InternalRequest.cpp new file mode 100644 index 0000000000..0f55a8486a --- /dev/null +++ b/dom/fetch/InternalRequest.cpp @@ -0,0 +1,399 @@ +/* -*- 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 "InternalResponse.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/FetchTypes.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/WorkerCommon.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/ipc/IPCStreamUtils.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "mozilla/RemoteLazyInputStreamChild.h" +#include "nsIContentPolicy.h" +#include "nsStreamUtils.h" + +namespace mozilla::dom { +// The global is used to extract the principal. +SafeRefPtr<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."); + auto copy = + MakeSafeRefPtr<InternalRequest>(mURLList.LastElement(), mFragment); + copy->SetMethod(mMethod); + copy->mHeaders = new InternalHeaders(*mHeaders); + copy->SetUnsafeRequest(); + copy->mBodyStream = mBodyStream; + copy->mBodyLength = mBodyLength; + // 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->mReferrer = mReferrer; + copy->mReferrerPolicy = mReferrerPolicy; + copy->mEnvironmentReferrerPolicy = mEnvironmentReferrerPolicy; + copy->mIntegrity = mIntegrity; + copy->mMozErrors = mMozErrors; + + copy->mContentPolicyType = mContentPolicyTypeOverridden + ? mContentPolicyType + : nsIContentPolicy::TYPE_FETCH; + copy->mMode = mMode; + copy->mCredentialsMode = mCredentialsMode; + copy->mCacheMode = mCacheMode; + copy->mRedirectMode = mRedirectMode; + copy->mContentPolicyTypeOverridden = mContentPolicyTypeOverridden; + + copy->mPreferredAlternativeDataType = mPreferredAlternativeDataType; + return copy; +} + +SafeRefPtr<InternalRequest> InternalRequest::Clone() { + auto clone = MakeSafeRefPtr<InternalRequest>(*this, ConstructorGuard{}); + + if (!mBodyStream) { + return clone; + } + + 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; +} +InternalRequest::InternalRequest(const nsACString& aURL, + const nsACString& aFragment) + : mMethod("GET"), + mHeaders(new InternalHeaders(HeadersGuardEnum::None)), + mBodyLength(InternalResponse::UNKNOWN_BODY_SIZE), + mContentPolicyType(nsIContentPolicy::TYPE_FETCH), + mReferrer(NS_LITERAL_STRING_FROM_CSTRING(kFETCH_CLIENT_REFERRER_STR)), + mReferrerPolicy(ReferrerPolicy::_empty), + mEnvironmentReferrerPolicy(ReferrerPolicy::_empty), + mMode(RequestMode::No_cors), + mCredentialsMode(RequestCredentials::Omit), + mCacheMode(RequestCache::Default), + mRedirectMode(RequestRedirect::Follow) { + 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), + mBodyLength(InternalResponse::UNKNOWN_BODY_SIZE), + mContentPolicyType(aContentPolicyType), + mReferrer(aReferrer), + mReferrerPolicy(aReferrerPolicy), + mEnvironmentReferrerPolicy(ReferrerPolicy::_empty), + mMode(aMode), + mCredentialsMode(aRequestCredentials), + mCacheMode(aCacheMode), + mRedirectMode(aRequestRedirect), + mIntegrity(aIntegrity) { + MOZ_ASSERT(!aURL.IsEmpty()); + AddURL(aURL, aFragment); +} +InternalRequest::InternalRequest(const InternalRequest& aOther, + ConstructorGuard) + : mMethod(aOther.mMethod), + mURLList(aOther.mURLList.Clone()), + mHeaders(new InternalHeaders(*aOther.mHeaders)), + mBodyLength(InternalResponse::UNKNOWN_BODY_SIZE), + 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), + mMozErrors(aOther.mMozErrors), + mFragment(aOther.mFragment), + mSkipServiceWorker(aOther.mSkipServiceWorker), + mSynchronous(aOther.mSynchronous), + mUnsafeRequest(aOther.mUnsafeRequest), + mUseURLCredentials(aOther.mUseURLCredentials), + 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.urlList().Clone()), + mHeaders(new InternalHeaders(aIPCRequest.headers(), + aIPCRequest.headersGuard())), + mBodyLength(aIPCRequest.bodySize()), + mPreferredAlternativeDataType(aIPCRequest.preferredAlternativeDataType()), + mContentPolicyType( + static_cast<nsContentPolicyType>(aIPCRequest.contentPolicyType())), + mReferrer(aIPCRequest.referrer()), + mReferrerPolicy(aIPCRequest.referrerPolicy()), + mMode(aIPCRequest.requestMode()), + mCredentialsMode(aIPCRequest.requestCredentials()), + mCacheMode(aIPCRequest.cacheMode()), + mRedirectMode(aIPCRequest.requestRedirect()), + mIntegrity(aIPCRequest.integrity()), + mFragment(aIPCRequest.fragment()) { + if (aIPCRequest.principalInfo()) { + mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>( + aIPCRequest.principalInfo().ref()); + } + + const Maybe<BodyStreamVariant>& body = aIPCRequest.body(); + + // This constructor is (currently) only used for parent -> child communication + // (constructed on the child side). + if (body) { + MOZ_ASSERT(body->type() == BodyStreamVariant::TParentToChildStream); + mBodyStream = static_cast<RemoteLazyInputStreamChild*>( + body->get_ParentToChildStream().actorChild()) + ->CreateStream(); + } +} + +InternalRequest::~InternalRequest() = default; + +void InternalRequest::SetContentPolicyType( + nsContentPolicyType aContentPolicyType) { + mContentPolicyType = aContentPolicyType; +} + +void InternalRequest::OverrideContentPolicyType( + nsContentPolicyType aContentPolicyType) { + SetContentPolicyType(aContentPolicyType); + mContentPolicyTypeOverridden = true; +} + +/* static */ +RequestDestination InternalRequest::MapContentPolicyTypeToRequestDestination( + nsContentPolicyType aContentPolicyType) { + switch (aContentPolicyType) { + case nsIContentPolicy::TYPE_OTHER: + return RequestDestination::_empty; + case nsIContentPolicy::TYPE_INTERNAL_SCRIPT: + case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD: + case nsIContentPolicy::TYPE_INTERNAL_MODULE: + case nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD: + case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER: + case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS: + case nsIContentPolicy::TYPE_INTERNAL_CHROMEUTILS_COMPILED_SCRIPT: + case nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT: + case nsIContentPolicy::TYPE_SCRIPT: + return RequestDestination::Script; + case nsIContentPolicy::TYPE_INTERNAL_WORKER: + return RequestDestination::Worker; + case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER: + return RequestDestination::Sharedworker; + case nsIContentPolicy::TYPE_IMAGESET: + case nsIContentPolicy::TYPE_INTERNAL_IMAGE: + case nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD: + case nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON: + case nsIContentPolicy::TYPE_IMAGE: + return RequestDestination::Image; + case nsIContentPolicy::TYPE_STYLESHEET: + case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET: + case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD: + return RequestDestination::Style; + case nsIContentPolicy::TYPE_OBJECT: + case nsIContentPolicy::TYPE_INTERNAL_OBJECT: + return RequestDestination::Object; + case nsIContentPolicy::TYPE_INTERNAL_EMBED: + return RequestDestination::Embed; + case nsIContentPolicy::TYPE_DOCUMENT: + return RequestDestination::Document; + case nsIContentPolicy::TYPE_SUBDOCUMENT: + case nsIContentPolicy::TYPE_INTERNAL_IFRAME: + return RequestDestination::Iframe; + case nsIContentPolicy::TYPE_INTERNAL_FRAME: + return RequestDestination::Frame; + case nsIContentPolicy::TYPE_PING: + return RequestDestination::_empty; + case nsIContentPolicy::TYPE_XMLHTTPREQUEST: + case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST: + return RequestDestination::_empty; + case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE: + return RequestDestination::_empty; + case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST: + return RequestDestination::_empty; + case nsIContentPolicy::TYPE_DTD: + case nsIContentPolicy::TYPE_INTERNAL_DTD: + case nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD: + return RequestDestination::_empty; + case nsIContentPolicy::TYPE_FONT: + case nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD: + return RequestDestination::Font; + case nsIContentPolicy::TYPE_MEDIA: + return RequestDestination::_empty; + case nsIContentPolicy::TYPE_INTERNAL_AUDIO: + return RequestDestination::Audio; + case nsIContentPolicy::TYPE_INTERNAL_VIDEO: + return RequestDestination::Video; + case nsIContentPolicy::TYPE_INTERNAL_TRACK: + return RequestDestination::Track; + case nsIContentPolicy::TYPE_WEBSOCKET: + return RequestDestination::_empty; + case nsIContentPolicy::TYPE_CSP_REPORT: + return RequestDestination::Report; + case nsIContentPolicy::TYPE_XSLT: + return RequestDestination::Xslt; + case nsIContentPolicy::TYPE_BEACON: + return RequestDestination::_empty; + case nsIContentPolicy::TYPE_FETCH: + case nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD: + return RequestDestination::_empty; + case nsIContentPolicy::TYPE_WEB_MANIFEST: + return RequestDestination::Manifest; + case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD: + return RequestDestination::_empty; + case nsIContentPolicy::TYPE_SPECULATIVE: + return RequestDestination::_empty; + case nsIContentPolicy::TYPE_INTERNAL_AUDIOWORKLET: + return RequestDestination::Audioworklet; + case nsIContentPolicy::TYPE_INTERNAL_PAINTWORKLET: + return RequestDestination::Paintworklet; + case nsIContentPolicy::TYPE_INVALID: + break; + // Do not add default: so that compilers can catch the missing case. + } + + MOZ_ASSERT(false, "Unhandled nsContentPolicyType value"); + return RequestDestination::_empty; +} + +// 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. + return aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT || + aContentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT || + aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME || + aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME; +} + +// 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" RequestDestination. + // 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 = aChannel->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_INHERITS_SEC_CONTEXT: + case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED: + return RequestMode::Same_origin; + case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT: + case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL: + return RequestMode::No_cors; + case nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT: + // TODO: Check additional flag force-preflight after bug 1199693 (bug + // 1189945) + return RequestMode::Cors; + default: + MOZ_ASSERT_UNREACHABLE("Unexpected security mode!"); + return RequestMode::Same_origin; + } +} + +// static +RequestCredentials InternalRequest::MapChannelToRequestCredentials( + nsIChannel* aChannel) { + MOZ_ASSERT(aChannel); + + nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); + + 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 = std::move(aPrincipalInfo); +} + +} // namespace mozilla::dom |