Skip to content

Commit c202c9a

Browse files
committed
Specify goTo()/back()/forward()
1 parent a99db09 commit c202c9a

File tree

2 files changed

+142
-9
lines changed

2 files changed

+142
-9
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,9 +1337,9 @@ interface AppHistory : EventTarget {
13371337
Promise<undefined> navigate(USVString url, optional AppHistoryNavigateOptions options = {});
13381338
Promise<undefined> reload(optional AppHistoryReloadOptions options = {});
13391339
1340-
Promise<undefined> goTo(DOMString key, optional AppHistoryNavigationOptions = {});
1341-
Promise<undefined> back(optional AppHistoryNavigationOptions = {});
1342-
Promise<undefined> forward(optional AppHistoryNavigationOptions = {});
1340+
Promise<undefined> goTo(DOMString key, optional AppHistoryNavigationOptions options = {});
1341+
Promise<undefined> back(optional AppHistoryNavigationOptions options = {});
1342+
Promise<undefined> forward(optional AppHistoryNavigationOptions options = {});
13431343
13441344
attribute EventHandler onnavigate;
13451345
attribute EventHandler onnavigatesuccess;

spec.bs

Lines changed: 139 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,7 @@ spec: html; urlPrefix: https://whatpr.org/html/6315/
5858
text: get all history steps; for: traversable navigable; url: history.html#getting-all-history-steps
5959
text: step; for: session history entry; url: history.html#she-step
6060
text: apply the history step; url: history.html#apply-the-history-step
61-
text: checkForUserCancelation; for: apply the history step; url: history.html#apply-the-history-step
62-
text: initiatorToCheck; for: apply the history step; url: history.html#apply-the-history-step
6361
text: containing navigable; for: browsing context; url: browsers.html#bc-navigable
64-
text: session; for: browsing context group; url: browsers.html#bcg-session
6562
text: active document; for: navigable; url: history.html#nav-document
6663
text: navigable; url: history.html#navigable
6764
spec: uuid; type: dfn; urlPrefix: https://wicg.github.io/uuid/
@@ -157,6 +154,10 @@ interface AppHistory : EventTarget {
157154
Promise<undefined> navigate(USVString url, optional AppHistoryNavigateOptions options = {});
158155
Promise<undefined> reload(optional AppHistoryReloadOptions options = {});
159156

157+
Promise<undefined> goTo(DOMString key, optional AppHistoryNavigationOptions options = {});
158+
Promise<undefined> back(optional AppHistoryNavigationOptions options = {});
159+
Promise<undefined> forward(optional AppHistoryNavigationOptions options = {});
160+
160161
attribute EventHandler onnavigate;
161162
attribute EventHandler onnavigatesuccess;
162163
attribute EventHandler onnavigateerror;
@@ -436,6 +437,139 @@ Each {{AppHistory}} object has an associated <dfn for="AppHistory">navigate meth
436437

437438
<p class="note">Unlike {{Location/assign()|location.assign()}} and friends, which are exposed across [=same origin-domain|origin-domain=] boundaries, {{AppHistory/navigate()|appHistory.navigate()}} and {{AppHistory/reload()|appHistory.reload()}} can only be accessed by code with direct synchronous access to the {{Window/appHistory}} property. Thus, we avoid the complications around tracking <a spec="HTML">source browsing contexts</a>, and we don't need to deal with the <a spec="HTML">allowed to navigate</a> check and its accompanying <i>[=navigate/exceptionsEnabled=]</i> flag. We just treat all navigations as being initiated by the {{AppHistory}} object itself.
438439

440+
<h3 id="global-traversing">Traversing</h3>
441+
442+
<dl class="domintro non-normative">
443+
<dt><code>await {{Window/appHistory}} . {{AppHistory/goTo(key)|goTo}}(<var ignore>key</var>)</code>
444+
<dt><code>await {{Window/appHistory}} . {{AppHistory/goTo(key, options)|goTo}}(<var ignore>key</var>, { {{AppHistoryNavigationOptions/navigateInfo}} })</code>
445+
<dd>
446+
<p>Traverses the <a spec=HTML>joint session history</a> to the closest joint session history entry that matches the {{AppHistoryEntry}} with the given key. {{AppHistoryNavigationOptions/navigateInfo}} can be set to any value; it will populate the {{AppHistoryNavigateEvent/info}} property of the corresponding {{AppHistory/navigate}} event.
447+
448+
<p>The returned promise will behave as follows:
449+
450+
* If there is no {{AppHistoryEntry}} in {{AppHistory/entries|appHistory.entries}} with the given key, it will reject with an "{{InvalidStateError}}" {{DOMException}}.
451+
* For same-document traversals intercepted by the {{AppHistory/navigate}} event's {{AppHistoryNavigateEvent/respondWith()}} method, it will fulfill or reject according to the promise passed to {{AppHistoryNavigateEvent/respondWith()}}.
452+
* For non-intercepted same-document traversals, it will fulfill after the traversal completes.
453+
* For cross-document traversals, it will never settle.
454+
</dd>
455+
456+
<dt><code>await {{Window/appHistory}} . {{AppHistory/back()|back}}()</code>
457+
<dt><code>await {{Window/appHistory}} . {{AppHistory/back(options)|back}}({ {{AppHistoryNavigationOptions/navigateInfo}} })</code>
458+
<dd>
459+
<p>Traverse the <a spec=HTML>joint session history</a> to the closest previous joint session history entry which results in this frame navigating, i.e. results in {{AppHistory/current|appHistory.current}} updating. {{AppHistoryNavigationOptions/navigateInfo}} can be set to any value; it will populate the {{AppHistoryNavigateEvent/info}} property of the corresponding {{AppHistory/navigate}} event.
460+
461+
<p>The returned promise will behave as follows:
462+
463+
* If {{AppHistory/canGoBack|appHistory.canGoBack}} is false, it will reject with an "{{InvalidStateError}}" {{DOMException}}.
464+
* For same-document traversals intercepted by the {{AppHistory/navigate}} event's {{AppHistoryNavigateEvent/respondWith()}} method, it will fulfill or reject according to the promise passed to {{AppHistoryNavigateEvent/respondWith()}}.
465+
* For non-intercepted same-document traversals, it will fulfill after the traversal completes.
466+
* For cross-document traversals, it will never settle.
467+
</dd>
468+
469+
<dt><code>await {{Window/appHistory}} . {{AppHistory/forward()|forward}}()</code>
470+
<dt><code>await {{Window/appHistory}} . {{AppHistory/forward(options)|forward}}({ {{AppHistoryNavigationOptions/navigateInfo}} })</code>
471+
<dd>
472+
<p>Traverse the <a spec=HTML>joint session history</a> to the closest forward joint session history entry which results in this frame navigating, i.e. results in {{AppHistory/current|appHistory.current}} updating. {{AppHistoryNavigationOptions/navigateInfo}} can be set to any value; it will populate the {{AppHistoryNavigateEvent/info}} property of the corresponding {{AppHistory/navigate}} event.
473+
474+
<p>The returned promise will behave as follows:
475+
476+
* If {{AppHistory/canGoBack|appHistory.canGoForward}} is false, it will reject with an "{{InvalidStateError}}" {{DOMException}}.
477+
* For same-document traversals intercepted by the {{AppHistory/navigate}} event's {{AppHistoryNavigateEvent/respondWith()}} method, it will fulfill or reject according to the promise passed to {{AppHistoryNavigateEvent/respondWith()}}.
478+
* For non-intercepted same-document traversals, it will fulfill after the traversal completes.
479+
* For cross-document traversals, it will never settle.
480+
</dd>
481+
</dl>
482+
483+
<div algorithm>
484+
The <dfn method for="AppHistory">goTo(|key|, |options|)</dfn> method steps are:
485+
486+
1. If [=this=]'s [=AppHistory/current index=] is &minus;1, then return [=a promise rejected with=] an "{{InvalidStateError}}" {{DOMException}}.
487+
488+
1. If [=this=]'s [=AppHistory/entry list=] does not contain any {{AppHistoryEntry}} whose [=AppHistoryEntry/session history entry=]'s [=session history entry/app history key=] equals |key|, then return [=a promise rejected with=] an "{{InvalidStateError}}" {{DOMException}}.
489+
490+
1. Return the result of [=performing an app history traversal=] given [=this=], |key|, and |options|.
491+
</div>
492+
493+
<div algorithm>
494+
The <dfn method for="AppHistory">back(|options|)</dfn> method steps are:
495+
496+
1. If [=this=]'s [=AppHistory/current index=] is &minus;1 or 0, then return [=a promise rejected with=] an "{{InvalidStateError}}" {{DOMException}}.
497+
498+
1. Let |key| be [=this=]'s [=AppHistory/entry list=][[=this=]'s [=AppHistory/current index=] &minus; 1]'s [=AppHistoryEntry/session history entry=]'s [=session history entry/app history key=].
499+
500+
1. Return the result of [=performing an app history traversal=] given [=this=], |key|, and |options|.
501+
</div>
502+
503+
<div algorithm>
504+
The <dfn method for="AppHistory">forward(|options|)</dfn> method steps are:
505+
506+
1. If [=this=]'s [=AppHistory/current index=] is &minus;1 or is equal to [=this=]'s [=AppHistory/entry list=]'s [=list/size=] &minus; 1, then return [=a promise rejected with=] an "{{InvalidStateError}}" {{DOMException}}.
507+
508+
1. Let |key| be [=this=]'s [=AppHistory/entry list=][[=this=]'s [=AppHistory/current index=] + 1]'s [=AppHistoryEntry/session history entry=]'s [=session history entry/app history key=].
509+
510+
1. Return the result of [=performing an app history traversal=] given [=this=], |key|, and |options|.
511+
</div>
512+
513+
<div algorithm>
514+
<p class="advisement">The following algorithm is specified in terms of the <a href="https://github.com/whatwg/html/pull/6315">session history rewrite pull request</a> against the HTML Standard, because the existing session history traversal infrastructure is broken enough that it's hard to build on. It is expected to track that work as it continues.</p>
515+
516+
To <dfn>perform an app history traversal</dfn> given an {{AppHistory}} object |appHistory|, a string |key|, and an {{AppHistoryNavigationOptions}} |options|:
517+
518+
1. If |appHistory|'s [=relevant global object=]'s [=associated Document=] is not [=Document/fully active=], then return [=a promise rejected with=] an "{{InvalidStateError}}" {{DOMException}}.
519+
520+
1. If |appHistory|'s [=relevant global object=]'s [=associated Document=]'s <a spec="HTML">unload counter</a> is greater than 0, then return [=a promise rejected with=] an "{{InvalidStateError}}" {{DOMException}}.
521+
522+
1. If |appHistory|'s [=AppHistory/entry list=][|appHistory|'s [=AppHistory/current index=]]'s [=AppHistoryEntry/session history entry=]'s [=session history entry/app history key=] equals |key|, then return [=a promise resolved with=] undefined.
523+
524+
1. Let |navigateInfo| be |options|["{{AppHistoryNavigationOptions/navigateInfo}}"] if it [=map/exists=], or undefined otherwise.
525+
526+
1. Let |navigable| be |appHistory|'s [=relevant global object=]'s [=Window/browsing context=]'s [=browsing context/containing navigable=].
527+
528+
1. Let |traversable| be |navigable|'s [=navigable/traversable navigable=].
529+
530+
1. Let |initiatorBC| be |appHistory|'s [=relevant global object=]'s [=Window/browsing context=].
531+
532+
1. Let |promise| be [=a new promise=] created in |appHistory|'s [=relevant Realm=].
533+
534+
1. [=parallel queue/Enqueue the following steps=] on |traversable|'s [=traversable navigable/session history traversal queue=]:
535+
536+
1. Let |navigableEntries| be the result of [=navigable/getting the session history entries=] given |navigable|.
537+
538+
1. Let |targetEntry| be the [=session history entry=] in |navigableEntries| whose [=session history entry/app history key=] equals |key|. If no such entry exists, then:
539+
540+
1. [=Reject=] |promise| with an "{{InvalidStateError}}" {{DOMException}}.
541+
542+
1. Abort these steps.
543+
544+
<p class="note">This can occur if the |appHistory| 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).
545+
546+
1. Assert: |targetEntry| is not |navigable|'s [=navigable/active session history entry=].
547+
548+
1. Let |targetStep| be null.
549+
550+
1. If |targetEntry|'s [=session history entry/step=] is greater than |traversable|'s [=traversable navigable/current session history step=], then set |targetStep| to |targetEntry|'s [=session history entry/step=].
551+
552+
1. Otherwise:
553+
554+
1. Let |afterTarget| be the [=session history entry=] after |targetEntry| in |navigableEntries|.
555+
556+
1. Let |allSteps| be the result of [=traversable navigable/getting all history steps=] that are part of the target session TODO.
557+
558+
1. Set |targetStep| to the greatest number in |allSteps| that is less than |afterTarget|'s [=session history entry/step=].
559+
560+
1. [=Apply the history step=] |targetStep| to |traversable|, with true, |initiatorBC|, "<code>[=user navigation involvement/none=]</code>", |navigateInfo|.
561+
562+
- If this aborts due to user-canceled unloading or the navigate event being canceled, then [=reject=] |promise| with an "{{AbortError}}" {{DOMException}}.
563+
564+
- If this aborts due to the initiator allowed-to-navigate check, then [=reject=] |promise| with a "{{SecurityError}}" {{DOMException}}.
565+
566+
- TODO: deal with another traversal aborting this one. (The HTML spec PR doesn't support this yet.) Should result in "{{AbortError}}" {{DOMException}}.
567+
568+
<p class="advisement">Eventually [=apply the history step=] will have well-specified hooks for communicating these conditions back to its caller.</p>
569+
</div>
570+
571+
TODO need to stash info and then use it appropriately.
572+
439573
<h3 id="global-events">Event handlers</h3>
440574

441575
The following are the [=event handlers=] (and their corresponding [=event handler event types=]) that must be supported, as [=event handler IDL attributes=], by objects implementing the {{AppHistory}} interface:
@@ -626,7 +760,7 @@ The <dfn attribute for="AppHistoryDestination">sameDocument</dfn> getter steps a
626760
<h3 id="navigate-event-firing">Firing the event</h3>
627761

628762
<div algorithm="fire a traversal navigate event">
629-
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):
763+
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 <dfn for="fire a traversal navigate event">|info|</dfn> (default undefined):
630764

631765
1. Let |destinationURL| be |destinationEntry|'s [=session history entry/URL=].
632766
1. Let |destinationState| be |destinationEntry|'s [=session history entry/app history state=].
@@ -760,8 +894,7 @@ interface AppHistoryEntry : EventTarget {
760894
<dd>
761895
<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.
762896

763-
<!-- TODO proper cross-link -->
764-
<p>This is useful for navigating back to this location in the app history entry list, using `appHistory.goTo(key)`.
897+
<p>This is useful for navigating back to this location in the app history entry list, using {{AppHistory/goTo(key)|appHistory.goTo(key)}}.
765898
</dd>
766899

767900
<dt><code>entry . {{AppHistoryEntry/id}}</code>

0 commit comments

Comments
 (0)