1. The Navigation
class
partial interface Window { [Replaceable ]readonly attribute Navigation navigation ; };
Each Window
object has an associated navigation API, which is a new Navigation
instance created alongside the Window
.
The navigation
getter steps are to return this's navigation API.
[Exposed =Window ]interface :
Navigation EventTarget {sequence <NavigationHistoryEntry >entries ();readonly attribute NavigationHistoryEntry ?currentEntry ;undefined updateCurrentEntry (NavigationUpdateCurrentEntryOptions );
options readonly attribute NavigationTransition ?transition ;readonly attribute boolean canGoBack ;readonly attribute boolean canGoForward ;NavigationResult navigate (USVString ,
url optional NavigationNavigateOptions = {});
options NavigationResult reload (optional NavigationReloadOptions = {});
options NavigationResult traverseTo (DOMString ,
key optional NavigationOptions = {});
options NavigationResult back (optional NavigationOptions = {});
options NavigationResult forward (optional NavigationOptions = {});
options attribute EventHandler onnavigate ;attribute EventHandler onnavigatesuccess ;attribute EventHandler onnavigateerror ;attribute EventHandler oncurrententrychange ; };dictionary {
NavigationUpdateCurrentEntryOptions required any ; };
state dictionary {
NavigationOptions any ; };
info dictionary :
NavigationNavigateOptions NavigationOptions {any ;
state NavigationHistoryBehavior = "auto"; };
history dictionary :
NavigationReloadOptions NavigationOptions {any ; };
state dictionary {
NavigationResult Promise <NavigationHistoryEntry >;
committed Promise <NavigationHistoryEntry >; };
finished enum {
NavigationHistoryBehavior ,
"auto" ,
"push" };
"replace"
Each Navigation
object has an associated entry list, a list of NavigationHistoryEntry
objects, initially empty.
Each Navigation
object has an associated current entry index, an integer, initially −1.
Navigation
navigation has entries and events disabled if the following steps return true:
-
If navigation’s relevant global object's browsing context is null, then return true.
-
Let document be navigation’s relevant global object's associated Document.
-
If document’s is initial about:blank is true, then return true.
-
Return false.
Navigation
instance navigation, a session history entry destinationSHE, and a NavigationType
navigationType:
-
If navigation has entries and events disabled, then:
-
Assert: navigation’s entry list is empty.
-
Return.
-
-
Let oldCurrentNHE be the current entry of navigation.
-
Let disposedNHEs be a new empty list.
-
If navigationType is "
traverse
":-
Set navigation’s current entry index to the index of the
NavigationHistoryEntry
within navigation’s entry list whose session history entry's navigation API key equals destinationSHE’s navigation API key. -
Assert: such a
NavigationHistoryEntry
must exist, because this algorithm is only called for same-document traversals. (Cross-document traversals will instead call either update the entries for reactivation or initialize the entries for a new Navigation.)
-
-
Otherwise, if navigationType is "
push
":-
Set navigation’s current entry index to navigation’s current entry index + 1.
-
Let i be navigation’s current entry index.
-
While i < navigation’s entry list's size:
-
Append navigation’s entry list[i] to disposedNHEs.
-
Set i to i + 1.
-
-
Remove all items in disposedNHEs from navigation’s entry list.
-
-
Otherwise, if navigationType is "
replace
":-
Append oldCurrentNHE to disposedNHEs.
-
-
If navigationType is "
push
" or "replace
":-
Let newNHE be a new
NavigationHistoryEntry
created in the relevant realm of navigation. -
Set newNHE’s session history entry to destinationSHE.
-
Set navigation’s entry list[navigation’s current entry index] to newNHE.
-
-
Assert: by this point, the current entry of navigation is different from oldCurrentNHE.
-
If navigation’s ongoing navigation is non-null, then notify about the committed-to entry given navigation’s ongoing navigation and the current entry of navigation.
It is important to do this before firing the
dispose
orcurrententrychange
events, since event handlers could start another navigation, or otherwise change the value of navigation’s ongoing navigation. -
Prepare to run script given navigation’s relevant settings object.
See a similar note elsewhere for other navigation API events to understand why we do this.
-
Fire an event named
currententrychange
at navigation usingNavigationCurrentEntryChangeEvent
, with itsnavigationType
attribute initialized to navigationType and itsfrom
initialized to oldCurrentNHE. -
For each disposedNHE of disposedNHEs:
-
Fire an event named
dispose
at disposedNHE.
-
-
Clean up after running script given navigation’s relevant settings object.
Navigation
instance navigation, a list of session history entries newSHEs, and a session history entry reactivatedSHE:
-
Let newNHEs be an empty list.
-
Let oldNHEs be a clone of navigation’s entry list.
-
For each newSHE of newSHEs:
-
Let newNHE be null.
-
If oldNHEs contains a
NavigationHistoryEntry
matchingOldNHE whose session history entry is newSHE, then:-
Set newNHE to matchingOldNHE.
-
Remove matchingOldNHE from oldNHEs.
-
-
Otherwise:
-
Set newNHE to a new
NavigationHistoryEntry
created in the relevant realm of navigation. -
Set newNHE’s session history entry to newSHE.
-
-
Append newNHE to newNHEs.
-
-
For each disposedNHE of oldNHEs:
-
Set disposedNHE’s index to −1.
-
-
Set navigation’s entry list to newNHEs.
-
Set navigation’s current entry index to the result of getting the navigation API history index of reactivatedSHE within navigation.
-
Queue a global task on the navigation and traversal task source given navigation’s relevant global object to run the following steps:
-
For each disposedNHE of oldNHEs:
-
Fire an event named
dispose
at disposedNHE.
-
We delay these events by a task to ensure that
dispose
events will fire after thepageshow
event. (However, the rest of this algorithm runs before thepageshow
event fires, to ensure thatnavigation.entries()
andnavigation.currentEntry
will have correctly-updated values during anypageshow
event handlers.) This is motivated by a desire to havepageshow
be the first event a page receives upon reactivation. -
Navigation
given a Navigation
instance navigation, a list of session history entries newSHEs, and a session history entry initialSHE:
-
Assert: navigation’s entry list is empty.
-
Assert: navigation’s current entry index is −1.
-
If navigation has entries and events disabled, then return.
-
For each newSHE of newSHEs:
-
Let newNHE be a new
NavigationHistoryEntry
created in the relevant realm of navigation. -
Set newNHE’s session history entry to newSHE.
-
Append newNHE to navigation’s entry list.
-
-
Set navigation’s current entry index to the result of getting the navigation API history index of initialSHE within navigation.
Navigation
navigation:
-
Let index be 0.
-
For each ahe of navigation’s entry list:
-
If ahe’s session history entry is equal to she, then return index.
-
Increment index by 1.
-
-
Assert: this step is never reached.
1.1. Introspecting the navigation API history entry list
entries =
navigation
.entries
()-
Returns an array of
NavigationHistoryEntry
instances representing the current navigation history entry list, i.e. all session history entries for thisWindow
that are same origin and contiguous to the current session history entry. navigation
.canGoBack
-
Returns true if the current
NavigationHistoryEntry
is not the first one in the navigation history entry list. navigation
.canGoForward
-
Returns true if the current
NavigationHistoryEntry
is not the last one in the navigation history entry list.
entries()
method steps are:
-
If this has entries and events disabled, then return the empty list.
-
Return this's entries list.
canGoBack
getter steps are:
-
If this has entries and events disabled, then return false.
-
Assert: this's current entry index is not −1.
-
If this's current entry index is 0, then return false.
-
Return true.
canGoForward
getter steps are:
-
If this has entries and events disabled, then return false.
-
Assert: this's current entry index is not −1.
-
If this's current entry index is equal to this's entry list's size − 1, then return false.
-
Return true.
1.2. The current entry
[Exposed =Window ]interface :
NavigationCurrentEntryChangeEvent Event {(
constructor DOMString ,
type NavigationCurrentEntryChangeEventInit );
eventInit readonly attribute NavigationType ?;
navigationType readonly attribute NavigationHistoryEntry ; };
from dictionary :
NavigationCurrentEntryChangeEventInit EventInit {NavigationType ?=
navigationType null ;required NavigationHistoryEntry ; };
destination
navigation
.currentEntry
-
The current
NavigationHistoryEntry
. navigation
.updateCurrentEntry
({state
})-
Update the navigation API state of the current
NavigationHistoryEntry
, without performing a navigation likenavigation.reload()
would do.This method is best used to capture updates to the page that have already happened, and need to be reflected into the navigation API state. For cases where the state update is meant to drive a page update, instead use
navigation.navigate()
ornavigation.reload()
.
Navigation
navigation is the result running of the following algorithm:
-
If navigation has entries and events disabled, then return null.
-
Assert: navigation’s current entry index is not −1.
-
Return navigation’s entry list[navigation’s current entry index].
The currentEntry
getter steps are to return the current entry for this.
updateCurrentEntry(options)
method steps are:
-
Let current be the current entry for this.
-
If current is null, then throw an "
InvalidStateError
"DOMException
. -
Let serializedState be StructuredSerializeForStorage(options["
state
"]), rethrowing any exceptions. -
Set current’s session history entry's navigation API state to serializedState.
-
Fire an event named
currententrychange
at this usingNavigationCurrentEntryChangeEvent
, with itsnavigationType
attribute initialized to null and itsfrom
initialized to current.
1.3. Ongoing navigation tracking
[Exposed =Window ]interface {
NavigationTransition readonly attribute NavigationType navigationType ;readonly attribute NavigationHistoryEntry from ;readonly attribute Promise <undefined >finished ; };
navigation
.transition
-
A
NavigationTransition
object representing any ongoing navigation that hasn’t yet reached thenavigatesuccess
ornavigateerror
stage, if one exists, or null if there is no such transition ongoing.Since
navigation.currentEntry
(and other properties likelocation.href
) are updated immediately upon navigation, thisnavigation.transition
property is useful for determining when such navigations are not yet fully settled, according to any handlers passed toevent.intercept()
. navigation
.transition
.navigationType
-
One of "
reload
", "push
", "replace
", or "traverse
", indicating what type of navigation this transition is for. navigation
.transition
.from
-
The
NavigationHistoryEntry
from which the transition is coming. This can be useful to compare againstnavigation.currentEntry
. navigation
.transition
.finished
-
A promise which fulfills at the same time the
navigatesuccess
event fires, or rejects at the same time thenavigateerror
fires.
A Navigation
has a transition, which is a NavigationTransition
or null.
The transition
getter steps are to return this's transition.
A NavigationTransition
has an associated navigation type, which is a NavigationType
.
A NavigationTransition
has an associated from entry, which is a NavigationHistoryEntry
.
A NavigationTransition
has an associated finished promise, which is an Promise
.
The navigationType
getter steps are to return this's navigation type.
The from
getter steps are to return this's from entry.
The finished
getter steps are to return this's finished promise.
During any given navigation, the Navigation
object needs to keep track of the following:
State | Duration | Explanation |
---|---|---|
The NavigateEvent
| For the duration of event firing | So that if the navigation is canceled while the event is firing, we can cancel the event. |
The event’s signal
| Until all promises returned from handlers passed to intercept() have settled
| So that if the navigation is canceled, we can signal abort. |
Whether a new element was focused | Until all promises returned from handlers passed to intercept() have settled
| So that if one was, focus is not reset |
The NavigationHistoryEntry being navigated to
| From when it is determined, until all promises returned from handlers passed to intercept() have settled
| So that we know what to resolve any committed and finished promises with.
|
Any finished Promise that was returned
| Until all promises returned from handlers passed to intercept() have settled
| So that we can resolve or reject it appropriately. |
State | Duration | Explanation |
---|---|---|
Any state
| For the duration of event firing | So that we can update the current entry’s state after the event successfully finishes firing without being canceled. |
State | Duration | Explanation |
---|---|---|
Any info
| Until the task is queued to fire the navigate event
| So that we can use it to fire the navigate event after the the trip through the session history traversal queue.
|
Any committed Promise that was returned
| Until the session history is updated (inside that same task) | So that we can resolve or reject it appropriately. |
Whether intercept() was called
| Until the session history is updated (inside that same task) | So that we can suppress the normal scroll restoration logic in favor of the chosen scroll option value.
|
Furthermore, we need to account for the fact that there might be multiple traversals queued up, e.g. via
const key1= navigation. entries()[ navigation. currentEntry. index- 1 ]. key; const key2= navigation. entries()[ navigation. currentEntry. index+ 1 ]. key; navigation. traverseTo( key1); // intentionally no await navigation. traverseTo( key2);
And, while non-traversal navigations cannot be queued in the same way since a new non-traversal navigation cancels an old one, we need to keep some state around so that we can properly cancel the old one. That is, given
const p1= navigation. navigate( url1). finished; const p2= navigation. navigate( url2). finished;
we need to ensure that when navigating to url2
, we still have the Promise
p1
around so that we can reject it. We can’t just get rid of any ongoing navigation promises the moment the second call to navigate()
happens.
We also need to ensure that, if we start a new navigation, navigations which have gotten as far as firing navigate
events, but not yet as far as firing navigatesuccess
or navigateerror
, get finalized with an aborted navigation error.
We end up accomplishing all this using the following setup:
Each Navigation
object has an associated ongoing navigate event, a NavigateEvent
or null, initially null.
Each Navigation
object has an associated ongoing navigation signal, which is an AbortSignal
or null, initially null.
Each Navigation
object has an associated focus changed during ongoing navigation, which is a boolean, initially false.
Each Navigation
object has an associated suppress normal scroll restoration during ongoing navigation, which is a boolean, initially false.
Each Navigation
object has an associated ongoing navigation, which is a navigation API method navigation or null, initially null.
Each Navigation
object has an associated upcoming non-traverse navigation, which is a navigation API method navigation or null, initially null.
Each Navigation
object has an associated upcoming traverse navigations, which is a map from strings to navigation API method navigations, initially empty.
An navigation API method navigation is a struct with the following items:
-
An navigation object, a
Navigation
-
A key, a string or null
-
An info, a JavaScript value
-
A serialized state, a serialized state or null
-
A committed-to entry, a
NavigationHistoryEntry
or null -
A committed promise, a
Promise
-
A finished promise, a
Promise
We need to store the ongoing navigation signal, focus changed during ongoing navigation, and suppress normal scroll restoration during ongoing navigation separately from the navigation API method navigation struct, since it needs to be tracked even for navigations that are not via the navigation API.
Navigation
navigation, a JavaScript value info, and a serialized state-or-null serializedState:
-
Let committedPromise and finishedPromise be new promises created in navigation’s relevant realm.
-
Mark as handled finishedPromise.
The web developer doesn’t necessarily care about finishedPromise being rejected:-
They might only care about committedPromise.
-
They could be doing multiple synchronous navigations within the same task, in which case all but the last will be aborted (causing their finishedPromise to reject). This could be an application bug, but also could just be an emergent feature of disparate parts of the application overriding each others' actions.
-
They might prefer to listen to other transition-failure signals instead of finishedPromise, e.g., the
navigateerror
event, or thenavigation.transition.finished
promise.
As such, we mark it as handled to ensure that it never triggers
unhandledrejection
events. -
-
Let ongoingNavigation be a navigation API method navigation whose navigation object is navigation, key is null, info is info, serialized state is serializedState, committed-to entry is null, committed promise is committedPromise, and finished promise is finishedPromise.
-
Assert: navigation’s upcoming non-traverse navigation is null.
-
Set navigation’s upcoming non-traverse navigation to ongoingNavigation.
-
Return ongoingNavigation.
Navigation
navigation, a string key, and a JavaScript value info:
-
Let committedPromise and finishedPromise be new promises created in navigation’s relevant realm.
-
Mark as handled finishedPromise.
See the previous discussion as to why this is done.
-
Let traversal be a navigation API method navigation whose whose navigation object is navigation, key is key, info is info, serialized state is null, committed-to entry is null, committed promise is committedPromise, and finished promise is finishedPromise.
-
Set navigation’s upcoming traverse navigations[key] to traversal.
-
Return traversal.
Navigation
navigation and a string-or-null destinationKey:
-
Assert: navigation’s ongoing navigation is null.
-
If destinationKey is not null, then:
-
Assert: navigation’s upcoming non-traverse navigation is null.
-
If navigation’s upcoming traverse navigations[destinationKey] exists, then:
-
Set navigation’s ongoing navigation to navigation’s upcoming traverse navigations[destinationKey].
-
Remove navigation’s upcoming traverse navigations[destinationKey].
-
-
-
Otherwise,
-
Set navigation’s ongoing navigation to navigation’s upcoming non-traverse navigation.
-
Set navigation’s upcoming non-traverse navigation to null.
-
-
Let navigation be navigation’s navigation object.
-
If navigation’s ongoing navigation is navigation, then set navigation’s ongoing navigation to null.
-
Otherwise,
-
Assert: navigation’s upcoming traverse navigations[navigation’s key] exists.
-
Remove navigation’s upcoming traverse navigations[navigation’s key].
NavigationHistoryEntry
entry:
-
Set navigation’s committed-to entry to entry.
-
If navigation’s serialized state is not null, then set entry’s session history entry's navigation API state to navigation’s serialized state.
If it’s null, then we’re traversing to entry via
traverseTo()
, which does not allow changing the state.After this point, navigation’s serialized state is no longer needed. Implementations might want to clear it out to avoid keeping it alive for the lifetime of the navigation API method navigation.
-
Resolve navigation’s committed promise with entry.
After this point, navigation’s committed promise is only needed in cases where it has not yet been returned to author code. Implementations might want to clear it out to avoid keeping it alive for the lifetime of the navigation API method navigation.
-
If navigation’s finished promise is null, then return.
-
Resolve navigation’s finished promise with its committed-to entry.
-
Clean up navigation.
-
If navigation’s finished promise is null, then return.
-
If navigation’s committed promise is not null, then reject navigation’s committed promise with exception.
-
Reject navigation’s finished promise with exception.
-
Clean up navigation.
1.4. Navigating
{
committed
,finished
} =navigation
.navigate
(url){
committed
,finished
} =navigation
.navigate
(url, options)-
Navigates the current page to the given url. options can contain the following values:
-
history
can be set to "replace
" to replace the current session history entry, instead of pushing a new one. -
info
can be set to any value; it will populate theinfo
property of the correspondingnavigate
event. -
state
can be set to any serializable value; it will populate the state retrieved bynavigation.currentEntry.getState()
once the navigation completes, for same-document navigations. (It will be ignored for navigations that end up cross-document.)
By default this will perform a full navigation (i.e., a cross-document navigation, unless the given URL differs only in a fragment from the current one). The
navigate
event’sintercept()
method can be used to convert it into a same-document navigation.The returned promises will behave as follows:
-
For navigations that get aborted, both promises will reject with an "
AbortError
"DOMException
. -
For same-document navigations created by using the
navigate
event’sintercept()
method,committed
will fulfill immediately, andfinished
will fulfill or reject according to any promises returned by handlers passed tointercept()
. -
For other same-document navigations (e.g., non-intercepted fragment navigations, both promises will fulfill immediately.
-
For cross-document navigations, or navigations that result in 204/205 statuses or
Content-Disposition: attachment
header fields from the server (and thus do not actually navigate), both promises will never settle.
In all cases, when the returned promises fulfill, it will be with the
NavigationHistoryEntry
that was navigated to. -
{
committed
,finished
} =navigation
.reload
(options)-
Reloads the current page. The
info
andstate
options behave as described above.The default behavior of performing a from-network-or-cache reload of the current page can be overriden by using the
navigate
event’sintercept()
method. Doing so will mean this call only updates state or passes along the appropriateinfo
, plus performing whatever actions thenavigate
event handler sees fit to carry out.The returned promises will behave as follows:
-
If the reload is aborted, both promises will reject with an "
AbortError
"DOMException
. -
If the reload is intercepted by using the
navigate
event’sintercept()
method,committed
will fulfill immediately, andfinished
will fulfill or reject according to the promises passed tointercept()
. -
Otherwise, both promises will never settle.
-
navigate(url, options)
method steps are:
-
Let document be this's relevant global object's associated document.
-
Let navigable be navigation’s relevant global object's navigable.
-
Assert: navigable is not null.
-
Parse url relative to this's relevant settings object. If that returns failure, then return an early error result for a "
SyntaxError
"DOMException
. Otherwise, let urlRecord be the resulting URL record. -
If options["
history
"] is "push
", and any of the following are true:-
document’s is initial about:blank is true;
-
document is not completely loaded;
-
url equals document’s URL; or
-
url’s scheme is "
javascript
"
then return an early error result for a "
NotSupportedError
"DOMException
.These are the conditions under which a push navigation will be converted into a replace navigation by the navigate algorithm or by the below step. If the developer explicitly requested a push, we fail to let them know it won’t happen.
In the future, we could consider loosening some of these conditions, e.g., allowing explicitly-requested push navigations to the current URL or before the document is completely loaded.
-
-
Let state be options["
state
"] if it exists; otherwise, undefined. -
Let serializedState be StructuredSerializeForStorage(state). If this throws an exception, then return an early error result for that exception.
It is important to perform this step early, since serialization can invoke web developer code, which in turn might change the state checked in later steps.
-
Let info be options["
info
"] if it exists; otherwise, undefined. -
Let historyHandling be "
replace
" if options["history
"] is "replace
" or if document is not completely loaded; otherwise, "push
". -
If document is not fully active, then return an early error result for an "
InvalidStateError
"DOMException
. -
If document’s unload counter is greater than 0, then return an early error result for an "
InvalidStateError
"DOMException
. -
Let ongoingNavigation be the result of setting the upcoming non-traverse navigation for navigation given info.
-
Navigate navigable to urlRecord using document, with historyHandling set to historyHandling and navigationAPIState set to serializedState.
Unlike
location.assign()
and friends, which are exposed across origin-domain boundaries,navigation.navigate()
can only be accessed by code with direct synchronous access to thenavigation
property. Thus, we avoid the complications around tracking the source document, and we don’t need to deal with the allowed by sandboxing to navigate check and its accompanying exceptionsEnabled flag. We just treat all navigations as being initiated by theNavigation
object itself. -
If navigation’s upcoming non-traverse navigation is ongoingNavigation, then:
This means the navigate algorithm bailed out before ever getting to the inner navigate event firing algorithm which would promote the upcoming navigation to ongoing.
-
Set navigation’s upcoming non-traverse navigation to null.
-
Return an early error result for an "
AbortError
"DOMException
.
-
-
Return «[ "
committed
" → ongoingNavigation’s committed promise, "finished
" → ongoingNavigation’s finished promise ]».
reload(options)
method steps are:
-
Let document be this's relevant global object's associated document.
-
Let navigable be navigation’s relevant global object's navigable.
-
Assert: navigable is not null.
-
Let serializedState be null.
-
If options["
state
"] exists, then set serializedState to StructuredSerializeForStorage(options["state
"]). If this throws an exception, then return an early error result for that exception.It is important to perform this step early, since serialization can invoke web developer code, which in turn might change the state checked in later steps.
-
Otherwise,
-
Let current be the current entry of this.
-
If current is not null, then set serializedState to current’s navigation API state.
-
Otherwise, set serializedState to StructuredSerializeForStorage(undefined).
-
-
Let info be options["
info
"] if it exists; otherwise, undefined. -
If document is not fully active, then return an early error result for an "
InvalidStateError
"DOMException
. -
If document’s unload counter is greater than 0, then return an early error result for an "
InvalidStateError
"DOMException
. -
Let ongoingNavigation be the result of setting the upcoming non-traverse navigation for navigation given info.
-
Reload navigable with navigationAPIState set to serializedState.
-
If navigation’s upcoming non-traverse navigation is ongoingNavigation, then:
This means the reload algorithm bailed out before ever getting to the inner navigate event firing algorithm which would promote the upcoming navigation to ongoing.
-
Set navigation’s upcoming non-traverse navigation to null.
-
Return an early error result for an "
AbortError
"DOMException
.
-
-
Return «[ "
committed
" → ongoingNavigation’s committed promise, "finished
" → ongoingNavigation’s finished promise ]».
An an early error result for an exception e is a dictionary instance given by «[ "committed
" → a promise rejected with e, "finished
" → a promise rejected with e ]».
1.5. Traversing
{
committed
,finished
} =navigation
.traverseTo
(key){
committed
,finished
} =navigation
.traverseTo
(key, {info
})-
Traverses the session history to the closest session history entry that matches the
NavigationHistoryEntry
with the given key.info
can be set to any value; it will populate theinfo
property of the correspondingnavigate
event.If a traversal to that session history entry is already in progress, then this will return the promises for that original traversal, and
info
will be ignored.The returned promises will behave as follows:
-
If there is no
NavigationHistoryEntry
innavigation.entries
with the given key, both will reject with an "InvalidStateError
"DOMException
. -
For same-document traversals intercepted by the
navigate
event’sintercept()
method,committed
will fulfill as soon as the traversal is processed andnavigation.currentEntry
is updated, andfinished
will fulfill or reject according to any promises returned by handlers passed tointercept()
. -
For non-intercepted same-document traversals, both promises will fulfill as soon as the traversal is processed and
navigation.currentEntry
is updated -
For cross-document traversals, or traversals that result in 204/205 statuses or
Content-Disposition: attachment
header fields from the server (and thus do not actually traverse), both promises will never settle.
-
{
committed
,finished
} =navigation
.back
(){
committed
,finished
} =navigation
.back
({info
})-
Traverse the session history to the closest previous session history entry which results in this navigable navigating, i.e. results in
navigation.currentEntry
updating.info
can be set to any value; it will populate theinfo
property of the correspondingnavigate
event.If a traversal to that session history entry is already in progress, then this will return the promises for that original traversal, and
info
will be ignored.The returned promises behave equivalently to those returned by
traverseTo()
. {
committed
,finished
} =navigation
.forward
(){
committed
,finished
} =navigation
.forward
({info
})-
Traverse the session history to the closest forward session history entry which results in this frame navigating, i.e. results in
navigation.currentEntry
updating.info
can be set to any value; it will populate theinfo
property of the correspondingnavigate
event.If a traversal to that session history entry is already in progress, then this will return the promises for that original traversal, and
info
will be ignored.The returned promises behave equivalently to those returned by
traverseTo()
.
traverseTo(key, options)
method steps are:
-
If this's current entry index is −1, then return an early error result for an "
InvalidStateError
"DOMException
. -
If this's entry list does not contain any
NavigationHistoryEntry
whose session history entry's navigation API key equals key, then return an early error result for an "InvalidStateError
"DOMException
. -
Return the result of performing a navigation API traversal given this, key, and options.
back(options)
method steps are:
-
If this's current entry index is −1 or 0, then return an early error result for an "
InvalidStateError
"DOMException
. -
Let key be this's entry list[this's current entry index − 1]'s session history entry's navigation API key.
-
Return the result of performing a navigation API traversal given this, key, and options.
forward(options)
method steps are:
-
If this's current entry index is −1 or is equal to this's entry list's size − 1, then return an early error result for an "
InvalidStateError
"DOMException
. -
Let key be this's entry list[this's current entry index + 1]'s session history entry's navigation API key.
-
Return the result of performing a navigation API traversal given this, key, and options.
Navigation
object navigation, a string key, and a NavigationOptions
options:
-
Let sourceDocument be navigation’s relevant global object's associated Document.
-
If sourceDocument is not fully active, then return an early error result for an "
InvalidStateError
"DOMException
. -
If sourceDocument’s unload counter is greater than 0, then return an early error result for an "
InvalidStateError
"DOMException
. -
If navigation’s current entry's session history entry's navigation API key equals key, then return «[ "
committed
" → a promise resolved with navigation’s current entry, "finished
" → a promise resolved with navigation’s current entry ]» -
If navigation’s upcoming traverse navigations[key] exists, then:
-
Let navigation be navigation’s upcoming traverse navigations[key].
-
Return «[ "
committed
" → navigation’s committed promise, "finished
" → navigation’s finished promise ]».
-
-
Let info be options["
info
"] if it exists, or undefined otherwise. -
Let ongoingNavigation be the result of setting an upcoming traverse navigation for navigation given key and info.
-
Let navigable be sourceDocument’s node navigable.
-
Let traversable be navigable’s traversable navigable.
-
Let sourceSnapshotParams be the result of snapshotting source snapshot params given sourceDocument.
-
Append the following session history traversal steps to traversable:
-
Let navigableEntries be the result of getting session history entries given navigable.
-
Let targetEntry be the session history entry in navigableEntries whose navigation API key equals key. If no such entry exists, then:
-
Queue a global task on the navigation and traversal task source given sourceDocument’s relevant global object to reject the finished promise for ongoingNavigation with an "
InvalidStateError
"DOMException
. -
Abort these steps.
This can occur if the navigation object’s view of session history is outdated, which can happen for brief periods while all the relevant threads and processes are being synchronized in reaction to a history change (such as the user clearing their history).
-
-
If targetEntry is navigable’s active session history entry, then abort these steps.
This can occur if a previously-queued-up traversal already took us to this session history entry. In that case that previous traversal will have dealt with ongoingNavigation already.
-
Apply the history step given by targetEntry’s step to traversable, with checkForUserCancellation set to true, sourceSnapshotParams set to sourceSnapshotParams, and initiatorToCheck set to navigable.
-
If this aborts due to user-canceled unloading or due to the
navigate
event being canceled, then queue a global task on the navigation and traversal task source given sourceDocument’s relevant global object to finalize with an aborted navigation error given navigation and ongoingNavigation. -
If this aborts due to the initiator allowed-to-navigate check, then queue a global task on the navigation and traversal task source given sourceDocument’s relevant global object to finalize with an aborted navigation error given navigation, ongoingNavigation, and a new "
SecurityError
"DOMException
created in navigation’s relevant realm.
As part of merging this spec into HTML, we would modify apply the history step to have well-specified hooks for communicating these conditions back to its caller.
-
-
-
Return «[ "
committed
" → ongoingNavigation’s committed promise, "finished
" → ongoingNavigation’s finished promise ]».
1.6. Event handlers
The following are the event handlers (and their corresponding event handler event types) that must be supported, as event handler IDL attributes, by objects implementing the Navigation
interface:
Event handler | Event handler event type |
---|---|
onnavigate
| navigate
|
onnavigatesuccess
| navigatesuccess
|
onnavigateerror
| navigateerror
|
oncurrententrychange
| currententrychange
|
2. The navigate
event
2.1. The NavigateEvent
class
[Exposed =Window ]interface :
NavigateEvent Event {(
constructor DOMString ,
type NavigateEventInit );
eventInit readonly attribute NavigationType navigationType ;readonly attribute NavigationDestination destination ;readonly attribute boolean canIntercept ;readonly attribute boolean userInitiated ;readonly attribute boolean hashChange ;readonly attribute AbortSignal signal ;readonly attribute FormData ?formData ;readonly attribute DOMString ?downloadRequest ;readonly attribute any info ;undefined intercept (optional NavigationInterceptOptions = {});
options undefined scroll (); };dictionary :
NavigateEventInit EventInit {NavigationType = "push";
navigationType required NavigationDestination ;
destination boolean =
canIntercept false ;boolean =
userInitiated false ;boolean =
hashChange false ;required AbortSignal ;
signal FormData ?=
formData null ;DOMString ?=
downloadRequest null ;any ; };
info dictionary {
NavigationInterceptOptions NavigationInterceptHandler ;
handler NavigationFocusReset ;
focusReset NavigationScrollBehavior ; };
scroll enum {
NavigationFocusReset ,
"after-transition" };
"manual" enum {
NavigationScrollBehavior ,
"after-transition" };
"manual" callback =
NavigationInterceptHandler Promise <undefined > ();enum {
NavigationType ,
"reload" ,
"push" ,
"replace" };
"traverse"
event.
navigationType
-
One of "
reload
", "push
", "replace
", or "traverse
", indicating what type of navigation this is. event.
destination
-
A
NavigationDestination
representing the destination of the navigation. event.
canIntercept
-
True if
intercept()
can be called to intercept this navigation and convert it into a single-page navigation; false otherwise.Generally speaking, this will be true whenever the current
Document
can have its URL rewritten to the destination URL, except for cross-document back/forward navigations, where it will always be false. event.
userInitiated
-
True if this navigation was due to a user clicking on an
a
element, submitting aform
element, or using the browser UI to navigate; false otherwise. event.
hashChange
-
True if this navigation is a fragment navigation; false otherwise.
event.
signal
-
An
AbortSignal
which will become aborted if the navigation gets canceled, e.g. by the user pressing their browser’s "Stop" button, or another higher-priority navigation interrupting this one.The expected pattern is for developers to pass this along to any async operations, such as
fetch()
, which they perform as part of handling this navigation. event.
formData
-
The
FormData
representing the submitted form entries for this navigation, if this navigation is a "push
" or "replace
" navigation representing a POST form submission; null otherwise.(Notably, this will be null even for "
reload
" and "traverse
" navigations that are revisiting a session history entry that was originally created from a form submission.) event.
downloadRequest
-
Represents whether or not this navigation was requested to be a download, by using an
a
orarea
element’sdownload
attribute:-
If a download was not requested, then this property is null.
-
If a download was requested, returns the filename that was supplied, via
<a download="filename" href="...">
. (This could be the empty string, as in the case of<a download href="...">
.)
Note that a download being requested does not always mean that a download will happen: for example, the download might be blocked by browser security policies, or end up being treated as a push navigation for unspecified reasons.
Similarly, a navigation might end up being a download even if it was not requested to be one, due to the destination server responding with a
Content-Disposition: attachment
header.Finally, note that the
navigate
event will not fire at all for downloads initiated using browser UI affordances, e.g., those created by right-clicking and choosing to save the target of the link. -
event.
info
-
An arbitrary JavaScript value passed via
navigation
APIs that initiated this navigation, or null if the navigation was initiated by the user or via a non-navigation
API. event.
intercept
({handler
,focusReset
,scroll
})-
Intercepts this navigation, preventing its normally handling and instead converting it into a same-document navigation to the destination URL.
The
handler
option can be a function that returns a promise. The handler function will run after thenavigate
event has finished firing, and thenavigation.currentEntry
property has been synchronously updated. This promise is used to signal the duration, and success or failure, of the navigation. After it settles, the browser signals to the user (e.g. via a loading spinner UI, or assistive technology) that the navigation is finished. Additionally, it firesnavigatesuccess
ornavigateerror
events as appropriate, which other parts of the web application can respond to.By default, using this method will cause focus to reset when any handlers' returned promises settle. Focus will be reset to the first element with the
autofocus
attribute set, or thebody
element if the attribute isn’t present. ThefocusReset
option can be set to "manual
" to avoid this behavior.By default, using this method will delay the browser’s scroll restoration logic for "
traverse
" or "reload
" navigations, or its scroll-reset/scroll-to-a-fragment logic for "push
" and "replace
" navigations, until any handlers' returned promises settle. Thescroll
option can be set to "manual
" to turn off any browser-driven scroll behavior entirely for this navigation, orevent.scroll()
can be called before the promise settles to trigger this behavior early.This method will throw a "
SecurityError
"DOMException
ifcanIntercept
is false, or ifisTrusted
is false. It will throw an "InvalidStateError
"DOMException
if not called synchronously, during event dispatch. event.
scroll
()-
For "
traverse
" or "reload
" navigations, restores the scroll position using the browser’s usual scroll restoration logic.For "
push
" or "replace
" navigations, either resets the scroll position to the top of the document or scrolls to the fragment specified byevent.destination.url
if there is one.If called more than once, or called after automatic post-transition scroll processing has happened due to the
scroll
option being left as "after-transition
", this method will throw an "InvalidStateError
"DOMException
.
The navigationType
, destination
, canIntercept
, userInitiated
, hashChange
, signal
, formData
, downloadRequest
, and info
getter steps are to return the value that the corresponding attribute was initialized to.
A NavigateEvent
has a classic history API serialized data, a serialized state-or-null. It is only used in some cases where the event’s navigationType
is "push
" or "replace
", and is set appropriately when the event is fired.
A NavigateEvent
has a focus reset behavior, a NavigationFocusReset
-or-null, initially null.
A NavigateEvent
has a scroll behavior, a NavigationScrollBehavior
-or-null, initially null.
A NavigateEvent
has a did process scroll behavior, a boolean, initially false.
A NavigateEvent
has a was intercepted, a boolean, initially false.
A NavigateEvent
has a needs continue, a boolean, initially false.
A NavigateEvent
has a navigation handler list, which is a list of NavigationInterceptHandler
callbacks, initially empty.
intercept(options)
method steps are:
-
If this's relevant global object's associated Document is not fully active, then throw an "
InvalidStateError
"DOMException
. -
If this's
isTrusted
attribute was initialized to false, then throw a "SecurityError
"DOMException
. -
If this's
canIntercept
attribute was initialized to false, then throw a "SecurityError
"DOMException
. -
If this's dispatch flag is unset, then throw an "
InvalidStateError
"DOMException
. -
If this's canceled flag is set, then throw an "
InvalidStateError
"DOMException
. -
If options["
handler
"] exists, then append it to this's navigation handler list. -
Set this's was intercepted to true.
-
If options["
focusReset
"] exists, then:-
If this's focus reset behavior is not null, and it is not equal to options["
focusReset
"], then the user agent may report a warning to the console indicating that thefocusReset
option for a previous call tointercept()
was overridden by this new value, and the previous value will be ignored. -
Set this's focus reset behavior to options["
focusReset
"].
-
-
If options["
scroll
"] exists, then:-
If this's scroll behavior is not null, and it is not equal to options["
scroll
"], then the user agent may report a warning to the console indicating that thescroll
option for a previous call tointercept()
was overridden by this new value, and the previous value will be ignored. -
Set this's scroll behavior to options["
scroll
"].
-
scroll()
method steps are:
-
If this's did process scroll behavior is true, then throw an "
InvalidStateError
"DOMException
. -
If this's was intercepted is false, then throw an "
InvalidStateError
"DOMException
. -
If this's relevant global object's associated Document is not fully active, then throw an "
InvalidStateError
"DOMException
.
2.2. The NavigationDestination
class
[Exposed =Window ]interface {
NavigationDestination readonly attribute USVString url ;readonly attribute DOMString ?key ;readonly attribute DOMString ?id ;readonly attribute long long index ;readonly attribute boolean sameDocument ;any getState (); };
event.
destination
.url
-
The URL being navigated to.
event.
destination
.key
-
The value of the
key
property of the destinationNavigationHistoryEntry
, if this is a "traverse
" navigation, or null otherwise. event.
destination
.id
-
The value of the
id
property of the destinationNavigationHistoryEntry
, if this is a "traverse
" navigation, or null otherwise. event.
destination
.index
-
The value of the
index
property of the destinationNavigationHistoryEntry
, if this is a "traverse
" navigation, or −1 otherwise. event.
destination
.sameDocument
-
Indicates whether or not this navigation is to the same
Document
as the currentdocument
value, or not. This will be true, for example, in cases of fragment navigations orhistory.pushState()
navigations.Note that this property indicates the original nature of the navigation. If a cross-document navigation is converted into a same-document navigation using
event.intercept()
, that will not change the value of this property. state = event.
destination
.getState()
-
For "
traverse
" navigations, returns the deserialization of the state stored in the destination session history entry.For "
push
" and "replace
" navigations, returns the deserialization of the state passed tonavigation.navigate()
, if the navigation was initiated in that way, or undefined if it wasn’t.For "
reload
" navigations, returns the deserialization of the state passed tonavigation.reload()
, if the reload was initiated in that way, or undefined if it wasn’t.
A NavigationDestination
has an associated URL, which is a URL.
A NavigationDestination
has an associated key, which is a string-or-null.
A NavigationDestination
has an associated id, which is a string-or-null.
A NavigationDestination
has an associated index, which is an integer.
A NavigationDestination
has an associated state, which is a serialized state-or-null.
A NavigationDestination
has an associated is same document, which is a boolean.
The url
getter steps are to return this's URL, serialized.
The key
getter steps are to return this's key.
The id
getter steps are to return this's id.
The index
getter steps are to return this's index.
The sameDocument
getter steps are to return this's is same document.
getState()
method steps are:
-
Return StructuredDeserialize(this's state).
2.3. Firing the event
navigate
event at a Navigation
navigation given a session history entry destinationEntry, and an optional user navigation involvement userInvolvement (default "none
"):
-
Let event be the result of creating an event given
NavigateEvent
, in navigation’s relevant realm. -
Set event’s classic history API serialized data to null.
-
Let destination be a new
NavigationDestination
created in navigation’s relevant realm. -
If destinationEntry’s document state's origin is same origin with navigation’s relevant settings object's origin, then:
-
Set destination’s key to destinationEntry’s navigation API key.
-
Set destination’s id to destinationEntry’s navigation API ID.
-
Set destination’s index to the result of getting the navigation API history index of destinationEntry within navigation.
-
Set destination’s state to destinationEntry’s navigation API state.
-
-
Otherwise,
-
Set destination’s is same document to true if destinationEntry’s document is equal to navigation’s relevant global object's associated Document; otherwise false.
-
Return the result of performing the inner navigate event firing algorithm given navigation, "
traverse
", event, destination, userInvolvement, null, and null.
navigate
event at a Navigation
navigation given a NavigationType
navigationType, a URL destinationURL, a boolean isSameDocument, an optional user navigation involvement userInvolvement (default "none
"), an optional serialized state-or-null state (default null), an optional entry list or null formDataEntryList (default null), and an optional serialized state-or-null classicHistoryAPISerializedData (default null):
-
Let event be the result of creating an event given
NavigateEvent
, in navigation’s relevant realm. -
Set event’s classic history API serialized data to classicHistoryAPISerializedData.
-
Let destination be a new
NavigationDestination
created in navigation’s relevant realm. -
Set destination’s URL to destinationURL.
-
Set destination’s key to null.
-
Set destination’s id to null.
-
Set destination’s index to −1.
-
Set destination’s state to state.
-
Set destination’s is same document to isSameDocument.
-
Return the result of performing the inner navigate event firing algorithm given navigation, navigationType, event, destination, userInvolvement, formDataEntryList, and null.
navigate
event at a Navigation
navigation given a URL destinationURL, a user navigation involvement userInvolvement, and a string filename:
-
Let event be the result of creating an event given
NavigateEvent
, in navigation’s relevant realm. -
Set event’s classic history API serialized data to null.
-
Let destination be a new
NavigationDestination
created in navigation’s relevant realm. -
Set destination’s URL to destinationURL.
-
Set destination’s key to null.
-
Set destination’s id to null.
-
Set destination’s index to −1.
-
Set destination’s state to null.
-
Set destination’s is same document to false.
-
Return the result of performing the inner navigate event firing algorithm given navigation, "
push
", event, destination, userInvolvement, null, and filename.
navigate
event firing algorithm is the following steps, given a Navigation
navigation, a NavigationType
navigationType, a NavigateEvent
event, a NavigationDestination
destination, a user navigation involvement userInvolvement, an entry list or null formDataEntryList, and a string or null downloadRequestFilename:
-
Promote the upcoming navigation to ongoing given navigation and destination’s key.
-
Let ongoingNavigation be navigation’s ongoing navigation.
-
If navigation has entries and events disabled, then:
-
If ongoingNavigation is not null, then clean up ongoingNavigation.
In this case the committed promise and finished promise will never fulfill, since we never create
NavigationHistoryEntry
s for the initialabout:blank
Document
so we have nothing to resolve them with. -
Return true.
-
-
Let navigable be navigation’s relevant global object's navigable.
-
Let document be navigation’s relevant global object's associated document.
-
If document can have its URL rewritten to destination’s URL, and either destination’s is same document is true or navigationType is not "
traverse
", then initialize event’scanIntercept
to true. Otherwise, initialize it to false. -
Let traverseCanBeCanceled be true if userInvolvement is not "
browser UI
" or navigation’s relevant global object has transient activation; otherwise, false.We are exploring the possibility of introducing the concept of a "consumable user activation" (i.e., a user activation that can be consumed like a transient user activation, but does not have the timeout of a transient user activation) instead of using transient activation here. This does not have a spec yet.
-
If any of the following are true:
-
navigationType is not "
traverse
"; or -
navigable is a top-level traversable and traverseCanBeCanceled is true
then initialize event’s
cancelable
to true. Otherwise, initialize it to false. -
-
Initialize event’s
navigationType
to navigationType. -
Initialize event’s
destination
to destination. -
Initialize event’s
downloadRequest
to downloadRequestFilename. -
If ongoingNavigation is not null, then initialize event’s
info
to ongoingNavigation’s info. Otherwise, initialize it to undefined.At this point ongoingNavigation’s info is no longer needed and can be nulled out instead of keeping it alive for the lifetime of the navigation API method navigation.
-
Initialize event’s
signal
to a newAbortSignal
created in navigation’s relevant realm. -
Let currentURL be document’s URL.
-
If all of the following are true:
-
destination’s is same document is true;
-
destination’s URL equals currentURL with exclude fragments set to true; and
-
destination’s URL's fragment is not identical to currentURL’s fragment
then initialize event’s
hashChange
to true. Otherwise, initialize it to false. -
-
If userInvolvement is not "
none
", then initialize event’suserInitiated
to true. Otherwise, initialize it to false. -
If formDataEntryList is not null, then initialize event’s
formData
to a newFormData
created in navigation’s relevant realm, associated to formDataEntryList. Otherwise, initialize it to null. -
Assert: navigation’s ongoing navigate event is null.
-
Set navigation’s ongoing navigate event to event.
-
Assert: navigation’s ongoing navigation signal is null.
-
Set navigation’s ongoing navigation signal to event’s
signal
. -
Set navigation’s focus changed during ongoing navigation to false.
-
Set navigation’s suppress normal scroll restoration during ongoing navigation to false.
-
Let dispatchResult be the result of dispatching event at navigation.
-
If dispatchResult is false:
-
If navigationType is not "
traverse
" and event’ssignal
is not aborted, then finalize with an aborted navigation error given navigation and ongoingNavigation.If navigationType is "
traverse
", then we will finalize with an aborted navigation error in perform a navigation API traversal. -
Return false.
-
-
If destination’s is same document is true and navigationType is "
traverse
":-
Set event’s needs continue to true.
-
Return true.
-
-
Return the result of reacting to navigate event result given navigation and event.
navigate
event result, given a Navigation
navigation and a NavigateEvent
event:
-
Let document be navigation’s relevant global object's associated document.
-
Let destination be event’s
destination
. -
Let ongoingNavigation be navigation’s ongoing navigation.
-
Let navigationType be event’s
navigationType
. -
Let endResultIsSameDocument be true if event’s was intercepted is true or destination’s is same document is true.
-
Prepare to run script given event’s relevant settings object.
-
If event’s was intercepted is true:
-
Let fromEntry be the current entry for navigation.
-
Assert: fromEntry is not null.
-
Set navigation’s transition to a new
NavigationTransition
created in navigation’s relevant realm, whose navigation type is navigationType, from entry is fromEntry, and whose finished promise is a new promise created in navigation’s relevant realm. -
Mark as handled navigation’s transition's finished promise.
See the discussion about other finished promises as to why this is done.
-
If navigationType is "
traverse
", then set navigation’s suppress normal scroll restoration during ongoing navigation to true.If event’s scroll behavior was set to "
after-transition
", then we will potentially perform scroll restoration below. Otherwise, there will be no scroll restoration. That is, no navigation which is intercepted byintercept()
goes through the normal scroll restoration process; scroll restoration for such navigations is either done manually, by the web developer, or is done after the transition. -
If navigationType is "
push
" or "replace
", then run the URL and history update steps given document and event’sdestination
's URL, with serializedData set to event’s classic history API serialized data and historyHandling set to navigationType.If navigationType is "
reload
", then we are converting a reload into a "same-document reload", for which the URL and history update steps are not appropriate. Navigation API-related stuff still happens, such as updating the active session history entry's navigation API state if this was caused by a call tonavigation.reload()
, and all the ongoing navigation tracking.
-
-
If endResultIsSameDocument is true:
-
Let promisesList be an empty list.
-
For each handler of event’s navigation handler list:
-
If promisesList’s size is 0, then set promisesList to « a promise resolved with
undefined
».There is a subtle timing difference between how waiting for all schedules its success and failure steps when given zero promises versus ≥1 promises. For most uses of waiting for all, this does not matter. However, with this API, there are so many events and promise handlers which could fire around the same time that the difference is pretty easily observable: it can cause the event/promise handler sequence to vary. (Some of the events and promises involved include:
navigatesuccess
/navigateerror
,currententrychange
,dispose
, ongoingNavigation’s promises, and thenavigation.transition.finished
promise.) -
Wait for all of promisesList, with the following success steps:
-
If event equals navigation’s ongoing navigate event, set navigation’s ongoing navigate event to null.
-
Fire an event named
navigatesuccess
at navigation. -
If navigation’s transition is not null, then resolve navigation’s transition's finished promise with undefined.
-
Set navigation’s transition to null.
-
If ongoingNavigation is non-null, then resolve the finished promise for ongoingNavigation.
-
Potentially reset the focus given navigation and event.
-
Potentially process scroll behavior given event.
-
Fire an event named
navigateerror
at navigation usingErrorEvent
, witherror
initialized to rejectionReason, andmessage
,filename
,lineno
, andcolno
initialized to appropriate values that can be extracted from rejectionReason in the same underspecified way the user agent typically does for the report an exception algorithm. -
If navigation’s transition is not null, then reject navigation’s transition's finished promise with rejectionReason.
-
Set navigation’s transition to null.
-
If ongoingNavigation is non-null, then reject the finished promise for ongoingNavigation with rejectionReason.
-
Potentially reset the focus given navigation and event.
Although we still potentially reset the focus for such failed transitions, we do not potentially process scroll behavior for them.
-
-
Otherwise, if ongoingNavigation is non-null, then clean up ongoingNavigation.
-
Clean up after running script given event’s relevant settings object.
Per the previous note, this stops suppressing any potential promise handler microtasks, causing them to run at this point or later.
-
If event’s was intercepted is true, then return false.
-
Return true.
navigate
event, given a Navigation
navigation:
-
If navigation’s ongoing navigate event is null, or navigation’s ongoing navigate event's needs continue is false, then return.
-
React to navigate event result given navigation and navigation’s ongoing navigate event.
Navigation
navigation, a navigation API method navigation or null ongoingNavigation, and an optional DOMException
error:
-
Set navigation’s focus changed during ongoing navigation to false.
-
Set navigation’s suppress normal scroll restoration during ongoing navigation to false.
-
If error was not given, then set error to a new "
AbortError
"DOMException
, created in navigation’s relevant realm. -
If navigation’s ongoing navigate event is non-null, then:
-
Set navigation’s ongoing navigate event's canceled flag to true.
-
Set navigation’s ongoing navigate event to null.
-
-
If navigation’s ongoing navigation signal is non-null, then:
-
Signal abort on navigation’s ongoing navigation signal given error.
-
Set navigation’s ongoing navigation signal to null.
-
-
Fire an event named
navigateerror
at navigation usingErrorEvent
, witherror
initialized to error, andmessage
,filename
,lineno
, andcolno
initialized to appropriate values that can be extracted from error and the current JavaScript stack in the same underspecified way the user agent typically does for the report an exception algorithm.Thus, for example, if this algorithm is reached because of a call to
window.stop()
, these properties would probably end up initialized based on the line of script that calledwindow.stop()
. But if it’s because the user clicked the stop button, these properties would probably end up with default values like the empty string or 0. -
If ongoingNavigation is non-null, then reject the finished promise for ongoingNavigation with error.
-
If navigation’s transition is not null, then:
-
Reject navigation’s transition's finished promise with error.
-
Set navigation’s transition to null.
-
-
Let navigation be navigable’s active window's navigation API.
-
If navigation’s ongoing navigation signal is null, then return.
-
Finalize with an aborted navigation error given navigation and navigation’s ongoing navigation.
-
Inform the navigation API about canceling navigation in navigable.
-
Let navigation be navigable’s active window's navigation API.
-
Let traversals be a clone of navigation’s upcoming traverse navigations.
-
For each traversal of traversals: finalize with an aborted navigation error given navigation and traversal.
Navigation
object navigation and an NavigateEvent
event:
-
Let focusChanged be navigation’s focus changed during ongoing navigation.
-
Set navigation’s focus changed during ongoing navigation to false.
-
If focusChanged is true, then return.
-
If event’s was intercepted is false, then return.
-
If event’s focus reset behavior is "
manual
", then return.If it was left as null, then we treat that as "
after-transition
", and continue onward. -
Let document be navigation’s relevant global object's associated Document.
-
Let focusTarget be the autofocus delegate for document.
-
If focusTarget is null, then set focusTarget to document’s body element.
-
If focusTarget is null, then set focusTarget to document’s document element.
-
Run the focusing steps for focusTarget, with document’s viewport as the fallback target.
-
Move the sequential focus navigation starting point to focusTarget.
NavigateEvent
event:
-
If event’s was intercepted is false, then return.
-
If event’s scroll behavior is "
manual
", then return.If it was left as null, then we treat that as "
after-transition
", and continue onward. -
If event’s did process scroll behavior is true, then return.
-
Definitely process scroll behavior given event.
NavigateEvent
event:
-
Set event’s did process scroll behavior to true.
-
If event’s
navigationType
was initialized to "traverse
" or "reload
", then restore scroll position data given event’s relevant global object's navigable's active session history entry. -
Otherwise,
-
Let document be event’s relevant global object's associated Document.
-
If document’s indicated part is null, then scroll to the beginning of the document given document.
-
Otherwise, scroll to the fragment given document.
-
3. Navigation API history entries
[Exposed =Window ]interface :
NavigationHistoryEntry EventTarget {readonly attribute USVString ?url ;readonly attribute DOMString key ;readonly attribute DOMString id ;readonly attribute long long index ;readonly attribute boolean sameDocument ;any getState ();attribute EventHandler ondispose ; };
entry.
url
-
The URL of this navigation history entry.
This can return null if the entry corresponds to a different
Document
than the current one (i.e. ifsameDocument
is false), and thatDocument
's referrer policy was "no-referrer
" or "origin
", since that indicates theDocument
in question is hiding its URL even from other same-origin pages. entry.
key
-
A user agent-generated random UUID string representing this navigation history entry’s place in the navigation history list. This value will be reused by other
NavigationHistoryEntry
instances that replace this one due to replace-style navigations. This value will survive session restores.This is useful for navigating back to this entry in the navigation history list, using
navigation.traverseTo(key)
. entry.
id
-
A user agent-generated random UUID string representing this specific navigation history entry. This value will not be reused by other
NavigationHistoryEntry
instances. This value will survive session restores.This is useful for associating data with this navigation history entry using other storage APIs.
entry.
index
-
The index of this navigation history entry within
navigation.entries()
, or −1 if the entry is not in the navigation history entry list. entry.
sameDocument
-
Indicates whether or not this navigation history entry is for the same
Document
as the currentdocument
value, or not. This will be true, for example, when the entry represents a fragment navigation or single-page app navigations. state = entry.
getState
()-
Returns the deserialization of the state stored in this entry, which was added to the entry using
navigation.navigate()
. This state survives session restores.Note that in general, unless the state value is a primitive,
entry.getState() !== entry.getState()
, since a fresh copy is returned each time.This state is unrelated to the classic history API’s
history.state
.
Each NavigationHistoryEntry
has an associated session history entry, which is a session history entry.
Each NavigationHistoryEntry
has an associated index, which is an integer.
key
getter steps are:
-
If this's relevant global object's associated Document is not fully active, then return the empty string.
-
Return this's session history entry's navigation API key.
id
getter steps are:
-
If this's relevant global object's associated Document is not fully active, then return the empty string.
-
Return this's session history entry's navigation API ID.
url
getter steps are:
-
If this's relevant global object's associated Document is not fully active, then return null.
-
Let she be this's session history entry.
-
If she’s document does not equal this's relevant global object's associated Document, and she’s document state's request referrer policy is "
no-referrer
" or "origin
", then return null. -
Return she’s URL, serialized.
index
getter steps are:
-
If this's relevant global object's associated Document is not fully active, then return −1.
sameDocument
getter steps are:
-
If this's relevant global object's associated Document is not fully active, then return false.
-
Return true if this's session history entry's document equals this's relevant global object's associated Document, and false otherwise.
getState()
method steps are:
-
If this's relevant global object's associated Document is not fully active, then return undefined.
-
If this's session history entry's navigation API state is null, then return undefined.
-
Return StructuredDeserialize(this's session history entry's navigation API state).
Unlike history.state
, this will deserialize upon each access.
This can in theory throw an exception, if attempting to deserialize a large ArrayBuffer
when not enough memory is available.
The following are the event handlers (and their corresponding event handler event types) that must be supported, as event handler IDL attributes, by objects implementing the NavigationHistoryEntry
interface:
Event handler | Event handler event type |
---|---|
ondispose
| dispose
|
4. HTML Standard monkeypatches
4.1. New session history entry items
Each session history entry gains the following new items:
-
navigation API key, a string, initially set to the result of generating a random UUID
-
navigation API ID, a string, initially set to the result of generating a random UUID
-
navigation API state, which is serialized state or null, initially null
4.2. Navigate
4.3. Navigate to a fragment
-
Let navigation be navigable’s active window's navigation API.
-
Let continue be the result of firing a non-traversal navigate event at navigation given with navigationType set to historyHandling, isSameDocument set to true, userInvolvement set to userInvolvement, and destinationURL set to url.
-
If continue is false, then return.
Modify the step which creates the new historyEntry to also copy over the navigation API state from the active session history entry.
Insert the following step after the call to update document for history step application:
-
Update the entries for a same-document navigation given navigable’s active window's navigation API, navigable’s active session history entry, and historyHandling.
4.4. Shared history push/replace state steps
-
Let navigation be history’s relevant global object's navigation API.
-
Let continue be the result of firing a non-traversal navigate event at navigation with navigationType set to historyHandling, isSameDocument set to true, destinationURL set to newURL, and classicHistoryAPISerializedData set to serializedData.
-
If continue is false, then return.
4.5. URL and history update steps
-
Update the entries for a same-document navigation given document’s relevant global object's navigation API, navigable’s active session history entry, and historyHandling.
4.6. Update document for history state application
Prepend the following step:
-
Let navigation be document’s relevant global object's navigation API.
Inside the "documentsEntryChanged is true" block, after restore the history state object:
-
If documentIsNew is false, then update the entries for a same-document navigation given navigation, entry, and "
traverse
". -
Otherwise, initialize the entries for a new Navigation given navigation, sessionHistoryEntries TODO pass this in, and entry.
This means any currententrychange
event and dispose
events will fire before the popstate
and hashchange
events, and those event handlers will see an updated Navigation
object.
4.7. Finalize a cross-document navigation
-
If historyEntry’s document state's origin is the same as entryToReplace’s document state's origin, then set historyEntry’s navigation API key to entryToReplace’s navigation API key.
4.8. Reactivate
-
Update the entries for reactivation given document’s relevant global object's navigation API, sessionHistoryEntries TODO pass this in (harder), and entry TODO pass this in (easier).
4.9. Reload
none
"). Then, prepend the following steps:
-
Let navigation be navigable’s active window's navigation API.
-
If both of the following are true:
-
userInvolvement is not "
browser UI
" -
navigable’s active document's is initial about:blank is false
then:
-
Let continue be the result of firing a non-traversal navigate event at navigation with navigationType set to "
reload
", isSameDocument set to false, userInvolvement set to userInvolvement, destinationURL set to navigable’s active document's URL, and state set to navigationAPIState. -
If continue is false, return.
-
4.10. Traversal
Modify the traverse the history by a delta algorithm to take an optional named argument userInvolvement (default "none
"). Pass it along when calling apply the history step, which also needs to be modified to take such a argument.
4.11. Download a hyperlink
The current specification for downloading a hyperlink has several known issues, most notably whatwg/html#5548 which indicates that the specification should probably be merged into the general navigation algorithm.
For the purposes of the navigation API, we need to fire the appropriate navigate
event, with downloadRequest
set to the correct value. We could rigorously detail the ways to modify the current spec to accomplish this. But, given that the current spec will be rewritten anyway, this is probably not very useful. So until such a time as we can properly investigate and rewrite the downloading a hyperlink algorithm, we describe here the expected behavior in a less-formal fashion. We believe this is still enough to get interoperability.
-
Ensure that the algorithm gets an appropriate user navigation involvement value, userInvolvement, passed to it. This is similar to the modifications for the follow the hyperlink algorithm described in § 4.15.1 User navigation involvement. One key difference is that, for the case where the user indicates a preference for downloading, userInvolvement must be "
browser UI
", even if it is triggered as part of activation behavior. -
Separate out the sandboxing checks in allowed to download from the user-safeguarding checks. If the sandboxing checks fail, then the user agent must not fire a
navigate
event. Whereas, the user-safeguarding checks generally happen later, probably in parallel. -
Before we reach the point at which it’s time to actually go in parallel and fetch content from the server, and after the cannot navigate check, the synchronously-possible part of the allowed to download check, the URL parsing step, and the hyperlink suffix appending step, run the equivalent of the following:
-
If userInvolvement is not "
browser UI
", then:-
Let navigation be subject’s relevant global object's navigation API.
-
Let filename be the value of subject’s
download
attribute. -
Let continue be the result of firing a download-requested navigate event at navigation with destinationURL set to URL, userInvolvement set to userInvolvement, and filename set to filename.
-
If continue is false, then return.
-
Here the variables subject and URL refer to the same things they currently do in the download the hyperlink algorithm, i.e. the
a
orarea
element in question, and the parsed URL.If we end up triggering the navigate algorithm from the download the hyperlink algorithm, then these steps won’t be directly incorporated into the download the hyperlink algorithm. Instead, the modifications in § 4.2 Navigate will get a bit more complicated, so as to use fire a download-requested navigate event with the above arguments, instead of fire a non-traversal navigate event, for downloads.
-
4.12. Canceling navigations
The navigation API introduces a new complication into canceling navigations, which is that a navigation might have been fully committed, but still be "ongoing", in the sense of § 1.3 Ongoing navigation tracking. That is, consider a case such as:
navigation. addEventListener( "navigate" , e=> { e. intercept({ handler() { return new Promise( r=> setTimeout( r, 1 _000)); } }); e. signal. addEventListener( "abort" , () => { ... }); }); const p= navigation. navigate( "#1" ); setTimeout(() => window. stop(), 500 );
Without the navigate
event handler, this kind of synchronous fragment navigation would be straightforward: it matures synchronously, and the stop()
call does nothing. But because we have used the navigate
handler to indicate that the navigation is still ongoing, we want the stop()
call to finalize that navigation with an aborted navigation error, in particular causing p
to reject and the abort
event to fire on e.signal
.
We thus need to add the following integrations:
-
Wherever the spec currently modifies a navigable navigable’s ongoing navigation, and thus cancels not-yet-committed navigations, we also inform the navigation API about canceling navigation in navigable. (Regardless of whether or not the ongoing navigation is set to a navigation ID or not.) This likely involves introducing a small abstraction around modifying the ongoing navigation.
-
When destroy the nested navigable algortihm runs given a navigable navigable, we also inform the navigation API about nested navigable destruction given navigable.
It would probably be a good idea to rename navigable's ongoing navigation concept and/or Navigation
objects' ongoing navigation, to avoid the naming overlap, since they mean slightly different things.
4.13. Check if unloading is user-canceled
To check if unloading is user-canceled given a list of navigables navigablesCrossingDocuments, an optional navigable traversable, an optional integer targetStep (default null), and an optional user navigation involvement userInvolvement (default "none
"):
-
Let unloadPromptShown be false.
-
Let unloadPromptCanceled be false.
-
Let changingNavigables be a list of navigables, initially empty.
-
If traversable is not null and targetStep is not null, append the result of get all navigables whose current session history entry will change or reload given traversable and targetStep to changingNavigables.
-
Otherwise, append navigablesCrossingDocuments to changingNavigables.
-
Let totalTasks be the size of changingNavigables.
-
Let completedTasks be 0.
-
If traversable is not null and changingNavigables contains traversable:
-
Let targetEntry be the result of getting the target history entry given traversable and targetStep.
-
Let isSameOrigin be true if targetEntry’s document state's origin is the same as traversable’s current session history entry's document state's origin; otherwise, false.
-
Queue a global task on the navigation and traversal task source given traversable’s active window to run the following steps:
-
If isSameOrigin is true:
-
Let navigateEventResult be the result of firing a traversal navigate event at traversable’s active window's navigation API with destinationEntry set to targetEntry and userInvolvement set to userInvolvement.
-
If navigateEventResult is false, then set unloadPromptCanceled to true.
-
-
If unloadPromptCanceled is false and navigablesCrossingDocuments contains traversable:
-
Run the steps to fire a beforeunload event (currently steps 6.1 thru 6.8 in the check if unloading is user-canceled steps).
-
-
Increment completedTasks.
-
-
Wait for completedTasks to be 1.
-
If unloadPromptCanceled is true, then return "
refuse
";
-
-
For each navigable of changingNavigables:
-
If navigable equals traversable, then continue.
-
Let targetEntry be the result of getting the target history entry given traversable and targetStep.
-
Let isSameOrigin be true if targetEntry’s document state's origin is the same as navigable’s current session history entry's document state's origin; otherwise, false.
-
Queue a global task on the navigation and traversal task source given navigable’s active window to run the following steps:
-
If isSameOrigin is true:
-
Let navigateEventResult be the result of firing a traversal navigate event at navigable’s active window's navigation API with destinationEntry set to targetEntry and userInvolvement set to userInvolvement.
-
Assert: navigateEventResult is true.
-
-
If navigablesCrossingDocuments contains navigable:
-
Run the steps to fire a beforeunload event (currently steps 6.1 thru 6.8 in the check if unloading is user-canceled steps).
-
-
Increment completedTasks.
-
-
-
Wait for completedTasks to be totalTasks.
-
Return unloadPromptCanceled.
Change step 5 (which runs the check if unloading is user-canceled steps) to:
-
If checkForUserCancelation is true, and the result of checking if unloading is user-canceled given navigablesCrossingDocuments, traversable, targetStep and userInvolvement is "
refuse
", then return.
(Recall that we introduced the userInvolvement parameter as part of § 4.10 Traversal.)
Before the update document for history step application (currently step 14.10.3), add the following step:
-
If changingNavigableContinuation’s update-only is true, maybe continue the navigate event given displayedDocument’s relevant global object's navigation API.
Potentially rename check if unloading is user-canceled to something like "check for unloading cancelation" to reflect that we are now allowing programmatic cancelation, not just user cancelation.
4.14. User navigation involvement
Introduce (right before the definition of the navigate algorithm) the concept of a user navigation involvement, which is one of the following:
- "
browser UI
" -
The navigation was initiated by the user via browser UI mechanisms
- "
activation
" -
The navigation was initiated by the user via the activation behavior of an element
- "
none
" -
The navigation was not initiated by the user
This infrastructure partially solves whatwg/html#5381, and it’d be ideal to update the `Sec-Fetch-Site
` spec at the same time.
Define the user navigation involvement for an Event
event as "activation
" if event’s isTrusted
attribute is initialized to true, and "none
" otherwise.
4.15. Various call site patches
4.15.1. User navigation involvement
Modify the Browser user interface considerations section to require setting navigate's userInvolvement, reload’s userInvolvement, and traverse the history by a delta's userInvolvement all to "browser UI
" for all operations in that section.
Modify the follow the hyperlink algorithm to take a new userInvolvement argument. Then, update the call to it from navigate to set userInvolvement to this userInvolvement value.
area
elements by introducing the event argument and replacing the follow the hyperlink step with the following:
-
Otherwise, follow the hyperlink created by element with the user navigation involvement for event.
a
elements by replacing its follow the hyperlink step with the following:
-
Otherwise, follow the hyperlink created by element with the user navigation involvement for event.
Expand the section on "Providing users with a means to follow hyperlinks created using the link
element" by adding the following sentence:
Such invocations of follow the hyperlink algorithm must set the userInvolvement argument to "browser UI
".
Modify the plan to navigate algorithm to take a userInvolvement argument. Then, update the call to it from navigate to set userInvolvement to this userInvolvement value.
Modify the submit algorithm to take an optional userInvolvement argument (default "none
"). Have the submit algorithm pass along its value to all invocations of plan to navigate.
Modify the definition of the activation behavior for input
elements to take an event argument. Then, pass along this argument to the invocation of the input activation behavior.
Modify the Submit Button state’s input activation behavior by having it take an event argument and pass along the user navigation involvement for event as the final argument when it calls submit.
Modify the Image Button state’s input activation behavior by having it take an event argument and pass along the user navigation involvement for event as the final argument when it calls submit.
Modify the button
element’s activation behavior by having it take an event argument and, in the Submit Button case, to pass along the user navigation involvement for event as the final argument when it calls submit.
Modify the no-submit button case for implicit form submission to pass along "activation
" as the final argument when it calls submit.
The case of implicit submission when a submit button is present is automatically taken care of because it fires a (trusted) click event at the submit button.
Update the call to from navigate into navigate to a fragment to pass along userInvolvement.
4.15.2. Form entry list
Modify the plan to navigate algorithm to take an additional optional argument entryList (default null). Then, modify the step which calls navigate to pass it along as entryList, in place of cspNavigationType.
Modify the submit as entity body algorithm to pass entry list along to plan to navigate as a second argument.
4.16. Focus tracking
To support the focusReset
option, the following patches need to be made:
Update the focusing steps to, right before they call the focus update steps, set the Document
's relevant global object's navigation API's focus changed during ongoing navigation to true.
Update the focus fixup rule to additionally set the Document
's relevant global object's navigation API's focus changed during ongoing navigation to false.
In combination, these ensure that the focus changed during ongoing navigation reflects any developer- or user-initiated focus changes, unless they were undone by the focus fixup rule. For example, if the user moved focus to an element which was removed from the DOM while the promise returned from a handler passed to intercept()
was settling, then that would not count as a focus change.
4.17. Scroll restoration
To support the scroll
option, as well as to fix whatwg/html#7517, the following patches need to be made:
Add a boolean, has been scrolled by the user, initially false, to Document
objects. State that if the user scrolls the document, the user agent must set that document’s has been scrolled by the user to true. Modify the unload a document algorithm to set this back to false.
-
Let document be entry’s document.
-
If document’s has been scrolled by the user is true, then the user agent should return.
-
The user agent should attempt to use entry’s scroll position data to restore the scroll positions of document’s restorable scrollable regions. The user agent may continue to attempt to do so periodically, until document’s has been scrolled by the user becomes true.
This is formulated as an attempt, which is potentially repeated until success or until the user scrolls, due to the fact that relevant content indicated by the scroll position data might take some time to load from the network.
Scroll restoration might be affected by scroll anchoring. [CSS-SCROLL-ANCHORING-1]
-
If entry’s scroll restoration mode is "
auto
", and entry’s document's relevant global object's navigation API's suppress normal scroll restoration during ongoing navigation is false, then restore scroll position data given entry.
In addition to the existing note, add the following one:
If the suppress normal scroll restoration during ongoing navigation boolean is true, then restoring scroll position data might still happen at a later point, as part of potentially processing scroll behavior for the relevant Navigation
object, or via a navigateEvent.scroll()
method call.
4.18. Helpful note: failed attempt to populate
Inside apply the history step, expand the note which currently says
This means we tried to populate the document, but were unable to do so, e.g. because of the server returning a 204.
to instead say:
This means we tried to populate the document, but were unable to do so, e.g. because of the server returning a 204.
These kinds of failed navigations or traversals will not be signaled to the navigation API (e.g., through the promises of any ongoing navigation, or the navigation.transition.finished
promise, or the navigateerror
event). Doing so would leak information about the timing of responses from other origins, in the cross-origin case, and providing different results in the cross-origin versus same-origin cases was deemed too confusing.
However, implementations could use this opportunity to clear any promise handlers for the navigation.transition.finished
promise, as they are guaranteed at this point to never run. And, they might wish to report a warning to the console if any part of the navigation API initiated these navigations, to make it clear to the web developer the reason why their promises will never settle and events will never fire.
5. Security and privacy considerations
5.1. Cross-site tracking
This specification does not enable any new cross-site tracking capabilities. This is largely because the navigation.entries()
method only returns information about the same-origin, same-frame history entries.
In more detail:
-
The storage of navigation API state in session history entries is a convenience with no tracking abilities, since the state is only accessible same-origin. That is, it provides the same power as APIs such as partitioned
sessionStorage
. -
The browser-generated UUIDs stored as navigation API ID and navigation API key live only for the lifetime of a browsing session; they are not stable user-specific identifiers, and in particular are not the same across different frames.
5.2. Navigation monitoring and interception
Through the navigate
event, this API allows web developers to monitor navigations, and in some cases replace cross-document navigations with same-document ones, or prevent the navigation from going through.
Care has been taken to avoid this being dangerous, or giving insight into user behavior that would not otherwise be available to the site. In particular:
-
It is not possible to prevent a back/forward navigation, as this could lead to trapping the user on the page. (We would like to make this possible in the future by adding additional security mitigations; see #32.)
-
Navigations initiated from cross-origin-domain frames do not fire
navigate
events. An example would be such a frame callingwindow.open(url, name)
withname
targeting the current frame. -
Navigations initiated using browser UI, apart from back/forward navigations or fragment-only navigations, do not fire
navigate
events. An example would be the user directly editing the URL bar.
We also have a few more restrictions worth noting, which don’t directly address any attack, but reduce the surface area and complexity of the navigation interception feature, which can have indirect security benefits:
-
Navigations initiated toward non-fetch scheme URLs, such as
javascript:
URLs, do not firenavigate
events. -
Navigations initiated on the initial
about:blank
document do not firenavigate
events. -
Navigations initiated by
document.open()
do not firenavigate
events. -
Cross-document traversals cannot be intercepted and converted into same-document traversals.
5.3. URL updates
This API, like history.pushState()
and history.replaceState()
, gives the ability to change what is shown in the browser’s URL bar. This is part of the navigation interception capability mentioned in the previous section.
This is not dangerous, because the navigation API is subject to the same restrictions as the classic history API: namely, the page’s URL can only be changed if the page can have its URL rewritten to the new URL. So in particular no authority-granting components, such as the site or origin, are impacted.
5.4. Other user agent UI
This specification does not add any requirements on how user agents implement their user interfaces. (Even the URL bar updates mentioned in the previous section are not technically part of the specification; the specification only governs the return value of other APIs, like location.href
.) This preserves the ability for user agents to protect users through UI changes.
For example, today some user agents take advantage of this flexibility to skip certain history entries when pressing the back button. This can be used to avoid back-trapping by abusive sites, by skipping entries with which the user did not interact and thus allowing the user to escape abusive sites faster.
The navigation API preserves all of these freedoms.