From b33d44fd24d5021cb0f94701afa359d923a9f841 Mon Sep 17 00:00:00 2001 From: Fernando Fiori Date: Thu, 3 Sep 2026 14:49:13 -0700 Subject: [PATCH 1/3] Thread focus initiator through focus-producing APIs Carry the initiating document through direct focus, dialog, and popover algorithms. Use explicit invoking documents for user-agent paths and block the focused-subtree exception while a focused descendant is fullscreen. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e74da64d-6b71-41bf-8b15-4e34d5f7a6ec --- source | 135 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 101 insertions(+), 34 deletions(-) diff --git a/source b/source index 1d4de3a60e2..0b6ec0e86dd 100644 --- a/source +++ b/source @@ -56934,7 +56934,8 @@ interface HTMLButtonElement : HTMLElement { validity be false.

  • If validity is true, then run the show popover - algorithm given target, false, and element.

  • + algorithm given target, false, element, and + element's node document.

  • Otherwise:

    @@ -56961,7 +56962,8 @@ interface HTMLButtonElement : HTMLElement { validity be false.

  • If validity is true, then run the show popover - algorithm given target, false, and element.

  • + algorithm given target, false, element, and + element's node document.

    @@ -57409,7 +57411,8 @@ interface HTMLSelectElement : HTMLElement { data-x="dom-MouseEvent-button">button attribute is not 0, then return.

  • Run the show popover algorithm given select's select - popover, false, and select.

  • + popover, false, select, and select's node + document.

    @@ -66407,6 +66410,10 @@ interface HTMLDialogElement : HTMLElement { method steps are:

      +
    1. Let focusSetterDocument be the incumbent global object's associated Document.

    2. +
    3. If this has an open attribute and is modal of this is false, then return.

    4. @@ -66441,14 +66448,23 @@ interface HTMLDialogElement : HTMLElement {
    5. Run hide popovers until given document, hideUntil, false, and true.

    6. -
    7. Run the dialog focusing steps given this.

    8. +
    9. Run the dialog focusing steps given this and + focusSetterDocument.

    The showModal() method steps are to show a modal - dialog given this and null.

    + data-x="dom-dialog-showModal">showModal() method steps are:

    + +
      +
    1. Let focusSetterDocument be the incumbent global object's associated Document.

    2. + +
    3. Show a modal dialog given this, null, and + focusSetterDocument.

    4. +
    @@ -66625,8 +66641,9 @@ interface HTMLDialogElement : HTMLElement {
    -

    To show a modal dialog given a dialog element subject and an - Element or null source:

    +

    To show a modal dialog given a dialog element subject, an + Element or null source, and a Document + focusSetterDocument:

    1. If subject has an open attribute and @@ -66700,7 +66717,8 @@ interface HTMLDialogElement : HTMLElement {

    2. Run hide popovers until given document, hideUntil, false, and true.

    3. -
    4. Run the dialog focusing steps given subject.

    5. +
    6. Run the dialog focusing steps given subject and + focusSetterDocument.

    @@ -66781,7 +66799,8 @@ interface HTMLDialogElement : HTMLElement {
  • If command is the Show Modal state and element does not have an open attribute, then - show a modal dialog given element and source.

  • + show a modal dialog given element, source, and + source's node document.

    @@ -66973,12 +66992,12 @@ interface HTMLDialogElement : HTMLElement {
    -

    The dialog focusing steps, given a dialog element subject, - are as follows:

    +

    The dialog focusing steps, given a dialog element subject + and a Document focusSetterDocument, are as follows:

      -
    1. If the allow focus steps given subject's node - document return false, then return.

    2. +
    3. If the allow focus steps given focusSetterDocument return false, + then return.

    4. Let control be null.

    5. @@ -87402,8 +87421,12 @@ dictionary CommandEventInit : EventInit {
    6. If current is null, then return.

    7. -
    8. If the allow focus steps given current's active document return false, then return.

    9. +
    10. Let focusSetterDocument be the incumbent global object's associated Document.

    11. + +
    12. If the allow focus steps given focusSetterDocument return false, + then return.

    13. Run the focusing steps with current.

    14. @@ -87430,8 +87453,12 @@ dictionary CommandEventInit : EventInit { data-x="dom-focus">focus(options) method steps are:

        -
      1. If the allow focus steps given this's node document - return false, then return.

      2. +
      3. Let focusSetterDocument be the incumbent global object's associated Document.

      4. + +
      5. If the allow focus steps given focusSetterDocument return false, + then return.

      6. Run the focusing steps for this.

      7. @@ -87469,17 +87496,44 @@ dictionary CommandEventInit : EventInit {

        The allow focus steps, given a Document object - target, are:

        + focusSetterDocument, are:

          -
        1. If target is allowed to use the "

          If focusSetterDocument is allowed to use the "focus-without-user-activation" feature, then return true.

        2. -
        3. If target's relevant global object has transient - activation, then return true.

        4. +
        5. If focusSetterDocument's relevant global object has + transient activation, then return true.

        6. -
        7. Return false.

        8. +
        9. If focusSetterDocument is not fully active, then return + false.

        10. + +
        11. Let focusSetterOrDescendantHasFocus be false.

        12. + +
        13. +

          For each navigable in the inclusive descendant navigables of + focusSetterDocument:

          + +
            +
          1. Let descendantDocument be navigable's active document.

          2. + +
          3. +

            If running the has focus steps given descendantDocument returns + true:

            + +
              +
            1. If descendantDocument's fullscreen element is not null, then + return false.

            2. + +
            3. Set focusSetterOrDescendantHasFocus to true.

            4. +
            +
          4. +
          +
        14. + +
        15. Return focusSetterOrDescendantHasFocus.

        @@ -92218,19 +92272,24 @@ dictionary DragEventInit : MouseEventInit { data-x="dom-showPopover">showPopover(options) method steps are:

          +
        1. Let focusSetterDocument be the incumbent global object's associated Document.

        2. +
        3. Let source be options["source"] if it exists; otherwise, null.

        4. -
        5. Run show popover given this, true, and - source.

        6. +
        7. Run show popover given this, true, source, and + focusSetterDocument.

    To show popover, given an HTML element - element, a boolean throwExceptions, and an HTML - element or null source:

    + element, a boolean throwExceptions, an HTML + element or null source, and a Document + focusSetterDocument:

    1. Let document be element's node document.

    2. @@ -92471,7 +92530,8 @@ dictionary DragEventInit : MouseEventInit {
    3. Set element's implicit anchor element to source.

    4. -
    5. Run the popover focusing steps given element.

    6. +
    7. Run the popover focusing steps given element and + focusSetterDocument.

    8. If shouldRestoreFocus is true and element's popover attribute is not in the DragEventInit : MouseEventInit { data-x="dom-togglePopover">togglePopover(options) method steps are:

        +
      1. Let focusSetterDocument be the incumbent global object's associated Document.

      2. +
      3. Let force be null.

      4. If options is a boolean, set force to options.

      5. @@ -92757,7 +92821,7 @@ dictionary DragEventInit : MouseEventInit { null.

      6. Otherwise, if force is null or true, then run show popover given - this, true, and source.

      7. + this, true, source, and focusSetterDocument.

      8. Otherwise:

        @@ -93002,14 +93066,15 @@ dictionary DragEventInit : MouseEventInit {

        To perform the popover focusing steps for an HTML - element subject:

        + element subject and a Document + focusSetterDocument:

          -
        1. If the allow focus steps given subject's node - document return false, then return.

        2. +
        3. If the allow focus steps given focusSetterDocument return false, + then return.

        4. If subject is a dialog element, then run the dialog focusing - steps given subject and return.

        5. + steps given subject and focusSetterDocument, and return.

        6. If subject has the autofocus attribute, then let control be subject. @@ -93292,7 +93357,8 @@ dictionary DragEventInit : MouseEventInit { validity be false.

        7. If validity is true, then run show popover given - popover, false, and node.

        8. + popover, false, node, and node's node + document.

      @@ -162133,6 +162199,7 @@ INSERT INTERFACES HERE Félix Sanz, Felix Sasaki, Fernando Altomare Serboncini, + Fernando Fiori, Forbes Lindesay, Francesco Schwarz, Francis Brosnan Blazquez, From c1fb9a9c5aba0e7f247399b11a058904d34d7513 Mon Sep 17 00:00:00 2001 From: Fernando Fiori Date: Thu, 3 Sep 2026 16:06:37 -0700 Subject: [PATCH 2/3] Guard dialog focus in inactive documents Prevent caller-based authorization from reaching dialog focusing steps that dereference a null node navigable for detached or synthetic documents. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e74da64d-6b71-41bf-8b15-4e34d5f7a6ec --- source | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source b/source index 0b6ec0e86dd..dcd281c8beb 100644 --- a/source +++ b/source @@ -66996,6 +66996,9 @@ interface HTMLDialogElement : HTMLElement { and a Document focusSetterDocument, are as follows:

        +
      1. If subject's node document is not fully active, then + return.

      2. +
      3. If the allow focus steps given focusSetterDocument return false, then return.

      4. From 6df89bea99fba0c520465e6b1777ed57b5d8d28b Mon Sep 17 00:00:00 2001 From: Fernando Fiori Date: Fri, 4 Sep 2026 10:51:03 -0700 Subject: [PATCH 3/3] Defer fullscreen focus handling Keep the focus-without-user-activation change scoped to caller attribution and focused descendants. Fullscreen interaction can be specified separately once its exact boundary is agreed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e74da64d-6b71-41bf-8b15-4e34d5f7a6ec --- source | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/source b/source index dcd281c8beb..7165ea2ffb4 100644 --- a/source +++ b/source @@ -87512,8 +87512,6 @@ dictionary CommandEventInit : EventInit {
      5. If focusSetterDocument is not fully active, then return false.

      6. -
      7. Let focusSetterOrDescendantHasFocus be false.

      8. -
      9. For each navigable in the inclusive descendant navigables of focusSetterDocument:

        @@ -87522,21 +87520,12 @@ dictionary CommandEventInit : EventInit {
      10. Let descendantDocument be navigable's active document.

      11. -
      12. -

        If running the has focus steps given descendantDocument returns - true:

        - -
          -
        1. If descendantDocument's fullscreen element is not null, then - return false.

        2. - -
        3. Set focusSetterOrDescendantHasFocus to true.

        4. -
        -
      13. +
      14. If running the has focus steps given descendantDocument returns + true, then return true.

    9. -
    10. Return focusSetterOrDescendantHasFocus.

    11. +
    12. Return false.