GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing html.spec.whatwg.org — HTML Standard
7.4 Navigation and session historyWelcome to the dragon's maw. Navigation, session history, and the traversal through that session history are some of the most complex parts of this standard. The basic concept may not seem so difficult:
You can see some of the intertwined complexity peeking through here, in how traversal can cause a navigation (i.e., a network fetch to a stored URL), and how a navigation necessarily needs to interface with the session history list to ensure that when it finishes the user is looking at the right thing. But the real problems come in with the various edge cases and interacting web platform features:
In what follows, we have attempted to guide the reader through these complexities by appropriately cordoning them off into labeled sections and algorithms, and giving appropriate words of introduction where possible. Nevertheless, if you wish to truly understand navigation and session history, the usual advice will be invaluable. 7.4.1 Session history7.4.1.1 Session history entriesA session history entry is a struct with the following items:
To get a session history entry's document, return its document state's document. Serialized state is a serialization (via StructuredSerializeForStorage) of an object representing a user interface state. We sometimes informally refer to "state objects", which are the objects representing user interface state supplied by the author, or alternately the objects created by deserializing (via StructuredDeserialize) serialized state. Pages can add serialized state to the session history. These are then deserialized and returned to the script when the user (or script) goes back in the history, thus enabling authors to use the "navigation" metaphor even in one-page applications. Serialized state is intended to be used for two main purposes: first, storing a
preparsed description of the state in the URL so that in the simple case an author
doesn't have to do the parsing (though one would still need the parsing for handling URLs passed around by users, so it's only a minor optimization). Second, so
that the author can store state that one wouldn't store in the URL because it only applies to the
current An example of the latter would be something like keeping track of the precise coordinate from
which a popup A scroll restoration mode indicates whether the user agent should restore the persisted scroll position (if any) when traversing to an entry. A scroll restoration mode is one of the following:
7.4.1.2 Document stateDocument state holds state inside a session history entry regarding
how to present and, if necessary, recreate, a
User agents may destroy a document and its descendants given the documents of document
states with non-null documents, as long as
the Apart from that restriction, this standard does not specify when user agents should destroy the document stored in a document state, versus keeping it cached. A POST resource has:
A nested history has:
This will later contain ways to identify a child navigable across reloads. Several contiguous entries in a session history can share the same document state. This can occur when the initial entry is
reached via normal navigation, and the following entry is added
via All entries that share the same document state (and that are therefore merely different states of one particular document) are contiguous by construction. A This is the entry that was most recently represented by a given
7.4.1.3 Centralized modifications of session historyTo maintain a single source of truth, all modifications to a traversable navigable's session history entries need to be synchronized. This is especially important due to how session history is influenced by all of the descendant navigables, and thus by multiple event loops. To accomplish this, we use the session history traversal parallel queue structure. A session history traversal parallel queue is very similar to a parallel queue. It has an algorithm set, an ordered set. The items in a session history traversal parallel queue's algorithm set are either algorithm steps, or synchronous navigation steps, which are a particular brand of algorithm steps involving a target navigable (a navigable). To append session history traversal steps to a traversable navigable traversable given algorithm steps steps, append steps to traversable's session history traversal queue's algorithm set. To append session history synchronous navigation steps involving a navigable targetNavigable to a traversable navigable traversable given algorithm steps steps, append steps as synchronous navigation steps targeting target navigable targetNavigable to traversable's session history traversal queue's algorithm set. To start a new session history traversal parallel queue:
Synchronous navigation steps are tagged in the algorithm set to allow them to conditionally "jump the queue". This is handled within apply the history step. Imagine the joint session history depicted by this Jake diagram:
And the following code runs at the top level:
The desired result is:
This isn't straightforward, as the sync navigation wins the race in terms of being observable, whereas the traversal wins the race in terms of queuing steps on the session history traversal parallel queue. To achieve this result, the following happens:
Here is another more complex example, involving races between populating two different
and then call
The end result is:
7.4.1.4 Low-level operations on session historyThis section contains a miscellaneous grab-bag of operations that we perform throughout the standard when manipulating session history. The best way to get a sense of what they do is to look at their call sites. To get session history entries of a navigable navigable:
To get session history entries for the navigation API of a navigable navigable given an integer targetStep:
To clear the forward session history of a traversable navigable navigable:
To get all used history steps that are part of traversable navigable traversable:
7.4.2 NavigationCertain actions cause a navigable to navigate to a new resource. For example, following a hyperlink,
form submission, and the Although in this standard the word "navigation" refers specifically to the navigate algorithm, this doesn't always line up with web developer or user perceptions. For example:
7.4.2.1 Supporting conceptsBefore we can jump into the navigation algorithm itself, we need to establish several important structures that it uses. The source snapshot params struct is used to capture data from a
To snapshot source snapshot params
given a
The target snapshot params struct is used to capture data from a navigable being navigated. Like source snapshot params, it is snapshotted at the beginning of a navigation and used throughout the navigation's lifetime. It has the following items:
To snapshot target snapshot params given a navigable targetNavigable, return a new target snapshot params with:
Much of the navigation process is concerned with determining how to create a new
Once a navigation params struct is created, this standard does not mutate any of its items. They are only passed onward to other algorithms. A navigation ID is a UUID string generated during navigation. It is used to interface with the WebDriver BiDi specification as well as to track the ongoing navigation. [WEBDRIVERBIDI] After
A history handling behavior is a The navigation must be a replace, given a URL url and a
Other cases that often, but not always, force a "
Various parts of the platform track whether a user is involved in a navigation. A user navigation involvement is one of the following:
For convenience at certain call sites, the user navigation
involvement for an
7.4.2.2 Beginning navigationTo navigate a navigable navigable to a
URL url using an optional
7.4.2.3 Ending navigationAlthough the usual cross-document navigation case will first foray into populating a session history entry with a
7.4.2.3.1 The usual cross-document navigation caseTo finalize a cross-document navigation given a navigable navigable, a history handling behavior historyHandling, a user navigation involvement userInvolvement, and a session history entry historyEntry:
7.4.2.3.2 The
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
top | /a | /a#foo | /a#bar | /b |
Also assume that the document state shared by the entries in steps 0, 1, and 2 has a null document, i.e., bfcache is not in play.
Now consider the scenario where we traverse back to step 2, but this time when fetching
/a, the server responds with a `Location`
header pointing to /c. That is, locationURL points to
/c and so we have reached this step instead of breaking out of the loop.
In this case, we replace the document state of the session history entry occupying step 2, but we do not replace the document state of the entries occupying steps 0 and 1. The resulting Jake diagram looks like this:
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
top | /a | /a#foo | /c#bar | /b |
Note that we perform this replacement even if we end up in a redirect chain back to the
original URL, for example if /c itself had a `Location` header pointing to /a. Such a case would
end up like so:
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
top | /a | /a#foo | /a#bar | /b |
If locationURL's scheme is not an HTTP(S) scheme:
Set entry's document state's resource to null.
Set currentURL to locationURL.
Set entry's URL to currentURL.
By the end of this loop we will be in one of these scenarios:
locationURL is failure, because of an unparseable `Location` header.
locationURL is null, either because response is a network
error or because we successfully fetched a non-network error HTTP(S)
response with no `Location` header.
If locationURL is a URL whose scheme is not a fetch scheme, then return a new non-fetch scheme navigation params, with
At this point, request's current URL is the last URL in the redirect chain with a fetch scheme before redirecting to a non-fetch scheme URL. It is this URL's origin that will be used as the initiator origin for navigations to non-fetch scheme URLs.
If any of the following are true:
response is a network error;
locationURL is failure; or
locationURL is a URL whose scheme is a fetch scheme,
then return null.
We allow redirects to non-fetch scheme URLs, but redirects to fetch scheme URLs that aren't HTTP(S) are treated like network errors.
Assert: locationURL is null and response is not a network error.
Let resultPolicyContainer be the result of determining navigation params policy container given response's URL, entry's document state's history policy container, sourceSnapshotParams's source policy container, null, and responsePolicyContainer.
If navigable's container is an
iframe, and response's timing allow passed flag is set, then
set navigable's container's pending resource-timing start time to
null.
If the iframe is allowed to report to resource timing,
we don't need to run its fallback steps as the normal reporting would happen.
Return a new navigation params, with
An element has a browsing context scope origin if its Document's
node navigable is a top-level traversable or if all of its
Document's ancestor navigables have active documents whose origins are the same origin as the element's
node document's origin. If an element
has a browsing context scope origin, then its value is the origin of the element's node document.
This definition is broken and needs investigation to see what it was intended to express: see issue #4703.
To load a document given navigation params
navigationParams, source snapshot params sourceSnapshotParams,
and origin initiatorOrigin, perform the following steps. They return a
Document or null.
Let type be the computed type of navigationParams's response.
If the user agent has been configured to process resources of the given type using some mechanism other than rendering the content in a navigable, then skip this step. Otherwise, if the type is one of the following types:
text/css"text/plain"text/vtt"multipart/x-mixed-replace"multipart/x-mixed-replace document, given navigationParams,
sourceSnapshotParams, and initiatorOrigin.application/pdf"text/pdf"Otherwise, proceed onward.
An explicitly supported XML MIME type is an XML MIME type for which
the user agent is configured to use an external application to render the content, or for which
the user agent has dedicated processing rules. For example, a web browser with a built-in Atom
feed viewer would be said to explicitly support the application/atom+xml MIME
type.
An explicitly supported JSON MIME type is a JSON MIME type for which the user agent is configured to use an external application to render the content, or for which the user agent has dedicated processing rules.
In both cases, the external application or user agent will either display the content inline directly in navigationParams's navigable, or hand it off to external software. Both happen in the steps below.
If, given type, the new resource is to be handled by displaying some sort of inline content, e.g., a native rendering of the content or an error message because the specified type is not supported, then return the result of creating a document for inline content that doesn't have a DOM given navigationParams's navigable, navigationParams's id, navigationParams's navigation timing type, and navigationParams's user involvement.
Otherwise, the document's type is such that the resource will not affect navigationParams's navigable, e.g., because the resource is to be handed to an external application or because it is an unknown type that will be processed by handle as a download. Hand-off to external software given navigationParams's response, navigationParams's navigable, navigationParams's final sandboxing flag set, sourceSnapshotParams's has transient activation, and initiatorOrigin.
Return null.
For both navigation and traversal, once we have an idea of where we want to head to in the
session history, much of the work comes about in applying that notion to the traversable
navigable and the relevant Document. For navigations, this work generally
occurs toward the end of the process; for traversals, it is the beginning.
Ensuring a traversable ends up at the right session history step is particularly complex, as it can involve coordinating across multiple navigable descendants of the traversable, populating them in parallel, and then synchronizing back up to ensure everyone has the same view of the result. This is further complicated by the existence of synchronous same-document navigations being mixed together with cross-document navigations, and how web pages have come to have certain relative timing expectations.
A changing navigable continuation state is used to store information during the apply the history step algorithm, allowing parts of the algorithm to continue only after other parts have finished. It is a struct with:
DocumentAlthough all updates to the traversable navigable end up in the same apply the history step algorithm, each possible entry point comes along with some minor customizations:
To update for navigable creation/destruction given a traversable navigable traversable:
Let step be traversable's current session history step.
Return the result of applying the history
step step to traversable given false, null, null, "none", and null.
To apply the push/replace history step given a non-negative integer step to a traversable navigable traversable, given a history handling behavior historyHandling and a user navigation involvement userInvolvement:
Return the result of applying the history step step to traversable given false, null, null, userInvolvement, and historyHandling.
Apply the push/replace history step never passes source snapshot params or an initiator navigable to apply the history step. This is because those checks are done earlier in the navigation algorithm.
To apply the reload history step to a traversable navigable traversable given user navigation involvement userInvolvement:
Let step be traversable's current session history step.
Return the result of applying the history
step step to traversable given true, null, null,
userInvolvement, and "reload".
Apply the reload history step never passes source snapshot
params or an initiator navigable to apply the history step. This
is because reloading is always treated as if it were done by the navigable itself,
even in cases like parent.location.reload().
To apply the traverse history step given a non-negative integer step to a traversable navigable traversable, with source snapshot params sourceSnapshotParams, navigable initiatorToCheck, and user navigation involvement userInvolvement:
Return the result of applying the history
step step to traversable given true,
sourceSnapshotParams, initiatorToCheck,
userInvolvement, and "traverse".
To resume applying the traverse history step given a non-negative integer
step, a traversable navigable traversable, and user
navigation involvement userInvolvement, apply step to traversable given false, null, null,
userInvolvement, and "traverse".
When resuming a traverse, we are already past the cancelation, initiator, and source snapshot checks, and this traversal has already been determined to be a same-document traversal. Hence, we can pass false and null for those arguments.
Now for the algorithm itself.
To apply the history step given a
non-negative integer step to a traversable navigable
traversable, with boolean checkForCancelation, source snapshot
params-or-null sourceSnapshotParams, navigable-or-null
initiatorToCheck, user navigation involvement userInvolvement,
and NavigationType-or-null navigationType, perform the following steps.
They return "initiator-disallowed", "canceled-by-beforeunload", "canceled-by-navigate", or
"applied".
Assert: This is running within traversable's session history traversal queue.
Let targetStep be the result of getting the used step given traversable and step.
If initiatorToCheck is not null:
Assert: sourceSnapshotParams is not null.
For each navigable of get all
navigables whose current session history entry will change or reload: if
initiatorToCheck is not allowed by sandboxing to navigate
navigable given sourceSnapshotParams, then return "initiator-disallowed".
Let navigablesCrossingDocuments be the result of getting all navigables that might experience a cross-document traversal given traversable and targetStep.
If checkForCancelation is true, and the result of checking if unloading
is canceled given navigablesCrossingDocuments, traversable,
targetStep, and userInvolvement is not "continue",
then return that result.
Let changingNavigables be the result of get all navigables whose current session history entry will change or reload given traversable and targetStep.
Let nonchangingNavigablesThatStillNeedUpdates be the result of getting all navigables that only need history object length/index update given traversable and targetStep.
For each navigable of changingNavigables:
Let targetEntry be the result of getting the target history entry given navigable and targetStep.
Set navigable's current session history entry to targetEntry.
If targetEntry's document is not navigable's active document, then queue a global task on the navigation and traversal task source of navigable's active window to run these steps:
Let navigation be navigable's active window's navigation API.
Set navigation's ongoing navigate event to null.
Set navigation's ongoing API method tracker to null.
This prevents the navigateerror
event and the corresponding signal from firing
as a result of a successful cross-document navigation.
Set navigable's ongoing navigation to "traversal".
Let totalChangeJobs be the size of changingNavigables.
Let completedChangeJobs be 0.
Let changingNavigableContinuations be an empty queue of changing navigable continuation states.
This queue is used to split the operations on changingNavigables into two parts. Specifically, changingNavigableContinuations holds data for the second part.
For each navigable of changingNavigables, queue a global task on the navigation and traversal task source of navigable's active window to run the steps:
This set of steps are split into two parts to allow synchronous navigations to be processed before documents unload. State is stored in changingNavigableContinuations for the second part.
Let displayedEntry be navigable's active session history entry.
Let targetEntry be navigable's current session history entry.
Let changingNavigableContinuation be a changing navigable continuation state with:
If displayedEntry is targetEntry and targetEntry's document state's reload pending is false:
Set changingNavigableContinuation's update-only to true.
Enqueue changingNavigableContinuation on changingNavigableContinuations.
Abort these steps.
This case occurs due to a synchronous navigation which already updated the active session history entry.
Switch on navigationType:
reload"Assert: targetEntry's document state's reload pending is true.
traverse"Assert: targetEntry's document state's ever populated is true.
replace"Assert: targetEntry's step is displayedEntry's step and targetEntry's document state's ever populated is false.
push"Assert: targetEntry's step is displayedEntry's step + 1 and targetEntry's document state's ever populated is false.
Let oldOrigin be targetEntry's document state's origin.
If all of the following are true:
navigationType is "traverse";
navigable is not traversable;
targetEntry is not displayedEntry; and
oldOrigin is the same as displayedEntry's document state's origin,
then:
Let navigation be navigable's active window's navigation API.
Fire a traverse navigate event at
navigation given targetEntry and userInvolvement.
If targetEntry's document is null, or targetEntry's document state's reload pending is true:
Let navTimingType be "back_forward" if
targetEntry's document is null; otherwise
"reload".
Let targetSnapshotParams be the result of snapshotting target snapshot params given navigable.
Let potentiallyTargetSpecificSourceSnapshotParams be sourceSnapshotParams.
If potentiallyTargetSpecificSourceSnapshotParams is null, then set it to the result of snapshotting source snapshot params given navigable's active document.
In this case there is no clear source of the traversal/reload. We treat this situation as if navigable navigated itself, but note that some properties of targetEntry's original initiator are preserved in targetEntry's document state, such as the initiator origin and referrer, which will appropriately influence the navigation.
Set targetEntry's document state's reload pending to false.
Let allowPOST be targetEntry's document state's reload pending.
In parallel, attempt to populate the history entry's document for targetEntry, given navigable, potentiallyTargetSpecificSourceSnapshotParams, targetSnapshotParams, userInvolvement, with allowPOST set to allowPOST and completionSteps set to queue a global task on the navigation and traversal task source given navigable's active window to run afterDocumentPopulated.
Otherwise, run afterDocumentPopulated immediately.
In both cases, let afterDocumentPopulated be the following steps:
If targetEntry's document is null, then set changingNavigableContinuation's update-only to true.
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
navigation API method tracker, 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 vs. 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 why their promises will never
settle and events will never fire.
If targetEntry's document's origin is not same origin with oldOrigin, then set targetEntry's classic history API state to StructuredSerializeForStorage(null).
This clears history state when the origin changed vs a previous load of targetEntry without a redirect occurring. This can happen due to a change in CSP sandbox headers.
If all of the following are true:
navigable's parent is null;
targetEntry's document's browsing context is not an auxiliary browsing context whose opener browsing context is non-null; and
targetEntry's document's origin is not same origin with oldOrigin,
then set targetEntry's document state's navigable target name to the empty string.
Enqueue changingNavigableContinuation on changingNavigableContinuations.
The rest of this job runs later in this algorithm.
Let navigablesThatMustWaitBeforeHandlingSyncNavigation be an empty set.
While completedChangeJobs does not equal totalChangeJobs:
If traversable's running nested apply history step is false:
While traversable's session history traversal queue's algorithm set contains one or more synchronous navigation steps with a target navigable not contained in navigablesThatMustWaitBeforeHandlingSyncNavigation:
Let steps be the first item in traversable's session history traversal queue's algorithm set that is synchronous navigation steps with a target navigable not contained in navigablesThatMustWaitBeforeHandlingSyncNavigation.
Remove steps from traversable's session history traversal queue's algorithm set.
Set traversable's running nested apply history step to true.
Run steps.
Set traversable's running nested apply history step to false.
Synchronous navigations that are intended to take place before this traversal jump the queue at this point, so they can be added to the correct place in traversable's session history entries before this traversal potentially unloads their document. More details can be found here.
Let changingNavigableContinuation be the result of dequeuing from changingNavigableContinuations.
If changingNavigableContinuation is nothing, then continue.
Let displayedDocument be changingNavigableContinuation's displayed document.
Let targetEntry be changingNavigableContinuation's target entry.
Let navigable be changingNavigableContinuation's navigable.
Let (scriptHistoryLength, scriptHistoryIndex) be the result of getting the history object length and index given traversable and targetStep.
These values might have changed since they were last calculated.
Append navigable to navigablesThatMustWaitBeforeHandlingSyncNavigation.
Once a navigable has reached this point in traversal, additionally queued synchronous navigation steps are likely to be intended to occur after this traversal rather than before it, so they no longer jump the queue. More details can be found here.
Let entriesForNavigationAPI be the result of getting session history entries for the navigation API given navigable and targetStep.
If changingNavigableContinuation's update-only is true, or targetEntry's document is displayedDocument:
This is a same-document navigation: we proceed without unloading.
Set navigable's ongoing navigation to null.
This allows new navigations of navigable to start, whereas during the traversal they were blocked.
Queue a global task on the navigation and traversal task source given navigable's active window to perform afterPotentialUnloads.
Otherwise:
Assert: navigationType is not null.
Deactivate displayedDocument, given userInvolvement, targetEntry, navigationType, and afterPotentialUnloads.
In both cases, let afterPotentialUnloads be the following steps:
Let previousEntry be navigable's active session history entry.
If changingNavigableContinuation's update-only is false, then activate history entry targetEntry for navigable.
Let updateDocument be an algorithm step which performs update document for history step application given targetEntry's document, targetEntry, changingNavigableContinuation's update-only, scriptHistoryLength, scriptHistoryIndex, navigationType, entriesForNavigationAPI, and previousEntry.
If targetEntry's document is equal to displayedDocument, then perform updateDocument.
Otherwise, queue a global task on the navigation and traversal task source given targetEntry's document's relevant global object to perform updateDocument.
Increment completedChangeJobs.
Let totalNonchangingJobs be the size of nonchangingNavigablesThatStillNeedUpdates.
This step onwards deliberately waits for all the previous operations to complete, as they include processing synchronous navigations which will also post tasks to update history length and index.
Let completedNonchangingJobs be 0.
Let (scriptHistoryLength, scriptHistoryIndex) be the result of getting the history object length and index given traversable and targetStep.
For each navigable of nonchangingNavigablesThatStillNeedUpdates, queue a global task on the navigation and traversal task source given navigable's active window to run the steps:
Let document be navigable's active document.
Set document's history object's index to scriptHistoryIndex.
Set document's history object's length to scriptHistoryLength.
Increment completedNonchangingJobs.
Wait for completedNonchangingJobs to equal totalNonchangingJobs.
Set traversable's current session history step to targetStep.
Return "applied".
To deactivate a document for a cross-document navigation given a
Document displayedDocument, a user navigation involvement
userNavigationInvolvement, a session history entry targetEntry,
a NavigationType navigationType, and afterPotentialUnloads,
which is an algorithm that receives no arguments:
Let navigable be displayedDocument's node navigable.
Let potentiallyTriggerViewTransition be false.
Let isBrowserUINavigation be true if
userNavigationInvolvement is "browser UI"; otherwise false.
Set potentiallyTriggerViewTransition to the result of calling can navigation trigger a cross-document view-transition? given displayedDocument, targetEntry's document, navigationType, and isBrowserUINavigation.
If potentiallyTriggerViewTransition is false:
Let firePageSwapBeforeUnload be the following step:
Fire the pageswap event given
displayedDocument, targetEntry, navigationType, and
null.
Set navigable's ongoing navigation to null.
This allows new navigations of navigable to start, whereas during the traversal they were blocked.
Unload a document and its descendants given displayedDocument, targetEntry's document, afterPotentialUnloads, and firePageSwapBeforeUnload.
Otherwise, queue a global task on the navigation and traversal task source given navigable's active window to run the steps:
Let proceedWithNavigationAfterViewTransitionCapture be the following step:
Append the following session history traversal steps to navigable's traversable navigable:
Set navigable's ongoing navigation to null.
This allows new navigations of navigable to start, whereas during the traversal they were blocked.
Unload a document and its descendants given displayedDocument, targetEntry's document, and afterPotentialUnloads.
Let viewTransition be the result of setting up a cross-document view-transition given displayedDocument, targetEntry's document, and proceedWithNavigationAfterViewTransitionCapture.
Fire the pageswap event given
displayedDocument, targetEntry, navigationType, and
viewTransition.
If viewTransition is null, then run proceedWithNavigationAfterViewTransitionCapture.
In the case where a view transition started, the view transitions algorithms are responsible for calling proceedWithNavigationAfterViewTransitionCapture.
To fire the pageswap event given a
Document displayedDocument, a session history entry
targetEntry, a NavigationType navigationType, and a
ViewTransition-or-null viewTransition:
Assert: this is running as part of a task queued on displayedDocument's relevant agent's event loop.
Let navigation be displayedDocument's relevant global object's navigation API.
Let activation be null.
If all of the following are true:
targetEntry's document's origin is same origin with displayedDocument's origin; and
targetEntry's document's was created via cross-origin redirects is false, or targetEntry's document's latest entry is not null,
then:
Let destinationEntry be determined by switching on navigationType:
reload"The current entry of navigation
traverse"The NavigationHistoryEntry in navigation's entry list whose session history entry is targetEntry
push"replace"A new NavigationHistoryEntry in displayedDocument's relevant realm with its session history entry set to targetEntry
Set activation to a new NavigationActivation
created in displayedDocument's relevant
realm, with
This means that a cross-origin redirect during a navigation would result in a
null activation in the old document's
PageSwapEvent, unless the new document is being restored from bfcache.
Fire an event named
pageswap at displayedDocument's
relevant global object, using PageSwapEvent with its activation set to activation,
and its viewTransition set to
viewTransition.
To activate history entry session history entry entry for navigable navigable:
Save persisted state to the navigable's active session history entry.
Let newDocument be entry's document.
Assert: newDocument's is initial
about:blank is false, i.e., we never traverse back to the initial about:blank Document because it
always gets replaced when we navigate away from
it.
Set navigable's active session history entry to entry.
Make active newDocument.
Set the initial visibility state of newDocument to navigable's traversable navigable's system visibility state.
To get the used step given a traversable navigable traversable, and a non-negative integer step, perform the following steps. They return a non-negative integer.
Let steps be the result of getting all used history steps within traversable.
Return the greatest item in steps that is less than or equal to step.
This caters for situations where there's no session history entry with step step, due to the removal of a navigable.
To get the history object length and index given a traversable navigable traversable, and a non-negative integer step, perform the following steps. They return a tuple of two non-negative integers.
Let steps be the result of getting all used history steps within traversable.
Let scriptHistoryLength be the size of steps.
It is assumed that step has been adjusted by getting the used step.
Let scriptHistoryIndex be the index of step in steps.
Return (scriptHistoryLength, scriptHistoryIndex).
To get all navigables whose current session history entry will change or reload given a traversable navigable traversable, and a non-negative integer targetStep, perform the following steps. They return a list of navigables.
Let results be an empty list.
Let navigablesToCheck be « traversable ».
This list is extended in the loop below.
For each navigable of navigablesToCheck:
Let targetEntry be the result of getting the target history entry given navigable and targetStep.
If targetEntry is not navigable's current session history entry or targetEntry's document state's reload pending is true, then append navigable to results.
If targetEntry's document is navigable's active document, and targetEntry's document state's reload pending is false, then extend navigablesToCheck with the child navigables of navigable.
Adding child navigables to navigablesToCheck means those navigables will also be checked by this loop. Child navigables are only checked if the navigable's active document will not change as part of this traversal.
Return results.
To get all navigables that only need history object length/index update given a traversable navigable traversable, and a non-negative integer targetStep, perform the following steps. They return a list of navigables.
Other navigables might not be impacted by the traversal. For example, if the response is a 204, the currently active document will remain. Additionally, going 'back' after a 204 will change the current session history entry, but the active session history entry will already be correct.
Let results be an empty list.
Let navigablesToCheck be « traversable ».
This list is extended in the loop below.
For each navigable of navigablesToCheck:
Let targetEntry be the result of getting the target history entry given navigable and targetStep.
If targetEntry is navigable's current session history entry and targetEntry's document state's reload pending is false:
Append navigable to results.
Extend navigablesToCheck with navigable's child navigables.
Adding child navigables to navigablesToCheck means those navigables will also be checked by this loop. child navigables are only checked if the navigable's active document will not change as part of this traversal.
Return results.
To get the target history entry given a navigable navigable, and a non-negative integer step, perform the following steps. They return a session history entry.
Let entries be the result of getting session history entries for navigable.
Return the item in entries that has the greatest step less than or equal to step.
To see why getting the target history entry returns the entry with the greatest step less than or equal to the input step, consider the following Jake diagram:
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
top | /t | /t#foo | ||
frames[0] | /i-0-a | /i-0-b | ||
For the input step 1, the target history entry for the top navigable
is the /t entry, whose step is 0, while the target history entry for the frames[0] navigable is the /i-0-b entry, whose step is 1:
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
top | /t | /t#foo | ||
frames[0] | /i-0-a | /i-0-b | ||
Similarly, given the input step 3 we get the top entry whose step is 3, and the frames[0] entry whose step is 1:
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
top | /t | /t#foo | ||
frames[0] | /i-0-a | /i-0-b | ||
To get all navigables that might experience a cross-document traversal given a traversable navigable traversable, and a non-negative integer targetStep, perform the following steps. They return a list of navigables.
From traversable's session history traversal queue's perspective, these documents are candidates for going cross-document during the traversal described by targetStep. They will not experience a cross-document traversal if the status code for their target document is HTTP 204 No Content.
Note that if a given navigable might experience a cross-document traversal, this algorithm will return navigable but not its child navigables. Those would end up unloaded, not traversed.
Let results be an empty list.
Let navigablesToCheck be « traversable ».
This list is extended in the loop below.
For each navigable of navigablesToCheck:
Let targetEntry be the result of getting the target history entry given navigable and targetStep.
If targetEntry's document is not navigable's active document or targetEntry's document state's reload pending is true, then append navigable to results.
Although navigable's active history entry can change synchronously, the
new entry will always have the same Document, so accessing
navigable's active document is reliable.
Otherwise, extend navigablesToCheck with navigable's child navigables.
Adding child navigables to navigablesToCheck means those navigables will also be checked by this loop. Child navigables are only checked if the navigable's active document will not change as part of this traversal.
Return results.
To update document for history step application given a Document
document, a session history entry entry, a boolean
doNotReactivate, integers scriptHistoryLength and
scriptHistoryIndex, NavigationType-or-null navigationType, an
optional list of session history entries
entriesForNavigationAPI, and an optional session history entry
previousEntryForActivation:
Let documentIsNew be true if document's latest entry is null; otherwise false.
Let documentsEntryChanged be true if document's latest entry is not entry; otherwise false.
Set document's history object's index to scriptHistoryIndex.
Set document's history object's length to scriptHistoryLength.
Let navigation be history's relevant global object's navigation API.
If documentsEntryChanged is true:
Let oldURL be document's latest entry's URL.
Set document's latest entry to entry.
Restore the history object state given document and entry.
If documentIsNew is false:
Assert: navigationType is not null.
Update the navigation API entries for a same-document navigation given navigation, entry, and navigationType.
Fire an event named popstate at document's relevant global
object, using PopStateEvent, with the state attribute initialized to document's
history object's state and hasUAVisualTransition initialized to
true if a visual transition, to display a cached rendered state of the latest
entry, was done by the user agent.
Restore persisted state given entry.
If oldURL's fragment is not
equal to entry's URL's fragment, then queue a global task on the
DOM manipulation task source given document's relevant global
object to fire an event named hashchange at document's relevant global
object, using HashChangeEvent, with the oldURL attribute initialized to the serialization of oldURL and the newURL attribute initialized to the serialization of entry's URL.
Otherwise:
Assert: entriesForNavigationAPI is given.
Restore persisted state given entry.
Initialize the navigation API entries for a new document given navigation, entriesForNavigationAPI, and entry.
If all the following are true:
previousEntryForActivation is given;
navigationType is non-null; and
navigationType is "reload" or
previousEntryForActivation's document is not
document,
then:
If navigation's activation is null, then set navigation's
activation to a new
NavigationActivation object in navigation's
relevant realm.
Let previousEntryIndex be the result of getting the navigation API entry index of previousEntryForActivation within navigation.
If previousEntryIndex is non-negative, then set activation's old entry to navigation's entry list[previousEntryIndex].
Otherwise, if all the following are true:
navigationType is "replace";
previousEntryForActivation's document state's origin is same origin with document's origin; and
previousEntryForActivation's document's
is initial about:blank is
false,
then set activation's old entry
to a new NavigationHistoryEntry in navigation's relevant realm, whose session history entry is
previousEntryForActivation.
Set activation's new entry to navigation's current entry.
Set activation's navigation type to navigationType.
If documentIsNew is true:
Assert: document's during-loading navigation ID for WebDriver BiDi is not null.
Invoke WebDriver BiDi navigation committed with navigable and a
new WebDriver BiDi navigation status whose id is document's during-loading navigation ID for WebDriver BiDi,
status is "committed", and url is document's URL.
Try to scroll to the fragment for document.
At this point scripts may run for the newly-created document document.
Otherwise, if documentsEntryChanged is false and doNotReactivate is false:
Assert: entriesForNavigationAPI is given.
Reactivate document given entry and entriesForNavigationAPI.
documentsEntryChanged can be false for one of two reasons: either we are restoring from bfcache, or we are asynchronously finishing up a synchronous navigation which already synchronously set document's latest entry. The doNotReactivate argument distinguishes between these two cases.
To restore the history object state given Document
document and session history entry entry:
Let targetRealm be document's relevant realm.
Let state be StructuredDeserialize(entry's classic history API state, targetRealm). If this throws an exception, catch it and let state be null.
Set document's history object's state to state.
To make active a Document document:
Let window be document's relevant global object.
Set document's browsing context's active document to document.
Set document's browsing context's
WindowProxy's [[Window]] internal
slot value to window.
Set window's relevant settings object's execution ready flag.
To reactivate a
Document document given a session history entry
reactivatedEntry and a list of session history entries entriesForNavigationAPI:
This algorithm updates document after it has come out of bfcache, i.e., after it has been made fully active again. Other specifications that want to watch for this change to the fully active state are encouraged to add steps into this algorithm, so that the ordering of events that happen in effect of the change is clear.
For each
formControl of form controls in document with an autofill field
name of "off", invoke the reset algorithm for formControl.
If document's suspended timer handles is not empty:
Assert: document's suspension time is not zero.
Let suspendDuration be the current high resolution time minus document's suspension time.
Let activeTimers be document's relevant global object's map of active timers.
For each handle in document's suspended timer handles, if activeTimers[handle] exists, then increase activeTimers[handle] by suspendDuration.
Update the navigation API entries for reactivation given document's relevant global object's navigation API, entriesForNavigationAPI, and reactivatedEntry.
If document's current document readiness is "complete", and document's page showing is false:
Set document's page showing to true.
Set document's has been revealed to false.
Update the visibility state of document to "visible".
Fire a page transition event named pageshow at document's relevant global
object with true.
To try to scroll to the fragment for a Document document,
perform the following steps in parallel:
Wait for an implementation-defined amount of time. (This is intended to allow the user agent to optimize the user experience in the face of performance concerns.)
Queue a global task on the navigation and traversal task source given document's relevant global object to run these steps:
If document has no parser, or its parser has stopped parsing, or the user agent has reason to believe the user is no longer interested in scrolling to the fragment, then abort these steps.
Scroll to the fragment given document.
If document's indicated part is still null, then try to scroll to the fragment for document.
To make document unsalvageable, given a Document
document and a string reason:
Let details be a new not restored reason details whose reason is reason.
Append details to document's bfcache blocking details.
Set document's salvageable state to false.
To build not restored reasons for document state given Document
document:
Let notRestoredReasonsForDocument be a new not restored reasons.
Let container be document's node navigable's container.
If container is an iframe element:
Let src be the empty string.
If container has a src attribute:
Let src be the result of encoding-parsing-and-serializing a
URL given container's src attribute's
value, relative to container's node document.
If src is failure, then set src to container's src attribute's value.
Set notRestoredReasonsForDocument's src to src.
Set notRestoredReasonsForDocument's id to
container's id attribute's value, or the empty string
if it has no such attribute.
Set notRestoredReasonsForDocument's name to
container's name attribute's value, or the
empty string if it has no such attribute.
Set notRestoredReasonsForDocument's reasons to a clone of document's bfcache blocking details.
For each navigable of document's document-tree child navigables:
Let childDocument be navigable's active document.
Build not restored reasons for document state given childDocument.
Append childDocument's not restored reasons to notRestoredReasonsForDocument's children.
Set document's node navigable's active session history entry's document state's not restored reasons to notRestoredReasonsForDocument.
To build not restored reasons for a top-level traversable and its descendants given top-level traversable topLevelTraversable:
Build not restored reasons for document state given topLevelTraversable's active document.
Let crossOriginDescendants be an empty list.
For each childNavigable of topLevelTraversable's active document's descendant navigables:
If childNavigable's active document's origin is not same origin with topLevelTraversable's active document's origin, then append childNavigable to crossOriginDescendants.
Let crossOriginDescendantsPreventsBfcache be false.
For each crossOriginNavigable of crossOriginDescendants:
Let reasonsForCrossOriginChild be crossOriginNavigable's active document's document state's not restored reasons.
If reasonsForCrossOriginChild's reasons is not empty, set crossOriginDescendantsPreventsBfcache to true.
Set reasonsForCrossOriginChild's URL to null.
Set reasonsForCrossOriginChild's reasons to null.
Set reasonsForCrossOriginChild's children to null.
If crossOriginDescendantsPreventsBfcache is true, make document
unsalvageable given topLevelTraversable's active document and "masked".
A Document has a boolean has been revealed, initially false. It is used
to ensure that the pagereveal event is fired once for each
activation of the Document (once when it's rendered initially, and once for each
reactivation).
To reveal a Document document:
If document's has been revealed is true, then return.
Set document's has been revealed to true.
Let transition be the result of resolving inbound cross-document view-transition for document.
Fire an event named
pagereveal at document's
relevant global object, using PageRevealEvent with its viewTransition set to
transition.
If transition is not null:
Prepare to run script given document's relevant settings object.
Activate transition.
Clean up after running script given document's relevant settings object.
Activating a view transition might resolve/reject promises, so by wrapping the activation with prepare/cleanup we ensure those promises are handled before the next rendering step.
Though pagereveal is guaranteed to be fired
during the first update the rendering step that displays an up-to-date version of the
page, user agents are free to display a cached frame of the page before firing it. This prevents
the presence of a pagereveal handler from delaying the
presentation of such cached frame.
To scroll to the fragment given a
Document document:
If document's indicated part is null, then set document's target element to null.
Otherwise, if document's indicated part is top of the document:
Set document's target element to null.
Scroll to the beginning of the document for document. [CSSOMVIEW]
Return.
Otherwise:
Assert: document's indicated part is an element.
Let target be document's indicated part.
Set document's target element to target.
Run the ancestor revealing algorithm on target.
Scroll target into view, with behavior set to "auto", block set to "start", and inline set to "nearest". [CSSOMVIEW]
Run the focusing steps for target, with the
Document's viewport as the fallback target.
Move the sequential focus navigation starting point to target.
A Document's indicated part is
the one that its URL's fragment identifies, or null if the fragment does not
identify anything. The semantics of the fragment in
terms of mapping it to a node is defined by the specification that defines the MIME
type used by the Document (for example, the processing of fragments for XML MIME
types is the responsibility of RFC7303). [RFC7303]
There is also a target element for each Document, which is used in
defining the :target pseudo-class and is updated by the
above algorithm. It is initially null.
For an HTML document document, its indicated part is the result of selecting the indicated part given document and document's URL.
To select the indicated part given a Document document and a
URL url:
If document's URL does not equal url with exclude fragments set to true, then return null.
Let fragment be url's fragment.
If fragment is the empty string, then return the special value top of the document.
Let potentialIndicatedElement be the result of finding a potential indicated element given document and fragment.
If potentialIndicatedElement is not null, then return potentialIndicatedElement.
Let fragmentBytes be the result of percent-decoding fragment.
Let decodedFragment be the result of running UTF-8 decode without BOM on fragmentBytes.
Set potentialIndicatedElement to the result of finding a potential indicated element given document and decodedFragment.
If potentialIndicatedElement is not null, then return potentialIndicatedElement.
If decodedFragment is an ASCII case-insensitive match for the
string top, then return the top of the document.
Return null.
To find a potential indicated element given a Document
document and a string fragment, run these steps:
If there is an element in the document tree whose root is document and that has an ID equal to fragment, then return the first such element in tree order.
If there is an a element in the document
tree whose root is document that has a name attribute whose value is equal to fragment, then
return the first such element in tree order.
Return null.
To save persisted state to a session history entry entry:
Set the scroll position data of entry to contain the scroll positions for all of entry's document's restorable scrollable regions.
Optionally, update entry's persisted user state to reflect any state that the user agent wishes to persist, such as the values of form fields.
To restore persisted state from a session history entry entry:
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.
The user agent not restoring scroll positions does not imply that scroll positions will be left at any particular value (e.g., (0,0)). The actual scroll position depends on the navigation type and the user agent's particular caching strategy. So web applications cannot assume any particular scroll position but rather are urged to set it to what they want it to be.
If suppress normal scroll restoration during ongoing navigation is
true, then restoring scroll position data
might still happen at a later point, as part of finishing the relevant NavigateEvent, or via a
navigateEvent.scroll() method call.
Optionally, update other aspects of entry's document and its rendering, for instance values of form fields, that the user agent had previously recorded in entry's persisted user state.
This can even include updating the dir attribute
of textarea elements or input elements whose type attribute is in the Text, Search,
Telephone, URL, or Email
state, if the persisted state includes the directionality of user input in such controls.
Restoring the value of form controls as part of this process does not fire any
input or change events, but
can trigger the formStateRestoreCallback of form-associated custom elements.
Each Document has a boolean has been scrolled by the user, initially
false. If the user scrolls the document, the user agent must set that document's has been
scrolled by the user to true.
The restorable scrollable regions of a Document document
are document's viewport, and all of document's scrollable
regions excepting any navigable containers.
Child navigable scroll restoration is handled as part of state
restoration for the session history entry for those navigables' Documents.
To restore scroll position data given a session history entry entry:
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 entry's 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. [CSSSCROLLANCHORING]