-
Notifications
You must be signed in to change notification settings - Fork 30
/
spec.bs
2156 lines (1524 loc) · 149 KB
/
spec.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<pre class="metadata">
Title: Navigation API
Shortname: navigation-api
Repository: WICG/navigation-api
Inline Github Issues: true
Group: WICG
Status: CG-DRAFT
Level: 1
URL: https://wicg.github.io/navigation-api/
Boilerplate: omit conformance, omit feedback-header
Editor: Domenic Denicola, Google https://www.google.com/, d@domenic.me, https://domenic.me/
Abstract: The navigation API provides a web application-focused way of managing same-origin same-frame history entries and navigations.
!Participate: <a href="https://github.com/WICG/navigation-api">GitHub WICG/navigation-api</a> (<a href="https://github.com/WICG/navigation-api/issues/new">new issue</a>, <a href="https://github.com/WICG/navigation-api/issues?state=open">open issues</a>)
!Commits: <a href="https://github.com/WICG/navigation-api/commits/main/spec.bs">GitHub spec.bs commits</a>
Complain About: accidental-2119 yes, missing-example-ids yes
Indent: 2
Default Biblio Status: current
Markup Shorthands: markdown yes
Assume Explicit For: yes
</pre>
<pre class="link-defaults">
spec: html; type: element; text: a
spec: html; type: element-attr; for: a; text: download
</pre>
<pre class="anchors">
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
</pre>
<style>
.selected-text-file-an-issue {
position: fixed;
bottom: 0;
right: 0;
background: rgba(255, 255, 255, 0.8);
font-size: smaller;
padding: 4px 10px;
z-index: 4;
}
dfn var {
font-style: italic;
}
table {
margin: 1em 0;
}
/* WHATWG-style <hr>s, instead of WICG-style. Specific selector is necessary to override WICG styles. */
:not(.head) > :not(.head) + hr {
display: block;
background: none;
border: none;
padding: 0;
margin: 3em 0;
height: auto;
}
:not(.head) > :not(.head) + hr::before {
content: none;
}
/* domintro from https://resources.whatwg.org/standard.css */
.domintro {
position: relative;
color: green;
background: #DDFFDD;
margin: 2.5em 0 2em 0;
padding: 1.5em 1em 0.5em 2em;
}
.domintro dt, .domintro dt * {
color: black;
font-size: inherit;
}
.domintro dd {
margin: 0.5em 0 1em 2em; padding: 0;
}
.domintro dd p {
margin: 0.5em 0;
}
.domintro::before {
content: 'For web developers (non-normative)';
background: green;
color: white;
padding: 0.15em 0.25em;
font-style: normal;
position: absolute;
top: -0.8em;
left: -0.8em;
}
/* .XXX from https://resources.whatwg.org/standard.css */
.XXX {
color: #D50606;
background: white;
border: solid #D50606;
}
</style>
<script src="https://resources.whatwg.org/file-issue.js" async></script>
<h2 id="global">The {{Navigation}} class</h2>
<xmp class="idl">
partial interface Window {
[Replaceable] readonly attribute Navigation navigation;
};
</xmp>
Each {{Window}} object has an associated <dfn for="Window">navigation API</dfn>, which is a new {{Navigation}} instance created alongside the {{Window}}.
The <dfn attribute for="Window">navigation</dfn> getter steps are to return [=this=]'s [=Window/navigation API=].
<xmp class="idl">
[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"
};
</xmp>
Each {{Navigation}} object has an associated <dfn for="Navigation">entry list</dfn>, a [=list=] of {{NavigationHistoryEntry}} objects, initially empty.
Each {{Navigation}} object has an associated <dfn for="Navigation">current entry index</dfn>, an integer, initially −1.
<div algorithm>
A {{Navigation}} |navigation| <dfn for="Navigation">has entries and events disabled</dfn> 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 <a spec="HTML">is initial about:blank</a> is true, then return true.
1. If |document|'s [=Document/origin=] is [=opaque origin|opaque=], then return true.
1. Return false.
</div>
<div algorithm>
To <dfn for="Navigation">update the entries for a same-document navigation</dfn> 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|.
<p class="note">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=].
<p class="note">See <a href="#note-suppress-microtasks-during-navigation-events">a similar note elsewhere for other navigation API events</a> 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=].
</div>
<div algorithm>
To <dfn for="Navigation">update the entries for reactivation</dfn> 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|.
<p class="note">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.
</div>
<div algorithm>
To <dfn for="Navigation">initialize the entries for a new {{Navigation}}</dfn> 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|.
</div>
<div algorithm>
To <dfn>get the navigation API history index</dfn> 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.
</div>
<h3 id="entries-api">Introspecting the navigation API history entry list</h3>
<dl class="domintro non-normative">
<dt><code><var ignore>entries</var> = {{Window/navigation}}.{{Navigation/entries()|entries}}()</code>
<dd>
<p>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.
</dd>
<dt><code>{{Window/navigation}}.{{Navigation/canGoBack}}</code>
<dd>
<p>Returns true if the current {{NavigationHistoryEntry}} is not the first one in the navigation history entry list.
</dd>
<dt><code>{{Window/navigation}}.{{Navigation/canGoForward}}</code>
<dd>
<p>Returns true if the current {{NavigationHistoryEntry}} is not the last one in the navigation history entry list.
</dd>
</dl>
<div algorithm>
The <dfn method for="Navigation">entries()</dfn> method steps are:
1. If [=this=] [=Navigation/has entries and events disabled=], then return the empty list.
1. Return [=this=]'s [=Navigation/entries list=].
</div>
<div algorithm>
The <dfn attribute for="Navigation">canGoBack</dfn> 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.
</div>
<div algorithm>
The <dfn attribute for="Navigation">canGoForward</dfn> 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.
</div>
<h3 id="current-entry">The current entry</h3>
<xmp class="idl">
[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;
};
</xmp>
<dl class="domintro non-normative">
<dt><code>{{Window/navigation}}.{{Navigation/currentEntry}}</code>
<dd>
<p>The current {{NavigationHistoryEntry}}.
</dd>
<dt><code>{{Window/navigation}}.{{Navigation/updateCurrentEntry()|updateCurrentEntry}}({ {{NavigationUpdateCurrentEntryOptions/state}} })</code>
<dd>
<p>Update the [=session history entry/navigation API state=] of the current {{NavigationHistoryEntry}}, without performing a navigation like {{Navigation/reload()|navigation.reload()}} would do.
<p>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()}}.
</dd>
</dl>
<div algorithm>
The <dfn for="Navigation">current entry</dfn> 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=]].
</div>
<p algorithm>
The <dfn attribute for="Navigation">currentEntry</dfn> getter steps are to return the [=Navigation/current entry=] for [=this=].
</p>
<div algorithm>
The <dfn method for="Navigation">updateCurrentEntry(|options|)</dfn> 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|.
</div>
<h3 id="ongoing-state">Ongoing navigation tracking</h3>
<xmp class="idl">
[Exposed=Window]
interface NavigationTransition {
readonly attribute NavigationType navigationType;
readonly attribute NavigationHistoryEntry from;
readonly attribute Promise<undefined> finished;
};
</xmp>
<dl class="domintro">
<dt><code>{{Window/navigation}}.{{Navigation/transition}}</code>
<dd>
<p>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.
<p>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()}}.
</dd>
<dt><code>{{Window/navigation}}.{{Navigation/transition}}.{{NavigationTransition/navigationType}}</code></dt>
<dd>
<p>One of "{{NavigationType/reload}}", "{{NavigationType/push}}", "{{NavigationType/replace}}", or "{{NavigationType/traverse}}", indicating what type of navigation this transition is for.
</dd>
<dt><code>{{Window/navigation}}.{{Navigation/transition}}.{{NavigationTransition/from}}</code></dt>
<dd>
<p>The {{NavigationHistoryEntry}} from which the transition is coming. This can be useful to compare against {{Navigation/currentEntry|navigation.currentEntry}}.
</dd>
<dt><code>{{Window/navigation}}.{{Navigation/transition}}.{{NavigationTransition/finished}}</code></dt>
<dd>
<p>A promise which fulfills at the same time the {{Navigation/navigatesuccess}} event fires, or rejects at the same time the {{Navigation/navigateerror}} fires.
</dd>
<!--
<dt><code>{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/transition}}.{{NavigationTransition/rollback(options)|rollback}}()</code></dt>
<dt><code>{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/transition}}.{{NavigationTransition/rollback(options)|rollback}}({ {{NavigationOptions/info}} })</code></dt>
<dd>
<p>Aborts the ongoing navigation, and immediately performs another navigation which is the logical opposite of the one represented by this transition:
* If {{NavigationTransition/navigationType}} is "{{NavigationType/reload}}", it will perform a replace navigation that resets the navigation API state to that found in the {{NavigationHistoryEntry}} stored in {{NavigationTransition/from}}.
* If {{NavigationTransition/navigationType}} is "{{NavigationType/push}}", it will traverse to the {{NavigationHistoryEntry}} stored in {{NavigationTransition/from}}, and then delete the previously-current {{NavigationHistoryEntry}} from the navigation history entry list, so that it cannot be reached with {{Navigation/forward()|navigation.forward()}} or the forward button.
* If {{NavigationTransition/navigationType}} is "{{NavigationType/replace}}", it will perform another replace navigation that resets the URL and navigation API state to those found in the {{NavigationHistoryEntry}} stored in {{NavigationTransition/from}}.
* If {{NavigationTransition/navigationType}} is "{{NavigationType/traverse}}", it will traverse to the {{NavigationHistoryEntry}} stored in {{NavigationTransition/from}}. (This could involve going either forward or backward in the navigation history entry list.)
<p>Aborting the ongoing navigation will cause {{Navigation/navigateerror}} to fire, any {{NavigateEvent/signal|navigateEvent.signal}} instances to fire {{AbortSignal/abort}}, and any relevant promises to reject. This includes {{NavigationTransition/finished|navigation.transition.finished}}.
<p>Then, the rollback navigation described above starts. This will fire a {{Navigation/navigate}} event, and reset {{Navigation/transition|navigation.transition}} to a new {{NavigationTransition}} instance. The {{NavigationOptions/info}} option, if provided, will populate the {{NavigateEvent/info}} property of the fired event.
<p>This method can only be called while the transition is still ongoing, i.e. while {{Navigation/transition|navigation.transition}} equals this {{NavigationTransition}} object. Calling it afterward will cause both returned promises rejected with an "{{InvalidStateError}}" {{DOMException}}.
</dd>
-->
</dl>
A {{Navigation}} has a <dfn for="Navigation">transition</dfn>, which is a {{NavigationTransition}} or null.
The <dfn attribute for="Navigation">transition</dfn> getter steps are to return [=this=]'s [=Navigation/transition=].
<hr>
A {{NavigationTransition}} has an associated <dfn for="NavigationTransition">navigation type</dfn>, which is a {{NavigationType}}.
A {{NavigationTransition}} has an associated <dfn for="NavigationTransition">from entry</dfn>, which is a {{NavigationHistoryEntry}}.
A {{NavigationTransition}} has an associated <dfn for="NavigationTransition">finished promise</dfn>, which is an {{Promise}}.
The <dfn attribute for="NavigationTransition">navigationType</dfn> getter steps are to return [=this=]'s [=NavigationTransition/navigation type=].
The <dfn attribute for="NavigationTransition">from</dfn> getter steps are to return [=this=]'s [=NavigationTransition/from entry=].
The <dfn attribute for="NavigationTransition">finished</dfn> getter steps are to return [=this=]'s [=NavigationTransition/finished promise=].
<hr>
During any given navigation, the {{Navigation}} object needs to keep track of the following:
<table class="data">
<caption>For all navigations
<thead>
<tr>
<th>State
<th>Duration
<th>Explanation
<tbody>
<tr>
<td>The {{NavigateEvent}}
<td>For the duration of event firing
<td>So that if the navigation is canceled while the event is firing, we can [=Event/canceled flag|cancel=] the event.
<tr>
<td>The event's {{NavigateEvent/signal}}
<td>Until all promises returned from handlers passed to {{NavigateEvent/intercept()}} have settled
<td>So that if the navigation is canceled, we can [=AbortSignal/signal abort=].
<tr>
<td>Whether a new element was <a spec="HTML" lt="focusing steps">focused</a>
<td>Until all promises returned from handlers passed to {{NavigateEvent/intercept()}} have settled
<td>So that if one was, focus is not [=potentially reset the focus|reset=]
<tr>
<td>The {{NavigationHistoryEntry}} being navigated to
<td>From when it is determined, until all promises returned from handlers passed to {{NavigateEvent/intercept()}} have settled
<td>So that we know what to [=resolve=] any {{NavigationResult/committed}} and {{NavigationResult/finished}} promises with.
<tr>
<td>Any {{NavigationResult/finished}} {{Promise}} that was returned
<td>Until all promises returned from handlers passed to {{NavigateEvent/intercept()}} have settled
<td>So that we can [=resolve=] or [=reject=] it appropriately.
</table>
<table class="data">
<caption>For non-"{{NavigationType/traverse}}" navigations
<thead>
<tr>
<th>State
<th>Duration
<th>Explanation
<tbody>
<tr>
<td>Any {{NavigationNavigateOptions/state}}
<td>For the duration of event firing
<td>So that we can update the current entry's state after the event successfully finishes firing without being canceled.
</table>
<table class="data">
<caption>For "{{NavigationType/traverse}}" navigations
<thead>
<tr>
<th>State
<th>Duration
<th>Explanation
<tbody>
<tr>
<td>Any {{NavigationOptions/info}}
<td>Until the task is queued to fire the {{Navigation/navigate}} event
<td>So that we can use it to fire the {{Navigation/navigate}} event after the the trip through the [=traversable navigable/session history traversal queue=].
<tr>
<td>Any {{NavigationResult/committed}} {{Promise}} that was returned
<td>Until the session history is updated (inside that same task)
<td>So that we can [=resolve=] or [=reject=] it appropriately.
<tr>
<td>Whether {{NavigateEvent/intercept()}} was called
<td>Until the session history is updated (inside that same task)
<td>So that we can suppress the normal scroll restoration logic in favor of the chosen {{NavigationInterceptOptions/scroll}} option value.
</table>
Furthermore, we need to account for the fact that there might be multiple traversals queued up, e.g. via
<xmp highlight="js">
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);
</xmp>
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
<xmp highlight="js">
const p1 = navigation.navigate(url1).finished;
const p2 = navigation.navigate(url2).finished;
</xmp>
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 <dfn for="Navigation">ongoing navigate event</dfn>, a {{NavigateEvent}} or null, initially null.
Each {{Navigation}} object has an associated <dfn for="Navigation">ongoing navigation signal</dfn>, which is an {{AbortSignal}} or null, initially null.
Each {{Navigation}} object has an associated <dfn for="Navigation">focus changed during ongoing navigation</dfn>, which is a boolean, initially false.
Each {{Navigation}} object has an associated <dfn for="Navigation">suppress normal scroll restoration during ongoing navigation</dfn>, which is a boolean, initially false.
Each {{Navigation}} object has an associated <dfn for="Navigation">ongoing navigation</dfn>, which is a [=navigation API method navigation=] or null, initially null.
Each {{Navigation}} object has an associated <dfn for="Navigation">upcoming non-traverse navigation</dfn>, which is a [=navigation API method navigation=] or null, initially null.
Each {{Navigation}} object has an associated <dfn for="Navigation">upcoming traverse navigations</dfn>, which is a [=map=] from strings to [=navigation API method navigations=], initially empty.
An <dfn>navigation API method navigation</dfn> is a [=struct=] with the following [=struct/items=]:
* An <dfn for="navigation API method navigation">navigation object</dfn>, a {{Navigation}}
* A <dfn for="navigation API method navigation">key</dfn>, a string or null
* An <dfn for="navigation API method navigation">info</dfn>, a JavaScript value
* A <dfn for="navigation API method navigation">serialized state</dfn>, a [=serialized state=] or null
* A <dfn for="navigation API method navigation">committed-to entry</dfn>, a {{NavigationHistoryEntry}} or null
* A <dfn for="navigation API method navigation">committed promise</dfn>, a {{Promise}}
* A <dfn for="navigation API method navigation">finished promise</dfn>, a {{Promise}}
<p class="note">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.
<div algorithm>
To <dfn for="Navigation">set the upcoming non-traverse navigation</dfn> 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|.
<div class="note" id="note-finished-promise-mark-as-handled">
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.
</div>
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|.
</div>
<div algorithm>
To <dfn for="Navigation">set an upcoming traverse navigation</dfn> 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|.
<p class="note">See <a href="#note-finished-promise-mark-as-handled">the previous discussion</a> as to why this is done.</p>
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|.
</div>
<div algorithm>
To <dfn for="Navigation">promote the upcoming navigation to ongoing</dfn> 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.
</div>
<div algorithm>
To <dfn for="navigation API method navigation">clean up</dfn> 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=]].
</div>
<div algorithm>
To <dfn for="navigation API method navigation">notify about the committed-to entry</dfn> 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=].
<p class="note">If it's null, then we're traversing to |entry| via {{Navigation/traverseTo()}}, which does not allow changing the state.
<p class="note">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|.
<p class="note">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=].
</div>
<div algorithm>
To <dfn for="navigation API method navigation">resolve the finished promise</dfn> 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|.
</div>
<div algorithm>
To <dfn for="navigation API method navigation">reject the finished promise</dfn> 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|.
</div>
<h3 id="global-navigate">Navigating</h3>
<dl class="domintro non-normative">
<dt><code>{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/navigate(url, options)|navigate}}(<var ignore>url</var>)</code>
<dt><code>{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/navigate(url, options)|navigate}}(<var ignore>url</var>, <var ignore>options</var>)</code>
<dd>
<p>Navigates the current page to the given <var ignore>url</var>. <var ignore>options</var> 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 <a spec="HTML" lt="serializable object">serializable</a> 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.)
<p>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.
<p>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.
<p>In all cases, when the returned promises fulfill, it will be with the {{NavigationHistoryEntry}} that was navigated to.
</dd>
<dt><code>{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/reload(options)|reload}}(<var ignore>options</var>)</code>
<dd>
<p>Reloads the current page. The {{NavigationOptions/info}} and {{NavigationReloadOptions/state}} options behave as described above.
<p>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.
<p>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.
</dd>
</dl>
<!-- The following algorithms have several steps that would benefit from https://github.com/heycam/webidl/issues/983. -->
<div algorithm>
The <dfn method for="Navigation">navigate(|url|, |options|)</dfn> 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. <a spec="HTML" lt="parse a URL">Parse</a> |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 <a spec="HTML">resulting URL record</a>.
1. If |options|["{{NavigationNavigateOptions/history}}"] is "{{NavigationHistoryBehavior/push}}", and any of the following are true:
* |document|'s <a spec="HTML">is initial about:blank</a> is true;
* |document| is not <a spec="HTML">completely loaded</a>;
* |url| equals |document|'s [=Document/URL=]; or
* |url|'s [=url/scheme=] is "`javascript`"
then return [=an early error result=] for a "{{NotSupportedError}}" {{DOMException}}.
<div class="note">
<p>These are the conditions under which a push navigation will be converted into a replace navigation by the <a spec="HTML">navigate</a> algorithm or by the below step. If the developer explicitly requested a push, we fail to let them know it won't happen.
<p>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.
</div>
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.
<p class="note">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.</p>
1. Let |info| be |options|["{{NavigationOptions/info}}"] if it exists; otherwise, undefined.
1. Let |historyHandling| be "<a for="history handling behavior">`replace`</a>" if |options|["{{NavigationNavigateOptions/history}}"] is "{{NavigationHistoryBehavior/replace}}" or if |document| is not <a spec="HTML">completely loaded</a>; otherwise, "<a for="history handling behavior">`push`</a>".
1. If |document| is not [=Document/fully active=], then return [=an early error result=] for an "{{InvalidStateError}}" {{DOMException}}.
1. If |document|'s <a spec="HTML">unload counter</a> 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. <a spec="HTML">Navigate</a> |navigable| to |urlRecord| using |document|, with <i>[=navigate/historyHandling=]</i> set to |historyHandling| and <i>[=navigate/navigationAPIState=]</i> set to |serializedState|.
<p class="note">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 <a spec="HTML">allowed by sandboxing to navigate</a> check and its accompanying <i>[=navigate/exceptionsEnabled=]</i> 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:
<p class="note">This means the <a spec="HTML">navigate</a> 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=] ]».
</div>
<div algorithm>
The <dfn method for="Navigation">reload(|options|)</dfn> 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.
<p class="note">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.</p>
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 <a spec="HTML">unload counter</a> 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. <a spec="HTML">Reload</a> |navigable| with <i>[=reload/navigationAPIState=]</i> set to |serializedState|.
1. If |navigation|'s [=Navigation/upcoming non-traverse navigation=] is |ongoingNavigation|, then:
<p class="note">This means the <a spec="HTML">reload</a> 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=] ]».
</div>
<p algorithm>
An <dfn>an early error result</dfn> for an exception |e| is a dictionary instance given by «[ "{{NavigationResult/committed}}" → [=a promise rejected with=] |e|, "{{NavigationResult/finished}}" → [=a promise rejected with=] |e| ]».
</p>
<h3 id="global-traversing">Traversing</h3>
<dl class="domintro non-normative">
<dt><code>{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/traverseTo(key)|traverseTo}}(<var ignore>key</var>)</code>
<dt><code>{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/traverseTo(key, options)|traverseTo}}(<var ignore>key</var>, { {{NavigationOptions/info}} })</code>
<dd>
<p>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.
<p>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.
<p>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.
</dd>
<dt><code>{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/back()|back}}()</code>
<dt><code>{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/back(options)|back}}({ {{NavigationOptions/info}} })</code>
<dd>
<p>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.
<p>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.
<p>The returned promises behave equivalently to those returned by {{Navigation/traverseTo()}}.
</dd>
<dt><code>{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/forward()|forward}}()</code>
<dt><code>{ {{NavigationResult/committed}}, {{NavigationResult/finished}} } = {{Window/navigation}}.{{Navigation/forward(options)|forward}}({ {{NavigationOptions/info}} })</code>
<dd>
<p>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.
<p>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.
<p>The returned promises behave equivalently to those returned by {{Navigation/traverseTo()}}.
</dd>
</dl>
<div algorithm>
The <dfn method for="Navigation">traverseTo(|key|, |options|)</dfn> method steps are:
1. If [=this=]'s [=Navigation/current entry index=] is −1, then return [=an early error result=] for an "{{InvalidStateError}}" {{DOMException}}.