/* -*- 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 "mozilla/dom/Document.h" #include "nsStreamUtils.h" #include "mozilla/ErrorResult.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" namespace mozilla { namespace dom { // The global is used to extract the principal. SafeRefPtr 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(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::Clone() { auto clone = MakeSafeRefPtr(*this, ConstructorGuard{}); if (!mBodyStream) { return clone; } nsCOMPtr clonedBody; nsCOMPtr 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(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 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())), mBodyStream(mozilla::ipc::DeserializeIPCStream(aIPCRequest.body())), mBodyLength(aIPCRequest.bodySize()), mPreferredAlternativeDataType(aIPCRequest.preferredAlternativeDataType()), mContentPolicyType( static_cast(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( aIPCRequest.principalInfo().ref()); } } InternalRequest::~InternalRequest() = default; template void InternalRequest::ToIPC( IPCInternalRequest* aIPCRequest, mozilla::ipc::PBackgroundChild* aManager, UniquePtr& aAutoStream); template void InternalRequest::ToIPC( IPCInternalRequest* aIPCRequest, M* aManager, UniquePtr& aAutoStream) { MOZ_ASSERT(aIPCRequest); MOZ_ASSERT(aManager); MOZ_ASSERT(!mURLList.IsEmpty()); aIPCRequest->method() = mMethod; aIPCRequest->urlList() = mURLList.Clone(); mHeaders->ToIPC(aIPCRequest->headers(), aIPCRequest->headersGuard()); if (mBodyStream) { aAutoStream.reset(new mozilla::ipc::AutoIPCStream(aIPCRequest->body())); DebugOnly ok = aAutoStream->Serialize(mBodyStream, aManager); MOZ_ASSERT(ok); } aIPCRequest->bodySize() = mBodyLength; aIPCRequest->preferredAlternativeDataType() = mPreferredAlternativeDataType; aIPCRequest->contentPolicyType() = static_cast(mContentPolicyType); aIPCRequest->referrer() = mReferrer; aIPCRequest->referrerPolicy() = mReferrerPolicy; aIPCRequest->requestMode() = mMode; aIPCRequest->requestCredentials() = mCredentialsMode; aIPCRequest->cacheMode() = mCacheMode; aIPCRequest->requestRedirect() = mRedirectMode; aIPCRequest->integrity() = mIntegrity; aIPCRequest->fragment() = mFragment; if (mPrincipalInfo) { aIPCRequest->principalInfo().emplace(*mPrincipalInfo); } } void InternalRequest::SetContentPolicyType( nsContentPolicyType aContentPolicyType) { mContentPolicyType = aContentPolicyType; } void InternalRequest::OverrideContentPolicyType( nsContentPolicyType aContentPolicyType) { SetContentPolicyType(aContentPolicyType); mContentPolicyTypeOverridden = true; } /* static */ RequestDestination InternalRequest::MapContentPolicyTypeToRequestDestination( nsContentPolicyType aContentPolicyType) { RequestDestination destination = RequestDestination::_empty; switch (aContentPolicyType) { case nsIContentPolicy::TYPE_OTHER: destination = RequestDestination::_empty; break; 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_SCRIPT: destination = RequestDestination::Script; break; case nsIContentPolicy::TYPE_INTERNAL_WORKER: destination = RequestDestination::Worker; break; case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER: destination = RequestDestination::Sharedworker; break; 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: destination = RequestDestination::Image; break; case nsIContentPolicy::TYPE_STYLESHEET: case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET: case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD: destination = RequestDestination::Style; break; case nsIContentPolicy::TYPE_OBJECT: case nsIContentPolicy::TYPE_INTERNAL_OBJECT: destination = RequestDestination::Object; break; case nsIContentPolicy::TYPE_INTERNAL_EMBED: destination = RequestDestination::Embed; break; case nsIContentPolicy::TYPE_DOCUMENT: case nsIContentPolicy::TYPE_SUBDOCUMENT: case nsIContentPolicy::TYPE_INTERNAL_IFRAME: destination = RequestDestination::Document; break; case nsIContentPolicy::TYPE_INTERNAL_FRAME: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_REFRESH: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_XBL: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_PING: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_XMLHTTPREQUEST: case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_DTD: case nsIContentPolicy::TYPE_INTERNAL_DTD: case nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_FONT: destination = RequestDestination::Font; break; case nsIContentPolicy::TYPE_MEDIA: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_INTERNAL_AUDIO: destination = RequestDestination::Audio; break; case nsIContentPolicy::TYPE_INTERNAL_VIDEO: destination = RequestDestination::Video; break; case nsIContentPolicy::TYPE_INTERNAL_TRACK: destination = RequestDestination::Track; break; case nsIContentPolicy::TYPE_WEBSOCKET: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_CSP_REPORT: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_XSLT: destination = RequestDestination::Xslt; break; case nsIContentPolicy::TYPE_BEACON: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_FETCH: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_WEB_MANIFEST: destination = RequestDestination::Manifest; break; case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_SPECULATIVE: destination = RequestDestination::_empty; break; case nsIContentPolicy::TYPE_INTERNAL_AUDIOWORKLET: destination = RequestDestination::Audioworklet; break; case nsIContentPolicy::TYPE_INTERNAL_PAINTWORKLET: destination = RequestDestination::Paintworklet; break; default: MOZ_ASSERT(false, "Unhandled nsContentPolicyType value"); break; } return destination; } // 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" 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 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_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: MOZ_ASSERT_UNREACHABLE("Unexpected security mode!"); return RequestMode::Same_origin; } } // static RequestCredentials InternalRequest::MapChannelToRequestCredentials( nsIChannel* aChannel) { MOZ_ASSERT(aChannel); nsCOMPtr 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 aPrincipalInfo) { mPrincipalInfo = std::move(aPrincipalInfo); } } // namespace dom } // namespace mozilla