From c89fa478af1a8487063cd88da160f99076ed3615 Mon Sep 17 00:00:00 2001 From: Joey Arhar Date: Thu, 12 Jan 2023 05:37:37 -0800 Subject: [PATCH 1/4] Add popover attribute Tests: https://wpt.fyi/results/html/semantics/popovers. Additional context: https://open-ui.org/components/popup.research.explainer. Prior PR: #8221. Closes #7785. --- source | 1356 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1335 insertions(+), 21 deletions(-) diff --git a/source b/source index 9da41e81003..73c01ff8470 100644 --- a/source +++ b/source @@ -1824,6 +1824,10 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute element.

+ +
  • If removedNode's popover attribute is not in + the no popover state, then run the hide + popover algorithm given removedNode, false, true, and false.

  • A The tree, shadow tree, and node tree concepts

  • The tree order and shadow-including tree order concepts
  • +
  • The element concept
  • The child concept
  • The root and shadow-including root concepts
  • The inclusive ancestor, @@ -3193,6 +3198,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute is value
  • MutationObserver interface and mutation observers in general
  • +
  • The get an attribute by name algorithm
  • The following features are defined in UI Events:

    @@ -3288,6 +3294,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute operation
  • requestFullscreen()
  • run the fullscreen steps
  • +
  • fullscreen flag
  • High Resolution Time provides the :focus-visible pseudo-class

  • indicate focus
  • +
  • pseudo-element
  • The following features are defined in CSS Values and Units: HTMLElement : Element { [CEReactions] attribute [LegacyNullToEmptyString] DOMString outerText; ElementInternals attachInternals(); + + // The popover API + void showPopover(); + void hidePopover(); + void togglePopover(optional boolean force); + [CEReactions] attribute DOMString? popover; }; HTMLElement includes GlobalEventHandlers; @@ -12399,6 +12413,7 @@ https://software.hixie.ch/utilities/js/live-dom-viewer/?%3C%21DOCTYPE%20HTML%3E%

  • itemtype
  • lang
  • nonce
  • +
  • popover
  • spellcheck
  • style
  • tabindex
  • @@ -12520,6 +12535,7 @@ https://software.hixie.ch/utilities/js/live-dom-viewer/?%3C%21DOCTYPE%20HTML%3E%
  • onauxclick
  • onbeforeinput
  • onbeforematch
  • +
  • onbeforetoggle
  • onblur*
  • oncancel
  • oncanplay
  • @@ -44904,6 +44920,9 @@ interface HTMLLabelElement : HTMLElement {
    name
    pattern
    placeholder
    +
    popoverhidetarget
    +
    popovershowtarget
    +
    popovertoggletarget
    readonly
    required
    size
    @@ -45069,7 +45088,8 @@ interface HTMLInputElement : HTMLElement { undefined showPicker(); // also has obsolete members -}; +}; +HTMLInputElement includes PopoverTargetElement;
    Uses HTMLInputElement.
    @@ -45749,6 +45769,78 @@ interface HTMLInputElement : HTMLElement { · + + popoverhidetarget + · + · + + · + · + · + · + + + + · + · + · + · + · + + · + Yes + Yes + Yes + + + + popovershowtarget + · + · + + · + · + · + · + + + + · + · + · + · + · + + · + Yes + Yes + Yes + + + + popovertoggletarget + · + · + + · + · + · + · + + + + · + · + · + · + · + + · + Yes + Yes + Yes + + readonly · @@ -46394,6 +46486,8 @@ interface HTMLInputElement : HTMLElement {
  • Run this element's input activation behavior, if any, and do nothing otherwise.

  • + +
  • Run the popover target attribute activation behavior on this element.

  • Recall that an element's activation behavior runs for both @@ -46672,6 +46766,9 @@ interface HTMLInputElement : HTMLElement { multiple, pattern, placeholder, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, readonly, required, size, @@ -46807,6 +46904,9 @@ interface HTMLInputElement : HTMLElement { max, min, multiple, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, src, step, and width.

    @@ -46908,6 +47008,9 @@ interface HTMLInputElement : HTMLElement { max, min, multiple, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, src, step, and width.

    @@ -47011,6 +47114,9 @@ interface HTMLInputElement : HTMLElement { max, min, multiple, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, src, step, and width.

    @@ -47279,6 +47385,9 @@ ldh-str = < as defined in height, max, min, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, src, step, and width.

    @@ -47375,6 +47484,9 @@ ldh-str = < as defined in
    max, min, multiple, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, src, step, and width.

    @@ -47548,6 +47660,9 @@ ldh-str = < as defined in
    multiple, pattern, placeholder, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, size, src, and width.

    @@ -47699,6 +47814,9 @@ ldh-str = < as defined in
    multiple, pattern, placeholder, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, size, src, and width.

    @@ -47854,6 +47972,9 @@ ldh-str = < as defined in
    multiple, pattern, placeholder, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, size, src, and width.

    @@ -48005,6 +48126,9 @@ ldh-str = < as defined in
    multiple, pattern, placeholder, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, size, src, and width.

    @@ -48149,6 +48273,9 @@ ldh-str = < as defined in
    multiple, pattern, placeholder, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, size, src, and width.

    @@ -48311,6 +48438,9 @@ ldh-str = < as defined in
    minlength, multiple, pattern, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, size, src, and width.

    @@ -48581,6 +48711,9 @@ ldh-str = < as defined in
    multiple, pattern, placeholder, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, readonly, required, size, @@ -48687,6 +48820,9 @@ ldh-str = < as defined in multiple, pattern, placeholder, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, readonly, required, size, @@ -48801,6 +48937,9 @@ ldh-str = < as defined in multiple, pattern, placeholder, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, readonly, size, src, @@ -48976,6 +49115,9 @@ ldh-str = < as defined in multiple, pattern, placeholder, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, readonly, size, src, @@ -49226,6 +49368,9 @@ ldh-str = < as defined in min, minlength, pattern, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, placeholder, readonly, size, @@ -49309,9 +49454,12 @@ ldh-str = < as defined in formaction, formenctype, formmethod, - formnovalidate, and - formtarget content attributes; - value IDL attribute.

    + formnovalidate, + formtarget, + popoverhidetarget, + popovershowtarget, and + popovertoggletarget content + attributes; value IDL attribute.

    The value IDL attribute is in mode default.

    @@ -49579,6 +49727,9 @@ ldh-str = < as defined in
    formnovalidate, formtarget, height, + popoverhidetarget, + popovershowtarget, + popovertoggletarget, src, and width content attributes; value IDL attribute.

    @@ -49698,6 +49849,12 @@ ldh-str = < as defined in
    applies to this element and is in mode default.

    +

    The following common input element content attributes apply to the element: + popoverhidetarget, + popovershowtarget, and + popovertoggletarget.

    +

    The following content attributes must not be specified and do not apply to the element: accept, @@ -49778,6 +49935,12 @@ ldh-str = < as defined in applies to this element and is in mode default.

    +

    The following common input element content attributes apply to the element: + popoverhidetarget, + popovershowtarget, and + popovertoggletarget.

    +

    The following content attributes must not be specified and do not apply to the element: accept, @@ -51356,6 +51519,9 @@ You cannot submit this form when the field is incorrect.

    formnovalidate
    formtarget
    name
    +
    popoverhidetarget
    +
    popovershowtarget
    +
    popovertoggletarget
    type
    value
    HTMLButtonElement : HTMLElement { undefined setCustomValidity(DOMString error); readonly attribute NodeList labels; -}; +}; +HTMLButtonElement includes PopoverTargetElement;
    Uses HTMLButtonElement.
    @@ -51446,13 +51613,12 @@ interface HTMLButtonElement : HTMLElement {
  • If element is disabled, then return.

  • -
  • If element does not have a form owner, then return.

  • -
  • If element's node document is not fully active, then return.

  • -

    Switch on element's type attribute's state:

    +

    If element has a form owner then switch on element's type attribute's state, then:

    Submit Button
    @@ -51467,6 +51633,9 @@ interface HTMLButtonElement : HTMLElement {

    Do nothing.

  • + +
  • Run the popover target attribute activation behavior given + element.

  • @@ -59373,6 +59542,10 @@ interface HTMLDialogElement : HTMLElement {
  • If the element already has an open attribute, then return.

  • +
  • If subject is in the popover showing + state, then throw an "InvalidStateError" + DOMException.

  • +
  • Add an open attribute to the dialog element, whose value is the empty string.

  • @@ -59397,6 +59570,10 @@ interface HTMLDialogElement : HTMLElement {
  • If subject is not connected, then throw an "InvalidStateError" DOMException.

  • +
  • If subject is in the popover showing + state, then throw an "InvalidStateError" + DOMException.

  • +
  • Add an open attribute to subject, whose value is the empty string.

  • @@ -59429,6 +59606,9 @@ interface HTMLDialogElement : HTMLElement { and a boolean isModal, are as follows:

      +
    1. Run hide all popovers until given null, + false, and false.

    2. +
    3. Let control be null.

    4. If isModal is true and subject has the HTMLDialogElement : HTMLElement {

      Each dialog element has an is modal flag. When a dialog element is created, this flag must be set to false.

      -

      Each dialog element has a previously focused element which is null or - an element, and it is initially null. When showModal() - and show() are called, this element is set to the currently - focused element before running the dialog focusing steps.

      +

      Each HTML element has a previously focused + element which is null or an element, and it is initially null. When showModal() and show() + are called, this element is set to the currently focused element before running the + dialog focusing steps. Elements with the popover + attribute set this element to the currently focused element during the show popover algorithm.


      @@ -71818,6 +72001,24 @@ Demos: not null, or containing no elements, if it is.

      +
      :open
      +
      +

      The :open pseudo-class is defined to match + any HTML element whose popover attribute is not in the no popover state and whose popover visibility + state is showing.

      +
      + +
      :closed
      +
      +

      The :closed pseudo-class is defined to + match any HTML element whose popover attribute is not in the no popover state and whose popover visibility + state is hidden.

      +
      +
      :enabled

      The :enabled pseudo-class must match any @@ -76602,7 +76803,9 @@ partial interface Navigator {


      A node is a focus navigation scope owner if it is a Document, a - shadow host, or a slot.

      + shadow host, a slot, or an element in the popover showing state which also has a popover + invoker set.

      Each focus navigation scope owner has a focus navigation scope, which is a list of elements. Its contents are determined as follows:

      @@ -76623,6 +76826,9 @@ partial interface Navigator {
    5. If element's parent is the document element, then return the parent's node document.

    6. +
    7. If element is in the popover showing + state and has a popover invoker set, then return element.

    8. +
    9. Return element's parent's associated focus navigation owner.

    @@ -76952,8 +77158,8 @@ partial interface Navigator {

    The focus delegate for a focusTarget, given an optional string - focusTrigger (default "other"), is given by the following - steps:

    + focusTrigger (default "other") and an optional boolean + autofocusOnly (default false), is given by the following steps:

    1. If focusTarget is a shadow host and its Navigator {

    2. If autofocusDelegate is not null, then return autofocusDelegate.

    3. +
    4. If autofocusOnly is true, then return null.

    5. +
    6. For each descendant of whereToLook's descendants, in tree @@ -77687,16 +77895,47 @@ partial interface Navigator {

      The autofocus content attribute allows the author to indicate that an element is to be focused as soon as the - page is loaded or as soon as the dialog within which it finds itself is shown, - allowing the user to just start typing without having to manually focus the main element.

      + page is loaded, allowing the user to just start typing without having to manually focus the main + element.

      + +

      When the autofocus attribute is specified on an element + inside dialog elements or HTML elements whose popover attribute is set, then it will be focused when the dialog or + popover becomes shown.

      The autofocus attribute is a boolean attribute.

      -

      An element's nearest ancestor autofocus scoping root element is the element itself - if the element is a dialog element, or else is the element's nearest ancestor - dialog element, if any, or else is the element's last inclusive ancestor - element.

      +

      To find the nearest ancestor autofocus scoping root element given an + Element element:

      + +
        +
      1. If element is a dialog element, then return + element.

      2. + +
      3. If element's popover attribute is not in the + no popover state, then return + element.

      4. + +
      5. Let ancestor be element.

      6. + +
      7. +

        While ancestor has a parent element:

        + +
          +
        1. Set ancestor to ancestor's parent element.

        2. + +
        3. If ancestor is a dialog element, then return + ancestor.

        4. + +
        5. If ancestor's popover attribute is not in + the no popover state, then return + ancestor.

        6. +
        +
      8. + +
      9. Return ancestor.

      10. +

      There must not be two elements with the same nearest ancestor autofocus scoping root element that both have the autofocus attribute @@ -81023,6 +81262,1013 @@ dictionary DragEventInit : MouseEventInit { +

      The popover attribute

      + +

      All HTML elements may have the popover content attribute set. When specified, the element + won't be rendered until it becomes shown, at which point it will be rendered on top of other page + content.

      + +

      The popover attribute is an enumerated + attribute. The following table lists the states for this attribute:

      + + + + + + + + + + + + + + + + + + + + + + + + +
      StateKeywordsDescription
      Auto stateautoCloses other popovers when opened; has light + dismiss.
      The empty string
      Manual statemanualDoes not close other popovers; does not light + dismiss.
      + +

      The attribute may be omitted. The invalid value default + is the manual state. The missing value default is the no popover + state.

      + +

      The popover IDL attribute + must reflect the popover attribute, limited + to only known values.

      + +

      Every HTML element has a popover visibility + state, initially hidden, with these potential + values:

      + +
        +
      • hidden

      • + +
      • showing

      • +
      + +

      The Document has an auto popover list, which is a list, + initially empty.

      + +

      The Document has a popover pointerdown target, which is an HTML element or null, initially null.

      + +

      Every HTML element has a popover invoker, which + is an HTML element or null, initially set to null.

      + +

      Every HTML element has a popover toggle task, + initially null, which is either null or a struct which has:

      + +
      +
      Task
      A task which fires a + ToggleEvent.
      + +
      Old state
      +
      A string which represents the task's event's + value for the oldState attribute.
      +
      + +

      The following attribute change + steps are used for all HTML elements:

      + +
        +
      1. If namespace is not null, then return.

      2. + +
      3. If localName is not popover, then + return.

      4. + +
      5. If oldValue and value are in different states, then run the hide popover algorithm given + element, true, false, and false.

      6. +
      + +
      +
      element.showPopover()
      + +
      Shows the popover element by adding it to the top layer. If element's + popover attribute is in the auto state, then this will also close all other auto popovers unless they are an ancestor of + element according to the topmost popover ancestor algorithm.
      + +
      element.hidePopover()
      + +
      Hides the popover element by removing it from the top layer and applying + display: none to it.
      + +
      element.togglePopover()
      + +
      If the popover element is not showing, then this method shows it. Otherwise, this + method hides it.
      +
      + +

      The showPopover() + method steps are:

      + +
        +
      1. Run show popover given this and true.

      2. +
      + +

      To show popover, given an HTML element + element and a boolean throwExceptions:

      + +
        +
      1. +

        If the result of running check popover validity given element and + false is false, then:

        + +
          +
        1. Assert: throwExceptions is true.

        2. + +
        3. Throw an "InvalidStateError" + DOMException.

        4. +
        +
      2. + +
      3. Assert: element is not in element's node + document's top layer.

      4. + +
      5. If the result of firing an event named beforetoggle, using ToggleEvent, with the cancelable attribute initialized to true, the oldState attribute initialized to "closed", and the newState + attribute initialized to "open" at element is false, then + return.

      6. + +
      7. +

        If the result of running check popover validity given element and + false is false:

        + +
          +
        1. If throwExceptions is true, then throw an + "InvalidStateError" DOMException.

        2. + +
        3. Otherwise, return.

        4. +
        + +

        Check popover validity is called again because firing the beforetoggle event could have disconnected this element or + changed its popover attribute.

        +
      8. + +
      9. Let document be element's node document.

      10. + +
      11. Let shouldRestoreFocus be false.

      12. + +
      13. +

        If element's popover attribute is in the auto state, then:

        + +
          +
        1. Let originalType be the value of element's popover attribute.

        2. + +
        3. Let ancestor be the result of running the topmost popover + ancestor algorithm given element.

        4. + +
        5. Run hide all popovers until given + ancestor, false, and false.

        6. + +
        7. +

          If originalType is not equal to the value of element's popover attribute, or if the result of running check + popover validity given element and false is false:

          + +
            +
          1. If throwExceptions is true, then throw an + "InvalidStateError" DOMException.

          2. + +
          3. Otherwise, return.

          4. +
          + +

          Check popover validity is called again because running hide all popovers until above could have fired the + beforetoggle event, and an event handler could have + disconnected this element or called showPopover() on + this element.

          +
        8. +
        +
      14. + +
      15. +

        If the result of running topmost auto popover on document + is null, then set shouldRestoreFocus to true.

        + +

        This ensures that focus is returned to the previously-focused element only for + the first popover in a stack.

        +
      16. + +
      17. Add element to document's auto popover list.

      18. + +
      19. Set element's previously focused element to null.

      20. + +
      21. Let originallyFocusedElement be document's focused area of the + document's DOM anchor.

      22. + +
      23. Add element to document's top layer.

      24. + +
      25. Set element's popover visibility state to showing.

      26. + +
      27. Run the popover focusing steps given element.

      28. + +
      29. If shouldRestoreFocus is true and element's popover attribute is not in the no popover state, then set element's + previously focused element to originallyFocusedElement.

      30. + +
      31. Let oldState be "closed".

      32. + +
      33. +

        If element's popover toggle task is not null, then:

        + +
          +
        1. Set oldState to element's popover toggle task's + old state.

        2. + +
        3. Remove element's popover toggle task's task from its task queue.

        4. + +
        5. Set element's popover toggle task to null.

        6. +
        +
      34. + +
      35. +

        Queue an element task given the user interaction task source and + element to run the following steps:

        + +
          +
        1. fire an event named toggle, using ToggleEvent, with the oldState attribute initialized to oldState, + and the newState attribute initialized to "open" at element.

        2. + +
        3. Set element's popover toggle task to null.

        4. +
        +
      36. + +
      37. Set element's popover toggle task to a struct with task set to the just-queued task and old + state set to oldState.

      38. +
      + +

      The hidePopover() + method steps are:

      + +
        +
      1. Run the hide popover algorithm given this, true, false, and + true.

      2. +
      + +

      To hide a popover given an HTML element element, a boolean focusPreviousElement, a + boolean dontFireEvents, and a boolean throwExceptions:

      + +
        +
      1. +

        If the result of running check popover validity given element and + true is false:

        + +
          +
        1. If throwExceptions is true, then throw an + "InvalidStateError" DOMException.

        2. + +
        3. Otherwise, return.

        4. +
        +
      2. + +
      3. Let document be element's node document.

      4. + +
      5. +

        If element's popover attribute is in the auto state, then:

        + +
          +
        1. Run hide all popovers until given + element, focusPreviousElement, and + dontFireEvents.

        2. + +
        3. +

          If element is not in document's auto popover list:

          + +
            +
          1. If throwExceptions is true, then throw an + "InvalidStateError" DOMException.

          2. + +
          3. Otherwise, return.

          4. +
          +
        4. + +
        5. Assert: The last item in document's auto popover + list is element.

        6. + +
        7. Remove element from document's + auto popover list.

        8. +
        +
      6. + +
      7. Set element's popover invoker to null.

      8. + +
      9. +

        If dontFireEvents is false:

        + +
          +
        1. Fire an event named beforetoggle, using ToggleEvent, with the oldState attribute initialized to "open", and the newState + attribute initialized to "closed" at element.

        2. + +
        3. +

          If the result of running check popover validity given element and + true is false:

          + +
            +
          1. If throwExceptions is true, then throw an + "InvalidStateError" DOMException.

          2. + +
          3. Otherwise, return.

          4. +
          + +

          Check popover validity is called again because firing the beforetoggle event could have disconnected this element or + changed its popover attribute.

          +
        4. +
        +
      10. + +
      11. Remove element from the top + layer.

      12. + +
      13. Set element's popover visibility state to hidden.

      14. + +
      15. +

        If dontFireEvents is false, then:

        + +
          +
        1. Let oldState be "open".

        2. + +
        3. +

          If element's popover toggle task is not null, then:

          + +
            +
          1. Set oldState to element's popover toggle task's + old state.

          2. + +
          3. Remove element's popover toggle task's task from its task queue.

          4. + +
          5. Set element's popover toggle task to null.

          6. +
          +
        4. + +
        5. +

          Queue an element task given the user interaction task source and + element to run the following steps:

          + +
            +
          1. fire an event named toggle, using ToggleEvent, with the oldState attribute initialized to + oldState, and the newState + attribute initialized to "closed" at element.

          2. + +
          3. Set element's popover toggle task to null.

          4. +
          +
        6. + +
        7. Set element's popover toggle task to a struct with task set to the just-queued task and old + state set to oldState.

        8. +
        +
      16. + +
      17. Let previouslyFocusedElement be element's previously focused + element.

      18. + +
      19. +

        If previouslyFocusedElement is not null, then:

        + +
          +
        1. Set element's previously focused element to null.

        2. + +
        3. If focusPreviousElement is true, then run the focusing steps for + previouslyFocusedElement; the viewport should not be scrolled by doing this + step.

        4. +
        +
      20. +
      + +

      The togglePopover(force) method steps are:

      + +
        +
      1. If this's popover visibility state is showing, and force is not present or false, then + run the hide popover algorithm given this, true, false, and + true.

      2. + +
      3. Otherwise, if force is not present or true, then run show popover + given this and true.

      4. +
      + +

      To hide all popovers until, given an HTML element endpoint, a boolean + focusPreviousElement, and a boolean dontFireEvents:

      + +
        +
      1. Let document be endpoint's node document.

      2. + +
      3. +

        Let closeAllOpenPopovers be an algorithm which performs the following steps:

        + +
          +
        1. Let popover be document's topmost auto + popover.

        2. + +
        3. +

          While popover is not null:

          + +
            +
          1. Run the hide popover algorithm given popover, + focusPreviousElement, dontFireEvents, and false.

          2. + +
          3. Set popover to document's topmost auto + popover.

          4. +
          +
        4. +
        +
      4. + +
      5. If endpoint is null, then run closeAllOpenPopovers and + return.

      6. + +
      7. Let lastToHide be null.

      8. + +
      9. Let foundEndpoint be false.

      10. + +
      11. +

        For each popover in document's auto popover list:

        + +
          +
        1. If popover is endpoint, then set foundEndpoint to + true.

        2. + +
        3. Otherwise, if foundEndpoint is true, then set lastToHide to + popover and break.

        4. +
        +
      12. + +
      13. If foundEndpoint is false, then run closeAllOpenPopovers and + return.

      14. + +
      15. +

        While lastToHide is not null and lastToHide's popover visibility + state is showing and document's + auto popover list is not empty:

        + +
          +
        1. Run the hide popover algorithm given document's auto + popover list's last element, focusPreviousElement, + dontFireEvents, and false.

        2. +
        +
      16. +
      + +

      The hide all popovers until + algorithm is used in several cases to hide all popovers that don't stay open when something + happens. For example, during light-dismiss of a popover, this algorithm ensures that we close only + the popovers that aren't related to the node clicked by the user.

      + +

      To find the topmost popover ancestor, given a Node + newPopover, perform the following steps. They return an HTML element or null.

      + +
      +

      The topmost popover ancestor algorithm will return the topmost (highest in the + auto popover list) ancestor popover for the provided popover. Popovers can be + related to each other in several ways, creating a tree of popovers. There are two paths through + which one popover (call it the "child" popover) can have an ancestor popover (call it the + "parent" popover):

      + +
        +
      1. The popovers are nested within each other in the DOM tree. In this case, the descendant + popover is the "child" and its ancestor popover is the "parent".

      2. + +
      3. An invoking element (e.g. a button) has one of the invoking attributes (e.g. + popovertoggletarget) pointing to a popover. In + this case, the popover is the "child", and the DOM-contained popover of the invoking element is + the "parent". The invoker must be in a popover and reference an open popover.

      4. +
      + +

      In each of the relationships formed above, the parent popover must be strictly lower in the + auto popover list than the child popover, or it does not form a valid ancestral + relationship. This eliminates non-showing popovers and self-pointers (e.g. a popover with an + anchor attribute that points back to the same popover), and it allows for the construction of a + well-formed tree from the (possibly cyclic) graph of connections. For example, if two popovers + have anchors pointing to each other, the only valid relationship is that the first one to open is + the "parent" and the second is the "child". Only auto popovers are considered.

      +
      + +
        +
      1. Let popoverPositions be an empty map.

      2. + +
      3. Let index be 0.

      4. + +
      5. Let document be newPopover's node document.

      6. + +
      7. +

        For each popover in document's auto popover list:

        + +
          +
        1. Set popoverPositions[popover] to + index.

        2. + +
        3. Increment index by 1.

        4. +
        +
      8. + +
      9. Set popoverPositions[newPopover] to + index.

      10. + +
      11. Increment index by 1.

      12. + +
      13. Let topmostPopoverAncestor be null.

      14. + +
      15. +

        Let checkAncestor be an algorithm which performs the following steps given + candidate:

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

        2. + +
        3. Let candidateAncestor be the result of running nearest inclusive open + popover given candidate.

        4. + +
        5. If candidateAncestor is null, then return.

        6. + +
        7. Let candidatePosition be + popoverPositions[candidateAncestor].

        8. + +
        9. If topmostPopoverAncestor is null or + popoverPositions[topmostPopoverAncestor] is less than + candidatePosition, then set topmostPopoverAncestor to + candidateAncestor.

        10. +
        +
      16. + +
      17. Run checkAncestor given the result of running nearest inclusive open + popover given newPopover's parent node within the flat + tree.

      18. + +
      19. +

        For each invoker in document's popover invokers:

        + +
          +
        1. If invoker's popover target element is newPopover, + then run checkAncestor given invoker.

        2. +
        +
      20. + +
      21. return topmostPopoverAncestor.

      22. +
      + +

      To find the nearest inclusive open popover given a Node + node, perform the following steps. They return an HTML + element or null.

      + +
        +
      1. Let currentNode be node.

      2. + +
      3. +

        While currentNode is not null:

        + +
          +
        1. If currentNode's popover attribute is in + the auto state and currentNode's + popover visibility state is showing, + then return currentNode.

        2. + +
        3. Set currentNode to currentNode's parent in the flat + tree.

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

      6. +
      + +

      To find the topmost auto popover given a + Document document, perform the following steps. They return an HTML element or null.

      + +
        +
      1. If document's auto popover list is not empty, then return + document's auto popover list's last element.

      2. + +
      3. Return null.

      4. +
      + +

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

      + +
        +
      1. Let control be the focus delegate of subject given + "other" and true.

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

      4. + +
      5. Run the focusing steps given control.

      6. + +
      7. Let topDocument be the active document of control's + node document's browsing context's + top-level browsing context.

      8. + +
      9. If control's node document's origin is not the same + as the origin of topDocument, then + return.

      10. + +
      11. Empty topDocument's + autofocus candidates.

      12. + +
      13. Set topDocument's autofocus processed flag to true.

      14. +
      + +

      To check popover validity for an HTML element + element given a boolean expectedToBeShowing, perform the following steps. + They return a boolean.

      + +
        +
      1. If element's popover attribute is in the + no popover state, then return false.

      2. + +
      3. If element is not connected, then return false.

      4. + +
      5. If expectedToBeShowing is true and element's popover + visibility state is not showing, then return + false.

      6. + +
      7. If expectedToBeShowing is false and element's popover + visibility state is not hidden, then return + false.

      8. + +
      9. If element is a dialog element and has the open attribute, then return false.

      10. + +
      11. If element's fullscreen flag is set, then return false.

      12. + +
      13. Return true.

      14. +
      + +

      The popover target attributes

      + +

      Buttons may have the following content attributes, known + as the popover target attributes:

      + +
        +
      • popovertoggletarget

      • + +
      • popoverhidetarget

      • + +
      • popovershowtarget

      • +
      + +

      The popover target attributes allow certain types of buttons to show and hide + element with the popover attribute. If a popover target + attribute is specified, then that attribute's value must be the ID of element with the popover attribute.

      + +
      +

      The following shows how popovershowtarget can + be used to open a popover:

      + +
      <div popover=auto id="foo">
      +  This is a popover!
      +</div>
      +
      +<button popovershowtarget="foo">
      +  Show a popover
      +</button>
      + +

      The following shows how popovertoggletarget + can open and close a manual popover, which can't be closed with light dismiss:

      + +
      <div popover=manual id="foo">
      +  This is a popover!
      +</div>
      +
      +<button popovertoggletarget="foo">
      +  Show or hide a popover
      +</button>
      +
      + + DOM interface: +
      interface mixin PopoverTargetElement {
      +  [CEReactions] attribute Element? popoverToggleTargetElement;
      +  [CEReactions] attribute Element? popoverHideTargetElement;
      +  [CEReactions] attribute Element? popoverShowTargetElement;
      +};
      + +

      The popoverToggleTargetElement IDL attribute must + reflect the popovertoggletarget + attribute.

      + +

      The popoverHideTargetElement IDL attribute must + reflect the popoverhidetarget + attribute.

      + +

      The popoverShowTargetElement IDL attribute must + reflect the popovershowtarget + attribute.

      + +

      To run the popover target attribute activation behavior given a Node + node:

      + +
        +
      1. Let popover be node's popover target element.

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

      4. + +
      5. +

        If node doesn't have the popovertoggletarget attribute, then:

        + +
          +
        1. If node has a popovershowtarget attribute and popover's + popover visibility state is showing, + then return.

        2. + +
        3. If node has a popoverhidetarget attribute and popover's + popover visibility state is hidden, + then return.

        4. +
        +
      6. + +
      7. If popover's popover visibility state is showing and the result of running check popover + validity given popover and true is true, then run the hide popover + algorithm given popover, true, false, and false.

      8. + +
      9. +

        Otherwise, if popover's popover visibility state is hidden and the result of running check popover + validity given popover and false is true:

        + +
          +
        1. Set popover's popover invoker to node.

        2. + +
        3. Run show popover given popover and false.

        4. +
        +
      10. +
      + +

      To get the popover target element given a Node node, perform + the following steps. They return an HTML element or null.

      + +
        +
      1. If node is not a button, then return + null.

      2. + +
      3. If node is disabled, then return + null.

      4. + +
      5. If node has a form owner and node is a submit button, then return null.

      6. + +
      7. Let idref be null.

      8. + +
      9. If node has a popovertoggletarget attribute, then set + idref to the value of node's popovertoggletarget attribute.

      10. + +
      11. Otherwise, if node has a popovershowtarget attribute, then set idref + to the value of node's popovershowtarget attribute.

      12. + +
      13. Otherwise, if node has a popoverhidetarget attribute, then set idref + to the value of node's popoverhidetarget attribute.

      14. + +
      15. If idref is null, then return null.

      16. + +
      17. Let popoverElement be the first element in tree + order, within node's root's descendants, whose ID is + idref; otherwise, if there is no such element, null.

      18. + +
      19. If popoverElement is null, then return null.

      20. + +
      21. If popoverElement's popover attribute is in + the no popover state, then return null.

      22. + +
      23. Return popoverElement.

      24. +
      + +

      To get the popover invokers given a Document document:

      + +
        +
      1. Return an HTMLCollection rooted at document whose filter matches + elements which are buttons.

      2. +
      + +

      Popover light dismiss

      + +

      "Light dismiss" means that pressing the Esc key or clicking outside of + a popover whose popover attribute is in the auto state will close the popover.

      + +

      Canceling popovers: When Document has a + topmost auto popover showing, user agents may provide a user interface that, upon + activation, queues an element task on the user + interaction task source given topmost auto popover to run the hide + popover algorithm given the topmost auto popover, true, and false.

      + +

      To light dismiss open popovers, given an Event event and a + Node node:

      + +
        +
      1. Assert: event's isTrusted attribute is true.

      2. + +
      3. Let topmostPopover be the result of running topmost auto popover + given node.

      4. + +
      5. If topmostPopover is null, then return.

      6. + +
      7. Let target be event's target.

      8. + +
      9. Let document be node's node document.

      10. + +
      11. +

        If event is a PointerEvent and event's type is pointerdown, + then:

        + +
          +
        1. Set document's popover pointerdown target to the result of + running topmost clicked popover given target and "inclusive".

        2. +
        +
      12. + +
      13. +

        If event is a PointerEvent and event's type is pointerup, + then:

        + +
          +
        1. Let ancestor be the result of running topmost clicked popover + given target.

        2. + +
        3. Let sameTarget be true if ancestor is document's + popover pointerdown target.

        4. + +
        5. Set document's popover pointerdown target to null.

        6. + +
        7. If sameTarget is true, then run hide + all popovers until given ancestor, false, and false.

        8. +
        +
      14. +
      + +

      Light dismiss open popovers is called by the Pointer Events spec when the user clicks + or touches anywhere on the page.

      + +

      To find the topmost clicked popover, given a Node node:

      + +
        +
      1. Let clickedPopover be the result of running nearest inclusive open + popover given node.

      2. + +
      3. Let invokerPopover be the result of running nearest inclusive target + popover for invoker given node.

      4. + +
      5. +

        Let getStackPosition be an algorithm which performs the following steps given + an HTML element popover:

        + +
          +
        1. Let popoverList be popover's node document's + auto popover list.

        2. + +
        3. If popover is in popoverList, then return the index of + popover in popoverList + 1.

        4. + +
        5. Return 0.

        6. +
        +
      6. + +
      7. If the result of running getStackPosition given clickedPopover is + greater than the result of running getStackPosition given invokerPopover, + then return clickedPopover.

      8. + +
      9. Return invokerPopover.

      10. +
      + +

      To find the nearest inclusive target popover for invoker given a Node + node:

      + +
        +
      1. Let currentNode be node.

      2. + +
      3. +

        While currentNode is not null:

        + +
          +
        1. Let targetPopover be currentNode's popover target + element.

        2. + +
        3. If targetPopover is not null and targetPopover's popover attribute is in the auto state and targetPopover's popover + visibility state is showing, then return + targetPopover.

        4. + +
        5. Set currentNode to currentNode's ancestor in the flat + tree.

        6. +
        +
      4. +
      + +

      The ToggleEvent interface

      + +
      [Exposed=Window]
      +interface ToggleEvent : Event {
      +  constructor(DOMString type, optional ToggleEventInit eventInitDict = {});
      +  readonly attribute DOMString oldState;
      +  readonly attribute DOMString newState;
      +};
      +
      +dictionary ToggleEventInit : EventInit {
      +  DOMString oldState = "";
      +  DOMString newState = "";
      +};
      + +
      +
      event.oldState
      + +
      +

      Set to "closed" when transitioning from closed to open, or set to + "open" when transitioning from open to closed.

      +
      + +
      event.newState
      + +
      +

      Set to "open" when transitioning from closed to open, or set to "closed" when transitioning from open to closed.

      +
      +
      + +

      The oldState attribute must return the value it + was initialized to. It is initialized to "open" if the element with the + popover attribute's popover visibility state is + showing; otherwise "closed".

      + +

      The newState attribute must return the value it was + initialized to. It is initialized to "closed" if the element with the popover attribute's popover visibility state is showing; otherwise "open".

      Loading web pages

      @@ -101111,6 +102357,7 @@ typedef OnBeforeUnloadEventHandlerNonNull? OnBeforeUnl onauxclick auxclick onbeforeinput beforeinput onbeforematch beforematch + onbeforetoggle beforetoggle oncancel cancel oncanplay canplay oncanplaythrough canplaythrough @@ -121677,6 +122924,33 @@ dialog::backdrop { background: rgba(0,0,0,0.1); } +[popover]:closed:not(dialog[open]) { + display:none; +} +dialog[popover]:not(:closed) { + display:block; +} + +[popover] { + position: fixed; + inset: 0; + width: fit-content; + height: fit-content; + margin: auto; + border: solid; + padding: 0.25em; + overflow: auto; + color: CanvasText; + background-color: Canvas; +} + +[popover]:open::backdrop { + position: fixed; + inset: 0; + pointer-events: none !important; + background-color: transparent; +} + slot { display: contents; }
      @@ -126872,6 +128146,9 @@ interface External { formnovalidate; formtarget; name; + popoverhidetarget; + popovershowtarget; + popovertoggletarget; type; value HTMLButtonElement @@ -127353,6 +128630,9 @@ interface External { name; pattern; placeholder; + popoverhidetarget; + popovershowtarget; + popovertoggletarget; readonly; required; size; @@ -129331,6 +130611,27 @@ interface External { video Encourage the user agent to display video content within the element's playback area Boolean attribute + + popover + HTML elements + Makes the element a popover element + "auto"; + "manual"; + + popoverhidetarget + input; button + Hides the specified popover element when clicked + ID of the element to hide + + popovershowtarget + input; button + Shows the specified popover element when clicked + ID of the element to show + + popovertoggletarget + input; button + Toggles the specified popover element when clicked + ID of the element to toggle poster video @@ -129711,6 +131012,12 @@ interface External { beforeunload event handler for Window object Event handler content attribute + + onbeforetoggle + HTML elements + beforetoggle event handler + Event handler content attribute + onblur HTML elements @@ -130701,6 +132008,13 @@ INSERT INTERFACES HERE data-x="attr-hidden-until-found-state">hidden=until-found attribute before they are revealed. + + beforetoggle + Event + Elements + Fired on elements with the popover attribute when they + are transitioning between showing and hidden. + beforeunload BeforeUnloadEvent From 0c983bfd83d4d331172ab8b5bf9790cde8a298d0 Mon Sep 17 00:00:00 2001 From: Anne van Kesteren Date: Fri, 27 Jan 2023 10:07:12 +0100 Subject: [PATCH 2/4] nits --- source | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/source b/source index 73c01ff8470..8e71faf2419 100644 --- a/source +++ b/source @@ -132010,10 +132010,10 @@ INSERT INTERFACES HERE beforetoggle - Event + ToggleEvent Elements Fired on elements with the popover attribute when they - are transitioning between showing and hidden. + are transitioning between showing and hidden beforeunload @@ -132211,9 +132211,11 @@ INSERT INTERFACES HERE toggle - Event - details element - Fired at details elements when they open or close + Event and ToggleEvent + details and popover elements + Fired at details elements when they open or close; fired on elements with the + popover attribute when they are transitioning between + showing and hidden unhandledrejection @@ -132232,7 +132234,7 @@ INSERT INTERFACES HERE Event Document Fired at the Document object when the page becomes visible or hidden to the - user. + user From 322338fa349d9418bfb5e541a957c0c0211068f5 Mon Sep 17 00:00:00 2001 From: Anne van Kesteren Date: Fri, 27 Jan 2023 10:57:10 +0100 Subject: [PATCH 3/4] refactor to share steps + nits --- source | 62 ++++++++++++++-------------------------------------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/source b/source index 8e71faf2419..543417e87dd 100644 --- a/source +++ b/source @@ -81494,8 +81494,14 @@ dictionary DragEventInit : MouseEventInit { data-x="attr-popover-none-state">no popover state, then set element's previously focused element to originallyFocusedElement.

    7. -
    8. Let oldState be "closed".

    9. +
    10. Queue a popover toggle event task given element, "closed", and "open".

    11. +
    + +

    To queue a popover toggle event task given an element element, a string + oldState, and a string newState: +

    1. If element's popover toggle task is not null, then:

      @@ -81518,8 +81524,8 @@ dictionary DragEventInit : MouseEventInit {
    2. fire an event named toggle, using ToggleEvent, with the oldState attribute initialized to oldState, - and the newState attribute initialized to "open" at element.

    3. + and the newState attribute initialized to + newState at element.

    4. Set element's popover toggle task to null.

    @@ -81622,47 +81628,9 @@ dictionary DragEventInit : MouseEventInit {
  • Set element's popover visibility state to hidden.

  • -
  • -

    If dontFireEvents is false, then:

    - -
      -
    1. Let oldState be "open".

    2. - -
    3. -

      If element's popover toggle task is not null, then:

      - -
        -
      1. Set oldState to element's popover toggle task's - old state.

      2. - -
      3. Remove element's popover toggle task's task from its task queue.

      4. - -
      5. Set element's popover toggle task to null.

      6. -
      -
    4. - -
    5. -

      Queue an element task given the user interaction task source and - element to run the following steps:

      - -
        -
      1. fire an event named toggle, using ToggleEvent, with the oldState attribute initialized to - oldState, and the newState - attribute initialized to "closed" at element.

      2. - -
      3. Set element's popover toggle task to null.

      4. -
      -
    6. - -
    7. Set element's popover toggle task to a struct with task set to the just-queued task and old - state set to oldState.

    8. -
    -
  • +
  • If dontFireEvents is false, then queue a popover toggle event task + given element, "open", and "closed".

  • Let previouslyFocusedElement be element's previously focused element.

  • @@ -82165,7 +82133,7 @@ dictionary DragEventInit : MouseEventInit { -

    Light dismiss open popovers is called by the Light dismiss open popovers will be called by the Pointer Events spec when the user clicks or touches anywhere on the page.

    @@ -132165,7 +132133,7 @@ INSERT INTERFACES HERE pointercancel PointerEvent Elements and Text nodes - Fired at the source node when the user attempts to initiate a drag-and-drop operation. + Fired at the source node when the user attempts to initiate a drag-and-drop operation popstate @@ -132211,7 +132179,7 @@ INSERT INTERFACES HERE toggle - Event and ToggleEvent + Event or ToggleEvent details and popover elements Fired at details elements when they open or close; fired on elements with the popover attribute when they are transitioning between From 3a3a9ed01259e0a324a6a0e8954871fef98e501d Mon Sep 17 00:00:00 2001 From: Joey Arhar Date: Fri, 27 Jan 2023 09:08:05 -0800 Subject: [PATCH 4/4] remove target parameter from popover light dismiss --- source | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/source b/source index 543417e87dd..150d57ae49e 100644 --- a/source +++ b/source @@ -82084,22 +82084,21 @@ dictionary DragEventInit : MouseEventInit { interaction task source given topmost auto popover to run the hide popover algorithm given the topmost auto popover, true, and false.

    -

    To light dismiss open popovers, given an Event event and a - Node node:

    +

    To light dismiss open popovers, given an Event event:

    1. Assert: event's isTrusted attribute is true.

    2. +
    3. Let target be event's target.

    4. +
    5. Let topmostPopover be the result of running topmost auto popover - given node.

    6. + given target.

    7. If topmostPopover is null, then return.

    8. -
    9. Let target be event's target.

    10. - -
    11. Let document be node's node document.

    12. +
    13. Let document be target's node document.

    14. If event is a PointerEvent and event's