From dacf095ee142a7f1fdfa3195854c9fd42aaf850d Mon Sep 17 00:00:00 2001 From: Anne van Kesteren Date: Tue, 17 Dec 2024 18:29:50 +0100 Subject: [PATCH] Add Scoped Custom Element Registries HTML PR: https://github.com/whatwg/html/pull/10869. Tests: ... Closes #1339. --- dom.bs | 364 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 252 insertions(+), 112 deletions(-) diff --git a/dom.bs b/dom.bs index 44de9d21..f6f7588a 100644 --- a/dom.bs +++ b/dom.bs @@ -2821,9 +2821,22 @@ before a child, with an optional suppress observers flag, run
  • Run the insertion steps with inclusiveDescendant.

  • -

    If inclusiveDescendant is connected: +

    If inclusiveDescendant is connected and is an element:

      +
    1. If inclusiveDescendant's custom element registry is + null, then set inclusiveDescendant's custom element registry to + the result of looking up a custom element registry given + inclusiveDescendant's parent. + + +

    2. Otherwise, if inclusiveDescendant's + custom element registry's is scoped is true, append + inclusiveDescendant's node document to + inclusiveDescendant's custom element registry's + scoped document set. + +

    3. If inclusiveDescendant is custom, then enqueue a custom element callback reaction with inclusiveDescendant, callback name "connectedCallback", and « ». @@ -2836,9 +2849,29 @@ before a child, with an optional suppress observers flag, run connectedCallback will be enqueued automatically during the upgrade an element algorithm.

    -
  • + +
  • +

    Otherwise, if inclusiveDescendant is connected and is a + shadow root: + +

      +
    1. If

    2. inclusiveDescendant's custom element registry + is null and
    3. inclusiveDescendant's + keep custom element registry null is false, then set + inclusiveDescendant's custom element registry to the result + of looking up a custom element registry given inclusiveDescendant's + host. + + +
    4. Otherwise, if inclusiveDescendant's + custom element registry is non-null and + inclusiveDescendant's custom element registry's + is scoped is true, append inclusiveDescendant's + node document to inclusiveDescendant's + custom element registry's scoped document set. + +

    -
  • If suppress observers flag is unset, then queue a tree mutation record for @@ -4547,15 +4580,17 @@ and <{template}>. SVG ought to do the same for its <{script}> elements, but does

    To clone a node given a node node and an optional document document (default node's node document), boolean -subtree (default false), and -node-or-null parent (default null): +subtree (default false), node-or-null +parent (default null), and null or a +{{CustomElementRegistry}} object fallbackRegistry +(default null):

    1. Assert: node is not a document or node is document. -

    2. Let copy be the result of cloning a single node given node and - document. +

    3. Let copy be the result of cloning a single node given node, + document, and fallbackRegistry.

    4. Run any cloning steps defined for node in other applicable specifications and pass node, copy, and @@ -4567,8 +4602,9 @@ and an optional document docume

    5. If subtree is true, then for each child of node's children, in tree order: clone a node given child with document set to document, - subtree set to subtree, and - parent set to copy. + subtree set to subtree, + parent set to copy, and + fallbackRegistry set to fallbackRegistry.

    6. If node is an element, node is a @@ -4578,11 +4614,18 @@ and an optional document docume

      1. Assert: copy is not a shadow host. +

      2. Let shadowRootRegistry be node's shadow root's + custom element registry. + +

      3. If shadowRootRegistry is null, then set shadowRootRegistry to + fallbackRegistry. +

      4. Attach a shadow root with copy, node's shadow root's mode, true, node's shadow root's serializable, node's - shadow root's delegates focus, and node's - shadow root's slot assignment. + shadow root's delegates focus, node's + shadow root's slot assignment, and + shadowRootRegistry.

      5. Set copy's shadow root's declarative to node's shadow root's declarative. @@ -4590,8 +4633,9 @@ and an optional document docume

      6. For each child of node's shadow root's children, in tree order: clone a node given child with document set to document, - subtree set to subtree, and - parent set to copy's shadow root. + subtree set to subtree, + parent set to copy's shadow root, + and fallbackRegistry set to fallbackRegistry.

    7. Return copy. @@ -4599,8 +4643,9 @@ and an optional document docume

      -

      To clone a single node given a node node and -document document: +

      To clone a single node given a node node, +document document, and null or a {{CustomElementRegistry}} object +fallbackRegistry:

      1. Let copy be null. @@ -4609,10 +4654,15 @@ and an optional document docume

        If node is an element:

          +
        1. Let registry be node's custom element registry. + +

        2. If registry is null, then set registry to + fallbackRegistry. +

        3. Set copy to the result of creating an element, given document, node's local name, node's - namespace, node's namespace prefix, and - node's is value. + namespace, node's namespace prefix, + node's is value, false, and registry.

        4. For each attribute of node's @@ -4620,7 +4670,7 @@ and an optional document docume

          1. Let copyAttribute be the result of cloning a single node given - attribute and document. + attribute, document, and null.

          2. Append copyAttribute to copy.

          @@ -6051,6 +6101,9 @@ interface ShadowRoot : DocumentFragment { readonly attribute boolean clonable; readonly attribute boolean serializable; readonly attribute Element host; + + readonly attribute CustomElementRegistry? customElements; + attribute EventHandler onslotchange; }; @@ -6061,22 +6114,23 @@ enum SlotAssignmentMode { "manual", "named" };

          {{ShadowRoot}} nodes are simply known as shadow roots. +

          Shadow roots's associated host is never null.

          + +

          Shadow roots have an associated mode ("open" or "closed").

          -

          Shadow roots have an associated delegates focus. -It is initially set to false.

          +

          Shadow roots have an associated delegates focus +(a boolean). It is initially set to false.

          Shadow roots have an associated -available to element internals. It is initially set to false.

          +available to element internals (a boolean). It is initially set to +false.

          Shadow roots have an associated declarative (a boolean). It is initially set to false.

          -

          Shadow roots's associated host is never null.

          - -

          Shadow roots have an associated slot assignment ("manual" or "named"). @@ -6086,12 +6140,26 @@ It is initially set to false.

          Shadow roots have an associated serializable (a boolean). It is initially set to false.

          +

          Shadow roots have an associated custom element registry +(null or a {{CustomElementRegistry}} object).

          + +

          Shadow roots have an associated +keep custom element registry null (a boolean). It is initially false. + +

          This can only ever be true in combination with declarative shadow roots. And it only +matters for as long as the shadow root's custom element registry +is null. + +


          +

          A shadow root's get the parent algorithm, given an event, returns null if event's composed flag is unset and shadow root is the root of event's path's first struct's invocation target; otherwise shadow root's host. +


          +

          The mode getter steps are to return this's mode.

          @@ -6110,6 +6178,13 @@ null if event's composed flag is unset and shadow roo

          The host getter steps are to return this's host. +


          + +

          The customElements getter steps are to return +this's custom element registry. + +


          +

          The onslotchange attribute is an event handler IDL attribute for the onslotchange event handler, whose @@ -6222,6 +6297,8 @@ interface Element : Node { ShadowRoot attachShadow(ShadowRootInit init); readonly attribute ShadowRoot? shadowRoot; + readonly attribute CustomElementRegistry? customElements; + Element? closest(DOMString selectors); boolean matches(DOMString selectors); boolean webkitMatchesSelector(DOMString selectors); // legacy alias of .matches @@ -6240,28 +6317,47 @@ dictionary ShadowRootInit { SlotAssignmentMode slotAssignment = "named"; boolean clonable = false; boolean serializable = false; + CustomElementRegistry customElements; };

          {{Element}} nodes are simply known as elements. -

          Elements have an associated -namespace, -namespace prefix, -local name, -custom element state, -custom element definition, -is value. When an -element is created, all of these values are +

          Elements have an associated: + +

          +
          namespace +
          Null or a non-empty string. + +
          namespace prefix +
          Null or a non-empty string. + +
          local name +
          A non-empty string. + +
          custom element registry +
          Null or a {{CustomElementRegistry}} object. + +
          custom element state +
          "undefined", "failed", "uncustomized", + "precustomized", or "custom". + +
          custom element definition +
          Null or a custom element definition. + + +
          is value +
          Null or a valid custom element name. +
          + +

          When an element is created, all of these values are initialized. -

          An element's custom element state is one of -"undefined", "failed", "uncustomized", -"precustomized", or "custom". An element whose -custom element state is "uncustomized" or "custom" is -said to be defined. An element -whose custom element state is "custom" is said to be +

          An element whose custom element state is +"uncustomized" or "custom" is said to be +defined. An element whose +custom element state is "custom" is said to be custom.

          Whether or not an element is defined is used to determine the @@ -6334,14 +6430,18 @@ value of these steps:

          To create an element, given a document document, string localName, string-or-null namespace, and optionally a string-or-null prefix (default null), string-or-null is (default -null), and boolean synchronousCustomElements (default false): +null), boolean synchronousCustomElements (default false), and null or a +{{CustomElementRegistry}} object registry:

          1. Let result be null. -

          2. Let definition be the result of - looking up a custom element definition given - document, namespace, localName, and is. +

          3. If registry is not given, then set registry to the result of + looking up a custom element registry given document. + + +

          4. Let definition be the result of looking up a custom element definition + given registry, namespace, localName, and is.

          5. If definition is non-null, and definition's @@ -6353,13 +6453,9 @@ null), and boolean synchronousCustomElements (default false):

          6. Let interface be the element interface for localName and the HTML namespace. -

          7. Set result to a new element that implements interface, - with no attributes, namespace set to the HTML namespace, - namespace prefix set to prefix, local name set - to localName, custom element state set to "undefined", - custom element definition set to null, - is value set to is, and node document set to - document. +

          8. Set result to the result of creating an element internal given + document, interface, localName, the HTML namespace, + prefix, "undefined", is, and registry.

          9. If synchronousCustomElements is true, then run this step while catching any @@ -6391,74 +6487,82 @@ null), and boolean synchronousCustomElements (default false):

            1. -

              If synchronousCustomElements is true, then run these steps while catching any - exceptions: +

              If synchronousCustomElements is true:

              1. Let C be definition's constructor. -

              2. Set result to the result of constructing C, with no - arguments. - -

              3. Assert: result's custom element state and - custom element definition are initialized. +

              4. Set the surrounding agent's + active custom element constructor map[C] to registry. +

              5. -

                Assert: result's namespace is the HTML namespace. +

                Run these steps while catching any exceptions: -

                IDL enforces that result is an {{HTMLElement}} object, which all use - the HTML namespace. +

                  +
                1. Set result to the result of constructing C, with no + arguments. -

                2. If result's attribute list is not empty, - then throw a "{{NotSupportedError!!exception}}" {{DOMException}}. +

                3. Assert: result's custom element state and + custom element definition are initialized. -

                4. If result has children, then throw a - "{{NotSupportedError!!exception}}" {{DOMException}}. +

                5. +

                  Assert: result's namespace is the HTML namespace. -

                6. If result's parent is not null, then throw a - "{{NotSupportedError!!exception}}" {{DOMException}}. +

                  IDL enforces that result is an {{HTMLElement}} object, which all + use the HTML namespace. -

                7. If result's node document is not document, then - throw a "{{NotSupportedError!!exception}}" {{DOMException}}. +

                8. If result's attribute list is not empty, + then throw a "{{NotSupportedError!!exception}}" {{DOMException}}. -

                9. If result's local name is not equal to - localName, then throw a "{{NotSupportedError!!exception}}" {{DOMException}}. +

                10. If result has children, then throw a + "{{NotSupportedError!!exception}}" {{DOMException}}. -

                11. Set result's namespace prefix to prefix. +

                12. If result's parent is not null, then throw a + "{{NotSupportedError!!exception}}" {{DOMException}}. -

                13. Set result's is value to null. -

                +
              6. If result's node document is not document, then + throw a "{{NotSupportedError!!exception}}" {{DOMException}}. -

                If any of these steps threw an exception exception: +

              7. If result's local name is not equal to + localName, then throw a "{{NotSupportedError!!exception}}" {{DOMException}}. -

                  -
                1. Report exception for definition's - constructor's corresponding JavaScript object's - associated realm's global object. +

                2. Set result's namespace prefix to prefix. + +

                3. Set result's is value to null. -

                4. Set result to a new element that implements the - {{HTMLUnknownElement}} interface, with no attributes, namespace set to the - HTML namespace, namespace prefix set to prefix, - local name set to localName, - custom element state set to "failed", - custom element definition set to null, - is value set to null, and node document set to - document. +

                5. Set result's custom element registry to + registry. +

                + +

                If any of these steps threw an exception exception: + +

                  +
                1. Report exception for + definition's constructor's corresponding + JavaScript object's associated realm's global object. + +

                2. Set result to the result of creating an element internal given + document, {{HTMLUnknownElement}}, localName, the HTML namespace, + prefix, "failed", null, and registry. +

                + +
              8. +

                Remove the surrounding agent's + active custom element constructor map[C]. + + +

                Under normal circumstances it will already have been removed at this point.

              -
            2. Otherwise:

                -
              1. Set result to a new element that implements the {{HTMLElement}} - interface, with no attributes, namespace set to the HTML namespace, - namespace prefix set to prefix, local name set - to localName, custom element state set to - "undefined", custom element definition set to null, - is value set to null, and node document set to - document. +

              2. Set result to the result of creating an element internal given + document, {{HTMLElement}}, localName, the HTML namespace, + prefix, "undefined", null, and registry.

              3. Enqueue a custom element upgrade reaction given result and definition. @@ -6474,13 +6578,9 @@ null), and boolean synchronousCustomElements (default false):

              4. Let interface be the element interface for localName and namespace. -

              5. Set result to a new element that implements interface, - with no attributes, namespace set to namespace, - namespace prefix set to prefix, local name set - to localName, custom element state set to - "uncustomized", custom element definition set to null, - is value set to is, and node document set to - document. +

              6. Set result to the result of creating an element internal given + document, interface, localName, namespace, + prefix, "uncustomized", is, and registry.

              7. If namespace is the HTML namespace, and either localName is a valid custom element name or is is non-null, then set result's @@ -6491,6 +6591,26 @@ null), and boolean synchronousCustomElements (default false):

              8. Return result.

              +

              To create an element internal given a document document, an +interface interface a string localName, a string-or-null namespace, +a string-or-null prefix, a string state, a string-or-null is, and +null or a {{CustomElementRegistry}} object registry: + +

                +
              1. Let element be a new element that implements interface, + with namespace set to namespace, namespace prefix + set to prefix, local name set to localName, + custom element registry set to registry, + custom element state set to state, + custom element definition set to null, is value + set to is, and node document set to document. + +

              2. Assert: element's attribute list + is empty. + +

              3. Return element. +

              +

              Elements also have an attribute list, which is a list exposed through a {{NamedNodeMap}}. Unless explicitly given when an @@ -7054,12 +7174,12 @@ are:


              -
              var shadow = element . {{attachShadow(init)}} +
              shadow = element . {{attachShadow(init)}}

              Creates a shadow root for element and returns it. -

              var shadow = element . {{shadowRoot}} +
              shadow = element . {{shadowRoot}}

              Returns element's shadow root, if any, and if - shadow root's mode is "open", and null otherwise. + shadow root's mode is "open"; otherwise null.

              A valid shadow host name is: @@ -7090,11 +7210,16 @@ are:

              The attachShadow(init) method steps are:

                +
              1. Let registry be this's custom element registry. + +

              2. If init["{{ShadowRootInit/customElements}}"] exists, then set + registry to it. +

              3. Run attach a shadow root with this, init["{{ShadowRootInit/mode}}"], init["{{ShadowRootInit/clonable}}"], init["{{ShadowRootInit/serializable}}"], - init["{{ShadowRootInit/delegatesFocus}}"], and - init["{{ShadowRootInit/slotAssignment}}"]. + init["{{ShadowRootInit/delegatesFocus}}"], + init["{{ShadowRootInit/slotAssignment}}"], and registry.

              4. Return this's shadow root.

              @@ -7103,8 +7228,8 @@ are:

              To attach a shadow root, given an element element, a string mode, a boolean clonable, -a boolean serializable, a boolean delegatesFocus, and a string -slotAssignment: +a boolean serializable, a boolean delegatesFocus, a string +slotAssignment, and null or a {{CustomElementRegistry}} object registry:

              1. If element's namespace is not the HTML namespace, @@ -7119,10 +7244,10 @@ a boolean serializable, a boolean delegatesFocus, and a st element's is value is non-null:

                  -
                1. Let definition be the result of - looking up a custom element definition given - element's node document, its namespace, its - local name, and its is value. +

                2. Let definition be the result of looking up a custom element definition + given element's custom element registry, its + namespace, its local name, and its + is value.

                3. If definition is not null and definition's disable shadow is true, then throw a @@ -7178,6 +7303,9 @@ a boolean serializable, a boolean delegatesFocus, and a st

                4. Set shadow's serializable to serializable. +

                5. Set shadow's custom element registry to + registry. +

                6. Set element's shadow root to shadow.

              @@ -7197,6 +7325,18 @@ a boolean serializable, a boolean delegatesFocus, and a st
              +
              +
              registry = element . {{Element/customElements}} +

              Returns element's {{CustomElementRegistry}} object, if any; otherwise null. +

              + +
              +

              The customElements getter steps are to return +this's custom element registry. +

              + +
              +
              element . {{closest(selectors)}}
              Returns the first (starting at element)