mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 00:32:11 +00:00
Bug 1515073 - Part 1 - Add nsISHEntry::hasUserInteraction. r=peterv
Differential Revision: https://phabricator.services.mozilla.com/D27585
This commit is contained in:
parent
9b32c2661d
commit
2121e27531
@ -19,6 +19,16 @@ class WindowGlobalInit;
|
||||
class BrowsingContextGroup;
|
||||
|
||||
#define MOZ_EACH_WC_FIELD(FIELD) \
|
||||
/* Whether the SHEntry associated with the current top-level \
|
||||
* window has already seen user interaction. \
|
||||
* As such, this will be reset to false when a new SHEntry is \
|
||||
* created without changing the WC (e.g. when using pushState or \
|
||||
* sub-frame navigation) \
|
||||
* This flag is set for optimization purposes, to avoid \
|
||||
* having to get the top SHEntry and update it on every \
|
||||
* user interaction. \
|
||||
* This is only meaningful on the top-level WC. */ \
|
||||
FIELD(SHEntryHasUserInteraction, bool) \
|
||||
FIELD(CookieBehavior, Maybe<uint32_t>) \
|
||||
FIELD(IsOnContentBlockingAllowList, bool) \
|
||||
/* Whether the given window hierarchy is third party. See \
|
||||
@ -147,6 +157,10 @@ class WindowContext : public nsISupports, public nsWrapperCache {
|
||||
ContentParent* aSource);
|
||||
bool CanSet(FieldIndex<IDX_AutoplayPermission>, const uint32_t& aValue,
|
||||
ContentParent* aSource);
|
||||
bool CanSet(FieldIndex<IDX_SHEntryHasUserInteraction>,
|
||||
const bool& aSHEntryHasUserInteraction, ContentParent* aSource) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Overload `DidSet` to get notifications for a particular field being set.
|
||||
//
|
||||
|
@ -10750,8 +10750,24 @@ nsresult nsDocShell::AddToSessionHistory(
|
||||
if (loadedEntryIndex.isSome()) {
|
||||
mLoadedEntryIndex = loadedEntryIndex.value();
|
||||
}
|
||||
|
||||
// aCloneChildren implies that we are retaining the same document, thus we
|
||||
// need to signal to the top WC that the new SHEntry may receive a fresh
|
||||
// user interaction flag.
|
||||
if (aCloneChildren) {
|
||||
WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
|
||||
if (topWc) {
|
||||
topWc->SetSHEntryHasUserInteraction(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This is a subframe.
|
||||
|
||||
// This is a subframe, make sure that this new SHEntry will be
|
||||
// marked with user interaction.
|
||||
WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
|
||||
if (topWc) {
|
||||
topWc->SetSHEntryHasUserInteraction(false);
|
||||
}
|
||||
if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
|
||||
rv = AddChildSHEntryToParent(entry, mChildOffset, aCloneChildren);
|
||||
}
|
||||
|
@ -130,6 +130,18 @@ SessionHistoryEntry::SetIsSubFrame(bool aIsSubFrame) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SessionHistoryEntry::GetHasUserInteraction(bool* aFlag) {
|
||||
MOZ_CRASH("Not needed in the parent process?");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SessionHistoryEntry::SetHasUserInteraction(bool aFlag) {
|
||||
MOZ_CRASH("Not needed in the parent process?");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SessionHistoryEntry::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) {
|
||||
nsCOMPtr<nsIReferrerInfo> referrerInfo = mInfo->mReferrerInfo;
|
||||
|
@ -83,6 +83,14 @@ interface nsISHEntry : nsISupports
|
||||
*/
|
||||
[infallible] attribute boolean isSubFrame;
|
||||
|
||||
/**
|
||||
* Whether the user interacted with the page while this entry was active.
|
||||
* This includes interactions with subframe documents associated with
|
||||
* child entries that are rooted at this entry.
|
||||
* This field will only be set on top-level entries.
|
||||
*/
|
||||
[infallible] attribute boolean hasUserInteraction;
|
||||
|
||||
/** Referrer Info*/
|
||||
[infallible] attribute nsIReferrerInfo referrerInfo;
|
||||
|
||||
|
@ -44,7 +44,8 @@ nsSHEntry::nsSHEntry(nsISHistory* aSHistory)
|
||||
mIsSrcdocEntry(false),
|
||||
mScrollRestorationIsManual(false),
|
||||
mLoadedInThisProcess(false),
|
||||
mPersist(true) {}
|
||||
mPersist(true),
|
||||
mHasUserInteraction(false) {}
|
||||
|
||||
nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
|
||||
: mShared(aOther.mShared),
|
||||
@ -70,7 +71,8 @@ nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
|
||||
mIsSrcdocEntry(aOther.mIsSrcdocEntry),
|
||||
mScrollRestorationIsManual(false),
|
||||
mLoadedInThisProcess(aOther.mLoadedInThisProcess),
|
||||
mPersist(aOther.mPersist) {}
|
||||
mPersist(aOther.mPersist),
|
||||
mHasUserInteraction(false) {}
|
||||
|
||||
nsSHEntry::~nsSHEntry() {
|
||||
// Null out the mParent pointers on all our kids.
|
||||
@ -286,6 +288,31 @@ nsSHEntry::SetIsSubFrame(bool aFlag) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSHEntry::GetHasUserInteraction(bool* aFlag) {
|
||||
// We can't assert that this getter isn't accessed only on root
|
||||
// entries because there's JS code that will iterate over entries
|
||||
// for serialization etc., so let's assert the next best thing.
|
||||
MOZ_ASSERT(!mParent || !mHasUserInteraction,
|
||||
"User interaction can only be set on root entries");
|
||||
|
||||
*aFlag = mHasUserInteraction;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSHEntry::SetHasUserInteraction(bool aFlag) {
|
||||
// The back button and menulist deal with root/top-level
|
||||
// session history entries, thus we annotate only the root entry.
|
||||
if (!mParent) {
|
||||
mHasUserInteraction = aFlag;
|
||||
} else {
|
||||
nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(this);
|
||||
root->SetHasUserInteraction(aFlag);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSHEntry::GetCacheKey(uint32_t* aResult) {
|
||||
*aResult = mShared->mCacheKey;
|
||||
@ -360,6 +387,8 @@ nsSHEntry::Create(nsIURI* aURI, const nsAString& aTitle,
|
||||
// all subframe navigations, sets the flag to true.
|
||||
mShared->mIsFrameNavigation = false;
|
||||
|
||||
mHasUserInteraction = false;
|
||||
|
||||
mShared->mExpired = aExpired;
|
||||
|
||||
mIsSrcdocEntry = aSrcdocEntry;
|
||||
|
@ -63,6 +63,7 @@ class nsSHEntry : public nsISHEntry {
|
||||
bool mScrollRestorationIsManual;
|
||||
bool mLoadedInThisProcess;
|
||||
bool mPersist;
|
||||
bool mHasUserInteraction;
|
||||
};
|
||||
|
||||
#endif /* nsSHEntry_h */
|
||||
|
@ -523,6 +523,7 @@ nsresult nsSHistory::AddChildSHEntryHelper(nsISHEntry* aCloneRef,
|
||||
uint32_t cloneID = aCloneRef->GetID();
|
||||
rv = nsSHistory::CloneAndReplace(currentHE, aBC, cloneID, aNewEntry,
|
||||
aCloneChildren, aNextEntry);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = AddEntry(*aNextEntry, true);
|
||||
}
|
||||
|
@ -93,6 +93,8 @@
|
||||
#include "nsINSSErrorsService.h"
|
||||
#include "nsISocketProvider.h"
|
||||
#include "nsISiteSecurityService.h"
|
||||
#include "nsISHEntry.h"
|
||||
#include "nsSHistory.h"
|
||||
#include "mozilla/PermissionDelegateHandler.h"
|
||||
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
@ -15061,6 +15063,36 @@ void Document::ReportHasScrollLinkedEffect() {
|
||||
nsContentUtils::eLAYOUT_PROPERTIES, "ScrollLinkedEffectFound2");
|
||||
}
|
||||
|
||||
void Document::SetSHEntryHasUserInteraction(bool aHasInteraction) {
|
||||
nsPIDOMWindowInner* inner = GetInnerWindow();
|
||||
if (NS_WARN_IF(!inner)) {
|
||||
return;
|
||||
}
|
||||
|
||||
WindowContext* wc = inner->GetWindowContext();
|
||||
if (NS_WARN_IF(!wc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
WindowContext* topWc = wc->TopWindowContext();
|
||||
topWc->SetSHEntryHasUserInteraction(aHasInteraction);
|
||||
}
|
||||
|
||||
bool Document::GetSHEntryHasUserInteraction() {
|
||||
nsPIDOMWindowInner* inner = GetInnerWindow();
|
||||
if (NS_WARN_IF(!inner)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WindowContext* wc = inner->GetWindowContext();
|
||||
if (NS_WARN_IF(!wc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WindowContext* topWc = wc->TopWindowContext();
|
||||
return topWc->GetSHEntryHasUserInteraction();
|
||||
}
|
||||
|
||||
void Document::SetUserHasInteracted() {
|
||||
MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
|
||||
("Document %p has been interacted by user.", this));
|
||||
@ -15068,6 +15100,27 @@ void Document::SetUserHasInteracted() {
|
||||
// We maybe need to update the user-interaction permission.
|
||||
MaybeStoreUserInteractionAsPermission();
|
||||
|
||||
// For purposes of reducing irrelevant session history entries on
|
||||
// the back button, we annotate entries with whether they had user
|
||||
// interaction. This is gated on its own flag on the WindowContext
|
||||
// (instead of mUserHasInteracted) to account for the fact that multiple
|
||||
// top-level SH entries can be associated with the same document.
|
||||
// Thus, whenever we create a new SH entry for this document,
|
||||
// this flag is reset.
|
||||
if (!GetSHEntryHasUserInteraction()) {
|
||||
nsIDocShell* docShell = this->GetDocShell();
|
||||
if (docShell) {
|
||||
nsCOMPtr<nsISHEntry> currentEntry;
|
||||
bool oshe;
|
||||
nsresult rv =
|
||||
docShell->GetCurrentSHEntry(getter_AddRefs(currentEntry), &oshe);
|
||||
if (!NS_WARN_IF(NS_FAILED(rv)) && currentEntry) {
|
||||
currentEntry->SetHasUserInteraction(true);
|
||||
}
|
||||
}
|
||||
SetSHEntryHasUserInteraction(true);
|
||||
}
|
||||
|
||||
if (mUserHasInteracted) {
|
||||
return;
|
||||
}
|
||||
|
@ -3742,12 +3742,15 @@ class Document : public nsINode,
|
||||
private:
|
||||
void DoCacheAllKnownLangPrefs();
|
||||
void RecomputeLanguageFromCharset();
|
||||
bool GetSHEntryHasUserInteraction();
|
||||
|
||||
public:
|
||||
void SetMayNeedFontPrefsUpdate() { mMayNeedFontPrefsUpdate = true; }
|
||||
|
||||
bool MayNeedFontPrefsUpdate() { return mMayNeedFontPrefsUpdate; }
|
||||
|
||||
void SetSHEntryHasUserInteraction(bool aHasInteraction);
|
||||
|
||||
already_AddRefed<nsAtom> GetContentLanguageAsAtomForStyle() const;
|
||||
already_AddRefed<nsAtom> GetLanguageForStyle() const;
|
||||
|
||||
|
@ -276,6 +276,8 @@ var SessionHistoryInternal = {
|
||||
);
|
||||
}
|
||||
|
||||
entry.hasUserInteraction = shEntry.hasUserInteraction;
|
||||
|
||||
if (shEntry.triggeringPrincipal) {
|
||||
entry.triggeringPrincipal_base64 = E10SUtils.serializePrincipal(
|
||||
shEntry.triggeringPrincipal
|
||||
@ -375,10 +377,21 @@ var SessionHistoryInternal = {
|
||||
continue;
|
||||
}
|
||||
let persist = "persist" in entry ? entry.persist : true;
|
||||
history.addEntry(
|
||||
this.deserializeEntry(entry, idMap, docIdentMap, history),
|
||||
persist
|
||||
);
|
||||
let shEntry = this.deserializeEntry(entry, idMap, docIdentMap, history);
|
||||
|
||||
// To enable a smooth migration, we treat values of null/undefined as having
|
||||
// user interaction (because we don't want to hide all session history that was
|
||||
// added before we started recording user interaction).
|
||||
//
|
||||
// This attribute is only set on top-level SH history entries, so we set it
|
||||
// outside of deserializeEntry since that is called recursively.
|
||||
if (entry.hasUserInteraction == undefined) {
|
||||
shEntry.hasUserInteraction = true;
|
||||
} else {
|
||||
shEntry.hasUserInteraction = entry.hasUserInteraction;
|
||||
}
|
||||
|
||||
history.addEntry(shEntry, persist);
|
||||
}
|
||||
|
||||
// Select the right history entry.
|
||||
|
Loading…
Reference in New Issue
Block a user