From 05cc693880a52140aec34fcbad98f7c889013608 Mon Sep 17 00:00:00 2001 From: David Bokan Date: Wed, 13 Dec 2023 15:22:54 -0500 Subject: [PATCH] [Spec] Overhaul 'restricting the text fragment' section (#239) * Use userInvolvement instead of sec-fetch-site sec-fetch-site was being checked for 'none' to indicate that a navigation was initiated from browser UI. However, we cannot inspect request headers from this part of the algorithm. Instead, the navigate algorithm now has a userInvolvement parameter which provides this information explicitly. Plumb that into navigation params and use it instead. Additionally, this change removes the top-level browsing context check from the document's text directive user activation flag since that's a confusing place to check it. Instead, move it to where this flag is being read and remove a (now-obviously) redundant check below. * Use initiatorOrigin instead of sec-fetch-site `sec-fetch-site: same-origin` was being checked to tell if a navigation was initiated by a different origin. However, request headers can't be inspected at this point of the algorithm. Plumb through the initiatorOrigin parameter when loading a document and compare that with navigation params's origin field, using the `is same site` steps. * Enable same-document navigations The main change in this commit is that enables same-document navigations (with restrictions) by moving the security checks to also happen from the navigate to fragment. As part of this, we do a fairly substantial clean up and refresh of the 'restricting a text fragment' section. The summarized high level changes: * Split out the restrictions into a single set of 'check if a text directive can be scrolled' steps, taking the necessary input as parameters. * Remove the 'allow text fragment scroll` flag on Document, instead computing this value and passing it through various steps into 'scroll to the fragment'. * The restriction is placed only on the 'scroll to the fragment' steps, meaning the text directive is still the 'indicated part' and can remain highlighted. Partially addresses #240 --- index.bs | 520 +++++++++++++++++++++++++++++++------------ index.html | 631 ++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 864 insertions(+), 287 deletions(-) diff --git a/index.bs b/index.bs index ae308ab..31c2af7 100644 --- a/index.bs +++ b/index.bs @@ -27,12 +27,21 @@ spec:dom; type:dfn; for:range; text:start spec:dom; type:dfn; text:parent element spec:dom; type:dfn; text:range spec:html; type:element; text:link -spec:html; type:dfn; for:/; text:origin spec:html; type:element; text:script spec:html; type:element; text:style spec:url; type:dfn; text:fragment +
+spec:html; type:dfn; for:browsing context; text:group; url: https://html.spec.whatwg.org/multipage/browsers.html#tlbc-group
+spec:html; type:dfn; for:/; text:navigable; url: https://html.spec.whatwg.org/multipage/document-sequences.html#navigable
+spec:html; type:dfn; for:/; text:origin; url: https://html.spec.whatwg.org/multipage/browsers.html#concept-origin
+spec:html; type:dfn; text:user navigation involvement; url: https://html.spec.whatwg.org/multipage/browsing-the-web.html#user-navigation-involvement
+spec:html; type:dfn; for:document state; text:initiator origin; url: https://html.spec.whatwg.org/multipage/browsing-the-web.html#document-state-initiator-origin
+spec:html; type:dfn; for:/; text:document state; url: https://html.spec.whatwg.org/multipage/browsing-the-web.html#she-document-state
+spec:html; type:dfn; for:she; text:document; url: https://html.spec.whatwg.org/multipage/browsing-the-web.html#she-document
+
+
   {
     "document-policy": {
@@ -593,7 +602,7 @@ The section above described how the [=fragment directive=] is separated from the
 session history entry.
 
 This section defines how and when navigations and traversals make use of history entry's [=she/directive
-state=] to apply the directives associated with a session history entry to a [=Document=].
+state=] to apply the directives associated with a session history entry to a [=/Document=].
 
 >   Monkeypatching [[DOM#interface-document]]:
 >
@@ -799,8 +808,7 @@ indicated part, enable a fragment to indicate a [=range=]. Make the followin
 >
 >   1. Let |directives| be the document's [=Document/uninvoked directives=].
 >       
->   1. If |directives| is non-null and |document|'s [=document/allow text
->       fragment scroll=] is true then:
+>   1. If |directives| is non-null then:
 >       1. Let |ranges| be a list that is the result of running
 >           the [=invoke text directives=] steps with |directives| and the document.
 >       1. If |ranges| is non-empty, then:
@@ -821,7 +829,7 @@ indicated part, enable a fragment to indicate a [=range=]. Make the followin
 >
 >   
 
-In scroll to the fragment, handle a indicated part that is a [=range=] and also
+In scroll to the fragment, handle an indicated part that is a [=range=] and also
 prevent fragment scrolling if the force-load-at-top policy is enabled. Make the following changes:
 
 >   Monkeypatching [[HTML#scrolling-to-a-fragment]]:
@@ -861,7 +869,7 @@ prevent fragment scrolling if the force-load-at-top policy is enabled. Make the
 >           
> Scrolling to a text directive centers it in the block flow direction. >
-> 10. Scroll target into view, with behavior set to "auto", block set to +> 10. Scroll |target| into view, with behavior set to "auto", block set to > "start", and inline set to "nearest". > >
  • [=scroll a target into view=], @@ -1053,11 +1061,28 @@ This specification does not specify exactly how a UA achieves this as there are multiple solutions with differing tradeoffs. For example, a UA may continue to walk the tree even after a match is found in [=find a range from a text directive=]. Alternatively, it may schedule an asynchronous task -to find and set the [=Document=]'s indicated part. +to find and set the [=/Document=]'s indicated part. ### Restricting the Text Fragment ### {#restricting-the-text-fragment} -Amend the definition of a [=/request=] and of a [=Document=] to include a new +
    + This section integrates with HTML navigation to restrict when an indicated text directive will + be allowed to scroll. In summary: + + * Add a boolean text directive user activation to both Document and Request. This + flag is set on a document when created from a user activated navigation and consumed if a text + directive is scrolled. If unconsumed, it can be transfered to an outgoing navigation request. + This implements the user-activation-through-redirects behavior described in the note below. + * Define a series of checks, performed on a document and the user involvement and initiator origin + state of a navigation, to determine whether a text directive should be allowed to perform a + scroll. + * Compute the scroll permission from "finalize a cross document navigation" and from "navigate to + a fragment steps" and plumb it through to the "scroll to the fragment" steps where its used to + abort a text directive scroll. + +
    + +Amend the definition of a [=/request=] and of a [=/Document=] to include a new boolean [=document/text directive user activation=] field: > Monkeypatching [[FETCH]]: @@ -1067,7 +1092,7 @@ boolean [=document/text directive user activation=] field: > Monkeypatching [[HTML]]: > -> Each [=Document=] has a text directive user activation, which is a boolean, +> Each [=/Document=] has a text directive user activation, which is a boolean, > initially false. > >
    @@ -1076,12 +1101,12 @@ boolean [=document/text directive user activation=] field: > navigation occurred as a result of a user activation and is propagated across client-side > redirects. > -> If a [=Document=]'s [=document/text directive user activation=] isn't used to activate a text +> If a [=/Document=]'s [=document/text directive user activation=] isn't used to activate a text > fragment, it is instead used to set a new navigation [=/request=]'s [=request/text directive user activation=] > to true. In this way, a [=document/text directive user activation=] can be propagated -> from one [=Document=] to another across a navigation. +> from one [=/Document=] to another across a navigation. > -> Both [=Document=]'s [=document/text directive user activation=] and [=/request=]'s +> Both [=/Document=]'s [=document/text directive user activation=] and [=/request=]'s > [=request/text directive user activation=] are always set to false when used, such that a > single user activation cannot be reused to activate more than one text fragment. >
    @@ -1115,143 +1140,360 @@ boolean [=document/text directive user activation=] field:

    +Amend the create navigation params by fetching steps to transfer the [=active +document=]'s [=document/text directive user activation=] value into request's +[=request/text directive user activation=]. + +> Monkeypatching [[HTML]]: +> +>
    +> 1. Assert: this is running in parallel. +> 2. Let documentResource be entry's document state's resource. +> 3. Let request be a new request, with +> +>
    +>
    url
    +>
    entry's URL
    +> +>
    ...
    +>
    ...
    +> +>
    referrer policy
    +>
    entry's document state's request referrer policy
    +> +>
    [=request/text directive user activation=]
    +>
    |navigable|'s [=navigable/active document=]'s [=document/text directive user activation=]
    +>
    +>
  • +> 4. Set |navigable|'s [=navigable/active document=]'s [=document/text directive +> user activation=] to false. +> 5. If documentResource is a POST resource, then: +> 1. ... +> +> + +Amend the definition of navigation params to include a new field: + > Monkeypatching [[HTML]]: > -> Each [=Document=] has an allow text fragment scroll, which is a -> boolean, initially false. ->
    ->

    -> [=document/allow text fragment scroll=] is used to determine whether a text fragment will -> perform scrolling when the document is loaded. If it is false, the text fragment can be -> visually indicated but will not be scrolled to. This implements the mitigations discussed in -> [[#scroll-on-navigation]]. ->

    ->

    -> The reason we compute and store allow text fragment scroll, rather than performing these -> checks at the time of use, is that it relies on the properties of the navigation while the -> invocation will occur as part of the scroll to the fragment steps which can -> happen outside the context of a navigation. ->

    ->
    +>
    +>
    user involvement
    +>
    A user navigation involvement value.
    +>
    +> -
    - TODO: This should really only prevent potentially observable side-effects like - automatic scrolling. Unobservable effects like a highlight could be safely - allowed in all cases. +Initialize the [=navigation params/user involvement=] value everywhere a navigation params is +created. Specifically: initialize it to true in the create navigation params by +fetching case: + +> Monkeypatching [[HTML]]: +> +>
    +> To create navigation params by fetching given 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 a user navigation +> involvement |user involvement|, perform the following steps. They return a navigation params, +> a non-fetch scheme navigation params, or null. +> +> 1. Assert: this is running in parallel. +> 2. ... +>
  • Let resultPolicyContainer be the result of determining navigation params policy container given +> response's URL, entry's document state's history policy container, sourceSnapshotParams's source +> policy container, null, and responsePolicyContainer.
  • +> 24. If navigable's container is an iframe, and response's timing allow passed flag is set, then +> set container's pending resource-timing start time to null. +> 25. Return a new navigation params, with +> +>
    +>
    id
    +>
    navigationId
    +>
    ...
    +>
    ...
    +>
    about base URL
    +>
    entry's document state's about base URL
    +>
    [=navigation params/user involvement=]
    +>
    |user involvement|
    +>
    +> +>
    + +Amend the + +create and initialize a Document object steps to compute and store the [=document/text directive +user activation=] flag: + +> Monkeypatching [[HTML]]: +> +>
    +> 18. Process link headers given document, navigationParams's response, and "pre-media". +> 19.
    Set |document|'s [=document/text directive user activation=] to true if any of the following +> conditions hold, false otherwise: +> * |navigationParams|'s [=user involvement=] is "activation"; +> * |navigationParams|'s [=user involvement=] is "browser UI"; or +> * |navigationParams|'s +> request's +> [=request/text directive user activation=] is true. +>
    +> It's important that [=document/text directive user activation=] not be copyable so that +> only one text fragment can be activated per user-activated navigation. +>
    +> 20. Return |document|. +> +>
    + +
    + To check if a text directive can be scrolled; given a [=/Document=] |document|, an + [=/origin=]-or-null |initiator origin|, and user navigation involvement-or-null + |user involvement|, follow these steps: + +
      + 1. If |document|'s [=Document/uninvoked directives=] field is null or empty, return false. + 1. Let |is user involved| be true if: |document|'s [=document/text directive user activation=] is + true, or |user involvement| is one of "activation" or "browser + UI"; false otherwise. + 1. Set |document|'s [=document/text directive user activation=] to false. + 1. If |user involvement| is "browser UI", return true. +
      +

      + If a navigation originates from browser UI, it's always ok to allow it since it'll be + user triggered and the page/script isn't providing the text snippet. +

      +

      + Note: The intent in this item is to distinguish cases where the app/page is able to + control the URL from those that are fully under the user's control. In the former we + want to prevent scrolling of the text fragment unless the destination is loaded in a + separate browsing context group (so that the source cannot both control the text snippet + and observe side-effects in the navigation). There are some cases where "browser UI" may + be a grey area in this regard. E.g. an "open in new window" context menu item when right + clicking on a link. +

      +

      + See + + sec-fetch-site in [[FETCH-METADATA]] for a related discussion of how this applies. +

      +
      + 1. If |is user involved| is false, return false. + 1. If |document|'s [=node navigable=] has a [=navigable/parent=], return false. + 1. If |initiator origin| is non-null and |document|'s [=Document/origin=] is [=same origin=] + with |initiator origin|, return true. + 1. If |document|'s [=Document/browsing context=]'s [=browsing context/group=]'s + browsing context set has length 1, return true. +
      + i.e. Only allow navigation from a cross-origin element/script if the + document is loaded in a noopener context. That is, a new top level + browsing context group to which the navigator does not have script access + and which can be placed into a separate process. +
      + 1. Otherwise, return false. +
    -Amend the create -and initialize a Document object steps by adding the following steps before returning |document|: +Amend (the already amended, in [[#invoking-text-directives]]) scroll to the +fragment steps to add a new parameter, a boolean |allow text directive scroll|: -> Monkeypatching [[HTML]]: +> Monkeypatching [[HTML#scrolling-to-a-fragment]]: > -> 15. Set |document|'s [=document/text directive user activation=] by following these sub-steps: -> 1. Let |is user activated| be true if the current navigation was initiated from -> a window that had a transient activation at the time the -> navigation was initiated, or the UA has reason to believe it comes from a -> direct user gesture (e.g. user typed into the address bar). ->
    -> TODO: it'd be better to refer to the [=request/user-activation=] flag. ->
    -> 1. If browsing context is a top-level browsing context and if either of |is -> user activated| or the [=request/text directive user activation=] of -> |navigationParam|'s -> request -> object is true, set the |document|'s [=document/text directive user activation=] -> to true. Otherwise, set it to false. ->
    -> It's important that the flag not be copyable so that only one text fragment can be -> activated per user-activated navigation. ->
    -> 16. Set |document|'s [=document/allow text fragment scroll=] by following these sub-steps: -> 1. If |document|'s [=Document/uninvoked directives=] field is null or empty, set -> [=document/allow text fragment scroll=] to false and abort these sub-steps. -> 1. Let |text directive user activation| be the value of |document|'s -> [=document/text directive user activation=] and set |document|'s -> [=document/text directive user activation=] to false. -> 1. If the |navigationParam|'s -> request -> has a sec-fetch-site -> header and its value is `"none"` set [=document/allow text fragment scroll=] to true and abort these sub-steps. ->
    ->

    -> If a navigation originates from browser UI, it's always ok to allow it -> since it'll be user triggered and the page/script isn't providing the -> text snippet. ->

    ->

    -> Note: Depending on the UA, there can be cases where the -> incumbentNavigationOrigin parameter is null but -> it's not clear that the navigation is to be considered as -> initiated from browser UI. E.g. an "open in new window" context -> menu item when right clicking on a link. The intent in this item -> is to distinguish cases where the app/page is able to set the URL -> from those that are fully under the user's control. In the former -> we want to prevent activation of the text fragment unless the -> destination is loaded in a separate browsing context group (so that -> the source cannot both control the text snippet and observe -> side-effects in the navigation). ->

    ->

    -> See -> sec-fetch-site -> in [[FETCH-METADATA]] for a more detailed discussion of how this applies. ->

    ->
    -> 1. If |text directive user activation| is false, set -> [=document/allow text fragment scroll=] to false and abort these sub-steps. -> 1. If the |navigationParam|'s -> request -> has a sec-fetch-site -> header and its value is `"same-origin"` set -> [=document/allow text fragment scroll=] to true and abort these -> sub-steps. -> 1. If |document|'s [=Document/browsing context=] is a [=top-level browsing -> context=] and its -> group's -> browsing context set has length 1, set -> [=document/allow text fragment scroll=] to true and abort these sub-steps. ->
    -> i.e. Only allow navigation from a cross-origin element/script if the -> document is loaded in a noopener context. That is, a new top level -> browsing context group to which the navigator does not have script access -> and which can be placed into a separate process. ->
    -> 1. Otherwise, set [=document/allow text fragment scroll=] to false. +>
    +> To scroll to the fragment given a Document |document| and boolean |allow text +> directive scroll|: +> +> 1. If document's indicated part is null, then set document's target element to null. +> 2. ... +> 3. Otherwise: +> 1. Assert: document's indicated part is an element or it is a [=range=]. +> 2. ... +>
  • If |target| is a [=range=], then:
  • +> 1. If |allow text directive scroll| is false, return. +> 1. Set |target| to be the [=first common ancestor=] of |target|'s [=range/start node=] +> and [=range/end node=]. +> 1. ... +> +>
    -Amend step 2 of the - -process a navigate fetch steps to additionally set request's -[=request/text directive user activation=] to the value of the [=active document=]'s -[=document/text directive user activation=] and set the [=active document=]'s value to -false. +Amend the try to scroll to the fragment by adding a boolean flag |allow text +directive scroll| and replacing the steps of the task queued in step 2: -> Monkeypatching [[HTML]]: +> Monkeypatching [[HTML]]: +> +>
    +> To try to scroll to the fragment for a Document |document|, with boolean +> |allow text directive scroll|, perform the following steps in parallel: > -> 2. Set request's client to sourceBrowsingContext's active document's relevant -> settings object, destination to "document", mode to "navigate", credentials -> mode to "include", use-URL-credentials flag, redirect mode to "manual", -> replaces client id to browsingContext's active document's relevant settings -> object's id, and [=request/text directive user activation=] to -> sourceBrowsingContext's active document's -> [=document/text directive user activation=]. Set sourceBrowsingContext's active -> document's [=document/text directive user activation=] to false. +> 1. Wait for an implementation-defined amount of time. (This is intended to allow the user agent +> to optimize the user experience in the face of performance concerns.) +> 2. Queue a global task on the navigation and traversal task source given document's relevant +> global object to run these steps: +> 1. If document has no parser, or its parser has stopped parsing, or the user +> agent has reason to believe the user is no longer interested in scrolling to +> the fragment, then abort these steps. +> 2. Scroll to the fragment given |document| and |allow text directive +> scroll|. +> 3. If document's indicated part is still null, then try to scroll to the fragment for +> |document| and |allow text directive scroll|. +> +>
    +Amend the update document for history step application steps to take a boolean +|allow text directive scroll| and use it when scrolling to a fragment: -Amend the try to scroll to the fragment steps by replacing the -steps of the task queued in step 2: +> Monkeypatching [[HTML]]: +> +>
    +> To update document for history step application given a Document document, a session history +> entry entry, a boolean doNotReactivate, integers scriptHistoryLength and scriptHistoryIndex, +> an optional list of session history entries entriesForNavigationAPI, and a +> boolean |allow text directive scroll|: +> +> 1. Let documentIsNew be true if |document|'s latest entry is null; otherwise false. +> 1. ... +>
  • If documentsEntryChanged is true, then: +> 1. Let oldURL be document's latest entry's URL. +> 2. ... +> 6. If documentIsNew is true, then: +> 1. Try to scroll to the fragment with |document| and |allow text +> directive scroll|. +> +>
  • -> Monkeypatching [[HTML]]: +Amend the apply the history step algorithm to take a boolean |allow text directive +scroll| and pass it through when calling update document for history step application +: + +> Monkeypatching [[HTML]]: +> +>
    +> +> To apply the history step given a non-negative integer step to a traversable navigable +> traversable, with boolean checkForCancelation, source snapshot params-or-null +> sourceSnapshotParams, navigable-or-null initiatorToCheck, user navigation involvement-or-null +> userInvolvementForNavigateEvents, and boolean |allow text directive scroll| +> (default false) perform the following steps. They +> return "initiator-disallowed", "canceled-by-beforeunload", "canceled-by-navigate", or "applied". +> +> 14. While completedChangeJobs does not equal totalChangeJobs: +> 1. ... +>
  • Queue a global task on the navigation and traversal task source given +> navigable's active window to run the steps: +> 1. If changingNavigableContinuation's update-only is false, then: +> 1. ... +> 2. Activate history entry |targetEntry| for navigable. +> 2. Let updateDocument be an algorithm step which performs update document for history +> step application given |targetEntry|'s document, |targetEntry|, +> changingNavigableContinuation's update-only, scriptHistoryLength, +> scriptHistoryIndex, entriesForNavigationAPI, and |allow text +> directive scroll| +> 3. If |targetEntry|'s document is equal to displayedDocument, then perform +> updateDocument. +> 15. Let totalNonchangingJobs be the size of nonchangingNavigablesThatStillNeedUpdates. +> +>
  • + +Amend the apply the push/replace history step to take and pass |allow text +directive scrolling| to apply the history step: + +> Monkeypatching [[HTML]]: +> +>
    +> To apply the push/replace history step given a non-negative integer |step| to a traversable +> navigable |traversable|, with boolean |allow text directive scroll| (default +> false): +> +> Return the result of applying the history step |step| to |traversable| given false, null, null, +> null, |allow text directive scroll|. +>
    + +Note: The |allow text directive scroll| is intentionally not set for traversal and reload cases. +This avoids extensive plumbing and checks for initiator origin and user involvement and history +scroll state should take precedence anyway. The text directive may still be used as the indicated +part of the document so highlights will be restored. + +Amend the finalize a cross-document navigation to take a |user involvement| +parameter and compute and pass |allow text directive scrolling| to apply the push/replace history +step: + +> Monkeypatching [[HTML]]: > -> 1. If document has no parser, or its parser has stopped parsing, or the user -> agent has reason to believe the user is no longer interested in scrolling to -> the fragment, then set document's -> [=document/allow text fragment scroll=] to false and abort these steps. -> 2. Scroll to the fragment given in document's URL. If this does not find an -> indicated part, then try to scroll to the fragment for -> document. -> 3. Set document's [=document/allow text fragment scroll=] to false. +>
    +> +> To finalize a cross-document navigation given a navigable navigable, history handling behavior +> historyHandling, session history entry historyEntry, and user +> navigation involvement |user involvement| (default "none"): +> +> 1. Assert: this is running on navigable's traversable navigable's session history traversal queue. +> 2. ... +>
  • Let |allow text directive scroll| be the result of [=check if a text +> directive can be scrolled|checking if a text directive can be scrolled=], given +> |historyEntry|'s [=she/document=], |historyEntry|'s document state's +> [=document state/initiator origin=], and |user involvement| +> 11. Apply the push/replace history step targetStep to traversable, with |allow +> text directive scroll|. + +Amend the navigate algorithm to pass |user involvement| to the finalize a +cross-document navigation steps: + +> Monkeypatching [[HTML]]: +> +>
    +> +> 1. ... +>
  • . In parallel, run these steps:
  • +> 1. ... +>
  • . Attempt to populate the history entry's document for historyEntry, given +> navigable, "navigate", sourceSnapshotParams, targetSnapshotParams, navigationId, +> navigationParams, cspNavigationType, with allowPOST set to true and completionSteps set to +> the following step:
  • +> 1. Append session history traversal steps to navigable's traversable to finalize a +> cross-document navigation given navigable, historyHandling, historyEntry, +> and |userInvolvement|. + +Amend the Navigate to a fragment algorithm to take an |initiator origin| parameter +and pass the |allow text directive scroll| flag when scrolling to the fragment: + +> Monkeypatching [[HTML]]: +> +>
    +> +> To navigate to a fragment given a navigable navigable, a URL url, a history handling behavior +> historyHandling, a user navigation involvement |userInvolvement|, a serialized state-or-null +> navigationAPIState, navigation ID navigationId, an [=/origin=] |initiator +> origin|: +> +> 1. Let navigation be |navigable|'s active window's navigation API. +> 2. ... +>
  • Update document for history step application given navigable's active document, historyEntry, true, scriptHistoryIndex, and scriptHistoryLength. +> 15. Update the navigation API entries for a same-document navigation given navigation, historyEntry, and historyHandling. +> 16. Let |allow text directive scroll| be the result of [=check if a text +> directive can be scrolled|checking if a text directive can be scrolled=], given +> |navigable|'s [=active document=], |initiator origin|, and |userInvolvement| +> 17. Scroll to the fragment given |navigable|'s active document, and |allow text +> directive scroll|. +> +>
  • + +Amend the navigate algorithm to pass the initiator origin when performing a +fragment navigation: + +> Monkeypatching [[HTML]]: +> +>
    +> +> 10. If the navigation must be a replace given url and navigable's active document, then set historyHandling to "replace". +> 11. If all of the following are true: +> * documentResource is null; +> * response is null; +> * url equals navigable's active session history entry's URL with exclude fragments set to true; and +> * url's fragment is non-null, +> +> then: +> +> 1. Navigate to a fragment given navigable, url, historyHandling, userInvolvement, +> navigationAPIState, navigationId, and +> initiatorOriginSnapshot +> 1. Let navigation be |navigable|'s active window's navigation API. +> +>
    ### Restricting Scroll on Load ### {#restricting-scroll-on-load} @@ -1386,7 +1628,7 @@ To find the shadow-including parent of |node| follow these steps:
    To invoke text directives, given as input an ASCII string |text directives| and a [=Document=] +spec=infra>ASCII string |text directives| and a [=/Document=] |document|, run these steps:
    @@ -1419,7 +1661,7 @@ spec=infra>ASCII string |text directives| and a [=Document=]
    To find a range from a text directive, given a -[=text directive=] |parsedValues| and [=Document=] |document|, run the +[=text directive=] |parsedValues| and [=/Document=] |document|, run the following steps:
    @@ -2096,9 +2338,9 @@ History scroll restoration is blocked by amending the restore persisted state steps by inserting a new step after 2: 3. Get - the document policy value of the "force-load-at-top" feature for the [=Document=]. If + the document policy value of the "force-load-at-top" feature for the [=/Document=]. If the result is true, then the user agent should not restore the scroll - position for the [=Document=] or any of its scrollable regions. + position for the [=/Document=] or any of its scrollable regions. ## Feature Detectability ## {#feature-detectability} diff --git a/index.html b/index.html index 6a90ac3..f335455 100644 --- a/index.html +++ b/index.html @@ -813,7 +813,7 @@

    URL Fragment Text Directives

    -

    Draft Community Group Report,

    +

    Draft Community Group Report,

    This version: @@ -1238,7 +1238,7 @@

    navigate to a fragment:

    +

    In the definition of navigate to a fragment:

    Monkeypatching HTML § 7.4.2.3.3 Fragment navigations:

    @@ -1333,7 +1333,7 @@

    create navigation params by fetching:

    +

    In the definition of create navigation params by fetching:

    Monkeypatching HTML § 7.4.5 Populating a session history entry:

    @@ -1392,7 +1392,7 @@

    -

    A same document navigation changed only the fragment. This adds a new session history entry in the navigate to +

    A same document navigation changed only the fragment. This adds a new session history entry in the navigate to a fragment steps. However, since only the fragment changed, the new entry’s directive state points to the same state as the first entry, with a value of "bar".

    onhashchange = () => console.assert(false, "hashchange doesn’t fire.");
    @@ -1612,8 +1612,7 @@ 

    Let directives be the document’s uninvoked directives.

  • -

    If directives is non-null and document’s allow text - fragment scroll is true then:

    +

    If directives is non-null then:

    1. Let ranges be a list that is the result of running @@ -1644,7 +1643,7 @@

  • -

    In scroll to the fragment, handle a indicated part that is a range and also +

    In scroll to the fragment, handle an indicated part that is a range and also prevent fragment scrolling if the force-load-at-top policy is enabled. Make the following changes:

    Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment:

    @@ -1697,7 +1696,7 @@

    Scrolling to a text directive centers it in the block flow direction.
  • - Scroll target into view, with behavior set to "auto", block set to + Scroll target into view, with behavior set to "auto", block set to "start", and inline set to "nearest".
  • scroll a target into view, @@ -1771,7 +1770,7 @@

  • -

    In the definition of navigate to a fragment:

    +

    In the definition of navigate to a fragment:

    Monkeypatching HTML § 7.4.2.3.3 Fragment navigations:

    @@ -1893,6 +1892,25 @@

    Document's indicated part.

    3.5.4. Restricting the Text Fragment

    +
    + This section integrates with HTML navigation to restrict when an indicated text directive will + be allowed to scroll. In summary: +
      +
    • +

      Add a boolean text directive user activation to both Document and Request. This + flag is set on a document when created from a user activated navigation and consumed if a text + directive is scrolled. If unconsumed, it can be transfered to an outgoing navigation request. + This implements the user-activation-through-redirects behavior described in the note below.

      +
    • +

      Define a series of checks, performed on a document and the user involvement and initiator origin + state of a navigation, to determine whether a text directive should be allowed to perform a + scroll.

      +
    • +

      Compute the scroll permission from "finalize a cross document navigation" and from "navigate to + a fragment steps" and plumb it through to the "scroll to the fragment" steps where its used to + abort a text directive scroll.

      +
    +

    Amend the definition of a request and of a Document to include a new boolean text directive user activation field:

    @@ -1941,116 +1959,405 @@

    See redirects.md for a more in-depth discussion.

    +

    Amend the create navigation params by fetching steps to transfer the active +document's text directive user activation value into request’s text directive user activation.

    Monkeypatching [HTML]:

    -

    Each Document has an allow text fragment scroll, which is a - boolean, initially false.

    -
    -

    allow text fragment scroll is used to determine whether a text fragment will - perform scrolling when the document is loaded. If it is false, the text fragment can be - visually indicated but will not be scrolled to. This implements the mitigations discussed in § 3.5.2 Scroll On Navigation.

    -

    The reason we compute and store allow text fragment scroll, rather than performing these - checks at the time of use, is that it relies on the properties of the navigation while the - invocation will occur as part of the scroll to the fragment steps which can - happen outside the context of a navigation.

    +
    +
      +
    1. +

      Assert: this is running in parallel.

      +
    2. +

      Let documentResource be entry’s document state’s resource.

      +
    3. +

      Let request be a new request, with

      +
      +
      url +
      entry’s URL +
      ... +
      ... +
      referrer policy +
      entry’s document state’s request referrer policy +
      text directive user activation +
      navigable’s active document's text directive user activation +
      +
    4. +

      Set navigable’s active document's text directive + user activation to false.

      +
    5. +

      If documentResource is a POST resource, then:

      +
        +
      1. +

        ...

        +
      +
    -
    TODO: This should really only prevent potentially observable side-effects like - automatic scrolling. Unobservable effects like a highlight could be safely - allowed in all cases.
    -

    Amend the create -and initialize a Document object steps by adding the following steps before returning document:

    +

    Amend the definition of navigation params to include a new field:

    Monkeypatching [HTML]:

    -
      +
      +
      user involvement +
      A user navigation involvement value. +
      +
    +

    Initialize the user involvement value everywhere a navigation params is +created. Specifically: initialize it to true in the create navigation params by +fetching case:

    +
    +

    Monkeypatching [HTML]:

    +
    + To create navigation params by fetching given 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 a user navigation + involvement user involvement, perform the following steps. They return a navigation params, + a non-fetch scheme navigation params, or null. +
      +
    1. +

      Assert: this is running in parallel.

      +
    2. +

      ...

      +
    3. Let resultPolicyContainer be the result of determining navigation params policy container given + response’s URL, entry’s document state’s history policy container, sourceSnapshotParams’s source + policy container, null, and responsePolicyContainer. +
    4. +

      If navigable’s container is an iframe, and response’s timing allow passed flag is set, then + set container’s pending resource-timing start time to null.

      +
    5. +

      Return a new navigation params, with

      +
      +
      id +
      navigationId +
      ... +
      ... +
      about base URL +
      entry’s document state’s about base URL +
      user involvement +
      user involvement +
      +
    +
    +
    +

    Amend the create and initialize a Document object steps to compute and store the text directive +user activation flag:

    +
    +

    Monkeypatching [HTML]:

    +
    +
      +
    1. +

      Process link headers given document, navigationParams’s response, and "pre-media".

      +
    2. +
      + Set document’s text directive user activation to true if any of the following +conditions hold, false otherwise: + +
      +
    3. +

      Return document.

      +
    +
    +
    +
    + To check if a text directive can be scrolled; given a Document document, an origin-or-null initiator origin, and user navigation involvement-or-null user involvement, follow these steps: +
    1. -

      Set document’s text directive user activation by following these sub-steps:

      -
        -
      1. -

        Let is user activated be true if the current navigation was initiated from - a window that had a transient activation at the time the - navigation was initiated, or the UA has reason to believe it comes from a - direct user gesture (e.g. user typed into the address bar).

        -
        TODO: it’d be better to refer to the user-activation flag.
        -
      2. -

        If browsing context is a top-level browsing context and if either of is - user activated or the text directive user activation of navigationParam’s request object is true, set the document’s text directive user activation to true. Otherwise, set it to false.

        -
        It’s important that the flag not be copyable so that only one text fragment can be - activated per user-activated navigation.
        -
      +

      If document’s uninvoked directives field is null or empty, return false.

      +
    2. +

      Let is user involved be true if: document’s text directive user activation is +true, or user involvement is one of "activation" or "browser +UI"; false otherwise.

      +
    3. +

      Set document’s text directive user activation to false.

      +
    4. +

      If user involvement is "browser UI", return true.

      +
      +

      If a navigation originates from browser UI, it’s always ok to allow it since it’ll be + user triggered and the page/script isn’t providing the text snippet.

      +

      Note: The intent in this item is to distinguish cases where the app/page is able to + control the URL from those that are fully under the user’s control. In the former we + want to prevent scrolling of the text fragment unless the destination is loaded in a + separate browsing context group (so that the source cannot both control the text snippet + and observe side-effects in the navigation). There are some cases where "browser UI" may + be a grey area in this regard. E.g. an "open in new window" context menu item when right + clicking on a link.

      +

      See sec-fetch-site in [FETCH-METADATA] for a related discussion of how this applies.

      +
      +
    5. +

      If is user involved is false, return false.

      +
    6. +

      If document’s node navigable has a parent, return false.

      +
    7. +

      If initiator origin is non-null and document’s origin is same origin with initiator origin, return true.

    8. -

      Set document’s allow text fragment scroll by following these sub-steps:

      +

      If document’s browsing context's group's browsing context set has length 1, return true.

      +
      i.e. Only allow navigation from a cross-origin element/script if the + document is loaded in a noopener context. That is, a new top level + browsing context group to which the navigator does not have script access + and which can be placed into a separate process.
      +
    9. +

      Otherwise, return false.

      +
    +
    +

    Amend (the already amended, in § 3.4.1 Invoking Text Directives) scroll to the +fragment steps to add a new parameter, a boolean allow text directive scroll:

    +
    +

    Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment:

    +
    + To scroll to the fragment given a Document document and boolean allow text + directive scroll: +
      +
    1. +

      If document’s indicated part is null, then set document’s target element to null.

      +
    2. +

      ...

      +
    3. +

      Otherwise:

      +
        +
      1. +

        Assert: document’s indicated part is an element or it is a range.

        +
      2. +

        ...

        +
      3. If target is a range, then: +
          +
        1. +

          If allow text directive scroll is false, return.

          +
        2. +

          Set target to be the first common ancestor of target’s start node and end node.

          +
        3. +

          ...

          +
        +
      +
    +
    +
    +

    Amend the try to scroll to the fragment by adding a boolean flag allow text +directive scroll and replacing the steps of the task queued in step 2:

    +
    +

    Monkeypatching [HTML]:

    +
    + To try to scroll to the fragment for a Document document, with boolean allow text directive scroll, perform the following steps in parallel: +
      +
    1. +

      Wait for an implementation-defined amount of time. (This is intended to allow the user agent + to optimize the user experience in the face of performance concerns.)

      +
    2. +

      Queue a global task on the navigation and traversal task source given document’s relevant + global object to run these steps:

      +
        +
      1. +

        If document has no parser, or its parser has stopped parsing, or the user + agent has reason to believe the user is no longer interested in scrolling to + the fragment, then abort these steps.

        +
      2. +

        Scroll to the fragment given document and allow text directive + scroll.

        +
      3. +

        If document’s indicated part is still null, then try to scroll to the fragment for document and allow text directive scroll.

        +
      +
    +
    +
    +

    Amend the update document for history step application steps to take a boolean allow text directive scroll and use it when scrolling to a fragment:

    +
    +

    Monkeypatching [HTML]:

    +
    + To update document for history step application given a Document document, a session history + entry entry, a boolean doNotReactivate, integers scriptHistoryLength and scriptHistoryIndex, + an optional list of session history entries entriesForNavigationAPI, and a + boolean allow text directive scroll: +
      +
    1. +

      Let documentIsNew be true if document’s latest entry is null; otherwise false.

      +
    2. +

      ...

      +
    3. + If documentsEntryChanged is true, then: +
        +
      1. +

        Let oldURL be document’s latest entry’s URL.

        +
      2. +

        ...

        +
      +
    4. +

      If documentIsNew is true, then:

      +
        +
      1. +

        Try to scroll to the fragment with document and allow text + directive scroll.

        +
      +
    +
    +
    +

    Amend the apply the history step algorithm to take a boolean allow text directive +scroll and pass it through when calling update document for history step application :

    +
    +

    Monkeypatching [HTML]:

    +
    +

    To apply the history step given a non-negative integer step to a traversable navigable +traversable, with boolean checkForCancelation, source snapshot params-or-null +sourceSnapshotParams, navigable-or-null initiatorToCheck, user navigation involvement-or-null +userInvolvementForNavigateEvents, and boolean allow text directive scroll (default false) perform the following steps. They +return "initiator-disallowed", "canceled-by-beforeunload", "canceled-by-navigate", or "applied".

    +
      +
    1. +

      While completedChangeJobs does not equal totalChangeJobs:

      +
        +
      1. +

        ...

        +
      2. + Queue a global task on the navigation and traversal task source given +navigable’s active window to run the steps: +
          +
        1. +

          If changingNavigableContinuation’s update-only is false, then:

          +
            +
          1. +

            ...

            +
          2. +

            Activate history entry targetEntry for navigable.

            +
          +
        2. +

          Let updateDocument be an algorithm step which performs update document for history +step application given targetEntry’s document, targetEntry, +changingNavigableContinuation’s update-only, scriptHistoryLength, +scriptHistoryIndex, entriesForNavigationAPI, and allow text +directive scroll

          +
        3. +

          If targetEntry’s document is equal to displayedDocument, then perform +updateDocument.

          +
        +
      +
    2. +

      Let totalNonchangingJobs be the size of nonchangingNavigablesThatStillNeedUpdates.

      +
    +
    +
    +

    Amend the apply the push/replace history step to take and pass allow text +directive scrolling to apply the history step:

    +
    +

    Monkeypatching [HTML]:

    +
    + To apply the push/replace history step given a non-negative integer step to a traversable + navigable traversable, with boolean allow text directive scroll (default + false): +

    Return the result of applying the history step step to traversable given false, null, null, + null, allow text directive scroll.

    +
    +
    +

    Note: The allow text directive scroll is intentionally not set for traversal and reload cases. +This avoids extensive plumbing and checks for initiator origin and user involvement and history +scroll state should take precedence anyway. The text directive may still be used as the indicated +part of the document so highlights will be restored.

    +

    Amend the finalize a cross-document navigation to take a user involvement parameter and compute and pass allow text directive scrolling to apply the push/replace history +step:

    +
    +

    Monkeypatching [HTML]:

    +
    +

    To finalize a cross-document navigation given a navigable navigable, history handling behavior +historyHandling, session history entry historyEntry, and user +navigation involvement user involvement (default "none"):

    +
      +
    1. +

      Assert: this is running on navigable’s traversable navigable’s session history traversal queue.

      +
    2. +

      ...

      +
    3. Let allow text directive scroll be the result of checking if a text directive can be scrolled, given historyEntry’s document, historyEntry’s document state’s initiator origin, and user involvement +
    4. +

      Apply the push/replace history step targetStep to traversable, with allow +text directive scroll.

      +
    +
    +
    +

    Amend the navigate algorithm to pass user involvement to the finalize a +cross-document navigation steps:

    +
    +

    Monkeypatching [HTML]:

    +
    +
      +
    1. +

      ...

      +
    2. . In parallel, run these steps:
      1. -

        If document’s uninvoked directives field is null or empty, set allow text fragment scroll to false and abort these sub-steps.

        -
      2. -

        Let text directive user activation be the value of document’s text directive user activation and set document’s text directive user activation to false.

        -
      3. -

        If the navigationParam’s request has a sec-fetch-site header and its value is "none" set allow text fragment scroll to true and abort these sub-steps.

        -
        -

        If a navigation originates from browser UI, it’s always ok to allow it - since it’ll be user triggered and the page/script isn’t providing the - text snippet.

        -

        Note: Depending on the UA, there can be cases where the incumbentNavigationOrigin parameter is null but - it’s not clear that the navigation is to be considered as - initiated from browser UI. E.g. an "open in new window" context - menu item when right clicking on a link. The intent in this item - is to distinguish cases where the app/page is able to set the URL - from those that are fully under the user’s control. In the former - we want to prevent activation of the text fragment unless the - destination is loaded in a separate browsing context group (so that - the source cannot both control the text snippet and observe - side-effects in the navigation).

        -

        See sec-fetch-site in [FETCH-METADATA] for a more detailed discussion of how this applies.

        -
        -
      4. -

        If text directive user activation is false, set allow text fragment scroll to false and abort these sub-steps.

        -
      5. -

        If the navigationParam’s request has a sec-fetch-site header and its value is "same-origin" set allow text fragment scroll to true and abort these - sub-steps.

        -
      6. -

        If document’s browsing context is a top-level browsing - context and its group’s browsing context set has length 1, set allow text fragment scroll to true and abort these sub-steps.

        -
        i.e. Only allow navigation from a cross-origin element/script if the - document is loaded in a noopener context. That is, a new top level - browsing context group to which the navigator does not have script access - and which can be placed into a separate process.
        -
      7. -

        Otherwise, set allow text fragment scroll to false.

        +

        ...

        +
      8. . Attempt to populate the history entry’s document for historyEntry, given +navigable, "navigate", sourceSnapshotParams, targetSnapshotParams, navigationId, +navigationParams, cspNavigationType, with allowPOST set to true and completionSteps set to +the following step: +
          +
        1. +

          Append session history traversal steps to navigable’s traversable to finalize a +cross-document navigation given navigable, historyHandling, historyEntry, and userInvolvement.

          +
      -
    + +
    -

    Amend step 2 of the process a navigate fetch steps to additionally set request’s text directive user activation to the value of the active document's text directive user activation and set the active document's value to -false.

    +

    Amend the Navigate to a fragment algorithm to take an initiator origin parameter +and pass the allow text directive scroll flag when scrolling to the fragment:

    Monkeypatching [HTML]:

    -
      -
    1. -

      Set request’s client to sourceBrowsingContext’s active document’s relevant - settings object, destination to "document", mode to "navigate", credentials - mode to "include", use-URL-credentials flag, redirect mode to "manual", - replaces client id to browsingContext’s active document’s relevant settings - object’s id, and text directive user activation to - sourceBrowsingContext’s active document’s text directive user activation. Set sourceBrowsingContext’s active - document’s text directive user activation to false.

      -
    +
    +

    To navigate to a fragment given a navigable navigable, a URL url, a history handling behavior +historyHandling, a user navigation involvement userInvolvement, a serialized state-or-null +navigationAPIState, navigation ID navigationId, an origin initiator +origin:

    +
      +
    1. +

      Let navigation be navigable’s active window’s navigation API.

      +
    2. +

      ...

      +
    3. Update document for history step application given navigable’s active document, historyEntry, true, scriptHistoryIndex, and scriptHistoryLength. +
    4. +

      Update the navigation API entries for a same-document navigation given navigation, historyEntry, and historyHandling.

      +
    5. +

      Let allow text directive scroll be the result of checking if a text directive can be scrolled, given navigable’s active document, initiator origin, and userInvolvement

      +
    6. +

      Scroll to the fragment given navigable’s active document, and allow text +directive scroll.

      +
    +
    -

    Amend the try to scroll to the fragment steps by replacing the -steps of the task queued in step 2:

    +

    Amend the navigate algorithm to pass the initiator origin when performing a +fragment navigation:

    Monkeypatching [HTML]:

    -
      -
    1. -

      If document has no parser, or its parser has stopped parsing, or the user - agent has reason to believe the user is no longer interested in scrolling to - the fragment, then set document’s allow text fragment scroll to false and abort these steps.

      -
    2. -

      Scroll to the fragment given in document’s URL. If this does not find an - indicated part, then try to scroll to the fragment for - document.

      -
    3. -

      Set document’s allow text fragment scroll to false.

      -
    +
    +
      +
    1. +

      If the navigation must be a replace given url and navigable’s active document, then set historyHandling to "replace".

      +
    2. +

      If all of the following are true:

      +
        +
      • +

        documentResource is null;

        +
      • +

        response is null;

        +
      • +

        url equals navigable’s active session history entry’s URL with exclude fragments set to true; and

        +
      • +

        url’s fragment is non-null,

        +
      +

      then:

      +
        +
      1. +

        Navigate to a fragment given navigable, url, historyHandling, userInvolvement, +navigationAPIState, navigationId, and initiatorOriginSnapshot

        +
      2. +

        Let navigation be navigable’s active window’s navigation API.

        +
      +
    +

    3.5.5. Restricting Scroll on Load

    This section defines how the force-load-at-top policy is used to prevent all @@ -2073,7 +2380,7 @@

    <

    -

    Amend the update document for history step application steps +

    Amend the update document for history step application steps to check the force-load-at-top policy and avoid scrolling in a new document if it’s set.

    @@ -2140,7 +2447,7 @@
    +indicated part processing model to return a range, rather than an element, that will be scrolled into view.
    To find the first common ancestor of two nodes nodeA and nodeB, follow these steps: @@ -2166,7 +2473,7 @@
    This section outlines several algorithms and definitions that specify how to - turn a full fragment directive string into a list of Ranges in the + turn a full fragment directive string into a list of Ranges in the document.

    At a high level, we take a fragment directive string that looks like this:

    text=prefix-,foo&unknown&text=bar,baz
    @@ -2179,8 +2486,8 @@ 

    -

    If a directive successfully matches to text in the document, it returns a range indicating that match in the document. The invoke text directives steps are the high level API provided by this - section. These return a list of ranges that were matched +

    If a directive successfully matches to text in the document, it returns a range indicating that match in the document. The invoke text directives steps are the high level API provided by this + section. These return a list of ranges that were matched by the individual directive matching steps, in the order the directives were specified in the fragment directive string.

    If a directive was not matched, it does not add an item to the returned @@ -2190,7 +2497,7 @@

    invoke text directives, given as input an ASCII string text directives and a Document document, run these steps:
    This algorithm takes as input a text directives, that is the raw text of the fragment directive and the document over which it operates. - It returns a list of ranges that are to be visually + It returns a list of ranges that are to be visually indicated, the first of which will be scrolled into view (if the UA scrolls automatically).
      @@ -2202,7 +2509,7 @@

      strictly splitting the string text directives on "&".

    1. -

      Let ranges be a list of ranges, initially empty.

      +

      Let ranges be a list of ranges, initially empty.

    2. For each ASCII string directive of directives:

        @@ -2226,12 +2533,12 @@

        This algorithm takes as input a successfully parsed text directive and a - document in which to search. It returns a range that points to the first + document in which to search. It returns a range that points to the first text passage within the document that matches the searched-for text and satisfies the surrounding context. Returns null if no such passage exists.

        end can be null. If omitted, this is an "exact" - search and the returned range will contain a string exactly matching start. If end is - provided, this is a "range" search; the returned range will start with start and end with end. In the normative text below, we’ll call a + search and the returned range will contain a string exactly matching start. If end is + provided, this is a "range" search; the returned range will start with start and end with end. In the normative text below, we’ll call a text passage that matches the provided start and end, regardless of which mode we’re in, the "matching text".

        Either or both of prefix and suffix can be null, in which case context on that @@ -2261,7 +2568,7 @@

        1. -

          Let searchRange be a range with start (document, 0) and end (document, document’s length)

          +

          Let searchRange be a range with start (document, 0) and end (document, document’s length)

        2. While searchRange is not collapsed:

            @@ -2278,7 +2585,7 @@

            Set searchRange’s start to the first boundary point after prefixMatch’s start

          1. -

            Let matchRange be a range whose start is prefixMatch’s end and end is searchRange’s end.

            +

            Let matchRange be a range whose start is prefixMatch’s end and end is searchRange’s end.

          2. Advance matchRange’s start to the next non-whitespace position.

          3. @@ -2286,7 +2593,7 @@

            This can happen if prefixMatch’s end or its subsequent non-whitespace position is at the end of the document.

  • -

    Assert: matchRange’s start node is a Text node.

    +

    Assert: matchRange’s start node is a Text node.

    matchRange’s start now points to the next non-whitespace text data following a matched prefix.
  • @@ -2318,7 +2625,7 @@

    Set searchRange’s start to the first boundary point after potentialMatch’s start

  • -

    Let rangeEndSearchRange be a range whose start is potentialMatch’s end and whose end is searchRange’s end.

    +

    Let rangeEndSearchRange be a range whose start is potentialMatch’s end and whose end is searchRange’s end.

  • While rangeEndSearchRange is not collapsed:

      @@ -2342,7 +2649,7 @@

      If parsedValues’s suffix is null, return potentialMatch.

    1. -

      Let suffixRange be a range with start equal to potentialMatch’s end and end equal to searchRange’s end.

      +

      Let suffixRange be a range with start equal to potentialMatch’s end and end equal to searchRange’s end.

    2. Advance suffixRange’s start to the next non-whitespace position.

      @@ -2394,14 +2701,14 @@

      - To advance a range range’s start to the next + To advance a range range’s start to the next non-whitespace position follow the steps:
      1. While range is not collapsed:

        1. -

          Let node be range’s start node.

          +

          Let node be range’s start node.

        2. Let offset be range’s start offset.

        3. @@ -2409,7 +2716,7 @@

          visible text node or if offset is equal to node’s length then:

          1. -

            Set range’s start node to the next node, in shadow-including tree order.

            +

            Set range’s start node to the next node, in shadow-including tree order.

          2. Set range’s start offset to 0.

          3. @@ -2441,16 +2748,16 @@

      - To find a string in range given a string query, a range searchRange, and booleans wordStartBounded and wordEndBounded, + To find a string in range given a string query, a range searchRange, and booleans wordStartBounded and wordEndBounded, run these steps: -
      This algorithm will return a range that represents the first instance of +
      This algorithm will return a range that represents the first instance of the query text that is fully contained within searchRange, optionally restricting itself to matches that start and/or end at word boundaries (see § 3.6.2 Word Boundaries). Returns null if none is found.

      The basic premise of this algorithm is to walk all searchable text nodes within a block, collecting them into a list. The list is then concatenated into a single string in which we can search, using the node list to - determine offsets with a node so we can return a range.

      + determine offsets with a node so we can return a range.

      Collection breaks when we hit a block node, e.g. searching over this tree:

      <div>
         a<em>b</em>c<div>d</div>e
      @@ -2466,12 +2773,12 @@ 

      While searchRange is not collapsed:

      1. -

        Let curNode be searchRange’s start node.

        +

        Let curNode be searchRange’s start node.

      2. If curNode is part of a non-searchable subtree:

        1. -

          Set searchRange’s start node to the next node, in shadow-including tree order, that isn’t a shadow-including +

          Set searchRange’s start node to the next node, in shadow-including tree order, that isn’t a shadow-including descendant of curNode.

        2. Set searchRange’s start offset to 0.

          @@ -2482,7 +2789,7 @@

          If curNode is not a visible text node:

          1. -

            Set searchRange’s start node to the next node, in shadow-including tree order, that is not a doctype.

            +

            Set searchRange’s start node to the next node, in shadow-including tree order, that is not a doctype.

          2. Set searchRange’s start offset to 0.

          3. @@ -2513,11 +2820,11 @@

            Set curNode to the next node in shadow-including tree order.

        3. -

          Run the find a range from a node list steps given query, searchRange, textNodeList, wordStartBounded and wordEndBounded as input. If the resulting range is not null, then return it.

          +

          Run the find a range from a node list steps given query, searchRange, textNodeList, wordStartBounded and wordEndBounded as input. If the resulting range is not null, then return it.

        4. If curNode is null, then break.

        5. -

          Assert: curNode follows searchRange’s start node.

          +

          Assert: curNode follows searchRange’s start node.

        6. Set searchRange’s start to the boundary point (curNode, 0).

          @@ -2561,7 +2868,7 @@

          To find a range from a node list given a search string queryString, -a range searchRange, a list of Text nodes nodes, and booleans wordStartBounded and wordEndBounded, follow these steps: +a range searchRange, a list of Text nodes nodes, and booleans wordStartBounded and wordEndBounded, follow these steps:
          Optionally, this will only return a match if the matched text begins and/or ends on a word boundary. For example: @@ -2586,7 +2893,7 @@

          Let searchStart be 0.

        7. -

          If the first item in nodes is searchRange’s start node then +

          If the first item in nodes is searchRange’s start node then set searchStart to searchRange’s start offset.

        8. Let start and end be boundary points, initially null.

          @@ -2625,7 +2932,7 @@

          Let endInset be 0.

        9. -

          If the last item in nodes is searchRange’s end node then set endInset to (searchRange’s end node's lengthsearchRange’s end offset)

          +

          If the last item in nodes is searchRange’s end node then set endInset to (searchRange’s end node's lengthsearchRange’s end offset)

          endInset is the offset from the last position in the last node in the reverse direction. Alternatively, it is the length of the node that’s not included in the range.
          @@ -2635,7 +2942,7 @@

          Assert: start and end are non-null, valid boundary points in searchRange.

        10. -

          Return a range with start start and end end.

          +

          Return a range with start start and end end.

      @@ -2972,8 +3279,8 @@

      Index

      Terms defined by this specification

        -
      • allow text fragment scroll, in § 3.5.4
      • CharacterString, in § 3.3.4 +
      • check if a text directive can be scrolled, in § 3.5.4
      • directives, in § 3.3
      • directive state @@ -3027,6 +3334,7 @@

        uninvoked directives, in § 3.3.2
      • UnknownDirective, in § 3.3.4 +
      • user involvement, in § 3.5.4
      • valid fragment directive, in § 3.3.4
      • value, in § 3.3.1
      • visible text node, in § 3.6.1 @@ -3084,6 +3392,7 @@

        length
      • node
      • node document +
      • origin
      • parent
      • parent element
      • range @@ -3102,7 +3411,6 @@

        [FETCH] defines the following terms:
        • request -
        • user-activation
      • [HTML] defines the following terms: @@ -3119,20 +3427,33 @@

        HashChangeEvent
      • Location
      • active document +
      • apply the history step +
      • apply the push/replace history step
      • being rendered
      • browsing context
      • browsing context set +
      • create navigation params by fetching +
      • document +
      • document state +
      • finalize a cross-document navigation +
      • group +
      • initiator origin
      • language
      • multiple
      • navigate +
      • navigate to a fragment +
      • navigation params +
      • node navigable +
      • origin +
      • parent
      • restore persisted state +
      • same origin
      • scroll to the fragment
      • select
      • serializes as void -
      • top-level browsing context -
      • transient activation
      • try to scroll to the fragment
      • update document for history step application +
      • user navigation involvement
    3. [INFRA] defines the following terms: @@ -3576,28 +3897,28 @@