You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+6-4Lines changed: 6 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -26,7 +26,7 @@ An application or framework's centralized router can use the `navigate` event to
26
26
27
27
```js
28
28
navigation.addEventListener("navigate", e=> {
29
-
if (!e.canTransition||e.hashChange) {
29
+
if (!e.canTransition||e.hashChange||e.downloadRequest!==null) {
30
30
return;
31
31
}
32
32
@@ -323,6 +323,8 @@ The event object has several useful properties:
323
323
324
324
- `formData`: a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object containing form submission data, or `null` if the navigation is not a form submission.
325
325
326
+
- `downloadRequest`: a string or null, indicating whether this navigation was initiated by a `<a href="..." download>` link. If it was, then this will contain the value of the attribute (which could be the empty string).
327
+
326
328
- `info`: any value passed by `navigation.navigate(url, { state, info })`, `navigation.back({ info })`, or similar, if the navigation was initiated by one of those methods and the `info` option was supplied. Otherwise, undefined. See [the example below](#example-using-info) for more.
327
329
328
330
- `signal`: an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) which can be monitored for when the navigation gets aborted.
@@ -358,8 +360,8 @@ navigation.addEventListener("navigate", e => {
358
360
return;
359
361
}
360
362
361
-
// Don't intercept fragment navigations.
362
-
if (e.hashChange) {
363
+
// Don't intercept fragment navigations or downloads.
364
+
if (e.hashChange||e.downloadRequest!==null) {
363
365
return;
364
366
}
365
367
@@ -405,7 +407,7 @@ Sometimes it's desirable to handle back/forward navigations specially, e.g. reus
405
407
```js
406
408
navigation.addEventListener("navigate", e=> {
407
409
// As before.
408
-
if (!e.canTransition||e.hashChange) {
410
+
if (!e.canTransition||e.hashChange||e.downloadRequest!==null) {
<p>(Notably, this will be null even for "{{NavigationNavigationType/reload}}" and "{{NavigationNavigationType/traverse}}" navigations that are revisiting a session history entry that was originally created from a form submission.)
<p>Represents whether or not this navigation was requested to be a download, by using an <{a}> or <{area}> element's <{a/download}> attribute:
1108
+
1109
+
* If a download was not requested, then this property is null.
1110
+
* If a download was requested, returns the filename that was supplied, via `<a download="filename" href="...">`. (This could be the empty string, as in the case of `<a download href="...">`.)
1111
+
1112
+
<p>Note that a download being requested does not always mean that a download will happen: for example, the download might be blocked by browser security policies, or end up being treated as a push navigation for <a href="https://github.com/whatwg/html/issues/7718" class="XXX">unspecified reasons</a>.
1113
+
1114
+
<p>Similarly, a navigation might end up being a download even if it was not requested to be one, due to the destination server responding with a `Content-Disposition: attachment` header.
1115
+
1116
+
<p>Finally, note that the {{Navigation/navigate}} event will not fire at all for downloads initiated using browser UI affordances, e.g., those created by right-clicking and choosing to save the target of the link.
<p>An arbitrary JavaScript value passed via {{Window/navigation}} APIs that initiated this navigation, or null if the navigation was initiated by the user or via a non-{{Window/navigation}} API.
The <dfn attribute for="NavigateEvent">navigationType</dfn>, <dfn attribute for="NavigateEvent">destination</dfn>, <dfn attribute for="NavigateEvent">canTransition</dfn>, <dfn attribute for="NavigateEvent">userInitiated</dfn>, <dfn attribute for="NavigateEvent">hashChange</dfn>, <dfn attribute for="NavigateEvent">signal</dfn>, <dfn attribute for="NavigateEvent">formData</dfn>, and <dfn attribute for="NavigateEvent">info</dfn> getter steps are to return the value that the corresponding attribute was initialized to.
1134
+
The <dfn attribute for="NavigateEvent">navigationType</dfn>, <dfn attribute for="NavigateEvent">destination</dfn>, <dfn attribute for="NavigateEvent">canTransition</dfn>, <dfn attribute for="NavigateEvent">userInitiated</dfn>, <dfn attribute for="NavigateEvent">hashChange</dfn>, <dfn attribute for="NavigateEvent">signal</dfn>, <dfn attribute for="NavigateEvent">formData</dfn>, <dfn attribute for="NavigateEvent">downloadRequest</dfn>, and <dfn attribute for="NavigateEvent">info</dfn> getter steps are to return the value that the corresponding attribute was initialized to.
1111
1135
1112
1136
A {{NavigateEvent}} has the following associated values which are only conditionally used:
1113
1137
@@ -1216,6 +1240,7 @@ The <dfn attribute for="NavigationDestination">sameDocument</dfn> getter steps a
1216
1240
To <dfn>fire a traversal `navigate` event</dfn> at a {{Navigation}} |navigation| given a [=session history entry=]<dfn for="fire a traversal navigate event">|destinationEntry|</dfn>, and an optional [=user navigation involvement=]<dfn for="fire a traversal navigate event">|userInvolvement|</dfn> (default "<code>[=user navigation involvement/none=]</code>"):
1217
1241
1218
1242
1. Let |event| be the result of [=creating an event=] given {{NavigateEvent}}, in |navigation|'s [=relevant Realm=].
1243
+
1. Set |event|'s [=NavigateEvent/classic history API serialized data=] to null.
1219
1244
1. Let |destination| be a [=new=]{{NavigationDestination}} created in |navigation|'s [=relevant Realm=].
1220
1245
1. Set |destination|'s [=NavigationDestination/URL=] to |destinationEntry|'s [=session history entry/URL=].
1221
1246
1. If |destinationEntry|'s [=session history entry/origin=] is [=same origin=] with |navigation|'s [=relevant settings object=]'s [=environment settings object/origin=], then:
@@ -1229,7 +1254,7 @@ The <dfn attribute for="NavigationDestination">sameDocument</dfn> getter steps a
1229
1254
1. Set |destination|'s [=NavigationDestination/index=] to −1.
1230
1255
1. Set |destination|'s [=NavigationDestination/state=] to null.
1231
1256
1. Set |destination|'s [=NavigationDestination/is same document=] to true if |destinationEntry|'s [=session history entry/document=] is equal to |navigation|'s [=relevant global object=]'s [=associated Document=]; otherwise false.
1232
-
1. Let |result| be the result of performing the [=inner navigate event firing algorithm=] given |navigation|, "{{NavigationNavigationType/traverse}}", |event|, |destination|, |userInvolvement|, and null.
1257
+
1. Let |result| be the result of performing the [=inner navigate event firing algorithm=] given |navigation|, "{{NavigationNavigationType/traverse}}", |event|, |destination|, |userInvolvement|, null, and null.
1233
1258
1. [=Assert=]: |result| is true (traversals are never cancelable).
1234
1259
</div>
1235
1260
@@ -1245,11 +1270,26 @@ The <dfn attribute for="NavigationDestination">sameDocument</dfn> getter steps a
1245
1270
1. Set |destination|'s [=NavigationDestination/index=] to −1.
1246
1271
1. Set |destination|'s [=NavigationDestination/state=] to |state|.
1247
1272
1. Set |destination|'s [=NavigationDestination/is same document=] to |isSameDocument|.
1248
-
1. Return the result of performing the [=inner navigate event firing algorithm=] given |navigation|, |navigationType|, |event|, |destination|, |userInvolvement|, and |formDataEntryList|.
1273
+
1. Return the result of performing the [=inner navigate event firing algorithm=] given |navigation|, |navigationType|, |event|, |destination|, |userInvolvement|, |formDataEntryList|, and null.
1274
+
</div>
1275
+
1276
+
<div algorithm="fire a download-requested navigate event">
1277
+
To <dfn>fire a download-requested `navigate` event</dfn> at a {{Navigation}} |navigation| given a [=URL=]<dfn for="fire a download-requested navigate event">|destinationURL|</dfn>, a [=user navigation involvement=]<dfn for="fire a download-requested navigate event">|userInvolvement|</dfn>, and a string <dfn for="fire a download-requested navigate event">|filename|</dfn>:
1278
+
1279
+
1. Let |event| be the result of [=creating an event=] given {{NavigateEvent}}, in |navigation|'s [=relevant Realm=].
1280
+
1. Set |event|'s [=NavigateEvent/classic history API serialized data=] to null.
1281
+
1. Let |destination| be a [=new=]{{NavigationDestination}} created in |navigation|'s [=relevant Realm=].
1282
+
1. Set |destination|'s [=NavigationDestination/URL=] to |destinationURL|.
1283
+
1. Set |destination|'s [=NavigationDestination/key=] to null.
1284
+
1. Set |destination|'s [=NavigationDestination/id=] to null.
1285
+
1. Set |destination|'s [=NavigationDestination/index=] to −1.
1286
+
1. Set |destination|'s [=NavigationDestination/state=] to null.
1287
+
1. Set |destination|'s [=NavigationDestination/is same document=] to false.
1288
+
1. Return the result of performing the [=inner navigate event firing algorithm=] given |navigation|, "{{NavigationNavigationType/push}}", |event|, |destination|, |userInvolvement|, null, and |filename|.
1249
1289
</div>
1250
1290
1251
1291
<div algorithm>
1252
-
The <dfn>inner `navigate` event firing algorithm</dfn> is the following steps, given a {{Navigation}} |navigation|, a {{NavigationNavigationType}} |navigationType|, a {{NavigateEvent}} |event|, a {{NavigationDestination}} |destination|, a [=user navigation involvement=] |userInvolvement|, and an [=entry list=] or null |formDataEntryList|:
1292
+
The <dfn>inner `navigate` event firing algorithm</dfn> is the following steps, given a {{Navigation}} |navigation|, a {{NavigationNavigationType}} |navigationType|, a {{NavigateEvent}} |event|, a {{NavigationDestination}} |destination|, a [=user navigation involvement=] |userInvolvement|, an [=entry list=] or null |formDataEntryList|, and a string or null |downloadRequestFilename|:
1253
1293
1254
1294
1. [=Navigation/Promote the upcoming navigation to ongoing=] given |navigation| and |destination|'s [=NavigationDestination/key=].
1255
1295
1. Let |ongoingNavigation| be |navigation|'s [=Navigation/ongoing navigation=].
@@ -1266,6 +1306,7 @@ The <dfn attribute for="NavigationDestination">sameDocument</dfn> getter steps a
1266
1306
1. Initialize |event|'s {{Event/type}} to "{{Navigation/navigate}}".
1267
1307
1. Initialize |event|'s {{NavigateEvent/navigationType}} to |navigationType|.
1268
1308
1. Initialize |event|'s {{NavigateEvent/destination}} to |destination|.
1309
+
1. Initialize |event|'s {{NavigateEvent/downloadRequest}} to |downloadRequestFilename|.
1269
1310
1. If |ongoingNavigation| is not null, then initialize |event|'s {{NavigateEvent/info}} to |ongoingNavigation|'s [=navigation API method navigation/info=]. Otherwise, initialize it to undefined.
1270
1311
<p class="note">At this point |ongoingNavigation|'s [=navigation API method navigation/info=] is no longer needed and can be nulled out instead of keeping it alive for the lifetime of the [=navigation API method navigation=].
1271
1312
1. Initialize |event|'s {{NavigateEvent/signal}} to a [=new=] {{AbortSignal}} created in |navigation|'s [=relevant Realm=].
@@ -1683,6 +1724,33 @@ Expand the section of the navigation/traversal response handling which deals wit
1683
1724
1. [=Fire a traversal navigate event=] at |previousDocument|'s [=relevant global object=]'s [=Window/navigation API=] with <i>[=fire a traversal navigate event/destinationEntry=]</i> set to |targetEntry| and <i>[=fire a traversal navigate event/userInvolvement=]</i> set to <var ignore>userInvolvement</var>.
1684
1725
</div>
1685
1726
1727
+
<h3 id="navigate-event-download-patches">Download a hyperlink updates</h3>
1728
+
1729
+
The current specification for <a spec="HTML" lt="download the hyperlink">downloading a hyperlink</a> has several known issues, most notably <a href="https://github.com/whatwg/html/issues/5548">whatwg/html#5548</a> which indicates that the specification should probably be merged into the general <a spec="HTML" lt="navigate">navigation</a> algorithm.
1730
+
1731
+
For the purposes of the navigation API, we need to fire the appropriate {{Navigation/navigate}} event, with {{NavigateEvent/downloadRequest}} set to the correct value. We could rigorously detail the ways to modify the current spec to accomplish this. But, given that the current spec will be rewritten anyway, this is probably not very useful. So until such a time as we can properly investigate and rewrite the <a spec="HTML" lt="download the hyperlink">downloading a hyperlink</a> algorithm, we describe here the expected behavior in a less-formal fashion. We believe this is still enough to get interoperability.
1732
+
1733
+
<div algorithm="download the hyperlink">
1734
+
<ul>
1735
+
<li><p>Ensure that the algorithm gets an appropriate [=user navigation involvement=] value, |userInvolvement|, passed to it. This is similar to the modifications for the <a spec="HTML">follow the hyperlink</a> algorithm described in [[#user-initiated-patches]]. One key difference is that, for the case where the user indicates a preference for downloading, |userInvolvement| must be "<code>[=user navigation involvement/browser UI=]</code>", even if it is triggered as part of [=EventTarget/activation behavior=].
1736
+
1737
+
<li><p>Separate out the sandboxing checks in <a spec="HTML">allowed to download</a> from the user-safeguarding checks. If the sandboxing checks fail, then the user agent must not fire a {{Navigation/navigate}} event. Whereas, the user-safeguarding checks generally happen later, probably [=in parallel=].
1738
+
1739
+
<li>
1740
+
<p>Before we reach the point at which it's time to actually go in parallel and fetch content from the server, and after the <a spec="HTML">cannot navigate</a> check, the synchronously-possible part of the <a spec="HTML">allowed to download</a> check, the URL parsing step, and the hyperlink suffix appending step, run the equivalent of the following:
1741
+
1742
+
1. If |userInvolvement| is not "<code>[=user navigation involvement/browser UI=]</code>", then:
1743
+
1. Let |navigation| be |subject|'s [=relevant global object=]'s [=Window/navigation API=].
1744
+
1. Let |filename| be the value of |subject|'s <{a/download}> attribute.
1745
+
1. Let |continue| be the result of [=firing a download-requested navigate event=] at |navigation| with <i>[=fire a download-requested navigate event/destinationURL=]</i> set to |URL|, <i>[=fire a download-requested navigate event/userInvolvement=]</i> set to |userInvolvement|, and <i>[=fire a download-requested navigate event/filename=]</i> set to |filename|.
1746
+
1. If |continue| is false, then return.
1747
+
1748
+
<p>Here the variables |subject| and |URL| refer to the same things they currently do in the <a spec="HTML">download the hyperlink</a> algorithm, i.e. the <{a}> or <{area}> element in question, and the parsed [=URL=].
1749
+
1750
+
<p>If we end up triggering the <a spec="HTML">navigate</a> algorithm from the <a spec="HTML">download the hyperlink</a> algorithm, then these steps won't be directly incorporated into the <a spec="HTML">download the hyperlink</a> algorithm. Instead, the modifications in [[#navigate-algorithm-patches]] will get a bit more complicated, so as to use [=fire a download-requested navigate event=] with the above arguments, instead of [=fire a non-traversal navigate event=], for downloads.
1751
+
</ul>
1752
+
</div>
1753
+
1686
1754
<h2 id="session-history-patches">Patches to session history</h2>
1687
1755
1688
1756
This section details monkeypatches to [[!HTML]] to track appropriate data for associating a {{Navigation}} with a [=session history entry=].
0 commit comments