tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit f3e6a0d058e9199bf05702704644044f5b09a0b7
parent 55022fd9d608912d396edb06385834070512a14b
Author: Andreas Farre <farre@mozilla.com>
Date:   Mon, 13 Oct 2025 07:22:27 +0000

Bug 1991265 - Make push to replace load conversion follow spec. r=smaug,webdriver-reviewers

Make sure that navigations from Location and Window.open perform correct
conversion of history handling when passed "auto".

Differential Revision: https://phabricator.services.mozilla.com/D266519

Diffstat:
Mdocshell/base/BrowsingContext.cpp | 26++++++--------------------
Mdocshell/base/BrowsingContext.h | 2+-
Mdocshell/base/nsDocShell.cpp | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mdocshell/base/nsDocShellLoadState.cpp | 43+++++++++++++++++++++++++++++++------------
Mdocshell/base/nsDocShellLoadState.h | 28++++++++++++++++++++--------
Mdocshell/base/nsDocShellLoadTypes.h | 13+++++++++++++
Mdocshell/test/chrome/bug311007_window.xhtml | 7+++++++
Mdom/base/Document.cpp | 3+++
Mdom/base/Document.h | 12++++++++++++
Mdom/base/Location.cpp | 14+++++++-------
Mdom/base/LocationBase.cpp | 34++++++++++++++++++++++++++--------
Mdom/base/LocationBase.h | 6++++--
Mdom/base/nsGlobalWindowOuter.cpp | 2+-
Mdom/ipc/DOMTypes.ipdlh | 5+++++
Mdom/ipc/NavigationAPIIPCUtils.h | 5+++++
Mdom/navigation/Navigation.cpp | 2+-
Mdom/security/test/sec-fetch/file_no_cache.sjs | 2+-
Mdom/security/test/sec-fetch/test_iframe_history_manipulation.html | 2+-
Mtesting/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html.ini | 3++-
Mtesting/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html.ini | 12+++++++++---
Mtesting/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html.ini | 9++++++---
Mtesting/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html.ini | 3++-
Mtesting/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html.ini | 3++-
Mtesting/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html.ini | 3++-
Mtesting/web-platform/meta/html/browsers/history/the-history-interface/traverse-during-beforeunload.html.ini | 4++--
Dtesting/web-platform/meta/navigation-api/currententrychange-event/location-api.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin-sameorigindomain.sub.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/cross-window/location-crossdocument-sameorigin.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin-sameorigindomain.sub.html.ini | 12------------
Dtesting/web-platform/meta/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/cross-window/location-samedocument-sameorigin.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin-sameorigindomain.sub.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/cross-window/open-crossdocument-sameorigin.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin-sameorigindomain.sub.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/cross-window/open-samedocument-sameorigin.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/navigate-iframe-location.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/navigate-navigation-navigate.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/navigate-window-open-self.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/navigate-event/navigate-window-open.html.ini | 5-----
Dtesting/web-platform/meta/navigation-api/navigation-history-entry/current-basic.html.ini | 3---
Mtesting/web-platform/meta/navigation-api/navigation-methods/sandboxing-back-parent.html.ini | 5++++-
Mtesting/web-platform/meta/navigation-api/precommit-handler/precommitHandler-push.html.ini | 2+-
Dtesting/web-platform/meta/navigation-api/state/cross-document-getState-undefined.html.ini | 3---
Dtesting/web-platform/meta/navigation-api/state/cross-document-location-api.html.ini | 15---------------
Mtesting/web-platform/meta/webdriver/tests/bidi/browsing_context/navigation_started/navigation_interrupted.py.ini | 3++-
Mtesting/web-platform/tests/css/cssom-view/scroll-behavior-smooth-navigation.html | 6+++++-
Mtesting/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-1.html | 2+-
Mtesting/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-2.html | 2+-
Mtesting/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load.html | 8++++++--
Mtesting/web-platform/tests/html/browsers/the-window-object/open-close/self-close.html | 2+-
Mtesting/web-platform/tests/navigation-api/navigate-event/navigate-navigation-navigate.html | 2+-
Mtoolkit/components/windowwatcher/nsWindowWatcher.cpp | 8+++++++-
Mtoolkit/components/windowwatcher/nsWindowWatcher.h | 3++-
54 files changed, 273 insertions(+), 169 deletions(-)

diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp @@ -2128,6 +2128,7 @@ nsresult BrowsingContext::LoadURI(nsDocShellLoadState* aLoadState, if (mDocShell) { nsCOMPtr<nsIDocShell> docShell = mDocShell; + return docShell->LoadURI(aLoadState, aSetNavigating); } @@ -2438,7 +2439,7 @@ BrowsingContext::CheckURLAndCreateLoadState(nsIURI* aURI, void BrowsingContext::Navigate(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv, NavigationHistoryBehavior aHistoryHandling, - bool aShouldNotForceReplaceInOnLoad) { + bool aNeedsCompletelyLoadedDocument) { MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, "Navigate to {} as {}", *aURI, aHistoryHandling); CallerType callerType = aSubjectPrincipal.IsSystemPrincipal() @@ -2457,27 +2458,12 @@ void BrowsingContext::Navigate(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, return; } - loadState->SetShouldNotForceReplaceInOnLoad(aShouldNotForceReplaceInOnLoad); - - // Step 12 - NavigationHistoryBehavior historyHandling = aHistoryHandling; - if (aHistoryHandling == NavigationHistoryBehavior::Auto) { - if (auto* document = GetExtantDocument()) { - bool equals = false; - aURI->Equals(document->GetDocumentURI(), &equals); - if (equals && document->GetPrincipal()) { - document->GetPrincipal()->Equals(&aSubjectPrincipal, &equals); - } - if (equals) { - historyHandling = NavigationHistoryBehavior::Replace; - } else { - historyHandling = NavigationHistoryBehavior::Push; - } - } + if (mozilla::SessionHistoryInParent()) { + loadState->SetNeedsCompletelyLoadedDocument(aNeedsCompletelyLoadedDocument); + loadState->SetHistoryBehavior(aHistoryHandling); } - // Step 13 of #navigate are handled later in nsDocShell::InternalLoad(). - if (historyHandling == NavigationHistoryBehavior::Replace) { + if (aHistoryHandling == NavigationHistoryBehavior::Replace) { loadState->SetLoadType(LOAD_STOP_CONTENT_AND_REPLACE); } else { loadState->SetLoadType(LOAD_STOP_CONTENT); diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h @@ -459,7 +459,7 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { void Navigate(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv, NavigationHistoryBehavior aHistoryHandling = NavigationHistoryBehavior::Auto, - bool aShouldNotForceReplaceInOnLoad = false); + bool aNeedsCompletelyLoadedDocument = false); // Removes the root document for this BrowsingContext tree from the BFCache, // if it is cached, and returns true if it was. diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp @@ -72,6 +72,7 @@ #include "mozilla/dom/Navigation.h" #include "mozilla/dom/NavigationBinding.h" #include "mozilla/dom/NavigationHistoryEntry.h" +#include "mozilla/dom/NavigationUtils.h" #include "mozilla/dom/PerformanceNavigation.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/PolicyContainer.h" @@ -981,7 +982,8 @@ bool nsDocShell::MaybeHandleSubframeHistory( nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem)); if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) { - if (mBrowsingContext && mBrowsingContext->IsTop()) { + if (mBrowsingContext && mBrowsingContext->IsTop() && + !aLoadState->HistoryBehavior()) { // This is the root docshell. If we got here while // executing an onLoad Handler,this load will not go // into session history. @@ -1137,7 +1139,7 @@ bool nsDocShell::MaybeHandleSubframeHistory( if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry || mLoadingEntry || mActiveEntry) && - !aLoadState->ShouldNotForceReplaceInOnLoad()) { + !aLoadState->HistoryBehavior()) { // This is a pre-existing subframe. If // 1. The load of this frame was not originally initiated by session // history directly (i.e. (!shEntry) condition succeeded, but it can @@ -8742,6 +8744,7 @@ struct SameDocumentNavigationState { bool mSameExceptHashes = false; bool mSecureUpgradeURI = false; bool mHistoryNavBetweenSameDoc = false; + bool mIdentical = false; }; bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState, @@ -8847,6 +8850,12 @@ bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState, } } + // Two URIs are identical if they're same except hashes, they both have + // hashes, and their hashes are the same. + aState.mIdentical = aState.mSameExceptHashes && + (aState.mNewURIHasRef == aState.mCurrentURIHasRef) && + aState.mCurrentHash.Equals(aState.mNewHash); + // A same document navigation happens when we navigate between two SHEntries // for the same document. We do a same document navigation under two // circumstances. Either @@ -9394,10 +9403,8 @@ nsresult nsDocShell::HandleSameDocumentNavigation( // https://html.spec.whatwg.org/multipage/browsing-the-web.html#updating-the-document navigation->UpdateEntriesForSameDocumentNavigation( mActiveEntry.get(), - LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) - ? NavigationType::Replace - : aLoadState->LoadIsFromSessionHistory() ? NavigationType::Traverse - : NavigationType::Push); + NavigationUtils::NavigationTypeFromLoadType(mLoadType).valueOr( + NavigationType::Push)); } // Fire a hashchange event URIs differ, and only in their hashes. @@ -9478,6 +9485,59 @@ uint32_t nsDocShell::GetLoadTypeForFormSubmission( : LOAD_LINK; } +static void MaybeConvertToReplaceLoad(nsDocShellLoadState* aLoadState, + Document* aExtantDocument, + bool aIdenticalURI, + bool aHasActiveEntry) { + // MaybeConvertToReplaceLoad implements steps 12 and 13 of #navigate, but + // since we're not yet using historyBehavior for all types of loads and + // configurations, we need to sometimes bail and revert to the old way of + // handling push to replace load conversion. The different cases we can't + // handle are: + // + // * When we don't have an active document + // * When a document doesn't yet have a session history entry + // * When we don't use SHIP + // * When we don't use historyBehavior + if (!aExtantDocument || !aHasActiveEntry || + !mozilla::SessionHistoryInParent() || !aLoadState->HistoryBehavior()) { + aLoadState->ResetHistoryBehavior(); + return; + } + + bool convertToReplaceLoad = aLoadState->NeedsCompletelyLoadedDocument() && + !aExtantDocument->IsCompletelyLoaded(); + if (const auto& historyBehavior = aLoadState->HistoryBehavior(); + !convertToReplaceLoad && historyBehavior && + *historyBehavior == NavigationHistoryBehavior::Auto) { + convertToReplaceLoad = aIdenticalURI; + if (convertToReplaceLoad && aExtantDocument->GetPrincipal()) { + aExtantDocument->GetPrincipal()->Equals(aLoadState->TriggeringPrincipal(), + &convertToReplaceLoad); + } + } + + convertToReplaceLoad = + convertToReplaceLoad || nsContentUtils::NavigationMustBeAReplace( + *aLoadState->URI(), *aExtantDocument); + + if (convertToReplaceLoad) { + MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, + "Convert to replace when navigating from {} to {}, {}", + *aExtantDocument->GetDocumentURI(), *aLoadState->URI(), + (aLoadState->NeedsCompletelyLoadedDocument() && + !nsContentUtils::NavigationMustBeAReplace(*aLoadState->URI(), + *aExtantDocument)) + ? "needs completely loaded document" + : "navigation must be a replace"); + aLoadState->SetLoadType(MaybeAddLoadFlags( + aLoadState->LoadType(), nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY)); + aLoadState->SetHistoryBehavior(NavigationHistoryBehavior::Replace); + } else { + aLoadState->SetHistoryBehavior(NavigationHistoryBehavior::Push); + } +} + // InternalLoad performs several of the steps from // https://html.spec.whatwg.org/#navigate. nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, @@ -9538,6 +9598,13 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, IsSameDocumentNavigation(aLoadState, sameDocumentNavigationState) && !aLoadState->GetPendingRedirectedChannel(); + if (mLoadType != LOAD_ERROR_PAGE && + !aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) { + MaybeConvertToReplaceLoad(aLoadState, GetExtantDocument(), + sameDocumentNavigationState.mIdentical, + !!mActiveEntry); + } + // Note: We do this check both here and in BrowsingContext:: // LoadURI/InternalLoad, since document-specific sandbox flags are only // available in the process triggering the load, and we don't want the target diff --git a/docshell/base/nsDocShellLoadState.cpp b/docshell/base/nsDocShellLoadState.cpp @@ -51,7 +51,6 @@ nsDocShellLoadState::nsDocShellLoadState( const DocShellLoadStateInit& aLoadState, mozilla::ipc::IProtocol* aActor, bool* aReadSuccess) : mNotifiedBeforeUnloadListeners(false), - mShouldNotForceReplaceInOnLoad(false), mLoadIdentifier(aLoadState.LoadIdentifier()) { // If we return early, we failed to read in the data. *aReadSuccess = false; @@ -71,6 +70,8 @@ nsDocShellLoadState::nsDocShellLoadState( mOriginalFrameSrc = aLoadState.OriginalFrameSrc(); mShouldCheckForRecursion = aLoadState.ShouldCheckForRecursion(); mIsFormSubmission = aLoadState.IsFormSubmission(); + mNeedsCompletelyLoadedDocument = aLoadState.NeedsCompletelyLoadedDocument(); + mHistoryBehavior = aLoadState.HistoryBehavior(); mLoadType = aLoadState.LoadType(); mTarget = aLoadState.Target(); mTargetBrowsingContext = aLoadState.TargetBrowsingContext(); @@ -172,7 +173,6 @@ nsDocShellLoadState::nsDocShellLoadState(const nsDocShellLoadState& aOther) mInheritPrincipal(aOther.mInheritPrincipal), mPrincipalIsExplicit(aOther.mPrincipalIsExplicit), mNotifiedBeforeUnloadListeners(aOther.mNotifiedBeforeUnloadListeners), - mShouldNotForceReplaceInOnLoad(aOther.mShouldNotForceReplaceInOnLoad), mPrincipalToInherit(aOther.mPrincipalToInherit), mPartitionedPrincipalToInherit(aOther.mPartitionedPrincipalToInherit), mForceAllowDataURI(aOther.mForceAllowDataURI), @@ -181,6 +181,8 @@ nsDocShellLoadState::nsDocShellLoadState(const nsDocShellLoadState& aOther) mOriginalFrameSrc(aOther.mOriginalFrameSrc), mShouldCheckForRecursion(aOther.mShouldCheckForRecursion), mIsFormSubmission(aOther.mIsFormSubmission), + mNeedsCompletelyLoadedDocument(aOther.mNeedsCompletelyLoadedDocument), + mHistoryBehavior(aOther.mHistoryBehavior), mLoadType(aOther.mLoadType), mSHEntry(aOther.mSHEntry), mTarget(aOther.mTarget), @@ -235,12 +237,13 @@ nsDocShellLoadState::nsDocShellLoadState(nsIURI* aURI, uint64_t aLoadIdentifier) mInheritPrincipal(false), mPrincipalIsExplicit(false), mNotifiedBeforeUnloadListeners(false), - mShouldNotForceReplaceInOnLoad(false), mForceAllowDataURI(false), mIsExemptFromHTTPSFirstMode(false), mOriginalFrameSrc(false), mShouldCheckForRecursion(false), mIsFormSubmission(false), + mNeedsCompletelyLoadedDocument(false), + mHistoryBehavior(Nothing()), mLoadType(LOAD_NORMAL), mSrcdocData(VoidString()), mLoadFlags(0), @@ -670,15 +673,6 @@ void nsDocShellLoadState::SetNotifiedBeforeUnloadListeners( mNotifiedBeforeUnloadListeners = aNotifiedBeforeUnloadListeners; } -bool nsDocShellLoadState::ShouldNotForceReplaceInOnLoad() const { - return mShouldNotForceReplaceInOnLoad; -} - -void nsDocShellLoadState::SetShouldNotForceReplaceInOnLoad( - bool aShouldNotForceReplaceInOnLoad) { - mShouldNotForceReplaceInOnLoad = aShouldNotForceReplaceInOnLoad; -} - bool nsDocShellLoadState::ForceAllowDataURI() const { return mForceAllowDataURI; } @@ -727,6 +721,29 @@ void nsDocShellLoadState::SetIsFormSubmission(bool aIsFormSubmission) { mIsFormSubmission = aIsFormSubmission; } +bool nsDocShellLoadState::NeedsCompletelyLoadedDocument() const { + return mNeedsCompletelyLoadedDocument; +} + +void nsDocShellLoadState::SetNeedsCompletelyLoadedDocument( + bool aNeedsCompletelyLoadedDocument) { + mNeedsCompletelyLoadedDocument = aNeedsCompletelyLoadedDocument; +} + +Maybe<mozilla::dom::NavigationHistoryBehavior> +nsDocShellLoadState::HistoryBehavior() const { + return mHistoryBehavior; +} + +void nsDocShellLoadState::SetHistoryBehavior( + mozilla::dom::NavigationHistoryBehavior aHistoryBehavior) { + mHistoryBehavior = Some(aHistoryBehavior); +} + +void nsDocShellLoadState::ResetHistoryBehavior() { + mHistoryBehavior = Nothing(); +} + uint32_t nsDocShellLoadState::LoadType() const { return mLoadType; } void nsDocShellLoadState::SetLoadType(uint32_t aLoadType) { @@ -1377,6 +1394,8 @@ DocShellLoadStateInit nsDocShellLoadState::Serialize( loadState.OriginalFrameSrc() = mOriginalFrameSrc; loadState.ShouldCheckForRecursion() = mShouldCheckForRecursion; loadState.IsFormSubmission() = mIsFormSubmission; + loadState.NeedsCompletelyLoadedDocument() = mNeedsCompletelyLoadedDocument; + loadState.HistoryBehavior() = mHistoryBehavior; loadState.LoadType() = mLoadType; loadState.userNavigationInvolvement() = mUserNavigationInvolvement; loadState.Target() = mTarget; diff --git a/docshell/base/nsDocShellLoadState.h b/docshell/base/nsDocShellLoadState.h @@ -149,10 +149,6 @@ class nsDocShellLoadState final { void SetNotifiedBeforeUnloadListeners(bool aNotifiedBeforeUnloadListeners); - bool ShouldNotForceReplaceInOnLoad() const; - - void SetShouldNotForceReplaceInOnLoad(bool aShouldNotForceReplaceInOnLoad); - bool ForceAllowDataURI() const; void SetForceAllowDataURI(bool aForceAllowDataURI); @@ -178,6 +174,18 @@ class nsDocShellLoadState final { void SetIsFormSubmission(bool aIsFormSubmission); + bool NeedsCompletelyLoadedDocument() const; + + void SetNeedsCompletelyLoadedDocument(bool aNeedsCompletelyLoadedDocument); + + mozilla::Maybe<mozilla::dom::NavigationHistoryBehavior> HistoryBehavior() + const; + + void SetHistoryBehavior( + mozilla::dom::NavigationHistoryBehavior aHistoryBehavior); + + void ResetHistoryBehavior(); + uint32_t LoadType() const; void SetLoadType(uint32_t aLoadType); @@ -542,10 +550,6 @@ class nsDocShellLoadState final { // notified if applicable. bool mNotifiedBeforeUnloadListeners; - // If this attribute is true, navigations for subframes taking place inside of - // an onload handler will not be changed to replace loads. - bool mShouldNotForceReplaceInOnLoad; - // Principal we're inheriting. If null, this means the principal should be // inherited from the current document. If set to NullPrincipal, the channel // will fill in principal information later in the load. See internal comments @@ -585,6 +589,14 @@ class nsDocShellLoadState final { // form submission. bool mIsFormSubmission; + // If this attribute is true, we need to check if the current document is + // completely loaded to determine if we should perform a push or replace load. + bool mNeedsCompletelyLoadedDocument; + + // If this attribute is `Auto`, we should determine if this should be a push + // or replace load when actually loading. + mozilla::Maybe<mozilla::dom::NavigationHistoryBehavior> mHistoryBehavior; + // Contains a load type as specified by the nsDocShellLoadTypes::load* // constants uint32_t mLoadType; diff --git a/docshell/base/nsDocShellLoadTypes.h b/docshell/base/nsDocShellLoadTypes.h @@ -21,6 +21,7 @@ # define MAKE_LOAD_TYPE(type, flags) ((type) | ((flags) << 16)) # define LOAD_TYPE_HAS_FLAGS(type, flags) ((type) & ((flags) << 16)) +# define LOAD_TYPE_SET_FLAGS(type, flags) ((type) | ((flags) << 16)) /** * These are flags that confuse ConvertLoadTypeToDocShellLoadInfo and should @@ -201,5 +202,17 @@ inline nsDOMNavigationTiming::Type ConvertLoadTypeToNavigationType( return result; } +static inline uint32_t MaybeAddLoadFlags(uint32_t aLoadType, uint32_t aFlags) { + uint32_t loadType = LOAD_TYPE_SET_FLAGS(aLoadType, aFlags); + if (IsValidLoadType(loadType)) { + return loadType; + } + + NS_WARNING("Adjusting load flags results in an invalid load type."); + return aLoadType; +} + +# undef LOAD_TYPE_SET_FLAGS + #endif // MOZILLA_INTERNAL_API #endif diff --git a/docshell/test/chrome/bug311007_window.xhtml b/docshell/test/chrome/bug311007_window.xhtml @@ -114,6 +114,13 @@ function step2A() { } function step2B(aWebProgress, aRequest, aLocation, aFlags) { + content.addEventListener( + 'load', + () => step2C(aWebProgress, aRequest, aLocation, aFlags), + {once: true}); +} + +function step2C(aWebProgress, aRequest, aLocation, aFlags) { is(aLocation.spec, kSecureURI, "A URI on HTTPS (2)"); ok(!(aFlags & Ci.nsIWebProgressListener diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp @@ -1390,6 +1390,7 @@ Document::Document(const char* aContentType) mHaveFiredTitleChange(false), mIsShowing(false), mVisible(true), + mIsCompletelyLoaded(false), mRemovedFromDocShell(false), // mAllowDNSPrefetch starts true, so that we can always reliably && it // with various values that might disable it. Since we never prefetch @@ -12572,6 +12573,8 @@ void Document::OnPageShow(bool aPersisted, EventTarget* aDispatchStartTarget, if (auto* wgc = GetWindowGlobalChild()) { wgc->UnblockBFCacheFor(BFCacheStatus::PAGE_LOADING); } + + mIsCompletelyLoaded = true; } static void DispatchFullscreenChange(Document& aDocument, nsINode* aTarget) { diff --git a/dom/base/Document.h b/dom/base/Document.h @@ -2676,12 +2676,20 @@ class Document : public nsINode, * called yet. */ bool IsShowing() const { return mIsShowing; } + /** * Return whether the document is currently visible (in the sense of * OnPageHide having been called and OnPageShow not yet having been called) */ bool IsVisible() const { return mVisible; } + /** + * Return whether the document has completely finished loading, in the spec + * sense. We only store a bool though, whereas spec stores when loading + * finished. See https://html.spec.whatwg.org/#completely-loaded-time + */ + bool IsCompletelyLoaded() const { return mIsCompletelyLoaded; } + void SetSuppressedEventListener(EventListener* aListener); EventListener* GetSuppressedEventListener() { @@ -4842,6 +4850,10 @@ class Document : public nsINode, // it's false only when we're in bfcache or unloaded. bool mVisible : 1; + // State for IsCompletelyLoaded. Starts off false and becomes true after + // pageshow has fired. Doesn't reset after that. + bool mIsCompletelyLoaded : 1; + // True if our content viewer has been removed from the docshell // (it may still be displayed, but in zombie state). Form control data // has been saved. diff --git a/dom/base/Location.cpp b/dom/base/Location.cpp @@ -173,7 +173,7 @@ void Location::SetHash(const nsACString& aHash, nsIPrincipal& aSubjectPrincipal, return; } - SetURI(uri, aSubjectPrincipal, aRv); + Navigate(uri, aSubjectPrincipal, aRv); } void Location::GetHost(nsACString& aHost, nsIPrincipal& aSubjectPrincipal, @@ -211,7 +211,7 @@ void Location::SetHost(const nsACString& aHost, nsIPrincipal& aSubjectPrincipal, return; } - SetURI(uri, aSubjectPrincipal, aRv); + Navigate(uri, aSubjectPrincipal, aRv); } void Location::GetHostname(nsACString& aHostname, @@ -248,7 +248,7 @@ void Location::SetHostname(const nsACString& aHostname, return; } - SetURI(uri, aSubjectPrincipal, aRv); + Navigate(uri, aSubjectPrincipal, aRv); } nsresult Location::GetHref(nsACString& aHref) { @@ -328,7 +328,7 @@ void Location::SetPathname(const nsACString& aPathname, return; } - SetURI(uri, aSubjectPrincipal, aRv); + Navigate(uri, aSubjectPrincipal, aRv); } void Location::GetPort(nsACString& aPort, nsIPrincipal& aSubjectPrincipal, @@ -387,7 +387,7 @@ void Location::SetPort(const nsACString& aPort, nsIPrincipal& aSubjectPrincipal, return; } - SetURI(uri, aSubjectPrincipal, aRv); + Navigate(uri, aSubjectPrincipal, aRv); } void Location::GetProtocol(nsACString& aProtocol, @@ -462,7 +462,7 @@ void Location::SetProtocol(const nsACString& aProtocol, return; } - SetURI(uri, aSubjectPrincipal, aRv); + Navigate(uri, aSubjectPrincipal, aRv); } void Location::GetSearch(nsACString& aSearch, nsIPrincipal& aSubjectPrincipal, @@ -513,7 +513,7 @@ void Location::SetSearch(const nsACString& aSearch, return; } - SetURI(uri, aSubjectPrincipal, aRv); + Navigate(uri, aSubjectPrincipal, aRv); } void Location::Reload(JSContext* aCx, bool aForceget, diff --git a/dom/base/LocationBase.cpp b/dom/base/LocationBase.cpp @@ -12,6 +12,7 @@ #include "mozilla/dom/WindowContext.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" +#include "nsDocLoader.h" #include "nsDocShellLoadState.h" #include "nsError.h" #include "nsGlobalWindowInner.h" @@ -23,16 +24,28 @@ namespace mozilla::dom { -void LocationBase::SetURI(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, - ErrorResult& aRv, bool aReplace) { - RefPtr<BrowsingContext> bc = GetBrowsingContext(); - if (!bc || bc->IsDiscarded()) { +static bool IncumbentGlobalHasTransientActivation() { + nsGlobalWindowInner* window = nsContentUtils::IncumbentInnerWindow(); + return window && window->GetWindowContext() && window->GetWindowContext() && + window->GetWindowContext()->HasValidTransientUserGestureActivation(); +} + +// https://html.spec.whatwg.org/#location-object-navigate +void LocationBase::Navigate(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, + ErrorResult& aRv, + NavigationHistoryBehavior aHistoryHandling) { + // Step 1 + RefPtr<BrowsingContext> navigable = GetBrowsingContext(); + if (!navigable || navigable->IsDiscarded()) { return; } - bc->Navigate(aURI, aSubjectPrincipal, aRv, - aReplace ? NavigationHistoryBehavior::Replace - : NavigationHistoryBehavior::Auto); + // Step 2-3, except the check for if document is completely loaded. + bool needsCompletelyLoadedDocument = !IncumbentGlobalHasTransientActivation(); + + // Step 4 + navigable->Navigate(aURI, aSubjectPrincipal, aRv, aHistoryHandling, + needsCompletelyLoadedDocument); } void LocationBase::SetHref(const nsACString& aHref, @@ -94,7 +107,12 @@ void LocationBase::SetHrefWithBase(const nsACString& aHref, nsIURI* aBase, } } - SetURI(newUri, aSubjectPrincipal, aRv, aReplace || inScriptTag); + NavigationHistoryBehavior historyHandling = NavigationHistoryBehavior::Auto; + if (aReplace || inScriptTag) { + historyHandling = NavigationHistoryBehavior::Replace; + } + + Navigate(newUri, aSubjectPrincipal, aRv, historyHandling); } void LocationBase::Replace(const nsACString& aUrl, diff --git a/dom/base/LocationBase.h b/dom/base/LocationBase.h @@ -7,6 +7,7 @@ #ifndef mozilla_dom_LocationBase_h #define mozilla_dom_LocationBase_h +#include "mozilla/dom/NavigationBinding.h" #include "nsStringFwd.h" class nsIDocShell; @@ -37,8 +38,9 @@ class LocationBase { virtual BrowsingContext* GetBrowsingContext() = 0; virtual nsIDocShell* GetDocShell() = 0; - void SetURI(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv, - bool aReplace = false); + void Navigate(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv, + NavigationHistoryBehavior aHistoryHandling = + NavigationHistoryBehavior::Auto); void SetHrefWithBase(const nsACString& aHref, nsIURI* aBase, nsIPrincipal& aSubjectPrincipal, bool aReplace, ErrorResult& aRv); diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp @@ -6769,7 +6769,7 @@ nsresult nsGlobalWindowOuter::OpenInternal( // BrowsingContext::RevisePopupAbuseLevel() below. RefPtr<nsDocShellLoadState> loadState = aLoadState; if (!loadState && aNavigate && uri) { - loadState = nsWindowWatcher::CreateLoadState(uri, this); + loadState = nsWindowWatcher::CreateLoadState(uri, this, aDoJSFixups); } PopupBlocker::PopupControlState abuseLevel = diff --git a/dom/ipc/DOMTypes.ipdlh b/dom/ipc/DOMTypes.ipdlh @@ -64,6 +64,7 @@ using mozilla::dom::NotificationDirection from "mozilla/dom/NotificationBinding. using mozilla::dom::UserNavigationInvolvement from "mozilla/dom/UserNavigationInvolvement.h"; using mozilla::net::ClassificationFlags from "nsIClassifiedChannel.h"; using mozilla::dom::ForceMediaDocument from "mozilla/dom/LoadURIOptionsBinding.h"; +using mozilla::dom::NavigationHistoryBehavior from "mozilla/dom/NavigationBinding.h"; namespace mozilla { namespace dom { @@ -226,6 +227,10 @@ struct DocShellLoadStateInit bool TextDirectiveUserActivation; bool AllowFocusMove; bool IsFromProcessingFrameAttributes; + bool NeedsCompletelyLoadedDocument; + bool ShouldNotForceReplaceInOnLoad; + NavigationHistoryBehavior? HistoryBehavior; + SchemelessInputType SchemelessInput; ForceMediaDocument forceMediaDocument; HTTPSUpgradeTelemetryType HttpsUpgradeTelemetry; diff --git a/dom/ipc/NavigationAPIIPCUtils.h b/dom/ipc/NavigationAPIIPCUtils.h @@ -23,5 +23,10 @@ template <> struct ParamTraits<mozilla::dom::NavigationType> : public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::NavigationType> { }; + +template <> +struct ParamTraits<mozilla::dom::NavigationHistoryBehavior> + : public mozilla::dom::WebIDLEnumSerializer< + mozilla::dom::NavigationHistoryBehavior> {}; } // namespace IPC #endif diff --git a/dom/navigation/Navigation.cpp b/dom/navigation/Navigation.cpp @@ -564,7 +564,7 @@ void Navigation::Navigate(JSContext* aCx, const nsAString& aUrl, MOZ_DIAGNOSTIC_ASSERT(bc); bc->Navigate(urlRecord, *document->NodePrincipal(), /* per spec, error handling defaults to false */ IgnoreErrors(), - aOptions.mHistory, /* aShouldNotForceReplaceInOnLoad */ true); + aOptions.mHistory); // 13. If this's upcoming non-traverse API method tracker is apiMethodTracker, // then: diff --git a/dom/security/test/sec-fetch/file_no_cache.sjs b/dom/security/test/sec-fetch/file_no_cache.sjs @@ -2,7 +2,7 @@ const MESSAGE_PAGE = function (msg) { return ` <html> <script type="text/javascript"> -window.parent.postMessage({test : "${msg}"},"*"); +onload = () => window.parent.postMessage({test : "${msg}"},"*"); </script> <script> addEventListener("back", () => { diff --git a/dom/security/test/sec-fetch/test_iframe_history_manipulation.html b/dom/security/test/sec-fetch/test_iframe_history_manipulation.html @@ -78,7 +78,7 @@ SimpleTest.waitForExplicitFinish(); testFrame = document.createElement('iframe'); testFrame.src = `https://example.org/${REQUEST_PATH}?test`; -document.body.appendChild(testFrame); +onload = () => setTimeout(() => document.body.appendChild(testFrame), 0); </script> </body> diff --git a/testing/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html.ini b/testing/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html.ini @@ -2,4 +2,5 @@ expected: if (processor == "x86") and (os == "linux"): [OK, ERROR] [NO replace before load, triggered by location.assign()] - expected: FAIL + expected: + if (os == "android") and not sessionHistoryInParent: FAIL diff --git a/testing/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html.ini b/testing/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html.ini @@ -2,10 +2,16 @@ expected: if (processor == "x86") and (os == "linux"): [OK, CRASH] [href] - expected: FAIL + expected: + if (os == "android") and not sessionHistoryInParent: FAIL + if (os == "mac"): [PASS, FAIL] [search] - expected: FAIL + expected: + if (os == "android") and not sessionHistoryInParent: FAIL + if (os == "mac"): [PASS, FAIL] [hash] - expected: FAIL + expected: + if (os == "android") and not sessionHistoryInParent: FAIL + if (os == "mac"): [PASS, FAIL] diff --git a/testing/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html.ini b/testing/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html.ini @@ -2,10 +2,13 @@ expected: if (os == "android") and fission: [OK, TIMEOUT] [href] - expected: FAIL + expected: + if (os == "android") and not sessionHistoryInParent: FAIL [search] - expected: FAIL + expected: + if (os == "android") and not sessionHistoryInParent: FAIL [hash] - expected: FAIL + expected: + if (os == "android") and not sessionHistoryInParent: FAIL diff --git a/testing/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html.ini b/testing/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html.ini @@ -2,4 +2,5 @@ expected: if (os == "android") and fission: [OK, TIMEOUT] [No replace during load, triggered by window.open(_self) on an iframe] - expected: FAIL + expected: + if (os == "android") and not sessionHistoryInParent: FAIL diff --git a/testing/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html.ini b/testing/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html.ini @@ -2,4 +2,5 @@ expected: if (os == "android") and fission: [OK, TIMEOUT] [No replace during pageshow, triggered by window.open(_self) on an iframe] - expected: FAIL + expected: + if (os == "android") and not sessionHistoryInParent: FAIL diff --git a/testing/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html.ini b/testing/web-platform/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html.ini @@ -2,4 +2,5 @@ expected: if (os == "android") and fission: [OK, TIMEOUT] [No replace before load, triggered by window.open(_self) on an iframe] - expected: FAIL + expected: + if (os == "android") and not sessionHistoryInParent: FAIL diff --git a/testing/web-platform/meta/html/browsers/history/the-history-interface/traverse-during-beforeunload.html.ini b/testing/web-platform/meta/html/browsers/history/the-history-interface/traverse-during-beforeunload.html.ini @@ -1,4 +1,4 @@ [traverse-during-beforeunload.html] [Traversing the history during beforeunload] - expected: FAIL - + expected: + if (os == "android") and not sessionHistoryInParent: FAIL diff --git a/testing/web-platform/meta/navigation-api/currententrychange-event/location-api.html.ini b/testing/web-platform/meta/navigation-api/currententrychange-event/location-api.html.ini @@ -1,3 +0,0 @@ -[location-api.html] - [currententrychange fires for location API navigations] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin-sameorigindomain.sub.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin-sameorigindomain.sub.html.ini @@ -1,3 +0,0 @@ -[location-crossdocument-crossorigin-sameorigindomain.sub.html] - [using location.href to navigate cross-document targeting a same-origin-domain (but cross-origin) window] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/cross-window/location-crossdocument-sameorigin.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/cross-window/location-crossdocument-sameorigin.html.ini @@ -1,3 +0,0 @@ -[location-crossdocument-sameorigin.html] - [using location.href to navigate cross-document targeting a same-origin window] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin-sameorigindomain.sub.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin-sameorigindomain.sub.html.ini @@ -1,12 +0,0 @@ -[location-samedocument-crossorigin-sameorigindomain.sub.html] - expected: - if (os == "win") and debug and (processor == "x86_64"): [OK, CRASH, FAIL, ERROR] - if (os == "win") and debug and (processor == "x86"): [OK, FAIL, ERROR, CRASH] - if (os == "linux") and asan and fission: [CRASH, OK, FAIL] - if (os == "linux") and asan and not fission: [OK, CRASH, FAIL] - if (os == "linux") and not asan and (processor == "x86"): [OK, CRASH, FAIL] - if (os == "mac") and not debug: [OK, ERROR, FAIL, CRASH] - if (os == "android") and debug: [CRASH, OK, FAIL] - [OK, FAIL, CRASH] - [using location.href to navigate same-document targeting a same-origin-domain (but cross-origin) window] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin.html.ini @@ -1,3 +0,0 @@ -[location-samedocument-crossorigin.html] - [using location.href to navigate same-document targeting a cross-origin window] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/cross-window/location-samedocument-sameorigin.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/cross-window/location-samedocument-sameorigin.html.ini @@ -1,3 +0,0 @@ -[location-samedocument-sameorigin.html] - [using location.href to navigate same-document targeting a same-origin window] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin-sameorigindomain.sub.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin-sameorigindomain.sub.html.ini @@ -1,3 +0,0 @@ -[open-crossdocument-crossorigin-sameorigindomain.sub.html] - [using window.open() to navigate cross-document targeting a same-origin-domain (but cross-origin) window] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/cross-window/open-crossdocument-sameorigin.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/cross-window/open-crossdocument-sameorigin.html.ini @@ -1,3 +0,0 @@ -[open-crossdocument-sameorigin.html] - [using window.open() to navigate cross-document targeting a same-origin window] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin-sameorigindomain.sub.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin-sameorigindomain.sub.html.ini @@ -1,3 +0,0 @@ -[open-samedocument-crossorigin-sameorigindomain.sub.html] - [using window.open() to navigate same-document targeting a same-origin-domain (but cross-origin) window] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin.html.ini @@ -1,3 +0,0 @@ -[open-samedocument-crossorigin.html] - [using window.open() to navigate same-document targeting a cross-origin window] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/cross-window/open-samedocument-sameorigin.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/cross-window/open-samedocument-sameorigin.html.ini @@ -1,3 +0,0 @@ -[open-samedocument-sameorigin.html] - [using window.open() to navigate same-document targeting a same-origin window] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/navigate-iframe-location.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/navigate-iframe-location.html.ini @@ -1,3 +0,0 @@ -[navigate-iframe-location.html] - [location API on another window fires navigate event in the target window only] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/navigate-navigation-navigate.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/navigate-navigation-navigate.html.ini @@ -1,3 +0,0 @@ -[navigate-navigation-navigate.html] - [navigate event for navigation.navigate()] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/navigate-window-open-self.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/navigate-window-open-self.html.ini @@ -1,3 +0,0 @@ -[navigate-window-open-self.html] - [window.open() fires navigate event when targeting self] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigate-event/navigate-window-open.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/navigate-window-open.html.ini @@ -1,5 +0,0 @@ -[navigate-window-open.html] - expected: - if os == "mac": [OK, ERROR, CRASH] - [window.open() fires navigate event in target window but not source] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigation-history-entry/current-basic.html.ini b/testing/web-platform/meta/navigation-api/navigation-history-entry/current-basic.html.ini @@ -1,3 +0,0 @@ -[current-basic.html] - [Basic tests for navigation.currentEntry] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/navigation-methods/sandboxing-back-parent.html.ini b/testing/web-platform/meta/navigation-api/navigation-methods/sandboxing-back-parent.html.ini @@ -1,3 +1,6 @@ [sandboxing-back-parent.html] + expected: + OK [A sandboxed iframe cannot navigate its parent via its own navigation object by using back()] - expected: FAIL + expected: + FAIL diff --git a/testing/web-platform/meta/navigation-api/precommit-handler/precommitHandler-push.html.ini b/testing/web-platform/meta/navigation-api/precommit-handler/precommitHandler-push.html.ini @@ -3,4 +3,4 @@ expected: FAIL [precommitHandler for a push navigation, reject after commit] - expected: FAIL + expected: [PASS, FAIL] diff --git a/testing/web-platform/meta/navigation-api/state/cross-document-getState-undefined.html.ini b/testing/web-platform/meta/navigation-api/state/cross-document-getState-undefined.html.ini @@ -1,3 +0,0 @@ -[cross-document-getState-undefined.html] - [Default behavior for entry.getState() for a non-current cross-document entry] - expected: FAIL diff --git a/testing/web-platform/meta/navigation-api/state/cross-document-location-api.html.ini b/testing/web-platform/meta/navigation-api/state/cross-document-location-api.html.ini @@ -1,15 +0,0 @@ -[cross-document-location-api.html?method=navigate] - expected: - if (os == "linux") and not tsan and (processor == "x86_64") and debug and not fission: [CRASH, OK] - if (os == "win") and not debug and (processor == "x86"): [CRASH, OK] - if (os == "mac") and not debug: [ERROR, CRASH, OK] - if (os == "linux") and tsan: [CRASH, OK] - if (os == "android") and not debug: [CRASH, OK] - [OK, CRASH] - [entry.getState() behavior after cross-document location API navigation] - expected: FAIL - - -[cross-document-location-api.html?method=updateCurrentEntry] - [entry.getState() behavior after cross-document location API navigation] - expected: FAIL diff --git a/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/navigation_started/navigation_interrupted.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/browsing_context/navigation_started/navigation_interrupted.py.ini @@ -1,3 +1,4 @@ [navigation_interrupted.py] [test_multiple_events_for_interrupted_navigation[Interrupted on load\]] - expected: FAIL + expected: + if not sessionHistoryInParent: FAIL diff --git a/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth-navigation.html b/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth-navigation.html @@ -70,7 +70,6 @@ }); location.hash = "foo"; }; - instant(); function smooth() { document.documentElement.className = ""; @@ -110,4 +109,9 @@ }; testContainer.style.display = "none"; + + // Setting location.hash while loading results in a replace load, which we don't want. + window.onload = () => { + step_timeout(instant, 0); + } </script> diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-1.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-1.html @@ -1,6 +1,6 @@ <!doctype html> <script> -opener.history_length = history.length; +opener.test_initial_history_length(history.length); </script> <form onsubmit="location = 'scripted_form_submit_assign_during_load-2.html'; return false;"> <input type=submit value="Click Me"> diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-2.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load-2.html @@ -2,6 +2,6 @@ <p>This window should close itself and the test result appear in the original window <script> onload = function() { - setTimeout(function() {opener.do_test(history.length); window.close();}, 100); + setTimeout(function() {opener.test_assign_during_load(history.length); window.close();}, 100); } </script> diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/scripted_form_submit_assign_during_load.html @@ -10,8 +10,12 @@ var t = async_test(); var win = window.open("scripted_form_submit_assign_during_load-1.html"); var history_length; -do_test = t.step_func(function(new_length) { - assert_equals(new_length, history_length); +test_initial_history_length = t.step_func(function(new_length) { + history_length = new_length; + assert_equals(history_length, 1, "Should have inital history"); +}); +test_assign_during_load = t.step_func(function(new_length) { + assert_equals(new_length, history_length, "Assigning to Location during load should replace"); t.done(); }); </script> diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/self-close.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/self-close.html @@ -2,7 +2,7 @@ <p>self-close.html?navs=n&channel=name will navigate n times, then close and notify the channel.</p> <script> -window.onload = setTimeout(() => { +window.onload = () => setTimeout(() => { const urlParams = new URLSearchParams(window.location.search); let n = parseInt(urlParams.get('navs')) || 0; diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-navigate.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-navigate.html @@ -4,7 +4,7 @@ <script> async_test(t => { navigation.onnavigate = t.step_func_done(e => { - assert_equals(e.navigationType, "replace"); + assert_equals(e.navigationType, "push"); assert_true(e.cancelable); assert_true(e.canIntercept); assert_false(e.userInitiated); diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.cpp b/toolkit/components/windowwatcher/nsWindowWatcher.cpp @@ -2064,7 +2064,7 @@ bool nsWindowWatcher::HaveSpecifiedSize(const WindowFeatures& features) { /* static */ already_AddRefed<nsDocShellLoadState> nsWindowWatcher::CreateLoadState( - nsIURI* aUri, nsPIDOMWindowOuter* aParent) { + nsIURI* aUri, nsPIDOMWindowOuter* aParent, bool aIsWindowOpen) { MOZ_ASSERT(aUri); RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aUri); @@ -2094,6 +2094,12 @@ already_AddRefed<nsDocShellLoadState> nsWindowWatcher::CreateLoadState( } } + // If we're called from JS, i.e window.open, we need to set history handling + // behavior here to be able to do push to replace conversion if needed. + if (aIsWindowOpen && mozilla::SessionHistoryInParent()) { + loadState->SetHistoryBehavior(NavigationHistoryBehavior::Auto); + } + return loadState.forget(); } diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.h b/toolkit/components/windowwatcher/nsWindowWatcher.h @@ -71,6 +71,7 @@ class nsWindowWatcher : public nsIWindowWatcher, * - the user gesture activation flag based on the parent document * - the text directive user activation flag; this will consume the parent * document's flag and OR's it with the user gesture activation flag. + * If `aIsWindowOpen` is true, history handling will be set to "auto". * * Currently, the returned load state is intended to be passed into * `OpenWindowInternal()`. @@ -78,7 +79,7 @@ class nsWindowWatcher : public nsIWindowWatcher, * function. */ static already_AddRefed<nsDocShellLoadState> CreateLoadState( - nsIURI* aUri, nsPIDOMWindowOuter* aParent); + nsIURI* aUri, nsPIDOMWindowOuter* aParent, bool aIsWindowOpen = false); protected: virtual ~nsWindowWatcher();