diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index ef46863444e2..15c52c043496 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -11179,7 +11179,8 @@ nsDocShell::AddState(JS::Handle aData, const nsAString& aTitle, // Here's what we do, roughly in the order specified by HTML5. The specific // steps we are executing are at - // + // , + // , // and // . // This function basically implements #dom-history-pushstate and @@ -11310,43 +11311,8 @@ nsDocShell::AddState(JS::Handle aData, const nsAString& aTitle, return NS_ERROR_DOM_SECURITY_ERR; } - // 7.4 and 7.5: Same-origin check. - if (!nsContentUtils::URIIsLocalFile(newURI)) { - // In addition to checking that the security manager says that - // the new URI has the same origin as our current URI, we also - // check that the two URIs have the same userpass. (The - // security manager says that |http://foo.com| and - // |http://me@foo.com| have the same origin.) currentURI - // won't contain the password part of the userpass, so this - // means that it's never valid to specify a password in a - // pushState or replaceState URI. - - nsCOMPtr secMan = - do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); - NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE); - - // It's very important that we check that newURI is of the same - // origin as currentURI, not docBaseURI, because a page can - // set docBaseURI arbitrarily to any domain. - nsAutoCString currentUserPass, newUserPass; - NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass), - NS_ERROR_FAILURE); - NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE); - bool isPrivateWin = - document->NodePrincipal()->OriginAttributesRef().IsPrivateBrowsing(); - if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true, - isPrivateWin)) || - !currentUserPass.Equals(newUserPass)) { - return NS_ERROR_DOM_SECURITY_ERR; - } - } else { - // It's a file:// URI - nsCOMPtr principal = document->GetPrincipal(); - - if (!principal || NS_FAILED(principal->CheckMayLoadWithReporting( - newURI, false, document->InnerWindowID()))) { - return NS_ERROR_DOM_SECURITY_ERR; - } + if (!document->CanRewriteURL(newURI)) { + return NS_ERROR_DOM_SECURITY_ERR; } if (currentURI) { diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 020c1a1a02c0..9fb836b9297b 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -13282,6 +13282,45 @@ bool Document::HasBeenScrolled() const { return false; } +bool Document::CanRewriteURL(nsIURI* aTargetURL) const { + if (nsContentUtils::URIIsLocalFile(aTargetURL)) { + // It's a file:// URI + nsCOMPtr principal = NodePrincipal(); + return NS_SUCCEEDED(principal->CheckMayLoadWithReporting( + mDocumentURI, false, InnerWindowID())); + } + + nsCOMPtr secMan = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); + if (!secMan) { + return false; + } + + // It's very important that we check that aTargetURL is of the same + // origin as mDocumentURI, not docBaseURI, because a page can + // set docBaseURI arbitrarily to any domain. + bool isPrivateWin = + NodePrincipal()->OriginAttributesRef().IsPrivateBrowsing(); + if (NS_FAILED(secMan->CheckSameOriginURI(mDocumentURI, aTargetURL, true, + isPrivateWin))) { + return false; + } + + // In addition to checking that the security manager says that + // the new URI has the same origin as our current URI, we also + // check that the two URIs have the same userpass. (The + // security manager says that |http://foo.com| and + // |http://me@foo.com| have the same origin.) mDocumentURI + // won't contain the password part of the userpass, so this + // means that it's never valid to specify a password in a + // pushState or replaceState URI. + nsAutoCString currentUserPass, newUserPass; + (void)mDocumentURI->GetUserPass(currentUserPass); + (void)aTargetURL->GetUserPass(newUserPass); + + return currentUserPass.Equals(newUserPass); +} + nsISupports* Document::GetCurrentContentSink() { return mParser ? mParser->GetContentSink() : nullptr; } diff --git a/dom/base/Document.h b/dom/base/Document.h index f006187ecd9e..a8b9aaac39fa 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -2696,6 +2696,12 @@ class Document : public nsINode, return inner && inner->IsCurrentInnerWindow() && inner->GetDoc() == this; } + /* + * Return true if the documents current url can be re-written to `aTargetURL`. + * This implements https://html.spec.whatwg.org/#can-have-its-url-rewritten. + */ + bool CanRewriteURL(nsIURI* aTargetURL) const; + /** * Return true if this document is fully active as described by spec. * https://html.spec.whatwg.org/multipage/document-sequences.html#fully-active diff --git a/dom/navigation/Navigation.cpp b/dom/navigation/Navigation.cpp index c798511bb122..c2766d7f223a 100644 --- a/dom/navigation/Navigation.cpp +++ b/dom/navigation/Navigation.cpp @@ -423,10 +423,6 @@ bool Navigation::FireDownloadRequestNavigateEvent( /* aClassicHistoryAPIState */ nullptr, aFilename); } -// Implementation of this will be done in Bug 1948596. -// https://html.spec.whatwg.org/#can-have-its-url-rewritten -static bool CanBeRewritten(nsIURI* aURI, nsIURI* aOtherURI) { return false; } - static bool HasHistoryActionActivation( Maybe aRelevantGlobalObject) { return aRelevantGlobalObject @@ -551,11 +547,10 @@ bool Navigation::InnerFireNavigateEvent( .valueOr(nullptr); // Step 9 - init.mCanIntercept = - document && - CanBeRewritten(document->GetDocumentURI(), aDestination->GetURI()) && - (aDestination->SameDocument() || - aNavigationType != NavigationType::Traverse); + init.mCanIntercept = document && + document->CanRewriteURL(aDestination->GetURI()) && + (aDestination->SameDocument() || + aNavigationType != NavigationType::Traverse); // Step 10 and step 11 init.mCancelable =