SSL nsDocShellLoadState.cpp
Interaktion und PortierbarkeitC
/* -*- 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/. */
nsDocShellLoadState::nsDocShellLoadState( const DocShellLoadStateInit& aLoadState, mozilla::ipc::IProtocol* aActor, bool* aReadSuccess)
: mNotifiedBeforeUnloadListeners(false),
mLoadIdentifier(aLoadState.LoadIdentifier()) { // If we return early, we failed to read in the data.
*aReadSuccess = false; if (!aLoadState.URI()) {
MOZ_ASSERT_UNREACHABLE("Cannot create a LoadState with a null URI!"); return;
}
// We know this was created remotely, as we just received it over IPC.
mWasCreatedRemotely = true;
// If we're in the parent process, potentially validate against a LoadState // which we sent to the source content process. if (XRE_IsParentProcess()) {
mozilla::ipc::IToplevelProtocol* top = aActor->ToplevelProtocol(); if (!top ||
top->GetProtocolId() != mozilla::ipc::ProtocolId::PContentMsgStart ||
top->GetSide() != mozilla::ipc::ParentSide) {
aActor->FatalError("nsDocShellLoadState must be received over PContent"); return;
}
ContentParent* cp = static_cast<ContentParent*>(top);
// If this load was sent down to the content process as a navigation // request, ensure it still matches the one we sent down. if (RefPtr<nsDocShellLoadState> originalState =
cp->TakePendingLoadStateForId(mLoadIdentifier)) { if (constchar* mismatch = ValidateWithOriginalState(originalState)) {
aActor->FatalError(
nsPrintfCString( "nsDocShellLoadState %s changed while in content process",
mismatch)
.get()); return;
}
} elseif (mTriggeringRemoteType != cp->GetRemoteType()) { // If we don't have a previous load to compare to, the content process // must be the triggering process.
aActor->FatalError( "nsDocShellLoadState with invalid triggering remote type"); return;
}
}
// We successfully read in the data - return a success value.
*aReadSuccess = true;
}
nsDocShellLoadState::nsDocShellLoadState(const nsDocShellLoadState& aOther)
: mReferrerInfo(aOther.mReferrerInfo),
mURI(aOther.mURI),
mOriginalURI(aOther.mOriginalURI),
mResultPrincipalURI(aOther.mResultPrincipalURI),
mResultPrincipalURIIsSome(aOther.mResultPrincipalURIIsSome),
mTriggeringPrincipal(aOther.mTriggeringPrincipal),
mTriggeringSandboxFlags(aOther.mTriggeringSandboxFlags),
mTriggeringWindowId(aOther.mTriggeringWindowId),
mTriggeringStorageAccess(aOther.mTriggeringStorageAccess),
mCsp(aOther.mCsp),
mKeepResultPrincipalURIIfSet(aOther.mKeepResultPrincipalURIIfSet),
mLoadReplace(aOther.mLoadReplace),
mInheritPrincipal(aOther.mInheritPrincipal),
mPrincipalIsExplicit(aOther.mPrincipalIsExplicit),
mNotifiedBeforeUnloadListeners(aOther.mNotifiedBeforeUnloadListeners),
mPrincipalToInherit(aOther.mPrincipalToInherit),
mPartitionedPrincipalToInherit(aOther.mPartitionedPrincipalToInherit),
mForceAllowDataURI(aOther.mForceAllowDataURI),
mIsExemptFromHTTPSFirstMode(aOther.mIsExemptFromHTTPSFirstMode),
mHttpsFirstDowngradeData(aOther.GetHttpsFirstDowngradeData()),
mOriginalFrameSrc(aOther.mOriginalFrameSrc),
mShouldCheckForRecursion(aOther.mShouldCheckForRecursion),
mIsFormSubmission(aOther.mIsFormSubmission),
mLoadType(aOther.mLoadType),
mSHEntry(aOther.mSHEntry),
mTarget(aOther.mTarget),
mTargetBrowsingContext(aOther.mTargetBrowsingContext),
mPostDataStream(aOther.mPostDataStream),
mHeadersStream(aOther.mHeadersStream),
mSrcdocData(aOther.mSrcdocData),
mSourceBrowsingContext(aOther.mSourceBrowsingContext),
mBaseURI(aOther.mBaseURI),
mLoadFlags(aOther.mLoadFlags),
mInternalLoadFlags(aOther.mInternalLoadFlags),
mFirstParty(aOther.mFirstParty),
mHasValidUserGestureActivation(aOther.mHasValidUserGestureActivation),
mTextDirectiveUserActivation(aOther.mTextDirectiveUserActivation),
mAllowFocusMove(aOther.mAllowFocusMove),
mTypeHint(aOther.mTypeHint),
mFileName(aOther.mFileName),
mIsFromProcessingFrameAttributes(aOther.mIsFromProcessingFrameAttributes),
mPendingRedirectedChannel(aOther.mPendingRedirectedChannel),
mOriginalURIString(aOther.mOriginalURIString),
mCancelContentJSEpoch(aOther.mCancelContentJSEpoch),
mLoadIdentifier(aOther.mLoadIdentifier),
mChannelInitialized(aOther.mChannelInitialized),
mIsMetaRefresh(aOther.mIsMetaRefresh),
mWasCreatedRemotely(aOther.mWasCreatedRemotely),
mUnstrippedURI(aOther.mUnstrippedURI),
mRemoteTypeOverride(aOther.mRemoteTypeOverride),
mTriggeringRemoteType(aOther.mTriggeringRemoteType),
mSchemelessInput(aOther.mSchemelessInput),
mHttpsUpgradeTelemetry(aOther.mHttpsUpgradeTelemetry) {
MOZ_DIAGNOSTIC_ASSERT(
XRE_IsParentProcess(), "Cloning a nsDocShellLoadState with the same load identifier is only " "allowed in the parent process, as it could break triggering remote type " "tracking in content."); if (aOther.mLoadingSessionHistoryInfo) {
mLoadingSessionHistoryInfo = MakeUnique<LoadingSessionHistoryInfo>(
*aOther.mLoadingSessionHistoryInfo);
}
}
// For https telemetry we set a flag indicating whether the load is https. // There are some corner cases, e.g. view-source and also about: pages. // about: pages, when hitting the network, always redirect to https. // Since we record https telemetry only within nsHTTPSChannel, it's fine // to set the flag here.
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI); if (innerURI->SchemeIs("https") || innerURI->SchemeIs("about")) {
mHttpsUpgradeTelemetry = nsILoadInfo::ALREADY_HTTPS;
} else {
mHttpsUpgradeTelemetry = nsILoadInfo::NO_UPGRADE;
}
}
// Pull relevant state from the channel, and store it on the // nsDocShellLoadState.
nsCOMPtr<nsIURI> originalUri;
rv = aPendingChannel->GetOriginalURI(getter_AddRefs(originalUri)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv;
}
loadState->SetOriginalURI(originalUri);
NS_ConvertUTF16toUTF8 uriString(aURI); // Cleanup the empty spaces that might be on each end.
uriString.Trim(" "); // Eliminate embedded newlines, which single-line text fields now allow:
uriString.StripCRLF();
NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
// Just create a URI and see what happens...
rv = NS_NewURI(getter_AddRefs(uri), uriString); bool fixup = true; if (NS_SUCCEEDED(rv) && uri &&
(uri->SchemeIs("about") || uri->SchemeIs("chrome"))) { // Avoid third party fixup as a performance optimization.
loadFlags &= ~nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
fixup = false;
} elseif (!sURIFixup && !XRE_IsContentProcess()) {
nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service(); if (uriFixup) {
sURIFixup = uriFixup;
ClearOnShutdown(&sURIFixup);
} else {
fixup = false;
}
}
// If we don't allow keyword lookups for this URL string, make sure to // update loadFlags to indicate this as well. if (!(fixupFlags & nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP)) {
loadFlags &= ~nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
} // Ensure URIFixup will use the right search engine in Private Browsing. if (aBrowsingContext->UsePrivateBrowsing()) {
fixupFlags |= nsIURIFixup::FIXUP_FLAG_PRIVATE_CONTEXT;
}
if (!XRE_IsContentProcess()) {
nsCOMPtr<nsIURIFixupInfo> fixupInfo;
sURIFixup->GetFixupURIInfo(uriString, fixupFlags,
getter_AddRefs(fixupInfo)); if (fixupInfo) { // We could fix the uri, clear NS_ERROR_MALFORMED_URI.
rv = NS_OK;
fixupInfo->GetPreferredURI(getter_AddRefs(uri));
fixupInfo->SetConsumer(aBrowsingContext);
fixupInfo->GetKeywordProviderName(searchProvider);
fixupInfo->GetKeywordAsSent(keyword); // GetFixupURIInfo only returns a post data stream if it succeeded // and changed the URI, in which case we should override the // passed-in post data by passing this as an override arg to // our internal method.
fixupInfo->GetPostData(getter_AddRefs(fixupStream));
// Don't pass certain flags that aren't needed and end up confusing // ConvertLoadTypeToDocShellInfoLoadType. We do need to ensure that they are // passed to LoadURI though, since it uses them.
uint32_t extraFlags = (loadFlags & EXTRA_LOAD_FLAGS);
loadFlags &= ~EXTRA_LOAD_FLAGS;
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
loadState->SetReferrerInfo(aLoadURIOptions.mReferrerInfo);
// Return early if the triggering principal doesn't exist. This could happen // when loading a URL by using a browsing context in the Browser Toolbox. if (!TriggeringPrincipal()) { return;
}
// We don't need to strip for sub frames because the query string has been // stripped in the top-level content. Also, we don't apply stripping if it // is triggered by addons. // // Note that we don't need to do the stripping if the channel has been // initialized. This means that this has been loaded speculatively in the // parent process before and the stripping was happening by then. if (GetChannelInitialized() || !aContext->IsTopContent() ||
BasePrincipal::Cast(TriggeringPrincipal())->AddonPolicy()) { return;
}
// We don't strip the URI if it's the same-site navigation. Note that we will // consider the system principal triggered load as third-party in case the // user copies and pastes a URL which has tracking query parameters or an // loading from external applications, such as clicking a link in an email // client. bool isThirdPartyURI = false; if (!TriggeringPrincipal()->IsSystemPrincipal() &&
(NS_FAILED(
TriggeringPrincipal()->IsThirdPartyURI(URI(), &isThirdPartyURI)) ||
!isThirdPartyURI)) { return;
}
#ifdef DEBUG // Make sure that unstripped URI is the same as URI() but only the query // string could be different. if (mUnstrippedURI) {
nsCOMPtr<nsIURI> uri;
Unused << queryStripper->Strip(mUnstrippedURI,
aContext->UsePrivateBrowsing(),
getter_AddRefs(uri), &numStripped); bool equals = false;
Unused << URI()->Equals(uri, &equals);
MOZ_ASSERT(equals);
} #endif
}
void nsDocShellLoadState::SetFileName(const nsAString& aFileName) {
MOZ_DIAGNOSTIC_ASSERT(aFileName.FindChar(char16_t(0)) == kNotFound, "The filename should never contain null characters");
mFileName = aFileName;
}
void nsDocShellLoadState::SetRemoteTypeOverride( const nsCString& aRemoteTypeOverride) {
MOZ_DIAGNOSTIC_ASSERT(
NS_IsAboutBlank(mURI), "Should only have aRemoteTypeOverride for about:blank URIs");
mRemoteTypeOverride = mozilla::Some(aRemoteTypeOverride);
}
const nsCString& nsDocShellLoadState::GetEffectiveTriggeringRemoteType() const { // Consider non-errorpage loads from session history as being triggred by the // parent process, as we'll validate them against the history entry. // // NOTE: Keep this check in-sync with the session-history validation check in // `DocumentLoadListener::Open`! if (LoadIsFromSessionHistory() && LoadType() != LOAD_ERROR_PAGE) { return NOT_REMOTE_TYPE;
} return mTriggeringRemoteType;
}
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED void nsDocShellLoadState::AssertProcessCouldTriggerLoadIfSystem() { // Early check to see if we're trying to start a file URI load with a system // principal within a web content process. // If this assertion fails, the load will fail later during // nsContentSecurityManager checks, however this assertion should happen // closer to whichever caller is triggering the system-principal load. if (mozilla::SessionHistoryInParent() &&
TriggeringPrincipal()->IsSystemPrincipal() &&
mozilla::dom::IsWebRemoteType(GetEffectiveTriggeringRemoteType())) { bool localFile = false; if (NS_SUCCEEDED(NS_URIChainHasFlags(
URI(), nsIProtocolHandler::URI_IS_LOCAL_FILE, &localFile)) &&
localFile) {
NS_WARNING(nsPrintfCString("Unexpected system load of file URI (%s) from " "web content process",
URI()->GetSpecOrDefault().get())
.get());
MOZ_CRASH("Unexpected system load of file URI from web content process");
}
}
} #endif
nsresult nsDocShellLoadState::SetupInheritingPrincipal(
BrowsingContext::Type aType, const mozilla::OriginAttributes& aOriginAttributes) { // We need a principalToInherit. // // If principalIsExplicit is not set there are 4 possibilities: // (1) If the system principal or an expanded principal was passed // in and we're a typeContent docshell, inherit the principal // from the current document instead. // (2) In all other cases when the principal passed in is not null, // use that principal. // (3) If the caller has allowed inheriting from the current document, // or if we're being called from system code (eg chrome JS or pure // C++) then inheritPrincipal should be true and InternalLoad will get // a principal from the current document. If none of these things are // true, then // (4) we don't pass a principal into the channel, and a principal will be // created later from the channel's internal data. // // If principalIsExplicit *is* set, there are 4 possibilities // (1) If the system principal or an expanded principal was passed in // and we're a typeContent docshell, return an error. // (2) In all other cases when the principal passed in is not null, // use that principal. // (3) If the caller has allowed inheriting from the current document, // then inheritPrincipal should be true and InternalLoad will get // a principal from the current document. If none of these things are // true, then // (4) we dont' pass a principal into the channel, and a principal will be // created later from the channel's internal data.
mPrincipalToInherit = mTriggeringPrincipal; if (mPrincipalToInherit && aType != BrowsingContext::Type::Chrome) { if (mPrincipalToInherit->IsSystemPrincipal()) { if (mPrincipalIsExplicit) { return NS_ERROR_DOM_SECURITY_ERR;
}
mPrincipalToInherit = nullptr;
mInheritPrincipal = true;
} elseif (nsContentUtils::IsExpandedPrincipal(mPrincipalToInherit)) { if (mPrincipalIsExplicit) { return NS_ERROR_DOM_SECURITY_ERR;
} // Don't inherit from the current page. Just do the safe thing // and pretend that we were loaded by a nullprincipal. // // We didn't inherit OriginAttributes here as ExpandedPrincipal doesn't // have origin attributes.
mPrincipalToInherit = NullPrincipal::Create(aOriginAttributes);
mInheritPrincipal = false;
}
}
if (!mPrincipalToInherit && !mInheritPrincipal && !mPrincipalIsExplicit) { // See if there's system or chrome JS code running
mInheritPrincipal = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
}
if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL) {
mInheritPrincipal = false; // Create a new null principal URI based on our precursor principal.
nsCOMPtr<nsIURI> nullPrincipalURI =
NullPrincipal::CreateURI(mPrincipalToInherit); // If mFirstParty is true and the pref 'privacy.firstparty.isolate' is // enabled, we will set firstPartyDomain on the origin attributes.
OriginAttributes attrs(aOriginAttributes); if (mFirstParty) {
attrs.SetFirstPartyDomain(true, nullPrincipalURI);
}
mPrincipalToInherit = NullPrincipal::Create(attrs, nullPrincipalURI);
}
return NS_OK;
}
nsresult nsDocShellLoadState::SetupTriggeringPrincipal( const mozilla::OriginAttributes& aOriginAttributes) { // If the triggeringPrincipal is not set, we first try to create a principal // from the referrer, since the referrer URI reflects the web origin that // triggered the load. If there is no referrer URI, we fall back to using the // SystemPrincipal. It's safe to assume that no provided triggeringPrincipal // and no referrer simulate a load that was triggered by the system. It's // important to note that this block of code needs to appear *after* the block // where we munge the principalToInherit, because otherwise we would never // enter code blocks checking if the principalToInherit is null and we will // end up with a wrong inheritPrincipal flag. if (!mTriggeringPrincipal) { if (mReferrerInfo) {
nsCOMPtr<nsIURI> referrer = mReferrerInfo->GetOriginalReferrer();
mTriggeringPrincipal =
BasePrincipal::CreateContentPrincipal(referrer, aOriginAttributes);
if (FirstParty()) { // tag first party URL loads
loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
}
const uint32_t loadType = LoadType();
// These values aren't available for loads initiated in the Parent process.
MOZ_ASSERT_IF(loadType == LOAD_ERROR_PAGE, aIsEmbeddingBlockedError.isSome());
if (loadType == LOAD_ERROR_PAGE) { // Error pages are LOAD_BACKGROUND, unless it's an // XFO / frame-ancestors error for which we want an error page to load // but additionally want the onload() event to fire. if (!*aIsEmbeddingBlockedError) {
loadFlags |= nsIChannel::LOAD_BACKGROUND;
}
}
// Mark the channel as being a document URI and allow content sniffing...
loadFlags |=
nsIChannel::LOAD_DOCUMENT_URI | nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
if (nsDocShell::SandboxFlagsImplyCookies(
aBrowsingContext->GetSandboxFlags())) {
loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
}
// Load attributes depend on load type... switch (loadType) { case LOAD_HISTORY: { // Only send VALIDATE_NEVER if mLSHE's URI was never changed via // push/replaceState (bug 669671). if (!aUriModified) {
loadFlags |= nsIRequest::VALIDATE_NEVER;
} break;
}
case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE: case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE:
loadFlags |=
nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::LOAD_FRESH_CONNECTION;
[[fallthrough]];
case LOAD_REFRESH:
loadFlags |= nsIRequest::VALIDATE_ALWAYS; break;
case LOAD_NORMAL_BYPASS_CACHE: case LOAD_NORMAL_BYPASS_PROXY: case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE: case LOAD_RELOAD_BYPASS_CACHE: case LOAD_RELOAD_BYPASS_PROXY: case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE: case LOAD_REPLACE_BYPASS_CACHE:
loadFlags |=
nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::LOAD_FRESH_CONNECTION; break;
case LOAD_RELOAD_NORMAL: if (!StaticPrefs::
browser_soft_reload_only_force_validate_top_level_document()) {
loadFlags |= nsIRequest::VALIDATE_ALWAYS; break;
}
[[fallthrough]]; case LOAD_NORMAL: case LOAD_LINK: // Set cache checking flags switch (StaticPrefs::browser_cache_check_doc_frequency()) { case 0:
loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION; break; case 1:
loadFlags |= nsIRequest::VALIDATE_ALWAYS; break; case 2:
loadFlags |= nsIRequest::VALIDATE_NEVER; break;
} break;
}
if (HasInternalLoadFlags(nsDocShell::INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER)) {
loadFlags |= nsIChannel::LOAD_BYPASS_URL_CLASSIFIER;
}
// If the user pressed shift-reload, then do not allow ServiceWorker // interception to occur. See step 12.1 of the SW HandleFetch algorithm. if (IsForceReloadType(loadType)) {
loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
}
// Check that `aOriginalState` is sufficiently similar to this state that // they're performing the same load. auto uriEq = [](nsIURI* a, nsIURI* b) -> bool { bool eq = false; return a == b || (a && b && NS_SUCCEEDED(a->Equals(b, &eq)) && eq);
}; if (!uriEq(mURI, aOriginalState->mURI)) { return"URI";
} if (!uriEq(mUnstrippedURI, aOriginalState->mUnstrippedURI)) { return"UnstrippedURI";
} if (!uriEq(mOriginalURI, aOriginalState->mOriginalURI)) { return"OriginalURI";
} if (!uriEq(mBaseURI, aOriginalState->mBaseURI)) { return"BaseURI";
}
if (!mTriggeringPrincipal->Equals(aOriginalState->mTriggeringPrincipal)) { return"TriggeringPrincipal";
} if (mTriggeringSandboxFlags != aOriginalState->mTriggeringSandboxFlags) { return"TriggeringSandboxFlags";
} if (mTriggeringRemoteType != aOriginalState->mTriggeringRemoteType) { return"TriggeringRemoteType";
}
if (mOriginalURIString != aOriginalState->mOriginalURIString) { return"OriginalURIString";
}
if (mRemoteTypeOverride != aOriginalState->mRemoteTypeOverride) { return"RemoteTypeOverride";
}
if (mSourceBrowsingContext.ContextId() !=
aOriginalState->mSourceBrowsingContext.ContextId()) { return"SourceBrowsingContext";
}
// FIXME: Consider calculating less information in the target process so that // we can validate more properties more easily. // FIXME: Identify what other flags will not change when sent through a // content process.
if (XRE_IsParentProcess()) {
mozilla::ipc::IToplevelProtocol* top = aActor->ToplevelProtocol();
MOZ_RELEASE_ASSERT(top &&
top->GetProtocolId() ==
mozilla::ipc::ProtocolId::PContentMsgStart &&
top->GetSide() == mozilla::ipc::ParentSide, "nsDocShellLoadState must be sent over PContent");
ContentParent* cp = static_cast<ContentParent*>(top);
cp->StorePendingLoadState(this);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.