Skip to content

Commit

Permalink
Expand AppHistoryDestination
Browse files Browse the repository at this point in the history
This adds key, id, and index to "traverse" navigations. For non-traverse navigations they are currently null, null, and -1 respectively. This takes care of most of #97 but there's still some potential discussion about non-traverse navigations and about AppHistoryDestination vs. AppHistoryEntry.

Also fixes getState() on AppHistoryDestination to return undefined instead of null, to match getState() on AppHistoryEntry.

Editorially this involves a refactoring to construct the AppHistoryDestination object outside of the inner navigate event firing algorithm, and pass it in.
  • Loading branch information
domenic committed Jul 23, 2021
1 parent 72754f7 commit 9a76e49
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 25 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ The event object has several useful properties:
- `userInitiated`: a boolean indicating whether the navigation is user-initiated (i.e., a click on an `<a>`, or a form submission) or application-initiated (e.g. `location.href = ...`, `appHistory.navigate(...)`, etc.). Note that this will _not_ be `true` when you use mechanisms such as `button.onclick = () => appHistory.navigate(...)`; the user interaction needs to be with a real link or form. See the table in the [appendix](#appendix-types-of-navigations) for more details.
- `destination`: an object containing the information about the destination of the navigation. It has many of the same properties as an `AppHistoryEntry`: namely `url`, `sameDocument`, and `getState()` so far (but see [#97](https://github.com/WICG/app-history/issues/97) for adding more).
- `destination`: an object containing the information about the destination of the navigation. It has many of the same properties as an `AppHistoryEntry`: namely `url`, `sameDocument`, and `getState()` for all navigations, and `id`, `key`, and `index` for `"traverse"` navigations. (See [#97](https://github.com/WICG/app-history/issues/97) for discussion as to whether we should add the latter to non-`"traverse"` navigations as well.)
- `hashChange`: a boolean, indicating whether or not this is a same-document [fragment navigation](https://html.spec.whatwg.org/#scroll-to-fragid).
Expand Down Expand Up @@ -1421,6 +1421,9 @@ dictionary AppHistoryNavigateEventInit : EventInit {
[Exposed=Window]
interface AppHistoryDestination {
readonly attribute USVString url;
readonly attribute DOMString? key;
readonly attribute DOMString? id;
readonly attribute long long index;
readonly attribute boolean sameDocument;
any getState();
Expand Down
86 changes: 62 additions & 24 deletions spec.bs
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,11 @@ An {{AppHistoryNavigateEvent}} also has an associated <dfn for="AppHistoryNaviga
[Exposed=Window]
interface AppHistoryDestination {
readonly attribute USVString url;
readonly attribute DOMString? key;
readonly attribute DOMString? id;
readonly attribute long long index;
readonly attribute boolean sameDocument;

any getState();
};
</xmp>
Expand All @@ -591,6 +595,21 @@ interface AppHistoryDestination {
<p>The URL being navigated to.
</dd>

<dt><code><var ignore>event</var> . {{AppHistoryNavigateEvent/destination}} . {{AppHistoryDestination/key}}</code>
<dd>
<p>The value of the {{AppHistoryEntry/key}} property of the destination {{AppHistoryEntry}}, if this is a "{{AppHistoryNavigationType/traverse}}" navigation, or null otherwise.
</dd>

<dt><code><var ignore>event</var> . {{AppHistoryNavigateEvent/destination}} . {{AppHistoryDestination/id}}</code>
<dd>
<p>The value of the {{AppHistoryEntry/id}} property of the destination {{AppHistoryEntry}}, if this is a "{{AppHistoryNavigationType/traverse}}" navigation, or null otherwise.
</dd>

<dt><code><var ignore>event</var> . {{AppHistoryNavigateEvent/destination}} . {{AppHistoryDestination/index}}</code>
<dd>
<p>The value of the {{AppHistoryEntry/index}} property of the destination {{AppHistoryEntry}}, if this is a "{{AppHistoryNavigationType/traverse}}" navigation, or &minus;1 otherwise.
</dd>

<dt><code><var ignore>event</var> . {{AppHistoryNavigateEvent/destination}} . {{AppHistoryDestination/sameDocument}}</code>
<dd>
<p>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.
Expand All @@ -602,26 +621,38 @@ interface AppHistoryDestination {
<dd>
<p>For "{{AppHistoryNavigationType/traverse}}" navigations, returns the deserialization of the state stored in the destination session history entry.

<p>For "{{AppHistoryNavigationType/push}}" and "{{AppHistoryNavigationType/replace}}" navigations, returns the deserialization of the state passed to {{AppHistory/navigate()|appHistory.navigate()}}, if the navigation was initiated in that way, or null if it wasn't.
<p>For "{{AppHistoryNavigationType/push}}" and "{{AppHistoryNavigationType/replace}}" navigations, returns the deserialization of the state passed to {{AppHistory/navigate()|appHistory.navigate()}}, if the navigation was initiated in that way, or undefined if it wasn't.

<p>For "{{AppHistoryNavigationType/reload}}" navigations, returns the deserialization of the state passed to {{AppHistory/reload()|appHistory.reload()}}, if the reload was initiated in that way, or null if it wasn't.
<p>For "{{AppHistoryNavigationType/reload}}" navigations, returns the deserialization of the state passed to {{AppHistory/reload()|appHistory.reload()}}, if the reload was initiated in that way, or undefined if it wasn't.
</dd>
</dl>

An {{AppHistoryDestination}} has an associated <dfn for="AppHistoryDestination">URL</dfn>, which is a [=URL=].

An {{AppHistoryDestination}} has an associated <dfn for="AppHistoryDestination">key</dfn>, which is a [=string=]-or-null.

An {{AppHistoryDestination}} has an associated <dfn for="AppHistoryDestination">id</dfn>, which is a [=string=]-or-null.

An {{AppHistoryDestination}} has an associated <dfn for="AppHistoryDestination">index</dfn>, which is an integer.

An {{AppHistoryDestination}} has an associated <dfn for="AppHistoryDestination">state</dfn>, which is a [=serialized state=]-or-null.

An {{AppHistoryDestination}} has an associated <dfn for="AppHistoryDestination">is same document</dfn>, which is a boolean.

The <dfn attribute for="AppHistoryDestination">url</dfn> getter steps are to return [=this=]'s [=AppHistoryDestination/URL=], [=URL serializer|serialized=].

The <dfn attribute for="AppHistoryDestination">key</dfn> getter steps are to return [=this=]'s [=AppHistoryDestination/key=].

The <dfn attribute for="AppHistoryDestination">id</dfn> getter steps are to return [=this=]'s [=AppHistoryDestination/id=].

The <dfn attribute for="AppHistoryDestination">index</dfn> getter steps are to return [=this=]'s [=AppHistoryDestination/index=].

The <dfn attribute for="AppHistoryDestination">sameDocument</dfn> getter steps are to return [=this=]'s [=AppHistoryDestination/is same document=].

<div algorithm>
The <dfn method for="AppHistoryDestination">getState()</dfn> method steps are:

1. If [=this=]'s [=AppHistoryDestination/state=] is null, then return null.
1. If [=this=]'s [=AppHistoryDestination/state=] is null, then return undefined.
1. Return [$StructuredDeserialize$]([=this=]'s [=AppHistoryDestination/state=]).
</div>

Expand All @@ -630,42 +661,49 @@ The <dfn attribute for="AppHistoryDestination">sameDocument</dfn> getter steps a
<div algorithm="fire a traversal navigate event">
To <dfn>fire a traversal `navigate` event</dfn> at an {{AppHistory}} |appHistory| given a [=session history entry=] <dfn for="fire a traversal navigate event">|destinationEntry|</dfn>, an optional [=user navigation involvement=] <dfn for="fire a traversal navigate event">|userInvolvement|</dfn> (default "<code>[=user navigation involvement/none=]</code>"), and an optional JavaScript value |info| (default undefined):

1. Let |destinationURL| be |destinationEntry|'s [=session history entry/URL=].
1. Let |destinationState| be |destinationEntry|'s [=session history entry/app history state=].
1. Let |isSameDocument| be true if |destinationEntry|'s [=session history entry/document=] is equal to |appHistory|'s [=relevant global object=]'s [=associated Document=]; otherwise false.
1. Let |event| be the result of [=creating an event=] given {{AppHistoryNavigateEvent}}, in |appHistory|'s [=relevant Realm=].
1. Set |event|'s [=AppHistoryNavigateEvent/destination entry=] to |destinationEntry|.
1. Return the result of performing the [=inner navigate event firing algorithm=] given |appHistory|, |event|, "{{AppHistoryNavigationType/traverse}}", |isSameDocument|, |destinationURL|, |destinationState|, |userInvolvement|, |info|, and null.
1. Let |destination| be a [=new=] {{AppHistoryDestination}} created in |appHistory|'s [=relevant Realm=].
1. Set |destination|'s [=AppHistoryDestination/URL=] to |destinationEntry|'s [=session history entry/URL=].
1. Set |destination|'s [=AppHistoryDestination/key=] to |destinationEntry|'s [=session history entry/app history key=].
1. Set |destination|'s [=AppHistoryDestination/id=] to |destinationEntry|'s [=session history entry/app history id=].
1. Set |destination|'s [=AppHistoryDestination/index=] to |destinationEntry|'s [=AppHistoryEntry/index=].
1. Set |destination|'s [=AppHistoryDestination/state=] to |destinationEntry|'s [=session history entry/app history state=].
1. Set |destination|'s [=AppHistoryDestination/is same document=] to true if |destinationEntry|'s [=session history entry/document=] is equal to |appHistory|'s [=relevant global object=]'s [=associated Document=]; otherwise false.
1. Return the result of performing the [=inner navigate event firing algorithm=] given |appHistory|, "{{AppHistoryNavigationType/traverse}}", |event|, |destination|, |userInvolvement|, |info|, and null.
</div>

<div algorithm="fire a push or replace navigate event">
To <dfn>fire a push or replace `navigate` event</dfn> at an {{AppHistory}} |appHistory| given an {{AppHistoryNavigationType}} <dfn for="fire a push or replace navigate event">|navigationType|</dfn>, a [=URL=] <dfn for="fire a push or replace navigate event">|destinationURL|</dfn>, a boolean <dfn for="fire a push or replace navigate event">|isSameDocument|</dfn>, an optional [=user navigation involvement=] <dfn for="fire a push or replace navigate event">|userInvolvement|</dfn> (default "<code>[=user navigation involvement/none=]</code>"), and an optional value <dfn for="fire a push or replace navigate event">|info|</dfn> (default undefined), an optional [=serialized state=]-or-null <dfn for="fire a push or replace navigate event">|state|</dfn> (default null), an optional [=list=] of {{FormData}} [=FormData/entries=] or null <dfn for="fire a push or replace navigate event">|formDataEntryList|</dfn> (default null), and an optional [=serialized state=]-or-null <dfn for="fire a push or replace navigate event">|classicHistoryAPISerializedData|</dfn> (default null):

1. Let |event| be the result of [=creating an event=] given {{AppHistoryNavigateEvent}}, in |appHistory|'s [=relevant Realm=].
1. Set |event|'s [=AppHistoryNavigateEvent/classic history API serialized data=] to |classicHistoryAPISerializedData|.
1. Return the result of performing the [=inner navigate event firing algorithm=] given |appHistory|, |event|, |navigationType|, |isSameDocument|, |destinationURL|, |state|, |userInvolvement|, |info|, and |formDataEntryList|.
1. Let |destination| be a [=new=] {{AppHistoryDestination}} created in |appHistory|'s [=relevant Realm=].
1. Set |destination|'s [=AppHistoryDestination/URL=] to |destinationURL|.
1. Set |destination|'s [=AppHistoryDestination/key=] to null.
1. Set |destination|'s [=AppHistoryDestination/id=] to null.
1. Set |destination|'s [=AppHistoryDestination/index=] to &minus;1.
1. Set |destination|'s [=AppHistoryDestination/state=] to |state|.
1. Set |destination|'s [=AppHistoryDestination/is same document=] to |isSameDocument|.
1. Return the result of performing the [=inner navigate event firing algorithm=] given |appHistory|, |navigationType|, |event|, |destination|, |userInvolvement|, |info|, and |formDataEntryList|.
</div>

<div algorithm>
The <dfn>inner `navigate` event firing algorithm</dfn> is the following steps, given an {{AppHistory}} |appHistory|, an {{AppHistoryNavigateEvent}} |event|, an {{AppHistoryNavigationType}} |navigationType|, a boolean |isSameDocument|, a [=URL=] |destinationURL|, a [=serialized state=]-or-null |destinationState|, a [=user navigation involvement=] |userInvolvement|, a JavaScript value |info|, and a [=list=] of {{FormData}} [=FormData/entries=] or null |formDataEntryList|:
The <dfn>inner `navigate` event firing algorithm</dfn> is the following steps, given an {{AppHistory}} |appHistory|, an {{AppHistoryNavigationType}} |navigationType|, an {{AppHistoryNavigateEvent}} |event|, an {{AppHistoryDestination}} |destination|, a [=user navigation involvement=] |userInvolvement|, a JavaScript value |info|, and a [=list=] of {{FormData}} [=FormData/entries=] or null |formDataEntryList|:

1. If |appHistory|'s [=relevant global object=]'s [=Window/browsing context=] is <a spec="HTML">still on its initial `about:blank` `Document`</a>, then return true.
1. Initialize |event|'s {{Event/type}} to "{{AppHistory/navigate}}".
1. Initialize |event|'s {{AppHistoryNavigateEvent/navigationType}} to |navigationType|.
1. Initialize |event|'s {{AppHistoryNavigateEvent/info}} to |info|.
1. Let |destination| be a [=new=] {{AppHistoryDestination}} created in |appHistory|'s [=relevant Realm=].
1. Set |destination|'s [=AppHistoryDestination/URL=] to |destinationURL|.
1. Set |destination|'s [=AppHistoryDestination/state=] to |destinationState|.
1. Set |destination|'s [=AppHistoryDestination/is same document=] to |isSameDocument|.
1. Initialize |event|'s {{AppHistoryNavigateEvent/destination}} to |destination|.
1. Let |currentURL| be |appHistory|'s [=relevant global object=]'s [=associated document=]'s [=Document/URL=].
1. If all of the following are true:
* |isSameDocument| is true;
* |destinationURL| [=url/equals=] |currentURL| with <i>[=url/equals/exclude fragments=]</i> set to true; and
* |destinationURL|'s [=url/fragment=] is not [=string/is|identical to=] |currentURL|'s [=url/fragment=]
* |destination|'s [=AppHistoryDestination/is same document=] is true;
* |destination|'s [=AppHistoryDestination/URL=] [=url/equals=] |currentURL| with <i>[=url/equals/exclude fragments=]</i> set to true; and
* |destination|'s [=AppHistoryDestination/URL=]'s [=url/fragment=] is not [=string/is|identical to=] |currentURL|'s [=url/fragment=]

then initialize |event|'s {{AppHistoryNavigateEvent/hashChange}} to true. Otherwise, initialize it to false.
1. If |destinationURL| is [=rewritable=] relative to |currentURL|, and either |isSameDocument| is true or |navigationType| is not "{{AppHistoryNavigationType/traverse}}", then initialize |event|'s {{AppHistoryNavigateEvent/canRespond}} to true. Otherwise, initialize it to false.
1. If |destination|'s [=AppHistoryDestination/URL=] is [=rewritable=] relative to |currentURL|, and either |destination|'s [=AppHistoryDestination/is same document=] is true or |navigationType| is not "{{AppHistoryNavigationType/traverse}}", then initialize |event|'s {{AppHistoryNavigateEvent/canRespond}} to true. Otherwise, initialize it to false.
1. If either |userInvolvement| is not "<code>[=user navigation involvement/browser UI=]</code>" or |navigationType| is not "{{AppHistoryNavigationType/traverse}}", then initialize |event|'s {{Event/cancelable}} to true.
1. If both |event|'s {{AppHistoryNavigateEvent/canRespond}} and |event|'s {{Event/cancelable}} are false, then return true. (No event is actually fired.)
1. If |userInvolvement| is "<code>[=user navigation involvement/none=]</code>", then initialize |event|'s {{AppHistoryNavigateEvent/userInitiated}} to false. Otherwise, initialize it to true.
Expand All @@ -685,7 +723,7 @@ The <dfn attribute for="AppHistoryDestination">sameDocument</dfn> getter steps a
1. Let |isPush| be true if |navigationType| is "{{AppHistoryNavigationType/push}}"; otherwise, false.
1. Run the <a spec="HTML">URL and history update steps</a> given |event|'s [=relevant global object=]'s [=associated document=] and |event|'s {{AppHistoryNavigateEvent/destination}}'s [=AppHistoryDestination/URL=], with <i>[=URL and history update steps/serializedData=]</i> set to |event|'s [=AppHistoryNavigateEvent/classic history API serialized data=] and <i>[=URL and history update steps/isPush=]</i> set to |isPush|.
1. Set |shouldContinue| to false.
1. If |event|'s [=AppHistoryNavigateEvent/navigation action promises list=] is not empty or |isSameDocument| is true, then:
1. If |event|'s [=AppHistoryNavigateEvent/navigation action promises list=] is not empty or |destination|'s [=AppHistoryDestination/is same document=] is true, then:
1. Let |navigateMethodCallPromise| be |appHistory|'s [=AppHistory/navigate method call promise=].
1. [=Wait for all=] of |event|'s [=AppHistoryNavigateEvent/navigation action promises list=], with the following success steps:
1. [=Fire an event=] named {{AppHistory/navigatesuccess}} at |appHistory|.
Expand Down Expand Up @@ -745,9 +783,9 @@ A [=URL=] is <dfn>rewritable</dfn> relative to another [=URL=] if they differ in
<xmp class="idl">
[Exposed=Window]
interface AppHistoryEntry : EventTarget {
readonly attribute USVString url;
readonly attribute DOMString key;
readonly attribute DOMString id;
readonly attribute USVString url;
readonly attribute long long index;
readonly attribute boolean sameDocument;

Expand All @@ -758,6 +796,11 @@ interface AppHistoryEntry : EventTarget {
</xmp>

<dl class="domintro non-normative">
<dt><code>entry . {{AppHistoryEntry/url}}</code>
<dd>
<p>The URL of this app history entry.
</dd>

<dt><code>entry . {{AppHistoryEntry/key}}</code>
<dd>
<p>A [=user agent=]-generated random UUID string representing this app history entry's place in the app history list. This value will be reused by other {{AppHistoryEntry}} instances that replace this one due to replace-style navigations. This value will survive session restores.
Expand All @@ -773,11 +816,6 @@ interface AppHistoryEntry : EventTarget {
<p>This is useful for associating data with this app history entry using other storage APIs.
</dd>

<dt><code>entry . {{AppHistoryEntry/url}}</code>
<dd>
<p>The URL of this app history entry.
</dd>

<dt><code>entry . {{AppHistoryEntry/index}}</code>
<dd>
<p>The index of this app history entry within {{AppHistory/entries()|appHistory.entries()}}, or &minus;1 if the entry is not in the app history list.
Expand Down

0 comments on commit 9a76e49

Please sign in to comment.