Bug 1515073 - Part 2 - Allow nsIWebNavigation::{goBack,goForward} to skip entries without user interaction. r=Gijs,peterv

Depends on D27585

Differential Revision: https://phabricator.services.mozilla.com/D27586
This commit is contained in:
Johann Hofmann 2020-06-09 14:50:14 +00:00
parent 2121e27531
commit 2133bb8e2c
13 changed files with 112 additions and 41 deletions

View File

@ -3076,7 +3076,7 @@ nsDocShell::GetCanGoForward(bool* aCanGoForward) {
}
NS_IMETHODIMP
nsDocShell::GoBack() {
nsDocShell::GoBack(bool aRequireUserInteraction) {
if (!IsNavigationAllowed()) {
return NS_OK; // JS may not handle returning of an error code
}
@ -3087,12 +3087,12 @@ nsDocShell::GoBack() {
RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
ErrorResult rv;
rootSH->Go(-1, rv);
rootSH->Go(-1, aRequireUserInteraction, rv);
return rv.StealNSResult();
}
NS_IMETHODIMP
nsDocShell::GoForward() {
nsDocShell::GoForward(bool aRequireUserInteraction) {
if (!IsNavigationAllowed()) {
return NS_OK; // JS may not handle returning of an error code
}
@ -3103,7 +3103,7 @@ nsDocShell::GoForward() {
RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
ErrorResult rv;
rootSH->Go(1, rv);
rootSH->Go(1, aRequireUserInteraction, rv);
return rv.StealNSResult();
}

View File

@ -50,11 +50,19 @@ interface nsIWebNavigation : nsISupports
* (if available) and page state (such as form values and scroll position) is
* restored.
*
* @param {boolean} aRequireUserInteraction
* Tells goBack to skip history items that did not record any user
* interaction on their corresponding document while they were active.
* This means in case of multiple entries mapping to the same document,
* each entry has to have been flagged with user interaction separately.
* If no items have user interaction, the function will fall back
* to the first session history entry.
*
* @throw NS_ERROR_UNEXPECTED
* Indicates that the call was unexpected at this time, which implies
* that canGoBack is false.
*/
void goBack();
void goBack([optional] in boolean aRequireUserInteraction);
/**
* Tells the object to navigate to the next session history item. When a
@ -62,11 +70,19 @@ interface nsIWebNavigation : nsISupports
* (if available) and page state (such as form values and scroll position) is
* restored.
*
* @param {boolean} aRequireUserInteraction
* Tells goForward to skip history items that did not record any user
* interaction on their corresponding document while they were active.
* This means in case of multiple entries mapping to the same document,
* each entry has to have been flagged with user interaction separately.
* If no items have user interaction, the function will fall back
* to the latest session history entry.
*
* @throw NS_ERROR_UNEXPECTED
* Indicates that the call was unexpected at this time, which implies
* that canGoForward is false.
*/
void goForward();
void goForward([optional] in boolean aRequireUserInteraction);
/**
* Tells the object to navigate to the session history item at a given index.

View File

@ -61,13 +61,35 @@ bool ChildSHistory::CanGo(int32_t aOffset) {
return index.value() < Count() && index.value() >= 0;
}
void ChildSHistory::Go(int32_t aOffset, ErrorResult& aRv) {
CheckedInt<int32_t> index = Index();
index += aOffset;
if (!index.isValid()) {
aRv.Throw(NS_ERROR_FAILURE);
void ChildSHistory::Go(int32_t aOffset, bool aRequireUserInteraction,
ErrorResult& aRv) {
if (aRequireUserInteraction && aOffset != -1 && aOffset != 1) {
NS_ERROR(
"aRequireUserInteraction may only be used with an offset of -1 or 1");
aRv.Throw(NS_ERROR_INVALID_ARG);
return;
}
CheckedInt<int32_t> index = Index();
while (true) {
index += aOffset;
if (!index.isValid()) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
// Check for user interaction if desired, except for the first and last
// history entries. We compare with >= to account for the case where
// aOffset >= Count().
if (!aRequireUserInteraction || index.value() >= Count() - 1 ||
index.value() <= 0) {
break;
}
if (mHistory->HasUserInteractionAtIndex(index.value())) {
break;
}
}
if (StaticPrefs::fission_sessionHistoryInParent()) {
nsCOMPtr<nsISHistory> shistory = mHistory;
ContentChild::GetSingleton()->SendHistoryGo(
@ -83,13 +105,13 @@ void ChildSHistory::Go(int32_t aOffset, ErrorResult& aRv) {
}
}
void ChildSHistory::AsyncGo(int32_t aOffset) {
void ChildSHistory::AsyncGo(int32_t aOffset, bool aRequireUserInteraction) {
if (!CanGo(aOffset)) {
return;
}
RefPtr<PendingAsyncHistoryNavigation> asyncNav =
new PendingAsyncHistoryNavigation(this, aOffset);
new PendingAsyncHistoryNavigation(this, aOffset, aRequireUserInteraction);
mPendingNavigations.insertBack(asyncNav);
NS_DispatchToCurrentThread(asyncNav.forget());
}

View File

@ -63,8 +63,8 @@ class ChildSHistory : public nsISupports, public nsWrapperCache {
* backwards.
*/
bool CanGo(int32_t aOffset);
void Go(int32_t aOffset, ErrorResult& aRv);
void AsyncGo(int32_t aOffset);
void Go(int32_t aOffset, bool aRequireUserInteraction, ErrorResult& aRv);
void AsyncGo(int32_t aOffset, bool aRequireUserInteraction);
void RemovePendingHistoryNavigations();
@ -84,21 +84,24 @@ class ChildSHistory : public nsISupports, public nsWrapperCache {
: public Runnable,
public mozilla::LinkedListElement<PendingAsyncHistoryNavigation> {
public:
PendingAsyncHistoryNavigation(ChildSHistory* aHistory, int32_t aOffset)
PendingAsyncHistoryNavigation(ChildSHistory* aHistory, int32_t aOffset,
bool aRequireUserInteraction)
: Runnable("PendingAsyncHistoryNavigation"),
mHistory(aHistory),
mRequireUserInteraction(aRequireUserInteraction),
mOffset(aOffset) {}
NS_IMETHOD Run() override {
if (isInList()) {
remove();
mHistory->Go(mOffset, IgnoreErrors());
mHistory->Go(mOffset, mRequireUserInteraction, IgnoreErrors());
}
return NS_OK;
}
private:
RefPtr<ChildSHistory> mHistory;
bool mRequireUserInteraction;
int32_t mOffset;
};

View File

@ -126,6 +126,13 @@ interface nsISHistory: nsISupports
[noscript]
void gotoIndex(in long aIndex);
/**
* If an element exists at the particular index and
* whether it has user interaction.
*/
[noscript,notxpcom]
boolean hasUserInteractionAtIndex(in long aIndex);
/**
* Called to obtain the index to a given history entry.
*

View File

@ -1505,6 +1505,16 @@ nsresult nsSHistory::GotoIndex(int32_t aIndex,
return LoadEntry(aIndex, LOAD_HISTORY, HIST_CMD_GOTOINDEX, aLoadResults);
}
NS_IMETHODIMP_(bool)
nsSHistory::HasUserInteractionAtIndex(int32_t aIndex) {
nsCOMPtr<nsISHEntry> entry;
GetEntryAtIndex(aIndex, getter_AddRefs(entry));
if (!entry) {
return false;
}
return entry->GetHasUserInteraction();
}
nsresult nsSHistory::LoadNextPossibleEntry(
int32_t aNewIndex, long aLoadType, uint32_t aHistCmd,
nsTArray<LoadEntryResult>& aLoadResults) {

View File

@ -158,9 +158,9 @@ void nsHistory::Go(int32_t aDelta, ErrorResult& aRv) {
// Ignore the return value from Go(), since returning errors from Go() can
// lead to exceptions and a possible leak of history length
if (StaticPrefs::dom_window_history_async()) {
session_history->AsyncGo(aDelta);
session_history->AsyncGo(aDelta, /* aRequireUserInteraction = */ false);
} else {
session_history->Go(aDelta, IgnoreErrors());
session_history->Go(aDelta, /* aRequireUserInteraction = */ false, IgnoreErrors());
}
}
@ -180,9 +180,9 @@ void nsHistory::Back(ErrorResult& aRv) {
}
if (StaticPrefs::dom_window_history_async()) {
sHistory->AsyncGo(-1);
sHistory->AsyncGo(-1, /* aRequireUserInteraction = */ false);
} else {
sHistory->Go(-1, IgnoreErrors());
sHistory->Go(-1, /* aRequireUserInteraction = */ false, IgnoreErrors());
}
}
@ -202,9 +202,9 @@ void nsHistory::Forward(ErrorResult& aRv) {
}
if (StaticPrefs::dom_window_history_async()) {
sHistory->AsyncGo(1);
sHistory->AsyncGo(1, /* aRequireUserInteraction = */ false);
} else {
sHistory->Go(1, IgnoreErrors());
sHistory->Go(1, /* aRequireUserInteraction = */ false, IgnoreErrors());
}
}

View File

@ -2228,10 +2228,12 @@ void EventStateManager::DoScrollHistory(int32_t direction) {
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(pcContainer));
if (webNav) {
// positive direction to go back one step, nonpositive to go forward
// This is doing user-initiated history traversal, hence we want
// to require that history entries we navigate to have user interaction.
if (direction > 0)
webNav->GoBack();
webNav->GoBack(/* aRequireUserInteraction = */ true);
else
webNav->GoForward();
webNav->GoForward(/* aRequireUserInteraction = */ true);
}
}
}

View File

@ -19,8 +19,7 @@ interface ChildSHistory {
readonly attribute long index;
boolean canGo(long aOffset);
[Throws]
void go(long aOffset);
[Throws] void go(long aOffset, optional boolean aRequireUserInteraction = false);
/**
* Reload the current entry. The flags which should be passed to this

View File

@ -46,7 +46,7 @@ class WebNavigationChild extends JSWindowActorChild {
let wn = this.webNavigation;
if (wn.canGoBack) {
this.docShell.setCancelContentJSEpoch(params.cancelContentJSEpoch);
this._wrapURIChangeCall(() => wn.goBack());
this._wrapURIChangeCall(() => wn.goBack(params.requireUserInteraction));
}
}
@ -54,7 +54,9 @@ class WebNavigationChild extends JSWindowActorChild {
let wn = this.webNavigation;
if (wn.canGoForward) {
this.docShell.setCancelContentJSEpoch(params.cancelContentJSEpoch);
this._wrapURIChangeCall(() => wn.goForward());
this._wrapURIChangeCall(() =>
wn.goForward(params.requireUserInteraction)
);
}
}

View File

@ -465,17 +465,17 @@ nsWebBrowser::GetCanGoForward(bool* aCanGoForward) {
}
NS_IMETHODIMP
nsWebBrowser::GoBack() {
nsWebBrowser::GoBack(bool aRequireUserInteraction) {
NS_ENSURE_STATE(mDocShell);
return mDocShellAsNav->GoBack();
return mDocShellAsNav->GoBack(aRequireUserInteraction);
}
NS_IMETHODIMP
nsWebBrowser::GoForward() {
nsWebBrowser::GoForward(bool aRequireUserInteraction) {
NS_ENSURE_STATE(mDocShell);
return mDocShellAsNav->GoForward();
return mDocShellAsNav->GoForward(aRequireUserInteraction);
}
nsresult nsWebBrowser::LoadURI(const nsAString& aURI,

View File

@ -38,17 +38,23 @@ class RemoteWebNavigation {
return epoch;
}
goBack() {
goBack(requireUserInteraction = false) {
let cancelContentJSEpoch = this.maybeCancelContentJSExecution(
Ci.nsIRemoteTab.NAVIGATE_BACK
);
this._sendMessage("WebNavigation:GoBack", { cancelContentJSEpoch });
this._sendMessage("WebNavigation:GoBack", {
cancelContentJSEpoch,
requireUserInteraction,
});
}
goForward() {
goForward(requireUserInteraction = false) {
let cancelContentJSEpoch = this.maybeCancelContentJSExecution(
Ci.nsIRemoteTab.NAVIGATE_FORWARD
);
this._sendMessage("WebNavigation:GoForward", { cancelContentJSEpoch });
this._sendMessage("WebNavigation:GoForward", {
cancelContentJSEpoch,
requireUserInteraction,
});
}
gotoIndex(aIndex) {
let cancelContentJSEpoch = this.maybeCancelContentJSExecution(

View File

@ -855,17 +855,21 @@
}
}
goBack() {
goBack(requireUserInteraction = false) {
var webNavigation = this.webNavigation;
if (webNavigation.canGoBack) {
this._wrapURIChangeCall(() => webNavigation.goBack());
this._wrapURIChangeCall(() =>
webNavigation.goBack(requireUserInteraction)
);
}
}
goForward() {
goForward(requireUserInteraction = false) {
var webNavigation = this.webNavigation;
if (webNavigation.canGoForward) {
this._wrapURIChangeCall(() => webNavigation.goForward());
this._wrapURIChangeCall(() =>
webNavigation.goForward(requireUserInteraction)
);
}
}