1. The AppHistory class
partial interface Window {readonly attribute AppHistory appHistory ; };
Each Window object has an associated app history, which is a new AppHistory instance created alongside the Window.
The appHistory getter steps are to return this's app history.
[Exposed =Window ]interface :AppHistory EventTarget {attribute AppHistoryEntry ?current ;sequence <AppHistoryEntry >entries ();readonly attribute boolean canGoBack ;readonly attribute boolean canGoForward ;Promise <undefined >navigate (USVString ,url optional AppHistoryNavigateOptions = {});options Promise <undefined >navigate (optional AppHistoryNavigateOptions = {});options attribute EventHandler onnavigate ;attribute EventHandler onnavigatesuccess ;attribute EventHandler onnavigateerror ; };dictionary {AppHistoryNavigationOptions any ; };navigateInfo dictionary :AppHistoryNavigateOptions AppHistoryNavigationOptions {any ;state boolean =replace false ; };
Each AppHistory object has an associated entry list, a list of AppHistoryEntry objects, initially empty.
Each AppHistory object has an associated current index, an integer, initially −1.
Each AppHistory object has an associated ongoing navigate event, an AppHistoryNavigateEvent or null, initially null.
Each AppHistory object has an associated navigate method call promise, which is either a Promise or null, initially null.
Each AppHistory object has an associated navigate method call serialized state, which is either a serialized state or null, initially null.
1.1. Introspecting the app history entry list
appHistory.current-
The current
AppHistoryEntry. appHistory.entries()-
Returns an array of
AppHistoryEntryinstances representing the current app history list, i.e. all session history entries for thisWindowthat are same origin and contiguous to the current session history entry. appHistory.canGoBack-
Returns true if the current
AppHistoryEntryis not the first one in the app history entries list. appHistory.canGoForward-
Returns true if the current
AppHistoryEntryis not the last one in the app history entries list.
current getter steps are:
-
If this's relevant global object's associated Document is not fully active, then return null.
-
If this's current index is −1, then return null.
This occurs if accessing the API while on the initial
about:blankDocument, where the update the entries algorithm will not yet have been run to completion. -
Return this's entry list[this's current index].
entries() method steps are:
-
If this's relevant global object's associated Document is not fully active, then return the empty list.
-
Return this's entries list.
canGoBack getter steps are:
-
If this's relevant global object's associated Document is not fully active, then return false.
-
If this's current index is −1, then return false.
-
If this's current index is 0, then return false.
-
Return true.
canGoForward getter steps are:
-
If this's relevant global object's associated Document is not fully active, then return false.
-
If this's current index is −1, then return false.
-
If this's current index is equal to this's entry list's size − 1, then return false.
-
Return true.
AppHistory instance appHistory:
-
Let browsingContext be appHistory’s relevant global object's associated Document's browsing context.
-
Assert: browsingContext is not null.
-
Let sessionHistory be browsingContext’s session history.
-
If browsingContext is still on its initial
about:blankDocument, then:-
Assert: appHistory’s entry list is empty.
-
Return.
This can occur if running the URL and history update steps, e.g. via
document.open(), on the initialabout:blankDocument. The app history API chooses not to expose the initialabout:blankDocument, so we bail early. -
-
Let appHistorySHEs be a new empty list.
-
Let currentSHE be sessionHistory’s current entry.
-
Let backwardIndex be the index of currentSHE within sessionHistory, minus 1.
-
While backwardIndex > 0:
-
Let she be sessionHistory[backwardIndex].
-
If she’s origin is same origin with currentSHE’s origin, then prepend she to appHistorySHEs.
-
Otherwise, break.
-
Set backwardIndex to backwardIndex − 1.
-
-
Append currentSHE to appHistorySHEs.
-
Let forwardIndex be the index of currentSHE within sessionHistory, plus 1.
-
While forwardIndex < sessionHistory’s size:
-
Let she be sessionHistory[forwardIndex].
-
If she’s origin is same origin with currentSHE’s origin, then append she to appHistorySHEs.
-
Otherwise, break.
-
Set forwardIndex to forwardIndex + 1.
-
-
Let newCurrentIndex be the index of currentSHE within appHistorySHEs.
-
Let newEntryList be an empty list.
-
For each oldAHE of appHistory’s entry list:
-
Set oldAHE’s index to −1.
-
-
Let index be 0.
-
For each she of appHistorySHEs:
-
If appHistory’s entry list contains an
AppHistoryEntryexistingAHE whose session history entry is she, then append existingAHE to newEntryList. -
Otherwise:
-
Let newAHE be a new
AppHistoryEntrycreated in the relevant realm of appHistory. -
Set newAHE’s session history entry to she.
-
Append newAHE to newEntryList.
-
-
Set newEntryList[index]'s index to index.
-
Set index to index + 1.
-
-
Set appHistory’s entry list to newEntryList.
-
Set appHistory’s current index to newCurrentIndex.
For same-document navigations only the current index and the indices of the various AppHistoryEntry objects will ultimately be updated by this algorithm. Implementations can special-case same-document navigations and avoid reassembling the entry list.
1.2. Navigating
awaitappHistory.navigate(url)awaitappHistory.navigate(url, options)-
Navigates the current page to the given url. options can contain the following values:
-
replacecan be set to true to replace the current session history entry, instead of pushing a new one -
navigateInfocan be set to any value; it will populate theinfoproperty of the correspondingnavigateevent. -
statecan be set to any serializable value; it will populate the state retrieved byappHistory.current.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
navigateevent’srespondWith()method can be used to convert it into a same-document navigation.The returned promise will behave as follows:
-
For same-document navigations created by using the
navigateevent’srespondWith()method, it will fulfill or reject according to the promise passed torespondWith(). -
For other same-document navigations (e.g., non-intercepted fragment navigations), it will fulfill immediately.
-
For cross-document navigations, it will never settle.
-
awaitappHistory.navigate(options)-
Navigates to the same URL as the current page. options needs to contain at least one of
navigateInfoorstate, which behave as described above.The default behavior of performing a full navigation to the current page can be overriden by using the
navigateevent’srespondWith()method. Doing so will mean this call only updates state or passes along the appropriatenavigateInfo.The returned promise behaves as described above.
navigate(url, options) method steps are:
-
Parse url relative to this's relevant settings object. If that returns failure, then return a promise rejected with a "
SyntaxError"DOMException. Otherwise, let urlRecord be the resulting URL record. -
Return the result of performing an app history navigation given this, urlRecord, and options.
navigate(options) method steps are:
-
If neither options["
navigateInfo"] nor options["state"] exists, then return a promise rejected with aTypeError. -
Set options["
replace"] to true.This is not technically necessary, as the main navigate algorithm will override any non-"
replace" history handling behavior for same-URL navigations. -
Let urlRecord be this's relevant global object's active document's URL.
-
Return the result of performing an app history navigation given this, urlRecord, and options.
AppHistory object appHistory, a URL url, and an AppHistoryNavigateOptions options:
-
Let browsingContext be appHistory’s relevant global object's browsing context.
-
Let historyHandling be "
replace" if options["replace"] exists and is true; otherwise, "default". -
Let navigateInfo be options["
navigateInfo"] if it exists; otherwise, undefined. -
Let serializedState be null.
-
If options["
state"] exists, then set serializedState to StructuredSerializeForStorage(options["state"]). If this throws an exception, return a promise rejected with that exception. -
Let promise be a new promise created in appHistory’s relevant Realm.
-
Let previousPromise be appHistory’s navigate method call promise.
-
Let previousState be appHistory’s navigate method call serialized state.
-
Set appHistory’s navigate method call promise to promise.
-
Set appHistory’s navigate method call serialized state to serializedState.
-
Navigate browsingContext to url with historyHandling set to historyHandling, appHistoryInfo set to navigateInfo, appHistoryState set to serializedState, and the source browsing context set to browsingContext.
-
If navigate method call serialized state is non-null, then set browsingContext’s session history's current entry's app history state to appHistory’s navigate method call serialized state.
-
Set appHistory’s navigate method call promise to previousPromise.
-
Set appHistory’s navigate method call serialized state to previousState.
-
Return promise.
Unlike location.assign() and friends, which are exposed across origin-domain boundaries, appHistory.navigate() can only be accessed by code with direct synchronous access to the appHistory property. Thus, we avoid the complications around tracking source browsing contexts, and we don’t need to deal with the allowed to navigate check and its accompanying exceptionsEnabled flag. We just treat all navigations as being initiated by the AppHistory object itself.
1.3. 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 AppHistory interface:
| Event handler | Event handler event type |
|---|---|
onnavigate
| navigate
|
onnavigatesuccess
| navigatesuccess
|
onnavigateerror
| navigateerror
|
2. The navigate event
2.1. The AppHistoryNavigateEvent class
[Exposed =Window ]interface :AppHistoryNavigateEvent Event {(constructor DOMString ,type optional AppHistoryNavigateEventInit = {});eventInit readonly attribute AppHistoryNavigationType navigationType ;readonly attribute AppHistoryDestination destination ;readonly attribute boolean canRespond ;readonly attribute boolean userInitiated ;readonly attribute boolean hashChange ; // readonly attribute AbortSignal signal;readonly attribute FormData ?formData ;readonly attribute any info ;undefined respondWith (Promise <undefined >); };newNavigationAction dictionary :AppHistoryNavigateEventInit EventInit {AppHistoryNavigationType = "push";navigationType required AppHistoryDestination ;destination boolean =canRespond false ;boolean =userInitiated false ;boolean =hashChange false ; // required AbortSignal signal;FormData ?=formData null ;any =info null ; };enum {AppHistoryNavigationType ,"push" ,"replace" };"traverse"
event .navigationType-
One of "
push", "replace", or "traverse", indicating what type of navigation this is. event .destination-
An
AppHistoryDestinationrepresenting the destination of the navigation. event .canRespond-
True if
respondWith()can be called to convert this navigation into a single-page navigation; false otherwise.Generally speaking, this will be true whenever the destination URL is rewritable relative to the page’s current 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
aelement, submitting aformelement, or using the browser UI to navigate; false otherwise. event .hashChange-
True if this navigation is a fragment navigation; false otherwise.
event .formData-
The
FormDatarepresenting the submitted form entries for this navigation, if this navigation is a POST form submission; null otherwise. event .info-
An arbitrary JavaScript value passed via other app history APIs that initiated this navigation, or null if the navigation was initiated by the user or via a non-app history API.
event .respondWith(newNavigationAction)-
Synchronously converts this navigation into a same-document navigation to the destination URL.
The given newNavigationAction 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 fires
navigatesuccessornavigateerrorevents as appropriate, which other parts of the web application can respond to.This method will throw a "
SecurityError"DOMExceptionifcanRespondis false, or ifisTrustedis false. It will throw an "InvalidStateError"DOMExceptionif not called synchronously, during event dispatch.
The navigationType, destination, canRespond, userInitiated, hashChange, formData, and info getter steps are to return the value that the corresponding attribute was initialized to.
An AppHistoryNavigateEvent has the following associated values which are only conditionally used:
-
classic history API serialized data, a serialized state-or-null, used when its
navigationTypeis "push" or "replace" -
destination entry, a session history entry, used when its
navigationTypeis "traverse"
One of these is set appropriately when the event is fired.
An AppHistoryNavigateEvent also has an associated Promise-or-null navigation action promise, initially null.
respondWith(newNavigationAction) method steps are:
-
If this's relevant global object's browsing context is null, then throw an "
InvalidStateError"DOMException. -
If this's
isTrustedattribute was initialized to false, then throw a "SecurityError"DOMException. -
If this's
canRespondattribute 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. -
Set this's canceled flag.
-
Set this's navigation action promise to newNavigationAction.
2.2. The AppHistoryDestination class
[Exposed =Window ]interface {AppHistoryDestination readonly attribute USVString url ;readonly attribute boolean sameDocument ;any getState (); };
event .destination.url-
The URL being navigated to.
event .destination.sameDocument-
Indicates whether or not this navigation is to the same
Documentas the currentdocumentvalue, 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.respondWith(), that will not change the value of this property. 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 toappHistory.navigate(), if the navigation was initiated in that way, or null if it wasn’t.
An AppHistoryDestination has an associated URL, which is a URL.
An AppHistoryDestination has an associated state, which is a serialized state-or-null.
An AppHistoryDestination has an associated is same document, which is a boolean.
The url getter steps are to return this's URL, serialized.
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 an AppHistory appHistory given a session history entry destinationEntry, a boolean isSameDocument, an optional user navigation involvement userInvolvement (default "none"), and an optional JavaScript value info (default undefined):
-
Let destinationURL be destinationEntry’s URL.
-
Let destinationState be destinationEntry’s app history state.
-
Let event be the result of creating an event given
AppHistoryNavigateEvent, in appHistory’s relevant Realm. -
Set event’s destination entry to destinationEntry.
-
Return the result of performing the inner navigate event firing algorithm given appHistory, event, "
traverse", isSameDocument, destinationURL, destinationState, userInvolvement, info, and null.
navigate event at an AppHistory appHistory given an AppHistoryNavigationType navigationType, a URL destinationURL, a boolean isSameDocument, an optional user navigation involvement userInvolvement (default "none"), and an optional value info (default undefined), an optional serialized state-or-null state (default null), an optional list of FormData entries 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
AppHistoryNavigateEvent, in appHistory’s relevant Realm. -
Set event’s classic history API serialized data to classicHistoryAPISerializedData.
-
Return the result of performing the inner navigate event firing algorithm given appHistory, event, navigationType, isSameDocument, destinationURL, state, userInvolvement, info, and formDataEntryList.
navigate event firing algorithm is the following steps, given an AppHistory appHistory, an AppHistoryNavigateEvent event, an AppHistoryNavigationType navigationType, a boolean isSameDocument, a URL destinationURL, a serialized state-or-null destinationState, a user navigation involvement userInvolvement, a JavaScript value info, and a list of FormData entries or null formDataEntryList:
-
If appHistory’s relevant global object's browsing context is still on its initial
about:blankDocument, then return true. -
Initialize event’s
navigationTypeto navigationType. -
Initialize event’s
infoto info. -
Let destination be a new
AppHistoryDestinationcreated in appHistory’s relevant Realm. -
Set destination’s URL to destinationURL.
-
Set destination’s state to destinationState.
-
Set destination’s is same document to isSameDocument.
-
Initialize event’s
destinationto destination. -
Let currentURL be appHistory’s relevant global object's associated document's URL.
-
If all of the following are true:
-
isSameDocument is true;
-
destinationURL equals currentURL with exclude fragments set to true; and
-
destinationURL’s fragment is not identical to currentURL’s fragment
then initialize event’s
hashChangeto true. Otherwise, initialize it to false. -
-
If destinationURL is rewritable relative to currentURL, and either isSameDocument is true or navigationType is not "
traverse", then initialize event’scanRespondto true. Otherwise, initialize it to false. -
If either userInvolvement is not "
browser UI" or navigationType is not "traverse", then initialize event’scancelableto true. -
If userInvolvement is "
none", then initialize event’suserInitiatedto false. Otherwise, initialize it to true. -
If formDataEntryList is not null, then initialize event’s
formDatato a newFormDatacreated in realm, associated to formDataEntryList. Otherwise, initialize it to null. -
Assert: appHistory’s ongoing navigate event is null.
-
Set appHistory’s ongoing navigate event to event.
-
Let result be the result of dispatching event at appHistory.
-
Set appHistory’s ongoing navigate event to null.
-
If appHistory’s relevant global object's browsing context is null, then return false.
-
Signal an aborted navigation for appHistory.
-
Return false.
This can occur if an event listener disconnected the
iframecorresponding to this's relevant global object. -
-
If event’s navigation action promise is non-null, then:
-
If event’s
navigationTypeattribute was initialized to "push" or "replace":-
Let isPush be true if event’s
navigationTypeattribute was initialized to "push"; otherwise, false. -
Run the URL and history update steps given event’s relevant global object's associated document and event’s
destination's URL, with serializedData set to event’s classic history API serialized data and isPush set to isPush.
-
-
Otherwise:
-
Traverse the history of event’s relevant global object's browsing context to destination entry.
-
-
-
If event’s navigation action promise is non-null, or both result and isSameDocument are true, then:
-
If event’s navigation action promise is null, then set it to a promise resolved with undefined, created in realm.
-
Let navigateMethodCallPromise be appHistory’s navigate method call promise.
-
React to event’s navigation action promise with the following fulfillment steps given fulfillmentValue:
-
Fire an event named
navigatesuccessat appHistory. -
If navigateMethodCallPromise is non-null, then resolve navigateMethodCallPromise with fulfillmentValue.
-
Fire an event named
navigateerrorat appHistory usingErrorEvent, witherrorinitialized to rejectionReason, andmessage,filename,lineno, andcolnoinitialized 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 navigateMethodCallPromise is non-null, then reject navigateMethodCallPromise with rejectionReason.
-
If event’s navigation action promise is non-null, then
respondWith()was called and so we’re performing a same-document navigation, for which we want to firenavigatesuccessornavigateerrorevents, and resolve or reject the promise returned by the correspondingappHistory.navigate()call if one exists. Otherwise, if the navigation is same-document and was not canceled, we still perform these actions after a microtask, treating them as an instantly-successful navigation. -
-
Otherwise:
-
Set appHistory’s navigate method call serialized state to null.
This ensures that any call to
appHistory.navigate()which triggered this algorithm does not overwrite the app history state of the current entry for cross-document navigations or canceled navigations. -
-
If event’s navigation action promise is null and result is false, then signal an aborted navigation for appHistory.
-
Return result.
AppHistory appHistory:
-
Queue a microtask on appHistory’s relevant agent's event loop to perform the following steps:
-
Let error be a new "
AbortError"DOMException, created in appHistory’s relevant Realm. -
Fire an event named
navigateerrorat appHistory usingErrorEvent, witherrorinitialized to error,messageinitialized to the value of error’smessageproperty,filenameinitialized to the empty string, andlinenoandcolnoinitialized to 0. -
If appHistory’s navigate method call promise is non-null, then reject appHistory’s navigate method call promise with error.
-
navigate event for an AppHistory appHistory:
-
If appHistory’s ongoing navigate event is non-null, then:
-
Set appHistory’s ongoing navigate event's canceled flag to true.
-
Set appHistory’s ongoing navigate event's navigation action promise to null.
-
A URL is rewritable relative to another URL if they differ in only the path, query, or fragment components.
https://example.com/foo?bar#baz is rewritable relative to https://example.com/qux.
However, the concept is not the same as the two URLs' origins being the same: https://user:password@example.com/qux is not rewritable relative to https://example.com/qux.
Similarly, about:blank or blob: URLs are not rewritable relative to https: URLs, despite there being cases where a https:-URL Document is same origin with an about:blank or blob:-derived Document.
3. App history entries
[Exposed =Window ]interface :AppHistoryEntry EventTarget {readonly attribute DOMString key ;readonly attribute DOMString id ;readonly attribute USVString url ;readonly attribute long long index ;readonly attribute boolean sameDocument ;any getState (); // TODO event handlers };
entry .key-
A user agent-generated random UUID string representing this app history entry’s place in the app history list. This value will be reused by other
AppHistoryEntryinstances that replace this one due to replace-style navigations. This value will survive session restores.This is useful for navigating back to this location in the app history entry list, using
appHistory.goTo(key). entry .id-
A user agent-generated random UUID string representing this specific app history entry. This value will not be reused by other
AppHistoryEntryinstances. This value will survive session restores.This is useful for associating data with this app history entry using other storage APIs.
entry .url-
The URL of this app history entry.
entry .index-
The index of this app history entry within
appHistory.entries(), or −1 if the entry is not in the app history list. entry .sameDocument-
Indicates whether or not this app history entry is for the same
Documentas the currentdocumentvalue, or not. This will be true, for example, when the entry represents a fragment navigation or single-page app navigations. entry .getState()-
Returns the deserialization of the state stored in this entry, which was added to the entry using
appHistory.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 AppHistoryEntry has an associated session history entry, which is a session history entry.
Each AppHistoryEntry 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 app history 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 app history id.
url 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 URL, serialized.
index getter steps are:
-
If this's relevant global object's associated Document is not fully active, then return −1.
-
Return this's session history entry's index.
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 null.
-
If this's session history entry's app history state is null, then return null.
-
Return StructuredDeserialize(this's session history entry's app history 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.
4. Patches to fire the navigate event
The following section details monkeypatches to [HTML] that cause the navigate event to be fired appropriately, and for canceling the event to cancel the navigation. The first few sections detail slight tweaks to existing algorithms to pass through useful information into the navigation and history traversal algorithms. Then, § 4.3 Navigation algorithm updates contains the actual firing of the event.
4.1. Form submission patches
To properly thread the form entry list from its creation through to AppHistoryNavigateEvent's formData property, we need the following modifications:
-
Let navigationType be "
form-submission" if entryList is non-null; otherwise, "other".
-
Navigate target browsing context to destination, with historyHandling set to historyHandling
and navigationType set to "entryList set to entryList .form-submission"
4.2. Browser UI/user-initiated patches
To more rigorously specify when a navigation is initiated from browser UI or by the user interacting with a, area, and form elements, both for the purposes of the AppHistoryNavigateEvent's userInitiated property and for prohibiting interception of certain types of browser-UI-initiated navigations, we need the following modifications:
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
Define the user navigation involvement for an Event event as "activation" if event’s isTrusted attribute is initialized to true, and "none" otherwise.
Modify the navigate algorithm to take an optional named argument userInvolvement (default "none"). Then, update the paragraph talking about browser-UI initiated navigation as follows:
A user agent may provide various ways for the user to explicitly cause a browsing context to navigate, in addition to those defined in this specification.
Such cases must set the userInvolvement argument to "browser UI".
This infrastructure partially solves whatwg/html#5381, and it’d be ideal to update the `Sec-Fetch-Site` spec at the same time.
Modify the navigate to a fragment algorithm to take a new userInvolvement argument. Then, update the call to it from navigate to set userInvolvement to this userInvolvement value.
Modify the traverse the history by a delta argument to take an optional named argument userInvolvement (default "none"). Then, update the paragraph talking about user-initiated navigation as follows:
When the user navigates through a browsing context, e.g. using a browser’s back and forward buttons, the user agent must traverse the history by a delta with a delta equivalent to the action specified by the userand, the browsing context being operated on , and userInvolvement set to "browser UI" .
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.
4.3. Navigation algorithm updates
With the above infrastructure in place, we can actually fire and handle the navigate event in the following locations:
-
Let appHistory be history’s relevant global object's app history.
-
Cancel any ongoing navigate event for appHistory.
-
Let navigationType be "
push" if isPush is true, and "replace" otherwise. -
Let continue be the result of firing a push or replace navigate event at appHistory with navigationType set to navigationType, isSameDocument set to true, destinationURL set to newURL, and classicHistoryAPISerializedData set to serializedData.
-
If continue is false, return.
-
Let appHistory be the current entry's document’s relevant global object's app history.
-
Let navigationType be "
push" if historyHandling is "default"; otherwise, "replace". -
Let continue be the result of firing a push or replace navigate event at appHistory given with navigationType set to navigationType, isSameDocument set to true, userInvolvement set to userInvolvement, and destinationURL set to url.
-
If continue is false, return.
-
Let appHistory be specified browsing context’s active window's app history.
-
Cancel any ongoing navigate event for appHistory.
-
Let isSameDocument be true if specified browsing context’s active document equals specified entry’s document; otherwise, false.
-
If either isSameDocument is true or userInvolvement is not "
browser UI", then:-
Let continue be the result of firing a traversal navigate event at appHistory with destinationEntry set to specified entry, isSameDocument set to isSameDocument, and userInvolvement set to userInvolvement.
-
If continue is false, abort these steps.
-
5. Patches to session history
This section details monkeypatches to [HTML] to track appropriate data for associating an AppHistory with a session history entry.
5.1. New session history entry items
Each session history entry gains the following new items:
-
origin, an origin
-
app history key, a string, initially set to the result of generating a random UUID
-
app history id, a string, initially set to the result of generating a random UUID
-
app history state, which is serialized state or null, initially null
5.2. Carrying over the app history key
replace" case by adding the following step after the construction of newEntry:
-
If newEntry’s origin is the same as sessionHistory’s current entry's origin, then set newEntry’s app history key to sessionHistory’s current entry's app history key.
5.3. Carrying over the app history state
5.4. Tracking the origin member
Update the update the session history with the new page algorithm’s "replace" and "default" cases to set newEntry’s origin to newDocument’s origin as part of its creation.
Update the navigate to a fragment algorithm to set the new session history entry's origin to the current entry's document's origin.
Update the URL and history update steps algorithm to set the new session history entry's origin to document’s origin.
Potentially update the traverse the history algorithm to consult the new origin field, instead of checking the document's origin, since the document can disappear?? Needs further investigation.
5.5. Updating the AppHistory object
-
Update the entries of newDocument’s relevant global object's app history.
-
Update the entries of document’s relevant global object's app history.
We do not update the entries when initially creating a new browsing context, as we intentionally don’t want to include the initial about:blank Document in any app history entry list.