diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index b3038890a3ce..75e78849fbfb 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -8691,6 +8691,29 @@ bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState, aState.mNewURIHasRef; } +static bool IsSamePrincipalForDocumentURI(nsIPrincipal* aCurrentPrincipal, + nsIURI* aCurrentURI, + nsIURI* aNewURI) { + if (!StaticPrefs::dom_security_setdocumenturi()) { + return true; + } + nsCOMPtr principalURI = aCurrentPrincipal->GetURI(); + if (aCurrentPrincipal->GetIsNullPrincipal()) { + nsCOMPtr precursor = + aCurrentPrincipal->GetPrecursorPrincipal(); + if (precursor) { + principalURI = precursor->GetURI(); + } + } + + return !nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI, + aNewURI) && + !nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI, + aCurrentURI) && + !nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(aCurrentURI, + aNewURI); +} + nsresult nsDocShell::HandleSameDocumentNavigation( nsDocShellLoadState* aLoadState, SameDocumentNavigationState& aState, bool& aSameDocument) { @@ -8707,13 +8730,6 @@ nsresult nsDocShell::HandleSameDocumentNavigation( RefPtr doc = GetDocument(); NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); - doc->DoNotifyPossibleTitleChange(); - - // Store the pending uninvoked directives if it is a same document navigation. - // We need to set it here, in case the navigation happens before the document - // has actually finished loading. - doc->FragmentDirective()->SetTextDirectives( - std::move(aState.mTextDirectives)); nsCOMPtr currentURI = mCurrentURI; @@ -8725,33 +8741,57 @@ nsresult nsDocShell::HandleSameDocumentNavigation( ("Upgraded URI to %s", newURI->GetSpecOrDefault().get())); } - if (StaticPrefs::dom_security_setdocumenturi()) { - // check if aLoadState->URI(), principalURI, mCurrentURI are same origin - // skip handling otherwise - nsCOMPtr origPrincipal = doc->NodePrincipal(); - nsCOMPtr principalURI = origPrincipal->GetURI(); - if (origPrincipal->GetIsNullPrincipal()) { - nsCOMPtr precursor = origPrincipal->GetPrecursorPrincipal(); - if (precursor) { - principalURI = precursor->GetURI(); + // check if documentPrincipal, mCurrentURI, and aLoadState->URI() are same + // origin skip handling otherwise + if (!IsSamePrincipalForDocumentURI(doc->NodePrincipal(), mCurrentURI, + newURI)) { + MOZ_LOG(gSHLog, LogLevel::Debug, + ("nsDocShell[%p]: possible violation of the same origin policy " + "during same document navigation", + this)); + return NS_OK; + } + + if (nsCOMPtr window = doc->GetInnerWindow()) { + // https://html.spec.whatwg.org/#navigate-fragid + // Step 1 + if (RefPtr navigation = window->Navigation()) { + // Step 2 + RefPtr destinationNavigationAPIState = + mActiveEntry ? mActiveEntry->GetNavigationState() : nullptr; + // Step 3 + if (aLoadState->GetNavigationAPIState()) { + destinationNavigationAPIState = aLoadState->GetNavigationAPIState(); + } + + AutoJSAPI jsapi; + if (jsapi.Init(window)) { + RefPtr sourceElement = aLoadState->GetSourceElement(); + // Step 4 + bool shouldContinue = navigation->FirePushReplaceReloadNavigateEvent( + jsapi.cx(), aLoadState->GetNavigationType(), newURI, + /* aIsSameDocument */ true, + Some(aLoadState->UserNavigationInvolvement()), sourceElement, + /* aFormDataEntryList */ Nothing(), + /* aNavigationAPIState */ destinationNavigationAPIState, + /* aClassicHistoryAPIState */ nullptr); + + // Step 5 + if (!shouldContinue) { + return NS_OK; + } } } - - if (nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI, - newURI) || - nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI, - mCurrentURI) || - nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(mCurrentURI, - newURI)) { - aSameDocument = false; - MOZ_LOG(gSHLog, LogLevel::Debug, - ("nsDocShell[%p]: possible violation of the same origin policy " - "during same document navigation", - this)); - return NS_OK; - } } + doc->DoNotifyPossibleTitleChange(); + + // Store the pending uninvoked directives if it is a same document + // navigation. We need to set it here, in case the navigation happens before + // the document has actually finished loading. + doc->FragmentDirective()->SetTextDirectives( + std::move(aState.mTextDirectives)); + #ifdef DEBUG if (aState.mSameExceptHashes) { bool sameExceptHashes = false; diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 347233a943d0..32d1673ea09a 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -15,6 +15,7 @@ #include "mozilla/UniquePtr.h" #include "mozilla/WeakPtr.h" #include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/NavigationBinding.h" #include "mozilla/dom/WindowProxyHolder.h" #include "nsCOMPtr.h" #include "nsCharsetSource.h" diff --git a/docshell/base/nsDocShellLoadState.cpp b/docshell/base/nsDocShellLoadState.cpp index d746bea3e9ff..d7de4d5a1ea9 100644 --- a/docshell/base/nsDocShellLoadState.cpp +++ b/docshell/base/nsDocShellLoadState.cpp @@ -755,6 +755,20 @@ bool nsDocShellLoadState::LoadIsFromSessionHistory() const { : !!mSHEntry; } +nsIStructuredCloneContainer* nsDocShellLoadState::GetNavigationAPIState() + const { + return mNavigationAPIState; +} + +void nsDocShellLoadState::SetNavigationAPIState( + nsIStructuredCloneContainer* aNavigationAPIState) { + mNavigationAPIState = aNavigationAPIState; +} + +NavigationType nsDocShellLoadState::GetNavigationType() const { + return LoadReplace() ? NavigationType::Replace : NavigationType::Push; +} + void nsDocShellLoadState::MaybeStripTrackerQueryStrings( BrowsingContext* aContext) { MOZ_ASSERT(aContext); diff --git a/docshell/base/nsDocShellLoadState.h b/docshell/base/nsDocShellLoadState.h index 087a47a079d6..f22c1dd85971 100644 --- a/docshell/base/nsDocShellLoadState.h +++ b/docshell/base/nsDocShellLoadState.h @@ -8,6 +8,7 @@ #define nsDocShellLoadState_h__ #include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/NavigationBinding.h" #include "mozilla/dom/SessionHistoryEntry.h" #include "mozilla/dom/UserNavigationInvolvement.h" @@ -195,6 +196,14 @@ class nsDocShellLoadState final { bool LoadIsFromSessionHistory() const; + nsIStructuredCloneContainer* GetNavigationAPIState() const; + + // This is used as the parameter for https://html.spec.whatwg.org/#navigate, + // but it's currently missing. See bug 1966674 + void SetNavigationAPIState(nsIStructuredCloneContainer* aNavigationAPIState); + + mozilla::dom::NavigationType GetNavigationType() const; + const nsString& Target() const; void SetTarget(const nsAString& aTarget); @@ -552,6 +561,8 @@ class nsDocShellLoadState final { mozilla::UniquePtr mLoadingSessionHistoryInfo; + nsCOMPtr mNavigationAPIState; + // Target for load, like _content, _blank etc. nsString mTarget;