From 39072b4470369ce0db10bca5f7904d96d42ca631 Mon Sep 17 00:00:00 2001 From: James Graham Date: Fri, 4 Jun 2021 15:15:29 +0100 Subject: [PATCH] Add a browsingContext.traverseHistory command This traverses the history by a specified `delta` (e.g. +1 for forward, or -1 for back). Like other navigation commands the `wait` parameter controls whether to return as soon as the history traversal starts, or wait for a specified level of completeness. Mush like other navigation commands, history traversal is tracked with a unique navigation id. Typically history traversal results in a navigation, but in the case of the history entry being in the bfcache, no navigation is necessary. In this case any non-"none" value for wait will return once the `pageshow` event is fired, and the `persisted` attribute of the return value is set to `true`. This model differs somewhat from the CDP model where one navigates to an explicit history entry and failure is only possible if the entry id is invalid. But back/forwward only seems closer to the use cases we have, and allowing events to be traced to a specific traversal seems consistent with the way we handle other navigation-related events. --- index.bs | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 159 insertions(+), 6 deletions(-) diff --git a/index.bs b/index.bs index 0017663f1..23040c24b 100644 --- a/index.bs +++ b/index.bs @@ -107,6 +107,7 @@ spec: HTML; urlPrefix: https://html.spec.whatwg.org/ text: session history; url: session-history text: set up a window environment settings object; url: set-up-a-window-environment-settings-object text: set up a worker environment settings object; url: set-up-a-worker-environment-settings-object + text: traverse the history by a delta; url: traverse-the-history-by-a-delta text: worker event loop; url: worker-event-loop-2 text: worklet global scopes; url: concept-document-worklet-global-scopes @@ -1700,7 +1701,8 @@ navigation status struct, which has the following items: BrowsingContextCommand = ( BrowsingContextGetTreeCommand // BrowsingContextNavigateCommand // - BrowsingContextReloadCommand + BrowsingContextReloadCommand // + BrowsingContextTraverseHistoryCommand ) @@ -1710,7 +1712,8 @@ BrowsingContextCommand = ( BrowsingContextResult = ( BrowsingContextGetTreeResult // - BrowsingContextNavigateResult + BrowsingContextNavigateResult // + BrowsingContextTraverseHistoryResult ) BrowsingContextEvent = ( @@ -2112,20 +2115,161 @@ The [=remote end steps=] with |command parameters| are: 1. Let |ignore cache| be the the value of the ignoreCache field of |command parameters| if present, or false otherwise. -1. Let |wait condition| be the value of the wait field of |command - parameters| if present, or "none" otherwise. - 1. Let |document| be |context|'s [=active document=]. 1. Let |URL| be |document|'s URL. -1. Let |request| be a new [=/request=] whose URL is |URl|. +1. Let |request| be a new [=/request=] whose URL is |URL|. 1. Return the result of [=await a navigation=] with |context|, |request|, history handling "reload", and ignore cache |ignore cache|. +#### The browsingContext.traverseHistory Command #### {#command-browsingContext-traverseHistory} + +The browsingContext.traverseHistory command +traverses the history of a given context by a delta. + +
+
Command Type
+
+
+      BrowsingContextTraverseHistoryCommand = {
+        method: "browsingContext.traverseHistory",
+        params: BrowsingContextTraverseHistoryParameters
+      }
+
+      BrowsingContextTraverseHistoryParameters = {
+        context: BrowsingContext,
+        delta: int,
+        ?wait: ReadinessState,
+      }
+      
+
+
Return Type
+
+
+        BrowsingContextTraverseHistoryResult = {
+            navigation: Navigation / null,
+            ?persisted: bool
+            url: text,
+        }
+    
+
+
+ +
+The [=remote end steps=] with |command parameters| are: + +1. Let |context id| be the value of the context field of + |command parameters|. + +1. Let |context| be the result of [=trying=] to [=get a browsing context=] + with |context id|. + +1. Assert: |context| is not null. + +1. Let |delta| be the value of the delta field of |command + parameters|. + +1. Let |wait condition| be the value of the wait field of |command + parameters| if present, or "none" otherwise. + +1. Let |navigation id| be the string representation of a + [[!RFC4122|UUID]] based on truly random, or pseudo-random numbers. + +1. [=Traverse the history by a delta=] given |delta|, |context|, and + |navigation id|. + +1. Let (|event received|, |navigate status|) be [=await=] given + «"navigation started", "navigation failed", + "fragment navigated", "pop state"», and + |navigation id|. + +1. Assert: |navigate status|'s id is |navigation id|. + +1. If |navigate status|'s status is "complete": + + 1. Let |body| be a [=map=] matching the + BrowsingContextTraverseHistoryResult production, with the + navigation field set to |navigation id|, and the + url field set to the result of the [=URL serializer=] given + |navigate status|'s url. + + 1. Return [=success=] with data |body|. + +1. If |navigate status|'s status is "canceled" return [=error=] + with [=error code=] [=unknown error=]. + +1. Assert: |navigate status|'s status is "pending" and + |navigation id| is not null. + +1. If |wait condition| is "none": + + 1. Let |body| be a [=map=] matching the + BrowsingContextTraverseHistoryResult production, with the + navigation field set to |navigation id|, and the + url field set to the result of the [=URL serializer=] given + |navigate status|'s url. + +1. If |wait condition| is "interactive", let |event name| be + "domContentLoaded", otherwise let |event name| be + "load". + +1. Let (|event received|, |status|) be [=await=] given «|event name|, + "download started", "navigation aborted", + "navigation failed", "page show"», and |navigation + id|. + +1. If |event received| is "navigation failed" + return [=error=] with [=error code=] [=unknown error=]. + +1. If event received is "page show", let |persisted| be true, + otherwise let |persisted| be false. + +1. Let |body| be a [=map=] matching the + BrowsingContextTraverseHistoryResult production, with the + navigation field set to |status|'s id, the + persisted field set to |persisted|, and the url + field set to the result of the [=URL serializer=] given |status|'s url. + +1. Return [=success=] with data |body|. + +
+ +
+ +The WebDriver-BiDi page show steps given context and |navigation status| are: + +Issue: Do we want to expose a `browsingContext.pageShow event? In that case we'd +need to call this whenever `pageshow` is going to be emitted, not just on +bfcache restore, and also add the persisted status to the data. + + 1. If the [=current session=] is null, return. + + 1. Let |navigation id| be |navigation status|'s id. + + 1. [=Resume=] with "page show", |navigation id|, and + |navigation status|. + +
+ +
+ +The WebDriver-BiDi pop state steps given context and |navigation status| are: + + 1. If the [=current session=] is null, return. + + 1. Let |navigation id| be |navigation status|'s id. + + 1. [=Resume=] with "pop state", |navigation id|, and + |navigation status|. + +
+ ### Events ### {#module-contexts-events} @@ -2493,6 +2637,15 @@ failed steps given |context| and |navigation status|: 1. Let |navigation id| be |navigation status|'s id. + 1. If |navigation id| is null, then return. + + Issue: The idea here is that we only get events if the navigation was + initiated by us and got far enough to have an id. In particular we wouldn't + get events for e.g. a page doing history.back() on a page where + that would fail. But maybe we want the actual invariant to be that we only + get a navigationFailed event if we already got a + navigationStarted event. + 1. Let |related browsing contexts| be a set containing |context|. 1. [=Resume=] with "navigation failed", |navigation id|, and |navigation status|.