-
Notifications
You must be signed in to change notification settings - Fork 42
Expand file tree
/
Copy pathprefetch.bs
More file actions
1075 lines (868 loc) · 84.8 KB
/
prefetch.bs
File metadata and controls
1075 lines (868 loc) · 84.8 KB
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: Prefetch
Shortname: prefetch
Group: WICG
Status: CG-DRAFT
Repository: WICG/nav-speculation
URL: https://wicg.github.io/nav-speculation/prefetch.html
Level: 1
Editor: Jeremy Roman, Google https://www.google.com/, jbroman@chromium.org
Editor: Domenic Denicola, Google https://www.google.com/, d@domenic.me
Abstract: A specification for navigational prefetching
Markup Shorthands: css no, markdown yes
Assume Explicit For: yes
Complain About: accidental-2119 yes, missing-example-ids yes
Indent: 2
Boilerplate: omit conformance
</pre>
<pre class="link-defaults">
spec:fetch; type:dfn; text:credentials
</pre>
<pre class="anchors">
spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/
type: dfn
text: destroy a top-level traversable; url: document-sequences.html#destroy-a-top-level-traversable
urlPrefix: browsers.html
text: CSP-derived sandboxing flags; url: csp-derived-sandboxing-flags
text: cross-origin opener policy enforcement result; url: coop-enforcement-result
text: cross-origin opener policy; url: cross-origin-opener-policy
for: cross-origin opener policy
text: value; url: coop-struct-value
text: reporting endpoint; url: coop-struct-report-endpoint
text: report-only reporting endpoint; url: coop-struct-report-only-endpoint
for: cross-origin opener policy enforcement result
text: needs a browsing context group switch; url: coop-enforcement-bcg-switch
text: would need a browsing context group switch due to report-only; url: coop-enforcement-bcg-switch-report-only
text: url; url: coop-enforcement-url
text: origin; url: coop-enforcement-origin
text: cross-origin opener policy; url: coop-enforcement-coop
text: current context is navigation source; url: coop-enforcement-source
text: determine navigation params policy container; url: determining-navigation-params-policy-container
text: determine the creation sandboxing flags; url: determining-the-creation-sandboxing-flags
text: enforce a response's cross-origin opener policy; url: coop-enforce
text: obtain a cross-origin opener policy; url: obtain-coop
urlPrefix: browsing-the-web.html
text: browsing context scope origin; url: browsing-context-scope-origin
text: document state; url: document-state-2
for: document state
text: history policy container; url: document-state-history-policy-container
text: request referrer; url: document-state-request-referrer
text: request referrer policy; url: document-state-request-referrer-policy
text: initiator origin; url: document-state-initiator-origin
text: origin; url: document-state-origin
text: resource; url: document-state-resource
text: reload pending; url: document-state-reload-pending
text: ever populated; url: document-state-ever-populated
text: navigable target name; url: document-state-nav-target-name
text: history handling behavior; url: history-handling-behavior
text: navigation id; url: navigation-id
text: navigation params; url: navigation-params
for: navigation params
text: id; url: navigation-params-id
text: request; url: navigation-params-request
text: response; url: navigation-params-response
text: origin; url: navigation-params-origin
text: policy container; url: navigation-params-policy-container
text: final sandboxing flag set; url: navigation-params-sandboxing
text: cross-origin opener policy; url: navigation-params-coop
text: COOP enforcement result; url: navigation-params-coop-enforcement-result
text: reserved environment; url: navigation-params-reserved-environment
text: browsing context; url: navigation-params-browsing-context
text: history handling; url: navigation-params-hh
text: has cross-origin redirects; url: navigation-params-has-cross-origin-redirects
text: navigable; url: navigation-params-navigable
text: navigation timing type; url: navigation-params-nav-timing-type
text: fetch controller; url: navigation-params-fetch-controller
text: commit early hints; url: navigation-params-commit-early-hints
text: non-fetch scheme navigation params; url: non-fetch-scheme-navigation-params
for: non-fetch scheme navigation params
text: initiator origin; url: non-fetch-scheme-params-initiator-origin
text: ongoing navigation; url: ongoing-navigation
text: session history entry; url: session-history-entry
for: session history entry
text: URL; url: she-url
text: document state; url: she-document-state
text: serialized state; url: she-serialized-state
text: snapshot source snapshot params; url: snapshotting-source-snapshot-params
text: snapshot target snapshot params; url: snapshotting-target-snapshot-params
text: source snapshot params; url: source-snapshot-params
for: source snapshot params
text: has transient activation; url: source-snapshot-params-activation
text: source policy container; url: source-snapshot-params-policy-container
text: fetch client; url: source-snapshot-params-client
text: sandboxing flags; url: source-snapshot-params-sandbox
text: target snapshot params; url: target-snapshot-params
for: target snapshot params
text: sandboxing flags; url: target-snapshot-params-sandbox
urlPrefix: document-lifecycle.html
text: create and initialize a Document object; url: initialise-the-document-object
urlPrefix: document-sequences.html
text: active browsing context; url: nav-bc
text: determine the origin; url: determining-the-origin
text: navigable container; url: navigable-container
text: parent; url: nav-parent
text: valid navigable target name or keyword; url: valid-navigable-target-name-or-keyword
urlPrefix: dom.html
text: cross-origin opener policy; for: Document; url: concept-document-coop
urlPrefix: semantics.html
text: process early hint headers; url: process-early-hint-headers
urlPrefix: structured-data.html
text: StructuredSerializeForStorage; url: structuredserializeforstorage
urlPrefix: speculative-loading.html
text: cross-origin prefetch IP anonymization policy; url: cross-origin-prefetch-ip-anonymization-policy
text: prefetch candidate; url: prefetch-candidate
text: prefetch IP anonymization policy; url: prefetch-ip-anonymization-policy
text: speculation rule eagerness; url: speculation-rule-eagerness
text: speculation rule tag; url: speculation-rule-tag
text: speculative load candidate; url: speculative-load-candidate
for: cross-origin prefetch IP anonymization policy
text: origin; url: copiap-origin
for: prefetch candidate
text: anonymization policy; url: sl-candidate-anonymization-policy
for: speculative load candidate
text: URL; url: sl-candidate-url
spec: fetch; urlPrefix: https://fetch.spec.whatwg.org/
type: dfn
text: network partition key; url: network-partition-key
spec: RFC9651; urlPrefix: https://www.rfc-editor.org/rfc/rfc9651.html
type: dfn
for: structured header
text: Item; url: name-items
text: List; url: name-lists
text: String; url: name-strings
text: Token; url: name-tokens
spec: COOKIES; urlPrefix: https://httpwg.org/specs/rfc6265.html
type: http-header; text: Cookie; url: cookie
type: http-header; text: Set-Cookie; url: set-cookie
type: dfn; text: cookie; url: storage-model
type: dfn; text: receive a cookie; url: storage-model
type: dfn; text: domain-matches; url: cookie-domain
type: dfn; text: canonicalized host name; url: cookie-domain-canonicalize
type: dfn; text: path-matches; url: cookie-path
spec: prerendering-revamped; urlPrefix: prerendering.html
type: dfn
text: getting the supported loading modes; url: get-the-supported-loading-modes
text: prerendering navigable
text: prerendering traversable
text: prerender candidate
spec: no-vary-search; urlPrefix: https://httpwg.org/http-extensions/draft-ietf-httpbis-no-vary-search.html
type: dfn
text: URL search variance; url: name-data-model
text: obtain a URL search variance; url: name-obtain-a-url-search-varianc
text: equivalent modulo search variance; url: name-comparing
type: http-header
text: No-Vary-Search; url: name-http-header-field-definitio
spec: resource-timing; urlPrefix: https://w3c.github.io/resource-timing/
type: dfn; for: PerformanceResourceTiming; text: delivery type; url: dfn-delivery-type
</pre>
<style>
dfn var { font-style: italic; } /* override bad base.css */
</style>
<h2 id="concepts">Concepts</h2>
In light of <a href="https://privacycg.github.io/storage-partitioning/">storage partitioning</a>, this specification defines prefetch for navigations which would occur within the same partition (for example, top-level navigations within the same site) and for navigations which would occur in a separate partition (for example, top-level navigations to a different site).
<div algorithm>
<dfn>Conflicting credentials exist</dfn> for [=response=] |response| given [=navigable=] |navigable| and [=network partition key=] |sourcePartitionKey| if the following steps return true:
1. Let |hypotheticalEnvironment| be the result of [=creating a reserved client=] given |navigable|, |response|'s [=response/URL=], and null.
1. Let |hypotheticalPartitionKey| be the result of [=determining the network partition key=] given |hypotheticalEnvironment|.
1. If |hypotheticalPartitionKey| is equal to |sourcePartitionKey| or there are no [=credentials=] associated with [=response/URL=] and |hypotheticalPartitionKey|, then return false.
1. Return true.
</div>
<hr>
An <dfn>exchange record</dfn> is a [=struct=] with the following [=struct/items=]:
* <dfn for="exchange record">request</dfn>, a [=request=]
* <dfn for="exchange record">response</dfn>, a [=response=] or null
<div class="note">These records can be used to defer checks that would ordinarily happen during a navigate fetch, and to check for modified [=credentials=].</div>
A <dfn>redirect chain</dfn> is a [=list=] of [=exchange records=].
<div algorithm>
To <dfn for="redirect chain">update</dfn> a [=redirect chain=] |redirectChain| given a [=request=] |request| and [=response=] |response|:
1. [=Assert=]: |redirectChain| is not [=list/empty=].
1. [=Assert=]: |redirectChain|'s last item's [=exchange record/request=] is the same as |request| and its [=exchange record/response=] is null.
1. Set |redirectChain|'s last item's [=exchange record/request=] to a [=request/clone=] of |request|.
<p class="note" id="note-exchange-record-clone-request">The cloning ensures that any modifications made to |request| during further redirects do not affect the stored [=exchange record/request=], which later needs to be consulted at activation time (during [=create navigation params from a prefetch record=]) to get a full picture of the original redirect chain. The only currently known case where a [=request=] field is mutated and is also consulted during activation is the [=request/referrer=] field, but for specification robustness we clone the entire request.</p>
<p class="note" id="note-exchange-record-clone-request-body">Since prefetches <a href="#note-prefetch-only-get">are only ever performed for `` `GET` `` requests</a>, the request [=request/body=] is always null, so cloning is relatively straightforward.</p>
1. Set |redirectChain|'s last item's [=exchange record/response=] to |response|.
</div>
<hr>
Each {{Document}} has <dfn export for="Document">prefetch records</dfn>, which is a [=list=] of [=prefetch records=].
A <dfn export>prefetch record</dfn> is a [=struct=] with the following [=struct/items=]:
* <dfn export for="prefetch record">tags</dfn>, an [=ordered set=] of [=speculation rule tags=]
* <dfn export for="prefetch record">URL</dfn>, a [=URL=]
* <dfn export for="prefetch record">anonymization policy</dfn>, a [=prefetch IP anonymization policy=]
* <dfn export for="prefetch record">referrer policy</dfn>, a [=referrer policy=]
* <dfn export for="prefetch record">No-Vary-Search hint</dfn>, a [=URL search variance=]
* <dfn export for="prefetch record">source</dfn>, a [=string=]
<div class="note">This is intended for use by a specification or [=implementation-defined=] feature to identify which prefetches it created. It might also associate other data with this struct.</div>
* <dfn export for="prefetch record">prerendering traversable</dfn>, a [=prerendering traversable=], "`to be created`", or null (null by default)
* <dfn export for="prefetch record">prerendering target navigable name hint</dfn>, a [=valid navigable target name or keyword=] or null (null by default)
* <dfn export for="prefetch record">state</dfn>, which is "`ongoing`" (the default), "`completed`", or "`canceled`"
<div class="note">"`canceled`" indicates that the prefetch was aborted by the author or user, or terminated by the user agent.</div>
* <dfn export for="prefetch record">fetch controller</dfn>, a [=fetch controller=] (a new [=fetch controller=] by default)
* <dfn export for="prefetch record">redirect chain</dfn>, a [=redirect chain=] (empty by default)
* <dfn export for="prefetch record">start time</dfn>, a {{DOMHighResTimeStamp}} (0.0 by default)
* <dfn export for="prefetch record">expiry time</dfn>, a {{DOMHighResTimeStamp}} (0.0 by default)
* <dfn export for="prefetch record">source partition key</dfn>, a [=network partition key=] or null (the default)
* <dfn export for="prefetch record">isolated partition key</dfn>, a [=network partition key=] whose first item is an [=opaque origin=] and which represents a separate partition in which cross-partition state can be temporarily stored, or null (the default)
* <dfn export for="prefetch record">had conflicting credentials</dfn>, a [=boolean=] (initially false)
<div class="note">This tracks prefetches from when they are started to when they are ultimately used or discarded. Consequently some of these fields are immutable, some pertain to the ongoing activity (like [=prefetch record/fetch controller=]), and some (like [=prefetch record/expiry time=]) are populated when the prefetch completes.</div>
<p class="note">A request which would have ordinarily sent credentials but could not due to cross-partition prefetch causes a prefetch to be abandoned.</p>
A [=prefetch record=]'s <dfn export for="prefetch record">response</dfn> is the [=exchange record/response=] of the last element of its [=prefetch record/redirect chain=], or null if that list [=list/is empty=].
The user agent may [=prefetch record/cancel and discard=] records from the [=Document/prefetch records=] even if they are not expired, e.g., due to resource constraints. Since completed records with expiry times in the past will never be [=wait for a matching prefetch record|matching prefetch records=], they can be removed with no observable consequences.
<div algorithm>
A [=prefetch record=] |prefetchRecord| <dfn export for="prefetch record">matches a URL</dfn> given a [=URL=] |url| if the following algorithm returns true:
1. If |prefetchRecord|'s [=prefetch record/URL=] is equal to |url|, return true.
1. If |prefetchRecord|'s [=prefetch record/response=] is not null:
1. Let |searchVariance| be the result of [=obtaining a URL search variance=] given |prefetchRecord|'s [=prefetch record/redirect chain=][0]'s [=exchange record/response=].
<p class="advisement">It is important that we check the 0th response, not the last response (i.e. not |prefetchRecord|'s [=prefetch record/response=]), because the prefetch record's [=prefetch record/URL=] is that of the first request/response pair, and so it is only that first response's [:No-Vary-Search:] header that should impact matching.
1. If |prefetchRecord|'s [=prefetch record/URL=] and |url| are [=equivalent modulo search variance=] given |searchVariance|, return true.
1. Otherwise, return false.
</div>
<div algorithm>
A [=prefetch record=] |prefetchRecord| <dfn export for="prefetch record">is expected to match a URL</dfn> given a [=URL=] |url| if the following algorithm returns true:
1. If |prefetchRecord| [=prefetch record/matches a URL=] given |url|, return true.
1. If |prefetchRecord|'s [=prefetch record/response=] is null:
1. Let |searchVariance| be |prefetchRecord|'s [=prefetch record/No-Vary-Search hint=].
1. If |prefetchRecord|'s [=prefetch record/URL=] and |url| are [=equivalent modulo search variance=] given |searchVariance|, return true.
1. Otherwise, return false.
</div>
<div algorithm>
To <dfn export for="prefetch record">cancel and discard</dfn> a [=prefetch record=] |prefetchRecord| given a {{Document}} |document|, perform the following steps.
1. [=Assert=]: |prefetchRecord| is in |document|'s [=Document/prefetch records=].
1. [=Assert=]: |prefetchRecord|'s [=prefetch record/state=] is not "`canceled`".
1. Set |prefetchRecord|'s [=prefetch record/state=] to "`canceled`".
1. [=fetch controller/Abort=] |prefetchRecord|'s [=prefetch record/fetch controller=]. <span class="note">This will cause any ongoing fetch to be canceled and yield a [=network error=].</span>
1. If |prefetchRecord|'s [=prefetch record/prerendering traversable=] is a [=traversable navigable=], then [=destroy a top-level traversable|destroy=] it.
1. [=list/Remove=] |prefetchRecord| from |document|'s [=Document/prefetch records=].
1. [=Trigger a prefetch status updated event=] given |document|'s [=node navigable=], |prefetchRecord|, and "{^speculation.PreloadingStatus/failure^}" status.
<p class="note">This means that even a completed prefetch or prerender will not be activated. However, the process of prefetching or prerendering might have modified the HTTP cache, making subsequent navigations faster anyway.</p>
</div>
<div algorithm>
To <dfn export for="prefetch record">complete</dfn> a [=prefetch record=] |prefetchRecord| given {{Document}} |document|, perform the following steps.
1. [=Assert=]: |document| is [=Document/fully active=].
1. Let |currentTime| be the [=current high resolution time=] for the [=relevant global object=] of |document|.
1. Let |expiryTime| be |currentTime| + 300000 (i.e., five minutes).
1. [=list/Remove=] all elements of |document|'s [=Document/prefetch records=] which have the same [=prefetch record/URL=] as |prefetchRecord| and whose [=prefetch record/state=] equals "`completed`".
1. Set |prefetchRecord|'s [=prefetch record/state=] to "`completed`" and [=prefetch record/expiry time=] to |expiryTime|.
1. [=Trigger a prefetch status updated event=] given |document|'s [=node navigable=], |prefetchRecord|, and "{^speculation.PreloadingStatus/ready^}" status.
</div>
<div algorithm>
To <dfn export>find a matching complete prefetch record</dfn> given a [=top-level traversable=] |navigable|, [=source snapshot params=] |sourceSnapshotParams|, and [=URL=] |url|:
1. Let |exactRecord| be null.
1. Let |inexactRecord| be null.
1. [=list/For each=] |record| of |sourceSnapshotParams|'s [=source snapshot params/prefetch records=]:
1. If |record|'s [=prefetch record/state=] is not "`completed`", then [=iteration/continue=].
1. If |record|'s [=prefetch record/URL=] is equal to |url|:
1. Set |exactRecord| to |record|.
1. [=iteration/Break=].
1. If |inexactRecord| is null and |record| [=prefetch record/matches a URL=] given |url|:
1. Set |inexactRecord| to |record|.
1. Let |recordToUse| be |exactRecord| if |exactRecord| is not null, otherwise |inexactRecord|.
1. If |recordToUse| is not null:
1. Let |currentTime| be the [=current high resolution time=] for |navigable|'s [=navigable/active document=].
1. If |recordToUse|'s [=prefetch record/expiry time=] is less than |currentTime|:
1. [=Trigger a prefetch status updated event=] given |navigable|, |recordToUse|, and "{^speculation.PreloadingStatus/failure^}" status.
1. Return null.
1. [=list/For each=] |exchangeRecord| of |recordToUse|'s [=prefetch record/redirect chain=]:
1. If [=conflicting credentials exist=] for |exchangeRecord|'s [=exchange record/response=] given |navigable| and |recordToUse|'s [=prefetch record/source partition key=]:
1. [=Trigger a prefetch status updated event=] given |navigable|, |recordToUse|, and "{^speculation.PreloadingStatus/failure^}" status.
1. Return null.
<div class="note">This handles the case where there were no cross-partition credentials initially, but there are now. User agents could use a slightly coarser algorithm, such as monitoring whether the cookies for the URL have been modified at all, or storing a hash, timestamp or revision number.</div>
1. Return |recordToUse|.
1. Return null.
<p class="note">It's not obvious, but this doesn't actually require that the prefetch have received the complete body, just the response headers. In particular, a navigation to a prefetched response might nonetheless not load instantaneously.</p>
<p class="issue">It might be possible to use cache response headers to determine when a response can be used multiple times, but given the short lifetime of the prefetch buffer it's unclear whether this is worthwhile.</p>
</div>
<div algorithm>
To <dfn export>wait for a matching prefetch record</dfn> given a [=top-level traversable=] |navigable|, [=source snapshot params=] |sourceSnapshotParams|, and [=URL=] |url|:
1. [=Assert=]: this is running [=in parallel=].
1. Let |timeout| be null.
1. Optionally, set |timeout| to an [=implementation-defined=] duration representing the maximum time the implementation is willing to wait for an ongoing prefetch to respond, before it gives up and restarts the navigation.
<p class="note" id="note-wait-for-matching-prefetch-record-timeout">Choosing a good timeout is difficult, and might depend on the exact initiator of the navigation or the prefetch (e.g., its [=speculation rule eagerness=], or whether it will be used for a prerender). In general, a timeout is best avoided, as it is rare that restarting the navigation as a non-prefetch will give a better result than awaiting the ongoing prefetch. But some implementations have found that in some situations, a timeout on the order of seconds can give better results.</p>
1. Let |startTime| be the [=unsafe shared current time=].
1. Run these steps, but [=abort when=] |timeout| is not null and the [=unsafe shared current time=] − |startTime| is greater than |timeout|:
1. While true:
1. Let |completeRecord| be the result of [=finding a matching complete prefetch record=] given |navigable|, |sourceSnapshotParams|, and |url|.
1. If |completeRecord| is not null, return |completeRecord|.
1. Let |potentialRecords| be an empty [=list=].
1. [=list/For each=] |record| of |sourceSnapshotParams|'s [=source snapshot params/prefetch records=]:
1. If all of the following are true:
* |record|'s [=prefetch record/state=] is "`ongoing`";
* |record| [=prefetch record/is expected to match a URL=] given |url|; and
* |record|'s [=prefetch record/expiry time=] is greater than the [=current high resolution time=] for |navigable|'s [=navigable/active window=],
then [=list/append=] |record| to |potentialRecords|.
<div class="note" id="note-wait-for-matching-prefetch-record-potential-records">
<p>Each iteration of the loop recomputes |potentialRecords|, in a way so that a subsequent iteration's |potentialRecords| will be a subset of the previous iteration's |potentialRecords|. This follows from how |sourceSnapshotParams|'s [=source snapshot params/prefetch records=] is a static snapshot, and none of these three conditions can flip from false to true.</p>
<p>An equivalent strategy would be to build a single initial instance of |potentialRecords|, and remove items from it as they no longer meet the criteria.</p>
</div>
1. If |potentialRecords| [=list/is empty=], return null.
1. Wait until the [=prefetch record/state=] of any element of |sourceSnapshotParams|'s [=source snapshot params/prefetch records=] changes.
1. Return null.
<p class="note" id="note-wait-for-matching-prefetch-record-subtleties">Because |sourceSnapshotParams|'s [=source snapshot params/prefetch records=] are a snapshot, prefetches that start after a navigation cannot be activated as part of that navigation.</p>
</div>
<div algorithm>
A [=prefetch record=] |prefetchRecord| is <dfn export for="prefetch record">still being speculated</dfn>, given a list of [=speculative load candidates=] |candidates|, if the following steps return true:
1. [=list/For each=] |candidate| of |candidates|:
1. If |prefetchRecord| does not [=prefetch record/match a URL=] given |candidate|'s [=speculative load candidate/URL=], then [=iteration/continue=].
1. If |candidate| is a [=prefetch candidate=] and |prefetchRecord|'s [=prefetch record/anonymization policy=] does not equal |candidate|'s [=prefetch candidate/anonymization policy=], then [=iteration/continue=].
1. If |candidate| is a [=prerender candidate=] and |prefetchRecord|'s [=prefetch record/prerendering traversable=] is null, then [=iteration/continue=].
1. Return true.
1. Return false.
</div>
<div algorithm="has a matching prefetch record">
A {{Document}} |document| <dfn export>has a matching prefetch record</dfn> given a [=prefetch record=] |recordUnderConsideration| and an optional boolean <dfn for="has a matching prefetch record" export>|checkPrerender|</dfn> (default false) if the following algorithm returns true:
1. [=list/For each=] |record| of |document|'s [=Document/prefetch records=]:
1. If |record|'s [=prefetch record/state=] is "`canceled`", then [=iteration/continue=].
1. If |checkPrerender| is true:
1. If |record|'s [=prefetch record/prerendering traversable=] is null, then [=iteration/continue=].
1. If |record|'s [=prefetch record/prerendering target navigable name hint=] is not equal to |recordUnderConsideration|'s [=prefetch record/prerendering target navigable name hint=], then the user agent may [=iteration/continue=].
<p class="note">User agents which create separate [=prerendering traversables=] depending on the [=prefetch record/prerendering target navigable name hint=] will continue here, since they consider such records to be distinct. Whereas, user agents which are able to activate an existing [=prerendering traversable=] regardless of the [=prefetch record/prerendering target navigable name hint=] will consider such records equivalent.</p>
1. Let |recordNVS| be null.
1. If |record|'s [=prefetch record/response=] is not null, then set |recordNVS| to the result of [=obtaining a URL search variance=] given |record|'s [=prefetch record/response=].
1. Otherwise, set |recordNVS| to |record|'s [=prefetch record/No-Vary-Search hint=].
1. If |recordUnderConsideration|'s [=prefetch record/No-Vary-Search hint=] is not equal to |recordNVS|, then [=iteration/continue=].
1. If |recordUnderConsideration|'s [=prefetch record/URL=] and |record|'s [=prefetch record/URL=] are [=equivalent modulo search variance=] given |recordUnderConsideration|'s [=prefetch record/No-Vary-Search hint=], then return true.
1. Return false.
<p class="note">See the discussion [:No-Vary-Search:] comparison <a href="https://html.spec.whatwg.org/multipage/document-lifecycle.html#note-sl-candidate-redundant-with-nvs-strictness">in the HTML Standard</a>.</p>
</div>
<div algorithm>
To <dfn export>create navigation params from a prefetch record</dfn> given a [=top-level traversable=] |navigable|, a [=document state=] |documentState|, a [=navigation id=] |navigationId|, a {{NavigationTimingType}} |navTimingType|, a [=request=] |request|, a [=prefetch record=] |record|, a [=target snapshot params=] |targetSnapshotParams|, and a [=source snapshot params=] |sourceSnapshotParams|, perform the following steps.
1. Let |responseOrigin| be null.
1. Let |responseCOOP| be null.
1. Let |coopEnforcementResult| be the result of [=creating a cross-origin opener policy enforcement result for navigation=] given |navigable|'s [=navigable/active document=] and |documentState|'s [=document state/initiator origin=].
1. Let |finalSandboxFlags| be an empty [=sandboxing flag set=].
1. Let |responsePolicyContainer| be null.
1. Let |urlList| be an empty [=list=].
1. Let |response| be |record|'s [=prefetch record/response=].
1. [=list/For each=] |exchangeRecord| in |record|'s [=prefetch record/redirect chain=]:
1. Let |redirectChainRequest| be |exchangeRecord|'s [=exchange record/request=].
1. Let |redirectChainResponse| be |exchangeRecord|'s [=exchange record/response=].
1. [=list/Append=] |redirectChainRequest|'s [=request/URL=] to |urlList|.
1. Set |responsePolicyContainer| to the result of [=creating a policy container from a fetch response=] given |redirectChainResponse| and |redirectChainRequest|'s [=request/reserved client=].
1. Set |finalSandboxFlags| to the [=set/union=] of |targetSnapshotParams|'s [=target snapshot params/sandboxing flags=] and |responsePolicyContainer|'s [=policy container/CSP list=]'s [=CSP-derived sandboxing flags=].
1. Set |responseOrigin| to the result of [=determining the origin=] given |redirectChainResponse|'s [=response/URL=], |finalSandboxFlags|, |documentState|'s [=document state/initiator origin=], and null.
1. Set |responseCOOP| to the result of [=obtaining a cross-origin opener policy=] given |redirectChainResponse| and |redirectChainRequest|'s [=request/reserved client=].
1. <span id="step-activation-time-coop-check"></span>Set |coopEnforcementResult| to the result of [=enforcing a response's cross-origin opener policy=] given |navigable|'s [=active browsing context=], |redirectChainResponse|'s [=response/URL=], |responseOrigin|, |responseCOOP|, |coopEnforcementResult|, and |redirectChainRequest|'s [=request/referrer=].
1. If |finalSandboxFlags| is not empty and |responseCOOP|'s [=cross-origin opener policy/value=] is "`unsafe-none`", then set |response| to an appropriate [=network error=] and [=iteration/break=].
1. If |request|'s [=request/URL=] is not equal to |urlList|[0], then insert |request|'s [=request/URL=] into |urlList| after the 0th [=list/item=].
<p class="note" id="note-no-vary-search-final-url-impact">In this case, we are navigating to |request|'s [=request/URL=], but fulfilling it with a prefetch that came from a [=response=] whose URL is |urlList|[0], due to [:No-Vary-Search:]. We treat this as if there was a redirect from the 0th response to [=request/URL=]. If, after this insertion, |urlList|'s [=list/size=] is 2, then the resulting {{Document}} will use the navigated-to URL. Otherwise, if the size is greater, then this will have no effect.
<div class="example" id="example-simple-nvs">
Consider the case where we prefetch `/page?param=1`, and the server responds with `No-Vary-Search: params=("param")` and a 200 response code.
Later, if we navigate to `/page?param=2`, the prefetch will be used. This step will cause |urlList| to become « `/page?param=1`, `/page?param=2` », so the resulting {{Document}} will have `/page?param=2` as its [=Document/URL=].
</div>
<div class="example" id="example-nvs-redirect">
Consider the case where we prefetch `/page?param=1`, and the server responds with `No-Vary-Search: params=("param")`, as well as a 301 response code and `Location: https://other.example/`.
Later, if we navigate to `/page?param=2`, the prefetch will be used. This step will cause |urlList| to become « `/page?param=1`, `/page?param=2`, `https://other.example/` », so the resulting {{Document}} will have `https://other.example/` as its [=Document/URL=].
</div>
1. Set |request|'s [=request/URL list=] to |urlList|.
1. <span id="prefetch-activation-client-creation"></span>Set |request|'s [=request/reserved client=] to the result of [=creating a reserved client=] given |navigable| and |request|'s [=request/URL=].
<p class="note">This will be the [=environment=] used for constructing the resulting {{Document}} and [=environment settings object=]. We need a separate [=environment=] for each time a [=prefetch record=] is consumed, instead of using |record|'s [=prefetch record/redirect chain=]'s last item's [=exchange record/request=]'s [=request/reserved client=], because otherwise reusing the same prefetch record for multiple [=navigation params=] (i.e., multiple navigations) would cause the created [=environment settings objects=] to share an [=environment/id=]. This would then be visible, e.g., through the {{Clients/get()|clients.get()}} API, in a very confusing way.
1. Set |request|'s [=request/reserved client=]'s [=environment/active service worker=] to |record|'s [=prefetch record/redirect chain=]'s last item's [=exchange record/request=]'s [=request/reserved client=]'s [=environment/active service worker=].
<div class="note" id="note-no-vary-search-final-service-worker-impact">
At this point there are potentially two different service workers that are reasonable choices to control the resulting {{Document}}: the one that intercepted the final response during the prefetch process, and the one whose registration [=Match Service Worker Registration|matches=] the navigated-to URL (i.e. |request|'s [=request/URL=]). These will almost always be the same, but in edge cases involving [:No-Vary-Search:] plus web developers who are, for some reason, creating different service workers for `/page?param=1` vs. `/page?param=2`, they could differ.
We choose to use the service worker that intercepted the final response during the prefetch process. This is slightly simpler (requiring no additional service worker lookups), and it matches the behavior for prerendering, where the <a spec="PRERENDERING-REVAMPED" for="prerendering traversable" lt="update the successor for activation">URL update during activation</a> does not cause a service worker switch.
</div>
1. Let |resultPolicyContainer| be the result of [=determining navigation params policy container=] given |record|'s [=prefetch record/response=]'s [=response/URL=], |documentState|'s [=document state/history policy container=], |sourceSnapshotParams|'s [=source snapshot params/source policy container=], null, and |responsePolicyContainer|.
1. If |response| is not a network error, then:
1. Optionally, set |response| to a [=response/clone=] of |response|.
<p class="note">An implementation might wish to do this if it believes that the prefetch will be consumed more than once. For example, if in the future the response is consumed by a prerender, that [=prerendering traversable=] might be [=destroy a top-level traversable|destroyed=] through various means. Normally that would mean the response is discarded, but if the implementation performs this step, then the prefetched response will still be available to serve a future navigation. [[PRERENDERING-REVAMPED]]
1. If the user agent did not perform the previous optional step, then it must [=list/remove=] |record| from |navigable|'s [=navigable/active document=]'s [=Document/prefetch records=].
1. Return a new [=navigation params=], with:
: [=navigation params/id=]
:: |navigationId|
: [=navigation params/request=]
:: |request|
: [=navigation params/response=]
:: |response|
: [=navigation params/origin=]
:: |responseOrigin|
: [=navigation params/policy container=]
:: |resultPolicyContainer|
: [=navigation params/final sandboxing flag set=]
:: |finalSandboxFlags|
: [=navigation params/cross-origin opener policy=]
:: |responseCOOP|
: [=navigation params/COOP enforcement result=]
:: |coopEnforcementResult|
: [=navigation params/reserved environment=]
:: |request|'s [=request/reserved client=]
: [=navigation params/navigable=]
:: |navigable|
: [=navigation params/navigation timing type=]
:: |navTimingType|
: [=navigation params/fetch controller=]
:: |record|'s [=prefetch record/fetch controller=]
<p class="note">In the case where the above <span class=allow-2119>optional</span> step is taken, and thus |record| could end up generating multiple [=navigation params=] via multiple calls to this algorithm, it is OK that all such [=navigation params=] share the same [=navigation params/fetch controller=]. This is because the only use of a [=navigation params=]'s [=navigation params/fetch controller=] is to calculate navigation timing information, which we want to be the same for any reuses.</p>
: [=navigation params/commit early hints=]
:: null
<p class="note" id="no-early-hints">This implies that early hints delivered for prefetched documents won't be processed. This could be revised in the future if it is common for there to be useful resource hints which are not repeated in the main response headers. Since prefetches aren't currently served until the response headers arrive, early hints would not be processed any earlier than ordinary response headers. It might be possible for a future specification to allow early hints found during prefetching to be used in some way.</p>
: [=navigation params/delivery type=]
:: "`navigational-prefetch`"
</div>
<div algorithm>
A [=prefetch IP anonymization policy=] |policy| <dfn for="prefetch IP anonymization policy">requires anonymity</dfn> for [=request=] |request| if the following steps return true:
1. If |policy| is a [=cross-origin prefetch IP anonymization policy=]:
1. If |request|'s [=request/URL=]'s [=url/origin=] is the [=same origin|same=] as |policy|'s [=cross-origin prefetch IP anonymization policy/origin=], then return false.
1. Return true.
1. [=Assert=]: |policy| is null.
1. Return false.
</div>
<h2 id="html-patches">HTML Patches</h2>
This section contains patches to [[HTML]].
Add an additional item to [=environment=] as follows:
: <dfn for="environment">is navigational prefetch client</dfn>
:: a [=boolean=] (default false)
Add an additional item to [=navigation params=] as follows:
: <dfn for="navigation params">delivery type</dfn>
:: a [=string=] (corresponding to {{PerformanceResourceTiming}} [=PerformanceResourceTiming/delivery type=])
Update all creation sites to supply an empty string, except for any in this document which supply a different value (in [=create navigation params from a prefetch record=]).
<hr>
Add an additional item to [=source snapshot params=] as follows:
: <dfn for="source snapshot params">prefetch records</dfn>
:: a [=list=] of [=prefetch records=].
Modify the [=snapshot source snapshot params=] algorithm to set the return value's [=source snapshot params/prefetch records=] to a [=list/clone=] of <var ignore>sourceDocument</var>'s [=Document/prefetch records=].
<hr>
<div algorithm>
<div class="note">This is extracted from <a spec=HTML>create navigation params by fetching</a>.</div>
To <dfn export>create a reserved client</dfn> given a [=navigable=] |navigable|, a [=URL=] |url| and an [=opaque origin=] or null |isolationOrigin|:
1. Let |topLevelCreationURL| be |url|.
1. Let |topLevelOrigin| be null.
1. If |isolationOrigin| is not null, then: <!-- these are new steps to isolate uncredentialed prefetch -->
1. Set |topLevelCreationURL| to `about:blank`.
1. Set |topLevelOrigin| to |isolationOrigin|.
1. Otherwise, if |navigable| is not a [=top-level traversable=], then:
1. Let |parentEnvironment| be |navigable|'s [=parent=]'s [=navigable/active document=]'s [=relevant settings object=].
1. Set |topLevelCreationURL| to |parentEnvironment|'s [=environment/top-level creation URL=].
1. Set |topLevelOrigin| to |parentEnvironment|'s [=environment/top-level origin=].
1. Return a new [=environment=] whose [=environment/id=] is a unique opaque string, [=environment/target browsing context=] is |navigable|'s [=active browsing context=], [=environment/creation URL=] is |url|, [=environment/top-level creation URL=] is |topLevelCreationURL|, and [=environment/top-level origin=] is |topLevelOrigin|.
</div>
<hr>
<div algorithm>
<div class="note">This is extracted from <a spec=HTML>create navigation params by fetching</a>.</div>
To <dfn export>create a cross-origin opener policy enforcement result for navigation</dfn> given a {{Document}} |activeDocument| and an [=origin=] |initiatorOrigin|, return a new [=cross-origin opener policy enforcement result=] with
: [=cross-origin opener policy enforcement result/url=]
:: |activeDocument|'s [=Document/URL=]
: [=cross-origin opener policy enforcement result/origin=]
:: |activeDocument|'s [=Document/origin=]
: [=cross-origin opener policy enforcement result/cross-origin opener policy=]
:: |activeDocument|'s [=Document/cross-origin opener policy=]
: [=cross-origin opener policy enforcement result/current context is navigation source=]
:: true if |activeDocument|'s [=Document/origin=] is [=same origin=] with |initiatorOrigin|; otherwise false
</div>
<hr>
<div algorithm>
<div class="note">This is extracted from <a spec=HTML>create navigation params by fetching</a>.</div>
To <dfn export>create a navigation request</dfn> given a [=session history entry=] |entry|, an [=environment settings object=] |fetchClient|, a [=navigable container=] or null |container|, and a boolean |hasTransientActivation|, perform the following steps.
1. Let |documentResource| be |entry|'s [=session history entry/document state=]'s [=document state/resource=].
1. Let |request| be a new [=request=], with
: [=request/url=]
:: |entry|'s [=session history entry/URL=]
: [=request/policy container=]
:: |entry|'s [=session history entry/document state=]'s [=document state/history policy container=]
: [=request/client=]
:: |fetchClient|
: [=request/destination=]
:: "`document`"
: [=request/credentials mode=]
:: "`include`"
: [=request/use-URL-credentials flag=]
:: set
: [=request/redirect mode=]
:: "`manual`"
: [=request/mode=]
:: "`navigate`"
: [=request/referrer=]
:: |entry|'s [=session history entry/document state=]'s [=document state/request referrer=]
: [=request/referrer policy=]
:: |entry|'s [=session history entry/document state=]'s [=document state/request referrer policy=]
1. If |documentResource| is a [=POST resource=], then:
1. Set |request|'s [=request/method=] to `` `POST` ``.
1. Set |request|'s [=request/body=] to |documentResource|'s [=POST resource/request body=].
1. [=header list/Set=] `` `Content-Type` `` to |documentResource|'s [=POST resource/request content-type=] in |request|'s [=request/header list=].
1. If |entry|'s [=session history entry/document state=]'s [=document state/reload pending=] is true, then set |request|'s [=request/reload-navigation flag=].
1. Otherwise, if |entry|'s [=session history entry/document state=]'s [=document state/ever populated=] is true, then set |request|'s [=request/history-navigation flag=].
1. If |hasTransientActivation| is true, then set |request|'s [=request/user-activation=] to true.
1. If |container| is non-null:
1. If |container| has a [=browsing context scope origin=], then set |request|'s [=request/origin=] to that [=browsing context scope origin=].
1. Set |request|'s [=request/destination=] and [=request/initiator type=] to |container|'s [=Element/local name=].
1. Return |request|.
</div>
<hr>
<div algorithm="attempt to populate the history entry's document">
In <a spec=HTML>attempt to populate the history entry's document</a>, replace the step which invokes <a spec=HTML>create navigation params by fetching</a> with the following:
1. Otherwise, if both of the following are true:
* <var ignore>entry</var>'s [=session history entry/URL=]'s [=url/scheme=] is a [=fetch scheme=]; and
* <var ignore>documentResource</var> is null, or <var ignore>allowPOST</var> is true and <var ignore>documentResource</var>'s [=POST resource/request body=] is not failure
then:
1. Let |request| be the result of [=creating a navigation request=] given <var ignore>entry</var>, <var ignore>sourceSnapshotParams</var>'s [=source snapshot params/fetch client=], <var ignore>navigable</var>'s [=navigable/container=], and <var ignore>sourceSnapshotParams</var>'s [=source snapshot params/has transient activation=].
1. Set |request|'s [=request/replaces client id=] to <var ignore>navigable</var>'s [=navigable/active document=]'s [=relevant settings object=]'s [=environment/id=].
1. Let |prefetched| be false.
1. If <var ignore>documentResource</var> is null and <var ignore>navigable</var> is a [=top-level traversable=]:
1. Let |prefetchRecord| be the result of [=waiting for a matching prefetch record=] given <var ignore>navigable</var>, <var ignore>sourceSnapshotParams</var>, and <var ignore>entry</var>'s [=session history entry/URL=].
1. If |prefetchRecord| is not null:
1. Set <var ignore>navigationParams</var> to the result of [=creating navigation params from a prefetch record=] given <var ignore>navigable</var>, <var ignore>entry</var>'s [=session history entry/document state=], <var ignore>navigationId</var>, <var ignore>navTimingType</var>, <var ignore>request</var>, |prefetchRecord|, <var ignore>targetSnapshotParams</var>, and <var ignore>sourceSnapshotParams</var>.
1. [=Copy prefetch cookies=] given |prefetchRecord|'s [=prefetch record/isolated partition key=] and <var ignore>navigationParams</var>'s [=navigation params/reserved environment=].
<div class="note">This copy is complete before continuing, in the sense that subresource fetches, {{Document/cookie|document.cookie}}, etc. can observe the cookies. If the prefetch never reached a cross-site URL, there will be no cookies to copy.</div>
1. Set |prefetched| to true.
1. [=Trigger a prefetch status updated event=] given <var ignore>navigable</var>, |prefetchRecord|, and "{^speculation.PreloadingStatus/success^}" status.
<p class="note" id="note-prefetch-only-get">The guard on these steps means that prefetches are only ever used to fulfill \``GET`\` requests, and only ever activated into [=top-level traversables=].</p>
1. If |prefetched| is false, then set <var ignore>navigationParams</var> to the result of [=creating navigation params by fetching=] given |request|, <var ignore>entry</var>, <var ignore>navigable</var>, <var ignore>sourceSnapshotParams</var>, <var ignore>targetSnapshotParams</var>, <var ignore>cspNavigationType</var>, <var ignore>navigationId</var>, and <var ignore>navTimingType</var>.
</div>
<hr>
<div algorithm="create navigation params by fetching">
<div class="note">This is an update of the existing <a spec=HTML>create navigation params by fetching</a> algorithm.</div>
To <dfn>create navigation params by fetching</dfn> given a [=request=] |request|, a [=session history entry=] |entry|, a navigable |navigable|, a [=source snapshot params=] |sourceSnapshotParams|, a [=target snapshot params=] |targetSnapshotParams|, a string |cspNavigationType|, a [=navigation ID=] or null |navigationId|, a {{NavigationTimingType}} |navTimingType|, and an optional [=prefetch record=] <dfn id="create-navigation-params-by-fetching-prefetchRecord">|prefetchRecord|</dfn>, perform the following steps.
1. [=Assert=]: this is running [=in parallel=].
1. [=Assert=]: |request|'s [=request/URL=] is |entry|'s [=session history entry/URL=].
1. [=Assert=]: |request|'s [=request/mode=] is "`navigate`".
1. [=Assert=]: |request|'s [=request/redirect mode=] is "`manual`".
1. [=Assert=]: |request|'s [=request/reserved client=] is null.
<!-- this begins around step 9 -->
1. Let |response| be null.
1. Let |responseOrigin| be null.
1. Let |fetchController| be null.
1. Let |coopEnforcementResult| be the result of [=creating a cross-origin opener policy enforcement result for navigation=] given <var ignore>navigable</var>'s [=navigable/active document=] and <var ignore>entry</var>'s [=session history entry/document state=]'s [=document state/initiator origin=].
1. Let |finalSandboxFlags| be an empty [=sandboxing flag set=].
1. Let |responsePolicyContainer| be null.
1. Let |responseCOOP| be a new [=cross-origin opener policy=].
1. Let |locationURL| be null.
1. Let |currentURL| be |request|'s [=request/current URL=].
1. Let |commitEarlyHints| be null.
1. Let |isolationOrigin| be null.
1. If |prefetchRecord| was given:
1. Let |isolationSite| be |prefetchRecord|'s [=prefetch record/isolated partition key=][0].
1. [=Assert=]: |isolationSite| is an [=opaque origin=].
1. Set |isolationOrigin| to |isolationSite|.
1. While true:
1. If |request|'s [=request/reserved client=] is not null and |currentURL|'s [=url/origin=] is not the [=same origin|same=] as |request|'s [=request/reserved client=]'s [=environment/creation URL=]'s [=url/origin=], then:
1. Run the [=environment discarding steps=] for |request|'s [=request/reserved client=].
1. Set |request|'s [=request/reserved client=] to null.
1. Set |commitEarlyHints| to null.
1. If |request|'s [=request/reserved client=] is null, then:
1. Set |request|'s [=request/reserved client=] to the result of [=creating a reserved client=] given |navigable|, |currentURL| and |isolationOrigin|.
1. If |prefetchRecord| was given, then set |request|'s [=request/reserved client=]'s [=environment/is navigational prefetch client=] to true.
1. If the result of [=should navigation request of type be blocked by Content Security Policy?=] given |request| and |cspNavigationType| is "`Blocked`", then set |response| to a [=network error=] and [=iteration/break=]. [[CSP]]
1. If |prefetchRecord| was given, then:
1. Let |purpose| be a [=structured header/List=] containing the [=structured header/Token=] `prefetch`.
1. If |prefetchRecord|'s [=prefetch record/anonymization policy=] [=prefetch IP anonymization policy/requires anonymity=] for |request|, then:
1. Add a parameter whose key is "<a for="Sec-Purpose prefetch">`anonymous-client-ip`</a>" and whose value is true to the `prefetch` token in |purpose|.
1. The user agent must use a [=connection=] which anonymizes the client IP address (e.g., using a proxy) when fetching |request|, or set |response| to a [=network error=] and [=iteration/break=].
<p class="issue">At the moment, how IP anonymization is achieved is handwaved. This will probably be done in an [=implementation-defined=] manner using some kind of proxy or relay. Ideally this would be plumbed down to [=obtain a connection=], and possibly even the mechanism could be further standardized.</p>
1. If |prefetchRecord|'s [=prefetch record/prerendering traversable=] is not null, then add a parameter whose key is "<a for="Sec-Purpose prefetch">`prerender`</a>" and whose value is true to the `prefetch` token in |purpose|.
1. [=header list/Set a structured field value=] given ([:Sec-Purpose:], |purpose|) in |request|'s [=request/header list=].
<div class="note">
Implementations might also send vendor-specific headers, like Chromium's `` `Purpose` ``/`` `prefetch` ``, Mozilla's `` `X-moz` ``/`` `prefetch` ``, and WebKit's `` `X-Purpose` ``/`` `preview` ``, for compatibility with existing server software. Over time we hope implementers and server software authors will adopt this common header.
</div>
1. If |request|'s [=request/current URL=]'s [=url/origin=] is [=same site=] with |prefetchRecord|'s [=prefetch record/URL=]'s [=url/origin=], then:
1. Let |tags| be a [=structured header/List=] composed by mapping each [=list/item=] in |prefetchRecord|'s [=prefetch record/tags=], sending [=strings=] to [=structured header/Strings=] and null to the [=structured header/Token=] `null`.
1. [=header list/Set a structured field value=] given ([:Sec-Speculation-Tags:], |tags|) in |request|'s [=request/header list=].
1. If |request|'s [=request/current URL=] is not [=potentially trustworthy URL|potentially trustworthy=], then set |response| to a [=network error=] and [=iteration/break=].
<div class="note">This is intended to both reduce the likelihood of prefetch traffic being visible to an on-path attacker, and to encourage the use of cryptographic schemes over public networks.</div>
1. Let |proposedPartitionKey| be the result of [=determining the network partition key=] given |request|'s [=request/reserved client=].
1. If |proposedPartitionKey| is not equal to |prefetchRecord|'s [=prefetch record/source partition key=] and |request|'s [=request/referrer policy=] is not in the [=list of sufficiently strict speculative navigation referrer policies=], then set |response| to a [=network error=] and [=iteration/break=].
<div class="note">In practice, this means that cross-site prefetches will abandon rather than expose more information about the referrer URL than the origin.</div>
1. If |request| cannot be fetched given |prefetchRecord|'s [=prefetch record/anonymization policy=] for an [=implementation-defined=] reason, then set |response| to a [=network error=] and [=iteration/break=].
<div class="note">This explicitly acknowledges that implementations might have additional restrictions. For instance, anonymized traffic might not be possible to some hosts, such as those that are not publicly routable and those that have <a href="https://buettner.github.io/private-prefetch-proxy/traffic-advice.html">traffic advice</a> declining private prefetch traffic.</div>
1. [=list/Append=] a new [=exchange record=] whose [=exchange record/request=] is |request| and [=exchange record/response=] is null to |prefetchRecord|'s [=prefetch record/redirect chain=].
1. Set |response| to null.
1. If |fetchController| is null, then set |fetchController| to the result of [=fetching=] |request|, with <i>[=fetch/processEarlyHintsResponse=]</i> set to |processEarlyHintsResponse| as defined below, <i>[=fetch/processResponse=]</i> set to |processResponse| as defined below, and <i>[=fetch/useParallelQueue=]</i> set to true.
Let |processEarlyHintsResponse| be the following algorithm given a [=response=] |earlyResponse|:
1. If |prefetchRecord| was given and |commitEarlyHints| is null, then set |commitEarlyHints| to the result of [=processing early hint headers=] given |earlyResponse| and |request|'s [=request/reserved client=]. <!-- this has an additional condition to suppress extra fetches due to early hints while prefetching -->
Let |processResponse| be the following algorithm given a [=response=] |fetchedResponse|:
1. Set |response| to |fetchedResponse|.
1. Otherwise, [=fetch controller/process the next manual redirect=] for |fetchController|.
1. Wait until either |response| is non-null, or |navigable|'s [=ongoing navigation=] changes to no longer equal |navigationId|.
If the latter condition occurs, then [=fetch controller/abort=] |fetchController|, and return. Otherwise, proceed onward. <span class="issue">This aborts prefetches the moment a navigation starts, which might not be desirable.</span>
1. If |request|'s [=request/body=] is null, then set |entry|'s [=session history entry/document state=]'s [=document state/resource=] to null.
1. Set |responsePolicyContainer| to the result of [=creating a policy container from a fetch response=] given |response| and |request|'s [=request/reserved client=].
1. Set |finalSandboxFlags| to the [=set/union=] of |targetSnapshotParams|'s [=target snapshot params/sandboxing flags=] and |responsePolicyContainer|'s [=policy container/CSP list=]'s [=CSP-derived sandboxing flags=].
1. Set |responseOrigin| to the result of [=determining the origin=] given |response|'s [=response/URL=], |finalSandboxFlags|, |entry|'s [=session history entry/document state=]'s [=document state/initiator origin=], and null.
1. If |navigable| is a [=top-level traversable=], and |prefetchRecord| was not given:
1. Set |responseCOOP| to the result of [=obtaining a cross-origin opener policy=] given |response| and |request|'s [=request/reserved client=].
1. Set |coopEnforcementResult| to the result of [=enforcing a response's cross-origin opener policy=] given |navigable|'s [=active browsing context=], |request|'s [=request/URL=], |responseOrigin|, |responseCOOP|, |coopEnforcementResult|, and |request|'s [=request/referrer=].
1. If |finalSandboxFlags| is not empty and |responseCOOP|'s [=cross-origin opener policy/value=] is not "`unsafe-none`", then set |response| to an appropriate [=network error=] and [=iteration/break=].
<p class="note" id="note-prefetch-coop-delay">COOP checks for prefetches are performed at activation time, when we know the target browsing context.
1. If |response| is not a [=network error=], |navigable| is a [=child navigable=], and the result of performing a [=cross-origin resource policy check=] with |navigable|'s [=navigable/container document=]'s [=Document/origin=], |navigable|'s [=navigable/container document=]'s [=relevant settings object=], |request|'s [=request/destination=], |response|, and true is <strong>blocked</strong>, then set |response| to a [=network error=] and [=iteration/break=].
1. If |prefetchRecord| was given, then:
1. [=redirect chain/Update=] |prefetchRecord|'s [=prefetch record/redirect chain=] given |request| and |response|.
1. If [=conflicting credentials exist=] for |response| given |navigable| and |prefetchRecord|'s [=prefetch record/source partition key=], then set |prefetchRecord|'s [=prefetch record/had conflicting credentials=] to true.
<p class="note">This does not immediately abort the prefetch or stop following redirects, because doing so might reveal whether or not the user has stored state outside the current partition, before the user navigates. Instead, the prefetch continues as though there were no conflicting credentials, except that the prefetch cannot actually be used. User agents might wish to [=report a warning to the console=] or otherwise inform authors that this has happened.</p>
1. Set |locationURL| to |response|'s [=response/location URL=] given |currentURL|'s [=url/fragment=].
1. If |locationURL| is failure or null, then [=iteration/break=].
1. [=Assert=]: |locationURL| is a [=URL=].
1. Set |entry|'s [=session history entry/serialized state=] to [=StructuredSerializeForStorage=](null).
1. Let |oldDocState| be |entry|'s [=session history entry/document state=].
1. Set |entry|'s [=session history entry/document state=] to a new [=document state=], with:
: [=document state/history policy container=]
:: a [=clone a policy container|clone=] of |oldDocState|'s [=document state/history policy container=]
: [=document state/request referrer=]
:: |oldDocState|'s [=document state/request referrer=]
: [=document state/request referrer policy=]
:: |oldDocState|'s [=document state/request referrer policy=]
: [=document state/origin=]
:: |oldDocState|'s [=document state/origin=]
: [=document state/resource=]
:: |oldDocState|'s [=document state/resource=]
: [=document state/ever populated=]
:: |oldDocState|'s [=document state/ever populated=]
: [=document state/navigable target name=]
:: |oldDocState|'s [=document state/navigable target name=]
1. If |locationURL|'s [=url/scheme=] is not an [=HTTP(S) scheme=], then:
1. Set |entry|'s [=session history entry/document state=]'s [=document state/resource=] to null.
1. [=iteration/Break=].
1. Set |currentURL| to |locationURL|.
1. Set |entry|'s [=session history entry/URL=] to |currentURL|.
1. If |locationURL| is a [=request/URL=] whose [=url/scheme=] is not a [=fetch scheme=], then return a new [=non-fetch scheme navigation params=], with:
: [=non-fetch scheme navigation params/initiator origin=]
:: |request|'s [=request/current URL=]'s [=url/origin=]
1. If any of the following are true:
* |response| is a [=network error=];
* |locationURL| is failure; or
* |locationURL| is a [=URL=] whose [=url/scheme=] is a [=fetch scheme=]
then return null.
1. Let |resultPolicyContainer| be the result of [=determining navigation params policy container=] given |response|'s [=response/URL=], |entry|'s [=session history entry/document state=]'s [=document state/history policy container=], |sourceSnapshotParams|'s [=source snapshot params/source policy container=], null, and |responsePolicyContainer|.
1. Return a new [=navigation params=], with:
: [=navigation params/id=]
:: |navigationId|
: [=navigation params/request=]
:: |request|
: [=navigation params/response=]
:: |response|
: [=navigation params/origin=]
:: |responseOrigin|
: [=navigation params/policy container=]
:: |resultPolicyContainer|
: [=navigation params/final sandboxing flag set=]
:: |finalSandboxFlags|
: [=navigation params/cross-origin opener policy=]
:: |responseCOOP|
: [=navigation params/COOP enforcement result=]
:: |coopEnforcementResult|
: [=navigation params/reserved environment=]
:: |request|'s [=request/reserved client=]
: [=navigation params/navigable=]
:: |navigable|
: [=navigation params/navigation timing type=]
:: |navTimingType|
: [=navigation params/fetch controller=]
:: |fetchController|
: [=navigation params/commit early hints=]
:: |commitEarlyHints|
</div>
<hr>
<div algorithm="create and initialize a Document object">
Update the steps to [=create and initialize a Document object=] to add an additional argument to [=create the navigation timing entry=], as follows: <var ignore>navigationParams</var>'s [=navigation params/delivery type=].
</div>
<hr>
<div algorithm=" obtain a browsing context to use for a navigation response">
Modify <a spec=HTML>obtain a browsing context to use for a navigation response</a>'s step which checks if <var ignore>sourceOrigin</var> is not [=same site=] with <var ignore>destinationOrigin</var> by inserting the following substep:
1. If <var ignore>navigationParams</var>'s [=navigation params/delivery type=] is "`prefetch`", then optionally set <var ignore>swapGroup</var> to true.
<p class="note">This makes it more difficult for the referring site to tell whether the prefetch was used or not by measuring load timing. This helps prevent information about the destination site from leaking to the referring site.</p>
</div>
<h2 id="navigation-timing-patches">Navigation Timing Patches</h2>
This section contains patches to [[NAVIGATION-TIMING]].
<div algorithm="create the navigation timing entry">
Add an additional parameter to [=create the navigation timing entry=], which is a [=string=] <var ignore>deliveryType</var>, and pass it as an additional argument to [=setup the resource timing entry=].
</div>
<h2 id="service-workers-patches">Service Workers Patches</h2>
This section contains patches to [[SERVICE-WORKERS]].
<div algorithm="Create Fetch Event and Dispatch">
Modify <a spec="SERVICE-WORKERS">Create Fetch Event and Dispatch</a>'s step which sets {{FetchEvent/resultingClientId}} as follows:
<blockquote>
If |request| is a [=non-subresource request=] and |request|'s [=request/destination=] is not "{{RequestDestination/report}}"<ins> and |reservedClient|'s [=environment/is navigational prefetch client=] is false</ins>, initialize <var ignore>e</var>'s {{FetchEvent/resultingClientId}} attribute to |reservedClient|'s [=environment/id=], and to the empty string otherwise.
</blockquote>
<p class="note">In the prefetch case, although |request| has a [=request/reserved client=] at this time, it is just a specification fiction to help the request go through. It does not represent an actual client, of the sort that can be seen in e.g. {{Clients/matchAll()|clients.matchAll()}}. Actual client creation happens <a href="#prefetch-activation-client-creation">at activation time</a>, which will be after the {{FetchEvent}} is done firing.
</div>
<h2 id="clear-site-data-patches">Clear Site Data patches</h2>
Add an additional header value description in [[CLEAR-SITE-DATA#header]]:
: "<dfn><code>prefetchCache</code></dfn>"
::
<p>The "`prefetchCache`" type indicates that the server wishes to remove any prefetches initiated by the [=url/origin=] of a particular [=/response=]'s [=response/URL=].
<p>This type is a subset of the "`cache`" type.
<p>Implementation details are below.
Modify <a abstract-op spec="CLEAR-SITE-DATA">parse response's Clear-Site-Data header</a>'s parsing step such that:
* encountering <code>\`"cache"\`</code> or <code>\`"*"\`</code> appends "<code>[=prefetchCache=]</code>" to <var ignore>types</var> (in addition to the values already appended in those branches); and
* encountering <code>\`"prefetchCache"\`</code> appends "<code>[=prefetchCache=]</code>" to <var ignore>types</var>.
Modify <a abstract-op spec="CLEAR-SITE-DATA">clear site data for response</a>'s switch to add a case that handles "<code>[=prefetchCache=]</code>" and calls [=clear prefetch cache=] for <var ignore>origin</var>.
<div>
To <dfn>clear prefetch cache</dfn> given an [=origin=] |origin|:
1. [=list/For each=] [=navigable/top-level traversable=] |traversable| of the user agent's [=user agent/top-level traversable set=]:
1. Let |navigables| be the [=Document/inclusive descendant navigables=] of |traversable|'s [=navigable/active document=].
1. [=list/For each=] |navigable| in |navigables|:
1. Let |activeDocument| be the |navigable|'s [=navigable/active document=].
1. If |activeDocument|'s [=Document/origin=] is not [=same origin=] with |origin|, then [=iteration/continue=].
1. [=list/For each=] |prefetchRecord| in |activeDocument|'s [=Document/prefetch records=]:
1. If |prefetchRecord|'s [=prefetch record/prerendering traversable=] is not null, then [=iteration/continue=].
1. [=prefetch record/Cancel and discard=] |prefetchRecord| given |activeDocument|.
</div>
<h2 id="prefetch-algorithms">Prefetch algorithms</h2>
The <dfn>list of sufficiently strict speculative navigation referrer policies</dfn> is a list containing the following: "", "`strict-origin-when-cross-origin`", "`strict-origin`", "`same-origin`", "`no-referrer`".
<div algorithm>
To <dfn export>start a referrer-initiated navigational prefetch</dfn> given a {{Document}} |document| and a [=prefetch record=] |prefetchRecord|, perform the following steps.
1. [=Assert=]: |document|'s [=node navigable=] is a [=top-level traversable=].
<p class="note" id="note-prefetch-top-level">Supporting prefetches in [=child navigables=] has some complexities and is not currently defined. It might be possible to define in the future.</p>
1. If |document| [=has a matching prefetch record=] given |prefetchRecord|, then return.
1. Let |sourceSnapshotParams| be the result of [=snapshotting source snapshot params=] given |document|.
1. Let |targetSnapshotParams| be the result of [=snapshotting target snapshot params=] given |document|'s [=node navigable=].
1. Set |prefetchRecord|'s [=prefetch record/source partition key=] to the result of [=determining the network partition key=] given |document|'s [=relevant settings object=].
1. [=Assert=]: |prefetchRecord|'s [=prefetch record/URL=]'s [=url/scheme=] is an [=HTTP(S) scheme=].
1. [=list/Append=] |prefetchRecord| to |document|'s [=Document/prefetch records=].
1. Set |prefetchRecord|'s [=prefetch record/start time=] to the [=current high resolution time=] for the [=relevant global object=] of |document|.
1. [=Trigger a prefetch status updated event=] given |document|'s [=node navigable=], |prefetchRecord|, and "{^speculation.PreloadingStatus/pending^}" status.
1. Let |referrerPolicy| be |prefetchRecord|'s [=prefetch record/referrer policy=] if |prefetchRecord|'s [=prefetch record/referrer policy=] is not the empty string, and |document|'s [=Document/policy container=]'s [=policy container/referrer policy=] otherwise.
1. Let |documentState| be a new [=document state=] with
: [=document state/request referrer policy=]
:: |referrerPolicy|
: [=document state/initiator origin=]
:: |document|'s [=Document/origin=]
1. Let |entry| be a new [=session history entry=] with
: [=session history entry/URL=]
:: |prefetchRecord|'s [=prefetch record/URL=]
: [=session history entry/document state=]
:: |documentState|
1. Let |request| be the result of [=creating a navigation request=] given |entry|, |document|'s [=relevant settings object=], |document|'s [=node navigable=]'s [=navigable/container=], and false.
1. Let |global| be |document|'s [=relevant global object=].
1. [=In parallel=]:
1. Let |navigationParams| be the result of [=creating navigation params by fetching=] given |request|, |entry|, |document|'s [=node navigable=], |sourceSnapshotParams|, |targetSnapshotParams|, "`other`", null (navigationId), "`navigate`", and <a href="#create-navigation-params-by-fetching-prefetchRecord"><i>prefetchRecord</i></a> |prefetchRecord|.
<div class="note" id="note-create-navigation-params-navigable">
The [=navigable=] used here at prefetch time, i.e., |document|'s [=node navigable=], could potentially be different from the one used at activation time, i.e., the one passed to the [=navigate=] algorithm which eventually calls [=create navigation params from a prefetch record=].
This is, actually, fine. During the [=navigate|navigation=] that results in prefetch activation, the target navigable will still be used for most of the important checks, which are performed earlier in the process before <a spec=HTML>attempt to populate the history entry's document</a> is called. And, looking at all the ways in which the navigable impacts the prefetch-time [=create navigation params by fetching=]:
* Setting the prefetch request's [=request/reserved client=] from |document|'s [=node navigable=] is expected. We have to pick one at prefetch time, and this is the only sensible choice.
(Also, the only potentially-problematic field of the reserved client is its [=environment/target browsing context=]. Since prefetches <a href="#note-prefetch-top-level">prefetches are only supported in top-level traversables</a> and the client's [=environment/execution ready flag=] is left unset, the impact is limited.)
* Checks related to [:Cross-Origin-Resource-Policy:] are not applicable, since <a href="#note-prefetch-top-level">prefetches are only supported in top-level traversables</a>.
* Checks related to [:Cross-Origin-Opener-Policy:] are skipped during prefetch time and instead <a href="#step-activation-time-coop-check">performed during activation</a>.
Also note that within this algorithm, |navigationParams| is basically just used as a wrapper for the prefetch response, and not stored anywhere for longer-term use. Thus, the value of |navigationParams|'s [=navigation params/navigable=] does not leak to the rest of the system. In particular, during activation a new [=navigation params=] is created by the [=create navigation params from a prefetch record=] algorithm, which contains the correct target navigable.
</div>
1. If |navigationParams|'s [=navigation params/response=] does not [=support prefetch=], then set |navigationParams| to null.
1. If |prefetchRecord|'s [=prefetch record/had conflicting credentials=] is true, then set |navigationParams| to null.
<div class="note">This means that if any cross-partition origin along the redirect chain had credentials, the prefetch is discarded. This reduces the chance of the user observing a logged-out page when they are logged in.</div>
1. [=Queue a global task=] on the [=networking task source=], given |global|, to:
1. If |navigationParams| is not a [=navigation params=], then [=prefetch record/cancel and discard=] |prefetchRecord| given |document| and abort these steps.
1. [=Assert=]: |navigationParams|'s [=navigation params/response=] is the [=exchange record/response=] of |prefetchRecord|'s [=prefetch record/redirect chain=]'s last element.
1. [=prefetch record/Complete=] |prefetchRecord| given |document|.
</div>
<div algorithm>
A [=response=] |response| <dfn>supports prefetch</dfn> if the following steps return true:
1. Let |status| be |response|'s [=response/status=].
1. [=Assert=]: |status| is not a [=redirect status=].
1. If |status| is not an [=ok status=], then return false.
<div class="note">In particular, this means that error responses aren't stored and will be retried when a navigation occurs. This increases the likelihood of navigations succeeding if the error was transient or due to the request being for prefetch. It also gives server software a simple way to refuse to handle requests which carry a [:Sec-Purpose:] request header indicating prefetch.</div>
1. Return true.
<div class="note">
<p>Although 103 is not an [=ok status=], early hints do not prevent prefetching. The [=supports prefetch=] predicate is only used on the actual [=response=], which never has a 1xx status.
<p>That said, as <a href="#no-early-hints">noted elsewhere</a>, early hints are currently ignored during prefetching.</p>
</div>
</div>
<h2 id="cookies">Cookies</h2>
[[COOKIES]] defines "cookies" which can be set using the [:Set-Cookie:] response header field. Because the "`uncredentialed`" partitioning scheme forces a separate [=network partition key=] to be used, it's necessary to copy these cookies into the ordinary partition as though they had been [=receive a cookie|received=] at the time of navigation.
<div class="issue">Cache, though not necessary for correctness, would be useful to copy, too.</div>
<div class="issue">Authentication entries can be acquired while cross-site, though this seems likely to be rare. (For instance, a site might redirect to a URL which contains URL credentials.) It might be reasonable to copy these, too, though it's unclear whether we actually want to encourage this.</div>
<div algorithm>
To <dfn>copy prefetch cookies</dfn> given a [=network partition key=] |isolatedPartitionKey| and an [=environment=] |environment|, perform the following steps.
<div class="note">
Though formally there is only one cookie store in [[COOKIES]], some browsers partition cookie stores so as to separate, for example, cookies with the same domain when loaded with different top-level sites. See <a href="https://github.com/privacycg/storage-partitioning">Client-Side Storage Partitioning (Privacy CG)</a>.
</div>
1. Let |isolatedCookieStore| be the cookie store associated with |isolatedPartitionKey|.
1. For each <a spec="COOKIES">cookie</a> |cookie| in |isolatedCookieStore|.
1. Remove |cookie| from |isolatedCookieStore|.
1. A user agent may ignore a cookie in its entirety. If so, continue.
<div class="note">This is consistent with [[COOKIES]] expressly permitting this when [=receiving a cookie=].</div>
1. Let |topLevelSite| be null.
1. If |environment|'s [=environment/target browsing context=] is a [=top-level browsing context=]:
1. Set |topLevelSite| to the result of [=obtaining a site=] given [=tuple origin=] ("`https`", |cookie|'s domain, null, null).
<div class="note">The use of the "`https`" scheme and null port here is arbitrary because cookies are visible across schemes and ports, in contrast to the usual same origin policy. The user agent's choice of associated cookie store, therefore, cannot be sensitive to either.</div>
<div class="note">When performing a prefetch in a [=top-level browsing context=], the request (including all redirects) is preparing for a top-level navigation. |environment|'s top-level site changes as redirects are followed, and since a redirect might be cross-site, |environment|'s top-level site might have changed since a given cookie was received. However, since the navigation is top-level, the origin delivering the cookie would have been the top-level site at the time. Since the cookie's domain has to be same-site with an origin delivering it, the cookie's domain can be used to determine the correct top-level site.</div>
1. Otherwise:
1. Let |topLevelOrigin| be |environment|'s [=environment/top-level origin=].
1. If |topLevelOrigin| is null, then set |topLevelOrigin| to |environment|'s [=environment/top-level creation URL=]'s [=url/origin=].
1. [=Assert=]: |topLevelOrigin| is an [=origin=].
1. Set |topLevelSite| be the result of [=obtaining a site=] given |topLevelOrigin|.
<div class="note">When performing a prefetch in a [=child navigable=], the top-level site is determined by the [=top-level traversable=] that contains it. Since that doesn't change as redirects are followed, |environment| can be used to establish the top-level site.</div>
1. Let |secondKey| be null or an [=implementation-defined=] value.
<div class="note">|secondKey| is expected to match the value it would have had if this response had been processed as part of an ordinary navigation in |environment|'s [=environment/target browsing context=].</div>
1. Let |destinationPartitionKey| be (|topLevelSite|, |secondKey|).
1. Let |cookieStore| be the cookie store associated with |destinationPartitionKey|.
1. Let |newCookie| be a copy of |cookie|.
1. Set |newCookie|'s creation-time and last-access-time to the current date and time.
1. If |cookieStore| contains a cookie |existingCookie| with the same name, domain and path as the newly created cookie:
1. Set the creation-time of |newCookie| to |existingCookie|'s creation-time.
1. Remove |existingCookie| from |cookieStore|.
1. Insert |newCookie| into |cookieStore|.
<div class="note">This remove-and-insert pattern is consistent with what happens when [=receiving a cookie=].</div>
</div>
<h2 id="sec-purpose-header">The [:Sec-Purpose:] HTTP request header</h2>
<em>This section overrides the definition of the [:Sec-Purpose:] HTTP request header from [[FETCH]].</em>
The [:Sec-Purpose:] HTTP request header specifies that the request serves one or more purposes other than requesting the resource for immediate use by the user.
The header field is an [[RFC9651]] Structured Header whose value must be a [=structured header/List=].
It may contain an [=structured header/Item=] member which is the [=structured header/Token=] `prefetch`. If so, this indicates the request's purpose is to download a resource it is anticipated will be fetched shortly.
The following parameters are defined for the `prefetch` token:
* A parameter whose key is "<dfn for="Sec-Purpose prefetch">`anonymous-client-ip`</dfn>".
If present with a value other than boolean false (`` `?0` `` in the field value), this parameter indicates that the prefetch request is being made using an anonymous client IP. Consequently, servers should not rely on it matching, or sharing a geographic location or network operator with, the client's IP address from which a non-prefetch request would have been made.
If a suitable response is not possible, for example because the resource depends on the client's geographic location, there is no other means of determining the location (e.g., the <a href="https://www.ietf.org/archive/id/draft-geohash-hint-00.html">Geohash client hint</a>), and no location-agnostic response is available, then the server should respond with an appropriate HTTP status code and response headers which mark the response as not suitable for caching.
<div class="note">
A future specification might define assign more specific meaning to non-boolean values. For now, they are treated the same as true. Implementations are advised not to emit such values.
This specification conforms to this advice; the [=start a referrer-initiated navigational prefetch=] algorithm does not emit non-boolean values.
</div>
* A parameter whose key is "<dfn for="Sec-Purpose prefetch">`prerender`</dfn>".
If present with a value other than boolean false (`` `?0` `` in the field value), this parameter indicates that the prefetch request is being made in anticipation of a prerender. Note that this value is also set for resources made from within a [=prerendering navigable=], before activation.
<div class="note">
A future specification might define assign more specific meaning to non-boolean values. For now, they are treated the same as true. Implementations are advised not to emit such values.
This specification conforms to this advice; the [=start a referrer-initiated navigational prefetch=] algorithm does not emit non-boolean values.
</div>
<h2 id="automated-testing">Automated testing</h2>
For the purposes of user-agent automation and application testing, this document defines extensions to the [[WebDriver-BiDi]] specification.
<div class="issue">CDDL snippets use the "text" type instead of "browsingContext.BrowsingContext" to allow indepedent programmatic processing of CDDL snippets. Currently, other modules cannot be referenced.</div>