diff --git a/spec.bs b/spec.bs index 115b4c6..51b5523 100644 --- a/spec.bs +++ b/spec.bs @@ -416,6 +416,8 @@ const p2 = appHistory.navigate(url2); we need to ensure that when navigating to `url2`, we still have the {{Promise}} `p1` around so that we can reject it. We can't just get rid of any ongoing navigation promises the moment the second call to {{AppHistory/navigate()}} happens. +We also need to ensure that, if we start a new navigation, navigations which have gotten as far as firing {{AppHistory/navigate}} events, but not yet as far as firing {{AppHistory/navigatesuccess}} or {{AppHistory/navigateerror}}, get [=finalized with an aborted navigation error=]. + We end up accomplishing all this using the following setup: Each {{AppHistory}} object has an associated ongoing navigate event, an {{AppHistoryNavigateEvent}} or null, initially null. @@ -434,6 +436,7 @@ An app history API navigation is a [=struct=] with the following [=st * An info, a JavaScript value * A returned promise, a {{Promise}} +* A fired `navigate` event, a boolean * A cleanup step, an algorithm step
We need to store the {{AbortSignal}} separately from the [=app history API navigation=] struct, since it needs to be tracked even for navigations that are not via the app history APIs. So, we store it some of the time in the [=AppHistory/ongoing navigate event=]'s {{AppHistoryNavigateEvent/signal}} property, and the rest of the time in the [=AppHistory/post-navigate event ongoing navigation signal=]. @@ -445,7 +448,7 @@ An app history API navigation is a [=struct=] with the following [=st 1. Let |cleanupStep| be an algorithm step which sets |appHistory|'s [=AppHistory/ongoing non-traverse navigation=] to null. - 1. Let |ongoingNavigation| be an [=app history API navigation=] whose [=app history API navigation/info=] is |info|, [=app history API navigation/returned promise=] is |promise|, and [=app history API navigation/cleanup step=] is |cleanupStep|. + 1. Let |ongoingNavigation| be an [=app history API navigation=] whose [=app history API navigation/info=] is |info|, [=app history API navigation/returned promise=] is |promise|, [=app history API navigation/fired navigate event=] is false, and [=app history API navigation/cleanup step=] is |cleanupStep|. 1. Assert: |appHistory|'s [=AppHistory/upcoming non-traverse navigation=] is null. @@ -471,7 +474,7 @@ An app history API navigation is a [=struct=] with the following [=st 1. Let |cleanupStep| be an algorithm step which [=map/removes=] |appHistory|'s [=AppHistory/ongoing traverse navigations=][|key|]. - 1. Let |ongoingNavigation| be an [=app history API navigation=] whose [=app history API navigation/info=] is |info|, [=app history API navigation/returned promise=] is |promise|, and [=app history API navigation/cleanup step=] is |cleanupStep|. + 1. Let |ongoingNavigation| be an [=app history API navigation=] whose [=app history API navigation/info=] is |info|, [=app history API navigation/returned promise=] is |promise|, [=app history API navigation/fired navigate event=] is false, and [=app history API navigation/cleanup step=] is |cleanupStep|. 1. Set |appHistory|'s [=AppHistory/ongoing traverse navigations=][|key|] to |ongoingNavigation|. @@ -569,16 +572,10 @@ An app history API navigation is a [=struct=] with the following [=st 1. Navigate |browsingContext| to |url| with [=navigate/historyHandling=] set to |historyHandling|, [=navigate/appHistoryState=] set to |serializedState|, and the source browsing context set to |browsingContext|. - 1. If |appHistory|'s [=AppHistory/upcoming non-traverse navigation=] is |ongoingNavigation|, then: + 1. If |appHistory|'s [=AppHistory/upcoming non-traverse navigation=] is |ongoingNavigation|, then [=finalize with an aborted navigation error=] given |appHistory| and |ongoingNavigation|.
This means the navigate algorithm bailed out before ever getting to the [=inner navigate event firing algorithm=] which would [=AppHistory/promote the upcoming non-traverse navigation to ongoing=]. - 1. [=Reject=] |ongoingNavigation|'s [=app history API navigation/returned promise=] with a [=new=] "{{AbortError}}" {{DOMException}}, created in |appHistory|'s [=relevant Realm=]. - - 1. Perform |ongoingNavigation|'s [=app history API navigation/cleanup step=]. - -
We don't [=finalize with an aborted navigation error=] since that algorithm only makes sense after the {{AppHistory/navigate}} event has fired.
-
1. If |appHistory|'s [=AppHistory/to-be-set serialized state=] is non-null, then set |browsingContext|'s [=session history=]'s [=session history/current entry=]'s [=session history entry/app history state=] to |appHistory|'s [=AppHistory/to-be-set serialized state=].
1. Set |appHistory|'s [=AppHistory/to-be-set serialized state=] to null.
@@ -997,9 +994,13 @@ The sameDocument getter steps a
1. Let |currentURL| be |appHistory|'s [=relevant global object=]'s [=associated document=]'s [=Document/URL=].
1. If |destination|'s [=AppHistoryDestination/URL=] is [=rewritable=] relative to |currentURL|, and either |destination|'s [=AppHistoryDestination/is same document=] is true or |navigationType| is not "{{AppHistoryNavigationType/traverse}}", then initialize |event|'s {{AppHistoryNavigateEvent/canRespond}} to true. Otherwise, initialize it to false.
1. If either |userInvolvement| is not "[=user navigation involvement/browser UI=]
" or |navigationType| is not "{{AppHistoryNavigationType/traverse}}", then initialize |event|'s {{Event/cancelable}} to true. Otherwise, initialize it to false.
- 1. If either |appHistory|'s [=relevant global object=]'s [=Window/browsing context=] is still on its initial `about:blank` `Document`, or both |event|'s {{AppHistoryNavigateEvent/canRespond}} and |event|'s {{Event/cancelable}} are false, then:
+ 1. If both |event|'s {{AppHistoryNavigateEvent/canRespond}} and |event|'s {{Event/cancelable}} are false, then return true.
+
In this case we are definitely performing a cross-document navigation or traversal. We don't clean up |ongoingNavigation| however, since we might end up [=finalizing with an aborted navigation error=] before the current {{Document}} unloads. + 1. If |appHistory|'s [=relevant global object=]'s [=Window/browsing context=] is still on its initial `about:blank` `Document`, then: 1. If |ongoingNavigation| is not null, then: - 1. [=Resolve=] |ongoingNavigation|'s [=app history API navigation/returned promise=]. + 1. If |destination|'s [=AppHistoryDestination/is same document=] is true, then: + 1. Assert: |navigationType| is not "{{AppHistoryNavigationType/traverse}}". + 1. [=Resolve=] |ongoingNavigation|'s [=app history API navigation/returned promise=]. 1. Perform |ongoingNavigation|'s [=app history API navigation/cleanup step=]. 1. Return true. 1. Initialize |event|'s {{Event/type}} to "{{AppHistory/navigate}}". @@ -1022,6 +1023,7 @@ The sameDocument getter steps a 1. Let |shouldContinue| be |dispatchResult|. 1. Set |appHistory|'s [=AppHistory/ongoing navigate event=] to null. 1. Set |appHistory|'s [=AppHistory/post-navigate event ongoing navigation signal=] to |event|'s {{AppHistoryNavigateEvent/signal}}. + 1. If |ongoingNavigation| is not null, then set |ongoingNavigation|'s [=app history API navigation/fired navigate event=] to true. 1. If |appHistory|'s [=relevant global object=]'s [=active Document=] is not [=Document/fully active=], then: 1. [=Finalize with an aborted navigation error=] given |appHistory| and |ongoingNavigation|. 1. Return false. @@ -1063,25 +1065,21 @@ The sameDocument getter steps a 1. Set |signal| to the value of |appHistory|'s [=AppHistory/ongoing navigate event=]'s {{AppHistoryNavigateEvent/signal}} property. 1. Set |appHistory|'s [=AppHistory/ongoing navigate event=]'s [=Event/canceled flag=] to true. 1. Set |appHistory|'s [=AppHistory/ongoing navigate event=] to null. - 1. Otherwise: - 1. Assert: |appHistory|'s [=AppHistory/post-navigate event ongoing navigation signal=] is not null. - 1. Set |signal| to |appHistory|'s [=AppHistory/post-navigate event ongoing navigation signal=]. + 1. Otherwise, set |signal| to |appHistory|'s [=AppHistory/post-navigate event ongoing navigation signal=]. 1. Set |appHistory|'s [=AppHistory/post-navigate event ongoing navigation signal=] to null. 1. Set |appHistory|'s [=AppHistory/to-be-set serialized state=] to null.
This ensures that any call to {{AppHistory/navigate()|appHistory.navigate()}} which triggered this algorithm does not overwrite the [=session history entry/app history state=] of the [=session history/current entry=] for aborted navigations. - 1. Let |promise| be null. - 1. If |ongoingNavigation| is non-null, then set |promise| to |ongoingNavigation|'s [=app history API navigation/returned promise=]. - 1. If |ongoingNavigation| is non-null, then perform |ongoingNavigation|'s [=app history API navigation/cleanup step=]. - 1. [=AbortSignal/Signal abort=] on |signal|. - 1. [=Queue a microtask=] on |appHistory|'s [=relevant agent=]'s [=agent/event loop=] to perform the following steps: + 1. If |signal| is not null, then [=AbortSignal/signal abort=] on |signal|. + 1. If |ongoingNavigation| is non-null, then: 1. If |error| was not given, then set |error| to a [=new=] "{{AbortError}}" {{DOMException}}, created in |appHistory|'s [=relevant Realm=]. - 1. [=Fire an event=] named {{AppHistory/navigateerror}} at |appHistory| using {{ErrorEvent}}, with {{ErrorEvent/error}} initialized to |error|, {{ErrorEvent/message}} initialized to the value of |error|'s {{DOMException/message}} property, {{ErrorEvent/filename}} initialized to the empty string, and {{ErrorEvent/lineno}} and {{ErrorEvent/colno}} initialized to 0. - 1. If |promise| is non-null, then [=reject=] it with |error|. + 1. If |ongoingNavigation|'s [=app history API navigation/fired navigate event=] is true, then [=fire an event=] named {{AppHistory/navigateerror}} at |appHistory| using {{ErrorEvent}}, with {{ErrorEvent/error}} initialized to |error|, {{ErrorEvent/message}} initialized to the value of |error|'s {{DOMException/message}} property, {{ErrorEvent/filename}} initialized to the empty string, and {{ErrorEvent/lineno}} and {{ErrorEvent/colno}} initialized to 0. + 1. [=Reject=] |ongoingNavigation|'s [=app history API navigation/returned promise=] with |error|. + 1. Perform |ongoingNavigation|'s [=app history API navigation/cleanup step=].
This includes navigation cancelation induced by the stop document loading algorithm, which is invoked by user interface elements such as a stop button and by {{Window/stop()|window.stop()}}. (However, note whatwg/html#6905, which notes that in reality sometimes {{Window/stop()|window.stop()}} does not cancel navigations.)
+* Wherever the spec ends up canceling queued-up traversals for a browsing context |bc|, we also [=inform app history about canceling traversals=] in |bc|. (Regardless of whether or not there are any traversals queued up.)