1. 7.4 Navigation and session history
      1. 7.4.1 Session history
        1. 7.4.1.1 Session history entries
        2. 7.4.1.2 Document state
        3. 7.4.1.3 Centralized modifications of session history
        4. 7.4.1.4 Low-level operations on session history
      2. 7.4.2 Navigation
        1. 7.4.2.1 Supporting concepts
        2. 7.4.2.2 Beginning navigation
        3. 7.4.2.3 Ending navigation
          1. 7.4.2.3.1 Cross-document navigations
          2. 7.4.2.3.2 Fragment navigations
          3. 7.4.2.3.3 Non-fetch schemes and external software
        4. 7.4.2.4 Preventing navigation
      3. 7.4.3 Reloading and traversing
      4. 7.4.4 Non-fragment synchronous "navigations"
      5. 7.4.5 Populating a session history entry
        1. 7.4.5.1 The usual case
        2. 7.4.5.2 The javascript: URL special case
      6. 7.4.6 Applying the history step
        1. 7.4.6.1 Updating the traversable
        2. 7.4.6.2 Updating the document
        3. 7.4.6.3 Scrolling to a fragment
        4. 7.4.6.4 Persisted history entry state
      7. 7.4.7 Implementation notes for session history
    2. 7.5 Document lifecycle
      1. 7.5.1 Shared document creation infrastructure
      2. 7.5.2 Loading HTML documents
      3. 7.5.3 Loading XML documents
      4. 7.5.4 Loading text documents
      5. 7.5.5 Loading multipart/x-mixed-replace documents
      6. 7.5.6 Loading media documents
      7. 7.5.7 Loading a document for inline content that doesn't have a DOM
      8. 7.5.8 Finishing the loading process
      9. 7.5.9 Unloading documents
      10. 7.5.10 Destroying documents
      11. 7.5.11 Aborting a document load
    3. 7.6 The `X-Frame-Options` header
    4. 7.7 The `Refresh` header

7.4 Navigation and session history

Welcome 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 history

7.4.1.1 Session history entries

A 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 Document instance and it would have to be reconstructed if a new Document were opened.

An example of the latter would be something like keeping track of the precise coordinate from which a popup div was made to animate, so that if the user goes back, it can be made to animate to the same location. Or alternatively, it could be used to keep a pointer into a cache of data that would be fetched from the server based on the information in the URL, so that when going back and forward, the information doesn't have to be fetched again.


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:

"auto"
The user agent is responsible for restoring the scroll position upon navigation.
"manual"
The page is responsible for restoring the scroll position and the user agent does not attempt to do so automatically
7.4.1.2 Document state

Document state holds state inside a session history entry regarding how to present and, if necessary, recreate, a Document. It has:

User agents may destroy the documents of document states with non-null documents, as long as the Document is not fully active.

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 nested 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 history.pushState(). Or it can occur via navigation to a fragment.

All entries that share the same document state (and that are therefore merely different states of one particular document) are contiguous by construction.


A Document has a latest entry, a session history entry or null.

This is the entry that was most recently represented by a given Document. A single Document can represent many session history entries over time, as many contiguous session history entries can share the same document state as explained above.

7.4.1.3 Centralized modifications of session history

To 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 to a traversable navigable traversable given algorithm steps steps and a navigable targetNavigable, append steps as synchronous navigation steps targeting target navigable targetNavigable to traversable's session history traversal queue's algorithm set.

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.

For example, imagine a flattened history sequence like this:

  1. Top level: "/1"

  2. Top level: "/2" - current entry

And the following is called at the top level:

history.back();
location.href = '#foo';

The desired result is:

  1. Top level: "/1" - current entry.

  2. Top level: "/2"

  3. Top level: "/2#foo"

This isn't straight forward, 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:

  1. history.back() queues steps.

  2. location.href = '#foo' synchronously changes the active history entry to "#foo", and queues steps tagged as synchronous navigation steps.

  3. The history.back() steps run:

    1. The target history step is step 0 in the flattened history sequence ("/1").

    2. The "/1" history entry has its Document populated if necessary.

      Meanwhile, the queue is checked for synchronous navigation steps. The "#foo" steps now run, and block the traversal from performing effects like unloading documents and switching active history entries:

      1. The "#foo" entry is added to the history tree at the current flattened history sequence step (1) plus 1 (2).

      2. Traverse to "#foo", which includes dispatching events like hashchange.

      Now the "/1" history entry is populated.

    3. "/2" unloads.

    4. "/1" becomes the active entry and 0 becomes the current step.

Another more complex example, starting with these flattened history steps:

  1. Top level: "/1" with two iframes at "/i-1-1" and "/i-2-1" respectively.

  2. Iframe 1: "/i-1-2"

  3. Iframe 2: "/i-2-2" - current entry

If history.go(-2) is called:

  1. history.go(-2) queues steps, and those steps run:

    1. The target flattened history step is 0.

    2. Meanwhile, the queue is being checked for synchronous navigation steps. There aren't any right now.

    3. In parallel, the fetches are made for the two iframes, to "/i-1-1" and "/i-2-1" respectively.

    4. In the race, "/i-1-1" wins. It becomes the active entry for the first iframe.

    5. The fetch for the 2nd iframe continues.

    6. Code in the first iframe ("/i-1-1") calls location.href = '#foo', which queues synchronous navigation steps.

    7. This item is seen, but it doesn't jump the queue like in the previous example. This is because its navigable has already been altered as part of this traversal. The current flattened step is still 2, and pushing the new history entry to step 3 doesn't make sense.

    8. "/i-2-1" is finally fetched, and becomes the active history entry.

    9. The flattened history step becomes 0.

  2. Now we can process the steps for the synchronous navigation:

    1. The "#foo" entry is added to the history tree at the current flattened history sequence step (0) plus 1 (1). This also clears existing forward history.

    2. Traverse to "#foo", which includes dispatching events like hashchange.

    3. The flattened history step becomes 0.

The flattened result is:

  1. Top level: "/1" with two iframes at "/i-1-1" and "/i-2-1" respectively.

  2. Iframe 1: "/i-1-1#foo" - current entry

To start a new session history traversal parallel queue:

  1. Let sessionHistoryTraversalQueue be a new session history traversal parallel queue.

  2. Run the following steps in parallel:

    1. While true:

      1. If sessionHistoryTraversalQueue's algorithm set is empty, then continue.

      2. Let steps be sessionHistoryTraversalQueue's algorithm set[0].

      3. Remove sessionHistoryTraversalQueue's algorithm set[0].

      4. Run steps.

  3. Return sessionHistoryTraversalQueue.

7.4.1.4 Low-level operations on session history

This 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 for a navigable, navigable:

  1. Let traversable be navigable's traversable navigable.

  2. Assert: This is running within traversable's session history traversal queue.

  3. If navigable is traversable, return traversable's session history entries.

  4. Let docStates be an empty ordered set of document states.

  5. For each entry of traversable's session history entries, append entry's document state to docStates.

  6. For each docState of docStates:

    1. For each nestedHistory of docState's nested histories:

      1. If nestedHistory's id equals navigable's id, return nestedHistory's entries.

      2. For each entry of nestedHistory's entries, append entry's document state to docStates.

  7. Assert: This step is not reached.

To clear the forward session history of a traversable navigable navigable:

  1. Assert: This is running within navigable's session history traversal queue.

  2. Let step be the navigable's current session history step.

  3. Let entryLists be the ordered set « navigable's session history entries ».

  4. For each entryList of entryLists:

    1. Remove every session history entry from entryList that has a step greater than step.

    2. For each entry of entryList:

      1. For each nestedHistory of entry's document state's nested histories, append nestedHistory's entries list to entryLists.

To get all used history steps that are part of traversable navigable traversable:

  1. Assert: This is running within traversable's session history traversal queue.

  2. Let steps be an empty ordered set of non-negative integers.

  3. Let entryLists be the ordered set « traversable's session history entries ».

  4. For each entryList of entryLists:

    1. For each entry of entryList:

      1. Append entry's step to steps.

      2. For each nestedHistory of entry's document state's nested histories, append nestedHistory's entries list to entryLists.

  5. Return steps, sorted.

To apply pending history changes to a traversable navigable traversable with optional boolean checkForUserCancelation (default false) and an optional number unsafeNavigationStartTime:

  1. Let targetStep be traversable's current session history step.

  2. Apply the history step targetStep to traversable with checkForUserCancelation set to checkForUserCancelation and unsafeNavigationStartTime set to unsafeNavigationStartTime if it was given.

Certain actions cause a navigable to navigate to a new resource. 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.

For example, following a hyperlink, form submission, and the window.open() and location.assign() methods can all cause navigation.

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, the URL and history update steps are often used during so-called "single-page app navigations" or "same-document navigations". And although fragment navigations are always done through the navigation algorithm, a user might perceive them as more like jumping around a single page, than as a true navigation.

Before 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 Document initiating a navigation. It is snapshotted at the beginning of a navigation and used throughout the navigation's lifetime. It has the following items:

has transient activation
a boolean
sandboxing flags
a sandboxing flag set
allows downloading
a boolean
fetch client
an environment settings object, only to be used as a request client
origin
an origin
source policy container
a policy container

To snapshot source snapshot params given a Document sourceDocument, return a new source snapshot params with

has transient activation
true if sourceDocument's relevant global object has transient activation; otherwise false
sandboxing flags
sourceDocument's active sandboxing flag set
allows downloading
false if sourceDocument's browsing context's sandboxing flags has the sandboxed downloads browsing context flag set; otherwise true
fetch client
sourceDocument's relevant settings object
origin
sourceDocument's origin
source policy container
sourceDocument's policy container

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:

sandboxing flags
a sandboxing flag set

To snapshot target snapshot params given a navigable targetNavigable, return a new target snapshot params with sandboxing flags set to targetNavigable's active browsing context's sandboxing flags.


Much of the navigation process is concerned with determining how to create a new Document, which ultimately happens in the create and initialize a Document object algorithm. The parameters to that algorithm are tracked via a navigation params struct, which has the following items:

id
null or a navigation ID
request
null or a request that started the navigation
response
a response that ultimately was navigated to (potentially a network error)
origin
an origin to use for the new Document
policy container
a policy container to use for the new Document
final sandboxing flag set
a sandboxing flag set to impose on the new Document
cross-origin opener policy
a cross-origin opener policy to use for the new Document
COOP enforcement result
a cross-origin opener policy enforcement result, used for reporting and potentially for causing a browsing context group switch
reserved environment
null or an environment reserved for the new Document
browsing context
the browsing context to be navigated (unless a browsing context group switch occurs)
navigation timing type
a NavigationTimingType used for creating the navigation timing entry for the new Document
has cross-origin redirects
a boolean
process response end of body
an algorithm expecting a response
unsafe start time
a number, representing a value of the unsafe shared current time when the navigation has started
uncommitted preloads
null or a list of algorithms representing early preloaded links, to be committed once the Document has been created

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]


DOMENIC-TODO: this could probably move somewhere more general since it isn't only for navigation, I think?

After Document creation, the relevant traversable navigable's session history gets updated. A history handling behavior is used to track the desired type of session history update throughout the navigation process. It is one of the following:

"push"
A regular navigation which adds a new session history entry, and will clear the forward session history.
"replace"
A navigation that will replace the active session history entry.
7.4.2.2 Beginning navigation

Each navigable has an ongoing navigation, which is a navigation ID, "traversal", or null, initially null. It is used to track navigation aborting and to prevent any navigations from taking place during traversal.

To navigate a navigable navigable to a URL url using a Document sourceDocument, with an optional POST resource or string documentResource (default null), an optional response response (default null), an optional boolean exceptionsEnabled (default false), an optional history handling behavior historyHandling (default "push"), an optional string cspNavigationType (default "other"), an optional referrer policy referrerPolicy (default the empty string), and an optional processResponseEndOfBody, which is an algorithm receiving a response (default an algorithm that does nothing):

  1. Let sourceSnapshotParams be the result of snapshotting source snapshot params given sourceDocument.

  2. Let unsafeNavigationStartTime be the unsafe shared current time.

  3. Let navigationId be the result of generating a random UUID. [UUID]

  4. If the surrounding agent' is equal to navigable's active document's relevant agent, then continue these steps. Otherwise, queue a global task on the navigation and traversal task source given navigable's active window to continue these steps.

    We do this because we are about to look at a lot of properties of navigable's active document, which are in theory only accessible over in the appropriate event loop. (But, we do not want to unconditionally queue a task, since — for example — same-event-loop fragment navigations need to take effect synchronously.)

    Another implementation strategy would be to replicate the relevant information across event loops, or into a canonical "browser process", so that it can be consulted without queueing a task. This could give different results than what we specify here in edge cases, where the relevant properties have changed over in the target event loop but not yet been replicated. Further testing is needed to determine which of these strategies best matches browser behavior, in such racy edge cases.

  5. If navigable's active document's unload counter is greater than 0, then invoke WebDriver BiDi navigation failed with a WebDriver BiDi navigation status whose id is navigationId, status is "canceled", and url is url, and return.

  6. If navigable's parent is non-null, then set navigable's is delaying load events to true.

  7. Let targetBrowsingContext be navigable's active browsing context.

  8. Let targetSnapshotParams be the result of snapshotting target snapshot params given navigable.

  9. Let activeDocumentAtNavigationStartTime be navigable's active document.

  10. Invoke WebDriver BiDi navigation started with targetBrowsingContext, and a new WebDriver BiDi navigation status whose id is navigationId, url is url, and status is "pending".

  11. In parallel, run these steps:

    1. If navigable's ongoing navigation is "traversal", then:

      1. Invoke WebDriver BiDi navigation failed with targetBrowsingContext and a new WebDriver BiDi navigation status whose id is navigationId, status is "canceled", and url is url.

      2. Abort these steps.

      Any attempts to navigate a navigable that is currently traversing are ignored.

    2. Set navigable's ongoing navigation to navigationId.

      This will have the effect of aborting other ongoing navigations of navigable, since at certain points during navigation changes to the ongoing navigation will cause further work to be abandoned.

    3. Let unloadPromptResult be the result of checking if unloading is user-canceled for navigable's active document's inclusive descendant navigables.

    4. If unloadPromptResult is "refuse", or navigable's ongoing navigation is no longer navigationId, then:

      1. Invoke WebDriver BiDi navigation failed with targetBrowsingContext and a new WebDriver BiDi navigation status whose id is navigationId, status is "canceled", and url is url.

      2. Abort these steps.

    5. If unloadPromptResult is "confirm", then set unsafeNavigationStartTime to the unsafe shared current time.

    6. Queue a global task on the navigation and traversal task source given navigable's active window to abort navigable's active document.

    7. Let documentState be a new document state with

      request referrer policy
      referrerPolicy
      resource
      documentResource
      navigable target name
      navigable's target name

      The navigable target name can get cleared under various conditions later in the navigation process, before the document state is finalized.

    8. If url is about:blank, then set documentState's origin to sourceSnapshotParams's origin.

    9. Otherwise, if url is about:srcdoc, then set documentState's origin to navigable's parent's active document's origin.

    10. Let historyEntry be a new session history entry, with its URL set to url and its document state set to documentState.

    11. If url's scheme is "javascript", then queue a global task on the DOM manipulation task source given navigable's active window to run these steps:

      1. Populate a history entry's document by evaluating a javascript: URL for historyEntry, given navigable, unsafeNavigationStartTime, sourceSnapshotParams, cspNavigationType, and url.

      2. Queue a task on the navigable's traversable's session history traversal queue to finalize a cross-document navigation with navigable, historyHandling, and historyEntry.

      3. Return.

      So for example a javascript: URL in an href attribute of an a element would only be evaluated when the link was followed, while such a URL in the src attribute of an iframe element would be evaluated in the context of the iframe's nested navigable's active window when the iframe is being set up. Once evaluated, its return value (if it was a string) would replace that navigable's active document, thus also changing the corresponding Window object.

    12. Let navigationParams be null.

    13. If response is non-null:

      The navigate algorithm is only supplied with a response as part of the object and embed processing models, or for processing parts of multipart/x-mixed-replace responses after the initial response.

      1. Let policyContainer be the result of determining navigation params policy container given response's URL, null, a clone of the sourceDocument's policy container, targetBrowsingContext's parent browsing context's active document's policy container, and null.

      2. Let finalSandboxFlags be the union of targetSnapshotParams's sandboxing flags and policyContainer's CSP list's CSP-derived sandboxing flags.

      3. Let responseOrigin be the result of determining the origin given response's URL, finalSandboxFlags, sourceSnapshotParams's origin, and null.

      4. Let coop be a new cross-origin opener policy.

      5. Let coopEnforcementResult be a new cross-origin opener policy enforcement result whose needs a browsing context group switch is false, would need a browsing context group switch due to report-only is false, url is response's URL, origin is responseOrigin, cross-origin opener policy is coop, and current context is navigation source is false.

      6. Set navigationParams to a new navigation params, with

        id
        navigationId
        request
        null
        response
        response
        origin
        responseOrigin
        policy container
        policyContainer
        final sandboxing flag set
        finalSandboxFlags
        cross-origin opener policy
        coop
        COOP enforcement result
        coopEnforcementResult
        reserved environment
        null
        browsing context
        targetBrowsingContext
        navigation timing type
        "navigate"
        has cross-origin redirects
        false
        process response end of body
        processResponseEndOfBody
        unsafe start time
        unsafeNavigationStartTime
        uncommitted preloads
        null
    14. Attempt to populate the history entry's document for historyEntry, given navigable, "navigate", unsafeNavigationStartTime, sourceSnapshotParams, targetSnapshotParams, navigationId, navigationParams, cspNavigationType, with allowPOST set to true, processResponseEndOfBody set to processResponseEndOfBody, and completionSteps set to the following steps:

      1. If navigable's active document is not activeDocumentAtNavigationStartTime, then abort these steps.

      2. Queue a task on the navigable's traversable's session history traversal queue to finalize a cross-document navigation given navigable, historyHandling, and historyEntry.

7.4.2.3 Ending navigation

Although a cross-document navigation will first foray into populating a session history entry with a Document, all navigations that don't get aborted will ultimately end up calling into one of the below algorithms.

7.4.2.3.1 Cross-document navigations

To finalize a cross-document navigation given a navigable navigable, history handling behavior historyHandling, and session history entry historyEntry:

  1. Set navigable's is delaying load events to false.

  2. If historyEntry's document is null, then return.

    This means that attempting to populate the history entry's document ended up not creating a document, e.g. due to a 204 No Content response.

  3. Assert: This algorithm is running on navigable's traversable navigable's session history traversal queue.

  4. Set the historyEntry's document state's ever populated to true.

    DOMFAROLINO-TODO: Should the above happen elsewhere? When the document is actually populated?

  5. If all of the following are true:

    then set historyEntry's document state's navigable target name to the empty string.

  6. Let entryToReplace be navigable's active session history entry if historyHandling is "replace", otherwise null.

  7. Let traversable be navigable's traversable navigable.

  8. Let targetStep be null.

  9. Let targetEntries be the result of getting session history entries for navigable.

  10. If entryToReplace is null, then:

    1. Clear the forward session history of traversable.

    2. Set targetStep to traversable's current session history step + 1.

    3. Set historyEntry's step to targetStep.

    4. Append historyEntry to targetEntries.

    Otherwise:

    1. Replace entryToReplace with historyEntry in targetEntries.

    2. Set historyEntry's step to entryToReplace's step.

    3. Set targetStep to traversable's current session history step.

  11. Apply the history step targetStep to traversable.

7.4.2.3.2 Fragment navigations

To navigate to a fragment given a navigable navigable, a URL url, a history handling behavior historyHandling, and a navigation ID navigationId:

  1. Let historyEntry be a new session history entry, with

    URL
    url
    document state
    navigable's active session history entry's document state
    serialized state
    StructuredSerializeForStorage(null)
    scroll restoration mode
    navigable's active session history entry's scroll restoration mode
  2. Let entryToReplace be navigable's active session history entry if historyHandling is "replace", otherwise null.

  3. Let history be navigable's active document's history object.

  4. Let scriptHistoryIndex be history's index.

  5. Let scriptHistoryLength be history's length.

  6. If historyHandling is "push", then:

    1. Set history's state to null.

    2. Increment scriptHistoryIndex.

    3. Set scriptHistoryLength to scriptHistoryIndex + 1.

  7. Set navigable's active session history entry to historyEntry.

  8. Update document for history step application given navigable's active document, historyEntry, true, scriptHistoryIndex, and scriptHistoryLength.

    This algorithm will be called twice as a result of a single fragment navigation: once synchronously, where best-guess values scriptHistoryIndex and scriptHistoryLength are set, history.state is nulled out, and various events are fired; and once asynchronously, where the final values for index and length are set, history.state remains untouched, and no events are fired.

  9. Scroll to the fragment given navigable's active document.

    If the scrolling fails because the Document is new and the relevant ID has not yet been parsed, then the second asynchronous call to update document for history step application will take care of scrolling.

  10. Let traversable be navigable's traversable navigable.

  11. Append the following session history synchronous navigation steps involving navigable to traversable:

    1. Finalize a same-document navigation given traversable, navigable, historyEntry, and entryToReplace.

    2. Invoke WebDriver BiDi fragment navigated with browsingContext, and a new WebDriver BiDi navigation status whose id is navigationId, url is resource's url, and status is "complete".

To finalize a same-document navigation given a traversable navigable traversable, a navigable targetNavigable, a session history entry targetEntry, and session history entry-or-null entryToReplace:

This is used by both fragment navigations and by the URL and history update steps, which are the only synchronous updates to session history. By virtue of being synchronous, those algorithms are performed outside of the top-level traversable's session history traversal queue. This puts them out of sync with the top-level traversable's current session history step, so this algorithm is used to resolve conflicts due to race conditions.

  1. Assert: this algorithm is running on traversable's session history traversal queue.

  2. If targetNavigable's active session history entry is not targetEntry, then return.

  3. Let targetStep be null.

  4. Let targetEntries be the result of getting session history entries for targetNavigable.

  5. If entryToReplace is null, then:

    1. Clear the forward session history of traversable.

    2. Set targetStep to traversable's current session history step + 1.

    3. Set targetEntry's step to targetStep.

    4. Append targetEntry to targetEntries.

    Otherwise:

    1. Replace entryToReplace with targetEntry in targetEntries.

    2. Set targetEntry's step to entryToReplace's step.

    3. Set targetStep to traversable's current session history step.

  6. Apply the history step targetStep to traversable.

    This is done even for "replace" navigations, as it resolves race conditions across multiple synchronous navigations.

7.4.2.3.3 Non-fetch schemes and external software

To attempt to create a URL scheme document, given a URL url, a browsing context browsingContext, a sandboxing flag set sandboxFlags, a navigation ID navigationId, a NavigationTimingType navTimingType, and a source snapshot params sourceSnapshotParams:

  1. If url is to be handled using a mechanism that does not affect browsingContext, e.g., because url's scheme is handled externally, then:

    1. Hand-off to external software given url, browsingContext, sandboxFlags, and sourceSnapshotParams.

    2. Return null.

  2. Handle url by displaying some sort of inline content, e.g., an error message because the specified scheme is not one of the supported protocols, or an inline prompt to allow the user to select a registered handler for the given scheme. Return the result of displaying the inline content given browsingContext, navigationId, and navTimingType.

    In the case of a registered handler being used, navigate will be invoked with a new URL.

To hand-off to external software given a URL or response resource, a browsing context browsingContext, a sandboxing flag set sandboxFlags, and a source snapshot params sourceSnapshotParams, user agents should:

  1. If all of the following conditions hold:

    then return without invoking the external software package.

    Navigation inside an iframe toward external software can be seen by users as a new popup or a new top-level navigation. That's why its is allowed in sandboxed iframe only when one of allow-popups, allow-top-navigation, allow-top-navigation-by-user-activation, or allow-top-navigation-to-custom-protocols is specified.

  2. Perform the appropriate handoff of resource while attempting to mitigate the risk that this is an attempt to exploit the target software. For example, user agents could prompt the user to confirm that sourceSnapshotParams's origin is to be allowed to invoke the external software in question. In particular, if sourceSnapshotParams's has transient activation is false, then the user agent should not invoke the external software package without prior user confirmation.

    For example, there could be a vulnerability in the target software's URL handler which a hostile page would attempt to exploit by tricking a user into clicking a link.

7.4.2.4 Preventing navigation

A couple of scenarios can intervene early in the navigation process and put the whole thing to a halt. This can be especially exciting when multiple navigables are navigating at the same time, due to a session history traversal.

A navigable source is allowed by sandboxing to navigate a second navigable target, given a source snapshot params sourceSnapshotParams, if the following steps return true:

  1. If source is target, then return true.

  2. If source is an ancestor of target, then return true.

  3. If target is an ancestor of source, then:

    1. If target is not a top-level traversable, then return true.

    2. If sourceSnapshotParams's has transient activation is true, and sourceSnapshotParams's sandboxing flags's sandboxed top-level navigation with user activation browsing context flag is set, then return false.

    3. If sourceSnapshotParams's has transient activation is false, and sourceSnapshotParams's sandboxing flags's sandboxed top-level navigation without user activation browsing context flag is set, then return false.

    4. Return true.

  4. If target is a top-level traversable:

    1. If source is the one permitted sandboxed navigator of target, then return true.

    2. If sourceSnapshotParams's sandboxing flags's sandboxed navigation browsing context flag is set, then return false.

    3. Return true.

  5. If sourceSnapshotParams's sandboxing flags's sandboxed navigation browsing context flag is set, then return false.

  6. Return true.

To check if unloading is user-canceled for list of navigables navigables:

  1. Let documents be the active document of each item in navigables.

  2. Let unloadPromptShown be false.

  3. Let unloadPromptCanceled be false.

  4. Let totalTasks be the size of documents.

  5. Let completedTasks be 0.

  6. For each document of documents, queue a global task on the navigation and traversal task source given document's relevant global object to run the steps:

    1. Increase the document's unload counter by 1.

    2. Increase the event loop's termination nesting level by 1.

    3. Let event be the result of creating an event using BeforeUnloadEvent.

    4. Initialize event's type attribute to beforeunload and its cancelable attribute true.

    5. Dispatch event at document's relevant global object.

    6. Decrease the event loop's termination nesting level by 1.

    7. If all of the following are true:

      then:

      1. Set unloadPromptShown to true.

      2. Ask the user to confirm that they wish to unload the document, and pause while waiting for the user's response.

        The message shown to the user is not customizable, but instead determined by the user agent. In particular, the actual value of the returnValue attribute is ignored.

      3. If the user did not confirm the page navigation, set unloadPromptCanceled to true.

    8. Decrease the document's unload counter by 1.

    9. Increment completedTasks.

  7. Wait for completedTasks to be totalTasks.

  8. If unloadPromptCanceled is true, then return "refuse".

  9. If unloadPromptShown is true, then return "confirm".

  10. Return "no-prompt".

7.4.3 Reloading and traversing

To reload a navigable navigable:

  1. Let unsafeNavigationStartTime be the unsafe shared current time.

  2. Set navigable's active session history entry's document state's reload pending to true.

  3. Let traversable be navigable's traversable navigable.

  4. Append the following session history traversal steps to traversable:

    1. Apply pending history changes to traversable with true and unsafeNavigationStartTime.

      It is intentional that the resulting call to apply the history step does not pass sourceSnapshotParams or initiatorToCheck. Reloading is always treated as if it were done by navigable itself, even in cases like parent.location.reload().

To traverse the history by a delta given a traversable navigable traversable, an integer delta, and an optional Document sourceDocument:

  1. Let unsafeNavigationStartTime be the unsafe shared current time.

  2. Let sourceSnapshotParams and initiatorToCheck be null.

  3. If sourceDocument is given, then:

    1. Set sourceSnapshotParams to the result of snapshotting source snapshot params given sourceDocument.

    2. Set initiatorToCheck to sourceDocument's node navigable.

  4. Append the following session history traversal steps to traversable:

    1. Let allSteps be the result of getting all used history steps for traversable.

    2. Let currentStepIndex be the index of traversable's current session history step within allSteps.

    3. Let targetStepIndex be currentStepIndex plus delta.

    4. If allSteps[targetStepIndex] does not exist, then abort these steps.

    5. Apply the history step allSteps[targetStepIndex] to traversable, with checkForUserCancelation set to true, unsafeNavigationStartTime set to unsafeNavigationStartTime, sourceSnapshotParams set to sourceSnapshotParams, and initiatorToCheck set to initiatorToCheck.

When the user navigates through a top-level traversable, e.g. using a browser's back and forward buttons, the user agent must traverse the history by a delta with the top-level traversable being operated on and a delta equivalent to the action specified by the user.

Apart from the navigate algorithm, session history entries can be pushed or replaced via one more mechanism, the URL and history update steps. The most well-known callers of these steps are the history.replaceState() and history.pushState() APIs, but various other parts of the standard also need to perform updates to the active history entry, and they use these steps to do so.

The URL and history update steps, given a Document document, a URL newURL, an optional serialized state-or-null serializedData (default null), and an optional history handling behavior historyHandling (default "replace"), are:

  1. Let navigable be document's node navigable.

  2. Let activeEntry be navigable's active session history entry.

  3. Let newEntry be a new session history entry, with

    URL
    newURL
    serialized state
    if serializedData is not null, serializedData; otherwise activeEntry's serialized state
    document state
    activeEntry's document state
    scroll restoration mode
    activeEntry's scroll restoration mode
    persisted user state
    activeEntry's persisted user state
  4. If document's is initial about:blank is true, then set historyHandling to "replace".

    This means that pushState() on an initial about:blank Document behaves as a replaceState() call.

  5. Let entryToReplace be activeEntry if historyHandling is "replace", otherwise null.

  6. If historyHandling is "push", then:

    1. Increment document's history object's index.

    2. Set document's history object's length to its index + 1.

    These are temporary best-guess values for immediate synchronous access.

  7. If serializedData is not null, then restore the history object state given document and newEntry.

  8. Set document's URL to newURL.

    Since this is neither a navigation nor a history traversal, it does not cause a hashchange event to be fired.

  9. Set document's latest entry to newEntry.

  10. Set navigable's active session history entry to newEntry.

  11. Let traversable be navigable's traversable navigable.

  12. Append the following session history synchronous navigation steps involving navigable to traversable:

    1. Finalize a same-document navigation given traversable, navigable, newEntry, and entryToReplace.

Although both fragment navigation and the URL and history update steps perform synchronous history updates, only fragment navigation contains a synchronous call to update document for history step application. The URL and history update steps instead perform a few select updates inside the above algorithm, omitting others. This is somewhat of an unfortunate historical accident, and generally leads to web-developer sadness about the inconsistency. For example, this means that popstate events fire for fragment navigations, but not for history.pushState() calls.

7.4.5 Populating a session history entry

As explained in the overview, both navigation and traversal involve creating a session history entry and then attempting to populate its document member, so that it can be presented inside the navigable.

This process breaks down into two high-level cases: the usual case, and the javascript: URL special case. The usual case involves either using an already-given response, using the srcdoc resource stored in the session history entry, or fetching. The javascript: case bypasses most of the usual machinery and instead assembles enough information to immediately load an HTML document. Both cases have several failure modes, which can either result in doing nothing (leaving the navigable on its currently-active Document) or can result in populating the session history entry with an error document.

7.4.5.1 The usual case

To attempt to populate the history entry's document for a session history entry entry, given a navigable navigable, a NavigationTimingType navTimingType, a number unsafeNavigationStartTime, a source snapshot params sourceSnapshotParams, a target snapshot params targetSnapshotParams, an optional navigation ID-or-null navigationId (default null), an optional navigation params-or-null navigationParams (default null), an optional string cspNavigationType (default "other"), an optional boolean allowPOST (default false), optional algorithm steps processResponseEndOfBody (default an empty algorithm), and optional algorithm steps completionSteps (default an empty algorithm):

  1. Assert: if navigationParams is non-null, then navigationParams's response is non-null.

  2. Let currentBrowsingContext be navigable's active browsing context.

  3. Let documentResource be entry's document state's resource.

  4. If navigationParams is null, then:

    1. If documentResource is a string, then set navigationParams to the result of creating navigation params from a srcdoc resource given entry, navigable, sourceSnapshotParams, targetSnapshotParams, cspNavigationType, navigationId, navTimingType, processResponseEndOfBody, and unsafeNavigationStartTime.

    2. Otherwise, if both of the following are true:

      then set navigationParams to the result of creating navigation params by fetching given entry, navigable, sourceSnapshotParams, targetSnapshotParams, cspNavigationType, navigationId, navTimingType, processResponseEndOfBody, and unsafeNavigationStartTime.

  5. If navigationParams is "aborted", then run completionSteps and return.

  6. Queue a global task on the networking task source, given currentBrowsingContext's active window to run these steps:

    1. Let failure be false.

    2. If navigationParams is null and entry's URL is not a fetch scheme, then set entry's document state's document to the result of running attempt to create a URL scheme document given entry's URL, currentBrowsingContext, sandboxFlags, navigationId, navTimingType, and sourceSnapshotParams.

      The entry's URL might have been changed within the previous step of this algorithm following an HTTP redirect.

    3. Otherwise, if navigationParams is null, then set failure to true.

    4. Otherwise, if the result of should navigation response to navigation request of type in target be blocked by Content Security Policy? given navigationParams's request, navigationParams's response, navigationParams's policy container's CSP list, cspNavigationType, and currentBrowsingContext is "Blocked", then set failure to true. [CSP]

    5. Otherwise, if navigationParams's reserved environment is non-null and the result of checking a navigation response's adherence to its embedder policy given navigationParams's response, currentBrowsingContext, and navigationParams's policy container's embedder policy is false, then set failure to true.

    6. Otherwise, if the result of checking a navigation response's adherence to `X-Frame-Options` given navigationParams's response, currentBrowsingContext, and navigationParams's origin is false, then set failure to true.

    7. If failure is true, then:

      1. Set entry's document state's document to the result of creating a document for inline content that doesn't have a DOM, given currentBrowsingContext, null, and navTimingType. The inline content should indicate to the user the sort of error that occurred.

      2. Set entry's document state's document's salvageable to false.

      3. If navigationParams is not null, then:

        1. Run processResponseEndOfBody given navigationParams's response.

        2. Run the environment discarding steps for navigationParams's reserved environment.

        3. Invoke WebDriver BiDi navigation failed with browsingContext and a new WebDriver BiDi navigation status whose id is navigationId, status is "canceled", and url is navigationParams's response's URL.

    8. Otherwise, if navigationParams's response's status is 204 or 205, then:

      1. Run processResponseEndOfBody given navigationParams's response.

      2. Run completionSteps.

      3. Return.

    9. Otherwise:

      1. Let document be the result of loading a document given navigationParams and sourceSnapshotParams.

      2. If document is null, then run completionSteps and return.

      3. Set entry's document state's document to document.

      4. Set entry's document state's origin to document's origin.

    10. If entry's document state's request referrer is "client", then set it to request's referrer.

      This ensures that if we traverse back entry and have to refetch, we use the same referrer, instead of deriving the referrer from the fetch client.

    11. Run completionSteps.

To create navigation params from a srcdoc resource given a session history entry entry, a navigable navigable, a target snapshot params targetSnapshotParams, a navigation ID-or-null navigationId, a NavigationTimingType navTimingType, an algorithm processResponseEndOfBody, and a number unsafeNavigationStartTime:

  1. Let documentResource be entry's document state's resource.

  2. Let response be a new response whose URL list consists of about:srcdoc, header list consists of (`Content-Type`, `text/html`), and body is documentResource.

  3. Let responseOrigin be the result of determining the origin given response's URL, targetSnapshotParams's sandboxing flags, null, and entry's document state's origin.

  4. Let coop be a new cross-origin opener policy.

  5. Let coopEnforcementResult be a new cross-origin opener policy enforcement result whose needs a browsing context group switch is false, would need a browsing context group switch due to report-only is false, url is response's URL, origin is responseOrigin, cross-origin opener policy is coop, and current context is navigation source is false.

  6. Let policyContainer be the result of determining navigation params policy container given response's URL, entry's document state's history policy container, null, currentBrowsingContext's parent browsing context's active document's policy container, and null.

  7. Return a new navigation params, with

    id
    navigationId
    request
    null
    response
    response
    origin
    responseOrigin
    policy container
    policyContainer
    final sandboxing flag set
    targetSnapshotParams's sandboxing flags
    cross-origin opener policy
    coop
    COOP enforcement result
    coopEnforcementResult
    reserved environment
    null
    browsing context
    navigable's active browsing context
    navigation timing type
    navTimingType
    has cross-origin redirects
    false
    process response end of body
    an algorithm that does nothing
    unsafe start time
    unsafeNavigationStartTime
    uncommitted preloads
    null

To create navigation params by fetching given a session history entry entry, a navigable navigable, a source snapshot params sourceSnapshotParams, a target snapshot params targetSnapshotParams, a string cspNavigationType, a navigation ID-or-null navigationId, a NavigationTimingType navTimingType, an algorithm processResponseEndOfBody, and a number unsafeNavigationStartTime:

This algorithm mutates entry.

  1. Let url be entry's URL.

  2. Let documentResource be entry's document state's resource.

  3. Let request be a new request, with

    url
    entry's URL
    policy container
    entry's document state's history policy container
    client
    sourceSnapshotParams's fetch client
    destination
    "document"
    credentials mode
    "include"
    use-URL-credentials flag
    set
    redirect mode
    "manual"
    replaces client id
    navigable's active document's relevant settings object's id
    mode
    "navigate"
    referrer
    entry's document state's request referrer
    referrer policy
    entry's document state's request referrer policy
  4. If documentResource is a POST resource, then:

    1. Set request's method to `POST`.

    2. Set request's body to documentResource's request body.

    3. Set `Content-Type` to documentResource's request content-type in request's header list.

  5. If entry's document state's reload pending is true, then set request's reload-navigation flag.

  6. Otherwise, if entry's document state's ever populated is true, then set request's history-navigation flag.

  7. If sourceSnapshotParams's has transient activation is true, then set request's user-activation to true.

  8. If navigable's container is non-null:

    1. If the navigable's container has a browsing context scope origin, then set request's origin to that browsing context scope origin.

    2. Set request's destination to navigable's container's local name.

  9. Let response be null.

  10. Let responseOrigin be null.

  11. Let coopEnforcementResult be a new cross-origin opener policy enforcement result, with

    needs a browsing context group switch
    false
    would need a browsing context group switch due to report-only
    false
    url
    navigable's active document's URL
    origin
    navigable's active document's origin
    cross-origin opener policy
    navigable's active document's cross-origin opener policy
    current context is navigation source
    true if navigable's active document's origin is same origin with sourceSnapshotParams's origin; otherwise false
  12. Let finalSandboxFlags be an empty sandboxing flag set.

  13. Let responsePolicyContainer be null.

  14. Let responseCOOP be a new cross-origin opener policy.

  15. Let locationURL be null.

  16. Let currentURL be request's current URL.

  17. Let hasCrossOriginRedirects be false.

  18. Let uncommittedPreloads be null.

  19. While true:

    1. If request's reserved client is not null and currentURL's origin is not the same as request's reserved client's creation URL's origin, then:

      1. Run the environment discarding steps for request's reserved client.

      2. Set request's reserved client to null.

      3. Set uncommittedPreloads to null.

        Preloaded links from early hint headers remain in the preload cache after a same origin redirect, but get discarded when the redirect is cross-origin.

    2. If request's reserved client is null, then:

      1. Let topLevelCreationURL be currentURL.

      2. Let topLevelOrigin be null.

      3. If navigable is not a top-level traversable, then:

        1. Let parentEnvironment be navigable's parent's active document's relevant settings object.

        2. Set topLevelCreationURL to parentEnvironment's top-level creation URL.

        3. Set topLevelOrigin to parentEnvironment's top-level origin.

      4. Set request's reserved client to a new environment whose id is a unique opaque string, target browsing context is navigable's active browsing context, creation URL is currentURL, top-level creation URL is topLevelCreationURL, and top-level origin is topLevelOrigin.

        The created environment's active service worker is set in the Handle Fetch algorithm during the fetch if the request URL matches a service worker registration. [SW]

    3. If the result of should navigation request of type be blocked by Content Security Policy? given request and cspNavigationType is "Blocked", then set response to a network error and break. [CSP]

    4. If response is null, fetch request, with processEarlyHintsResponse set to the following step given a response earlyResponse: If uncommittedPreloads is null, then set uncommittedPreloads to the result of processing early hint headers given earlyResponse and request's reserved client.

    5. Otherwise, perform HTTP-redirect fetch using request and response.

    6. If request's body is null, then set entry's document state's resource to null.

      Fetch unsets the body for particular redirects.

    7. Wait for the task on the networking task source to process response and set response to the result.

      If navigationId is not null, then, while waiting, if navigable's ongoing navigation changes to no longer equal navigationId, terminate the ongoing fetch with the aborted flag set, and return "aborted".

    8. Set responsePolicyContainer to the result of creating a policy container from a fetch response given response and request's reserved client.

    9. Set finalSandboxFlags to the union of targetSnapshotParams's sandboxing flags and responsePolicyContainer's CSP list's CSP-derived sandboxing flags.

    10. Let sourceOrigin be entry's document state's origin if request's current URL matches about:blank, and sourceSnapshotParams's origin otherwise.

    11. Set responseOrigin to the result of determining the origin given response's URL, finalSandboxFlags, sourceOrigin, and null.

    12. If navigable is a top-level traversable, then:

      1. Set responseCOOP to the result of obtaining a cross-origin opener policy given response and request's reserved client.

      2. Set coopEnforcementResult to the result of enforcing the response's cross-origin opener policy given navigable's active browsing context, request's URL, responseOrigin, responseCOOP, coopEnforcementResult and request's referrer.

      3. If sandboxFlags is not empty and responseCOOP's value is not "unsafe-none", then set response to an appropriate network error and break.

        This results in a network error as one cannot simultaneously provide a clean slate to a response using cross-origin opener policy and sandbox the result of navigating to that response.

    13. If response is not a network error, navigable is a child navigable, and the result of performing a cross-origin resource policy check with navigable's container document's origin, navigable's container document's relevant settings object, request's destination, response, and true is blocked, then set response to a network error and break.

      Here we're running the cross-origin resource policy check against the parent navigable rather than navigable itself. This is because we care about the same-originness of the embedded content against the parent context, not the navigation source.

    14. Set locationURL to response's location URL given currentURL's fragment.

    15. If locationURL is failure or null, then break.

    16. Set entry's URL to locationURL.

    17. Set entry's serialized state to null.

    18. Let oldDocState be entry's document state.

    19. Set entry's document state to a new document state, with request referrer, request referrer policy, resource, ever populated, and navigable target name copied from oldDocState, and the history policy container set to a clone of the oldDocState's history policy container.

      For the navigation case, only entry referenced oldDocState, so this is functionally just an update to entry's document state. For the traversal case, it's possible adjacent session history entries also reference oldDocState, in which case they will continue doing so even after we've updated entry's document state.

      Consider a session history which contains... TODO-DOMENIC: fill out the full example of the traversal case, maybe.

    20. If locationURL is not a URL whose scheme is an HTTP(S) scheme, then:

      1. Set entry's document state's resource to null.

      2. Break.

      Navigation handles redirects manually as navigation is the only place in the web platform that cares for redirects to mailto: URLs and such.

    21. If locationURL's origin is not the same as currentURL's origin, then set hasCrossOriginRedirects to true.

    22. Set currentURL to locationURL.

    By the end of this loop we will be in one of these scenarios:

    • response is a network error.

    • locationURL is failure, because of an unparseable `Location` header.

    • locationURL is null, because we successfully fetched a non-network error HTTP(S) response with no `Location` header.

    • locationURL is a URL with a non-HTTP(S) scheme.

  20. If locationURL is not null or response is a network error, then return null.

  21. 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.

  22. Return a new navigation params, with

    id
    navigationId
    request
    request
    response
    response
    origin
    responseOrigin
    policy container
    resultPolicyContainer
    final sandboxing flag set
    finalSandboxFlags
    cross-origin opener policy
    responseCOOP
    COOP enforcement result
    coopEnforcementResult
    reserved environment
    request's reserved client
    browsing context
    navigable's active browsing context
    navigation timing type
    navTimingType
    has cross-origin redirects
    hasCrossOriginRedirects
    process response end of body
    processResponseEndOfBody
    unsafe start time
    unsafeNavigationStartTime
    uncommitted preloads
    uncommittedPreloads

An element has a browsing context scope origin if its Document's browsing context is a top-level browsing context or if all of its Document's ancestor browsing contexts all 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 and source snapshot params sourceSnapshotParams, perform the following steps. They return a Document or null.

  1. Let type be the computed type of navigationParams's response.

  2. If the user agent has been configured to process resources of the given type using some mechanism other than rendering the content in a browsing context, then skip this step. Otherwise, if the type is one of the following types:

    an HTML MIME type
    Return the result of loading an HTML document, given navigationParams.
    an XML MIME type that is not an explicitly supported XML MIME type
    Return the result of loading an XML document given navigationParams and type.
    a JavaScript MIME type
    a JSON MIME type that is not an explicitly supported JSON MIME type
    "text/css"
    "text/plain"
    "text/vtt"
    Return the result of loading a text document given navigationParams and type.
    "multipart/x-mixed-replace"
    Return the result of loading a multipart/x-mixed-replace document, given navigationParams and sourceSnapshotParams.
    A supported image, video, or audio type
    Return the result of loading a media document given navigationParams and type.
    "application/pdf"
    "text/pdf"
    If the user agent's PDF viewer supported is true, return the result of creating a document for inline content that doesn't have a DOM given navigationParams's browsing context.

    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 browsing context, or hand it off to external software. Both happen in the steps below.

  3. Otherwise, the document's type is such that the resource will not affect navigationParams's browsing context, e.g., because the resource is to be handed to an external application or because it is an unknown type that will be processed as a download. Hand-off to external software given navigationParams's response, navigationParams's browsing context, navigationParams's final sandboxing flag set, and sourceSnapshotParams.

  4. Return null.

7.4.5.2 The javascript: URL special case

javascript: URLs have a dedicated label on the issue tracker documenting various problems with their specification.

To populate a history entry's document by evaluating a javascript: URL for session history entry historyEntry, given a navigable targetNavigable, a number unsafeNavigationStartTime, a source snapshot params sourceSnapshotParams, a string cspNavigationType, and a URL url:

  1. Let request be a new request whose URL is url.

    This is a synthetic request solely for plumbing into other algorithms. It will never hit the network.

  2. Let response be a response whose status is 204.

  3. If both of the following are true:

    then:

    1. Let urlString be the result of running the URL serializer on url.

    2. Let encodedScriptSource be the result of removing the leading "javascript:" from urlString.

    3. Let scriptSource be the UTF-8 decoding of the percent-decoding of encodedScriptSource.

    4. Append targetNavigable's active document's URL to request's URL list.

    5. Let settings be targetNavigable's active document's relevant settings object.

    6. Let baseURL be settings's API base URL.

    7. Let script be the result of creating a classic script given scriptSource, settings, baseURL, and the default classic script fetch options.

    8. Let evaluationStatus be the result of running the classic script script.

    9. Let result be undefined if evaluationStatus is an abrupt completion or evaluationStatus.[[Value]] is empty, or evaluationStatus.[[Value]] otherwise.

    10. If Type(result) is String, then set response to a response whose header list is « (`Content-Type`, `text/html;charset=utf-8`) », and whose body is the result of UTF-8 encoding result.

      The encoding to UTF-8 means that unpaired surrogates will not roundtrip, once the HTML parser decodes the response body.

  4. Let policyContainer be targetNavigable's active document's policy container.

  5. Let finalSandboxFlags be policyContainer's CSP list's CSP-derived sandboxing flags.

  6. Let coop be targetNavigable's active document's cross-origin opener policy.

  7. Let coopEnforcementResult be a new cross-origin opener policy enforcement result with

    needs a browsing context group switch
    false
    would need a browsing context group switch due to report-only
    false
    url
    url
    origin
    sourceSnapshotParams's origin
    cross-origin opener policy
    coop
    current context is navigation source
    false
  8. Let navigationParams be a new navigation params, with

    id
    navigationId
    request
    request
    response
    response
    origin
    sourceSnapshotParams's origin
    policy container
    policyContainer
    final sandboxing flag set
    finalSandboxFlags
    cross-origin opener policy
    coop
    COOP enforcement result
    coopEnforcementResult
    reserved environment
    null
    browsing context
    targetNavigable's active browsing context
    navigation timing type
    "navigate"
    has cross-origin redirects
    false
    process response end of body
    an algorithm that does nothing
    unsafe start time
    unsafeNavigationStartTime
    uncommitted preloads
    null
  9. Set historyEntry's document state's document to the result loading an HTML document given navigationParams.

    javascript: URL documents go through a special path wherein we never actually call into attempt to populate the history entry's document, but instead we synchronously populate its document right here.

7.4.6 Applying the history step

7.4.6.1 Updating the traversable

A changing navigable continuation state is a struct with:

displayed document
A Document
target entry
A session history entry
navigable
A navigable
update only
A boolean

To apply the history step non-negative integer step to a traversable navigable traversable, with optional boolean checkForUserCancelation (default false), optional number unsafeNavigationStartTime, optional source snapshot params-or-null sourceSnapshotParams (default null), and optional navigable initiatorToCheck:

sourceSnapshotParams and initiatorToCheck are always either both given or both not given. They are usually not given, as most callers do not need the extra checks on the navigation initiator that they cause. (Possibly because the caller has already performed such checks themselves.)

  1. Assert: This is running within traversable's session history traversal queue.

  2. Let targetStep be the result of getting the used step given traversable and step.

  3. If initiatorToCheck is given, then:

    1. Assert: sourceSnapshotParams is not null.

    2. 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.

  4. Let navigablesCrossingDocuments be the result of getting all navigables that might experience a cross-document traversal given traversable and targetStep.

  5. If checkForUserCancelation is true, and the result of checking if unloading is user-canceled given navigablesCrossingDocuments given traversable and targetStep is "refuse", then return.

    Some algorithms check if unloading is user-canceled as a prerequisite to modifying the history tree. Those algorithms will set checkForUserCancelation to false when calling this algorithm to avoid performing the check twice.

    It might not be correct to block on beforeunload results here. This may have observable consequences.

  6. Let changingNavigables be the result of get all navigables whose current session history entry will change or reload given traversable and targetStep.

  7. Let nonchangingNavigablesThatStillNeedUpdates be the result of getting all navigables that only need history object length/index update given traversable and targetStep.

  8. For each navigable of changingNavigables:

    1. Let targetEntry be the result of getting the target history entry given navigable and targetStep.

    2. Set navigable's current session history entry to targetEntry.

    3. Set navigable's ongoing navigation to "traversal".

  9. Let changeJobs be the size of changingNavigables.

  10. Let completedChangeJobs be 0.

  11. 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.

  12. 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.

    1. Let displayedEntry be navigable's active session history entry.

    2. Let targetEntry be navigable's current session history entry.

    3. Let changingNavigableContinuation be a changing navigable continuation state with:

      displayed document
      displayedEntry's document
      target entry
      targetEntry
      navigable
      navigable
      update-only
      false
    4. If displayedEntry is targetEntry and targetEntry's document state's reload pending is false, then:

      1. Set changingNavigableContinuation's update-only to true.

      2. Enqueue changingNavigableContinuation on changingNavigableContinuations.

      3. Abort these steps.

      This case occurs due to a synchronous navigation which already updated the active session history entry.

    5. Let oldOrigin be targetEntry's document state's origin.

    6. If targetEntry's document is null, or targetEntry's document state's reload pending is true, then:

      1. Assert: unsafeNavigationStartTime is given.

      2. Let navTimingType be "back_forward" if targetEntry's document is null; otherwise "reload".

      3. Let targetSnapshotParams be the result of snapshotting target snapshot params given navigable.

      4. Let potentiallyTargetSpecificSourceSnapshotParams be sourceSnapshotParams.

      5. 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.

      6. Attempt to populate the history entry's document for targetEntry, given navigable, unsafeNavigationStartTime, potentiallyTargetSpecificSourceSnapshotParams, targetSnapshotParams, with allowPOST set to targetEntry's document state's reload pending, and completionSteps set to queue a global task on the navigation and traversal task source given navigable's active document's relevant global object to continue these steps.

      7. Set targetEntry's document state's reload pending to false.

    7. 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.

    8. If targetEntry's document's origin is not oldOrigin, then set targetEntry's serialized state to null.

      This clears history state when the origin changed vs a previous load of targetEntry without a redirect occuring. This can happen due to a change in CSP sandbox headers.

    9. If all of the following is true:

      Then set targetEntry's document state's navigable target name to the empty string.

    10. Enqueue changingNavigableContinuation on changingNavigableContinuations.

      The rest of this job runs later in this algorithm.

  13. Let navigablesThatMustWaitBeforeHandlingSyncNavigation be an empty set.

  14. While completedChangeJobs does not equal totalChangeJobs:

    1. If traversable's running nested apply history step is false, then:

      1. 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:

        1. 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.

        2. Remove steps from traversable's session history traversal queue's algorithm set.

        3. Set traversable's running nested apply history step to true.

        4. Run steps.

        5. 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.

    2. Let changingNavigableContinuation be the result of dequeuing from changingNavigableContinuations.

    3. If changingNavigableContinuation is nothing, then continue.

    4. Let displayedDocument be changingNavigableContinuation's displayed document.

    5. Let targetEntry be changingNavigableContinuation's target entry.

    6. Let navigable be changingNavigableContinuation's navigable.

    7. Set navigable's ongoing navigation to null.

      This allows new navigations of navigable to start, whereas during the traversal they were blocked.

    8. 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.

    9. 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.

    10. Queue a global task on the navigation and traversal task source of navigable's active window to run the steps:

      1. If changingNavigableContinuation's update-only is false, then:

        1. Unload displayedDocument given targetEntry's document.

        2. For each childNavigable of displayedDocument's descendant navigables, queue a global task on the navigation and traversal task source given childNavigable's active window to unload childNavigable's active document.

        3. Activate history entry targetEntry for navigable.

      2. If targetEntry's document is not equal to displayedDocument, then queue a global task on the navigation and traversal task source given targetEntry's document's relevant global object to perform the following step. Otherwise, continue onward to perform the following step within the currently-queued task.

      3. Update document for history step application given targetEntry's document, targetEntry, changingNavigableContinuation's update-only, scriptHistoryLength, and scriptHistoryIndex.

      4. Increment completedChangeJobs.

  15. 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.

  16. Let completedNonchangingJobs be 0.

  17. Let (scriptHistoryLength, scriptHistoryIndex) be the result of getting the history object length and index given traversable and targetStep.

  18. For each navigable of nonchangingNavigablesThatStillNeedUpdates, queue a global task on the navigation and traversal task source of navigable's active window to run the steps:

    1. Let document be navigable's active document.

    2. Set document's history object's index to scriptHistoryIndex.

    3. Set document's history object's length to scriptHistoryLength.

    4. Increment completedNonchangingJobs.

  19. Wait for completedNonchangingJobs to equal totalNonchangingJobs.

  20. Set traversable's current session history step to targetStep.

To activate history entry session history entry entry for navigable navigable:

  1. Save persisted state to the navigable's active session history entry.

  2. Let newDocument be entry's document.

  3. 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.

  4. Set navigable's active session history entry to entry.

  5. Make active newDocument.

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.

  1. Let steps be the result of getting all used history steps within traversable.

  2. 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.

  1. Let steps be the result of getting all used history steps within traversable.

  2. Let scriptHistoryLength be the size of steps.

  3. Assert: steps contains step.

    It is assumed that step has been adjusted by getting the used step.

  4. Let scriptHistoryIndex be the index of step in sessionSteps.

  5. 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.

  1. Let results be an empty list.

  2. Let navigablesToCheck be « traversable ».

    This list is extended in the loop below.

  3. For each navigable of navigablesToCheck:

    1. Let targetEntry be the result of getting the target history entry given navigable and targetStep.

    2. 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.

    3. If targetEntry's document is navigable's 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.

  4. 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.

  1. Let results be an empty list.

  2. Let navigablesToCheck be « traversable ».

    This list is extended in the loop below.

  3. For each navigable of navigablesToCheck:

    1. Let targetEntry be the result of getting the target history entry given navigable and targetStep.

    2. If targetEntry is navigable's current session history entry and targetEntry's document state's reload pending is false, then:

      1. Append navigable to results.

      2. 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.

  4. 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.

  1. Let entries be the result of getting the history entries for navigable.

  2. Return the item in entries that has the greatest step less than or equal to step.

    JAKE-TODO: Create a timeline diagram to explain why it's "greatest step less than or equal to step".

DOMENIC-TODO: the below algorithm is only called once, but there is a very similar getting session history entries, which seems very suspicious and demands a rename at least.

To get the history entries for a navigable navigable:

  1. Let traversable be navigable's traversable navigable.

  2. Assert: This is running within traversable's session history traversal queue.

  3. If traversable is navigable, then return traversable's session history entries.

  4. Let entryLists be the ordered set « traversable's session history entries ».

  5. For each entryList of entryLists:

    1. For each entry of entryList:

      1. For each nestedHistory of entry's document state's nested histories:

        1. If nestedHistory's id is navigable's id, then return nestedHistory's entries list.

        2. Append nestedHistory's entries list to entryLists.

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.

  1. Let results be an empty list.

  2. Let navigablesToCheck be « traversable ».

    This list is extended in the loop below.

  3. For each navigable of navigablesToCheck:

    1. Let targetEntry be the result of getting the target history entry given navigable and targetStep.

    2. If targetEntry's document is not navigable's 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 document is reliable.

    3. 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.

  4. Return results.

7.4.6.2 Updating the document

To update document for history step application given a Document document, a session history entry entry, a boolean doNotReactivate, and integers scriptHistoryLength and scriptHistoryIndex:

  1. Let documentIsNew be true if document's latest entry is null; otherwise false.

  2. Let documentsEntryChanged be true if document's latest entry is not entry; otherwise false.

  3. Set document's history object's index to scriptHistoryIndex.

  4. Set document's history object's length to scriptHistoryLength.

  5. If documentsEntryChanged is true, then:

    1. Let oldURL be document's latest entry's URL.

    2. Set document's latest entry to entry.

    3. Restore the history object state given document and entry.

    4. If documentIsNew is false, then 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.

    5. Restore persisted state given entry.

    6. If documentIsNew is false, and 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.

  6. If documentIsNew is true, then:

    1. Try to scroll to the fragment for document.

    2. At this point scripts may run for the newly-created document document. DOMENIC-TODO: reference this appropriately.

  7. Otherwise, if documentsEntryChanged is false and doNotReactivate is false, then reactivate document.

    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:

  1. Let targetRealm be document's relevant Realm.

  2. Let state be null.

  3. If entry's serialized state is not null, then set state to StructuredDeserialize(entry's serialized state, targetRealm). If this throws an exception, catch it and ignore the exception.

  4. Set document's history object's state to state.

To make active a Document document:

  1. Let window be document's relevant global object.

  2. Set document's browsing context's WindowProxy's [[Window]] internal slot value to window.

  3. Set document's visibility state to document's node navigable's traversable navigable's system visibility state.

  4. Set window's relevant settings object's execution ready flag.

To reactivate a Document document:

This algorithm updates document after it has come out of bfcache, i.e., after it has been made fully active again.

  1. For each formControl of form controls in document with an autofill field name of "off", invoke the reset algorithm for formControl.

  2. If document's suspended timer handles is not empty:

    1. Assert: document's suspension time is not zero.

    2. Let suspendDuration be the current high resolution time minus document's suspension time.

    3. Let activeTimers be document's relevant global object's map of active timers.

    4. For each handle in document's suspended timer handles, if activeTimers[handle] exists, then increase activeTimers[handle] by suspendDuration.

  3. If document's current document readiness is "complete", and document's page showing flag is false, then:

    1. Set document's page showing flag to true.

    2. Update the visibility state of document to "visible".

    3. 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:

  1. 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.)

  2. Queue a global task on the networking task source given document's relevant global object to run these steps:

    1. 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.

    2. Scroll to the fragment given document.

    3. If document's indicated part is still null, then try to scroll to the fragment for document.

7.4.6.3 Scrolling to a fragment

To scroll to the fragment given a Document document:

  1. If document's indicated part is null, then set document's target element to null.

  2. Otherwise, if document's indicated part is top of the document, then:

    1. Set document's target element to null.

    2. Scroll to the beginning of the document for document. [CSSOMVIEW]

    3. Return.

  3. Otherwise:

    1. Assert: document's indicated part is an element.

    2. Let target be document's indicated part.

    3. Set document's target element to target.

    4. Run the ancestor details revealing algorithm on target.

    5. Run the ancestor hidden-until-found revealing algorithm on target.

    6. Scroll target into view, with behavior set to "auto", block set to "start", and inline set to "nearest". [CSSOMVIEW]

    7. Run the focusing steps for target, with the Document's viewport as the fallback target.

    8. 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, the following processing model must be followed to determine its indicated part:

  1. Let fragment be document's URL's fragment.

  2. If fragment is the empty string, then return the special value top of the document.

  3. Let potentialIndicatedElement be the result of finding a potential indicated element given document and fragment.

  4. If potentialIndicatedElement is not null, then return potentialIndicatedElement.

  5. Let fragmentBytes be the result of percent-decoding fragment.

  6. Let decodedFragment be the result of running UTF-8 decode without BOM on fragmentBytes.

  7. Set potentialIndicatedElement to the result of finding a potential indicated element given document and decodedFragment.

  8. If potentialIndicatedElement is not null, then return potentialIndicatedElement.

  9. If decodedFragment is an ASCII case-insensitive match for the string top, then return the top of the document.

  10. Return null.

To find a potential indicated element given a Document document and a string fragment, run these steps:

  1. 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.

  2. 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.

  3. Return null.

7.4.6.4 Persisted history entry state

To save persisted state to a session history entry entry:

  1. Set the scroll position data of entry to contain the scroll positions for all of entry's document's restorable scrollable regions.

  2. 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:

  1. If entry's scroll restoration mode is "auto", then the user agent may use entry's scroll position data to restore the scroll positions of entry's document's restorable scrollable regions.

    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.

  2. 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 either the Text state or the Search 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.


The restorable scrollable regions of a Document document are document's viewport, and all of document's scrollable regions excepting any child browsing contexts of document.

Child browsing context scroll restoration is handled by the history entry for those browsing contexts' Documents.

7.4.7 Implementation notes for session history

JAKE-TODO: review this section

This section is non-normative.

The History interface is not meant to place restrictions on how implementations represent the session history to the user.

For example, session history could be implemented in a tree-like manner, with each page having multiple "forward" pages. This specification doesn't define how the linear list of pages in the history object are derived from the actual session history as seen from the user's perspective.

Similarly, a page containing two iframes has a history object distinct from the iframes' history objects, despite the fact that typical web browsers present the user with just one "Back" button, with a session history that interleaves the navigation of the two inner frames and the outer page.

Security: It is suggested that to avoid letting a page "hijack" the history navigation facilities of a UA by abusing pushState(), the UA provide the user with a way to jump back to the previous page (rather than just going back to the previous state). For example, the back button could have a drop down showing just the pages in the session history, and not showing any of the states. Similarly, an aural browser could have two "back" commands, one that goes back to the previous state, and one that jumps straight back to the previous page.

For both pushState() and replaceState(), user agents are encouraged to prevent abuse of these APIs via too-frequent calls or over-large state objects. As detailed above, the algorithm explicitly allows user agents to ignore any such calls when appropriate.

7.5 Document lifecycle

7.5.1 Shared document creation infrastructure

When loading a document using one of the below algorithms, we use the following steps to create and initialize a Document object, given a type type, content type contentType, and navigation params navigationParams:

Document objects are also created when creating a new browsing context and document; such initial about:blank Document are never created by this algorithm. Also, browsing context-less Document objects can be created via various APIs, such as document.implementation.createHTMLDocument().

  1. Let browsingContext be the result of the obtaining a browsing context to use for a navigation response given navigationParams's browsing context, navigationParams's final sandboxing flag set, navigationParams's cross-origin opener policy, and navigationParams's COOP enforcement result.

    This can result in a browsing context group switch, in which case browsingContext will be a newly-created browsing context instead of being navigationParams's browsing context. In such a case, the created Window, Document, and agent will not end up being used; because the created Document's origin is opaque, we will end up creating a new agent and Window later in this algorithm to go along with the new Document.

  2. Let permissionsPolicy be the result of creating a permissions policy from a response given browsingContext, navigationParams's origin, and navigationParams's response. [PERMISSIONSPOLICY]

    The creating a permissions policy from a response algorithm makes use of the passed origin. If document.domain has been used for browsingContext's container document, then its origin cannot be same origin-domain with the passed origin, because these steps run before the document is created, so it cannot itself yet have used document.domain. Note that this means that Permissions Policy checks are less permissive compared to doing a same origin check instead.

    See below for some examples of this in action.

  3. Let creationURL be navigationParams's response's URL.

  4. If navigationParams's request is non-null, then set creationURL to navigationParams's request's current URL.

  5. Let window be null.

  6. If browsingContext's active document's is initial about:blank is true, and browsingContext's active document's origin is same origin-domain with navigationParams's origin, then set window to browsingContext's active window.

    This means that both the initial about:blank Document, and the new Document that is about to be created, will share the same Window object.

  7. Otherwise:

    1. Let oacHeader be the result of getting a structured field value given `Origin-Agent-Cluster` and "item" from navigationParams's response's header list.

    2. Let requestsOAC be true if oacHeader is not null and oacHeader[0] is the boolean true; otherwise false.

    3. If navigationParams's reserved environment is a non-secure context, then set requestsOAC to false.

    4. Let agent be the result of obtaining a similar-origin window agent given navigationParams's origin, browsingContext's group, and requestsOAC.

    5. Let realmExecutionContext be the result of creating a new JavaScript realm given agent and the following customizations:

      • For the global object, create a new Window object.

      • For the global this binding, use browsingContext's WindowProxy object.

    6. Set window to the global object of realmExecutionContext's Realm component.

    7. Let topLevelCreationURL be creationURL.

    8. Let topLevelOrigin be navigationParams's origin.

    9. If browsingContext is not a top-level browsing context, then:

      1. Let parentEnvironment be browsingContext's container's relevant settings object.

      2. Set topLevelCreationURL to parentEnvironment's top-level creation URL.

      3. Set topLevelOrigin to parentEnvironment's top-level origin.

    10. Set up a window environment settings object with creationURL, realmExecutionContext, navigationParams's reserved environment, topLevelCreationURL, and topLevelOrigin.

    This is the usual case, where the new Document we're about to create gets a new Window to go along with it.

  8. Let loadTimingInfo be a new document load timing info with its navigation start time set to the result of calling coarsen time with navigationParams's unsafe start time and document's relevant settings object's cross-origin isolated capability.

  9. Let document be a new Document, with

    type
    type
    content type
    contentType
    origin
    navigationParams's origin
    browsing context
    browsingContext
    policy container
    navigationParams's policy container
    permissions policy
    permissionsPolicy
    active sandboxing flag set
    navigationParams's final sandboxing flag set
    cross-origin opener policy
    navigationParams's cross-origin opener policy
    load timing info
    loadTimingInfo
    was created via cross-origin redirects
    navigationParams's has cross-origin redirects
    navigation id
    navigationParams's id
    URL
    creationURL
    current document readiness
    "loading"
  10. Set window's associated Document to document.

  11. Run CSP initialization for a Document given document. [CSP]

  12. If navigationParams's request is non-null, then:

    1. Set document's referrer to the empty string.

    2. Let referrer be navigationParams's request's referrer.

    3. If referrer is a URL record, then set document's referrer to the serialization of referrer.

      Per Fetch, referrer will be either a URL record or "no-referrer" at this point.

  13. Let redirectCount be 0 if navigationParams's has cross-origin redirects is true; otherwise navigationParams's request's redirect count.

  14. Create the navigation timing entry for document, with navigationParams's response's timing info, redirectCount, navigationParams's navigation timing type, and navigationParams's response's service worker timing info.

  15. If navigationParams's response has a `Refresh` header, then:

    1. Let value be the isomorphic decoding of the value of the header.

    2. Run the shared declarative refresh steps with document and value.

    We do not currently have a spec for how to handle multiple `Refresh` headers. This is tracked as issue #2900.

  16. If navigationParams's uncommitted preloads is not null, then for each commitEarlyPreload of navigationParams's uncommitted preloads, call commitEarlyPreload given document.

  17. Process link headers given document, navigationParams's response, and "pre-media".

  18. Return document.

In this example, the child document is not allowed to use PaymentRequest, despite being same origin-domain at the time the child document tries to use it. At the time the child document is initialized, only the parent document has set document.domain, and the child document has not.

<!-- https://foo.example.com/a.html -->
<!doctype html>
<script>
document.domain = 'example.com';
</script>
<iframe src=b.html></iframe>
<!-- https://bar.example.com/b.html -->
<!doctype html>
<script>
document.domain = 'example.com'; // This happens after the document is initialized
new PaymentRequest(…); // Not allowed to use
</script>

In this example, the child document is allowed to use PaymentRequest, despite not being same origin-domain at the time the child document tries to use it. At the time the child document is initialized, none of the documents have set document.domain yet so same origin-domain falls back to a normal same origin check.

<!-- https://example.com/a.html -->
<!doctype html>
<iframe src=b.html></iframe>
<!-- The child document is now initialized, before the script below is run. -->
<script>
document.domain = 'example.com';
</script>
<!-- https://example.com/b.html -->
<!doctype html>
<script>
new PaymentRequest(…); // Allowed to use
</script>

7.5.2 Loading HTML documents

To load an HTML document, given navigation params navigationParams:

  1. Let document be the result of creating and initializing a Document object given "html", "text/html", and navigationParams.

  2. Create an HTML parser and associate it with the document. Each task that the networking task source places on the task queue while fetching runs must then fill the parser's input byte stream with the fetched bytes and cause the HTML parser to perform the appropriate processing of the input stream.

    The first task that the networking task source places on the task queue while fetching runs must process link headers given document, navigationParams's response, and "media", after the task has been procesed by the HTML parser.

    The input byte stream converts bytes into characters for use in the tokenizer. This process relies, in part, on character encoding information found in the real Content-Type metadata of the resource; the computed type is not used for this purpose.

    When no more bytes are available, the user agent must queue a global task on the networking task source given document's relevant global object to run the following steps:

    1. Call navigationParams's process response end of body with navigationParams's response.

    2. Have the parser to process the implied EOF character, which eventually causes a load event to be fired.

  3. Return document.

7.5.3 Loading XML documents

When faced with displaying an XML file inline, provided navigation params navigationParams and a string type, user agents must follow the requirements defined in XML and Namespaces in XML, XML Media Types, DOM, and other relevant specifications to create and initialize a Document object document, given "xml", type, and navigationParams, and return that Document. They must also create a corresponding XML parser. [XML] [XMLNS] [RFC7303] [DOM]

The first task that the networking task source places on the task queue while fetching runs must process link headers given document, navigationParams's response, and "media", after the task has been procesed by the XML parser.

At the time of writing, the XML specification community had not actually yet specified how XML and the DOM interact.

The actual HTTP headers and other metadata, not the headers as mutated or implied by the algorithms given in this specification, are the ones that must be used when determining the character encoding according to the rules given in the above specifications. Once the character encoding is established, the document's character encoding must be set to that character encoding.

When no more bytes are available, the user agent must queue a global task on the networking task source given document's relevant global object to call navigationParams's process response end of body with navigationParams's response.

Once parsing is complete, the user agent must set document's navigation id to null.

For HTML documents this is reset when parsing is complete, after firing the load event.

Error messages from the parse process (e.g., XML namespace well-formedness errors) may be reported inline by mutating the Document.

7.5.4 Loading text documents

To load a text document, given a navigation params navigationParams and a string type:

  1. Let document be the result of creating and initializing a Document object given "html", type, and navigationParams.

  2. Set document's parser cannot change the mode flag to true.

  3. Set document's mode to "no-quirks".

  4. Create an HTML parser and associate it with the document. Act as if the tokenizer had emitted a start tag token with the tag name "pre" followed by a single U+000A LINE FEED (LF) character, and switch the HTML parser's tokenizer to the PLAINTEXT state. Each task that the networking task source places on the task queue while fetching runs must then fill the parser's input byte stream with the fetched bytes and cause the HTML parser to perform the appropriate processing of the input stream.

    The first task that the networking task source places on the task queue while fetching runs must process link headers given document, navigationParams's response, and "media", after the task has been procesed by the HTML parser.

    When no more bytes are available, the user agent must queue a global task on the networking task source given document's relevant global object to run the following steps:

    1. Call navigationParams's process response end of body with navigationParams's response.

    2. Have the parser to process the implied EOF character, which eventually causes a load event to be fired.

  5. Return document.

The rules for how to convert the bytes of the plain text document into actual characters, and the rules for actually rendering the text to the user, are defined by the specifications for the computed MIME type of the resource (i.e., type).

The document's character encoding must be set to the character encoding used to decode the document.

User agents may add content to the head element of the Document, e.g., linking to a style sheet, providing script, or giving the document a title.

In particular, if the user agent supports the Format=Flowed feature of RFC 3676 then the user agent would need to apply extra styling to cause the text to wrap correctly and to handle the quoting feature. This could be performed using, e.g., a CSS extension.

7.5.5 Loading multipart/x-mixed-replace documents

To load a multipart/x-mixed-replace document, given navigation params navigationParams and source snapshot params sourceSnapshotParams:

  1. Parse navigationParams's response's body using the rules for multipart types. [RFC2046]

  2. Let firstPartNavigationParams be a copy of navigationParams.

  3. Set firstPartNavigationParams response to a new response representing the first part of navigationParams's response's body's multipart stream.

  4. Let document be the result of loading a document given firstPartNavigationParams and sourceSnapshotParams.

    For each additional body part obtained from navigationParams's response, the user agent must navigate document's node navigable to navigationParams's request's URL, using document, with response set to navigationParams's response and historyHandling set to "replace".

  5. Return document.

For the purposes of algorithms processing these body parts as if they were complete stand-alone resources, the user agent must act as if there were no more bytes for those resources whenever the boundary following the body part is reached.

Thus, load events (and for that matter unload events) do fire for each body part loaded.

7.5.6 Loading media documents

To load a media document, given navigationParams and a string type:

  1. Let document be the result of creating and initializing a Document object given "html", type, and navigationParams.

  2. Set document's mode to "no-quirks".

  3. Append an html element to document.

  4. Append a head element to the html element.

  5. Append a body element to the html element.

  6. Append an element host element for the media, as described below, to the body element.

  7. Set the appropriate attribute of the element host element, as described below, to the address of the image, video, or audio resource.

  8. Process link headers given document, navigationParams's response, and "media".

  9. Return document.

The element host element to create for the media is the element given in the table below in the second cell of the row whose first cell describes the media. The appropriate attribute to set is the one given by the third cell in that same row.

Type of media Element for the media Appropriate attribute
Image img src
Video video src
Audio audio src

Then, the user agent must act as if it had stopped parsing.

User agents may add content to the head element of the Document, or attributes to the element host element, e.g., to link to a style sheet, to provide a script, to give the document a title, or to make the media autoplay.

When no more bytes are available, the user agent must queue a global task on the networking task source given the newly-created Document's relevant global object to call navigationParams's process response end of body with navigationParams's response.

7.5.7 Loading a document for inline content that doesn't have a DOM

When the user agent is to create a document to display a user agent page or PDF viewer inline, provided a browsing context browsingContext, a navigation ID navigationId, a NavigationTimingType navTimingType, the user agent should:

  1. Let navigationParams be a new navigation params with

    id
    navigationId
    request
    null
    response
    a new response
    origin
    a new opaque origin
    policy container
    a new policy container
    final sandboxing flag set
    an empty set
    cross-origin opener policy
    a new cross-origin opener policy
    COOP enforcement result
    a new cross-origin opener policy enforcement result
    reserved environment
    null
    browsing context
    browsingContext
    navigation timing type
    navTimingType
    has cross-origin redirects
    false
    process response end of body
    an algorithm that does nothing
    unsafe start time
    0
    uncommitted preloads
    null
  2. Let document be the result of creating and initializing a Document object given "html", "text/html", and navigationParams.

  3. Either associate document with a custom rendering that is not rendered using the normal Document rendering rules, or mutate document until it represents the content the user agent wants to render.

  4. Return document.

Because we ensure the resulting Document's origin is opaque, and the resulting Document cannot run script with access to the DOM, the existence and properties of this Document are not observable to web developer code. This means that most of the above values, e.g., the text/html type, do not matter. Similarly, most of the items in navigationParams don't have any observable effect, besides preventing the Document-creation algorithm from getting confused, and so are set to default values.

Once the page has been set up, the user agent must act as if it had stopped parsing.

7.5.8 Finishing the loading process

A Document has a completely loaded time (a time or null), which is initially null.

A Document is considered completely loaded if its completely loaded time is non-null.

To completely finish loading a Document document:

  1. Assert: document's browsing context is non-null.

  2. Set document's completely loaded time to the current time.

  3. Let container be document's node navigable's container.

    This will be null in the case where document is the initial about:blank Document in a frame or iframe, since at the point of browsing context creation which calls this algorithm, the container relationship has not yet been established. (That happens in a subsequent step of create a new nested navigable.)

    The consequence of this is that the following steps do nothing, i.e., we do not fire an asynchronous load event on the container element for such cases. Instead, a synchronous load event is fired in a special initial-insertion case in the shared attribute processing steps for iframe and frame elements.

  4. If container is an iframe element, then queue an element task on the DOM manipulation task source given container to run the iframe load event steps given container.

  5. Otherwise, if container is non-null, then queue an element task on the DOM manipulation task source given container to fire an event named load at container.

7.5.9 Unloading documents

A Document has a salvageable state, which must initially be true, and a page showing flag, which must initially be false. The page showing flag is used to ensure that scripts receive pageshow and pagehide events in a consistent manner (e.g. that they never receive two pagehide events in a row without an intervening pageshow, or vice versa).

A Document has a DOMHighResTimeStamp suspension time, initially 0.

A Document has a list of suspended timer handles, initially empty.

Event loops have a termination nesting level counter, which must initially be 0.

Document objects have an unload counter, which is used to ignore certain operations while the below algorithms run. Initially, the counter must be set to zero.

To unload a Document oldDocument, given an optional Document newDocument:

  1. Assert: this algorithm is running as part of a task queued on oldDocument's event loop.

  2. Let unloadTimingInfo be a new document unload timing info.

  3. Let intendToStoreInBfcache be true if the user agent intends to keep oldDocument alive in a session history entry, such that it can later be used for history traversal.

    This must be false if oldDocument is not salvageable, or if there are any descendants of oldDocument which the user agent does not intend to keep alive in the same way (including due to their lack of salvageability).

  4. Let eventLoop be oldDocument's relevant agent's event loop.

  5. Increase eventLoop's termination nesting level by 1.

  6. Increase oldDocument's unload counter by 1.

  7. If intendToKeepInBfcache is false, then set oldDocument's salvageable state to false.

  8. If oldDocument's page showing is true:

    1. Set oldDocument's page showing to false.

    2. Fire a page transition event named pagehide at oldDocument's relevant global object with oldDocument's salvageable state.

    3. Update the visibility state of oldDocument to "hidden".

  9. If unloadTimingInfo is given, then set unloadTimingInfo's unload event start time to the current high resolution time given oldDocument's relevant global object.

  10. If oldDocument's salvageable state is false, then fire an event named unload at oldDocument's relevant global object, with legacy target override flag set.

  11. If unloadTimingInfo is given, then set unloadTimingInfo's unload event end time to the current high resolution time given oldDocument's relevant global object.

  12. Decrease eventLoop's termination nesting level by 1.

  13. Set oldDocument's suspension time to the current high resolution time given document's relevant global object.

  14. Set oldDocument's suspended timer handles to the result of getting the keys for the map of active timers.

  15. Run any unloading document cleanup steps for oldDocument that are defined by this specification and other applicable specifications.

  16. If oldDocument's salvageable state is false, then destroy oldDocument.

  17. Decrease oldDocument's unload counter by 1.

  18. If newDocument is given, newDocument's was created via cross-origin redirects is false, and newDocument's origin is the same as oldDocument's origin, then set newDocument's previous document unload timing to unloadTimingInfo.

This specification defines the following unloading document cleanup steps. Other specifications can define more. Given a Document document:

  1. Let window be document's relevant global object.

  2. For each WebSocket object webSocket whose relevant global object is window, make disappear webSocket.

    If this affected any WebSocket objects, then set document's salvageable state to false.

  3. If document's salvageable state is false, then:

    1. For each EventSource object eventSource whose relevant global object is equal to window, forcibly close eventSource.

    2. Clear window's map of active timers.

7.5.10 Destroying documents

To destroy a Document document:

  1. Destroy the active documents of each of document's descendant navigables. In what order?

  2. Set document's salvageable state to false.

  3. Run any unloading document cleanup steps for document that are defined by this specification and other applicable specifications.

  4. Abort document.

  5. Remove any tasks whose document is document from any task queue (without running those tasks).

  6. Set document's browsing context to null.

  7. Set document's node navigable's active session history entry's document state's document to null.

  8. Remove document from the owner set of each WorkerGlobalScope object whose set contains document.

  9. For each workletGlobalScope in document's worklet global scopes, terminate workletGlobalScope.

Even after destruction, the Document object itself might still be accessible to script, in the case where we are destroying a nested navigable.

7.5.11 Aborting a document load

To abort a Document document:

  1. Abort the active documents of each of document's descendant navigables. In what order? If this results in any of those Document objects having their salvageable state set to false, then set document's salvageable state to false also.

  2. Cancel any instances of the fetch algorithm in the context of document, discarding any tasks queued for them, and discarding any further data received from the network for them. If this resulted in any instances of the fetch algorithm being canceled or any queued tasks or any network data getting discarded, then set document's salvageable state to false.

  3. If document's navigation id is non-null, then:

    1. Invoke WebDriver BiDi navigation aborted with document's browsing context, and new WebDriver BiDi navigation status whose whose id is document's navigation id, status is "canceled", and url is document's URL.

    2. Set document's navigation id to null.

  4. If document has an active parser, then:

    1. Set document's active parser was aborted to true.

    2. Abort that parser.

    3. Set document's salvageable state to false.

To stop document loading given a Document document:

  1. Let navigable be document's node navigable.

  2. If navigable is null, then return.

  3. If document's unload counter is 0, and navigable's ongoing navigation is a navigation ID, then set navigable's ongoing navigation to null.

    This will have the effect of aborting any ongoing navigations of navigable, since at certain points during navigation, changes to the ongoing navigation will cause further work to be abandoned.

  4. Abort document.

User agents may allow users to explicitly invoke the stop document loading for a Document.

Through their user interface, user agents also allow stopping traversals, i.e. cases where the ongoing navigation is "traversal". The above algorithm does not account for this. (On the other hand, user agents do not allow window.stop() to stop traversals, so the above algorithm is correct for that caller.) See issue #6905.

7.6 The `X-Frame-Options` header

The `X-Frame-Options` HTTP response header is a legacy way of controlling whether and how a Document may be loaded inside of a child browsing context. It is obsoleted by the frame-ancestors CSP directive, which provides more granular control over the same situations. It was originally defined in HTTP Header Field X-Frame-Options, but the definition and processing model here supersedes that document. [CSP] [RFC7034]

In particular, HTTP Header Field X-Frame-Options specified an `ALLOW-FROM` variant of the header, but that is not to be implemented.

Per the below processing model, if both a CSP frame-ancestors directive and an `X-Frame-Options` header are used in the same response, then `X-Frame-Options` is ignored.

For web developers and conformance checkers, its value ABNF is:

X-Frame-Options = "DENY" / "SAMEORIGIN"

To check a navigation response's adherence to `X-Frame-Options`, given navigation params navigationParams, a browsing context browsingContext, and an origin destinationOrigin:

  1. If browsingContext is not a child browsing context, then return true.

  2. For each policy of navigationParams's policy container's CSP list:

    1. If policy's disposition is not "enforce", then continue.

    2. If policy's directive set contains a frame-ancestors directive, then return true.

  3. Let rawXFrameOptions be the result of getting, decoding, and splitting `X-Frame-Options` from navigationParams's response's header list.

  4. Let xFrameOptions be a new set.

  5. For each value of rawXFrameOptions, append value, converted to ASCII lowercase, to xFrameOptions.

  6. If xFrameOptions's size is greater than 1, and xFrameOptions contains any of "deny", "allowall", or "sameorigin", then return false.

    The intention here is to block any attempts at applying `X-Frame-Options` which were trying to do something valid, but appear confused.

    This is the only impact of the legacy `ALLOWALL` value on the processing model.

  7. If xFrameOptions's size is greater than 1, then return true.

    This means it contains multiple invalid values, which we treat the same way as if the header was omitted entirely.

  8. If xFrameOptions[0] is "deny", then return false.

  9. If xFrameOptions[0] is "sameorigin", then:

    1. Let containerDocument be browsingContext's container document.

    2. While containerDocument is not null:

      1. If containerDocument's origin is not same origin with destinationOrigin, then return false.

      2. Let containerBC be containerDocument's browsing context.

      3. Set containerDocument to containerBC's container document, if containerBC is non-null; otherwise, null.

  10. Return true.

    If we've reached this point then we have a lone invalid value (which could potentially be one the legacy `ALLOWALL` or `ALLOW-FROM` forms). These are treated as if the header were omitted entirely.


The following table illustrates the processing of various values for the header, including non-conformant ones:

`X-Frame-Options`ValidResult
`DENY`embedding disallowed
`SAMEORIGIN`same-origin embedding allowed
`INVALID`embedding allowed
`ALLOWALL`embedding allowed
`ALLOW-FROM=https://example.com/`embedding allowed (from anywhere)

The following table illustrates how various non-conformant cases involving multiple values are processed:

`X-Frame-Options`Result
`SAMEORIGIN, SAMEORIGIN`same-origin embedding allowed
`SAMEORIGIN, DENY`embedding disallowed
`SAMEORIGIN,`embedding disallowed
`SAMEORIGIN, ALLOWALL`embedding disallowed
`SAMEORIGIN, INVALID`embedding disallowed
`ALLOWALL, INVALID`embedding disallowed
`ALLOWALL,`embedding disallowed
`INVALID, INVALID`embedding allowed

The same results are obtained whether the values are delivered in a single header whose value is comma-delimited, or in multiple headers.

7.7 The `Refresh` header

The `Refresh` HTTP response header is the HTTP-equivalent to a meta element with an http-equiv attribute in the Refresh state. It takes the same value and works largely the same. Its processing model is detailed in create and initialize a Document object.