Bug 444222 - Update the window.name when doing the navigation, r=smaug,nika

This patch implements the window.name updating in the spec
https://html.spec.whatwg.org/#history-traversal.

Differential Revision: https://phabricator.services.mozilla.com/D81361
This commit is contained in:
Andrea Marchesini 2020-09-15 17:51:05 +00:00
parent d332cd8f36
commit e733096393
12 changed files with 238 additions and 0 deletions

View File

@ -350,6 +350,18 @@ void CanonicalBrowsingContext::SessionHistoryCommit(uint64_t aLoadId,
newActiveEntry->SetForInitialLoad(false);
SessionHistoryEntry::RemoveLoadId(aLoadId);
mLoadingEntries.RemoveElementAt(i);
// If there is a name in the new entry, clear the name of all contiguous
// entries. This is for https://html.spec.whatwg.org/#history-traversal
// Step 4.4.2.
nsAutoString nameOfNewEntry;
newActiveEntry->GetName(nameOfNewEntry);
if (!nameOfNewEntry.IsEmpty()) {
nsSHistory::WalkContiguousEntries(
newActiveEntry,
[](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); });
}
if (IsTop()) {
mActiveEntry = newActiveEntry;
if (loadFromSessionHistory) {

View File

@ -2574,6 +2574,70 @@ void nsDocShell::MaybeClearStorageAccessFlag() {
}
}
void nsDocShell::MaybeRestoreWindowName() {
if (!StaticPrefs::privacy_window_name_update_enabled()) {
return;
}
// We only restore window.name for the top-level content.
if (!mBrowsingContext->IsTopContent()) {
return;
}
nsAutoString name;
// Following implements https://html.spec.whatwg.org/#history-traversal:
// Step 4.4. Check if the loading entry has a name.
if (mLSHE) {
mLSHE->GetName(name);
}
if (mLoadingEntry) {
name = mLoadingEntry->mInfo.GetName();
}
if (name.IsEmpty()) {
return;
}
// Step 4.4.1. Set the name to the browsing context.
Unused << mBrowsingContext->SetName(name);
// Step 4.4.2. Clear the name of all entries that are contiguous and
// same-origin with the loading entry.
if (mLSHE) {
nsSHistory::WalkContiguousEntries(
mLSHE, [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); });
}
if (mLoadingEntry) {
// Clear the name of the session entry in the child side. For parent side,
// the clearing will be done when we commit the history to the parent.
mLoadingEntry->mInfo.SetName(EmptyString());
}
}
void nsDocShell::StoreWindowNameToSHEntries() {
MOZ_ASSERT(mBrowsingContext->IsTopContent());
nsAutoString name;
mBrowsingContext->GetName(name);
if (mOSHE) {
nsSHistory::WalkContiguousEntries(
mOSHE, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
}
// Ask parent process to store the name in entries.
if (StaticPrefs::fission_sessionHistoryInParent()) {
mozilla::Unused
<< ContentChild::GetSingleton()
->SendSessionHistoryEntryStoreWindowNameInContiguousEntries(
mBrowsingContext, name);
}
}
NS_IMETHODIMP
nsDocShell::GetInProcessSameTypeParent(nsIDocShellTreeItem** aParent) {
if (BrowsingContext* parentBC = mBrowsingContext->GetParent()) {

View File

@ -409,6 +409,10 @@ class nsDocShell final : public nsDocLoader,
// Clear the document's storage access flag if needed.
void MaybeClearStorageAccessFlag();
void MaybeRestoreWindowName();
void StoreWindowNameToSHEntries();
void SetWillChangeProcess() { mWillChangeProcess = true; }
bool WillChangeProcess() { return mWillChangeProcess; }

View File

@ -536,6 +536,58 @@ nsresult nsSHistory::CloneAndReplace(
return rv;
}
// static
void nsSHistory::WalkContiguousEntries(
nsISHEntry* aEntry, const std::function<void(nsISHEntry*)>& aCallback) {
MOZ_ASSERT(aEntry);
nsCOMPtr<nsISHistory> shistory = aEntry->GetShistory();
if (!shistory) {
// If there is no session history in the entry, it means this is not a root
// entry. So, we can return from here.
return;
}
int32_t index = shistory->GetIndexOfEntry(aEntry);
int32_t count = shistory->GetCount();
nsCOMPtr<nsIURI> targetURI = aEntry->GetURI();
// First, call the callback on the input entry.
aCallback(aEntry);
// Walk backward to find the entries that have the same origin as the
// input entry.
for (int32_t i = index - 1; i >= 0; i--) {
RefPtr<nsISHEntry> entry;
shistory->GetEntryAtIndex(i, getter_AddRefs(entry));
if (entry) {
nsCOMPtr<nsIURI> uri = entry->GetURI();
if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
targetURI, uri, false, false))) {
break;
}
aCallback(entry);
}
}
// Then, Walk forward.
for (int32_t i = index + 1; i < count; i++) {
RefPtr<nsISHEntry> entry;
shistory->GetEntryAtIndex(i, getter_AddRefs(entry));
if (entry) {
nsCOMPtr<nsIURI> uri = entry->GetURI();
if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
targetURI, uri, false, false))) {
break;
}
aCallback(entry);
}
}
}
NS_IMETHODIMP
nsSHistory::AddChildSHEntryHelper(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry,
BrowsingContext* aRootBC,

View File

@ -127,6 +127,12 @@ class nsSHistory : public mozilla::LinkedListElement<nsSHistory>,
WalkHistoryEntriesFunc aCallback,
void* aData);
// This function finds all entries that are contiguous and same-origin with
// the aEntry. And call the aCallback on them, including the aEntry. This only
// works for the root entries. It will do nothing for non-root entries.
static void WalkContiguousEntries(
nsISHEntry* aEntry, const std::function<void(nsISHEntry*)>& aCallback);
nsTArray<nsCOMPtr<nsISHEntry>>& Entries() { return mEntries; }
void RemoveEntries(nsTArray<nsID>& aIDs, int32_t aStartIndex,

View File

@ -2110,6 +2110,8 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
}
}
MaybeResetWindowName(aDocument);
/* No mDocShell means we're already been partially closed down. When that
happens, setting status isn't a big requirement, so don't. (Doesn't happen
under normal circumstances, but bug 49615 describes a case.) */
@ -2130,6 +2132,8 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
// document.
mDoc = aDocument;
nsDocShell::Cast(mDocShell)->MaybeRestoreWindowName();
// We drop the print request for the old document on the floor, it never made
// it.
mShouldDelayPrintUntilAfterLoad = true;
@ -7748,6 +7752,63 @@ AbstractThread* nsGlobalWindowOuter::AbstractMainThreadFor(
return DispatcherTrait::AbstractMainThreadFor(aCategory);
}
void nsGlobalWindowOuter::MaybeResetWindowName(Document* aNewDocument) {
MOZ_ASSERT(aNewDocument);
if (!StaticPrefs::privacy_window_name_update_enabled()) {
return;
}
// We only reset the window name for the top-level content as well as storing
// in session entries.
if (!GetBrowsingContext()->IsTopContent()) {
return;
}
// Following implements https://html.spec.whatwg.org/#history-traversal:
// Step 4.2. Check if the loading document has a different origin than the
// previous document.
// We don't need to do anything if we haven't loaded a non-initial document.
if (!GetBrowsingContext()->GetHasLoadedNonInitialDocument()) {
return;
}
// If we have an existing doucment, directly check the document prinicpals
// with the new document to know if it is cross-origin.
//
// When running wpt, we could have an existing about:blank documnet which has
// a principal that is the same as the principal of the new document. But the
// new document doesn't load an about:blank page. In this case, we should
// treat them as cross-origin despite both doucments have same-origin
// principals. This only happens when Fission is enabled.
if (mDoc && mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal()) &&
(NS_IsAboutBlank(mDoc->GetDocumentURI()) ==
NS_IsAboutBlank(aNewDocument->GetDocumentURI()))) {
return;
}
// If we don't have an existing document, and if it's not the initial
// about:blank, we could be loading a document because of the
// process-switching. In this case, this should be a cross-origin navigation.
// Step 4.2.1 Store the window.name into all session history entries that have
// the same origin as the privious document.
nsDocShell::Cast(mDocShell)->StoreWindowNameToSHEntries();
// Step 4.2.2 Clear the window.name if the browsing context is the top-level
// content and doesn't have an opener.
// We need to reset the window name in case of a cross-origin navigation,
// without an opener.
RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext();
if (opener) {
return;
}
Unused << mBrowsingContext->SetName(EmptyString());
}
nsGlobalWindowOuter::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
nsGlobalWindowOuter* aWindow)
: mSavedDialogsEnabled(false) {

View File

@ -1046,6 +1046,8 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget,
bool IsOnlyTopLevelDocumentInSHistory();
void MaybeResetWindowName(Document* aNewDocument);
public:
bool DelayedPrintUntilAfterLoad() const {
return mDelayedPrintUntilAfterLoad;

View File

@ -6909,6 +6909,29 @@ ContentParent::RecvSessionHistoryEntryScrollRestorationIsManual(
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvSessionHistoryEntryStoreWindowNameInContiguousEntries(
const MaybeDiscarded<BrowsingContext>& aContext, const nsString& aName) {
if (aContext.IsNullOrDiscarded()) {
return IPC_OK();
}
// Per https://html.spec.whatwg.org/#history-traversal 4.2.1, we need to set
// the name to all contiguous entries. This has to be called before
// CanonicalBrowsingContext::SessionHistoryCommit(), so the active entry is
// still the old entry that we want to set.
SessionHistoryEntry* entry =
aContext.get_canonical()->GetActiveSessionHistoryEntry();
if (entry) {
nsSHistory::WalkContiguousEntries(
entry, [&](nsISHEntry* aEntry) { aEntry->SetName(aName); });
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryCacheKey(
const MaybeDiscarded<BrowsingContext>& aContext,
const uint32_t& aCacheKey) {

View File

@ -1338,6 +1338,10 @@ class ContentParent final
const MaybeDiscarded<BrowsingContext>& aContext,
const uint32_t& aCacheKey);
mozilla::ipc::IPCResult
RecvSessionHistoryEntryStoreWindowNameInContiguousEntries(
const MaybeDiscarded<BrowsingContext>& aContext, const nsString& aName);
mozilla::ipc::IPCResult RecvSetActiveSessionHistoryEntryForTop(
const MaybeDiscarded<BrowsingContext>& aContext,
const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo&& aInfo,

View File

@ -941,6 +941,9 @@ parent:
async SessionHistoryEntryCacheKey(MaybeDiscardedBrowsingContext aContext,
uint32_t aCacheKey);
async SessionHistoryEntryStoreWindowNameInContiguousEntries(MaybeDiscardedBrowsingContext aContext,
nsString aName);
async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
async CreateGMPService();

View File

@ -9003,6 +9003,11 @@
value: @IS_NIGHTLY_BUILD@
mirror: always
- name: privacy.window.name.update.enabled
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: always
# By default, the network state isolation is not active when there is a proxy
# setting. This pref forces the network isolation even in these scenarios.
- name: privacy.partition.network_state.connection_with_proxy

View File

@ -0,0 +1,2 @@
[clear-window-name.https.html]
prefs: [privacy.window.name.update.enabled:true]