diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp index 79d148133720..1fb4feb81637 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/docshell/base/CanonicalBrowsingContext.cpp @@ -389,6 +389,40 @@ CanonicalBrowsingContext::CreateLoadingSessionHistoryEntryForLoad( return loadingInfo; } +UniquePtr +CanonicalBrowsingContext::ReplaceLoadingSessionHistoryEntryForLoad( + LoadingSessionHistoryInfo* aInfo, nsIChannel* aChannel) { + MOZ_ASSERT(aInfo); + MOZ_ASSERT(aChannel); + + UniquePtr newInfo = MakeUnique( + aChannel, aInfo->mInfo.LoadType(), + aInfo->mInfo.GetPartitionedPrincipalToInherit(), aInfo->mInfo.GetCsp()); + + RefPtr newEntry = new SessionHistoryEntry(newInfo.get()); + if (IsTop()) { + // Only top level pages care about Get/SetPersist. + nsCOMPtr uri; + aChannel->GetURI(getter_AddRefs(uri)); + newEntry->SetPersist(nsDocShell::ShouldAddToSessionHistory(uri, aChannel)); + } + newEntry->SetDocshellID(GetHistoryID()); + newEntry->SetIsDynamicallyAdded(CreatedDynamically()); + newEntry->SetForInitialLoad(true); + + // Replacing the old entry. + SessionHistoryEntry::SetByLoadId(aInfo->mLoadId, newEntry); + + for (size_t i = 0; i < mLoadingEntries.Length(); ++i) { + if (mLoadingEntries[i].mLoadId == aInfo->mLoadId) { + mLoadingEntries[i].mEntry = newEntry; + break; + } + } + + return MakeUnique(newEntry, aInfo->mLoadId); +} + void CanonicalBrowsingContext::SessionHistoryCommit(uint64_t aLoadId, const nsID& aChangeID, uint32_t aLoadType) { diff --git a/docshell/base/CanonicalBrowsingContext.h b/docshell/base/CanonicalBrowsingContext.h index c781c00e11ae..74489c15825c 100644 --- a/docshell/base/CanonicalBrowsingContext.h +++ b/docshell/base/CanonicalBrowsingContext.h @@ -108,6 +108,10 @@ class CanonicalBrowsingContext final : public BrowsingContext { UniquePtr CreateLoadingSessionHistoryEntryForLoad( nsDocShellLoadState* aLoadState, nsIChannel* aChannel); + + UniquePtr ReplaceLoadingSessionHistoryEntryForLoad( + LoadingSessionHistoryInfo* aInfo, nsIChannel* aChannel); + void SessionHistoryCommit(uint64_t aLoadId, const nsID& aChangeID, uint32_t aLoadType); diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index be7b74b24cbb..665db419cb49 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -13273,7 +13273,12 @@ void nsDocShell::MoveLoadingToActiveEntry() { RefPtr rootSH = GetRootSessionHistory(); if (rootSH) { if (!loadingEntry->mLoadIsFromSessionHistory) { - changeID = rootSH->AddPendingHistoryChange(); + // It is possible that this leads to wrong length temporarily, but + // so would not having the check for replace. + if (!LOAD_TYPE_HAS_FLAGS( + mLoadType, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY)) { + changeID = rootSH->AddPendingHistoryChange(); + } } else { // This is a load from session history, so we can update // index and length immediately. diff --git a/docshell/shistory/SessionHistoryEntry.cpp b/docshell/shistory/SessionHistoryEntry.cpp index 356f452406d9..604ede23d015 100644 --- a/docshell/shistory/SessionHistoryEntry.cpp +++ b/docshell/shistory/SessionHistoryEntry.cpp @@ -38,11 +38,6 @@ SessionHistoryInfo::SessionHistoryInfo(nsDocShellLoadState* aLoadState, /* FIXME Is this correct? */ aLoadState->TypeHint())) { MaybeUpdateTitleFromURI(); - bool isNoStore = false; - if (nsCOMPtr httpChannel = do_QueryInterface(aChannel)) { - Unused << httpChannel->IsNoStoreResponse(&isNoStore); - mPersist = !isNoStore; - } } SessionHistoryInfo::SessionHistoryInfo( @@ -70,6 +65,39 @@ SessionHistoryInfo::SessionHistoryInfo( MaybeUpdateTitleFromURI(); } +SessionHistoryInfo::SessionHistoryInfo( + nsIChannel* aChannel, uint32_t aLoadType, + nsIPrincipal* aPartitionedPrincipalToInherit, + nsIContentSecurityPolicy* aCsp) { + aChannel->GetURI(getter_AddRefs(mURI)); + mLoadType = aLoadType; + + nsCOMPtr loadInfo; + aChannel->GetLoadInfo(getter_AddRefs(loadInfo)); + + loadInfo->GetResultPrincipalURI(getter_AddRefs(mResultPrincipalURI)); + loadInfo->GetTriggeringPrincipal( + getter_AddRefs(mSharedState.Get()->mTriggeringPrincipal)); + loadInfo->GetPrincipalToInherit( + getter_AddRefs(mSharedState.Get()->mPrincipalToInherit)); + + mSharedState.Get()->mPartitionedPrincipalToInherit = + aPartitionedPrincipalToInherit; + mSharedState.Get()->mCsp = aCsp; + aChannel->GetContentType(mSharedState.Get()->mContentType); + aChannel->GetOriginalURI(getter_AddRefs(mOriginalURI)); + + uint32_t loadFlags; + aChannel->GetLoadFlags(&loadFlags); + mLoadReplace = !!(loadFlags & nsIChannel::LOAD_REPLACE); + + MaybeUpdateTitleFromURI(); + + if (nsCOMPtr httpChannel = do_QueryInterface(aChannel)) { + mReferrerInfo = httpChannel->GetReferrerInfo(); + } +} + void SessionHistoryInfo::Reset(nsIURI* aURI, const nsID& aDocShellID, bool aDynamicCreation, nsIPrincipal* aTriggeringPrincipal, @@ -291,13 +319,7 @@ nsDataHashtable* LoadingSessionHistoryInfo::LoadingSessionHistoryInfo( SessionHistoryEntry* aEntry) : mInfo(aEntry->Info()), mLoadId(++gLoadingSessionHistoryInfoLoadId) { - if (!SessionHistoryEntry::sLoadIdToEntry) { - SessionHistoryEntry::sLoadIdToEntry = - new nsDataHashtable(); - } - MOZ_LOG(gSHLog, LogLevel::Verbose, - ("SHEntry::AddLoadId(%" PRIu64 " - %p", mLoadId, aEntry)); - SessionHistoryEntry::sLoadIdToEntry->Put(mLoadId, aEntry); + SessionHistoryEntry::SetByLoadId(mLoadId, aEntry); } LoadingSessionHistoryInfo::LoadingSessionHistoryInfo( @@ -330,6 +352,19 @@ SessionHistoryEntry* SessionHistoryEntry::GetByLoadId(uint64_t aLoadId) { return sLoadIdToEntry->Get(aLoadId); } +void SessionHistoryEntry::SetByLoadId(uint64_t aLoadId, + SessionHistoryEntry* aEntry) { + if (!sLoadIdToEntry) { + sLoadIdToEntry = + new nsDataHashtable(); + } + + MOZ_LOG( + gSHLog, LogLevel::Verbose, + ("SessionHistoryEntry::SetByLoadId(%" PRIu64 " - %p", aLoadId, aEntry)); + sLoadIdToEntry->Put(aLoadId, aEntry); +} + void SessionHistoryEntry::RemoveLoadId(uint64_t aLoadId) { MOZ_ASSERT(XRE_IsParentProcess()); if (!sLoadIdToEntry) { diff --git a/docshell/shistory/SessionHistoryEntry.h b/docshell/shistory/SessionHistoryEntry.h index 099ccb64fc25..0cb825a59526 100644 --- a/docshell/shistory/SessionHistoryEntry.h +++ b/docshell/shistory/SessionHistoryEntry.h @@ -43,6 +43,9 @@ class SessionHistoryInfo { nsIPrincipal* aPartitionedPrincipalToInherit, nsIContentSecurityPolicy* aCsp, const nsACString& aContentType); + SessionHistoryInfo(nsIChannel* aChannel, uint32_t aLoadType, + nsIPrincipal* aPartitionedPrincipalToInherit, + nsIContentSecurityPolicy* aCsp); void Reset(nsIURI* aURI, const nsID& aDocShellID, bool aDynamicCreation, nsIPrincipal* aTriggeringPrincipal, @@ -122,6 +125,8 @@ class SessionHistoryInfo { void FillLoadInfo(nsDocShellLoadState& aLoadState) const; + uint32_t LoadType() { return mLoadType; } + private: friend class SessionHistoryEntry; friend struct mozilla::ipc::IPDLParamTraits; @@ -262,6 +267,7 @@ class SessionHistoryEntry : public nsISHEntry { // Get an entry based on LoadingSessionHistoryInfo's mLoadId. Parent process // only. static SessionHistoryEntry* GetByLoadId(uint64_t aLoadId); + static void SetByLoadId(uint64_t aLoadId, SessionHistoryEntry* aEntry); static void RemoveLoadId(uint64_t aLoadId); const nsTArray>& Children() { return mChildren; } diff --git a/docshell/test/mochitest/file_redirect_history.html b/docshell/test/mochitest/file_redirect_history.html new file mode 100644 index 000000000000..3971faf4fdfb --- /dev/null +++ b/docshell/test/mochitest/file_redirect_history.html @@ -0,0 +1,18 @@ + + + + + +
+ +
+ + diff --git a/docshell/test/mochitest/form_submit_redirect.sjs b/docshell/test/mochitest/form_submit_redirect.sjs new file mode 100644 index 000000000000..9953f28989ef --- /dev/null +++ b/docshell/test/mochitest/form_submit_redirect.sjs @@ -0,0 +1,10 @@ +"use strict"; + +async function handleRequest(request, response) { + if (request.method !== "POST") { + message = "bad"; + } else { + response.setStatusLine(request.httpVersion, 302, "Moved Temporarily"); + response.setHeader("Location", request.getHeader("referer")); + } +} diff --git a/docshell/test/mochitest/mochitest.ini b/docshell/test/mochitest/mochitest.ini index ebd424aafd33..774799c888ad 100644 --- a/docshell/test/mochitest/mochitest.ini +++ b/docshell/test/mochitest/mochitest.ini @@ -127,6 +127,10 @@ support-files = file_history_length_during_pageload_2.html [test_pushState_after_document_open.html] [test_navigate_after_pagehide.html] +[test_redirect_history.html] +support-files = + file_redirect_history.html + form_submit_redirect.sjs [test_windowedhistoryframes.html] skip-if = (!debug && os == 'android') [test_triggeringprincipal_location_seturi.html] diff --git a/docshell/test/mochitest/test_redirect_history.html b/docshell/test/mochitest/test_redirect_history.html new file mode 100644 index 000000000000..82754ad7a478 --- /dev/null +++ b/docshell/test/mochitest/test_redirect_history.html @@ -0,0 +1,57 @@ + + + + Test for redirect from POST + + + + + + + + diff --git a/netwerk/ipc/DocumentLoadListener.cpp b/netwerk/ipc/DocumentLoadListener.cpp index 274ed0d91c52..47c5edc2607b 100644 --- a/netwerk/ipc/DocumentLoadListener.cpp +++ b/netwerk/ipc/DocumentLoadListener.cpp @@ -2394,12 +2394,20 @@ DocumentLoadListener::AsyncOnChannelRedirect( return NS_OK; } - if (GetDocumentBrowsingContext() && !net::ChannelIsPost(aOldChannel)) { - AddURIVisit(aOldChannel, 0); + if (GetDocumentBrowsingContext()) { + if (mLoadingSessionHistoryInfo) { + mLoadingSessionHistoryInfo = + GetDocumentBrowsingContext() + ->ReplaceLoadingSessionHistoryEntryForLoad( + mLoadingSessionHistoryInfo.get(), aNewChannel); + } + if (!net::ChannelIsPost(aOldChannel)) { + AddURIVisit(aOldChannel, 0); - nsCOMPtr oldURI; - aOldChannel->GetURI(getter_AddRefs(oldURI)); - nsDocShell::SaveLastVisit(aNewChannel, oldURI, aFlags); + nsCOMPtr oldURI; + aOldChannel->GetURI(getter_AddRefs(oldURI)); + nsDocShell::SaveLastVisit(aNewChannel, oldURI, aFlags); + } } mHaveVisibleRedirect |= true;