Bug 1736570 - Avoid DocumentChannel for nsParser-created initial about:blank replacement. r=nika,smaug

Differential Revision: https://phabricator.services.mozilla.com/D135106
This commit is contained in:
Henri Sivonen 2022-02-17 13:27:15 +00:00
Родитель 4814249c00
Коммит 2fd5af8ef4
13 изменённых файлов: 196 добавлений и 47 удалений

Просмотреть файл

@ -3562,7 +3562,7 @@ void BrowsingContext::SessionHistoryCommit(
void BrowsingContext::SetActiveSessionHistoryEntry(
const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo* aInfo,
uint32_t aLoadType, uint32_t aUpdatedCacheKey) {
uint32_t aLoadType, uint32_t aUpdatedCacheKey, bool aUpdateLength) {
if (XRE_IsContentProcess()) {
// XXX Why we update cache key only in content process case?
if (aUpdatedCacheKey != 0) {
@ -3570,9 +3570,11 @@ void BrowsingContext::SetActiveSessionHistoryEntry(
}
nsID changeID = {};
RefPtr<ChildSHistory> shistory = Top()->GetChildSessionHistory();
if (shistory) {
changeID = shistory->AddPendingHistoryChange();
if (aUpdateLength) {
RefPtr<ChildSHistory> shistory = Top()->GetChildSessionHistory();
if (shistory) {
changeID = shistory->AddPendingHistoryChange();
}
}
ContentChild::GetSingleton()->SendSetActiveSessionHistoryEntry(
this, aPreviousScrollPos, *aInfo, aLoadType, aUpdatedCacheKey,

Просмотреть файл

@ -817,7 +817,8 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
void SetActiveSessionHistoryEntry(const Maybe<nsPoint>& aPreviousScrollPos,
SessionHistoryInfo* aInfo,
uint32_t aLoadType,
uint32_t aUpdatedCacheKey);
uint32_t aUpdatedCacheKey,
bool aUpdateLength = true);
// Replace the active entry for this browsing context. This is used for
// implementing history.replaceState and same document navigations.

Просмотреть файл

@ -755,6 +755,8 @@ void CanonicalBrowsingContext::SessionHistoryCommit(
MOZ_LOG(gSHLog, LogLevel::Verbose,
("CanonicalBrowsingContext::SessionHistoryCommit %p %" PRIu64, this,
aLoadId));
MOZ_ASSERT(aLoadId != UINT64_MAX,
"Must not send special about:blank loadinfo to parent.");
for (size_t i = 0; i < mLoadingEntries.Length(); ++i) {
if (mLoadingEntries[i].mLoadId == aLoadId) {
nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());

Просмотреть файл

@ -232,6 +232,7 @@
#include "nsWidgetsCID.h"
#include "nsXULAppAPI.h"
#include "ThirdPartyUtil.h"
#include "BRNameMatchingPolicy.h"
#include "GeckoProfiler.h"
#include "mozilla/NullPrincipal.h"
@ -391,7 +392,8 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
mIsNavigating(false),
mSuspendMediaWhenInactive(false),
mForcedAutodetection(false),
mCheckingSessionHistory(false) {
mCheckingSessionHistory(false),
mNeedToReportActiveAfterLoadingBecomesActive(false) {
// If no outer window ID was provided, generate a new one.
if (aContentWindowID == 0) {
mContentWindowID = nsContentUtils::GenerateWindowId();
@ -1339,7 +1341,8 @@ void nsDocShell::FirePageHideShowNonRecursive(bool aShow) {
mLoadGroup->AddRequest(channel, nullptr);
SetCurrentURI(doc->GetDocumentURI(), channel,
/* aFireOnLocationChange */ true,
/* aIsInitialAboutBlank */ false, /* aLocationFlags */ 0);
/* aIsInitialAboutBlank */ false,
/* aLocationFlags */ 0);
mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
mIsRestoringDocument = false;
}
@ -1577,7 +1580,8 @@ nsDocShell::SetCurrentURI(nsIURI* aURI) {
// Note that securityUI will set STATE_IS_INSECURE, even if
// the scheme of |aURI| is "https".
SetCurrentURI(aURI, nullptr, /* aFireOnLocationChange */ true,
/* aIsInitialAboutBlank */ false, /* aLocationFlags */ 0);
/* aIsInitialAboutBlank */ false,
/* aLocationFlags */ 0);
return NS_OK;
}
@ -6743,7 +6747,8 @@ nsresult nsDocShell::CreateAboutBlankContentViewer(
SetCurrentURI(blankDoc->GetDocumentURI(), nullptr,
/* aFireLocationChange */ true,
/* aIsInitialAboutBlank */ true, /* aLocationFlags */ 0);
/* aIsInitialAboutBlank */ true,
/* aLocationFlags */ 0);
rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
}
}
@ -7561,7 +7566,8 @@ nsresult nsDocShell::RestoreFromHistory() {
// is still mLSHE or whether it's now mOSHE.
nsCOMPtr<nsIURI> uri = origLSHE->GetURI();
SetCurrentURI(uri, document->GetChannel(), /* aFireLocationChange */ true,
/* aIsInitialAboutBlank */ false, /* aLocationFlags */ 0);
/* aIsInitialAboutBlank */ false,
/* aLocationFlags */ 0);
}
// This is the end of our CreateContentViewer() replacement.
@ -8839,6 +8845,7 @@ nsresult nsDocShell::HandleSameDocumentNavigation(
if (aLoadState->GetLoadingSessionHistoryInfo()) {
mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(
*aLoadState->GetLoadingSessionHistoryInfo());
mNeedToReportActiveAfterLoadingBecomesActive = false;
}
// Set the doc's URI according to the new history entry's URI.
@ -10116,6 +10123,14 @@ nsIPrincipal* nsDocShell::GetInheritedPrincipal(
return true;
}
bool nsDocShell::IsAboutBlankLoadOntoInitialAboutBlank(
nsIURI* aURI, bool aInheritPrincipal, nsIPrincipal* aPrincipalToInherit) {
return NS_IsAboutBlank(aURI) && aInheritPrincipal &&
(aPrincipalToInherit == GetInheritedPrincipal(false)) &&
(!mContentViewer || !mContentViewer->GetDocument() ||
mContentViewer->GetDocument()->IsInitialDocument());
}
nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
Maybe<uint32_t> aCacheKey,
nsIRequest** aRequest) {
@ -10257,11 +10272,44 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
"DoURILoad thinks this is a document and InternalLoad does not");
}
// We want to inherit aLoadState->PrincipalToInherit() when:
// 1. ChannelShouldInheritPrincipal returns true.
// 2. aLoadState->URI() is not data: URI, or data: URI is not
// configured as unique opaque origin.
bool inheritPrincipal = false;
if (aLoadState->PrincipalToInherit()) {
bool isSrcdoc =
aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
aLoadState->PrincipalToInherit(), aLoadState->URI(),
true, // aInheritForAboutBlank
isSrcdoc);
inheritPrincipal = inheritAttrs && !SchemeIsData(aLoadState->URI());
}
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1736570
const bool isAboutBlankLoadOntoInitialAboutBlank =
IsAboutBlankLoadOntoInitialAboutBlank(aLoadState->URI(), inheritPrincipal,
aLoadState->PrincipalToInherit());
// FIXME We still have a ton of codepaths that don't pass through
// DocumentLoadListener, so probably need to create session history info
// in more places.
if (aLoadState->GetLoadingSessionHistoryInfo()) {
SetLoadingSessionHistoryInfo(*aLoadState->GetLoadingSessionHistoryInfo());
} else if (isAboutBlankLoadOntoInitialAboutBlank &&
mozilla::SessionHistoryInParent()) {
// Materialize LoadingSessionHistoryInfo here, because DocumentChannel
// loads have it, and later history behavior depends on it existing.
UniquePtr<SessionHistoryInfo> entry = MakeUnique<SessionHistoryInfo>(
aLoadState->URI(), aLoadState->TriggeringPrincipal(),
aLoadState->PrincipalToInherit(),
aLoadState->PartitionedPrincipalToInherit(), aLoadState->Csp(),
mContentTypeHint);
mozilla::dom::LoadingSessionHistoryInfo info(*entry);
SetLoadingSessionHistoryInfo(info, true);
}
// open a channel for the url
@ -10345,23 +10393,6 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
return NS_ERROR_FAILURE;
}
// We want to inherit aLoadState->PrincipalToInherit() when:
// 1. ChannelShouldInheritPrincipal returns true.
// 2. aLoadState->URI() is not data: URI, or data: URI is not
// configured as unique opaque origin.
bool inheritPrincipal = false;
if (aLoadState->PrincipalToInherit()) {
bool isSrcdoc =
aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
aLoadState->PrincipalToInherit(), aLoadState->URI(),
true, // aInheritForAboutBlank
isSrcdoc);
inheritPrincipal = inheritAttrs && !SchemeIsData(aLoadState->URI());
}
uint32_t sandboxFlags = mBrowsingContext->GetSandboxFlags();
nsSecurityFlags securityFlags =
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
@ -10394,6 +10425,30 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
sandboxFlags);
RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
if (isAboutBlankLoadOntoInitialAboutBlank) {
// Match the DocumentChannel case where the default for third-partiness
// differs from the default in LoadInfo construction here.
// toolkit/components/antitracking/test/browser/browser_aboutblank.js
// fails without this.
BrowsingContext* top = mBrowsingContext->Top();
if (top == mBrowsingContext) {
// If we're at the top, this must be a window.open()ed
// window, and we can't be third-party relative to ourselves.
loadInfo->SetIsThirdPartyContextToTopWindow(false);
} else {
if (Document* topDoc = top->GetDocument()) {
bool thirdParty = false;
mozilla::Unused << topDoc->GetPrincipal()->IsThirdPartyPrincipal(
aLoadState->PrincipalToInherit(), &thirdParty);
loadInfo->SetIsThirdPartyContextToTopWindow(thirdParty);
} else {
// If top is in a different process, we have to be third-party relative
// to it.
loadInfo->SetIsThirdPartyContextToTopWindow(true);
}
}
}
if (mLoadType != LOAD_ERROR_PAGE && context && context->IsInProcess() &&
context->HasValidTransientUserGestureActivation()) {
aLoadState->SetHasValidUserGestureActivation(true);
@ -10461,7 +10516,8 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
currentUnstrippedURI);
nsCOMPtr<nsIChannel> channel;
if (DocumentChannel::CanUseDocumentChannel(aLoadState->URI())) {
if (DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
!isAboutBlankLoadOntoInitialAboutBlank) {
channel = DocumentChannel::CreateForDocument(aLoadState, loadInfo,
loadFlags, this, cacheKey,
uriModified, isXFOError);
@ -13541,13 +13597,16 @@ bool nsDocShell::GetIsAttemptingToNavigate() {
}
void nsDocShell::SetLoadingSessionHistoryInfo(
const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo) {
const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo,
bool aNeedToReportActiveAfterLoadingBecomesActive) {
// FIXME Would like to assert this, but can't yet.
// MOZ_ASSERT(!mLoadingEntry);
MOZ_LOG(gSHLog, LogLevel::Debug,
("Setting the loading entry on nsDocShell %p to %s", this,
aLoadingInfo.mInfo.GetURI()->GetSpecOrDefault().get()));
mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(aLoadingInfo);
mNeedToReportActiveAfterLoadingBecomesActive =
aNeedToReportActiveAfterLoadingBecomesActive;
}
void nsDocShell::MoveLoadingToActiveEntry(bool aPersist, bool aExpired,
@ -13570,9 +13629,16 @@ void nsDocShell::MoveLoadingToActiveEntry(bool aPersist, bool aExpired,
mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
mLoadingEntry.swap(loadingEntry);
if (!mActiveEntryIsLoadingFromSessionHistory) {
if (mNeedToReportActiveAfterLoadingBecomesActive) {
// Needed to pass various history length WPTs.
mBrowsingContext->SetActiveSessionHistoryEntry(
mozilla::Nothing(), mActiveEntry.get(), mLoadType,
/* aUpdatedCacheKey = */ 0, false);
}
mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
}
}
mNeedToReportActiveAfterLoadingBecomesActive = false;
if (mActiveEntry) {
if (aCacheKey != 0) {
@ -13582,12 +13648,14 @@ void nsDocShell::MoveLoadingToActiveEntry(bool aPersist, bool aExpired,
uint32_t loadType =
mLoadType == LOAD_ERROR_PAGE ? mFailedLoadType : mLoadType;
// We're passing in mCurrentURI, which could be null. SessionHistoryCommit
// does require a non-null uri if this is for a refresh load of the same
// URI, but in that case mCurrentURI won't be null here.
mBrowsingContext->SessionHistoryCommit(*loadingEntry, loadType, mCurrentURI,
hadActiveEntry, aPersist, false,
aExpired, aCacheKey);
if (loadingEntry->mLoadId != UINT64_MAX) {
// We're passing in mCurrentURI, which could be null. SessionHistoryCommit
// does require a non-null uri if this is for a refresh load of the same
// URI, but in that case mCurrentURI won't be null here.
mBrowsingContext->SessionHistoryCommit(
*loadingEntry, loadType, mCurrentURI, hadActiveEntry, aPersist, false,
aExpired, aCacheKey);
}
}
}

Просмотреть файл

@ -501,7 +501,8 @@ class nsDocShell final : public nsDocLoader,
mozilla::dom::BrowsingContext* aBrowsingContext, uint32_t aLoadType);
void SetLoadingSessionHistoryInfo(
const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo);
const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo,
bool aNeedToReportActiveAfterLoadingBecomesActive = false);
const mozilla::dom::LoadingSessionHistoryInfo*
GetLoadingSessionHistoryInfo() {
return mLoadingEntry.get();
@ -675,6 +676,12 @@ class nsDocShell final : public nsDocLoader,
nsIURI* aCurrentURI, nsIReferrerInfo* aReferrerInfo,
bool aNotifiedBeforeUnloadListeners = false);
public:
bool IsAboutBlankLoadOntoInitialAboutBlank(nsIURI* aURI,
bool aInheritPrincipal,
nsIPrincipal* aPrincipalToInherit);
private:
//
// URI Load
//
@ -1213,7 +1220,9 @@ class nsDocShell final : public nsDocLoader,
// These are only set when fission.sessionHistoryInParent is set.
mozilla::UniquePtr<mozilla::dom::SessionHistoryInfo> mActiveEntry;
bool mActiveEntryIsLoadingFromSessionHistory = false;
// mLoadingEntry is set when we're about to start loading.
// mLoadingEntry is set when we're about to start loading. Whenever
// setting mLoadingEntry, be sure to also set
// mNeedToReportActiveAfterLoadingBecomesActive.
mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> mLoadingEntry;
// Holds a weak pointer to a RestorePresentationEvent object if any that
@ -1362,6 +1371,12 @@ class nsDocShell final : public nsDocLoader,
* a possible history load. Used only with iframes.
*/
bool mCheckingSessionHistory : 1;
// Whether mBrowsingContext->SetActiveSessionHistoryEntry() needs to be called
// when the loading entry becomes the active entry. This is used for the
// initial about:blank-replacing about:blank in order to make the history
// length WPTs pass.
bool mNeedToReportActiveAfterLoadingBecomesActive : 1;
};
inline nsISupports* ToSupports(nsDocShell* aDocShell) {

Просмотреть файл

@ -361,6 +361,10 @@ LoadingSessionHistoryInfo::LoadingSessionHistoryInfo(
SessionHistoryEntry::sLoadIdToEntry->Get(mLoadId) == aEntry);
}
LoadingSessionHistoryInfo::LoadingSessionHistoryInfo(
const SessionHistoryInfo& aInfo)
: mInfo(aInfo), mLoadId(UINT64_MAX) {}
already_AddRefed<nsDocShellLoadState>
LoadingSessionHistoryInfo::CreateLoadInfo() const {
RefPtr<nsDocShellLoadState> loadState(
@ -1215,7 +1219,7 @@ void SessionHistoryEntry::AddChild(SessionHistoryEntry* aChild, int32_t aOffset,
// This should ideally stop being an issue once the Fission-aware
// session history rewrite is complete.
NS_ASSERTION(
aUseRemoteSubframes,
aUseRemoteSubframes || NS_IsAboutBlank(oldChild->Info().GetURI()),
"Adding a child where we already have a child? This may misbehave");
oldChild->SetParent(nullptr);
}

Просмотреть файл

@ -218,6 +218,8 @@ struct LoadingSessionHistoryInfo {
// Initializes mInfo using aEntry and otherwise copies the values from aInfo.
LoadingSessionHistoryInfo(SessionHistoryEntry* aEntry,
LoadingSessionHistoryInfo* aInfo);
// For about:blank only.
explicit LoadingSessionHistoryInfo(const SessionHistoryInfo& aInfo);
already_AddRefed<nsDocShellLoadState> CreateLoadInfo() const;

Просмотреть файл

@ -1758,6 +1758,15 @@ nsresult nsObjectLoadingContent::CloseChannel() {
return NS_OK;
}
bool nsObjectLoadingContent::IsAboutBlankLoadOntoInitialAboutBlank(
nsIURI* aURI, bool aInheritPrincipal, nsIPrincipal* aPrincipalToInherit) {
return NS_IsAboutBlank(aURI) && aInheritPrincipal &&
(!mFrameLoader || !mFrameLoader->GetExistingDocShell() ||
mFrameLoader->GetExistingDocShell()
->IsAboutBlankLoadOntoInitialAboutBlank(aURI, aInheritPrincipal,
aPrincipalToInherit));
}
nsresult nsObjectLoadingContent::OpenChannel() {
nsCOMPtr<nsIContent> thisContent =
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
@ -1830,7 +1839,9 @@ nsresult nsObjectLoadingContent::OpenChannel() {
loadInfo->SetCSPToInherit(cspToInherit);
}
if (DocumentChannel::CanUseDocumentChannel(mURI)) {
if (DocumentChannel::CanUseDocumentChannel(mURI) &&
!IsAboutBlankLoadOntoInitialAboutBlank(mURI, inheritPrincipal,
thisContent->NodePrincipal())) {
// --- Create LoadState
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(mURI);
loadState->SetPrincipalToInherit(thisContent->NodePrincipal());

Просмотреть файл

@ -373,6 +373,12 @@ class nsObjectLoadingContent : public nsImageLoadingContent,
*/
void QueueCheckPluginStopEvent();
public:
bool IsAboutBlankLoadOntoInitialAboutBlank(nsIURI* aURI,
bool aInheritPrincipal,
nsIPrincipal* aPrincipalToInherit);
private:
/**
* Opens the channel pointed to by mURI into mChannel.
*/

Просмотреть файл

@ -153,8 +153,6 @@ nsDocShell* DocumentChannel::GetDocShell() {
return nsDocShell::Cast(docshell);
}
// Changes here should also be made in
// E10SUtils.documentChannelPermittedForURI().
static bool URIUsesDocChannel(nsIURI* aURI) {
if (SchemeIsJavascript(aURI)) {
return false;

Просмотреть файл

@ -10,10 +10,10 @@ async_test(t => {
i.src = iframe_url;
history.pushState("a", "", "#a");
assert_equals(starting_history_length + 1, history.length);
assert_equals(history.length, starting_history_length + 1, "First history length");
i.onload = t.step_func(() => {
assert_equals(starting_history_length + 1, history.length);
assert_equals(history.length, starting_history_length + 1, "Second history length");
assert_equals(i.contentWindow.location.href, iframe_url);
assert_equals(location.hash, "#a");
history.back();

Просмотреть файл

@ -0,0 +1,39 @@
<!doctype html>
<title>load event for empty iframe in relation to the event loop</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script>
setup({explicit_done:true});
let ran = false;
onload = function() {
let iframe = document.createElement("iframe");
iframe.onload = function() {
test(function() {
assert_equals(ran, false, 'Expected onload to run first');
}, "Check execution order on load handler");
if (ran) {
done();
} else {
ran = true;
}
};
document.body.appendChild(iframe);
// Nested timeout to accommodate Gecko, because the it seems
// the outer setTimeout takes its slot in the event queue right away
// but the load event task takes its slot only at the end of this script.
setTimeout(function() {
setTimeout(function() {
test(function() {
assert_equals(ran, true, 'Expected nested setTimeout to run second');
}, "Check execution order from nested timeout");
if (ran) {
done();
} else {
ran = true;
}
});
});
};
</script>

Просмотреть файл

@ -418,15 +418,16 @@ async function testAboutProcessesWithConfig({ showAllFrames, showThreads }) {
skipAnimation: true,
});
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
let p = BrowserTestUtils.browserLoaded(
tab.linkedBrowser,
true /* includeSubFrames */
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
// Open an in-process iframe to test toolkit.aboutProcesses.showAllSubframes
let frame = content.document.createElement("iframe");
content.document.body.appendChild(frame);
});
await BrowserTestUtils.browserLoaded(
tab.linkedBrowser,
true /* includeSubFrames */
);
await p;
return tab;
})();