From 9aecb051a6687a71ff401e4742ac813e539ab229 Mon Sep 17 00:00:00 2001 From: Anne van Kesteren Date: Tue, 17 Dec 2024 18:32:24 +0100 Subject: [PATCH] Add Scoped Custom Element Registries Do not comment directly on this PR while it is in draft state. Use #10854 instead. DOM PR: https://github.com/whatwg/dom/pull/1341. Tests: ... --- source | 282 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 218 insertions(+), 64 deletions(-) diff --git a/source b/source index 0639bb4821c..365ed24cae1 100644 --- a/source +++ b/source @@ -3319,8 +3319,9 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute no-quirks mode
  • The algorithm clone a node with its arguments document, - subtree, and - parent, and the concept of + subtree, + parent, and + fallbackRegistry, and the concept of cloning steps
  • The concept of base URL change steps and the definition of what happens when an element is affected by a base URL change
  • The concept of an element's unique identifier (ID)
  • @@ -12044,9 +12045,6 @@ console.assert(el.constructor === Element); overridden constructor steps:

      -
    1. Let registry be current global object's custom element registry.

    2. -
    3. If NewTarget is equal to the active function object, then throw a TypeError.

      @@ -12067,6 +12065,24 @@ document.createElement("bad-1"); // (2)
    4. +
    5. Let registry be null.

    6. + +
    7. +

      If current global object's active custom element constructor + map[NewTarget] exists:

      + +
        +
      1. Set registry to current global object's active custom + element constructor map[NewTarget].

      2. + +
      3. Remove current global object's + active custom element constructor map[NewTarget].

      4. +
      + +
    8. If registry is null, then set registry to current global + object's custom element + registry.

    9. +
    10. Let definition be the item in registry's custom element definition set with shadowrootdelegatesfocus

      shadowrootclonable
      shadowrootserializable
      +
      shadowrootcustomelementregistry
      Accessibility considerations:
      For authors.
      @@ -64057,6 +64074,7 @@ interface HTMLTemplateElement : HTMLElement { [CEReactions] attribute boolean shadowRootDelegatesFocus; [CEReactions] attribute boolean shadowRootClonable; [CEReactions] attribute boolean shadowRootSerializable; + [CEReactions] attribute DOMString shadowRootCustomElementRegistry; };
      Uses HTMLTemplateElement.
      @@ -64105,6 +64123,10 @@ interface HTMLTemplateElement : HTMLElement { data-x="attr-template-shadowrootserializable">shadowrootserializable content attribute is a boolean attribute.

      +

      The shadowrootcustomelementregistry + content attribute is a boolean attribute.

      +

      The template contents of a template element are not children of the element itself.

      @@ -64257,6 +64279,15 @@ interface HTMLTemplateElement : HTMLElement { must reflect the shadowrootserializable content attribute.

      +

      The shadowRootCustomElementRegistry + IDL attribute must reflect the shadowrootcustomelementregistry + content attribute.

      + +

      The IDL attribute does intentionally not have a boolean type so it can be + extended.

      +

      The cloning steps for template @@ -72617,18 +72648,15 @@ document.body.append(parent);

      Controls attachShadow(). -

      To look up a custom element definition, given a document, - namespace, localName, and is, perform the following steps. They - will return either a custom element definition or null:

      +

      To look up a custom element definition, given null or a + CustomElementRegistry object registry, string-or-null + namespace, string localName, and string-or-null is, perform the + following steps. They will return either a custom element definition or null:

        -
      1. If namespace is not the HTML namespace, then return null.

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

      4. -
      5. If document's browsing context is - null, then return null.

      6. - -
      7. Let registry be document's relevant global object's - custom element registry.

      8. +
      9. If namespace is not the HTML namespace, then return null.

      10. If registry's custom element definition set contains an item with +

        Each Window object has an associated active custom + element constructor map, which is a map of constructors to + CustomElementRegistry objects.

        +

        The customElements attribute of the Window interface must return the CustomElementRegistry object for that @@ -72668,11 +72700,16 @@ document.body.append(parent);

        [Exposed=Window]
         interface CustomElementRegistry {
        -  [CEReactions] undefined define(DOMString name, CustomElementConstructor constructor, optional ElementDefinitionOptions options = {});
        -  (CustomElementConstructor or undefined) get(DOMString name);
        -  DOMString? getName(CustomElementConstructor constructor);
        -  Promise<CustomElementConstructor> whenDefined(DOMString name);
        -  [CEReactions] undefined upgrade(Node root);
        +  constructor();
        +
        +  [CEReactions] undefined define(DOMString name, CustomElementConstructor constructor, optional ElementDefinitionOptions options = {});
        +  (CustomElementConstructor or undefined) get(DOMString name);
        +  DOMString? getName(CustomElementConstructor constructor);
        +  Promise<CustomElementConstructor> whenDefined(DOMString name);
        +  [CEReactions] undefined upgrade(Node root);
        +
        +  HTMLElement createElement(DOMString name);
        +  Node cloneSubtree(Node root);
         };
         
         callback CustomElementConstructor = HTMLElement ();
        @@ -72681,6 +72718,12 @@ dictionary ElementDefinitionOptions {
           DOMString extends;
         };
        +

        Every CustomElementRegistry has a is + scoped, a boolean, initially false.

        + +

        Every CustomElementRegistry has a scoped + document set, a set of Document objects, initially « ».

        +

        Every CustomElementRegistry has a custom element definition set, a set of custom element definitions, initially « ». Lookup of items in this set uses their ElementDefinitionOptions { element names to promises. It is used to implement the whenDefined() method.

        +

        To look up a custom element registry, given an Element object, + ShadowRoot object, or Document object parentNode: + +

          +
        1. If parentNode is an Element object, then return + parentNode's custom element + registry. + +

        2. If parentNode is a ShadowRoot object, then return + parentNode's custom element + registry. + +

        3. If parentNode's browsing context is + null, then return null.

        4. + +
        5. Return parentNode's relevant global object's custom element registry.

        6. +
        +
        -
        window.customElements.define(name, - constructor)
        +
        registry = window.customElements
        +
        Returns the global CustomElementRegistry object.
        + +
        registry = new CustomElementRegistry()
        +
        Constructs a new CustomElementRegistry object, for scoped usage.
        +
        registry.define(name, + constructor)
        Defines a new custom element, mapping the given name to the given constructor as an autonomous custom element.
        - -
        window.customElements.define(name, constructor, +
        registry.define(name, constructor, { extends: baseLocalName })
        -
        Defines a new custom element, mapping the given name to the given constructor as a customized built-in element for the element type identified by the supplied baseLocalName. A "NotSupportedError" DOMException will be thrown upon trying to extend a custom element or an unknown element.
        -
        window.customElements.get(name)
        - +
        registry.get(name)
        Retrieves the custom element constructor defined for the given name. Returns undefined if there is no custom element definition with the given name.
        -
        window.customElements.getName(constructor)
        - +
        registry.getName(constructor)
        Retrieves the given name for a custom element defined for the given constructor. Returns null if there is no custom element definition with the given constructor.
        -
        window.customElements.whenDefined(name)
        - +
        registry.whenDefined(name)
        Returns a promise that will be fulfilled with the custom element's constructor when a custom element becomes defined with the given name. (If such a custom element is already defined, the returned promise will be immediately fulfilled.) Returns a promise rejected with a "SyntaxError" DOMException if not given a valid custom element name.
        -
        window.customElements.upgrade(root)
        - +
        registry.upgrade(root)
        Tries to upgrade all shadow-including inclusive descendant elements of root, even if they are not connected.
        + +
        registry.createElement(name)
        +
        +

        Returns an HTML element with name as its local name and + registry as its registry.

        + +

        If name does not match the Name production an + "InvalidCharacterError" DOMException will be thrown.

        +
        + +
        registry.cloneSubtree(root)
        +
        Returns a copy of root, changing the registry from root's inclusive + descendants from null (if any) to registry in the process.
        +

        The new CustomElementRegistry() constructor + steps are to set this's is scoped to true.

        +

        Element definition is a process of adding a custom element definition to the CustomElementRegistry. This is accomplished by the define() method. The ElementDefinitionOptions {

        If extends is not null:

          +
        1. If this is not this's relevant global object's + custom element registry, then throw a + "NotSupportedError" DOMException.

        2. +
        3. If extends is a valid custom element name, then throw a "NotSupportedError" DOMException.

        4. @@ -72915,22 +72997,14 @@ dictionary ElementDefinitionOptions {
        5. Append definition to this's custom element definition set.

        6. -
        7. Let document be this's relevant global object's associated Document.

        8. +
        9. If this's is scoped is true, then for each document + of this's scoped document set: upgrade particular elements within + a document given document and localName. -

        10. Let upgradeCandidates be all elements that are shadow-including descendants of document, whose namespace - is the HTML namespace and whose local name is localName, in - shadow-including tree order. Additionally, if extends is non-null, only - include elements whose is - value is equal to name.

        11. - -
        12. For each element element of upgradeCandidates, enqueue a - custom element upgrade reaction given element and - definition.

        13. - - +
        14. Otherwise, upgrade particular elements within a document given + this's relevant global object's associated Document, localName, + and name.

        15. If this's when-defined promise map[name] shadow-including descendants of document, whose namespace + is the HTML namespace and whose local name is localName, in + shadow-including tree order. Additionally, if name is not + localName, only include elements whose is value is equal to name.

        16. + +
        17. For each element element of upgradeCandidates: enqueue a + custom element upgrade reaction given element and + definition.

        18. +
        + +

        The get(name) method steps are:

        @@ -73025,9 +73118,9 @@ fetch(articleURL)
        -

        When invoked, the upgrade(root) method must run - these steps:

        +

        The upgrade(root) method steps + are:

        1. Let candidates be a list of all of root's @@ -73059,6 +73152,29 @@ console.assert(el instanceof SpiderMan); // upgraded!

          +

          The createElement(name) + method steps are:

          + +
            +
          1. If name does not match the XML Name production, + then throw an "InvalidCharacterError" + DOMException.

          2. + +
          3. Return the result of creating an element given + this's relevant global object's associated Document, name, the + HTML namespace, null, null, true, and this.

          4. +
          + +

          The cloneSubtree(root) + method steps are to return the result of cloning a node + given root with subtree set to + true and fallbackRegistry set + this.

        2. + +

          Upgrades

          To upgrade an element, given as input a @@ -73130,8 +73246,15 @@ customElements.define("x-foo", class extends HTMLElement {

        3. Let C be definition's constructor.

        4. +
        5. Let registry be element's custom element registry.

        6. + +
        7. Set registry's relevant global + object's active custom element constructor map[C] to + registry.

        8. +
        9. -

          Run the following substeps while catching any exceptions:

          +

          Run the following steps while catching any exceptions:

          1. @@ -73173,10 +73296,18 @@ customElements.define("x-foo", class extends HTMLElement {
          -

          Then, perform the following substep, regardless of whether the above steps threw an exception +

          Then, perform the following steps, regardless of whether the above steps threw an exception or not:

            +
          1. +

            Remove registry's relevant global + object's active custom element constructor map[C].

            + +

            This is a no-op if C immediately calls super() as it ought to do.

            +
          2. +
          3. Remove the last entry from the end of definition's construction stack.

            @@ -73232,15 +73363,16 @@ customElements.define("x-foo", class extends HTMLElement { element state to "custom".

          -

          To try to upgrade an element, given as input an - element element, run the following steps:

          +

          To try to upgrade an element given an element + element:

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

          2. + definition">looking up a custom element definition given element's custom element registry, element's + namespace, element's local name, and element's is value.

          3. If definition is not null, then enqueue a custom element upgrade reaction given element and definition.

          4. @@ -73594,9 +73726,10 @@ customElements.define("x-foo", class extends HTMLElement { DOMException.

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

          6. + definition">looking up a custom element definition given this's custom element registry, this's + namespace, this's local name, and null.

          7. If definition is null, then throw an "NotSupportedError" DOMException.

          8. @@ -129197,8 +129330,10 @@ dictionary StorageEventInit : EventInit {
          9. Let is be the value of the "is" attribute in the given token, if such an attribute exists, or null otherwise.

          10. +
          11. Obtain registry from the parser context.

          12. +
          13. Let definition be the result of looking up a custom element definition given document, given + definition">looking up a custom element definition given registry, given namespace, local name, and is.

          14. Let willExecuteScript be true if definition is non-null and the @@ -130092,6 +130227,13 @@ document.body.appendChild(text); data-x="attr-template-shadowrootdelegatesfocus">shadowrootdelegatesfocus attribute; otherwise false.

          15. +
          16. Let registry be declarative shadow host element's custom element registry.

          17. + +
          18. If template start tag has a shadowrootcustomelementregistry + attribute, then set registry to null.

          19. +
          20. If declarative shadow host element is a shadow host, then insert an element at the adjusted insertion location with template.

          21. @@ -130103,7 +130245,8 @@ document.body.appendChild(text);
          22. Attach a shadow root with declarative shadow host element, mode, clonable, - serializable, delegatesFocus, and "named".

            + serializable, delegatesFocus, "named", and + registry.

            If an exception is thrown, then catch it and:

            @@ -133931,6 +134074,11 @@ document.body.appendChild(text);
          23. If shadow's clonable is set, then append " shadowrootclonable=""".

          24. +
          25. If current node's custom + element registry is not shadow's custom element registry, then append + " shadowrootcustomelementregistry=""".

          26. +
          27. Append ">".

          28. Append the value of running the HTML fragment serialization algorithm with @@ -141654,7 +141802,8 @@ interface External { shadowrootmode; shadowrootdelegatesfocus; shadowrootclonable; - shadowrootserializable + shadowrootserializable; + shadowrootcustomelementregistry HTMLTemplateElement @@ -143116,6 +143265,11 @@ interface External { template Sets clonable on a declarative shadow root Boolean attribute + + shadowrootcustomelementregistry + template + Enables declarative shadow roots to indicate they will use a custom element registry + Boolean attribute shadowrootdelegatesfocus template