diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 2f2e77e..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -* text=auto -*.bs diff=html linguist-language=HTML diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9a665df..a308cdf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,9 +11,11 @@ jobs: name: Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Build - run: make ci + - uses: actions/checkout@v3 + - name: Assemble out/ directory + run: | + mkdir out + mv spec.html out/index.html - name: Deploy if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} uses: peaceiris/actions-gh-pages@v3 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index c5ebf80..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/out/ -/spec.html diff --git a/.pr-preview.json b/.pr-preview.json deleted file mode 100644 index 54feea3..0000000 --- a/.pr-preview.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "src_file": "spec.bs", - "type": "bikeshed" -} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 799ad9c..baeb78d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,15 +4,6 @@ This repository is being used for work in the W3C [Web Platform Incubator Community Group](https://www.w3.org/community/wicg/) (WICG), governed by the [W3C Community License Agreement (CLA)](http://www.w3.org/community/about/agreements/cla/). To make substantive contributions, you must join the Community Group, thus signing the CLA. -## Editing the specification - -Edits to the specification are done in the `spec.bs` file, which is then compiled with the [Bikeshed](https://tabatkins.github.io/bikeshed/) spec pre-processor. - -To build the specification, you can use one of: - -- `make local`: uses a locally-installed copy of Bikeshed -- `make remote`: uses a Bikeshed web service, so you don't have to install anything locally - ## For maintainers: identifying contributors to a pull request If the author is not the sole contributor to a pull request, please identify all contributors in the pull request comment. diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 30c171f..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,5 +0,0 @@ -All Reports in this Repository are licensed by Contributors under the [W3C Software and Document License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). - -Contributions to Specifications are made under the [W3C CLA](https://www.w3.org/community/about/agreements/cla/). - -Contributions to Test Suites are made under the [W3C 3-clause BSD License](https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html). diff --git a/navigation_api.d.ts b/navigation_api.d.ts deleted file mode 100644 index 4feccd7..0000000 --- a/navigation_api.d.ts +++ /dev/null @@ -1,145 +0,0 @@ -interface Window { - readonly navigation: Navigation; -} - -interface NavigationEventMap { - "navigate": NavigateEvent; - "navigatesuccess": Event; - "navigateerror": ErrorEvent; - "currententrychange": NavigationCurrentEntryChangeEvent; -} - -interface NavigationResult { - committed: Promise; - finished: Promise; -} - -declare class Navigation extends EventTarget { - entries(): NavigationHistoryEntry[]; - readonly currentEntry: NavigationHistoryEntry|null; - updateCurrentEntry(options: NavigationUpdateCurrentEntryOptions): void; - readonly transition: NavigationTransition|null; - - readonly canGoBack: boolean; - readonly canGoForward: boolean; - - navigate(url: string, options?: NavigationNavigateOptions): NavigationResult; - reload(options?: NavigationReloadOptions): NavigationResult; - - traverseTo(key: string, options?: NavigationOptions): NavigationResult; - back(options?: NavigationOptions): NavigationResult; - forward(options?: NavigationOptions): NavigationResult; - - onnavigate: ((this: Navigation, ev: NavigateEvent) => any)|null; - onnavigatesuccess: ((this: Navigation, ev: Event) => any)|null; - onnavigateerror: ((this: Navigation, ev: ErrorEvent) => any)|null; - oncurrententrychange: ((this: Navigation, ev: NavigationCurrentEntryChangeEvent) => any)|null; - - addEventListener(type: K, listener: (this: Navigation, ev: NavigationEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; - addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; - removeEventListener(type: K, listener: (this: Navigation, ev: NavigationEventMap[K]) => any, options?: boolean | EventListenerOptions): void; - removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; -} - -declare class NavigationTransition { - readonly navigationType: NavigationType; - readonly from: NavigationHistoryEntry; - readonly finished: Promise; -} - -interface NavigationHistoryEntryEventMap { - "dispose": Event; -} - -declare class NavigationHistoryEntry extends EventTarget { - readonly key: string; - readonly id: string; - readonly url: string|null; - readonly index: number; - readonly sameDocument: boolean; - - getState(): unknown; - - ondispose: ((this: NavigationHistoryEntry, ev: Event) => any)|null; - - addEventListener(type: K, listener: (this: NavigationHistoryEntry, ev: NavigationHistoryEntryEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; - addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; - removeEventListener(type: K, listener: (this: NavigationHistoryEntry, ev: NavigationHistoryEntryEventMap[K]) => any, options?: boolean | EventListenerOptions): void; - removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; -} - -type NavigationType = 'reload'|'push'|'replace'|'traverse'; - -interface NavigationUpdateCurrentEntryOptions { - state: unknown; -} - -interface NavigationOptions { - info?: unknown; -} - -interface NavigationNavigateOptions extends NavigationOptions { - state?: unknown; - history?: "auto"|"push"|"replace"; -} - -interface NavigationReloadOptions extends NavigationOptions { - state?: unknown; -} - -declare class NavigationCurrentEntryChangeEvent extends Event { - constructor(type: string, eventInit?: NavigationCurrentEntryChangeEventInit); - - readonly navigationType: NavigationType|null; - readonly from: NavigationHistoryEntry; -} - -interface NavigationCurrentEntryChangeEventInit extends EventInit { - navigationType?: NavigationType|null; - from: NavigationHistoryEntry; -} - -declare class NavigateEvent extends Event { - constructor(type: string, eventInit?: NavigateEventInit); - - readonly navigationType: NavigationType; - readonly canIntercept: boolean; - readonly userInitiated: boolean; - readonly hashChange: boolean; - readonly destination: NavigationDestination; - readonly signal: AbortSignal; - readonly formData: FormData|null; - readonly downloadRequest: string|null; - readonly info?: unknown; - - intercept(options?: NavigationInterceptOptions): void; - scroll(): void; -} - -interface NavigateEventInit extends EventInit { - navigationType?: NavigationType; - canIntercept?: boolean; - userInitiated?: boolean; - hashChange?: boolean; - destination: NavigationDestination; - signal: AbortSignal; - formData?: FormData|null; - downloadRequest?: string|null; - info?: unknown; -} - -interface NavigationInterceptOptions { - handler?: () => Promise, - focusReset?: "after-transition"|"manual", - scroll?: "after-transition"|"manual" -} - -declare class NavigationDestination { - readonly url: string; - readonly key: string|null; - readonly id: string|null; - readonly index: number; - readonly sameDocument: boolean; - - getState(): unknown; -} diff --git a/spec.bs b/spec.bs deleted file mode 100644 index 03cff4f..0000000 --- a/spec.bs +++ /dev/null @@ -1,2156 +0,0 @@ - - - -
-spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/
-  type: dfn
-    text: append the following session history traversal steps; url: browsing-the-web.html#tn-append-session-history-traversal-steps
-    text: apply the history step; url: browsing-the-web.html#apply-the-history-step
-    text: check if unloading is user-canceled; url: browsing-the-web.html#checking-if-unloading-is-user-canceled
-    text: finalize a cross-document navigation; url: browsing-the-web.html#finalize-a-cross-document-navigation
-    text: get all navigables whose current session history entry will change or reload; url: browsing-the-web.html#get-all-navigables-whose-current-session-history-entry-will-change-or-reload
-    text: get the target history entry; url: browsing-the-web.html#getting-the-target-history-entry
-    text: navigate to a fragment; url: browsing-the-web.html#navigate-fragid
-    text: navigation and traversal task source; url: webappapis.html#navigation-and-traversal-task-source
-    text: navigation ID; url: browsing-the-web.html#navigation-id
-    text: node navigable; url: document-sequences.html#node-navigable
-    text: restore the history state object; url: browsing-the-web.html#restore-the-history-object-state
-    text: scroll to the fragment; url: browsing-the-web.html#scroll-to-the-fragment-identifier
-    text: serialized state; url: browsing-the-web.html#serialized-state
-    text: session history entry; url: browsing-the-web.html#session-history-entry
-    text: shared history push/replace state steps; url: nav-history-apis.html#shared-history-push/replace-state-steps
-    text: snapshot source snapshot params; url: browsing-the-web.html#snapshotting-source-snapshot-params
-    text: top-level traversable; url: document-sequences.html#top-level-traversable
-    text: traverse the history by a delta; url: browsing-the-web.html#traverse-the-history-by-a-delta
-    text: update document for history step application; url: browsing-the-web.html#update-document-for-history-step-application
-    text: update-only; url: browsing-the-web.html#changing-nav-continuation-update-only
-    text: URL and history update steps; url: browsing-the-web.html#url-and-history-update-steps
-    for: apply the history step
-      text: checkForUserCancellation; url: browsing-the-web.html#apply-history-step-check
-      text: initiatorToCheck; url: browsing-the-web.html#apply-history-step-initiator
-      text: sourceSnapshotParams; url: browsing-the-web.html#apply-history-step-source-snapshot
-    for: Document
-      text: indicated part; url: browsing-the-web.html#the-indicated-part-of-the-document
-      text: reactivate; url: browsing-the-web.html#reactivate-a-document
-      text: unload; url: document-lifecycle.html#unload-a-document
-    for: document state
-      text: origin; url: browsing-the-web.html#document-state-origin
-      text: request referrer policy; url: browsing-the-web.html#document-state-request-referrer-policy
-    for: history handling behavior
-      text: push; url: browsing-the-web.html#hh-push
-      text: replace; url: browsing-the-web.html#hh-replace
-    for: navigable
-      text: active document; url: document-sequences.html#nav-document
-      text: active session history entry; url: document-sequences.html#nav-active-history-entry
-      text: active window; url: document-sequences.html#nav-window
-      text: current session history entry; url: document-sequences.html#nav-current-history-entry
-      text: get session history entries; url: browsing-the-web.html#getting-session-history-entries
-      text: ongoing navigation; url: browsing-the-web.html#ongoing-navigation
-      text: top-level traversable; url: document-sequences.html#nav-top
-      text: traversable; url: document-sequences.html#nav-traversable
-    for: navigate
-      text: exceptionsEnabled; url: browsing-the-web.html#exceptions-enabled
-      text: historyHandling; url: browsing-the-web.html#navigation-hh
-    for: session history entry
-      text: document state; url: browsing-the-web.html#she-document-state
-      text: document; url: browsing-the-web.html#she-document
-      text: scroll position data; url: browsing-the-web.html#she-scroll-position
-      text: scroll restoration mode; url: browsing-the-web.html#she-scroll-restoration-mode
-      text: step; url: browsing-the-web.html#she-step
-      text: URL; url: browsing-the-web.html#she-url
-    for: traversable navigable
-      text: session history entries; url: document-sequences.html#tn-session-history-entries
-      text: session history traversal queue; url: document-sequences.html#tn-session-history-traversal-queue
-    for: URL and history update steps
-      text: historyHandling; url: browsing-the-web.html#uhus-historyhandling
-      text: serializedData; url: browsing-the-web.html#uhus-serializeddata
-    for: Window
-      text: navigable; url: nav-history-apis.html#window-navigable
-
- - - - - -

The {{Navigation}} class

- - -partial interface Window { - [Replaceable] readonly attribute Navigation navigation; -}; - - -Each {{Window}} object has an associated navigation API, which is a new {{Navigation}} instance created alongside the {{Window}}. - -The navigation getter steps are to return [=this=]'s [=Window/navigation API=]. - - -[Exposed=Window] -interface Navigation : EventTarget { - sequence<NavigationHistoryEntry> entries(); - readonly attribute NavigationHistoryEntry? currentEntry; - undefined updateCurrentEntry(NavigationUpdateCurrentEntryOptions options); - readonly attribute NavigationTransition? transition; - - readonly attribute boolean canGoBack; - readonly attribute boolean canGoForward; - - NavigationResult navigate(USVString url, optional NavigationNavigateOptions options = {}); - NavigationResult reload(optional NavigationReloadOptions options = {}); - - NavigationResult traverseTo(DOMString key, optional NavigationOptions options = {}); - NavigationResult back(optional NavigationOptions options = {}); - NavigationResult forward(optional NavigationOptions options = {}); - - attribute EventHandler onnavigate; - attribute EventHandler onnavigatesuccess; - attribute EventHandler onnavigateerror; - attribute EventHandler oncurrententrychange; -}; - -dictionary NavigationUpdateCurrentEntryOptions { - required any state; -}; - -dictionary NavigationOptions { - any info; -}; - -dictionary NavigationNavigateOptions : NavigationOptions { - any state; - NavigationHistoryBehavior history = "auto"; -}; - -dictionary NavigationReloadOptions : NavigationOptions { - any state; -}; - -dictionary NavigationResult { - Promise<NavigationHistoryEntry> committed; - Promise<NavigationHistoryEntry> finished; -}; - -enum NavigationHistoryBehavior { - "auto", - "push", - "replace" -}; - - -Each {{Navigation}} object has an associated entry list, a [=list=] of {{NavigationHistoryEntry}} objects, initially empty. - -Each {{Navigation}} object has an associated current entry index, an integer, initially −1. - -
- A {{Navigation}} |navigation| has entries and events disabled if the following steps return true: - - 1. If |navigation|'s [=relevant global object=]'s [=Window/browsing context=] is null, then return true. - - 1. Let |document| be |navigation|'s [=relevant global object=]'s [=associated Document=]. - - 1. If |document|'s is initial about:blank is true, then return true. - - 1. If |document|'s [=Document/origin=] is [=opaque origin|opaque=], then return true. - - 1. Return false. -
- -
- To update the entries for a same-document navigation given a {{Navigation}} instance |navigation|, a [=session history entry=] |destinationSHE|, and a {{NavigationType}} |navigationType|: - - 1. If |navigation| [=Navigation/has entries and events disabled=], then: - - 1. [=Assert=]: |navigation|'s [=Navigation/entry list=] [=list/is empty=]. - - 1. Return. - - 1. Let |oldCurrentNHE| be the [=Navigation/current entry=] of |navigation|. - - 1. Let |disposedNHEs| be a new empty [=list=]. - - 1. If |navigationType| is "{{NavigationType/traverse}}": - - 1. Set |navigation|'s [=Navigation/current entry index=] to the index of the {{NavigationHistoryEntry}} within |navigation|'s [=Navigation/entry list=] whose [=NavigationHistoryEntry/session history entry=]'s [=session history entry/navigation API key=] equals |destinationSHE|'s [=session history entry/navigation API key=]. - - 1. [=Assert=]: such a {{NavigationHistoryEntry}} must exist, because this algorithm is only called for same-document traversals. (Cross-document traversals will instead call either [=Navigation/update the entries for reactivation=] or [=Navigation/initialize the entries for a new Navigation=].) - - 1. Otherwise, if |navigationType| is "{{NavigationType/push}}": - - 1. Set |navigation|'s [=Navigation/current entry index=] to |navigation|'s [=Navigation/current entry index=] + 1. - - 1. Let |i| be |navigation|'s [=Navigation/current entry index=]. - - 1. [=iteration/While=] |i| < |navigation|'s [=Navigation/entry list=]'s [=list/size=]: - - 1. [=list/Append=] |navigation|'s [=Navigation/entry list=][|i|] to |disposedNHEs|. - - 1. Set |i| to |i| + 1. - - 1. [=list/Remove=] all [=list/items=] in |disposedNHEs| from |navigation|'s [=Navigation/entry list=]. - - 1. Otherwise, if |navigationType| is "{{NavigationType/replace}}": - - 1. [=list/Append=] |oldCurrentNHE| to |disposedNHEs|. - - 1. If |navigationType| is "{{NavigationType/push}}" or "{{NavigationType/replace}}": - - 1. Let |newNHE| be a [=new=] {{NavigationHistoryEntry}} created in the [=relevant realm=] of |navigation|. - - 1. Set |newNHE|'s [=NavigationHistoryEntry/session history entry=] to |destinationSHE|. - - 1. Set |navigation|'s [=Navigation/entry list=][|navigation|'s [=Navigation/current entry index=]] to |newNHE|. - - 1. [=Assert=]: by this point, the [=Navigation/current entry=] of |navigation| is different from |oldCurrentNHE|. - - 1. If |navigation|'s [=Navigation/ongoing navigation=] is non-null, then [=navigation API method navigation/notify about the committed-to entry=] given |navigation|'s [=Navigation/ongoing navigation=] and the [=Navigation/current entry=] of |navigation|. - -

It is important to do this before firing the {{NavigationHistoryEntry/dispose}} or {{Navigation/currententrychange}} events, since event handlers could start another navigation, or otherwise change the value of |navigation|'s [=Navigation/ongoing navigation=]. - - 1. [=Prepare to run script=] given |navigation|'s [=relevant settings object=]. - -

See a similar note elsewhere for other navigation API events to understand why we do this. - - 1. [=Fire an event=] named {{Navigation/currententrychange}} at |navigation| using {{NavigationCurrentEntryChangeEvent}}, with its {{NavigationCurrentEntryChangeEvent/navigationType}} attribute initialized to |navigationType| and its {{NavigationCurrentEntryChangeEvent/from}} initialized to |oldCurrentNHE|. - - 1. [=list/For each=] |disposedNHE| of |disposedNHEs|: - - 1. [=Fire an event=] named {{NavigationHistoryEntry/dispose}} at |disposedNHE|. - - 1. [=Clean up after running script=] given |navigation|'s [=relevant settings object=]. -

- -
- To update the entries for reactivation given a {{Navigation}} instance |navigation|, a [=list=] of [=session history entries=] |newSHEs|, and a [=session history entry=] |reactivatedSHE|: - - 1. Let |newNHEs| be an empty list. - - 1. Let |oldNHEs| be a [=list/clone=] of |navigation|'s [=Navigation/entry list=]. - - 1. [=list/For each=] |newSHE| of |newSHEs|: - - 1. Let |newNHE| be null. - - 1. If |oldNHEs| [=list/contains=] a {{NavigationHistoryEntry}} |matchingOldNHE| whose [=NavigationHistoryEntry/session history entry=] is |newSHE|, then: - - 1. Set |newNHE| to |matchingOldNHE|. - - 1. [=list/Remove=] |matchingOldNHE| from |oldNHEs|. - - 1. Otherwise: - - 1. Set |newNHE| to a [=new=] {{NavigationHistoryEntry}} created in the [=relevant realm=] of |navigation|. - - 1. Set |newNHE|'s [=NavigationHistoryEntry/session history entry=] to |newSHE|. - - 1. [=list/Append=] |newNHE| to |newNHEs|. - - 1. [=list/For each=] |disposedNHE| of |oldNHEs|: - - 1. Set |disposedNHE|'s [=NavigationHistoryEntry/index=] to −1. - - 1. Set |navigation|'s [=Navigation/entry list=] to |newNHEs|. - - 1. Set |navigation|'s [=Navigation/current entry index=] to the result of [=getting the navigation API history index=] of |reactivatedSHE| within |navigation|. - - 1. [=Queue a global task=] on the [=navigation and traversal task source=] given |navigation|'s [=relevant global object=] to run the following steps: - - 1. [=list/For each=] |disposedNHE| of |oldNHEs|: - - 1. [=Fire an event=] named {{NavigationHistoryEntry/dispose}} at |disposedNHE|. - -

We delay these events by a task to ensure that {{NavigationHistoryEntry/dispose}} events will fire after the {{Window/pageshow}} event. (However, the rest of this algorithm runs before the {{Window/pageshow}} event fires, to ensure that {{Navigation/entries()|navigation.entries()}} and {{Navigation/currentEntry|navigation.currentEntry}} will have correctly-updated values during any {{Window/pageshow}} event handlers.) This is motivated by a desire to have {{Window/pageshow}} be the first event a page receives upon reactivation. -

- -
- To initialize the entries for a new {{Navigation}} given a {{Navigation}} instance |navigation|, a [=list=] of [=session history entries=] |newSHEs|, and a [=session history entry=] |initialSHE|: - - 1. [=Assert=]: |navigation|'s [=Navigation/entry list=] [=list/is empty=]. - - 1. [=Assert=]: |navigation|'s [=Navigation/current entry index=] is −1. - - 1. If |navigation| [=Navigation/has entries and events disabled=], then return. - - 1. [=list/For each=] |newSHE| of |newSHEs|: - - 1. Let |newNHE| be a [=new=] {{NavigationHistoryEntry}} created in the [=relevant realm=] of |navigation|. - - 1. Set |newNHE|'s [=NavigationHistoryEntry/session history entry=] to |newSHE|. - - 1. [=list/Append=] |newNHE| to |navigation|'s [=Navigation/entry list=]. - - 1. Set |navigation|'s [=Navigation/current entry index=] to the result of [=getting the navigation API history index=] of |initialSHE| within |navigation|. -
- -
- To get the navigation API history index of a [=session history entry=] |she| within a {{Navigation}} |navigation|: - - 1. Let |index| be 0. - - 1. [=list/For each=] |ahe| of |navigation|'s [=Navigation/entry list=]: - - 1. If |ahe|'s [=NavigationHistoryEntry/session history entry=] is equal to |she|, then return |index|. - - 1. Increment |index| by 1. - - 1. [=Assert=]: this step is never reached. -
- -

Introspecting the navigation API history entry list

- -
-
entries = {{Window/navigation}}.{{Navigation/entries()|entries}}() -
-

Returns an array of {{NavigationHistoryEntry}} instances representing the current navigation history entry list, i.e. all session history entries for this {{Window}} that are [=same origin=] and contiguous to the current session history entry. -

- -
{{Window/navigation}}.{{Navigation/canGoBack}} -
-

Returns true if the current {{NavigationHistoryEntry}} is not the first one in the navigation history entry list. -

- -
{{Window/navigation}}.{{Navigation/canGoForward}} -
-

Returns true if the current {{NavigationHistoryEntry}} is not the last one in the navigation history entry list. -

-
- -
- The entries() method steps are: - - 1. If [=this=] [=Navigation/has entries and events disabled=], then return the empty list. - - 1. Return [=this=]'s [=Navigation/entries list=]. -
- -
- The canGoBack getter steps are: - - 1. If [=this=] [=Navigation/has entries and events disabled=], then return false. - - 1. [=Assert=]: [=this=]'s [=Navigation/current entry index=] is not −1. - - 1. If [=this=]'s [=Navigation/current entry index=] is 0, then return false. - - 1. Return true. -
- -
- The canGoForward getter steps are: - - 1. If [=this=] [=Navigation/has entries and events disabled=], then return false. - - 1. [=Assert=]: [=this=]'s [=Navigation/current entry index=] is not −1. - - 1. If [=this=]'s [=Navigation/current entry index=] is equal to [=this=]'s [=Navigation/entry list=]'s [=list/size=] − 1, then return false. - - 1. Return true. -
- -

The current entry

- - -[Exposed=Window] -interface NavigationCurrentEntryChangeEvent : Event { - constructor(DOMString type, NavigationCurrentEntryChangeEventInit eventInit); - - readonly attribute NavigationType? navigationType; - readonly attribute NavigationHistoryEntry from; -}; - -dictionary NavigationCurrentEntryChangeEventInit : EventInit { - NavigationType? navigationType = null; - required NavigationHistoryEntry destination; -}; - - -
-
{{Window/navigation}}.{{Navigation/currentEntry}} -
-

The current {{NavigationHistoryEntry}}. -

- -
{{Window/navigation}}.{{Navigation/updateCurrentEntry()|updateCurrentEntry}}({ {{NavigationUpdateCurrentEntryOptions/state}} }) -
-

Update the [=session history entry/navigation API state=] of the current {{NavigationHistoryEntry}}, without performing a navigation like {{Navigation/reload()|navigation.reload()}} would do. - -

This method is best used to capture updates to the page that have already happened, and need to be reflected into the navigation API state. For cases where the state update is meant to drive a page update, instead use {{Navigation/navigate()|navigation.navigate()}} or {{Navigation/reload()|navigation.reload()}}. -

-
- -
- The current entry for a {{Navigation}} |navigation| is the result running of the following algorithm: - - 1. If |navigation| [=Navigation/has entries and events disabled=], then return null. - - 1. [=Assert=]: |navigation|'s [=Navigation/current entry index=] is not −1. - - 1. Return |navigation|'s [=Navigation/entry list=][|navigation|'s [=Navigation/current entry index=]]. -
- -

- The currentEntry getter steps are to return the [=Navigation/current entry=] for [=this=]. -

- -
- The updateCurrentEntry(|options|) method steps are: - - 1. Let |current| be the [=Navigation/current entry=] for [=this=]. - - 1. If |current| is null, then throw an "{{InvalidStateError}}" {{DOMException}}. - - 1. Let |serializedState| be [$StructuredSerializeForStorage$](|options|["{{NavigationUpdateCurrentEntryOptions/state}}"]), rethrowing any exceptions. - - 1. Set |current|'s [=NavigationHistoryEntry/session history entry=]'s [=session history entry/navigation API state=] to |serializedState|. - - 1. [=Fire an event=] named {{Navigation/currententrychange}} at [=this=] using {{NavigationCurrentEntryChangeEvent}}, with its {{NavigationCurrentEntryChangeEvent/navigationType}} attribute initialized to null and its {{NavigationCurrentEntryChangeEvent/from}} initialized to |current|. -
- -

Ongoing navigation tracking

- - -[Exposed=Window] -interface NavigationTransition { - readonly attribute NavigationType navigationType; - readonly attribute NavigationHistoryEntry from; - readonly attribute Promise<undefined> finished; -}; - - -
-
{{Window/navigation}}.{{Navigation/transition}} -
-

A {{NavigationTransition}} object representing any ongoing navigation that hasn't yet reached the {{Navigation/navigatesuccess}} or {{Navigation/navigateerror}} stage, if one exists, or null if there is no such transition ongoing. - -

Since {{Navigation/currentEntry|navigation.currentEntry}} (and other properties like {{Location/href|location.href}}) are updated immediately upon navigation, this {{Navigation/transition|navigation.transition}} property is useful for determining when such navigations are not yet fully settled, according to any handlers passed to {{NavigateEvent/intercept()|event.intercept()}}. -

- -
{{Window/navigation}}.{{Navigation/transition}}.{{NavigationTransition/navigationType}}
-
-

One of "{{NavigationType/reload}}", "{{NavigationType/push}}", "{{NavigationType/replace}}", or "{{NavigationType/traverse}}", indicating what type of navigation this transition is for. -

- -
{{Window/navigation}}.{{Navigation/transition}}.{{NavigationTransition/from}}
-
-

The {{NavigationHistoryEntry}} from which the transition is coming. This can be useful to compare against {{Navigation/currentEntry|navigation.currentEntry}}. -

- -
{{Window/navigation}}.{{Navigation/transition}}.{{NavigationTransition/finished}}
-
-

A promise which fulfills at the same time the {{Navigation/navigatesuccess}} event fires, or rejects at the same time the {{Navigation/navigateerror}} fires. -

- - -
- -A {{Navigation}} has a transition, which is a {{NavigationTransition}} or null. - -The transition getter steps are to return [=this=]'s [=Navigation/transition=]. - -
- -A {{NavigationTransition}} has an associated navigation type, which is a {{NavigationType}}. - -A {{NavigationTransition}} has an associated from entry, which is a {{NavigationHistoryEntry}}. - -A {{NavigationTransition}} has an associated finished promise, which is an {{Promise}}. - -The navigationType getter steps are to return [=this=]'s [=NavigationTransition/navigation type=]. - -The from getter steps are to return [=this=]'s [=NavigationTransition/from entry=]. - -The finished getter steps are to return [=this=]'s [=NavigationTransition/finished promise=]. - -
- -During any given navigation, the {{Navigation}} object needs to keep track of the following: - - - - - - - - - - -
For all navigations -
State - Duration - Explanation -
The {{NavigateEvent}} - For the duration of event firing - So that if the navigation is canceled while the event is firing, we can [=Event/canceled flag|cancel=] the event. -
The event's {{NavigateEvent/signal}} - Until all promises returned from handlers passed to {{NavigateEvent/intercept()}} have settled - So that if the navigation is canceled, we can [=AbortSignal/signal abort=]. -
Whether a new element was focused - Until all promises returned from handlers passed to {{NavigateEvent/intercept()}} have settled - So that if one was, focus is not [=potentially reset the focus|reset=] -
The {{NavigationHistoryEntry}} being navigated to - From when it is determined, until all promises returned from handlers passed to {{NavigateEvent/intercept()}} have settled - So that we know what to [=resolve=] any {{NavigationResult/committed}} and {{NavigationResult/finished}} promises with. -
Any {{NavigationResult/finished}} {{Promise}} that was returned - Until all promises returned from handlers passed to {{NavigateEvent/intercept()}} have settled - So that we can [=resolve=] or [=reject=] it appropriately. -
- - - - - - -
For non-"{{NavigationType/traverse}}" navigations -
State - Duration - Explanation -
Any {{NavigationNavigateOptions/state}} - For the duration of event firing - So that we can update the current entry's state after the event successfully finishes firing without being canceled. -
- - - - - - - - -
For "{{NavigationType/traverse}}" navigations -
State - Duration - Explanation -
Any {{NavigationOptions/info}} - Until the task is queued to fire the {{Navigation/navigate}} event - So that we can use it to fire the {{Navigation/navigate}} event after the the trip through the [=traversable navigable/session history traversal queue=]. -
Any {{NavigationResult/committed}} {{Promise}} that was returned - Until the session history is updated (inside that same task) - So that we can [=resolve=] or [=reject=] it appropriately. -
Whether {{NavigateEvent/intercept()}} was called - Until the session history is updated (inside that same task) - So that we can suppress the normal scroll restoration logic in favor of the chosen {{NavigationInterceptOptions/scroll}} option value. -
- -Furthermore, we need to account for the fact that there might be multiple traversals queued up, e.g. via - - -const key1 = navigation.entries()[navigation.currentEntry.index - 1].key; -const key2 = navigation.entries()[navigation.currentEntry.index + 1].key; - -navigation.traverseTo(key1); // intentionally no await -navigation.traverseTo(key2); - - -And, while non-traversal navigations cannot be queued in the same way since a new non-traversal navigation cancels an old one, we need to keep some state around so that we can properly cancel the old one. That is, given - - -const p1 = navigation.navigate(url1).finished; -const p2 = navigation.navigate(url2).finished; - - -we need to ensure that when navigating to `url2`, we still have the {{Promise}} `p1` around so that we can reject it. We can't just get rid of any ongoing navigation promises the moment the second call to {{Navigation/navigate()}} happens. - -We also need to ensure that, if we start a new navigation, navigations which have gotten as far as firing {{Navigation/navigate}} events, but not yet as far as firing {{Navigation/navigatesuccess}} or {{Navigation/navigateerror}}, get [=finalized with an aborted navigation error=]. - -We end up accomplishing all this using the following setup: - -Each {{Navigation}} object has an associated ongoing navigate event, a {{NavigateEvent}} or null, initially null. - -Each {{Navigation}} object has an associated ongoing navigation signal, which is an {{AbortSignal}} or null, initially null. - -Each {{Navigation}} object has an associated focus changed during ongoing navigation, which is a boolean, initially false. - -Each {{Navigation}} object has an associated suppress normal scroll restoration during ongoing navigation, which is a boolean, initially false. - -Each {{Navigation}} object has an associated ongoing navigation, which is a [=navigation API method navigation=] or null, initially null. - -Each {{Navigation}} object has an associated upcoming non-traverse navigation, which is a [=navigation API method navigation=] or null, initially null. - -Each {{Navigation}} object has an associated upcoming traverse navigations, which is a [=map=] from strings to [=navigation API method navigations=], initially empty. - -An navigation API method navigation is a [=struct=] with the following [=struct/items=]: - -* An navigation object, a {{Navigation}} -* A key, a string or null -* An info, a JavaScript value -* A serialized state, a [=serialized state=] or null -* A committed-to entry, a {{NavigationHistoryEntry}} or null -* A committed promise, a {{Promise}} -* A finished promise, a {{Promise}} - -

We need to store the [=Navigation/ongoing navigation signal=], [=Navigation/focus changed during ongoing navigation=], and [=Navigation/suppress normal scroll restoration during ongoing navigation=] separately from the [=navigation API method navigation=] struct, since it needs to be tracked even for navigations that are not via the navigation API. - -

- To set the upcoming non-traverse navigation given a {{Navigation}} |navigation|, a JavaScript value |info|, and a [=serialized state=]-or-null |serializedState|: - - 1. Let |committedPromise| and |finishedPromise| be [=a new promise|new promises=] created in |navigation|'s [=relevant realm=]. - - 1. [=Mark as handled=] |finishedPromise|. - -
- The web developer doesn't necessarily care about |finishedPromise| being rejected: - - * They might only care about |committedPromise|. - - * They could be doing multiple synchronous navigations within the same task, in which case all but the last will be aborted (causing their |finishedPromise| to reject). This could be an application bug, but also could just be an emergent feature of disparate parts of the application overriding each others' actions. - - * They might prefer to listen to other transition-failure signals instead of |finishedPromise|, e.g., the {{Navigation/navigateerror}} event, or the {{NavigationTransition/finished|navigation.transition.finished}} promise. - - As such, we mark it as handled to ensure that it never triggers {{Window/unhandledrejection}} events. -
- - 1. Let |ongoingNavigation| be a [=navigation API method navigation=] whose [=navigation API method navigation/navigation object=] is |navigation|, [=navigation API method navigation/key=] is null, [=navigation API method navigation/info=] is |info|, [=navigation API method navigation/serialized state=] is |serializedState|, [=navigation API method navigation/committed-to entry=] is null, [=navigation API method navigation/committed promise=] is |committedPromise|, and [=navigation API method navigation/finished promise=] is |finishedPromise|. - - 1. [=Assert=]: |navigation|'s [=Navigation/upcoming non-traverse navigation=] is null. - - 1. Set |navigation|'s [=Navigation/upcoming non-traverse navigation=] to |ongoingNavigation|. - - 1. Return |ongoingNavigation|. -
- -
- To set an upcoming traverse navigation given a {{Navigation}} |navigation|, a string |key|, and a JavaScript value |info|: - - 1. Let |committedPromise| and |finishedPromise| be [=a new promise|new promises=] created in |navigation|'s [=relevant realm=]. - - 1. [=Mark as handled=] |finishedPromise|. - -

See the previous discussion as to why this is done.

- - 1. Let |traversal| be a [=navigation API method navigation=] whose whose [=navigation API method navigation/navigation object=] is |navigation|, [=navigation API method navigation/key=] is |key|, [=navigation API method navigation/info=] is |info|, [=navigation API method navigation/serialized state=] is null, [=navigation API method navigation/committed-to entry=] is null, [=navigation API method navigation/committed promise=] is |committedPromise|, and [=navigation API method navigation/finished promise=] is |finishedPromise|. - - 1. Set |navigation|'s [=Navigation/upcoming traverse navigations=][|key|] to |traversal|. - - 1. Return |traversal|. -
- -
- To promote the upcoming navigation to ongoing given a {{Navigation}} |navigation| and a string-or-null |destinationKey|: - - 1. [=Assert=]: |navigation|'s [=Navigation/ongoing navigation=] is null. - - 1. If |destinationKey| is not null, then: - - 1. [=Assert=]: |navigation|'s [=Navigation/upcoming non-traverse navigation=] is null. - - 1. If |navigation|'s [=Navigation/upcoming traverse navigations=][|destinationKey|] [=map/exists=], then: - - 1. Set |navigation|'s [=Navigation/ongoing navigation=] to |navigation|'s [=Navigation/upcoming traverse navigations=][|destinationKey|]. - - 1. [=map/Remove=] |navigation|'s [=Navigation/upcoming traverse navigations=][|destinationKey|]. - - 1. Otherwise, - - 1. Set |navigation|'s [=Navigation/ongoing navigation=] to |navigation|'s [=Navigation/upcoming non-traverse navigation=]. - - 1. Set |navigation|'s [=Navigation/upcoming non-traverse navigation=] to null. -
- -
- To clean up a [=navigation API method navigation=] |navigation|: - - 1. Let |navigation| be |navigation|'s [=navigation API method navigation/navigation object=]. - - 1. If |navigation|'s [=Navigation/ongoing navigation=] is |navigation|, then set |navigation|'s [=Navigation/ongoing navigation=] to null. - - 1. Otherwise, - - 1. [=Assert=]: |navigation|'s [=navigation API method navigation/key=] is not null. - - 1. [=Assert=]: |navigation|'s [=Navigation/upcoming traverse navigations=][|navigation|'s [=navigation API method navigation/key=]] [=map/exists=]. - - 1. [=map/Remove=] |navigation|'s [=Navigation/upcoming traverse navigations=][|navigation|'s [=navigation API method navigation/key=]]. -
- -
- To notify about the committed-to entry given a [=navigation API method navigation=] |navigation| and a {{NavigationHistoryEntry}} |entry|: - - 1. Set |navigation|'s [=navigation API method navigation/committed-to entry=] to |entry|. - - 1. If |navigation|'s [=navigation API method navigation/serialized state=] is not null, then set |entry|'s [=NavigationHistoryEntry/session history entry=]'s [=session history entry/navigation API state=] to |navigation|'s [=navigation API method navigation/serialized state=]. - -

If it's null, then we're traversing to |entry| via {{Navigation/traverseTo()}}, which does not allow changing the state. - -

After this point, |navigation|'s [=navigation API method navigation/serialized state=] is no longer needed. Implementations might want to clear it out to avoid keeping it alive for the lifetime of the [=navigation API method navigation=]. - - 1. [=Resolve=] |navigation|'s [=navigation API method navigation/committed promise=] with |entry|. - -

After this point, |navigation|'s [=navigation API method navigation/committed promise=] is only needed in cases where it has not yet been returned to author code. Implementations might want to clear it out to avoid keeping it alive for the lifetime of the [=navigation API method navigation=]. -

- -
- To resolve the finished promise for a [=navigation API method navigation=] |navigation|: - - 1. If |navigation|'s [=navigation API method navigation/finished promise=] is null, then return. - - 1. [=Resolve=] |navigation|'s [=navigation API method navigation/finished promise=] with its [=navigation API method navigation/committed-to entry=]. - - 1. [=navigation API method navigation/Clean up=] |navigation|. -
- -
- To reject the finished promise for a [=navigation API method navigation=] |navigation| with a JavaScript value |exception|: - - 1. If |navigation|'s [=navigation API method navigation/finished promise=] is null, then return. - - 1. If |navigation|'s [=navigation API method navigation/committed promise=] is not null, then [=reject=] |navigation|'s [=navigation API method navigation/committed promise=] with |exception|. - - 1. [=Reject=] |navigation|'s [=navigation API method navigation/finished promise=] with |exception|. - - 1. [=navigation API method navigation/Clean up=] |navigation|. -
- - - -
-
{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/navigate(url, options)|navigate}}(url) -
{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/navigate(url, options)|navigate}}(url, options) -
-

Navigates the current page to the given url. options can contain the following values: - - * {{NavigationNavigateOptions/history}} can be set to "{{NavigationHistoryBehavior/replace}}" to replace the current session history entry, instead of pushing a new one. - * {{NavigationOptions/info}} can be set to any value; it will populate the {{NavigateEvent/info}} property of the corresponding {{Navigation/navigate}} event. - * {{NavigationNavigateOptions/state}} can be set to any serializable value; it will populate the state retrieved by {{NavigationHistoryEntry/getState()|navigation.currentEntry.getState()}} once the navigation completes, for same-document navigations. (It will be ignored for navigations that end up cross-document.) - -

By default this will perform a full navigation (i.e., a cross-document navigation, unless the given URL differs only in a fragment from the current one). The {{Navigation/navigate}} event's {{NavigateEvent/intercept()}} method can be used to convert it into a same-document navigation. - -

The returned promises will behave as follows: - - * For navigations that get aborted, both promises will reject with an "{{AbortError}}" {{DOMException}}. - * For same-document navigations created by using the {{Navigation/navigate}} event's {{NavigateEvent/intercept()}} method, {{NavigationResult/committed}} will fulfill immediately, and {{NavigationResult/finished}} will fulfill or reject according to any promises returned by handlers passed to {{NavigateEvent/intercept()}}. - * For other same-document navigations (e.g., non-intercepted [=navigate to a fragment|fragment navigations=], both promises will fulfill immediately. - * For cross-document navigations, or navigations that result in 204/205 [=response/statuses=] or `Content-Disposition: attachment` header fields from the server (and thus do not actually navigate), both promises will never settle. - -

In all cases, when the returned promises fulfill, it will be with the {{NavigationHistoryEntry}} that was navigated to. -

- -
{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/reload(options)|reload}}(options) -
-

Reloads the current page. The {{NavigationOptions/info}} and {{NavigationReloadOptions/state}} options behave as described above. - -

The default behavior of performing a from-network-or-cache reload of the current page can be overriden by using the {{Navigation/navigate}} event's {{NavigateEvent/intercept()}} method. Doing so will mean this call only updates state or passes along the appropriate {{NavigationOptions/info}}, plus performing whatever actions the {{Navigation/navigate}} event handler sees fit to carry out. - -

The returned promises will behave as follows: - - * If the reload is aborted, both promises will reject with an "{{AbortError}}" {{DOMException}}. - * If the reload is intercepted by using the {{Navigation/navigate}} event's {{NavigateEvent/intercept()}} method, {{NavigationResult/committed}} will fulfill immediately, and {{NavigationResult/finished}} will fulfill or reject according to the promises passed to {{NavigateEvent/intercept()}}. - * Otherwise, both promises will never settle. -

-
- - - -
- The navigate(|url|, |options|) method steps are: - - 1. Let |document| be [=this=]'s [=relevant global object=]'s [=associated document=]. - - 1. Let |navigable| be |navigation|'s [=relevant global object=]'s [=Window/navigable=]. - - 1. [=Assert=]: |navigable| is not null. - - 1. Parse |url| relative to [=this=]'s [=relevant settings object=]. If that returns failure, then return [=an early error result=] for a "{{SyntaxError}}" {{DOMException}}. Otherwise, let |urlRecord| be the resulting URL record. - - 1. If |options|["{{NavigationNavigateOptions/history}}"] is "{{NavigationHistoryBehavior/push}}", and any of the following are true: - - * |document|'s is initial about:blank is true; - * |document| is not completely loaded; - * |url| equals |document|'s [=Document/URL=]; or - * |url|'s [=url/scheme=] is "`javascript`" - - then return [=an early error result=] for a "{{NotSupportedError}}" {{DOMException}}. - -
-

These are the conditions under which a push navigation will be converted into a replace navigation by the navigate algorithm or by the below step. If the developer explicitly requested a push, we fail to let them know it won't happen. - -

In the future, we could consider loosening some of these conditions, e.g., allowing explicitly-requested push navigations to the current URL or before the document is completely loaded. -

- - 1. Let |state| be |options|["{{NavigationNavigateOptions/state}}"] if it exists; otherwise, undefined. - - 1. Let |serializedState| be [$StructuredSerializeForStorage$](|state|). If this throws an exception, then return [=an early error result=] for that exception. - -

It is important to perform this step early, since serialization can invoke web developer code, which in turn might change the state checked in later steps.

- - 1. Let |info| be |options|["{{NavigationOptions/info}}"] if it exists; otherwise, undefined. - - 1. Let |historyHandling| be "`replace`" if |options|["{{NavigationNavigateOptions/history}}"] is "{{NavigationHistoryBehavior/replace}}" or if |document| is not completely loaded; otherwise, "`push`". - - 1. If |document| is not [=Document/fully active=], then return [=an early error result=] for an "{{InvalidStateError}}" {{DOMException}}. - - 1. If |document|'s unload counter is greater than 0, then return [=an early error result=] for an "{{InvalidStateError}}" {{DOMException}}. - - 1. Let |ongoingNavigation| be the result of [=Navigation/setting the upcoming non-traverse navigation=] for |navigation| given |info|. - - 1. Navigate |navigable| to |urlRecord| using |document|, with [=navigate/historyHandling=] set to |historyHandling| and [=navigate/navigationAPIState=] set to |serializedState|. - -

Unlike {{Location/assign()|location.assign()}} and friends, which are exposed across [=same origin-domain|origin-domain=] boundaries, {{Navigation/navigate()|navigation.navigate()}} can only be accessed by code with direct synchronous access to the {{Window/navigation}} property. Thus, we avoid the complications around tracking the source document, and we don't need to deal with the allowed by sandboxing to navigate check and its accompanying [=navigate/exceptionsEnabled=] flag. We just treat all navigations as being initiated by the {{Navigation}} object itself. - - 1. If |navigation|'s [=Navigation/upcoming non-traverse navigation=] is |ongoingNavigation|, then: - -

This means the navigate algorithm bailed out before ever getting to the [=inner navigate event firing algorithm=] which would [=Navigation/promote the upcoming navigation to ongoing=]. - - 1. Set |navigation|'s [=Navigation/upcoming non-traverse navigation=] to null. - - 1. Return [=an early error result=] for an "{{AbortError}}" {{DOMException}}. - - 1. Return «[ "{{NavigationResult/committed}}" → |ongoingNavigation|'s [=navigation API method navigation/committed promise=], "{{NavigationResult/finished}}" → |ongoingNavigation|'s [=navigation API method navigation/finished promise=] ]». -

- -
- The reload(|options|) method steps are: - - 1. Let |document| be [=this=]'s [=relevant global object=]'s [=associated document=]. - - 1. Let |navigable| be |navigation|'s [=relevant global object=]'s [=Window/navigable=]. - - 1. [=Assert=]: |navigable| is not null. - - 1. Let |serializedState| be null. - - 1. If |options|["{{NavigationReloadOptions/state}}"] [=map/exists=], then set |serializedState| to [$StructuredSerializeForStorage$](|options|["{{NavigationReloadOptions/state}}"]). If this throws an exception, then return [=an early error result=] for that exception. - -

It is important to perform this step early, since serialization can invoke web developer code, which in turn might change the state checked in later steps.

- - 1. Otherwise, - - 1. Let |current| be the [=Navigation/current entry=] of [=this=]. - - 1. If |current| is not null, then set |serializedState| to |current|'s [=session history entry/navigation API state=]. - - 1. Otherwise, set |serializedState| to [$StructuredSerializeForStorage$](undefined). - - 1. Let |info| be |options|["{{NavigationOptions/info}}"] if it exists; otherwise, undefined. - - 1. If |document| is not [=Document/fully active=], then return [=an early error result=] for an "{{InvalidStateError}}" {{DOMException}}. - - 1. If |document|'s unload counter is greater than 0, then return [=an early error result=] for an "{{InvalidStateError}}" {{DOMException}}. - - 1. Let |ongoingNavigation| be the result of [=Navigation/setting the upcoming non-traverse navigation=] for |navigation| given |info|. - - 1. Reload |navigable| with [=reload/navigationAPIState=] set to |serializedState|. - - 1. If |navigation|'s [=Navigation/upcoming non-traverse navigation=] is |ongoingNavigation|, then: - -

This means the reload algorithm bailed out before ever getting to the [=inner navigate event firing algorithm=] which would [=Navigation/promote the upcoming navigation to ongoing=]. - - 1. Set |navigation|'s [=Navigation/upcoming non-traverse navigation=] to null. - - 1. Return [=an early error result=] for an "{{AbortError}}" {{DOMException}}. - - 1. Return «[ "{{NavigationResult/committed}}" → |ongoingNavigation|'s [=navigation API method navigation/committed promise=], "{{NavigationResult/finished}}" → |ongoingNavigation|'s [=navigation API method navigation/finished promise=] ]». -

- -

- An an early error result for an exception |e| is a dictionary instance given by «[ "{{NavigationResult/committed}}" → [=a promise rejected with=] |e|, "{{NavigationResult/finished}}" → [=a promise rejected with=] |e| ]». -

- -

Traversing

- -
-
{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/traverseTo(key)|traverseTo}}(key) -
{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/traverseTo(key, options)|traverseTo}}(key, { {{NavigationOptions/info}} }) -
-

Traverses the session history to the closest [=session history entry=] that matches the {{NavigationHistoryEntry}} with the given key. {{NavigationOptions/info}} can be set to any value; it will populate the {{NavigateEvent/info}} property of the corresponding {{Navigation/navigate}} event. - -

If a traversal to that [=session history entry=] is already in progress, then this will return the promises for that original traversal, and {{NavigationOptions/info}} will be ignored. - -

The returned promises will behave as follows: - - * If there is no {{NavigationHistoryEntry}} in {{Navigation/entries|navigation.entries}} with the given key, both will reject with an "{{InvalidStateError}}" {{DOMException}}. - * For same-document traversals intercepted by the {{Navigation/navigate}} event's {{NavigateEvent/intercept()}} method, {{NavigationResult/committed}} will fulfill as soon as the traversal is processed and {{Navigation/currentEntry|navigation.currentEntry}} is updated, and {{NavigationResult/finished}} will fulfill or reject according to any promises returned by handlers passed to {{NavigateEvent/intercept()}}. - * For non-intercepted same-document traversals, both promises will fulfill as soon as the traversal is processed and {{Navigation/currentEntry|navigation.currentEntry}} is updated - * For cross-document traversals, or traversals that result in 204/205 [=response/statuses=] or `Content-Disposition: attachment` header fields from the server (and thus do not actually traverse), both promises will never settle. -

- -
{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/back()|back}}() -
{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/back(options)|back}}({ {{NavigationOptions/info}} }) -
-

Traverse the session history to the closest previous [=session history entry=] which results in this [=navigable=] navigating, i.e. results in {{Navigation/currentEntry|navigation.currentEntry}} updating. {{NavigationOptions/info}} can be set to any value; it will populate the {{NavigateEvent/info}} property of the corresponding {{Navigation/navigate}} event. - -

If a traversal to that [=session history entry=] is already in progress, then this will return the promises for that original traversal, and {{NavigationOptions/info}} will be ignored. - -

The returned promises behave equivalently to those returned by {{Navigation/traverseTo()}}. -

- -
{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/forward()|forward}}() -
{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/forward(options)|forward}}({ {{NavigationOptions/info}} }) -
-

Traverse the session history to the closest forward [=session history entry=] which results in this frame navigating, i.e. results in {{Navigation/currentEntry|navigation.currentEntry}} updating. {{NavigationOptions/info}} can be set to any value; it will populate the {{NavigateEvent/info}} property of the corresponding {{Navigation/navigate}} event. - -

If a traversal to that [=session history entry=] is already in progress, then this will return the promises for that original traversal, and {{NavigationOptions/info}} will be ignored. - -

The returned promises behave equivalently to those returned by {{Navigation/traverseTo()}}. -

-
- -
- The traverseTo(|key|, |options|) method steps are: - - 1. If [=this=]'s [=Navigation/current entry index=] is −1, then return [=an early error result=] for an "{{InvalidStateError}}" {{DOMException}}. - - 1. If [=this=]'s [=Navigation/entry list=] does not contain any {{NavigationHistoryEntry}} whose [=NavigationHistoryEntry/session history entry=]'s [=session history entry/navigation API key=] equals |key|, then return [=an early error result=] for an "{{InvalidStateError}}" {{DOMException}}. - - 1. Return the result of [=performing a navigation API traversal=] given [=this=], |key|, and |options|. -
- -
- The back(|options|) method steps are: - - 1. If [=this=]'s [=Navigation/current entry index=] is −1 or 0, then return [=an early error result=] for an "{{InvalidStateError}}" {{DOMException}}. - - 1. Let |key| be [=this=]'s [=Navigation/entry list=][[=this=]'s [=Navigation/current entry index=] − 1]'s [=NavigationHistoryEntry/session history entry=]'s [=session history entry/navigation API key=]. - - 1. Return the result of [=performing a navigation API traversal=] given [=this=], |key|, and |options|. -
- -
- The forward(|options|) method steps are: - - 1. If [=this=]'s [=Navigation/current entry index=] is −1 or is equal to [=this=]'s [=Navigation/entry list=]'s [=list/size=] − 1, then return [=an early error result=] for an "{{InvalidStateError}}" {{DOMException}}. - - 1. Let |key| be [=this=]'s [=Navigation/entry list=][[=this=]'s [=Navigation/current entry index=] + 1]'s [=NavigationHistoryEntry/session history entry=]'s [=session history entry/navigation API key=]. - - 1. Return the result of [=performing a navigation API traversal=] given [=this=], |key|, and |options|. -
- -
- To perform a navigation API traversal given a {{Navigation}} object |navigation|, a string |key|, and a {{NavigationOptions}} |options|: - - 1. Let |sourceDocument| be |navigation|'s [=relevant global object=]'s [=associated Document=]. - - 1. If |sourceDocument| is not [=Document/fully active=], then return [=an early error result=] for an "{{InvalidStateError}}" {{DOMException}}. - - 1. If |sourceDocument|'s unload counter is greater than 0, then return [=an early error result=] for an "{{InvalidStateError}}" {{DOMException}}. - - 1. If |navigation|'s [=Navigation/current entry=]'s [=NavigationHistoryEntry/session history entry=]'s [=session history entry/navigation API key=] equals |key|, then return «[ "{{NavigationResult/committed}}" → [=a promise resolved with=] |navigation|'s [=Navigation/current entry=], "{{NavigationResult/finished}}" → [=a promise resolved with=] |navigation|'s [=Navigation/current entry=] ]» - - 1. If |navigation|'s [=Navigation/upcoming traverse navigations=][|key|] [=map/exists=], then: - - 1. Let |navigation| be |navigation|'s [=Navigation/upcoming traverse navigations=][|key|]. - - 1. Return «[ "{{NavigationResult/committed}}" → |navigation|'s [=navigation API method navigation/committed promise=], "{{NavigationResult/finished}}" → |navigation|'s [=navigation API method navigation/finished promise=] ]». - - 1. Let |info| be |options|["{{NavigationOptions/info}}"] if it [=map/exists=], or undefined otherwise. - - 1. Let |ongoingNavigation| be the result of [=Navigation/setting an upcoming traverse navigation=] for |navigation| given |key| and |info|. - - 1. Let |navigable| be |sourceDocument|'s [=node navigable=]. - - 1. Let |traversable| be |navigable|'s [=navigable/traversable navigable=]. - - 1. Let |sourceSnapshotParams| be the result of [=snapshotting source snapshot params=] given |sourceDocument|. - - 1. [=Append the following session history traversal steps=] to |traversable|: - - 1. Let |navigableEntries| be the result of [=navigable/getting session history entries=] given |navigable|. - - 1. Let |targetEntry| be the [=session history entry=] in |navigableEntries| whose [=session history entry/navigation API key=] equals |key|. If no such entry exists, then: - - 1. [=Queue a global task=] on the [=navigation and traversal task source=] given |sourceDocument|'s [=relevant global object=] to [=navigation API method navigation/reject the finished promise=] for |ongoingNavigation| with an "{{InvalidStateError}}" {{DOMException}}. - - 1. Abort these steps. - -

This can occur if the |navigation| object's view of session history is outdated, which can happen for brief periods while all the relevant threads and processes are being synchronized in reaction to a history change (such as the user clearing their history). - - 1. If |targetEntry| is |navigable|'s [=navigable/active session history entry=], then abort these steps. - -

This can occur if a previously-queued-up traversal already took us to this session history entry. In that case that previous traversal will have dealt with |ongoingNavigation| already. - - 1. [=Apply the history step=] given by |targetEntry|'s [=session history entry/step=] to |traversable|, with [=apply the history step/checkForUserCancellation=] set to true, [=apply the history step/sourceSnapshotParams=] set to |sourceSnapshotParams|, and [=apply the history step/initiatorToCheck=] set to |navigable|. - - - If this aborts due to user-canceled unloading or due to the {{Navigation/navigate}} event being canceled, then [=queue a global task=] on the [=navigation and traversal task source=] given |sourceDocument|'s [=relevant global object=] to [=finalize with an aborted navigation error=] given |navigation| and |ongoingNavigation|. - - - If this aborts due to the initiator allowed-to-navigate check, then [=queue a global task=] on the [=navigation and traversal task source=] given |sourceDocument|'s [=relevant global object=] to [=finalize with an aborted navigation error=] given |navigation|, |ongoingNavigation|, and a [=new=] "{{SecurityError}}" {{DOMException}} created in |navigation|'s [=relevant realm=]. - -

As part of merging this spec into HTML, we would modify [=apply the history step=] to have well-specified hooks for communicating these conditions back to its caller.

- - 1. Return «[ "{{NavigationResult/committed}}" → |ongoingNavigation|'s [=navigation API method navigation/committed promise=], "{{NavigationResult/finished}}" → |ongoingNavigation|'s [=navigation API method navigation/finished promise=] ]». -
- -

Event handlers

- -The following are the [=event handlers=] (and their corresponding [=event handler event types=]) that must be supported, as [=event handler IDL attributes=], by objects implementing the {{Navigation}} interface: - - - - - - - - -
[=Event handler=] - [=Event handler event type=] -
onnavigate - navigate -
onnavigatesuccess - navigatesuccess -
onnavigateerror - navigateerror -
oncurrententrychange - currententrychange -
- - - - - - -[Exposed=Window] -interface NavigateEvent : Event { - constructor(DOMString type, NavigateEventInit eventInit); - - readonly attribute NavigationType navigationType; - readonly attribute NavigationDestination destination; - readonly attribute boolean canIntercept; - readonly attribute boolean userInitiated; - readonly attribute boolean hashChange; - readonly attribute AbortSignal signal; - readonly attribute FormData? formData; - readonly attribute DOMString? downloadRequest; - readonly attribute any info; - - undefined intercept(optional NavigationInterceptOptions options = {}); - undefined scroll(); -}; - -dictionary NavigateEventInit : EventInit { - NavigationType navigationType = "push"; - required NavigationDestination destination; - boolean canIntercept = false; - boolean userInitiated = false; - boolean hashChange = false; - required AbortSignal signal; - FormData? formData = null; - DOMString? downloadRequest = null; - any info; -}; - -dictionary NavigationInterceptOptions { - NavigationInterceptHandler handler; - NavigationFocusReset focusReset; - NavigationScrollBehavior scroll; -}; - -enum NavigationFocusReset { - "after-transition", - "manual" -}; - -enum NavigationScrollBehavior { - "after-transition", - "manual" -}; - -callback NavigationInterceptHandler = Promise<undefined> (); - -enum NavigationType { - "reload", - "push", - "replace", - "traverse" -}; - - -
-
event.{{NavigateEvent/navigationType}} -
-

One of "{{NavigationType/reload}}", "{{NavigationType/push}}", "{{NavigationType/replace}}", or "{{NavigationType/traverse}}", indicating what type of navigation this is. -

-
event.{{NavigateEvent/destination}} -
-

A {{NavigationDestination}} representing the destination of the navigation. -

- -
event.{{NavigateEvent/canIntercept}} -
-

True if {{NavigateEvent/intercept()}} can be called to intercept this navigation and convert it into a single-page navigation; false otherwise. - -

Generally speaking, this will be true whenever the current {{Document}} can have its URL rewritten to the destination URL, except for cross-document back/forward navigations, where it will always be false. -

- -
event.{{NavigateEvent/userInitiated}} -
-

True if this navigation was due to a user clicking on an <{a}> element, submitting a <{form}> element, or using the browser UI to navigate; false otherwise. -

- -
event.{{NavigateEvent/hashChange}} -
-

True if this navigation is a [=navigate to a fragment|fragment navigation=]; false otherwise. -

- -
event.{{NavigateEvent/signal}} -
-

An {{AbortSignal}} which will become aborted if the navigation gets canceled, e.g. by the user pressing their browser's "Stop" button, or another higher-priority navigation interrupting this one. - -

The expected pattern is for developers to pass this along to any async operations, such as {{WindowOrWorkerGlobalScope/fetch()}}, which they perform as part of handling this navigation. -

- -
event.{{NavigateEvent/formData}} -
-

The {{FormData}} representing the submitted form entries for this navigation, if this navigation is a "{{NavigationType/push}}" or "{{NavigationType/replace}}" navigation representing a POST form submission; null otherwise. - -

(Notably, this will be null even for "{{NavigationType/reload}}" and "{{NavigationType/traverse}}" navigations that are revisiting a session history entry that was originally created from a form submission.) -

- -
event.{{NavigateEvent/downloadRequest}} -
-

Represents whether or not this navigation was requested to be a download, by using an <{a}> or <{area}> element's <{a/download}> attribute: - - * If a download was not requested, then this property is null. - * If a download was requested, returns the filename that was supplied, via ``. (This could be the empty string, as in the case of ``.) - -

Note that a download being requested does not always mean that a download will happen: for example, the download might be blocked by browser security policies, or end up being treated as a push navigation for unspecified reasons. - -

Similarly, a navigation might end up being a download even if it was not requested to be one, due to the destination server responding with a `Content-Disposition: attachment` header. - -

Finally, note that the {{Navigation/navigate}} event will not fire at all for downloads initiated using browser UI affordances, e.g., those created by right-clicking and choosing to save the target of the link. -

- -
event.{{NavigateEvent/info}} -
-

An arbitrary JavaScript value passed via {{Window/navigation}} APIs that initiated this navigation, or null if the navigation was initiated by the user or via a non-{{Window/navigation}} API. -

- -
event.{{NavigateEvent/intercept()|intercept}}({ {{NavigationInterceptOptions/handler}}, {{NavigationInterceptOptions/focusReset}}, {{NavigationInterceptOptions/scroll}} }) -
-

Intercepts this navigation, preventing its normally handling and instead converting it into a same-document navigation to the destination URL. - -

The {{NavigationInterceptOptions/handler}} option can be a function that returns a promise. The handler function will run after the {{Navigation/navigate}} event has finished firing, and the {{Navigation/currentEntry|navigation.currentEntry}} property has been synchronously updated. This promise is used to signal the duration, and success or failure, of the navigation. After it settles, the browser signals to the user (e.g. via a loading spinner UI, or assistive technology) that the navigation is finished. Additionally, it fires {{Navigation/navigatesuccess}} or {{Navigation/navigateerror}} events as appropriate, which other parts of the web application can respond to. - -

By default, using this method will cause focus to reset when any handlers' returned promises settle. Focus will be reset to the first element with the <{html-global/autofocus}> attribute set, or the <{body}> element if the attribute isn't present. The {{NavigationInterceptOptions/focusReset}} option can be set to "{{NavigationFocusReset/manual}}" to avoid this behavior. - -

By default, using this method will delay the browser's scroll restoration logic for "{{NavigationType/traverse}}" or "{{NavigationType/reload}}" navigations, or its scroll-reset/scroll-to-a-fragment logic for "{{NavigationType/push}}" and "{{NavigationType/replace}}" navigations, until any handlers' returned promises settle. The {{NavigationInterceptOptions/scroll}} option can be set to "{{NavigationScrollBehavior/manual}}" to turn off any browser-driven scroll behavior entirely for this navigation, or {{NavigateEvent/scroll()|event.scroll()}} can be called before the promise settles to trigger this behavior early. - -

This method will throw a "{{SecurityError}}" {{DOMException}} if {{NavigateEvent/canIntercept}} is false, or if {{Event/isTrusted}} is false. It will throw an "{{InvalidStateError}}" {{DOMException}} if not called synchronously, during event dispatch. -

- -
event.{{NavigateEvent/scroll()|scroll}}() -
-

For "{{NavigationType/traverse}}" or "{{NavigationType/reload}}" navigations, restores the scroll position using the browser's usual scroll restoration logic. - -

For "{{NavigationType/push}}" or "{{NavigationType/replace}}" navigations, either resets the scroll position to the top of the document or scrolls to the fragment specified by {{NavigationDestination/url|event.destination.url}} if there is one. - -

If called more than once, or called after automatic post-transition scroll processing has happened due to the {{NavigationInterceptOptions/scroll}} option being left as "{{NavigationScrollBehavior/after-transition}}", this method will throw an "{{InvalidStateError}}" {{DOMException}}. -

-
- -The navigationType, destination, canIntercept, userInitiated, hashChange, signal, formData, downloadRequest, and info getter steps are to return the value that the corresponding attribute was initialized to. - -A {{NavigateEvent}} has a classic history API serialized data, a [=serialized state=]-or-null. It is only used in some cases where the event's {{NavigateEvent/navigationType}} is "{{NavigationType/push}}" or "{{NavigationType/replace}}", and is set appropriately when the event is [[#navigate-event-firing|fired]]. - -A {{NavigateEvent}} has a focus reset behavior, a {{NavigationFocusReset}}-or-null, initially null. - -A {{NavigateEvent}} has a scroll behavior, a {{NavigationScrollBehavior}}-or-null, initially null. - -A {{NavigateEvent}} has a did process scroll behavior, a boolean, initially false. - -A {{NavigateEvent}} has a was intercepted, a boolean, initially false. - -A {{NavigateEvent}} has a needs continue, a boolean, initially false. - -A {{NavigateEvent}} has a navigation handler list, which is a [=list=] of {{NavigationInterceptHandler}} callbacks, initially empty. - -
- The intercept(|options|) method steps are: - - 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=Document/fully active=], then throw an "{{InvalidStateError}}" {{DOMException}}. - 1. If [=this=]'s {{Event/isTrusted}} attribute was initialized to false, then throw a "{{SecurityError}}" {{DOMException}}. - 1. If [=this=]'s {{NavigateEvent/canIntercept}} attribute was initialized to false, then throw a "{{SecurityError}}" {{DOMException}}. - 1. If [=this=]'s [=Event/dispatch flag=] is unset, then throw an "{{InvalidStateError}}" {{DOMException}}. - 1. If [=this=]'s [=Event/canceled flag=] is set, then throw an "{{InvalidStateError}}" {{DOMException}}. - 1. If |options|["{{NavigationInterceptOptions/handler}}"] [=map/exists=], then [=list/append=] it to [=this=]'s [=NavigateEvent/navigation handler list=]. - 1. Set [=this=]'s [=NavigateEvent/was intercepted=] to true. - 1. If |options|["{{NavigationInterceptOptions/focusReset}}"] [=map/exists=], then: - 1. If [=this=]'s [=NavigateEvent/focus reset behavior=] is not null, and it is not equal to |options|["{{NavigationInterceptOptions/focusReset}}"], then the user agent may [=report a warning to the console=] indicating that the {{NavigationInterceptOptions/focusReset}} option for a previous call to {{NavigateEvent/intercept()}} was overridden by this new value, and the previous value will be ignored. - 1. Set [=this=]'s [=NavigateEvent/focus reset behavior=] to |options|["{{NavigationInterceptOptions/focusReset}}"]. - 1. If |options|["{{NavigationInterceptOptions/scroll}}"] [=map/exists=], then: - 1. If [=this=]'s [=NavigateEvent/scroll behavior=] is not null, and it is not equal to |options|["{{NavigationInterceptOptions/scroll}}"], then the user agent may [=report a warning to the console=] indicating that the {{NavigationInterceptOptions/scroll}} option for a previous call to {{NavigateEvent/intercept()}} was overridden by this new value, and the previous value will be ignored. - 1. Set [=this=]'s [=NavigateEvent/scroll behavior=] to |options|["{{NavigationInterceptOptions/scroll}}"]. -
- -
- The scroll() method steps are: - - 1. If [=this=]'s [=NavigateEvent/did process scroll behavior=] is true, then throw an "{{InvalidStateError}}" {{DOMException}}. - 1. If [=this=]'s [=NavigateEvent/was intercepted=] is false, then throw an "{{InvalidStateError}}" {{DOMException}}. - 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=Document/fully active=], then throw an "{{InvalidStateError}}" {{DOMException}}. - 1. [=Definitely process scroll behavior=] given [=this=]. -
- - - - -[Exposed=Window] -interface NavigationDestination { - readonly attribute USVString url; - readonly attribute DOMString? key; - readonly attribute DOMString? id; - readonly attribute long long index; - readonly attribute boolean sameDocument; - - any getState(); -}; - - -
-
event.{{NavigateEvent/destination}}.{{NavigationDestination/url}} -
-

The URL being navigated to. -

- -
event.{{NavigateEvent/destination}}.{{NavigationDestination/key}} -
-

The value of the {{NavigationHistoryEntry/key}} property of the destination {{NavigationHistoryEntry}}, if this is a "{{NavigationType/traverse}}" navigation, or null otherwise. -

- -
event.{{NavigateEvent/destination}}.{{NavigationDestination/id}} -
-

The value of the {{NavigationHistoryEntry/id}} property of the destination {{NavigationHistoryEntry}}, if this is a "{{NavigationType/traverse}}" navigation, or null otherwise. -

- -
event.{{NavigateEvent/destination}}.{{NavigationDestination/index}} -
-

The value of the {{NavigationHistoryEntry/index}} property of the destination {{NavigationHistoryEntry}}, if this is a "{{NavigationType/traverse}}" navigation, or −1 otherwise. -

- -
event.{{NavigateEvent/destination}}.{{NavigationDestination/sameDocument}} -
-

Indicates whether or not this navigation is to the same {{Document}} as the current {{Window/document}} value, or not. This will be true, for example, in cases of fragment navigations or {{History/pushState()|history.pushState()}} navigations. - -

Note that this property indicates the original nature of the navigation. If a cross-document navigation is converted into a same-document navigation using {{NavigateEvent/intercept()|event.intercept()}}, that will not change the value of this property. -

- -
state = event.{{NavigateEvent/destination}}.{{NavigationDestination/getState()}} -
-

For "{{NavigationType/traverse}}" navigations, returns the deserialization of the state stored in the destination session history entry. - -

For "{{NavigationType/push}}" and "{{NavigationType/replace}}" navigations, returns the deserialization of the state passed to {{Navigation/navigate()|navigation.navigate()}}, if the navigation was initiated in that way, or undefined if it wasn't. - -

For "{{NavigationType/reload}}" navigations, returns the deserialization of the state passed to {{Navigation/reload()|navigation.reload()}}, if the reload was initiated in that way, or undefined if it wasn't. -

-
- -A {{NavigationDestination}} has an associated URL, which is a [=URL=]. - -A {{NavigationDestination}} has an associated key, which is a [=string=]-or-null. - -A {{NavigationDestination}} has an associated id, which is a [=string=]-or-null. - -A {{NavigationDestination}} has an associated index, which is an integer. - -A {{NavigationDestination}} has an associated state, which is a [=serialized state=]-or-null. - -A {{NavigationDestination}} has an associated is same document, which is a boolean. - -The url getter steps are to return [=this=]'s [=NavigationDestination/URL=], [=URL serializer|serialized=]. - -The key getter steps are to return [=this=]'s [=NavigationDestination/key=]. - -The id getter steps are to return [=this=]'s [=NavigationDestination/id=]. - -The index getter steps are to return [=this=]'s [=NavigationDestination/index=]. - -The sameDocument getter steps are to return [=this=]'s [=NavigationDestination/is same document=]. - -
- The getState() method steps are: - - 1. If [=this=]'s [=NavigationDestination/state=] is null, then return undefined. - 1. Return [$StructuredDeserialize$]([=this=]'s [=NavigationDestination/state=]). -
- - - -
- To fire a traversal `navigate` event at a {{Navigation}} |navigation| given a [=session history entry=] |destinationEntry|, and an optional [=user navigation involvement=] |userInvolvement| (default "[=user navigation involvement/none=]"): - - 1. Let |event| be the result of [=creating an event=] given {{NavigateEvent}}, in |navigation|'s [=relevant realm=]. - 1. Set |event|'s [=NavigateEvent/classic history API serialized data=] to null. - 1. Let |destination| be a [=new=] {{NavigationDestination}} created in |navigation|'s [=relevant realm=]. - 1. Set |destination|'s [=NavigationDestination/URL=] to |destinationEntry|'s [=session history entry/URL=]. - 1. If |destinationEntry|'s [=session history entry/document state=]'s [=document state/origin=] is [=same origin=] with |navigation|'s [=relevant settings object=]'s [=environment settings object/origin=], then: - 1. Set |destination|'s [=NavigationDestination/key=] to |destinationEntry|'s [=session history entry/navigation API key=]. - 1. Set |destination|'s [=NavigationDestination/id=] to |destinationEntry|'s [=session history entry/navigation API ID=]. - 1. Set |destination|'s [=NavigationDestination/index=] to the result of [=getting the navigation API history index=] of |destinationEntry| within |navigation|. - 1. Set |destination|'s [=NavigationDestination/state=] to |destinationEntry|'s [=session history entry/navigation API state=]. - 1. Otherwise, - 1. Set |destination|'s [=NavigationDestination/key=] to null. - 1. Set |destination|'s [=NavigationDestination/id=] to null. - 1. Set |destination|'s [=NavigationDestination/index=] to −1. - 1. Set |destination|'s [=NavigationDestination/state=] to null. - 1. Set |destination|'s [=NavigationDestination/is same document=] to true if |destinationEntry|'s [=session history entry/document=] is equal to |navigation|'s [=relevant global object=]'s [=associated Document=]; otherwise false. - 1. Return the result of performing the [=inner navigate event firing algorithm=] given |navigation|, "{{NavigationType/traverse}}", |event|, |destination|, |userInvolvement|, null, and null. -
- -
- To fire a non-traversal `navigate` event at a {{Navigation}} |navigation| given a {{NavigationType}} |navigationType|, a [=URL=] |destinationURL|, a boolean |isSameDocument|, an optional [=user navigation involvement=] |userInvolvement| (default "[=user navigation involvement/none=]"), an optional [=serialized state=]-or-null |state| (default null), an optional [=entry list=] or null |formDataEntryList| (default null), and an optional [=serialized state=]-or-null |classicHistoryAPISerializedData| (default null): - - 1. Let |event| be the result of [=creating an event=] given {{NavigateEvent}}, in |navigation|'s [=relevant realm=]. - 1. Set |event|'s [=NavigateEvent/classic history API serialized data=] to |classicHistoryAPISerializedData|. - 1. Let |destination| be a [=new=] {{NavigationDestination}} created in |navigation|'s [=relevant realm=]. - 1. Set |destination|'s [=NavigationDestination/URL=] to |destinationURL|. - 1. Set |destination|'s [=NavigationDestination/key=] to null. - 1. Set |destination|'s [=NavigationDestination/id=] to null. - 1. Set |destination|'s [=NavigationDestination/index=] to −1. - 1. Set |destination|'s [=NavigationDestination/state=] to |state|. - 1. Set |destination|'s [=NavigationDestination/is same document=] to |isSameDocument|. - 1. Return the result of performing the [=inner navigate event firing algorithm=] given |navigation|, |navigationType|, |event|, |destination|, |userInvolvement|, |formDataEntryList|, and null. -
- -
- To fire a download-requested `navigate` event at a {{Navigation}} |navigation| given a [=URL=] |destinationURL|, a [=user navigation involvement=] |userInvolvement|, and a string |filename|: - - 1. Let |event| be the result of [=creating an event=] given {{NavigateEvent}}, in |navigation|'s [=relevant realm=]. - 1. Set |event|'s [=NavigateEvent/classic history API serialized data=] to null. - 1. Let |destination| be a [=new=] {{NavigationDestination}} created in |navigation|'s [=relevant realm=]. - 1. Set |destination|'s [=NavigationDestination/URL=] to |destinationURL|. - 1. Set |destination|'s [=NavigationDestination/key=] to null. - 1. Set |destination|'s [=NavigationDestination/id=] to null. - 1. Set |destination|'s [=NavigationDestination/index=] to −1. - 1. Set |destination|'s [=NavigationDestination/state=] to null. - 1. Set |destination|'s [=NavigationDestination/is same document=] to false. - 1. Return the result of performing the [=inner navigate event firing algorithm=] given |navigation|, "{{NavigationType/push}}", |event|, |destination|, |userInvolvement|, null, and |filename|. -
- -
- The inner `navigate` event firing algorithm is the following steps, given a {{Navigation}} |navigation|, a {{NavigationType}} |navigationType|, a {{NavigateEvent}} |event|, a {{NavigationDestination}} |destination|, a [=user navigation involvement=] |userInvolvement|, an [=entry list=] or null |formDataEntryList|, and a string or null |downloadRequestFilename|: - - 1. [=Navigation/Promote the upcoming navigation to ongoing=] given |navigation| and |destination|'s [=NavigationDestination/key=]. - 1. Let |ongoingNavigation| be |navigation|'s [=Navigation/ongoing navigation=]. - 1. If |navigation| [=Navigation/has entries and events disabled=], then: - 1. If |ongoingNavigation| is not null, then [=navigation API method navigation/clean up=] |ongoingNavigation|. - -

In this case the [=navigation API method navigation/committed promise=] and [=navigation API method navigation/finished promise=] will never fulfill, since we never create {{NavigationHistoryEntry}}s for the initial `about:blank` {{Document}} so we have nothing to [=resolve=] them with. - 1. Return true. - 1. Let |navigable| be |navigation|'s [=relevant global object=]'s [=Window/navigable=]. - 1. Let |document| be |navigation|'s [=relevant global object=]'s [=associated document=]. - 1. If |document| can have its URL rewritten to |destination|'s [=NavigationDestination/URL=], and either |destination|'s [=NavigationDestination/is same document=] is true or |navigationType| is not "{{NavigationType/traverse}}", then initialize |event|'s {{NavigateEvent/canIntercept}} to true. Otherwise, initialize it to false. - 1. Let |traverseCanBeCanceled| be true if |userInvolvement| is not "[=user navigation involvement/browser UI=]" or |navigation|'s [=relevant global object=] has [=transient activation=]; otherwise, false. -

We are exploring the possibility of introducing the concept of a "consumable user activation" (i.e., a user activation that can be consumed like a transient user activation, but does not have the timeout of a transient user activation) instead of using transient activation here. This does not have a spec yet. - 1. If any of the following are true: - * |navigationType| is not "{{NavigationType/traverse}}"; or - * |navigable| is a [=top-level traversable=] and |traverseCanBeCanceled| is true - - then initialize |event|'s {{Event/cancelable}} to true. Otherwise, initialize it to false. - 1. Initialize |event|'s {{Event/type}} to "{{Navigation/navigate}}". - 1. Initialize |event|'s {{NavigateEvent/navigationType}} to |navigationType|. - 1. Initialize |event|'s {{NavigateEvent/destination}} to |destination|. - 1. Initialize |event|'s {{NavigateEvent/downloadRequest}} to |downloadRequestFilename|. - 1. If |ongoingNavigation| is not null, then initialize |event|'s {{NavigateEvent/info}} to |ongoingNavigation|'s [=navigation API method navigation/info=]. Otherwise, initialize it to undefined. -

At this point |ongoingNavigation|'s [=navigation API method navigation/info=] is no longer needed and can be nulled out instead of keeping it alive for the lifetime of the [=navigation API method navigation=]. - 1. Initialize |event|'s {{NavigateEvent/signal}} to a [=new=] {{AbortSignal}} created in |navigation|'s [=relevant realm=]. - 1. Let |currentURL| be |document|'s [=Document/URL=]. - 1. If all of the following are true: - * |destination|'s [=NavigationDestination/is same document=] is true; - * |destination|'s [=NavigationDestination/URL=] [=url/equals=] |currentURL| with [=url/equals/exclude fragments=] set to true; and - * |destination|'s [=NavigationDestination/URL=]'s [=url/fragment=] is not [=string/is|identical to=] |currentURL|'s [=url/fragment=] - - then initialize |event|'s {{NavigateEvent/hashChange}} to true. Otherwise, initialize it to false. - 1. If |userInvolvement| is not "[=user navigation involvement/none=]", then initialize |event|'s {{NavigateEvent/userInitiated}} to true. Otherwise, initialize it to false. - 1. If |formDataEntryList| is not null, then initialize |event|'s {{NavigateEvent/formData}} to a [=new=] {{FormData}} created in |navigation|'s [=relevant realm=], associated to |formDataEntryList|. Otherwise, initialize it to null. - 1. [=Assert=]: |navigation|'s [=Navigation/ongoing navigate event=] is null. - 1. Set |navigation|'s [=Navigation/ongoing navigate event=] to |event|. - 1. [=Assert=]: |navigation|'s [=Navigation/ongoing navigation signal=] is null. - 1. Set |navigation|'s [=Navigation/ongoing navigation signal=] to |event|'s {{NavigateEvent/signal}}. - 1. Set |navigation|'s [=Navigation/focus changed during ongoing navigation=] to false. - 1. Set |navigation|'s [=Navigation/suppress normal scroll restoration during ongoing navigation=] to false. - 1. Let |dispatchResult| be the result of [=dispatching=] |event| at |navigation|. - 1. If |dispatchResult| is false: - 1. If |navigationType| is not "{{NavigationType/traverse}}" and |event|'s {{NavigateEvent/signal}} is not [=AbortSignal/aborted=], then [=finalize with an aborted navigation error=] given |navigation| and |ongoingNavigation|. -

If |navigationType| is "{{NavigationType/traverse}}", then we will [=finalize with an aborted navigation error=] in [=perform a navigation API traversal=]. - 1. Return false. - - 1. If |destination|'s [=NavigationDestination/is same document=] is true and |navigationType| is "{{NavigationType/traverse}}": - 1. Set |event|'s [=NavigateEvent/needs continue=] to true. - 1. Return true. - 1. Return the result of [=reacting to navigate event result=] given |navigation| and |event|. -

- -
- To react to `navigate` event result, given a {{Navigation}} |navigation| and a {{NavigateEvent}} |event|: - 1. Let |document| be |navigation|'s [=relevant global object=]'s [=associated document=]. - 1. Let |destination| be |event|'s {{NavigateEvent/destination}}. - 1. Let |ongoingNavigation| be |navigation|'s [=Navigation/ongoing navigation=]. - 1. Let |navigationType| be |event|'s {{NavigateEvent/navigationType}}. - 1. Let |endResultIsSameDocument| be true if |event|'s [=NavigateEvent/was intercepted=] is true or |destination|'s [=NavigationDestination/is same document=] is true. - 1. [=Prepare to run script=] given |event|'s [=relevant settings object=]. -
-

This is done to avoid the [=ECMAScript/execution context stack|JavaScript execution context stack=] becoming empty right after any {{Navigation/currententrychange}} event handlers run as a result of the [=URL and history update steps=] that could soon happen. If the stack were to become empty at that time, then it would immediately [=perform a microtask checkpoint=], causing various promise fulfillment handlers to run interleaved with the event handlers and before any handlers passed to {{NavigateEvent/intercept()|navigateEvent.intercept()}}. This is undesirable since it means promise handler ordering vs. {{Navigation/currententrychange}} event handler ordering vs. {{NavigateEvent/intercept()}} handler ordering is dependent on whether the navigation is happening with an empty [=ECMAScript/execution context stack|JavaScript execution context stack=] (e.g., because the navigation was user-initiated) or with a nonempty one (e.g., because the navigation was caused by a JavaScript API call). - -

By inserting an otherwise-unnecessary [=ECMAScript/execution context|JavaScript execution context=] onto the stack in this step, we essentially suppress the [=perform a microtask checkpoint=] algorithm until later, thus ensuring that the sequence is always: {{Navigation/currententrychange}} event handlers, then {{NavigateEvent/intercept()}} handlers, then promise handlers. -

- 1. If |event|'s [=NavigateEvent/was intercepted=] is true: - 1. Let |fromEntry| be the [=Navigation/current entry=] for |navigation|. - 1. [=Assert=]: |fromEntry| is not null. - 1. Set |navigation|'s [=Navigation/transition=] to a [=new=] {{NavigationTransition}} created in |navigation|'s [=relevant realm=], whose [=NavigationTransition/navigation type=] is |navigationType|, [=NavigationTransition/from entry=] is |fromEntry|, and whose [=NavigationTransition/finished promise=] is [=a new promise=] created in |navigation|'s [=relevant realm=]. - 1. [=Mark as handled=] |navigation|'s [=Navigation/transition=]'s [=NavigationTransition/finished promise=]. -

See the discussion about other finished promises as to why this is done.

- 1. If |navigationType| is "{{NavigationType/traverse}}", then set |navigation|'s [=Navigation/suppress normal scroll restoration during ongoing navigation=] to true. -

If |event|'s [=NavigateEvent/scroll behavior=] was set to "{{NavigationScrollBehavior/after-transition}}", then we will [=potentially process scroll behavior|potentially perform scroll restoration=] below. Otherwise, there will be no scroll restoration. That is, no navigation which is intercepted by {{NavigateEvent/intercept()}} goes through the normal scroll restoration process; scroll restoration for such navigations is either done manually, by the web developer, or is done after the transition. - 1. If |navigationType| is "{{NavigationType/push}}" or "{{NavigationType/replace}}", then run the [=URL and history update steps=] given |document| and |event|'s {{NavigateEvent/destination}}'s [=NavigationDestination/URL=], with [=URL and history update steps/serializedData=] set to |event|'s [=NavigateEvent/classic history API serialized data=] and [=URL and history update steps/historyHandling=] set to |navigationType|. - -

If |navigationType| is "{{NavigationType/reload}}", then we are converting a reload into a "same-document reload", for which the URL and history update steps are not appropriate. Navigation API-related stuff still happens, such as updating the [=navigable/active session history entry=]'s [=session history entry/navigation API state=] if this was caused by a call to {{Navigation/reload()|navigation.reload()}}, and all the ongoing navigation tracking. - 1. If |endResultIsSameDocument| is true: - 1. Let |promisesList| be an empty [=list=]. - 1. [=list/For each=] |handler| of |event|'s [=NavigateEvent/navigation handler list=]: - 1. [=list/Append=] the result of [=invoking=] |handler| to |promisesList| with an empty arguments list. - 1. If |promisesList|'s [=list/size=] is 0, then set |promisesList| to « [=a promise resolved with=] {{undefined}} ». -

There is a subtle timing difference between how [=waiting for all=] schedules its success and failure steps when given zero promises versus ≥1 promises. For most uses of [=waiting for all=], this does not matter. However, with this API, there are so many events and promise handlers which could fire around the same time that the difference is pretty easily observable: it can cause the event/promise handler sequence to vary. (Some of the events and promises involved include: {{Navigation/navigatesuccess}} / {{Navigation/navigateerror}}, {{Navigation/currententrychange}}, {{NavigationHistoryEntry/dispose}}, |ongoingNavigation|'s promises, and the {{NavigationTransition/finished|navigation.transition.finished}} promise.) - 1. [=Wait for all=] of |promisesList|, with the following success steps: - 1. If |event|'s {{NavigateEvent/signal}} is [=AbortSignal/aborted=], then abort these steps. - 1. If |event| equals |navigation|'s [=Navigation/ongoing navigate event=], set |navigation|'s [=Navigation/ongoing navigate event=] to null. - 1. [=Fire an event=] named {{Navigation/navigatesuccess}} at |navigation|. - 1. If |navigation|'s [=Navigation/transition=] is not null, then [=resolve=] |navigation|'s [=Navigation/transition=]'s [=NavigationTransition/finished promise=] with undefined. - 1. Set |navigation|'s [=Navigation/transition=] to null. - 1. If |ongoingNavigation| is non-null, then [=navigation API method navigation/resolve the finished promise=] for |ongoingNavigation|. - 1. [=Potentially reset the focus=] given |navigation| and |event|. - 1. [=Potentially process scroll behavior=] given |event|. - and the following failure steps given reason |rejectionReason|: - 1. If |event|'s {{NavigateEvent/signal}} is [=AbortSignal/aborted=], then abort these steps. - 1. [=Fire an event=] named {{Navigation/navigateerror}} at |navigation| using {{ErrorEvent}}, with {{ErrorEvent/error}} initialized to |rejectionReason|, and {{ErrorEvent/message}}, {{ErrorEvent/filename}}, {{ErrorEvent/lineno}}, and {{ErrorEvent/colno}} initialized to appropriate values that can be extracted from |rejectionReason| in the same underspecified way the user agent typically does for the report an exception algorithm. - 1. If |navigation|'s [=Navigation/transition=] is not null, then [=reject=] |navigation|'s [=Navigation/transition=]'s [=NavigationTransition/finished promise=] with |rejectionReason|. - 1. Set |navigation|'s [=Navigation/transition=] to null. - 1. If |ongoingNavigation| is non-null, then [=navigation API method navigation/reject the finished promise=] for |ongoingNavigation| with |rejectionReason|. - 1. [=Potentially reset the focus=] given |navigation| and |event|. -

Although we still [=potentially reset the focus=] for such failed transitions, we do not [=potentially process scroll behavior=] for them. - 1. Otherwise, if |ongoingNavigation| is non-null, then [=navigation API method navigation/clean up=] |ongoingNavigation|. - 1. [=Clean up after running script=] given |event|'s [=relevant settings object=]. -

Per the previous note, this stops suppressing any potential promise handler microtasks, causing them to run at this point or later.

- 1. If |event|'s [=NavigateEvent/was intercepted=] is true, then return false. - 1. Return true. -
- -
- To maybe continue the `navigate` event, given a {{Navigation}} |navigation|: - 1. If |navigation|'s [=Navigation/ongoing navigate event=] is null, or |navigation|'s [=Navigation/ongoing navigate event=]'s [=NavigateEvent/needs continue=] is false, then return. - 1. [=React to navigate event result=] given |navigation| and |navigation|'s [=Navigation/ongoing navigate event=]. -
- -
- To finalize with an aborted navigation error given a {{Navigation}} |navigation|, a [=navigation API method navigation=] or null |ongoingNavigation|, and an optional {{DOMException}} |error|: - - 1. Set |navigation|'s [=Navigation/focus changed during ongoing navigation=] to false. - 1. Set |navigation|'s [=Navigation/suppress normal scroll restoration during ongoing navigation=] to false. - 1. If |error| was not given, then set |error| to a [=new=] "{{AbortError}}" {{DOMException}}, created in |navigation|'s [=relevant realm=]. - 1. If |navigation|'s [=Navigation/ongoing navigate event=] is non-null, then: - 1. Set |navigation|'s [=Navigation/ongoing navigate event=]'s [=Event/canceled flag=] to true. - 1. Set |navigation|'s [=Navigation/ongoing navigate event=] to null. - 1. If |navigation|'s [=Navigation/ongoing navigation signal=] is non-null, then: - 1. [=AbortSignal/Signal abort=] on |navigation|'s [=Navigation/ongoing navigation signal=] given |error|. - 1. Set |navigation|'s [=Navigation/ongoing navigation signal=] to null. - 1. [=Fire an event=] named {{Navigation/navigateerror}} at |navigation| using {{ErrorEvent}}, with {{ErrorEvent/error}} initialized to |error|, and {{ErrorEvent/message}}, {{ErrorEvent/filename}}, {{ErrorEvent/lineno}}, and {{ErrorEvent/colno}} initialized to appropriate values that can be extracted from |error| and the current JavaScript stack in the same underspecified way the user agent typically does for the report an exception algorithm. -

Thus, for example, if this algorithm is reached because of a call to {{Window/stop()|window.stop()}}, these properties would probably end up initialized based on the line of script that called {{Window/stop()|window.stop()}}. But if it's because the user clicked the stop button, these properties would probably end up with default values like the empty string or 0. - 1. If |ongoingNavigation| is non-null, then [=navigation API method navigation/reject the finished promise=] for |ongoingNavigation| with |error|. - 1. If |navigation|'s [=Navigation/transition=] is not null, then: - 1. [=Reject=] |navigation|'s [=Navigation/transition=]'s [=NavigationTransition/finished promise=] with |error|. - 1. Set |navigation|'s [=Navigation/transition=] to null. -

- -
- To inform the navigation API about canceling navigation in a [=navigable=] |navigable|: - - 1. Let |navigation| be |navigable|'s [=navigable/active window=]'s [=Window/navigation API=]. - 1. If |navigation|'s [=Navigation/ongoing navigation signal=] is null, then return. - 1. [=Finalize with an aborted navigation error=] given |navigation| and |navigation|'s [=Navigation/ongoing navigation=]. -
- -
- To inform the navigation API about nested navigable destruction given a [=navigable=] |navigable|: - - 1. [=Inform the navigation API about canceling navigation=] in |navigable|. - 1. Let |navigation| be |navigable|'s [=navigable/active window=]'s [=Window/navigation API=]. - 1. Let |traversals| be a [=list/clone=] of |navigation|'s [=Navigation/upcoming traverse navigations=]. - 1. For each |traversal| of |traversals|: [=finalize with an aborted navigation error=] given |navigation| and |traversal|. -
- -
- To potentially reset the focus given a {{Navigation}} object |navigation| and an {{NavigateEvent}} |event|: - - 1. Let |focusChanged| be |navigation|'s [=Navigation/focus changed during ongoing navigation=]. - 1. Set |navigation|'s [=Navigation/focus changed during ongoing navigation=] to false. - 1. If |focusChanged| is true, then return. - 1. If |event|'s [=NavigateEvent/was intercepted=] is false, then return. - 1. If |event|'s [=NavigateEvent/focus reset behavior=] is "{{NavigationFocusReset/manual}}", then return. -

If it was left as null, then we treat that as "{{NavigationFocusReset/after-transition}}", and continue onward. - 1. Let |document| be |navigation|'s [=relevant global object=]'s [=associated Document=]. - 1. Let |focusTarget| be the autofocus delegate for |document|. - 1. If |focusTarget| is null, then set |focusTarget| to |document|'s body element. - 1. If |focusTarget| is null, then set |focusTarget| to |document|'s [=document element=]. - 1. Run the focusing steps for |focusTarget|, with |document|'s [=viewport=] as the fallback target. - 1. Move the sequential focus navigation starting point to |focusTarget|. -

- -
- To potentially process scroll behavior given a {{NavigateEvent}} |event|: - - 1. If |event|'s [=NavigateEvent/was intercepted=] is false, then return. - 1. If |event|'s [=NavigateEvent/scroll behavior=] is "{{NavigationScrollBehavior/manual}}", then return. -

If it was left as null, then we treat that as "{{NavigationScrollBehavior/after-transition}}", and continue onward. - 1. If |event|'s [=NavigateEvent/did process scroll behavior=] is true, then return. - 1. [=Definitely process scroll behavior=] given |event|. -

- -
- To definitely process scroll behavior given a {{NavigateEvent}} |event|: - - 1. Set |event|'s [=NavigateEvent/did process scroll behavior=] to true. - 1. If |event|'s {{NavigateEvent/navigationType}} was initialized to "{{NavigationType/traverse}}" or "{{NavigationType/reload}}", then [=restore scroll position data=] given |event|'s [=relevant global object=]'s [=Window/navigable=]'s [=navigable/active session history entry=]. - 1. Otherwise, - 1. Let |document| be |event|'s [=relevant global object=]'s [=associated Document=]. - 1. If |document|'s [=Document/indicated part=] is null, then [=scroll to the beginning of the document=] given |document|. - 1. Otherwise, [=scroll to the fragment=] given |document|. -
- - - - -[Exposed=Window] -interface NavigationHistoryEntry : EventTarget { - readonly attribute USVString? url; - readonly attribute DOMString key; - readonly attribute DOMString id; - readonly attribute long long index; - readonly attribute boolean sameDocument; - - any getState(); - - attribute EventHandler ondispose; -}; - - -
-
entry.{{NavigationHistoryEntry/url}} -
-

The URL of this navigation history entry. - -

This can return null if the entry corresponds to a different {{Document}} than the current one (i.e. if {{NavigationHistoryEntry/sameDocument}} is false), and that {{Document}}'s [=policy container/referrer policy=] was "`no-referrer`" or "`origin`", since that indicates the {{Document}} in question is hiding its URL even from other same-origin pages. -

- -
entry.{{NavigationHistoryEntry/key}} -
-

A [=user agent=]-generated random UUID string representing this navigation history entry's place in the navigation history list. This value will be reused by other {{NavigationHistoryEntry}} instances that replace this one due to replace-style navigations. This value will survive session restores. - -

This is useful for navigating back to this entry in the navigation history list, using {{Navigation/traverseTo(key)|navigation.traverseTo(key)}}. -

- -
entry.{{NavigationHistoryEntry/id}} -
-

A [=user agent=]-generated random UUID string representing this specific navigation history entry. This value will not be reused by other {{NavigationHistoryEntry}} instances. This value will survive session restores. - -

This is useful for associating data with this navigation history entry using other storage APIs. -

- -
entry.{{NavigationHistoryEntry/index}} -
-

The index of this navigation history entry within {{Navigation/entries()|navigation.entries()}}, or −1 if the entry is not in the navigation history entry list. -

- -
entry.{{NavigationHistoryEntry/sameDocument}} -
-

Indicates whether or not this navigation history entry is for the same {{Document}} as the current {{Window/document}} value, or not. This will be true, for example, when the entry represents a fragment navigation or single-page app navigations. -

- -
state = entry.{{NavigationHistoryEntry/getState()|getState}}() -
-

Returns the deserialization of the state stored in this entry, which was added to the entry using {{Navigation/navigate()|navigation.navigate()}}. This state survives session restores. - -

Note that in general, unless the state value is a primitive, entry.getState() !== entry.getState(), since a fresh copy is returned each time. - -

This state is unrelated to the classic history API's {{History/state|history.state}}. -

-
- -Each {{NavigationHistoryEntry}} has an associated session history entry, which is a [=session history entry=]. - -Each {{NavigationHistoryEntry}} has an associated index, which is an integer. - -
- The key getter steps are: - - 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=Document/fully active=], then return the empty string. - 1. Return [=this=]'s [=NavigationHistoryEntry/session history entry=]'s [=session history entry/navigation API key=]. -
- -
- The id getter steps are: - - 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=Document/fully active=], then return the empty string. - 1. Return [=this=]'s [=NavigationHistoryEntry/session history entry=]'s [=session history entry/navigation API ID=]. -
- -
- The url getter steps are: - - 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=Document/fully active=], then return null. - 1. Let |she| be [=this=]'s [=NavigationHistoryEntry/session history entry=]. - 1. If |she|'s [=session history entry/document=] does not equal [=this=]'s [=relevant global object=]'s [=associated Document=], and |she|'s [=session history entry/document state=]'s [=document state/request referrer policy=] is "`no-referrer`" or "`origin`", then return null. - 1. Return |she|'s [=session history entry/URL=], [=URL serializer|serialized=]. -
- -
- The index getter steps are: - - 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=Document/fully active=], then return −1. - 1. Return [=this=]'s [=NavigationHistoryEntry/index=]. -
- -
- The sameDocument getter steps are: - - 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=Document/fully active=], then return false. - 1. Return true if [=this=]'s [=NavigationHistoryEntry/session history entry=]'s [=session history entry/document=] equals [=this=]'s [=relevant global object=]'s [=associated Document=], and false otherwise. -
- -
- The getState() method steps are: - - 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=Document/fully active=], then return undefined. - 1. If [=this=]'s [=NavigationHistoryEntry/session history entry=]'s [=session history entry/navigation API state=] is null, then return undefined. - 1. Return [$StructuredDeserialize$]([=this=]'s [=NavigationHistoryEntry/session history entry=]'s [=session history entry/navigation API state=]). - -

Unlike {{History/state|history.state}}, this will deserialize upon each access. - -

This can in theory throw an exception, if attempting to deserialize a large {{ArrayBuffer}} when not enough memory is available. -

- -The following are the [=event handlers=] (and their corresponding [=event handler event types=]) that must be supported, as [=event handler IDL attributes=], by objects implementing the {{NavigationHistoryEntry}} interface: - - - - - -
[=Event handler=] - [=Event handler event type=] -
ondispose - dispose -
- -

HTML Standard monkeypatches

- -

New [=session history entry=] items

- -Each [=session history entry=] gains the following new [=struct/items=]: - -* navigation API key, a string, initially set to the result of [=generating a random UUID=] - -* navigation API ID, a string, initially set to the result of [=generating a random UUID=] - -* navigation API state, which is [=serialized state=] or null, initially null - - - - - -

Navigate to a fragment

- -
- Modify the [=navigate to a fragment=] algorithm to take a new |userInvolvement| argument. Then prepend the following steps: - - 1. Let |navigation| be navigable's [=navigable/active window=]'s [=Window/navigation API=]. - 1. Let |continue| be the result of [=firing a non-traversal navigate event=] at |navigation| given with [=fire a non-traversal navigate event/navigationType=] set to historyHandling, [=fire a non-traversal navigate event/isSameDocument=] set to true, [=fire a non-traversal navigate event/userInvolvement=] set to |userInvolvement|, and [=fire a non-traversal navigate event/destinationURL=] set to url. - 1. If |continue| is false, then return. - - Modify the step which creates the new historyEntry to also copy over the [=session history entry/navigation API state=] from the [=navigable/active session history entry=]. - - Insert the following step after the call to [=update document for history step application=]: - - 1. [=Navigation/Update the entries for a same-document navigation=] given navigable's [=navigable/active window=]'s [=Window/navigation API=], navigable's [=navigable/active session history entry=], and historyHandling. -
- - - -

Shared history push/replace state steps

- -
- Modify the shared history push/replace state steps by inserting the following steps right before the step that runs the [=URL and history update steps=]. - - 1. Let |navigation| be history's [=relevant global object=]'s [=Window/navigation API=]. - 1. Let |continue| be the result of [=firing a non-traversal navigate event=] at |navigation| with [=fire a non-traversal navigate event/navigationType=] set to historyHandling, [=fire a non-traversal navigate event/isSameDocument=] set to true, [=fire a non-traversal navigate event/destinationURL=] set to newURL, and [=fire a non-traversal navigate event/classicHistoryAPISerializedData=] set to serializedData. - 1. If |continue| is false, then return. -
- -

URL and history update steps

- -
- Update the [=URL and history update steps=] by appending the following step right after setting the [=navigable/active session history entry=]: - - 1. [=Navigation/Update the entries for a same-document navigation=] given document's [=relevant global object=]'s [=Window/navigation API=], navigable's [=navigable/active session history entry=], and historyHandling. -
- -

Update document for history state application

- -
- Update the [=update document for history step application=] algorithm as follows: - - Prepend the following step: - - 1. Let |navigation| be document's [=relevant global object=]'s [=Window/navigation API=]. - - Inside the "documentsEntryChanged is true" block, after [=restore the history state object=]: - - 1. If documentIsNew is false, then [=Navigation/update the entries for a same-document navigation=] given |navigation|, |entry|, and "{{NavigationType/traverse}}". - - 1. Otherwise, [=Navigation/initialize the entries for a new Navigation=] given |navigation|, sessionHistoryEntries TODO pass this in, and |entry|. - -

This means any {{Navigation/currententrychange}} event and {{NavigationHistoryEntry/dispose}} events will fire before the {{Window/popstate}} and {{Window/hashchange}} events, and those event handlers will see an updated {{Navigation}} object. -

- -

Finalize a cross-document navigation

- -
- Insert the following step into [=finalize a cross-document navigation=]'s "Otherwise" branch (where |entryToReplace| is non-null): - - 1. If |historyEntry|'s [=session history entry/document state=]'s [=document state/origin=] is the [=same origin|same=] as |entryToReplace|'s [=session history entry/document state=]'s [=document state/origin=], then set |historyEntry|'s [=session history entry/navigation API key=] to |entryToReplace|'s [=session history entry/navigation API key=]. -
- -

Reactivate

- -
- Update the [=Document/reactivate=] algorithm by adding the following step before the final top-level step which checks the current document readiness: - - 1. [=Navigation/Update the entries for reactivation=] given document's [=relevant global object=]'s [=Window/navigation API=], sessionHistoryEntries TODO pass this in (harder), and entry TODO pass this in (easier). -
- -

Reload

- -
- Modify the reload algorithm to take an optional |navigationAPIState| (default null) and an optional |userInvolvement| (default "[=user navigation involvement/none=]"). Then, prepend the following steps: - - 1. Let |navigation| be |navigable|'s [=navigable/active window=]'s [=Window/navigation API=]. - 1. If both of the following are true: - * |userInvolvement| is not "[=user navigation involvement/browser UI=]" - * |navigable|'s [=navigable/active document=]'s is initial about:blank is false - - then: - - 1. Let |continue| be the result of [=firing a non-traversal navigate event=] at |navigation| with [=fire a non-traversal navigate event/navigationType=] set to "{{NavigationType/reload}}", [=fire a non-traversal navigate event/isSameDocument=] set to false, [=fire a non-traversal navigate event/userInvolvement=] set to |userInvolvement|, [=fire a non-traversal navigate event/destinationURL=] set to |navigable|'s [=navigable/active document=]'s [=Document/URL=], and [=fire a non-traversal navigate event/state=] set to |navigationAPIState|. - 1. If |continue| is false, return. -
- -

Traversal

- -Modify the [=traverse the history by a delta=] algorithm to take an optional named argument userInvolvement (default "[=user navigation involvement/none=]"). Pass it along when calling [=apply the history step=], which also needs to be modified to take such a argument. - - - -The current specification for downloading a hyperlink has several known issues, most notably whatwg/html#5548 which indicates that the specification should probably be merged into the general navigation algorithm. - -For the purposes of the navigation API, we need to fire the appropriate {{Navigation/navigate}} event, with {{NavigateEvent/downloadRequest}} set to the correct value. We could rigorously detail the ways to modify the current spec to accomplish this. But, given that the current spec will be rewritten anyway, this is probably not very useful. So until such a time as we can properly investigate and rewrite the downloading a hyperlink algorithm, we describe here the expected behavior in a less-formal fashion. We believe this is still enough to get interoperability. - -
-
    -
  • Ensure that the algorithm gets an appropriate [=user navigation involvement=] value, |userInvolvement|, passed to it. This is similar to the modifications for the follow the hyperlink algorithm described in [[#call-site-patch-userinvolvement]]. One key difference is that, for the case where the user indicates a preference for downloading, |userInvolvement| must be "[=user navigation involvement/browser UI=]", even if it is triggered as part of [=EventTarget/activation behavior=]. - -

  • Separate out the sandboxing checks in allowed to download from the user-safeguarding checks. If the sandboxing checks fail, then the user agent must not fire a {{Navigation/navigate}} event. Whereas, the user-safeguarding checks generally happen later, probably [=in parallel=]. - -

  • -

    Before we reach the point at which it's time to actually go in parallel and fetch content from the server, and after the cannot navigate check, the synchronously-possible part of the allowed to download check, the URL parsing step, and the hyperlink suffix appending step, run the equivalent of the following: - - 1. If |userInvolvement| is not "[=user navigation involvement/browser UI=]", then: - 1. Let |navigation| be |subject|'s [=relevant global object=]'s [=Window/navigation API=]. - 1. Let |filename| be the value of |subject|'s <{a/download}> attribute. - 1. Let |continue| be the result of [=firing a download-requested navigate event=] at |navigation| with [=fire a download-requested navigate event/destinationURL=] set to |URL|, [=fire a download-requested navigate event/userInvolvement=] set to |userInvolvement|, and [=fire a download-requested navigate event/filename=] set to |filename|. - 1. If |continue| is false, then return. - -

    Here the variables |subject| and |URL| refer to the same things they currently do in the download the hyperlink algorithm, i.e. the <{a}> or <{area}> element in question, and the parsed [=URL=]. - -

    If we end up triggering the navigate algorithm from the download the hyperlink algorithm, then these steps won't be directly incorporated into the download the hyperlink algorithm. Instead, the modifications in [[#navigate-patch]] will get a bit more complicated, so as to use [=fire a download-requested navigate event=] with the above arguments, instead of [=fire a non-traversal navigate event=], for downloads. -

-
- -

Canceling navigations

- -The navigation API introduces a new complication into canceling navigations, which is that a navigation might have been fully committed, but still be "ongoing", in the sense of [[#ongoing-state]]. That is, consider a case such as: - - -navigation.addEventListener("navigate", e => { - e.intercept({ - handler() { - return new Promise(r => setTimeout(r, 1_000)); - } - }); - e.signal.addEventListener("abort", () => { ... }); -}); - -const p = navigation.navigate("#1"); - -setTimeout(() => window.stop(), 500); - - -Without the {{Navigation/navigate}} event handler, this kind of synchronous fragment navigation would be straightforward: it matures synchronously, and the {{Window/stop()}} call does nothing. But because we have used the {{Navigation/navigate}} handler to indicate that the navigation is still ongoing, we want the {{Window/stop()}} call to [=finalize with an aborted navigation error|finalize that navigation with an aborted navigation error=], in particular causing `p` to reject and the {{AbortSignal/abort}} event to fire on `e.signal`. - -We thus need to add the following integrations: - -* Wherever the spec currently modifies a [=navigable=] |navigable|'s [=navigable/ongoing navigation=], and thus cancels not-yet-committed navigations, we also [=inform the navigation API about canceling navigation=] in |navigable|. (Regardless of whether or not the [=navigable/ongoing navigation=] is set to a [=navigation ID=] or not.) This likely involves introducing a small abstraction around modifying the ongoing navigation. - -* When destroy the nested navigable algortihm runs given a [=navigable=] |navigable|, we also [=inform the navigation API about nested navigable destruction=] given |navigable|. - -It would probably be a good idea to rename [=navigable=]'s [=navigable/ongoing navigation=] concept and/or {{Navigation}} objects' [=Navigation/ongoing navigation=], to avoid the naming overlap, since they mean slightly different things. - - - -
- Modify the [=check if unloading is user-canceled=] algorithm as follows: - - To [=check if unloading is user-canceled=] given a [=list=] of [=navigables=] |navigablesCrossingDocuments|, an optional [=navigable=] |traversable|, an optional integer |targetStep| (default null), and an optional [=user navigation involvement=] |userInvolvement| (default "[=user navigation involvement/none=]"): - 1. Let unloadPromptShown be false. - 1. Let |unloadPromptCanceled| be false. - 1. Let |changingNavigables| be a [=list=] of [=navigables=], initially empty. - 1. If |traversable| is not null and |targetStep| is not null, append the result of [=get all navigables whose current session history entry will change or reload=] given |traversable| and |targetStep| to |changingNavigables|. - 1. Otherwise, append |navigablesCrossingDocuments| to |changingNavigables|. - 1. Let |totalTasks| be the size of |changingNavigables|. - 1. Let |completedTasks| be 0. - 1. If |traversable| is not null and |changingNavigables| [=list/contains=] |traversable|: - 1. Let |targetEntry| be the result of [=getting the target history entry=] given |traversable| and |targetStep|. - 1. Let |isSameOrigin| be true if |targetEntry|'s [=session history entry/document state=]'s [=document state/origin=] is the [=same origin|same=] as |traversable|'s [=navigable/current session history entry=]'s [=session history entry/document state=]'s [=document state/origin=]; otherwise, false. - 1. [=Queue a global task=] on the [=navigation and traversal task source=] given |traversable|'s [=navigable/active window=] to run the following steps: - 1. If |isSameOrigin| is true: - 1. Let |navigateEventResult| be the result of [=firing a traversal navigate event=] at |traversable|'s [=navigable/active window=]'s [=Window/navigation API=] with [=fire a traversal navigate event/destinationEntry=] set to |targetEntry| and [=fire a traversal navigate event/userInvolvement=] set to |userInvolvement|. - 1. If |navigateEventResult| is false, then set |unloadPromptCanceled| to true. - 1. If |unloadPromptCanceled| is false and |navigablesCrossingDocuments| [=list/contains=] |traversable|: - 1. Run the steps to fire a beforeunload event (currently steps 6.1 thru 6.8 in the [=check if unloading is user-canceled=] steps). - 1. Increment |completedTasks|. - 1. Wait for |completedTasks| to be 1. - 1. If |unloadPromptCanceled| is true, then return "refuse"; - 1. For each |navigable| of |changingNavigables|: - 1. If |navigable| equals |traversable|, then [=iteration/continue=]. - 1. Let |targetEntry| be the result of [=getting the target history entry=] given |traversable| and |targetStep|. - 1. Let |isSameOrigin| be true if |targetEntry|'s [=session history entry/document state=]'s [=document state/origin=] is the [=same origin|same=] as |navigable|'s [=navigable/current session history entry=]'s [=session history entry/document state=]'s [=document state/origin=]; otherwise, false. - 1. [=Queue a global task=] on the [=navigation and traversal task source=] given |navigable|'s [=navigable/active window=] to run the following steps: - 1. If |isSameOrigin| is true: - 1. Let |navigateEventResult| be the result of [=firing a traversal navigate event=] at |navigable|'s [=navigable/active window=]'s [=Window/navigation API=] with [=fire a traversal navigate event/destinationEntry=] set to |targetEntry| and [=fire a traversal navigate event/userInvolvement=] set to |userInvolvement|. - 1. [=Assert=]: |navigateEventResult| is true. - 1. If |navigablesCrossingDocuments| [=list/contains=] |navigable|: - 1. Run the steps to fire a beforeunload event (currently steps 6.1 thru 6.8 in the [=check if unloading is user-canceled=] steps). - 1. Increment |completedTasks|. - 1. Wait for |completedTasks| to be |totalTasks|. - 1. Return |unloadPromptCanceled|. -
- -
- Modify the [=apply the history step=] algorithm as follows: - - Change step 5 (which runs the [=check if unloading is user-canceled=] steps) to: - 1. If checkForUserCancelation is true, and the result of [=checking if unloading is user-canceled=] given navigablesCrossingDocuments, traversable, targetStep and userInvolvement is "refuse", then return. - -(Recall that we introduced the userInvolvement parameter as part of [[#traverse-patch]].) - - Before the update document for history step application (currently step 14.10.3), add the following step: - 1. If changingNavigableContinuation's [=update-only=] is true, [=maybe continue the navigate event=] given displayedDocument's [=relevant global object=]'s [=Window/navigation API=]. - -
- -Potentially rename [=check if unloading is user-canceled=] to something like "check for unloading cancelation" to reflect that we are now allowing programmatic cancelation, not just user cancelation. - -

User navigation involvement

- -Introduce (right before the definition of the navigate algorithm) the concept of a user navigation involvement, which is one of the following: - -: "browser UI" -:: The navigation was initiated by the user via browser UI mechanisms -: "activation" -:: The navigation was initiated by the user via the [=EventTarget/activation behavior=] of an element -: "none" -:: The navigation was not initiated by the user - -

This infrastructure partially solves whatwg/html#5381, and it'd be ideal to update the [:Sec-Fetch-Site:] spec at the same time.

- -Define the user navigation involvement for an {{Event}} |event| as "[=user navigation involvement/activation=]" if |event|'s {{Event/isTrusted}} attribute is initialized to true, and "[=user navigation involvement/none=]" otherwise. - -

Various call site patches

- -

User navigation involvement

- -Modify the Browser user interface considerations section to require setting [=navigate=]'s [=navigate/userInvolvement=], reload's [=reload/userInvolvement=], and [=traverse the history by a delta=]'s [=traverse the history by a delta/userInvolvement=] all to "[=user navigation involvement/browser UI=]" for all operations in that section. - -
- -Modify the follow the hyperlink algorithm to take a new userInvolvement argument. Then, update the call to it from navigate to set [=navigate/userInvolvement=] to this userInvolvement value. - -
- Modify the [=EventTarget/activation behavior=] of <{area}> elements by introducing the |event| argument and replacing the follow the hyperlink step with the following: - - 1. Otherwise, follow the hyperlink created by element with the [=Event/user navigation involvement=] for |event|. -
- -
- Modify the [=EventTarget/activation behavior=] of <{a}> elements by replacing its follow the hyperlink step with the following: - - 1. Otherwise, follow the hyperlink created by element with the [=Event/user navigation involvement=] for event. -
- -Expand the section on "Providing users with a means to follow hyperlinks created using the `link` element" by adding the following sentence: - -
Such invocations of follow the hyperlink algorithm must set the [=navigate/userInvolvement=] argument to "[=user navigation involvement/browser UI=]".
- -
- -Modify the plan to navigate algorithm to take a userInvolvement argument. Then, update the call to it from navigate to set [=navigate/userInvolvement=] to this userInvolvement value. - -Modify the submit algorithm to take an optional userInvolvement argument (default "[=user navigation involvement/none=]"). Have the submit algorithm pass along its value to all invocations of plan to navigate. - -Modify the definition of the [=EventTarget/activation behavior=] for <{input}> elements to take an event argument. Then, pass along this argument to the invocation of the input activation behavior. - -Modify the Submit Button state's input activation behavior by having it take an event argument and pass along the [=Event/user navigation involvement=] for event as the final argument when it calls submit. - -Modify the Image Button state's input activation behavior by having it take an event argument and pass along the [=Event/user navigation involvement=] for event as the final argument when it calls submit. - -Modify the <{button}> element's [=EventTarget/activation behavior=] by having it take an event argument and, in the Submit Button case, to pass along the [=Event/user navigation involvement=] for event as the final argument when it calls submit. - -Modify the no-submit button case for implicit form submission to pass along "[=user navigation involvement/activation=]" as the final argument when it calls submit. - -

The case of implicit submission when a submit button is present is automatically taken care of because it fires a (trusted) click event at the submit button.

- -
- -Update the call to from [=navigate=] into [=navigate to a fragment=] to pass along userInvolvement. - -

Form entry list

- -Modify the plan to navigate algorithm to take an additional optional argument entryList (default null). Then, modify the step which calls navigate to pass it along as [=navigate/entryList=], in place of cspNavigationType. - -Modify the submit as entity body algorithm to pass entry list along to plan to navigate as a second argument. - -

Focus tracking

- -To support the {{NavigationInterceptOptions/focusReset}} option, the following patches need to be made: - -Update the focusing steps to, right before they call the focus update steps, set the {{Document}}'s [=relevant global object=]'s [=Window/navigation API=]'s [=Navigation/focus changed during ongoing navigation=] to true. - -Update the focus fixup rule to additionally set the {{Document}}'s [=relevant global object=]'s [=Window/navigation API=]'s [=Navigation/focus changed during ongoing navigation=] to false. - -

In combination, these ensure that the [=Navigation/focus changed during ongoing navigation=] reflects any developer- or user-initiated focus changes, unless they were undone by the focus fixup rule. For example, if the user moved focus to an element which was removed from the DOM while the promise returned from a handler passed to {{NavigateEvent/intercept()}} was settling, then that would not count as a focus change. - -

Scroll restoration

- -To support the {{NavigationInterceptOptions/scroll}} option, as well as to fix whatwg/html#7517, the following patches need to be made: - -Add a boolean, has been scrolled by the user, initially false, to {{Document}} objects. State that if the user scrolls the document, the user agent must set that document's [=Document/has been scrolled by the user=] to true. Modify the [=Document/unload|unload a document=] algorithm to set this back to false. - -
- Define the process of restoring scroll position data given a [=session history entry=] |entry| as follows: - - 1. Let |document| be |entry|'s [=session history entry/document=]. - 1. If |document|'s [=Document/has been scrolled by the user=] is true, then the user agent should return. - 1. The user agent should attempt to use |entry|'s [=session history entry/scroll position data=] to restore the scroll positions of |document|'s restorable scrollable regions. The user agent may continue to attempt to do so periodically, until |document|'s [=Document/has been scrolled by the user=] becomes true. - -

This is formulated as an attempt, which is potentially repeated until success or until the user scrolls, due to the fact that relevant content indicated by the [=session history entry/scroll position data=] might take some time to load from the network. - -

Scroll restoration might be affected by scroll anchoring. [[CSS-SCROLL-ANCHORING-1]] -

- -
- With this in place, modify the restore persisted state algorithm's first step to read as follows: - - 1. If |entry|'s [=session history entry/scroll restoration mode=] is "{{ScrollRestoration/auto}}", and |entry|'s [=session history entry/document=]'s [=relevant global object=]'s [=Window/navigation API=]'s [=Navigation/suppress normal scroll restoration during ongoing navigation=] is false, then [=restore scroll position data=] given |entry|. - - In addition to the existing note, add the following one: - -

If the [=Navigation/suppress normal scroll restoration during ongoing navigation=] boolean is true, then [=restoring scroll position data=] might still happen at a later point, as part of [=potentially process scroll behavior|potentially processing scroll behavior=] for the relevant {{Navigation}} object, or via a {{NavigateEvent/scroll()|navigateEvent.scroll()}} method call. -

- -

Helpful note: failed attempt to populate

- -Inside [=apply the history step=], expand the note which currently says - -
-

This means we tried to populate the document, but were unable to do so, e.g. because of the server returning a 204. -

- -to instead say: - -
-

This means we tried to populate the document, but were unable to do so, e.g. because of the server returning a 204. - -

These kinds of failed navigations or traversals will not be signaled to the navigation API (e.g., through the promises of any [=Navigation/ongoing navigation=], or the {{NavigationTransition/finished|navigation.transition.finished}} promise, or the {{Navigation/navigateerror}} event). Doing so would leak information about the timing of responses from other origins, in the cross-origin case, and providing different results in the cross-origin versus same-origin cases was deemed too confusing. - -

However, implementations could use this opportunity to clear any promise handlers for the {{NavigationTransition/finished|navigation.transition.finished}} promise, as they are guaranteed at this point to never run. And, they might wish to [=report a warning to the console=] if any part of the navigation API initiated these navigations, to make it clear to the web developer the reason why their promises will never settle and events will never fire. -

- -

Security and privacy considerations

- -
- -

Cross-site tracking

- -This specification does not enable any new cross-site tracking capabilities. This is largely because the {{Navigation/entries()|navigation.entries()}} method only returns information about the same-origin, same-frame history entries. - -In more detail: - -* The storage of [=session history entry/navigation API state=] in session history entries is a convenience with no tracking abilities, since the state is only accessible same-origin. That is, it provides the same power as APIs such as partitioned {{WindowSessionStorage/sessionStorage}}. -* The browser-generated UUIDs stored as [=session history entry/navigation API ID=] and [=session history entry/navigation API key=] live only for the lifetime of a browsing session; they are not stable user-specific identifiers, and in particular are not the same across different frames. - -

Navigation monitoring and interception

- -Through the {{Navigation/navigate}} event, this API allows web developers to monitor navigations, and in some cases replace cross-document navigations with same-document ones, or prevent the navigation from going through. - -Care has been taken to avoid this being dangerous, or giving insight into user behavior that would not otherwise be available to the site. In particular: - -* It is not possible to prevent a back/forward navigation, as this could lead to trapping the user on the page. (We would like to make this possible in the future by adding additional security mitigations; see #32.) - -* Navigations initiated from cross-origin-domain frames do not fire {{Navigation/navigate}} events. An example would be such a frame calling `window.open(url, name)` with `name` targeting the current frame. - -* Navigations initiated using browser UI, apart from back/forward navigations or fragment-only navigations, do not fire {{Navigation/navigate}} events. An example would be the user directly editing the URL bar. - -We also have a few more restrictions worth noting, which don't directly address any attack, but reduce the surface area and complexity of the navigation interception feature, which can have indirect security benefits: - -* Navigations initiated toward non-[=fetch scheme=] URLs, such as `javascript:` URLs, do not fire {{Navigation/navigate}} events. - -* Navigations initiated on the initial `about:blank` document do not fire {{Navigation/navigate}} events. - -* Navigations initiated by {{Document/open(unused1, unused2)|document.open()}} do not fire {{Navigation/navigate}} events. - -* Cross-document traversals cannot be intercepted and converted into same-document traversals. - -

URL updates

- -This API, like {{History/pushState()|history.pushState()}} and {{History/replaceState()|history.replaceState()}}, gives the ability to change what is shown in the browser's URL bar. This is part of the navigation interception capability mentioned in the previous section. - -This is not dangerous, because the navigation API is subject to the same restrictions as the classic history API: namely, the page's URL can only be changed if the page can have its URL rewritten to the new URL. So in particular no authority-granting components, such as the site or origin, are impacted. - -

Other user agent UI

- -This specification does not add any requirements on how user agents implement their user interfaces. (Even the URL bar updates mentioned in the previous section are not technically part of the specification; the specification only governs the return value of other APIs, like {{Location/href|location.href}}.) This preserves the ability for user agents to protect users through UI changes. - -For example, today some user agents take advantage of this flexibility to skip certain history entries when pressing the back button. This can be used to avoid back-trapping by abusive sites, by skipping entries with which the user did not interact and thus allowing the user to escape abusive sites faster. - -The navigation API preserves all of these freedoms. - -
diff --git a/spec.html b/spec.html new file mode 100644 index 0000000..bc6b252 --- /dev/null +++ b/spec.html @@ -0,0 +1,10 @@ + + + + + +

The navigation API is moving to the HTML Standard!

+ +

The best specification for the navigation API currently lives as a draft pull request against the HTML Standard: whatwg/html#8502. You can also view a PR preview. Compared to previous versions that lived at this URL, a few bugs were fixed, several terms were renamed, and many monkeypatches were moved to the appropriate location.

+ +

Once that PR is merged, this URL will redirect to the final specification.