mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-04 15:51:37 +00:00
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:
parent
d332cd8f36
commit
e733096393
@ -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) {
|
||||
|
@ -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()) {
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -1046,6 +1046,8 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget,
|
||||
|
||||
bool IsOnlyTopLevelDocumentInSHistory();
|
||||
|
||||
void MaybeResetWindowName(Document* aNewDocument);
|
||||
|
||||
public:
|
||||
bool DelayedPrintUntilAfterLoad() const {
|
||||
return mDelayedPrintUntilAfterLoad;
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,2 @@
|
||||
[clear-window-name.https.html]
|
||||
prefs: [privacy.window.name.update.enabled:true]
|
Loading…
x
Reference in New Issue
Block a user