From 4b8e0b694ebe337ad446ffa2a92c9a74376495d2 Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Thu, 11 Sep 2025 21:26:39 -0400 Subject: [PATCH 1/7] Some ROUGH progress Move around More --- source | 1138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1127 insertions(+), 11 deletions(-) diff --git a/source b/source index 5361f494458..c399848d804 100644 --- a/source +++ b/source @@ -2298,11 +2298,15 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
  • normalize newlines
  • strip leading and trailing ASCII whitespace
  • strip and collapse ASCII whitespace
  • +
  • strictly split a string
  • split a string on ASCII whitespace
  • split a string on commas
  • collect a sequence of code points and its associated position variable
  • skip ASCII whitespace
  • +
  • code point substring
  • +
  • code point substring by positions
  • +
  • code point substring to the end of the string
  • The ordered map data structure and the associated definitions for key, value, @@ -2499,6 +2503,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
  • serialize an integer
  • Default encode set
  • component percent-encode set
  • +
  • fragment percent-encode set
  • UTF-8 percent-encode
  • percent-decode
  • set the username
  • @@ -3244,7 +3249,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
  • A shadow root's declarative member
  • The attach a shadow root algorithm
  • The retargeting algorithm
  • -
  • Node interface
  • +
  • Node interface, and its length concept
  • NodeList interface
  • ProcessingInstruction interface
  • ShadowRoot interface
  • @@ -3385,9 +3390,12 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
  • The concept of a DOM range, and the terms start node, + start offset, start, - end, and - boundary point as applied to ranges.
  • + end, + collapsed, + boundary point, and + after as applied to ranges.
  • The create an element algorithm
  • The element interface concept
  • @@ -105058,6 +105066,12 @@ interface NotRestoredReasons { when the user had originally entered the values with an explicit, non-default directionality.

    + + +
  • directive state, which is a directive + state-or-null, initially a new directive state.

  • @@ -105112,6 +105126,31 @@ interface NotRestoredReasons { attempt to do so automatically +
    + +

    A directive state is a struct that holds the value of a fragment + directive at the time a session history entry is created. It is used to invoke + directives, such as text highlighting, whenever the entry is traversed. It has:

    + +
    +
    value
    +
    null or an ASCII string, initially null
    +
    + +

    A directive state may be shared by multiple session history entries.

    + +
    +

    The fragment directive is removed from the URL before the URL is set to the session + history entry. It is instead stored in the directive state. This prevents it from being + visible to script APIs so that a directive can be specified without interfering with a + page's operation.

    + +

    The fragment directive is stored in the directive state object, rather than a raw string, + since the same directive state can be shared across multiple contiguous session history + entries. On a traversal, the directive is only processed (i.e. search text and highlight) if + the directive state has changed between two entries.

    +
    +
    Document state
    @@ -106440,10 +106479,17 @@ location.href = '#foo'; +
  • Let fragment directive be the result of running remove the fragment + directive on url.

  • + +
  • Let directive state be a new directive state with its value set to fragment directive.

  • +
  • Let historyEntry be a new session history entry, with its URL set to url and its document state set to - documentState.

  • + data-x="she-url">URL set to url, its document state set to documentState, and its + directive state set to directive + state.

  • Let navigationParams be null.

  • @@ -106984,6 +107030,23 @@ location.href = '#foo'; navigationAPIState, and a navigation ID navigationId:

      +
    1. Let directive state be navigable's active session history entry's directive state.

    2. + +
    3. Let fragment directive be the result of running remove the fragment + directive on url.

    4. + +
    5. +

      If fragment directive is not null, then set directive state to a new + directive state whose value is set to + fragment directive.

      + +

      Otherwise, when only the fragment has changed and it did not specify a + directive, the active entry's directive state is reused. This prevents a fragment change from + clobbering highlights.

      +
    6. +
    7. Let navigation be navigable's active window's navigation API.

    8. @@ -107024,6 +107087,9 @@ location.href = '#foo';
      scroll restoration mode
      navigable's active session history entry's scroll restoration mode
      + +
      directive state
      +
      directive state
      @@ -107799,6 +107865,9 @@ location.href = '#foo';
    9. Let activeEntry be navigable's active session history entry.

    10. +
    11. Let fragment directive be the result of running remove the fragment + directive on newUrl.

    12. +
    13. Let newEntry be a new session history entry, with

      @@ -107820,6 +107889,9 @@ location.href = '#foo';
      persisted user state
      activeEntry's persisted user state
      + +
      directive state
      +
      activeEntry's directive state
    14. @@ -107845,14 +107917,36 @@ location.href = '#foo';
    15. Increment document's history object's index.

    16. -
    17. Set document's history object's length to its index + 1.

    18. -
    +
  • +

    Set document's history object's length to its index + 1.

    + +

    These are temporary best-guess values for immediate synchronous access.

    +
  • -

    These are temporary best-guess values for immediate synchronous access.

    +
  • +

    If newURL does not equal + activeEntry's URL with exclude fragments set to true, or if fragment directive is not null, + then set newEntry's directive state be a + new directive state whose value is + fragment directive.

    + +

    We limit diverging newEntry's directive state from that of activeEntry's to + these conditions, so that simple fragment changes to activeEntry's URL that don't specify a fragment directive continue to + reuse the existing one, as to not clobber existing highlights or other user interface + indications.

    +
  • + +
  • Otherwise, if fragment directive is not null, then set newEntry's + directive state's value to fragment directive.

  • +
  • If serializedData is not null, then restore the history object state given document and newEntry.

  • @@ -108881,8 +108975,14 @@ location.href = '#foo';
  • Set currentURL to locationURL.

  • +
  • Let fragment directive be the result of running remove the fragment + directive on locationURL.

  • +
  • Set entry's URL to currentURL.

  • + +
  • Set entry's directive state's value to fragment directive.

  • @@ -110329,6 +110429,19 @@ location.href = '#foo';
  • Let oldURL be document's latest entry's URL.

  • + +
  • If document's latest entry's directive state is not entry's directive state, then set document's + pending text directives to the result of parsing entry's directive + state's value.

  • +
  • Set document's latest entry to entry.

  • Restore the history object state given document and @@ -110911,6 +111024,37 @@ location.href = '#foo'; url with exclude fragments set to true, then return null.

  • +
  • Let text directives be document's pending text directives.

  • + +
  • +

    If text directives is non-null:

    + +
      +
    1. Let ranges be the result of running the invoke text directives + steps, given text directives and document.

    2. + +
    3. +

      If ranges is not empty:

      + +
        +
      1. Let firstRange by ranges[0].

      2. + +
      3. +

        Visually indicate each range in ranges in an + implementation-defined way. The indication must not be observable from author + script. See Text directive security and + privacy considerations.

        + +

        Only the first range in ranges gets scrolled into + view, but all ranges will be visually indicated to the user.

        +
      4. + +
      5. Return firstRange.

      6. +
      +
    4. +
    +
  • +
  • Let fragment be url's fragment.

  • @@ -114620,6 +114764,978 @@ new PaymentRequest(…); // Allowed to use
    + +

    URL fragment and text directives

    + +

    The core use case for text fragments is to allow URLs to serve as an exact text reference across the web. For example, Wikipedia references could link to the exact text they are quoting from a page. Similarly, search engines can serve URLs that direct the user to the answer they are looking for in the page rather than linking to the top of the page.

    + +

    With text directives, browsers may implement an option to 'Copy URL to here' when the user opens the context menu on a text selection. The browser can then generate a URL with the text selection appropriately specified, and the recipient of the URL will have the specified text conveniently indicated. Without text fragments, if a user wants to share a passage of text from a page, they would likely just copy and paste the passage, in which case the receiver loses the context of the page.

    + +

    Link lifetime

    + +

    This specification attempts to maximize the useful lifetime of text directive links, for example, by + using the actual text content as the URL payload, and allowing a fallback element-id fragment. + However, pages on the web often update and change their content. As such, links like this may "rot" + in that the text content they point to no longer exists on the destination page.

    + +

    Text directive links can be useful despite this problem. In user sharing use cases, the link is + often transient, intended to be used only within a short time of sending. For longer duration use + cases, such as references and web page links, text directives are still valuable since they degrade + gracefully into an ordinary link. Additionally, the presence of a stale text directive can be useful + information to surface to a user, to help them understand the link creator's original intent and + that the page content may have changed since the link was created.

    + + + See [[#generating-text-fragment-directives]] for best practices on how to create robust text + directive links. + +

    Other intro content??

    + +

    This section describes the mechanism by which the fragment directive is hidden from script and how it fits into [[HTML#navigation-and-session-history]].

    + +
      +
    1. Session history entries now include a new "directive state" item.

    2. + +
    3. All new entries are created with a directive state with an empty value. If the new URL + includes a fragment directive it will be written to the state's value (otherwise it remains + null).

    4. + +
    5. +

      Any time a URL potentially including a fragment directive is written to a session history + entry, extract the fragment directive from the URL and store it in a directive state item of the + entry. There are four such points where a URL can potentially include a directive:

      + +
        +
      1. In the "navigate" steps for typical cross-document navigations.

      2. + +
      3. In the "navigate to a fragment" steps for fragment based same-document + navigations.

      4. + +
      5. In the "URL and history update steps" for synchronous updates such as + pushState/replaceState.

      6. + +
      7. In the "create navigation params by fetching" steps for URLs coming from a + redirect.

      8. +
      +
    6. + +
    7. Same-document navigations that change only the fragment, and the new URL doesn't specify a + directive, will create an entry whose directive state refers to the previous entry's directive + state.

    8. +
    + +

    Since a Document is populated from a session history entry, its URL will not include the fragment directive. Similarly, + since the Location object is a representation of the active document's + URL, all getters on it will produce a + fragment-directive-stripped version of the URL.

    + +

    Additionally, since the HashChangeEvent is fired + in response to a changed fragment between URLs of session history entries, + hashchange will not be fired if a navigation or traversal + changes only the fragment directive.

    + +

    Consider the below examples, which help clarify various edge cases.

    + +
    +
    
    +window.location = "https://example.com#page1:~:hello";
    +console.log(window.location.href); // 'https://example.com#page1'
    +console.log(window.location.hash); // '#page1'
    +
    + +

    The initial navigation created a new session history entry. The entry's URL is stripped of the + fragment directive: "https://example.com#page1". The entry's directive state value is set to + "hello". Since the document is populated from the entry, web APIs don't include the fragment + directive in URLs.

    + +
    
    +location.hash = "page2";
    +console.log(location.href); // 'https://example.com#page2'
    +
    + +

    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.");
    +    location.hash = "page2:~:world";
    +    console.log(location.href); // 'https://example.com#page2'
    +    onhashchange = null;
    +
    + +

    A same document navigation changes only the fragment but includes a fragment directive. Since an + explicit directive was provided, the new entry includes its own directive state with a value of + "fizz".

    + +

    The hashchange event is not fired since the page-visible fragment is unchanged; only the fragment + directive changed. This is because the comparison for hashchange is done on the URLs in the + session history entries, where the fragment directive has been removed.

    + +
    
    +    history.pushState("", "", "page3");
    +    console.log(location.href); // 'https://example.com/page3'
    +
    + +

    pushState creates a new session history entry for the same document. However, since the + non-fragment URL has changed, this entry has its own directive state with value currently null.

    +
    + +
    +

    In other cases where a URL is not set to a session history entry, there is no fragment directive stripping.

    + +

    For URL objects:

    + +
    
    +let url = new URL('https://example.com#foo:~:bar');
    +console.log(url.href); // 'https://example.com#foo:~:bar'
    +console.log(url.hash); // '#foo:~:bar'
    +
    +document.url = url;
    +console.log(document.url.href); // 'https://example.com#foo:~:bar'
    +console.log(document.url.hash); // '#foo:~:bar'
    +
    + +

    The a or area elements:

    + +
    
    +<a id='anchor' href="https://example.com#foo:~:bar">Anchor</a>
    +<script>
    +  console.log(anchor.href); // 'https://example.com#foo:~:bar'
    +  console.log(anchor.hash); // '#foo:~:bar'
    +</script>
    +
    +
    + +

    Applying directives to a document

    + + + TODO: Should this contain content about "indicating a match"? + + + + + + + +

    Supporting concepts

    + +

    To avoid compatibility issues with usage of existing URL fragments, this spec introduces the concept of a fragment directive. It is the portion of the URL fragment that follows the fragment directive delimiter and may be null if the delimiter does not appear in the fragment.

    + +

    The fragment directive delimiter is the string ":~:", that is the three consecutive code points U+003A (:), U+007E (~), U+003A (:).

    + +

    The fragment directive is part of the URL fragment. This means it always appears after a U+0023 (#) code point in a URL.

    + +

    To add a fragment directive to a URL like "https://example.com", a fragment is first appended to the URL: "https://example.com#:~:text=foo".

    + +
    + +

    The fragment directive is parsed and processed into individual directives, which are + instructions to the user agent to perform some action. Multiple directives may appear in the fragment directive.

    + +

    The only directive introduced in this spec is the text directive but others could be added in the future.

    + +

    "https://example.com#:~:text=foo&text=bar&unknownDirective" Contains 2 text directives and one unknown directive.

    + +

    To prevent impacting page operation, it is stripped from script-accessible APIs to prevent interaction with author script. This also ensures future directives can be added without web compatibility risk.

    + +

    A text directive is a kind of directive representing a range of text to + be indicated to the user. It is a struct consisting of four strings:

    + +
    +
    start
    +

    a non-empty string

    + +
    end
    +

    null or a non-empty string

    + +
    prefix
    +

    null or a non-empty string

    + +
    suffix
    +

    null or a non-empty string

    +
    + +
    + +

    Each Document has a pending text directives which is either a + list of text directives or null, initially + null.

    + + +

    Syntax

    + + + +

    A text directive is specified in the fragment directive with the following format:

    + +
    +#:~:text=[prefix-,]start[,end][,-suffix]
    +          context  |--match--|  context
    +  
    + +

    (Square brackets indicate an optional parameter).

    + +

    The text parameters are percent-decoded before matching. Dash (-), ampersand + (&), and comma (,) characters in text parameters are percent-encoded to avoid + being interpreted as part of the text directive syntax.

    + +

    The only required parameter is "start". If only "start" is specified, then the first instance of this exact text string is the target text.

    + +

    "#:~:text=an%20example%20text%20fragment" indicates that the exact text "an example text fragment" is the target text.

    + +

    If the "end" parameter is also specified, then the text directive refers to a + range of text in the page. The target text range is the text range starting at + the first instance of "start", until the first instance of "end" that + appears after "start". This is equivalent to specifying the entire text range + in the "start" parameter, but allows the URL to avoid being bloated with a + long text directive.

    + +

    "#:~:text=an%20example,text%20fragment" indicates that the first instance of "an example" until the following first instance of "text fragment" is the target text.

    + +

    The other two optional parameters are context terms. They are specified by the + dash (-) character succeeding the prefix and preceding the suffix, to + differentiate them from the "start" and "end" parameters, as any + combination of optional parameters can be specified.

    + +

    Context terms are used to disambiguate the target text fragment. The context + terms can specify the text immediately before (prefix) and immediately after + (suffix) the text fragment, allowing for whitespace.

    + +

    While a match succeeds only if the context terms surround the target text + fragment, any amount of whitespace is allowed between context terms and the text + fragment. This allows context terms to cross element boundaries, for example if + the target text fragment is at the beginning of a paragraph and needs + disambiguation by the previous element's text as a prefix.

    + +

    The context terms are not part of the targeted text fragment and are not + visually indicated.

    + +

    "#:~:text=this%20is-,an%20example,-text%20fragment" would match + to "an example" in "this is an example text fragment", but not match to "an example" in "here is an example text".

    + + +

    Parsing and processing model

    + +

    To parse a text directive, on a string text directive value, + perform the following steps. They return a text directive-or-null.

    + +

    This algorithm takes a single text directive value string as input (e.g., + "prefix-,foo,bar") and attempts to parse the string into the components of the directive (e.g., + ("prefix", "foo", "bar", null)). See [[#syntax]] for the what each of these components means and + how they're used.

    + +
      +
    1. Let prefix, suffix, start, and end each be + null.

    2. + +
    3. Assert: text directive value is an ASCII string + with no code points in the fragment percent-encode set and no instances of U+0026 + AMPERSAND character (&).

    4. + +
    5. Let tokens be a list of strings that + result from strictly splitting text directive + value on U+002C (,).

    6. + +
    7. If tokens has size less than 1 or greater than 4, then return null.

    8. + +
    9. +

      If the first item of tokens ends with U+002D (-):

      + +
        +
      1. Set prefix to the substring of + tokens[0] from 0 with length tokens[0]'s length - 1.

      2. + +
      3. Remove the first item of tokens.

      4. + +
      5. If prefix is the empty string or contains any instances of U+002D (-), then return null.

      6. + +
      7. If tokens is empty, then return null.

      8. +
      +
    10. + +
    11. +

      If the last item of tokens starts with U+002D (-):

      + +
        +
      1. Set suffix to the substring of the last item of tokens from 1 to the end of the string.

      2. + +
      3. Remove the last item of tokens.

      4. + +
      5. If suffix is the empty string or contains any instances of U+002D (-), then return null.

      6. + +
      7. If tokens is empty, then return null.

      8. +
      +
    12. + +
    13. If tokens has size greater than 2, then return null.

    14. + +
    15. Assert: tokens has size 1 or 2.

    16. + +
    17. Set start to the first item in tokens.

    18. + +
    19. Remove the first item of tokens.

    20. + +
    21. If start is the empty string or contains any instances of U+002D (-), then return null.

    22. + +
    23. +

      If tokens is not empty:

      + +
        +
      1. Set end to the first item in tokens.

      2. + +
      3. If end is the empty string or contains any instances of U+002D (-), return null.

      4. +
      +
    24. + +
    25. +

      Return a new text directive, with

      + +
      +
      start
      +
      The percent-decoding of start
      + +
      end
      +
      The percent-decoding of end
      + +
      prefix
      +
      The percent-decoding of prefix
      + +
      suffix
      +
      The percent-decoding of suffix
      +
      +
    26. +
    + +

    To percent-decode a text directive term given an ASCII string-or-null term, perform the following steps. They return a string-or-null.

    + +
      +
    1. If term is null, then return null.

    2. + +
    3. Let decoded bytes be the result of percent-decoding term.

    4. + +
    5. Return the result of running UTF-8 decode without BOM on decoded bytes.

    6. +
    + + +

    To parse the fragment directive, given an ASCII string fragment directive, perform the following steps. They return a list of text directives parsed from fragment directive.

    + +
      +
    1. Let directives be the result of strictly splitting fragment directive on U+0026 AMPERSAND character (&).

    2. + +
    3. Let output be an empty list.

    4. + +
    5. +

      For each directive in directives:

      + +
        +
      1. If directive does not start with "text=", then continue

      2. . + +
      3. +

        Let text directive value be the code point substring from 5 to the end of directive.

        + +

        Note: this might be the empty string.

        +
      4. + +
      5. Let parsed text directive be the result of parsing text directive value.

      6. + +
      7. If parsed text directive is non-null, append it to output.

      8. +
      + +
    6. Return output.

    7. +
    + + + +Add a helper algorithm for removing and returning a fragment directive string from a URL: + +

    This algorithm makes a URL's fragment end at the fragment directive + delimiter. The returned fragment directive includes all characters that follow + the delimiter but does not include the delimiter.

    + +

    TODO: If a URL's fragment ends with ':~:' (i.e., empty directive), this will return null which is treated as the URL not specifying an explicit directive (and avoids clobbering an existing one. But maybe in this case we should return the empty string? That way a page can explicitly clear directives/highlights by navigating/pushState to '#:~:'.

    + +

    To remove the fragment directive from a URL url, run these steps:

    + +
      +
    1. Let raw fragment be equal to url's fragment.

    2. + +
    3. Let fragment directive be null.

    4. + +
    5. +

      If raw fragment is non-null and contains the fragment directive delimiter as a substring:

      + +
        +
      1. Let position be the position variable pointing to the first code + point of the first instance, if one exists, of the fragment directive delimiter in + raw fragment, or past the end of raw fragment otherwise.

      2. + +
      3. Let new fragment be the code point substring by positions of raw fragment from the start of raw fragment to position.

      4. + +
      5. Advance position by the code point length of the fragment directive delimiter.

      6. + +
      7. If position does not point past the end of raw fragment, then set fragment directive to the code point substring to the end of the string raw fragment starting from position.

      8. + +
      9. Set url's fragment to new fragment.

      10. +
      + +
    6. Return fragment directive.

    7. +
    + +
    +

    https://example.org/#test:~:text=foo will be parsed such that the fragment is the string "test" and the fragment directive is the string "text=foo".

    +
    + + +
    Finding and invoking text directives
    + +

    This section outlines several algorithms and definitions that specify how to turn a full fragment directive string into a list of Range objects.

    + +

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

    +
    
    +    text=prefix-,foo&unknown&text=bar,baz
    +  
    + +

    We break this up into the individual text directives:

    + +
    
    +    text=prefix-,foo
    +    text=bar,baz
    +  
    + +

    For each text directive, we perform a search in the document for the first + instance of rendered text that matches the restrictions in the directive. + Each search is independent of any others; that is, the result is the same + regardless of how many other directives are provided or their match result.

    + +

    If a directive successfully matches to text in the document, it returns a + range indicating the 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 list.

    + +

    To invoke text directives, given a list of text directives text directives and a Document + document, perform these steps:

    + +
      +
    1. Let ranges be a list of Range objects, initially + empty.

    2. + +
    3. For each directive of text + directives, if the result of running find a range from a text directive given + directive and document is non-null, then append directive to ranges.

    4. + +
    5. Return ranges.

    6. +
    + +
    + +

    Maybe describe what this next set of algorithms and primitives do.

    + +
    +

    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 + text passage within the document that matches the searched-for text and + satisfies the surrounding context. Returns null if no such passage exists.

    + + + [=text directive/end=] can be null. If omitted, this is an "exact" + search and the returned [=range=] will contain a string exactly matching + [=text directive/start=]. If [=text directive/end=] is + provided, this is a "range" search; the returned [=range=] will start with + [=text directive/start=] and end with + [=text directive/end=]. In the normative text below, we'll call a + text passage that matches the provided [=text directive/start=] and + [=text directive/end=], regardless of which mode we're in, the + "matching text". + + Either or both of [=text directive/prefix=] and + [=text directive/suffix=] can be null, in which case context on that + side of a match is not checked. E.g. If [=text directive/prefix=] is + null, text is matched without any requirement on what text precedes it. +
    +
    + While the matching text and its prefix/suffix can span across + block-boundaries, the individual parameters to these steps cannot. That is, + each of [=text directive/prefix=], [=text directive/start=], + [=text directive/end=], and [=text directive/suffix=] will only + match text within a single block. + +
    +
    :~:text=The quick,lazy dog
    will fail to match in + + ``` +
    The
    quick brown fox
    +
    jumped over the lazy dog
    + ``` + + because the starting string "The quick" does not appear within a single, + uninterrupted block. The instance of "The quick" in the document has a + block element between "The" and "quick". + + It does, however, match in this example: + + ``` +
    The quick brown fox
    +
    jumped over the lazy dog
    + ``` + +
    +
    + + DOMFAROLINO FROM HERE (EARLY) + +

    To find a range from a text directive, given a text directive parsedValues and Document document, run the following steps: + +

      +
    1. Let searchRange be a range with start + (document, 0) and end (document, document's length).

    2. + +
    3. + +

      While searchRange is not collapsed:

      + +
        +
      1. Let potentialMatch be null.

      2. + +
      3. +

        If parsedValues's prefix is not null:

        + +
          +
        1. Let prefixMatch be the result of running the find a string in + range steps with parsedValues's prefix, searchRange, true, false, and false.

        2. + +
        3. If prefixMatch is null, then return null.

        4. + +
        5. Set searchRange's start to the + first boundary point after prefixMatch's start.

        6. + +
        7. Let matchRange be a range whose start is + prefixMatch's end and end is + searchRange's end.

        8. + +
        9. Advance matchRange's start to the next + non-whitespace position.

        10. + +
        11. +

          If matchRange is collapsed return null.

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

          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. +
          +
        14. + +
        15. Let mustEndAtWordBoundary be true if parsedValues's end is non-null or parsedValues's suffix is null, false otherwise.

        16. + +
        17. Set potentialMatch to the result of running the find a string in + range steps with parsedValues's start, matchRange, false, mustEndAtWordBoundary, and true.

        18. + + +
        19. +

          If potentialMatch is null, then continue.

          + +
          + In this case, we found a prefix but it was followed by something other than a matching text so we'll + continue searching for the next instance of prefix. +
          +
        20. +
        +
      4. + +
      5. +

        Otherwise:

        + +
          +
        1. Let mustEndAtWordBoundary be true if parsedValues's end is non-null or parsedValues's suffix is null, false otherwise.

        2. + +
        3. Set potentialMatch to the result of running the find a string in + range steps with parsedValues's start, searchRange, + true, mustEndAtWordBoundary, and false.

        4. + +
        5. If potentialMatch is null, return null.

        6. + +
        7. Set searchRange's start to the + first boundary point after + potentialMatch's start.

        8. +
        +
      6. + +
      7. Let rangeEndSearchRange be a range whose start + is potentialMatch's end and whose end is + searchRange's end.

      8. + +
      9. +

        While rangeEndSearchRange is not collapsed:

        + +
          +
        1. +

          If parsedValues's end item is non-null, then:

          + +
            +
          1. Let mustEndAtWordBoundary be true if parsedValues's suffix is null, false otherwise.

          2. + +
          3. Let endMatch be the result of running the find a string in + range steps with parsedValues's end, rangeEndSearchRange, + true, mustEndAtWordBoundary, and false.

          4. + +
          5. If endMatch is null then return null.

          6. + +
          7. Set potentialMatch's end to endMatch's + end.

          8. +
          +
        2. + +
        3. Assert: potentialMatch is non-null, not + collapsed and represents a range exactly containing an instance of matching + text.

        4. + +
        5. If parsedValues's suffix is null, return + potentialMatch.

        6. + +
        7. Let suffixRange be a range with start equal + to potentialMatch's end and end equal to + searchRange's end.

        8. + +
        9. Advance suffixRange's start to the next + non-whitespace position.

        10. + +
        11. Let suffixMatch be result of running the find a string in range + steps with parsedValues's suffix, + suffixRange, false, true, and true.

        12. + +
        13. If suffixMatch is non-null, return potentialMatch.

        14. + +
        15. +

          If parsedValues's end is null and suffixMatch is null, then break.

          + +
          + If this is an exact match and the suffix doesn't match, start searching for the next range start by + breaking out of this loop without rangeEndSearchRange being collapsed. If we're looking + for a range match, we'll continue iterating this inner loop since the range start will already be + correct. +
          +
        16. + +
        17. +

          Set rangeEndSearchRange's start to + potentialMatch's end.

          + +
          + Otherwise, it is possible that we found the correct range start, but not the correct range end. + Continue the inner loop to keep searching for another matching instance of rangeEnd. +
          +
        18. +
        +
      10. + +
      11. +

        If rangeEndSearchRange is collapsed:

        + +
          +
        1. Assert: parsedValues's end item is + non-null.

        2. + +
        3. +

          Return null.

          + +
          + This can only happen for range matches due to the break for exact matches in + step 9 of the above loop. If we couldn't find a valid rangeEnd+suffix pair anywhere in the doc then + there's no possible way to make a match. +
          +
        4. +
        +
      12. +
      +
    4. + +
    5. Return null.

    6. +
    + +

    To advance a range range's start to the next non-whitespace position, run these steps:

    + +
      +
    1. +

      While range is not collapsed:

      + +
        +
      1. Let node be range's start node.

      2. + +
      3. Let offset be range's start offset.

      4. + +
      5. +

        If node is part of a non-searchable subtree or if node + is not a 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.

        2. + +
        3. Set range's start offset to 0.

        4. + +
        5. Continue.

        6. +
        +
      6. + +
      7. +

        If the Text/substring data of node at offset offset and count 6 is equal to the string "&nbsp;" then:

        + +
          +
        1. Add 6 to range's start offset.

        2. +
        +
      8. + +
      9. +

        Otherwise, if the Text/substring data of node at offset offset and count 5 is equal to the string "&nbsp" then:

        + +
          +
        1. Add 5 to range's start offset.

        2. +
        +
      10. + +
      11. +

        Otherwise:

        + +
          +
        1. Let cp be the code point at the offset index in node's CharacterData/data.

        2. + +
        3. If cp does not have the White_Space property set, then return.

        4. + +
        5. Add 1 to range's start offset.

        6. +
        +
      12. +
      +
    2. +
    + +

    To find a string in range given a string query, a + range searchRange, and three booleans wordStartBounded, + wordEndBounded and matchMustBeAtBeginning, run these steps:

    + + +

    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 or end at word boundaries (see [[#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.

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

    Will perform a search on "abc", then on "d", + then on "e". + +

    Thus, query will only match text that is continuous (i.e., uninterrupted by a block-level container) within a single block-level container.

    +
    + +
      +
    1. +

      While searchRange is not collapsed:

      + +
        +
      1. Let curNode be searchRange's start node.

      2. + +
      3. +

        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 descendant of curNode.

        2. + +
        3. Set searchRange's start offset to 0.

        4. + +
        5. Continue.

        6. +
        +
      4. + +
      5. +

        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.

        2. + +
        3. Set searchRange's start offset to 0.

        4. + +
        5. Continue.

        6. +
        +
      6. + +
      7. Let blockAncestor be the nearest block ancestor of curNode.

      8. + +
      9. Let textNodeList be a list of Text nodes, initially empty.

      10. + +
      11. +

        While curNode is a shadow-including descendant of + blockAncestor and the position of the boundary point + (curNode, 0) is not after + searchRange's end:

        + +
          +
        1. If curNode has block-level display, then break.

        2. + +
        3. +

          If curNode is search invisible:

          + +
            +
          1. Set curNode to the next node, in shadow-including tree order, that isn't a shadow-including descendant of curNode.

          2. + +
          3. Continue.

          4. +
          +
        4. + +
        5. If curNode is a visible text node then append it to textNodeList.

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

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

      14. + +
      15. If matchMustBeAtBeginning is true, return null.

      16. + +
      17. If curNode is null, then break.

      18. + +
      19. Assert: curNode follows searchRange's start node.

      20. + +
      21. Set searchRange's start to the boundary point (curNode, 0).

      22. +
      +
    2. + +
    3. Return null.

    4. +
    + + + domfarolino FROM HERE + + +

    UI considerations — indicating the text match

    + +

    This specification intentionally doesn't define what actions a user agent takes + to "indicate" a text match. There are different experiences and trade-offs a + user agent could make. Some examples of possible actions:

    + +
      +
    1. Providing visual emphasis or highlight of the text passage.

    2. + +
    3. Automatically scrolling the passage into view when the page is navigated.

    4. + +
    5. Activating a UA's find-in-page feature on the text passage.

    6. + +
    7. Providing a "Click to scroll to text passage" notification.

    8. + +
    9. Providing a notification when the text passage isn't found in the page.

    10. +
    + +

    The choice of action can have implications for user security and privacy. See the security and privacy sectionfor details.

    + +

    Security and privacy considerations

    + +

    Care must be taken when implementing text directives so + that it cannot be used to exfiltrate information across origins. Scripts can navigate a page to a + cross-origin URL with a text directive. If a malicious actor can determine that the + text fragment was successfully found in victim page as a result of such a navigation, they can + infer the existence of any text on the page.

    + +

    This section describes some of the attacks that could be executed with the help of text directives, and the navigation processing model changes that + restrict this feature to mitigate said attacks. In summary, text directives are restricted to:

    + + + +
    Scroll on navigation
    + +

    A UA may choose to automatically scroll a matched text passage into view. This can be a convenient experience for the user but does present some risks that implementing UAs need to be aware of.

    + +

    There are known (and potentially unknown) ways a scroll on navigation might be detectable and distinguished from natural user scrolls.

    + +
    +

    An origin embedded in an iframe in the target page registers an IntersectionObserver and determines in the first 500ms of page load whether a scroll has occurred. This scroll can be indicative of whether the text fragment was successfully found on the page.

    +
    + +
    +

    Two users share the same network on which traffic is visible between them. A malicious user sends the victim a link with a text fragment to a page. The searched-for text appears nearby to a resource located on a unique (on the page) domain. The attacker might be able to infer the success or failure of the fragment search based on the order of requests for DNS lookup.

    +
    + +
    +

    An attacker sends a link to a victim, sending them to a page that displays a private token. The attacker asks the victim to read back the token. Using a text fragment, the attacker gets the page to load for the victim such that warnings about keeping the token secret are scrolled out of view.

    +
    + +

    All known cases like this rely on specific circumstances about the target page so don't apply generally. With additional restrictions about when the text fragment can invoke an attacker is further restricted. Nonetheless, different UAs can come to different conclusions about whether these risks are acceptable. UAs need to consider these factors when determining whether to scroll as part of navigating to a text fragment.

    + +

    Conforming UAs may choose not to scroll automatically on navigation. Such UAs may, instead, provide UI to initiate the scroll ("click to scroll") or none at all. In these cases UA should provide some indication to the user that an indicated passage exists further down on the page.

    + +

    The examples above illustrate that in specific circumstances, it can be possible for an attacker to extract 1 bit of information about content on the page. However, care must be taken so that such opportunities cannot be exploited to extract arbitrary content from the page by repeating the attack. For this reason, restrictions based on user activation and browsing context isolation are very important and must be implemented.

    + +
    +

    Browsing context isolation ensures that no other document can script the target document which helps reduce the attack surface. However, it also ensures any malicious use is difficult to hide. A browsing context that's the only one in a group will be a top level browsing context (i.e. a full tab/window).

    +
    + +

    If a UA does choose to scroll automatically, it must ensure no scrolling is performed while the document is in the background (for example, in an inactive tab). This ensures any malicious usage is visible to the user and prevents attackers from trying to secretly automate a search in background documents.

    + +

    If a UA chooses not to scroll automatically, it must scroll a fallback element-id into view, if provided, regardless of whether a text fragment was matched. Not doing so would allow detecting the text fragment match based on whether the element-id was scrolled.

    + +
    Search timing
    + +

    A naive implementation of the text search algorithm could allow information exfiltration based on runtime duration differences between a matching and non- matching query. If an attacker could find a way to synchronously navigate to a text directive-invoking URL, they would be able to determine the existence of a text snippet by measuring how long the navigation call takes.

    + +
    + +

    The restrictions in [[#restricting-the-text-fragment]] prevent this specific case; in particular, the no-same-document-navigation restriction. However, these restrictions are provided as multiple layers of defence.

    +
    + +

    For this reason, the implementation must ensure the runtime of [[#navigating-to-text-fragment]] steps does not differ based on whether a match has been successfully found.

    + + +

    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.

    + + TODO: Consider another section describing the normative, algorithmic changes made elsewhere that support the security and privacy considerations discussed here, but do not include those changes here obviously. + + + + + + +

    The `Refresh` header

    The `Refresh` HTTP response header is the HTTP-equivalent From ae6e6b056915b9a6dcc57da290e5fe92a4fdf7b4 Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Sun, 9 Nov 2025 10:54:18 +0900 Subject: [PATCH 2/7] More upstreaming and fixes --- source | 604 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 556 insertions(+), 48 deletions(-) diff --git a/source b/source index c399848d804..cb04f081fe0 100644 --- a/source +++ b/source @@ -3302,8 +3302,9 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute

  • The first child, last child, next sibling, - previous sibling, and - parent concepts
  • + previous sibling, + parent, and + following concepts
  • The parent element concept
  • The document element concept
  • The in a document tree, in a document (legacy), and connected concepts
  • @@ -3331,7 +3332,8 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
  • The local name concept for attributes
  • The attribute list concept
  • The data of a CharacterData node and its - replace data algorithm
  • + replace data and + substring data algorithms
  • The child text content of a node
  • The descendant text content of a node
  • The name, @@ -3392,9 +3394,12 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute start node, start offset, start, + end node, + end offset, end, collapsed, - boundary point, and + boundary point, + node, and after as applied to ranges.
  • The create an element algorithm
  • @@ -114764,30 +114769,56 @@ new PaymentRequest(…); // Allowed to use
    - -

    URL fragment and text directives

    +

    Text directives and URL fragments

    -

    The core use case for text fragments is to allow URLs to serve as an exact text reference across the web. For example, Wikipedia references could link to the exact text they are quoting from a page. Similarly, search engines can serve URLs that direct the user to the answer they are looking for in the page rather than linking to the top of the page.

    +

    Introduction

    -

    With text directives, browsers may implement an option to 'Copy URL to here' when the user opens the context menu on a text selection. The browser can then generate a URL with the text selection appropriately specified, and the recipient of the URL will have the specified text conveniently indicated. Without text fragments, if a user wants to share a passage of text from a page, they would likely just copy and paste the passage, in which case the receiver loses the context of the page.

    + + +

    Text directives add support for specifying a text snippet + in a URL fragment. When navigating to a URL whose fragment contains a text directive, the user + agent can quickly emphasize the captured text on the page, bringing it to the user's + attention.

    + +

    The core use case for text directives is to allow URLs to + serve as an exact text reference across the web. For example, Wikipedia URLs might link to the + exact text they are quoting from a page. Similarly, search engines can serve URLs that embed text directives that direct the user to the content they are + looking for in the page, rather than just linking to the top of the page.

    + +

    This section defines how text directives are parsed, constructed, and "invoked", which is the process of finding the text encapsulated + by the directive, in a document.

    + +

    With text directives, browsers may implement an option to + "Copy URL to this text" when the user engages with various UI components, such as a context menu + referring to a text selection. The browser can then generate a URL with the text selection appropriately + specified, and the recipient of the URL will have the specified text conveniently indicated. + Without text directives, if a user wants to share a passage of text from a page, they would likely + just copy and paste the passage, in which case the recipient loses the context of the page.

    Link lifetime

    -

    This specification attempts to maximize the useful lifetime of text directive links, for example, by - using the actual text content as the URL payload, and allowing a fallback element-id fragment. - However, pages on the web often update and change their content. As such, links like this may "rot" - in that the text content they point to no longer exists on the destination page.

    + -

    Text directive links can be useful despite this problem. In user sharing use cases, the link is - often transient, intended to be used only within a short time of sending. For longer duration use - cases, such as references and web page links, text directives are still valuable since they degrade - gracefully into an ordinary link. Additionally, the presence of a stale text directive can be useful +

    However, pages on the web often update and change their content. As such, text + directive links may "rot" in that the text content they point to no longer exists on the + destination page. This specification attempts to maximize the useful lifetime of text + directive links by using the actual text content as the URL payload, and allowing a + fallback element-id fragment.

    + +

    In user sharing use cases, the link is often transient, intended to be used only within a short + time of sending. For longer duration use cases, such as references and web page links, text directives are still valuable since they degrade gracefully + into an ordinary link. Additionally, the presence of a stale text directive can be useful information to surface to a user, to help them understand the link creator's original intent and that the page content may have changed since the link was created.

    - - See [[#generating-text-fragment-directives]] for best practices on how to create robust text - directive links. +

    See the Generating text fragment directives + section for best practices on how to create robust text directive links.

    Other intro content??

    @@ -114912,11 +114943,107 @@ console.log(document.url.hash); // '#foo:~:bar'

    Applying directives to a document

    +

    This specification intentionally doesn't define what actions a user agent takes + to "indicate" a text match. There are different experiences and trade-offs a + user agent could make. Some examples of possible actions:

    + +
      +
    1. Providing visual emphasis or highlight of the text passage.

    2. + +
    3. Automatically scrolling the passage into view when the page is navigated.

    4. + +
    5. Activating a UA's find-in-page feature on the text passage.

    6. + +
    7. Providing a "Click to scroll to text passage" notification.

    8. + +
    9. Providing a notification when the text passage isn't found in the page.

    10. +
    + +

    The choice of action can have implications for user security and privacy. See the security and privacy section for details.

    + +
    + +

    The UA may choose to scroll the text fragment into view as part of the try to scroll to the fragment steps or by some other mechanism; + however, it is not required to scroll the match into view.

    + +

    The UA should visually indicate the matched text in some way such that the user + is made aware of the text match, such as with a high-contrast highlight.

    - TODO: Should this contain content about "indicating a match"? +

    The UA should provide to the user some method of dismissing the match, such + that the matched text no longer appears visually indicated.

    +

    The exact appearance and mechanics of the indication are left as UA-defined. + However, the UA must not use any methods observable by author script, such as + the Document's selection, to indicate the text match. Doing so could allow attack vectors + for content exfiltration.

    +

    The UA must not visually indicate any provided context terms.

    +

    Since the indicator is not part of the document's content, UAs should consider + ways to differentiate it from the page's content as perceived by the user.

    + +

    The UA could provide an in-product help prompt the first few times the indicator appears to help train the user that it comes from the linking page and is provided by the UA.

    + +
    URLs in UA features
    + +

    UAs provide a number of consumers for a document's URL (outside of programmatic + APIs like window.location). Examples include a location bar + indicating the URL of the currently visible document, or the URL used when a + user requests to create a bookmark for the current page.

    + +

    To avoid user confusion, UAs should be consistent in whether such URLs include + the fragment directive. This section provides a default set of + recommendations for how UAs can handle these cases.

    + +
    +

    We provide these as a baseline for consistent behavior; however, as these features don't affect cross-UA interoperability, they are not strict conformance requirements.

    + +

    Exact behavior is left up to the implementing UA which can have differing constraints or reasons for modifying the behavior. e.g. UAs can allow users to configure defaults or expose UI options so users can choose whether they prefer to include fragment directives in these URLs.

    + +

    It's also useful to allow UAs to experiment with providing a better experience. E.g. perhaps the UA's displayed URL can elide the text fragment if the user scrolls it out of view?

    +
    + +

    The general principle is that a URL should include the fragment directive only while the visual indicator is visible (i.e., not dismissed). If the user dismisses the indicator, then the URL should reflect this by removing the fragment directive.

    + +

    If the URL includes a text fragment but a match wasn't found in the current page, the UA may choose to omit it from the exposed URL.

    + +
    +

    A text fragment that isn't found on the page can be useful information to surface to a user to indicate that the page has changed since the link was created.

    + +

    However, it's unlikely to be useful to the user in a bookmark.

    +
    + +

    A few common examples are provided in the subsections below.

    + +

    We use "text fragment" and "fragment directive" interchangeably here as text fragments are assumed to be the only kind of directive. If additional directives are added in the future, the UX in these cases will have to be re-evaluated separately for new directive types.

    + +
    Location bar
    + +

    The location bar's URL should include a text fragment while it is visually + indicated. The fragment directiveshould be stripped from the location bar + URL when the user dismisses the indication.

    + +

    It is recommended that the text fragment be displayed in the location bar's URL + even if a match wasn't located in the document.

    + +
    Bookmarks
    + +

    Many UAs provide a "bookmark" feature allowing users to store a convenient link to the current page in the UA's interface.

    + +

    A newly created bookmark should, by default, include the fragment directive + in the URL if, and only if, a match was found and the visual indicator hasn't + been dismissed.

    + +

    Navigating to a URL from a bookmark should process a fragment directive as + if it were navigated to in a typical navigation.

    + +
    Sharing
    + +

    Some UAs provide a method for users to share the current page with others, + typically by providing the URL to another app or messaging service.

    + +

    When providing a URL in these situations, it should include the fragment directive if, and only if, a match was found and the visual indicator hasn't + been dismissed.

    @@ -114987,12 +115114,12 @@ console.log(document.url.hash); // '#foo:~:bar'

    "#:~:text=an%20example%20text%20fragment" indicates that the exact text "an example text fragment" is the target text.

    -

    If the "end" parameter is also specified, then the text directive refers to a - range of text in the page. The target text range is the text range starting at - the first instance of "start", until the first instance of "end" that - appears after "start". This is equivalent to specifying the entire text range - in the "start" parameter, but allows the URL to avoid being bloated with a - long text directive.

    +

    If the "end" parameter is also specified, then the text directive refers + to a range of text in the page. The target text range is the text range starting at the first + instance of "start", until the first instance of "end" that appears after "start". This is equivalent to + specifying the entire text range in the "start" parameter, but allows the + URL to avoid being bloated with a long text directive.

    "#:~:text=an%20example,text%20fragment" indicates that the first instance of "an example" until the following first instance of "text fragment" is the target text.

    @@ -115020,6 +115147,9 @@ console.log(document.url.hash); // '#foo:~:bar'

    Parsing and processing model

    +
    Parsing
    + +

    To parse a text directive, on a string text directive value, perform the following steps. They return a text directive-or-null.

    @@ -115109,7 +115239,9 @@ console.log(document.url.hash); // '#foo:~:bar' +
    +

    To percent-decode a text directive term given an ASCII string-or-null term, perform the following steps. They return a string-or-null.

      @@ -115119,8 +115251,9 @@ console.log(document.url.hash); // '#foo:~:bar'
    1. Return the result of running UTF-8 decode without BOM on decoded bytes.

    +
    - +

    To parse the fragment directive, given an ASCII string fragment directive, perform the following steps. They return a list of text directives parsed from fragment directive.

      @@ -115147,6 +115280,7 @@ console.log(document.url.hash); // '#foo:~:bar'
    1. Return output.

    +
    @@ -115158,6 +115292,7 @@ Add a helper algorithm for removing and returning a fragment directive string fr

    TODO: If a URL's fragment ends with ':~:' (i.e., empty directive), this will return null which is treated as the URL not specifying an explicit directive (and avoids clobbering an existing one. But maybe in this case we should return the empty string? That way a page can explicitly clear directives/highlights by navigating/pushState to '#:~:'.

    +

    To remove the fragment directive from a URL url, run these steps:

      @@ -115184,13 +115319,12 @@ Add a helper algorithm for removing and returning a fragment directive string fr
    1. Return fragment directive.

    +
    -
    -

    https://example.org/#test:~:text=foo will be parsed such that the fragment is the string "test" and the fragment directive is the string "text=foo".

    -
    +

    https://example.org/#test:~:text=foo will be parsed such that the fragment is the string "test" and the fragment directive is the string "text=foo".

    -
    Finding and invoking text directives
    +
    Finding and invoking text directives

    This section outlines several algorithms and definitions that specify how to turn a full fragment directive string into a list of Range objects.

    @@ -115219,6 +115353,7 @@ Add a helper algorithm for removing and returning a fragment directive string fr

    If a directive was not matched, it does not add an item to the returned list.

    +

    To invoke text directives, given a list of text directives text directives and a Document document, perform these steps:

    @@ -115234,6 +115369,7 @@ Add a helper algorithm for removing and returning a fragment directive string fr
  • Return ranges.

  • +

    @@ -115292,6 +115428,7 @@ Add a helper algorithm for removing and returning a fragment directive string fr DOMFAROLINO FROM HERE (EARLY) +

    To find a range from a text directive, given a text directive parsedValues and Document document, run the following steps:

      @@ -115470,7 +115607,9 @@ Add a helper algorithm for removing and returning a fragment directive string fr
    1. Return null.

    +
    +

    To advance a range range's start to the next non-whitespace position, run these steps:

      @@ -115497,7 +115636,7 @@ Add a helper algorithm for removing and returning a fragment directive string fr
    1. -

      If the Text/substring data of node at offset offset and count 6 is equal to the string "&nbsp;" then:

      +

      If the substring data of node at offset offset and count 6 is equal to the string "&nbsp;" then:

      1. Add 6 to range's start offset.

      2. @@ -115505,7 +115644,7 @@ Add a helper algorithm for removing and returning a fragment directive string fr
      3. -

        Otherwise, if the Text/substring data of node at offset offset and count 5 is equal to the string "&nbsp" then:

        +

        Otherwise, if the substring data of node at offset offset and count 5 is equal to the string "&nbsp;" then:

        1. Add 5 to range's start offset.

        2. @@ -115516,7 +115655,7 @@ Add a helper algorithm for removing and returning a fragment directive string fr

          Otherwise:

            -
          1. Let cp be the code point at the offset index in node's CharacterData/data.

          2. +
          3. Let cp be the code point at the offset index in node's data.

          4. If cp does not have the White_Space property set, then return.

          5. @@ -115526,7 +115665,9 @@ Add a helper algorithm for removing and returning a fragment directive string fr
        +
    +

    To find a string in range given a string query, a range searchRange, and three booleans wordStartBounded, wordEndBounded and matchMustBeAtBeginning, run these steps:

    @@ -115576,7 +115717,7 @@ Add a helper algorithm for removing and returning a fragment directive string fr

    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.

    2. +
    3. Set searchRange's start node to the next node, in shadow-including tree order, that is not a DocumentType node.

    4. Set searchRange's start offset to 0.

    5. @@ -115622,7 +115763,7 @@ Add a helper algorithm for removing and returning a fragment directive string fr
    6. If curNode is null, then break.

    7. -
    8. Assert: curNode follows searchRange's start node.

    9. +
    10. Assert: curNode is following searchRange's start node.

    11. Set searchRange's start to the boundary point (curNode, 0).

    @@ -115630,30 +115771,396 @@ Add a helper algorithm for removing and returning a fragment directive string fr
  • Return null.

  • +
    +

    A node is search invisible if it is an element in the HTML namespace and meets any of the following conditions:

    - domfarolino FROM HERE + + +

    A node is part of a non-searchable subtree if it is or has a shadow-including ancestor that is search invisible.

    + +

    A node is a visible text node if it is a Text node, the computed value of its parent element's 'visibility' property is 'visible', and it is being rendered. + +

    A node has block-level display if it is an element and the computed value of its 'display' property is any of 'block', 'table', 'flow-root', 'grid', 'flex', 'list-item'.

    + +
    +

    To find the nearest block ancestor of a node follow the steps:

      -
    1. Providing visual emphasis or highlight of the text passage.

    2. +
    3. Let curNode be node.

    4. -
    5. Automatically scrolling the passage into view when the page is navigated.

    6. +
    7. +

      While curNode is non-null.

      -
    8. Activating a UA's find-in-page feature on the text passage.

    9. +
        +
      1. If curNode is not a Text node and it has block-level display then return curNode.

      2. -
      3. Providing a "Click to scroll to text passage" notification.

      4. +
      5. Otherwise, set curNode to curNode's parent.

      6. +
      + -
    10. Providing a notification when the text passage isn't found in the page.

    11. +
    12. Return node's node document's document element.

    +
    + + +
    +

    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, wordEndBounded and matchMustBeAtBeginning, follow these steps: + +

      +
    1. +

      Let searchBuffer be the concatenation of the data of each item in nodes.

      + +

      data is not correct here since that's the text data as it + exists in the DOM. This algorithm means to run over the text as rendered (and then convert back to + Ranges in the DOM). See this issue.

      +
    2. + +
    3. Let searchStart be 0.

    4. + +
    5. If the first item in nodes is searchRange's start node, then set searchStart to searchRange's start offset.

    6. + +
    7. Let start and end be boundary points, initially null.

    8. + +
    9. Let matchIndex be null.

    10. -

      The choice of action can have implications for user security and privacy. See the security and privacy sectionfor details.

      +
    11. +

      While matchIndex is null.

      + +
        +
      1. +

        Set matchIndex to the index of the first instance of queryString in searchBuffer, starting at searchStart. The string search must be performed using a base character comparison, or the primary level, as defined in [[!UTS10]].

        + +

        Note: Intuitively, this is a case-insensitive search also ignoring accents, umlauts, and other marks.

        +
      2. + +
      3. If matchIndex is null, return null.

      4. + +
      5. If matchMustBeAtBeginning is true and matchIndex is not 0, return null.

      6. + +
      7. +

        Let endIx be matchIndex + queryString's length.

        + +

        Note: endIx is the index of the last character in the match + 1.

        +
      8. + +
      9. Set start to the boundary point result of get boundary point at index matchIndex run over nodes with isEnd false.

      10. + +
      11. Set end to the boundary point result of get boundary point at index endIx run over nodes with isEnd true.

      12. + +
      13. +

        If wordStartBounded is true and matchIndex is not at a word boundary in searchBuffer, given the language from start's node as the locale; or wordEndBounded is true and matchIndex + queryString's length is not at a word boundary in searchBuffer, given the language from end's boundary point/node as the locale:

        + +
          +
        1. Set searchStart to matchIndex + 1.

        2. + +
        3. Set matchIndex to null.

        4. +
        +
      14. +
      +
    12. + +
    13. Let endInset be 0.

    14. + +
    15. +

      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.

      +
    16. + +
    17. +

      If matchIndex + queryString's length is greater than searchBuffer's length − endInset return null.

      + +
      If the match runs past the end of the search range, return null.
      +
    18. + +
    19. Assert: start and end are non-null, valid boundary points in searchRange.

    20. + +
    21. Return a range with start start and end end.

    22. +
    +
    + +
    +

    Optionally, this will only return a match if the matched text begins or ends on a word boundary. For example, the query string "range" will always match in "mountain range", but:

    + +
      +
    1. When requiring a word boundary at the beginning, it will not match in "color orange".

    2. + +
    3. When requiring a word boundary at the end, it will not match in "forest ranger".

    4. +
    + +

    See [[#word-boundaries]] for details and more examples.

    +
    + +

    Optionally, this will only return a match if the matched text is at the beginning of the node list.

    + +
    +

    To get boundary point at index, given an integer index, list of Text nodes nodes, and a boolean isEnd, follow these steps:

    + +
    +

    This is a small helper routine used by the steps above to determine which node a given index in the concatenated string belongs to.

    +

    isEnd is used to differentiate start and end indices. An end index points to the "one-past-last" character of the matching string. If the match ends at node boundary, we want the end offset to remain within that node, rather than the start of the next node.

    +
    + +
      +
    1. Let counted be 0.

    2. + +
    3. +

      For each curNode of nodes:

      + +
        +
      1. Let nodeEnd be counted + curNode's length.

      2. + +
      3. If isEnd is true, add 1 to nodeEnd.

      4. + +
      5. If nodeEnd is greater than index, then return the boundary point (curNode, indexcounted).

      6. + +
      7. Increment counted by curNode's length.

      8. +
      +
    4. + +
    5. Return null.

    6. +
    +
    + + +
    Word boundaries
    + +

    Limiting matching to word boundaries is one of the mitigations to limit cross-origin information leakage.

    + +

    See Intl.Segmenter, + a proposal to specify unicode segmentation, including word segmentation. Once specified, this + algorithm can be improved by making use of the Intl.Segmenter API for word boundary matching.

    + + +

    A word boundary is defined in [[!UAX29]] in [[UAX29#Word_Boundaries]]. + [[UAX29#Default_Word_Boundaries]] defines a default set of what constitutes a word boundary, but + as the specification mentions, a more sophisticated algorithm should be used based on the + locale.

    + +

    Dictionary-based word bounding should take specific care in locales without a word-separating + character. E.g. In English, words are separated by the space character (' '); however, in Japanese + there is no character that separates one word from the next. In such cases, and where the alphabet + contains fewer than 100 characters, the dictionary must not contain more than 20% of the alphabet + as valid, one-letter words.

    + +

    A locale is a string containing a valid [[BCP47]] language tag, or + the empty string. An empty string indicates that the primary language is unknown.

    + +
    +

    A substring is word bounded in a string text, given + locales startLocale and endLocale, if both the + position of its first character is at a word boundary given startLocale, + and the position after its last character is at a word boundary given + endLocale.

    +
    + +
    +

    A number position is at a word boundary in a string + text, given a locale locale, if, using locale, + either a word boundary immediately precedes the positionth code unit, or + text's length is more than 0 and position equals either 0 or + text's length.

    +
    + +
    +

    Intuitively, a substring is word bounded if it neither begins nor ends in the + middle of a word.

    + +

    In languages with a word separator (e.g., " " space) this is (mostly) straightforward; though + there are details covered by the above technical reports such as new lines, hyphenations, quotes, + etc.

    + +

    Some languages do not have such a separator (notably, Chinese/Japanese/Korean). Languages such + as these requires dictionaries to determine what a valid word in the given locale is.

    +
    + +
    +

    Text fragments are restricted such that match terms, when combined with their adjacent context + terms, are word bounded. For example, in an exact search like prefix,start,suffix, "prefix+start+suffix" will match + only if the entire result is word bounded. However, in a range search like prefix,start,end,suffix, a match is found only if both "prefix+start" and "end+suffix" are word bounded.

    + +

    The goal is that a third-party must already know the full tokens they are matching against. A + range match like start,end must be word bounded on the inside of the two + terms; otherwise a third party could use this repeatedly to try and reveal a token (e.g., on a + page with "Balance: 123,456 $", a third-party could set prefix="Balance: ", end="$" and vary start to try and + guess the numeric token one digit at a time).

    + +

    For more details, refer to the Security + Review Doc.

    +
    + +
    The substring "mountain range" is word bounded within the string "An impressive mountain range" but not within "An impressive + mountain ranger".
    + +
    In the Japanese string "ウィキペディアへようこそ" + (Welcome to Wikipedia), "ようこそ" (Welcome) is considered word-bounded + but "ようこ" is not.
    + + + + + + + + + + + + + + +

    Generating text fragment directives

    + + + +

    This section contains recommendations for UAs automatically generating URLs with a text + directive. These recommendations aren't normative but are provided to ensure generated URLs + result in maximally stable and usable URLs.

    + +
    Prefer exact matching to range-based
    + +

    The match text can be provided either as an exact string "text=foo%20bar%20baz" or as a range "text=foo,bar".

    + +

    Prefer to specify the entire string where practical. This ensures that if the destination page + is removed or changed, the intended destination can still be derived from the URL itself.

    + +
    + Suppose we wish to craft a URL to + https://en.wikipedia.org/wiki/History_of_computing quoting the sentence: + +
    +      The first recorded idea of using digital electronics for computing was the
    +      1931 paper "The Use of Thyratrons for High Speed Automatic Counting of
    +      Physical Phenomena" by C. E. Wynn-Williams.
    +    
    + + We could create a range-based match like so: + + + https://en.wikipedia.org/wiki/History_of_computing#:~:text=The%20first%20recorded,Williams + + Or we could encode the entire sentence using an exact match term: + + + https://en.wikipedia.org/wiki/History_of_computing#:~:text=The%20first%20recorded%20idea%20of%20using%20digital%20electronics%20for%20computing%20was%20the%201931%20paper%20%22The%20Use%20of%20Thyratrons%20for%20High%20Speed%20Automatic%20Counting%20of%20Physical%20Phenomena%22%20by%20C.%20E.%20Wynn-Williams + + The range-based match is less stable, meaning that if the page is changed to + include another instance of "The first recorded" somewhere earlier in the + page, the link will now target an unintended text snippet. + + The range-based match is also less useful semantically. If the page is + changed to remove the sentence, the user won't know what the intended + target was. In the exact match case, the user can read, or the UA can + surface, the text that was being searched for but not found. +
    + +

    Range-based matches can be helpful when the quoted text is excessively long and encoding the entire string would produce an unwieldy URL.

    + +

    Text snippets shorter than 300 characters are encouraged to be encoded using an exact match. Above this limit, the UA can encode the string as a range-based match.

    + +

    TODO: Can we determine the above limit in some less arbitrary way?

    + +
    Use context only when necessary
    + +

    Context terms allow the text directive to disambiguate text + snippets on a page. However, their use can make the URL more brittle in some + cases. Often, the desired string will start or end at an element boundary. The + context will therefore exist in an adjacent element. Changes to the page + structure could invalidate the text directive since the context and + match text will no longer appear to be adjacent.

    + +
    +

    Suppose we wish to craft a URL for the following text:

    + +
    +      <div class="section">HEADER</div>
    +      <div class="content">Text to quote</div>
    +    
    + + We could craft the text directive as follows: + +
    +      text=HEADER-,Text%20to%20quote
    +    
    + + However, suppose the page changes to add a "[edit]" link beside all section + headers. This would now break the URL. +
    + +

    Where a text snippet is long enough and unique, a UAs are encouraged to avoid adding superfluous context terms.

    + +

    Use context only if one of the following is true:

    + + + +

    TODO: Determine the numeric limit above in less arbitrary way.

    + +
    Determine if fragment ID is needed
    + +

    When the UA navigates to a URL containing a text directive, it will fallback to + scrolling into view a regular element-id based fragment if it exists and the text fragment isn't + found.

    + +

    This can be useful to provide a fallback, in case the text in the document changes, invalidating the text directive.

    + +
    +

    Suppose we wish to craft a URL to https://en.wikipedia.org/wiki/History_of_computing quoting the sentence:

    + +
    +      The earliest known tool for use in computation is the Sumerian abacus
    +    
    + +

    By specifying the section that the text appears in, we ensure that, if the text is changed or removed, the user will still be pointed to the relevant section:

    + + + https://en.wikipedia.org/wiki/History_of_computing#Early_computation:~:text=The%20earliest%20known%20tool%20for%20use%20in%20computation%20is%20the%20Sumerian%20abacus +
    + +

    However, UAs should take care that the fallback element-id fragment is the correct one:

    + +
    +

    Suppose the user navigates to https://en.wikipedia.org/wiki/History_of_computing#Early_computation. They now scroll down to the Symbolic Computations section. There, they select a text snippet and choose to create a URL to it:

    + +
    +      By the late 1960s, computer systems could perform symbolic algebraic
    +      manipulations
    +    
    + +

    Even though the current URL of the page is: + https://en.wikipedia.org/wiki/History_of_computing#Early_computation, using + #Early_computation as a fallback is inappropriate. If the above sentence is + changed or removed, the page will load in the #Early_computation section + which could be quite confusing to the user.

    + +

    If the UA cannot reliably determine an appropriate fragment to fallback to, it should remove the fragment id from the URL:

    + + + https://en.wikipedia.org/wiki/History_of_computing#:~:text=By%20the%20late%201960s,%20computer%20systems%20could%20perform%20symbolic%20algebraic%20manipulations +
    + + + + domfarolino FROM HERE

    Security and privacy considerations

    @@ -115728,8 +116235,9 @@ Add a helper algorithm for removing and returning a fragment directive string fr

    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.

    - TODO: Consider another section describing the normative, algorithmic changes made elsewhere that support the security and privacy considerations discussed here, but do not include those changes here obviously. +
    Restricting the text fragment
    +
    Restricting scroll on load
    From fc625ba96eb33c612205b817984adcce79f2716f Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Sun, 9 Nov 2025 11:20:55 +0900 Subject: [PATCH 3/7] Clean up more intro content and examples --- source | 71 ++++++++++++++++++++++++---------------------------------- 1 file changed, 29 insertions(+), 42 deletions(-) diff --git a/source b/source index cb04f081fe0..17a09e710aa 100644 --- a/source +++ b/source @@ -114804,11 +114804,11 @@ new PaymentRequest(…); // Allowed to use -

    However, pages on the web often update and change their content. As such, text - directive links may "rot" in that the text content they point to no longer exists on the - destination page. This specification attempts to maximize the useful lifetime of text - directive links by using the actual text content as the URL payload, and allowing a - fallback element-id fragment.

    +

    Page on the web often update and change their content. As such, text directive + links may "rot" in that the text content they point to no longer exists on the destination page. + This specification attempts to maximize the useful lifetime of text directive links + by using the actual text content as the URL payload, and allowing a fallback element-id + fragment.

    In user sharing use cases, the link is often transient, intended to be used only within a short time of sending. For longer duration use cases, such as references and web page links, See the Generating text fragment directives section for best practices on how to create robust text directive links.

    -

    Other intro content??

    +

    Exposure to script

    -

    This section describes the mechanism by which the fragment directive is hidden from script and how it fits into [[HTML#navigation-and-session-history]].

    - -
      -
    1. Session history entries now include a new "directive state" item.

    2. - -
    3. All new entries are created with a directive state with an empty value. If the new URL - includes a fragment directive it will be written to the state's value (otherwise it remains - null).

    4. - -
    5. -

      Any time a URL potentially including a fragment directive is written to a session history - entry, extract the fragment directive from the URL and store it in a directive state item of the - entry. There are four such points where a URL can potentially include a directive:

      - -
        -
      1. In the "navigate" steps for typical cross-document navigations.

      2. - -
      3. In the "navigate to a fragment" steps for fragment based same-document - navigations.

      4. + -
      5. In the "URL and history update steps" for synchronous updates such as - pushState/replaceState.

      6. +

        This section describes how fragment directives are + exposed to script and updated across navigations.

        -
      7. In the "create navigation params by fetching" steps for URLs coming from a - redirect.

      8. -
      -
    6. +

      When a URL including a fragment directive is written to a session history + entry, the directive is extracted from the URL and stored in the entry's directive state member. Importantly, since a + Document is populated from a session history entry, its URL will not include fragment directives. Similarly, since the Location + object is a representation of the active document's URL, all getters on it will produce a fragment + directive-stripped version of the URL.

      -
    7. Same-document navigations that change only the fragment, and the new URL doesn't specify a - directive, will create an entry whose directive state refers to the previous entry's directive - state.

    8. -
    - -

    Since a Document is populated from a session history entry, its URL will not include the fragment directive. Similarly, - since the Location object is a representation of the active document's - URL, all getters on it will produce a - fragment-directive-stripped version of the URL.

    + +

    In short, this ensures that fragment directives are + not exposed to script, and are rather tracked by internal mechanisms alongside script-exposed + URLs.

    Additionally, since the HashChangeEvent is fired @@ -114867,7 +114848,13 @@ new PaymentRequest(…); // Allowed to use hashchange will not be fired if a navigation or traversal changes only the fragment directive.

    -

    Consider the below examples, which help clarify various edge cases.

    +

    Furthermore, same-document navigations that only change a URL's fragment without specifying a + new directive, create new session history entries + whose directive state refers to the previous entry's + directive state.

    + +

    Consider the below examples, which help clarify various edge cases of the above + implications.

    
    
    From 5dfcccb790f7a16a1013a508bc820a9a798089cf Mon Sep 17 00:00:00 2001
    From: Dominic Farolino 
    Date: Sun, 9 Nov 2025 12:24:00 +0900
    Subject: [PATCH 4/7] Two more monkeypatches and supporting algorithms
    
    ---
     source | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++------
     1 file changed, 102 insertions(+), 12 deletions(-)
    
    diff --git a/source b/source
    index 17a09e710aa..b841898406a 100644
    --- a/source
    +++ b/source
    @@ -110734,15 +110734,34 @@ location.href = '#foo';
    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. +
    3. If the user agent has reason to believe the user is no longer interested in scrolling to + the fragment, then set document's + pending text directives to null and abort these steps.

    4. + +
    5. +

      If document has no parser, or its parser has stopped + parsing, then.

      + +
        +
      1. +

        If document's pending text directives is not null, then.

        + +
          +
        1. Set document's pending text directives to null.

        2. + +
        3. Scroll to the fragment given document.

        4. +
        +
      2. + +
      3. Abort these steps.

      4. +
      +
    6. Scroll to the fragment given document.

    7. If document's indicated part is still null, then try to - scroll to the fragment for document.

    8. + scroll to the fragment for document. Otherwise, set document's + pending text directives to null.

    @@ -110973,18 +110992,63 @@ location.href = '#foo';

    Otherwise:

      -
    1. Assert: document's indicated part is an - element.

    2. +
    3. Assert: document's indicated part is an element or + a range.

    4. + +
    5. Let scrollTarget be document's indicated part.

    6. + +
    7. Let target be scrollTarget.

    8. + +
    9. +

      If target is a range, then:

      + +
        +
      1. Set target to the first common ancestor of target's + start node and target's end node.

      2. + +
      3. +

        While target is not null and is not an element, set + target to target's parent.

        + +

        What should this do if target is inside a shadow tree? See WICG/scroll-to-text-fragment#190 + for discussion.

        +
      4. +
      +
    10. -
    11. Let target be document's indicated part.

    12. +
    13. Assert: target is an element.

    14. Set document's target element to target.

    15. -
    16. Run the ancestor revealing algorithm on target.

    17. +
    18. +

      Run the ancestor revealing algorithm on target.

      + +

      This currently won't work well with text + directives, since target could be an ancestor or even the root document + node. WICG/scroll-to-text-fragment#89 + proposes restricting matches to "contain:style layout" blocks which + would resolve this problem.

      +
    19. -
    20. Scroll target into view, - with behavior set to "auto", block set to "start", and inline - set to "nearest". CSSOMVIEW

    21. +
    22. +

      Let blockPosition be "center" if scrollTarget + is a range, and "start" otherwise.

      + +

      Scrolling to a text directive centers it in the block flow + direction.

      +
    23. + +
    24. +

      Scroll scrollTarget into + view, with behavior set to "auto", block set to blockPosition, + and inline set to "nearest". CSSOMVIEW

      + +

      Implementations may avoid scrolling to the target if it is produced from a text + directive.

      +
    25. Run the focusing steps for target, with the Document's viewport as the fallback target.

    26. @@ -115798,6 +115862,32 @@ Add a helper algorithm for removing and returning a fragment directive string fr
    +
    +

    To find the first common ancestor of two nodes nodeA and + nodeB, follow these steps:

    + +
      +
    1. Let commonAncestor be nodeA.

    2. + +
    3. While commonAncestor is non-null and is not a + shadow-including inclusive ancestor of nodeB, let + commonAncestor be commonAncestor's + shadow-including parent.

    4. + +
    5. Return commonAncestor.

    6. +
    +
    + +
    +

    To find the shadow-including parent of node follow these steps:

    + +
      +
    1. If node is a shadow root, then return node's host.

    2. + +
    3. Otherwise, return node's parent.

    4. +
    +

    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, wordEndBounded and matchMustBeAtBeginning, follow these steps: From 5e2ec9d9a8e7ea1a14f2c478940251445b9ec1c9 Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Fri, 14 Nov 2025 13:40:04 +0900 Subject: [PATCH 5/7] Text directive user activation --- .../text-directive-user-activation-flag.png | Bin 0 -> 89869 bytes source | 60 +++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 images/text-directive-user-activation-flag.png diff --git a/images/text-directive-user-activation-flag.png b/images/text-directive-user-activation-flag.png new file mode 100644 index 0000000000000000000000000000000000000000..322d71af9d0d147ea640102686524c6815ab317a GIT binary patch literal 89869 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 *m)*iSG)=uWzjhLl_NMc>GsZ@4@XHp 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 zht}eBOtop-level navigation initiator origin

  • add a range header
  • destination type
  • +
  • text directive user activation
  • @@ -107170,6 +107171,9 @@ location.href = '#foo'; care of scrolling.

  • +
  • Set navigable's active document's + pending text directives to null.

  • +
  • Let traversable be navigable's traversable navigable.

  • @@ -115143,12 +115147,60 @@ console.log(document.url.hash); // '#foo:~:bar' list of text directives or null, initially null.

    +

    Each Document has a text directive user activation which is a boolean, + initially false.

    + +
    +

    The text directive user activation provides the necessary user gesture signal to + + + allow a single activation of a text directive. It is set to true during document + loading only if the navigation occurred as a result of a user activation and is propagated + across client-side redirects.

    + +

    If a Document's text directive user activation isn't used to + activate a text directive, rather it is used to set a navigation request's text directive user activation to + true, so than an "unused" text directive user activation can be propagated from one + Document to another across a navigation.

    + + +

    Both Document's text directive user activation and request's 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.

    +
    + +
    +

    This mechanism allows text fragments to activate through a common redirect technique used by + many popular web sites. Such sites "redirect" users to their intended destination by responding + with a "200" status code containing script that triggers a navigation.

    + +

    Unlike real HTTP redirects, these "client-side" redirects cannot propagate the fact that the + navigation is the result of a user gesture. The text directive user activation + mechanism allows passing through this specifically scoped user-activation through such + navigations. This means a page is able to programmatically navigate to a text fragment a single + time, as if it has a user gesture. However, since this resets the text directive user + activation, further text fragment navigations cannot activate without a new user + gesture.

    + +

    The following diagram demonstrates how the flag is used to activate a text fragment through + this mechanism:

    + + Diagram showing how the text directive
+   user activation flag is set and used +
    +

    Syntax

    -

    A text directive is specified in the fragment directive with the following format:

    +

    A text directive is specified in the fragment directive with the + following format:

     #:~:text=[prefix-,]start[,end][,-suffix]
    @@ -116241,6 +116293,10 @@ Add a helper algorithm for removing and returning a fragment directive string fr
     
       

    Security and privacy considerations

    +
    Motivation
    + + +

    Care must be taken when implementing text directives so that it cannot be used to exfiltrate information across origins. Scripts can navigate a page to a cross-origin URL with a text directive. If a malicious actor can determine that the @@ -116263,7 +116319,7 @@ Add a helper algorithm for removing and returning a fragment directive string fr

  • navigations that are the result of a user action.

  • in cases where the navigation has a cross-origin initiator, the destination must be opener - isolated (i.e. no references to its global objects in other documents)

  • + isolated (i.e., no references to its global objects in other documents)

    Scroll on navigation
    From 53ba12c096eb16dfe6bf8b04b79863544600b1f2 Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Fri, 14 Nov 2025 14:32:42 +0900 Subject: [PATCH 6/7] Set text directive activation on creation --- source | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/source b/source index 69e17c73373..5bc89da2bb7 100644 --- a/source +++ b/source @@ -108530,9 +108530,15 @@ location.href = '#foo';
    traversable for user prompts
    navigable's top-level traversable
    + +
    text direction user activation
    +
    navigable's active document's text directive user activation
    +
  • Set navigable's active document's text + directive user activation to false.

  • +
  • If navigable is a top-level traversable, then set request's top-level navigation initiator origin to entry's document state's navigationParams's response, and "pre-media".

  • +
  • +

    If any of the following are true:

    + +
      +
    • navigationParams's user + involvement is "activation";

    • + +
    • navigationParams's user + involvement is "browser-ui"; or

    • + +
    • navigationParams's request's + text directive user + activation is true

    • +
    + +

    then set document's text directive user activation to true.

    +
  • +
  • If navigationParams's navigable is a top-level traversable, then process the `Speculation-Rules` From d383a4daffaf9bcd10cc744eb0f24b9547fb64ef Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Mon, 24 Nov 2025 21:23:31 -0500 Subject: [PATCH 7/7] Finish the upstreaming --- source | 271 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 217 insertions(+), 54 deletions(-) diff --git a/source b/source index 5bc89da2bb7..01911164d75 100644 --- a/source +++ b/source @@ -106297,7 +106297,8 @@ location.href = '#foo';

    1. Navigate to a fragment given navigable, url, historyHandling, userInvolvement, - sourceElement, navigationAPIState, and navigationId.

    2. + sourceElement, navigationAPIState, navigationId, and + initiatorOriginSnapshot.

    3. Return.

    @@ -106733,8 +106734,16 @@ location.href = '#foo';
  • +
  • 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 + userInvolvement.

  • +
  • Apply the push/replace history step targetStep to - traversable given historyHandling and userInvolvement.

  • + traversable given historyHandling, userInvolvement, and + allow text directive scroll.

    @@ -107033,7 +107042,8 @@ location.href = '#foo'; navigable, a URL url, a history handling behavior historyHandling, a user navigation involvement userInvolvement, an Element-or-null sourceElement, a serialized state-or-null - navigationAPIState, and a navigation ID navigationId:

    + navigationAPIState, a navigation ID navigationId, and an + origin initiator origin:

    1. Let directive state be navigable's entry to historyEntry.

    2. +

      Update document for history step application given navigable's active document, historyEntry, true, scriptHistoryIndex, scriptHistoryLength, and @@ -107161,9 +107173,14 @@ location.href = '#foo'; untouched, and no events are fired.

    3. +
    4. 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.

    5. +
    6. Scroll to the fragment given navigable's active document.

      + data-x="nav-document">active document and allow text directive scroll.

      If the scrolling fails because the Document is new and the relevant ID has not yet been parsed, then the second @@ -109331,13 +109348,22 @@ location.href = '#foo';

      To apply the push/replace history step given a non-negative integer step to a traversable navigable traversable, given a history handling - behavior historyHandling and a user navigation involvement - userInvolvement:

      + behavior historyHandling, a user navigation involvement + userInvolvement, and an optional boolean allow text directive scroll + (default false):

        -
      1. Return the result of applying the history - step step to traversable given false, null, null, - userInvolvement, and historyHandling.

      2. +
      3. +

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

        + +

        Note that allow text directive scroll is intentionally not set for + traversal and reload cases. This avoids extensive plumbing and checks for the initiator origin, + and it's best for userInvolvement and history scroll state to take precedence anyway. + The text directive will still be used as the indicated part of the document, so + highlights will be restored.

        +
      @@ -109404,10 +109430,10 @@ location.href = '#foo'; traversable, with boolean checkForCancelation, source snapshot params-or-null sourceSnapshotParams, navigable-or-null initiatorToCheck, user navigation involvement userInvolvement, - and NavigationType-or-null navigationType, perform the following steps. - They return "initiator-disallowed", "canceled-by-beforeunload", "canceled-by-navigate", or - "applied".

      + NavigationType-or-null navigationType, and an optional boolean allow + text directive scroll (default false), perform the following steps. They return "initiator-disallowed", "canceled-by-beforeunload", + "canceled-by-navigate", or "applied".

      1. Assert: This is running within traversable's changingNavigableContinuation's update-only, scriptHistoryLength, scriptHistoryIndex, navigationType, - entriesForNavigationAPI, and previousEntry.

      2. + allow text directive scroll, entriesForNavigationAPI, and + previousEntry.

      3. If targetEntry's document is equal to displayedDocument, then perform updateDocument.

      4. @@ -110416,10 +110443,10 @@ location.href = '#foo';

        To update document for history step application given a Document document, a session history entry entry, a boolean doNotReactivate, integers scriptHistoryLength and - scriptHistoryIndex, NavigationType-or-null navigationType, an - optional list of session history entries - entriesForNavigationAPI, and an optional session history entry - previousEntryForActivation:

        + scriptHistoryIndex, NavigationType-or-null navigationType, a + boolean allow text directive scroll, an optional list of session history entries entriesForNavigationAPI, + an optional session history entry previousEntryForActivation:

        1. Let documentIsNew be true if document's latest entry @@ -110593,7 +110620,8 @@ location.href = '#foo'; data-x="navigation-status-url">url is document's URL.

        2. -
        3. Try to scroll to the fragment for document.

        4. +
        5. Try to scroll to the fragment for document given allow text + directive scroll.

        6. At this point scripts may run for the newly-created document document.

        7. @@ -110732,8 +110760,9 @@ location.href = '#foo';
          -

          To try to scroll to the fragment for a Document document, - perform the following steps in parallel:

          +

          To try to scroll to the fragment for a Document document + given a 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 @@ -110759,7 +110788,15 @@ location.href = '#foo';

            1. Set document's pending text directives to null.

            2. -
            3. Scroll to the fragment given document.

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

          2. @@ -110767,11 +110804,13 @@ location.href = '#foo';
          -
        8. Scroll to the fragment given document.

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

        11. If document's indicated part is still null, then try to - scroll to the fragment for document. Otherwise, set document's - pending text directives to null.

        12. + scroll to the fragment for document given allow text directive + scroll. Otherwise, set document's pending text directives to + null.

      @@ -110978,7 +111017,8 @@ location.href = '#foo';

      To scroll to the fragment given a - Document document:

      + Document document and a boolean allow text directive + scroll:

      1. If document's indicated part is null, then set @@ -111013,6 +111053,8 @@ location.href = '#foo';

        If target is a range, then:

          +
        1. If allow text directive scroll is false, then return.

        2. +
        3. Set target to the first common ancestor of target's start node and target's end node.

        4. @@ -114892,6 +114934,44 @@ new PaymentRequest(…); // Allowed to use Without text directives, if a user wants to share a passage of text from a page, they would likely just copy and paste the passage, in which case the recipient loses the context of the page.

          +
          Bidirectional text considerations
          + + + +

          Since URL strings are ASCII encoded, they provide no built-in support for bidirectional text. + However, the content that we wish to target on a page can be left-to-right, right-to-left, or + bidirectional. This section provides an intuitive description the behavior implicitly described by + the normative sections further in this spec, as they pertain to text + directives.

          + +

          The characters of each term in a text directive are in logical order, + that is, the order in which a native reader would read them. Similarly, the prefix and start terms identify text coming before another term + in logical order, while suffix and end follow other terms in logical order.

          + +

          Suppose we want to select the text "مِصر" ("Egypt", in Arabic), that's preceeded by "البحرين" ("Bahrain", in Arabic). We would first + percent-encode each term: + +

          "مِصر" becomes "%D9%85%D8%B5%D8%B1".

          + +

          The U+0645 ARABIC LETTER MEEM, whose UTF-8 byte sequence is "[0xD9,0x85]", is the first logical (right-most) character of the decoded Arabic + word.

          + +

          "البحرين" becomes "%D8%A7%D9%84%D8%A8%D8%AD%D8%B1%D9%8A%D9%86".

          + +

          The text fragment would then become: ":~:text=%D8%A7%D9%84%D8%A8%D8%AD%D8%B1%D9%8A%D9%86-,%D9%85%D8%B5%D8%B1". When + displayed in a browser's address bar, the browser can visually render the text in its natural RTL + direction, appearing to the user: ":~:text=البحرين-,مِصر" +

          Link lifetime

          @@ -115218,6 +115298,22 @@ console.log(document.url.hash); // '#foo:~:bar' user activation flag is set and used">
      +

      A text directive allowing MIME type is a MIME type whose essence is "text/html" or "text/plain". + +

      As noted in Scrolling + to a fragment, fragment processing is defined individually by each MIME type. As such, the + scroll to the fragment steps where text directives are scrolled only apply to + text/html media types. However, in practice, web browsers tend to apply HTML fragment processing + to other types, such as text/plain (e.g., add an element with an id to a text/plain document, + navigating to the fragment-id causes scrolling). While this is the case, enabling text directives + in text/plain documents is useful. Other types are explicitly disallowed to prevent the + possibility of XS-Search attacks on potentially sensitive application data (e.g., text/css, + application/json, application/javascript, etc.).

      + +

      Is this valid to say in the HTML spec?

      Syntax

      @@ -115411,7 +115507,7 @@ console.log(document.url.hash); // '#foo:~:bar' -Add a helper algorithm for removing and returning a fragment directive string from a URL: + Add a helper algorithm for removing and returning a fragment directive string from a URL:

      This algorithm makes a URL's fragment end at the fragment directive delimiter. The returned fragment directive includes all characters that follow @@ -115503,33 +115599,36 @@ Add a helper algorithm for removing and returning a fragment directive string fr

      Maybe describe what this next set of algorithms and primitives do.

      -

      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 - text passage within the document that matches the searched-for text and - satisfies the surrounding context. Returns null if no such passage exists.

      - - - [=text directive/end=] can be null. If omitted, this is an "exact" - search and the returned [=range=] will contain a string exactly matching - [=text directive/start=]. If [=text directive/end=] is - provided, this is a "range" search; the returned [=range=] will start with - [=text directive/start=] and end with - [=text directive/end=]. In the normative text below, we'll call a - text passage that matches the provided [=text directive/start=] and - [=text directive/end=], regardless of which mode we're in, the - "matching text". - - Either or both of [=text directive/prefix=] and - [=text directive/suffix=] can be null, in which case context on that - side of a match is not checked. E.g. If [=text directive/prefix=] is - null, text is matched without any requirement on what text precedes it. +

      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 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 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 + side of a match is not checked. E.g., If prefix is null, text is matched without any + requirement on what text precedes it.
      - While the matching text and its prefix/suffix can span across - block-boundaries, the individual parameters to these steps cannot. That is, - each of [=text directive/prefix=], [=text directive/start=], - [=text directive/end=], and [=text directive/suffix=] will only - match text within a single block. +

      While the matching text and its prefix/suffix can span across block-boundaries, the individual + parameters to these steps cannot. That is, each of prefix, start, end, and suffix will only match text within a single + block.

      :~:text=The quick,lazy dog
      will fail to match in @@ -115553,8 +115652,6 @@ Add a helper algorithm for removing and returning a fragment directive string fr
      - DOMFAROLINO FROM HERE (EARLY) -

      To find a range from a text directive, given a text directive parsedValues and Document document, run the following steps: @@ -116084,6 +116181,72 @@ Add a helper algorithm for removing and returning a fragment directive string fr

    +
    + +
    +

    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 pending text directives field is null or empty, then + return false.

    2. + +
    3. 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", + and false otherwise.

    4. + +
    5. Set document's text directive user activation to false.

    6. + +
    7. If document's content type + is not a text directive allowing MIME type, return false.

    8. + +
    9. +

      If user involvement is "browser UI", then + 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 + for discussion about how this applies. FETCHMETADATA.

      +
      +
    10. + +
    11. If is user involved is false, return false.

    12. + +
    13. If document's node navigable has a parent, then return false.

    14. + +
    15. If initiator origin is non-null and document's origin is same origin with + initiator origin, then return true.

    16. + +
    17. +

      If document's browsing context's top-level browsing context's group's + browsing context set's size is 1, then return + true.

      + +

      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.

      +
    18. + +
    19. Return false.

    20. +
    +
    +
    Word boundaries