Changeset 205340 in webkit


Ignore:
Timestamp:
Sep 1, 2016 11:17:17 PM (8 years ago)
Author:
rniwa@webkit.org
Message:

Only update connected custom elements
https://bugs.webkit.org/show_bug.cgi?id=161480

Reviewed by Yusuke Suzuki.

Source/WebCore:

In the latest specs, creating an element only upgrades an element if the custom element had already been defined:
https://dom.spec.whatwg.org/#concept-create-element

Otherwise, an element remains unresolved until it gets connected to the document associated with the global object:
https://dom.spec.whatwg.org/#concept-node-insert

This patch removes the upgrade candidate map in CustomElementRegistry, and traverses the entire document associated
with global object (DOMWindow) in addElementDefinition: https://html.spec.whatwg.org/#dom-customelementregistry-define

The traversal is done in the shadow-including tree order (different from depth-first preorder traversal of flat tree)
since it doesn't enter slots and children of shadow hosts are always visited even if they are not assigned to a slot:
https://dom.spec.whatwg.org/#concept-shadow-including-tree-order

Test: fast/custom-elements/enqueue-custom-element-upgrade-reaction.html

  • bindings/js/JSCustomElementInterface.cpp:

(WebCore::JSCustomElementInterface::upgradeElement): Assert that the element being upgraded as the same qualified name
as the custom element interface.

  • bindings/js/JSCustomElementRegistryCustom.cpp:

(WebCore::JSCustomElementRegistry::define): Moved the code to resolve the promise from here to addElementDefinition.
Also cleaned up the code to extract callbacks a little.

  • dom/CustomElementReactionQueue.cpp:

(WebCore::CustomElementReactionQueue::enqueueElementUpgrade): Added an assertion.
(WebCore::CustomElementReactionQueue::enqueueElementUpgradeIfDefined): Added. Upgrade an element if the custom element
had already been defined.

  • dom/CustomElementReactionQueue.h:
  • dom/CustomElementRegistry.cpp:

(WebCore::CustomElementRegistry::create): Stores the reference to DOMWindow to find its document in addElementDefinition.
(WebCore::CustomElementRegistry::CustomElementRegistry): Ditto.
(WebCore::enqueueUpgradeInShadowIncludingTreeOrder): Added. Enqueue upgrade reactions in shadow-including tree order.
(WebCore::CustomElementRegistry::addElementDefinition): Upgrade all unresolved elements that matches this definition and
resolve the the promise returned by "whenDefined" if there is any.
(WebCore::CustomElementRegistry::addUpgradeCandidate): Deleted.
(WebCore::CustomElementRegistry::findInterface): Added a new variant that takes an element.

  • dom/CustomElementRegistry.h:
  • dom/Document.cpp:

(WebCore::createUpgradeCandidateElement): No longer takes DOMWindow since we don't upgrade synchronously here. It's also
wrong not to mark the element as unresolved custom element in a document without a browsing context per new semantics.
(WebCore::createHTMLElementWithNameValidation): Ditto.
(WebCore::createFallbackHTMLElement): Ditto.

  • dom/Element.cpp:

(WebCore::Element::insertedInto): Enqueue an upgrade reaction if this is an unsolved custom element and there is now
a definition for it (the latter condition is checked in enqueueElementUpgradeIfDefined).

  • html/parser/HTMLConstructionSite.cpp:

(WebCore::HTMLConstructionSite::createHTMLElementOrFindCustomElementInterface): Don't upgrade this element until it gets
connected to a document in Element::insertedInto.

  • page/DOMWindow.cpp:

(WebCore::DOMWindow::ensureCustomElementRegistry):

LayoutTests:

Added a W3c-style testharness.js test for https://html.spec.whatwg.org/#enqueue-a-custom-element-upgrade-reaction
and added more test cases for :defined and customElements.define.

  • fast/custom-elements/CustomElementRegistry.html: Revised descriptions for "get" and "whenDefined" test cases consistent

with ones for "define".

  • fast/custom-elements/defined-pseudo-class-expected.txt:
  • fast/custom-elements/defined-pseudo-class.html:
  • fast/custom-elements/enqueue-custom-element-upgrade-reaction-expected.txt: Added.
  • fast/custom-elements/enqueue-custom-element-upgrade-reaction.html: Added.
  • fast/custom-elements/resources/document-types.js:

(create):

Location:
trunk
Files:
2 added
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r205339 r205340  
     12016-09-01  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        Only update connected custom elements
     4        https://bugs.webkit.org/show_bug.cgi?id=161480
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        Added a W3c-style testharness.js test for https://html.spec.whatwg.org/#enqueue-a-custom-element-upgrade-reaction
     9        and added more test cases for :defined and customElements.define.
     10
     11        * fast/custom-elements/CustomElementRegistry.html: Revised descriptions for "get" and "whenDefined" test cases consistent
     12        with ones for "define".
     13        * fast/custom-elements/defined-pseudo-class-expected.txt:
     14        * fast/custom-elements/defined-pseudo-class.html:
     15        * fast/custom-elements/enqueue-custom-element-upgrade-reaction-expected.txt: Added.
     16        * fast/custom-elements/enqueue-custom-element-upgrade-reaction.html: Added.
     17        * fast/custom-elements/resources/document-types.js:
     18        (create):
     19
    1202016-09-01  Gyuyoung Kim  <gyuyoung.kim@webkit.org>
    221
  • trunk/LayoutTests/fast/custom-elements/CustomElementRegistry-expected.txt

    r205316 r205340  
    99PASS customElements.define must check IsConstructor on the constructor before checking the element definition is running flag
    1010PASS customElements.define must validate the custom element name before checking the element definition is running flag
     11PASS customElements.define unset the element definition is running flag before upgrading custom elements
    1112PASS customElements.define must not throw when defining another custom element in a different global object during Get(constructor, "prototype")
    1213PASS Custom Elements: CustomElementRegistry interface
     
    2425PASS customElements.define must not throw even if "observedAttributes" fails to convert if "attributeChangedCallback" is not defined
    2526PASS customElements.define must define an instantiatable custom element
     27PASS customElements.define must upgrade elements in the shadow-including tree order
    2628PASS CustomElementRegistry interface must have get as a method
    27 PASS "get" must return undefined when the registry does not contain an entry with the given name
    28 PASS "get" must return undefined when the registry does not contain an entry with the given name even if the name was not a valid custom element name
    29 PASS "get" return the constructor of the entry with the given name when there is a matching entry.
    30 PASS "whenDefined" must return a promise for a valid custom element name
    31 PASS "whenDefined" must return the same promise each time invoked for a valid custom element name which has not been defined
    32 PASS "whenDefined" must return an unresolved promise when the registry does not contain the entry with the given name
    33 PASS "whenDefined" must return a rejected promise when the given name is not a valid custom element name
    34 PASS "whenDefined" must return a resolved promise when the registry contains the entry with the given name
    35 PASS "whenDefined" must return a new resolved promise each time invoked when the registry contains the entry with the given name
    36 PASS A promise returned by "whenDefined" must be resolved by "define"
     29PASS customElements.get must return undefined when the registry does not contain an entry with the given name
     30PASS customElements.get must return undefined when the registry does not contain an entry with the given name even if the name was not a valid custom element name
     31PASS customElements.get return the constructor of the entry with the given name when there is a matching entry.
     32PASS customElements.whenDefined must return a promise for a valid custom element name
     33PASS customElements.whenDefined must return the same promise each time invoked for a valid custom element name which has not been defined
     34PASS customElements.whenDefined must return an unresolved promise when the registry does not contain the entry with the given name
     35PASS customElements.whenDefined must return a rejected promise when the given name is not a valid custom element name
     36PASS customElements.whenDefined must return a resolved promise when the registry contains the entry with the given name
     37PASS customElements.whenDefined must return a new resolved promise each time invoked when the registry contains the entry with the given name
     38PASS A promise returned by customElements.whenDefined must be resolved by "define"
    3739
  • trunk/LayoutTests/fast/custom-elements/CustomElementRegistry.html

    r205316 r205340  
    149149    assert_array_equals(calls, ['prototype'], 'customElements.define must get "prototype"');
    150150}, 'customElements.define must validate the custom element name before checking the element definition is running flag');
     151
     152test(function () {
     153    var unresolvedElement = document.createElement('constructor-calls-define');
     154    document.body.appendChild(unresolvedElement);
     155    var elementUpgradedDuringUpgrade = document.createElement('defined-during-upgrade');
     156    document.body.appendChild(elementUpgradedDuringUpgrade);
     157
     158    var DefinedDuringUpgrade = class extends HTMLElement { };
     159
     160    class ConstructorCallsDefine extends HTMLElement {
     161        constructor() {
     162            customElements.define('defined-during-upgrade', DefinedDuringUpgrade);
     163            assert_false(unresolvedElement instanceof ConstructorCallsDefine);
     164            assert_true(elementUpgradedDuringUpgrade instanceof DefinedDuringUpgrade);
     165            super();
     166            assert_true(unresolvedElement instanceof ConstructorCallsDefine);
     167            assert_true(elementUpgradedDuringUpgrade instanceof DefinedDuringUpgrade);
     168        }
     169    }
     170
     171    assert_false(unresolvedElement instanceof ConstructorCallsDefine);
     172    assert_false(elementUpgradedDuringUpgrade instanceof DefinedDuringUpgrade);
     173
     174    customElements.define('constructor-calls-define', ConstructorCallsDefine);
     175}, 'customElements.define unset the element definition is running flag before upgrading custom elements');
    151176
    152177(function () {
     
    385410
    386411test(function () {
     412    var disconnectedElement = document.createElement('some-custom');
     413    var connectedElementBeforeShadowHost = document.createElement('some-custom');
     414    var connectedElementAfterShadowHost = document.createElement('some-custom');
     415    var elementInShadowTree = document.createElement('some-custom');
     416    var childElementOfShadowHost = document.createElement('some-custom');
     417    var customShadowHost = document.createElement('some-custom');
     418    var elementInNestedShadowTree = document.createElement('some-custom');
     419
     420    var container = document.createElement('div');
     421    var shadowHost = document.createElement('div');
     422    var shadowRoot = shadowHost.attachShadow({mode: 'closed'});
     423    container.appendChild(connectedElementBeforeShadowHost);
     424    container.appendChild(shadowHost);
     425    container.appendChild(connectedElementAfterShadowHost);
     426    shadowHost.appendChild(childElementOfShadowHost);
     427    shadowRoot.appendChild(elementInShadowTree);
     428    shadowRoot.appendChild(customShadowHost);
     429
     430    var innerShadowRoot = customShadowHost.attachShadow({mode: 'closed'});
     431    innerShadowRoot.appendChild(elementInNestedShadowTree);
     432
     433    var calls = [];
     434    class SomeCustomElement extends HTMLElement {
     435        constructor() {
     436            super();
     437            calls.push(this);
     438        }
     439    };
     440
     441    document.body.appendChild(container);
     442    customElements.define('some-custom', SomeCustomElement);
     443    assert_array_equals(calls, [connectedElementBeforeShadowHost, elementInShadowTree, customShadowHost, elementInNestedShadowTree, childElementOfShadowHost, connectedElementAfterShadowHost]);
     444}, 'customElements.define must upgrade elements in the shadow-including tree order');
     445
     446test(function () {
    387447    assert_true('get' in CustomElementRegistry.prototype, '"get" exists on CustomElementRegistry.prototype');
    388448    assert_true('get' in customElements, '"get" exists on window.customElements');
     
    391451test(function () {
    392452    assert_equals(customElements.get('a-b'), undefined);
    393 }, '"get" must return undefined when the registry does not contain an entry with the given name');
     453}, 'customElements.get must return undefined when the registry does not contain an entry with the given name');
    394454
    395455test(function () {
     
    399459    assert_equals(customElements.get('g'), undefined);
    400460    assert_equals(customElements.get('ab'), undefined);
    401 }, '"get" must return undefined when the registry does not contain an entry with the given name even if the name was not a valid custom element name');
     461}, 'customElements.get must return undefined when the registry does not contain an entry with the given name even if the name was not a valid custom element name');
    402462
    403463test(function () {
     
    406466    customElements.define('existing-custom-element', ExistingCustomElement);
    407467    assert_equals(customElements.get('existing-custom-element'), ExistingCustomElement);
    408 }, '"get" return the constructor of the entry with the given name when there is a matching entry.');
     468}, 'customElements.get return the constructor of the entry with the given name when there is a matching entry.');
    409469
    410470test(function () {
    411471    assert_true(customElements.whenDefined('some-name') instanceof Promise);
    412 }, '"whenDefined" must return a promise for a valid custom element name');
     472}, 'customElements.whenDefined must return a promise for a valid custom element name');
    413473
    414474test(function () {
    415475    assert_equals(customElements.whenDefined('some-name'), customElements.whenDefined('some-name'));
    416 }, '"whenDefined" must return the same promise each time invoked for a valid custom element name which has not been defined');
     476}, 'customElements.whenDefined must return the same promise each time invoked for a valid custom element name which has not been defined');
    417477
    418478promise_test(function () {
     
    424484        assert_false(rejected, 'The promise returned by "whenDefined" must not be rejected until a custom element is defined');
    425485    });   
    426 }, '"whenDefined" must return an unresolved promise when the registry does not contain the entry with the given name')
     486}, 'customElements.whenDefined must return an unresolved promise when the registry does not contain the entry with the given name')
    427487
    428488promise_test(function () {
     
    437497        assert_true('rejected' in promise, 'The promise returned by "whenDefined" must not be rejected when a custom element is defined');
    438498    });
    439 }, '"whenDefined" must return a rejected promise when the given name is not a valid custom element name');
     499}, 'customElements.whenDefined must return a rejected promise when the given name is not a valid custom element name');
    440500
    441501promise_test(function () {
     
    454514        assert_false('rejected' in promise, 'The promise returned by "whenDefined" must not be rejected when a custom element is defined');
    455515    });
    456 }, '"whenDefined" must return a resolved promise when the registry contains the entry with the given name');
     516}, 'customElements.whenDefined must return a resolved promise when the registry contains the entry with the given name');
    457517
    458518promise_test(function () {
     
    480540        assert_false('rejected' in promise2, 'The promise returned by "whenDefined" must not be rejected when a custom element is defined');
    481541    });
    482 }, '"whenDefined" must return a new resolved promise each time invoked when the registry contains the entry with the given name');
     542}, 'customElements.whenDefined must return a new resolved promise each time invoked when the registry contains the entry with the given name');
    483543
    484544promise_test(function () {
     
    515575        assert_false('rejected' in promiseAfterDefine, 'The promise returned by "whenDefined" must not be rejected when a custom element is defined');
    516576    });
    517 }, 'A promise returned by "whenDefined" must be resolved by "define"');
     577}, 'A promise returned by customElements.whenDefined must be resolved by "define"');
    518578
    519579</script>
  • trunk/LayoutTests/fast/custom-elements/defined-pseudo-class-expected.txt

    r205060 r205340  
    11
    22PASS The defined flag of a custom element must not be set if a custom element has not been upgraded yet
     3PASS The defined flag of a custom element must not be set if a custom element has not been upgraded yet even if the element has been defined
    34PASS The defined flag of a custom element must be set when a custom element is successfully upgraded
    45PASS The defined flag of a custom element must be set if there is a matching definition
     6PASS The defined flag of an upgraded custom element must be set
    57PASS The defined flag of a custom element created by HTML parser must be unset if there is no matching definition
    68PASS The defined flag of a custom element created by HTML parser must be set if there is a matching definition
  • trunk/LayoutTests/fast/custom-elements/defined-pseudo-class.html

    r205060 r205340  
    2929
    3030test(function () {
     31    assert_false(upgradeCandidate.matches(':defined'));
     32}, 'The defined flag of a custom element must not be set if a custom element has not been upgraded yet even if the element has been defined');
     33
     34test(function () {
     35    document.body.appendChild(upgradeCandidate);
    3136    assert_true(upgradeCandidate.matches(':defined'));
    3237    assert_false(matchInsideConstructor, 'Upgrading a custom element must set defined flag after invoking the constructor');
     
    3843    assert_false(matchInsideConstructor, 'Creating a custom element must set defined flag after invoking the constructor');
    3944}, 'The defined flag of a custom element must be set if there is a matching definition');
     45
     46test(function () {
     47    var upgradedElement = document.createElement('my-element').cloneNode(true);
     48    assert_true(upgradedElement.matches(':defined'));
     49    assert_false(matchInsideConstructor, 'Creating a custom element must set defined flag after invoking the constructor');
     50}, 'The defined flag of an upgraded custom element must be set');
    4051
    4152document.write('<my-other-element></my-other-element>');
  • trunk/LayoutTests/fast/custom-elements/resources/document-types.js

    r205085 r205340  
    44        create: function () { return Promise.resolve(document); },
    55        isOwner: true,
     6        hasBrowsingContext: true,
    67    },
    78    {
     
    1516                resolve(doc);
    1617            });
    17         }
     18        },
     19        hasBrowsingContext: false,
    1820    },
    1921    {
     
    2527                resolve(doc);
    2628            });
    27         }
     29        },
     30        hasBrowsingContext: false,
    2831    },
    2932    {
     
    3538                resolve(doc);
    3639            });
    37         }
     40        },
     41        hasBrowsingContext: false,
    3842    },
    3943    {
     
    4145        create: function () {
    4246            return Promise.resolve(document.implementation.createHTMLDocument());
    43         }
     47        },
     48        hasBrowsingContext: false,
    4449    },
    4550    {
     
    4752        create: function () {
    4853            return Promise.resolve(document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null));
    49         }
     54        },
     55        hasBrowsingContext: false,
    5056    },
    5157    {
     
    5864                document.body.appendChild(iframe);
    5965            });
    60         }
     66        },
     67        hasBrowsingContext: true,
    6168    },
    6269    {
     
    7178                xhr.send();
    7279            });
    73         }
     80        },
     81        hasBrowsingContext: false,
    7482    }
    7583];
  • trunk/Source/WebCore/ChangeLog

    r205335 r205340  
     12016-09-01  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        Only update connected custom elements
     4        https://bugs.webkit.org/show_bug.cgi?id=161480
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        In the latest specs, creating an element only upgrades an element if the custom element had already been defined:
     9        https://dom.spec.whatwg.org/#concept-create-element
     10
     11        Otherwise, an element remains unresolved until it gets connected to the document associated with the global object:
     12        https://dom.spec.whatwg.org/#concept-node-insert
     13
     14        This patch removes the upgrade candidate map in CustomElementRegistry, and traverses the entire document associated
     15        with global object (DOMWindow) in addElementDefinition: https://html.spec.whatwg.org/#dom-customelementregistry-define
     16
     17        The traversal is done in the shadow-including tree order (different from depth-first preorder traversal of flat tree)
     18        since it doesn't enter slots and children of shadow hosts are always visited even if they are not assigned to a slot:
     19        https://dom.spec.whatwg.org/#concept-shadow-including-tree-order
     20
     21        Test: fast/custom-elements/enqueue-custom-element-upgrade-reaction.html
     22
     23        * bindings/js/JSCustomElementInterface.cpp:
     24        (WebCore::JSCustomElementInterface::upgradeElement): Assert that the element being upgraded as the same qualified name
     25        as the custom element interface.
     26        * bindings/js/JSCustomElementRegistryCustom.cpp:
     27        (WebCore::JSCustomElementRegistry::define): Moved the code to resolve the promise from here to addElementDefinition.
     28        Also cleaned up the code to extract callbacks a little.
     29        * dom/CustomElementReactionQueue.cpp:
     30        (WebCore::CustomElementReactionQueue::enqueueElementUpgrade): Added an assertion.
     31        (WebCore::CustomElementReactionQueue::enqueueElementUpgradeIfDefined): Added. Upgrade an element if the custom element
     32        had already been defined.
     33        * dom/CustomElementReactionQueue.h:
     34        * dom/CustomElementRegistry.cpp:
     35        (WebCore::CustomElementRegistry::create): Stores the reference to DOMWindow to find its document in addElementDefinition.
     36        (WebCore::CustomElementRegistry::CustomElementRegistry): Ditto.
     37        (WebCore::enqueueUpgradeInShadowIncludingTreeOrder): Added. Enqueue upgrade reactions in shadow-including tree order.
     38        (WebCore::CustomElementRegistry::addElementDefinition): Upgrade all unresolved elements that matches this definition and
     39        resolve the the promise returned by "whenDefined" if there is any.
     40        (WebCore::CustomElementRegistry::addUpgradeCandidate): Deleted.
     41        (WebCore::CustomElementRegistry::findInterface): Added a new variant that takes an element.
     42        * dom/CustomElementRegistry.h:
     43        * dom/Document.cpp:
     44        (WebCore::createUpgradeCandidateElement): No longer takes DOMWindow since we don't upgrade synchronously here. It's also
     45        wrong not to mark the element as unresolved custom element in a document without a browsing context per new semantics.
     46        (WebCore::createHTMLElementWithNameValidation): Ditto.
     47        (WebCore::createFallbackHTMLElement): Ditto.
     48        * dom/Element.cpp:
     49        (WebCore::Element::insertedInto): Enqueue an upgrade reaction if this is an unsolved custom element and there is now
     50        a definition for it (the latter condition is checked in enqueueElementUpgradeIfDefined).
     51        * html/parser/HTMLConstructionSite.cpp:
     52        (WebCore::HTMLConstructionSite::createHTMLElementOrFindCustomElementInterface): Don't upgrade this element until it gets
     53        connected to a document in Element::insertedInto.
     54        * page/DOMWindow.cpp:
     55        (WebCore::DOMWindow::ensureCustomElementRegistry):
     56
    1572016-09-01  Yusuke Suzuki  <utatane.tea@gmail.com>
    258
  • trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp

    r205198 r205340  
    105105void JSCustomElementInterface::upgradeElement(Element& element)
    106106{
     107    ASSERT(element.tagQName() == name());
    107108    ASSERT(element.isUnresolvedCustomElement());
    108109    if (!canInvokeCallback())
  • trunk/Source/WebCore/bindings/js/JSCustomElementRegistryCustom.cpp

    r205315 r205340  
    128128    auto elementInterface = JSCustomElementInterface::create(name, constructor, globalObject());
    129129
    130     auto* connectedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "connectedCallback"));
    131     if (state.hadException())
    132         return jsUndefined();
    133     if (connectedCallback)
     130    if (auto* connectedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "connectedCallback")))
    134131        elementInterface->setConnectedCallback(connectedCallback);
    135 
    136     auto* disconnectedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "disconnectedCallback"));
    137     if (state.hadException())
    138         return jsUndefined();
    139     if (disconnectedCallback)
     132    if (state.hadException())
     133        return jsUndefined();
     134
     135    if (auto* disconnectedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "disconnectedCallback")))
    140136        elementInterface->setDisconnectedCallback(disconnectedCallback);
    141 
    142     auto* adoptedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "adoptedCallback"));
    143     if (state.hadException())
    144         return jsUndefined();
    145     if (adoptedCallback)
     137    if (state.hadException())
     138        return jsUndefined();
     139
     140    if (auto* adoptedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "adoptedCallback")))
    146141        elementInterface->setAdoptedCallback(adoptedCallback);
     142    if (state.hadException())
     143        return jsUndefined();
    147144
    148145    auto* attributeChangedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "attributeChangedCallback"));
     
    162159    registry.addElementDefinition(WTFMove(elementInterface));
    163160
    164     // FIXME: 17. Let map be registry's upgrade candidates map.
    165     // FIXME: 18. Upgrade a newly-defined element given map and definition.
    166 
    167     auto& promiseMap = registry.promiseMap();
    168     auto promise = promiseMap.take(localName);
    169     if (promise)
    170         promise.value()->resolve(nullptr);
    171 
    172161    return jsUndefined();
    173162}
  • trunk/Source/WebCore/dom/CustomElementReactionQueue.cpp

    r205085 r205340  
    117117void CustomElementReactionQueue::enqueueElementUpgrade(Element& element, JSCustomElementInterface& elementInterface)
    118118{
     119    ASSERT(element.tagQName() == elementInterface.name());
    119120    if (auto* queue = CustomElementReactionStack::ensureCurrentQueue())
    120121        queue->m_items.append({CustomElementReactionQueueItem::Type::ElementUpgrade, element, elementInterface});
     122}
     123
     124void CustomElementReactionQueue::enqueueElementUpgradeIfDefined(Element& element)
     125{
     126    ASSERT(element.inDocument());
     127    ASSERT(element.isUnresolvedCustomElement());
     128    auto* window = element.document().domWindow();
     129    if (!window)
     130        return;
     131
     132    auto* registry = window->customElementRegistry();
     133    if (!registry)
     134        return;
     135
     136    auto* elementInterface = registry->findInterface(element);
     137    if (!elementInterface)
     138        return;
     139
     140    enqueueElementUpgrade(element, *elementInterface);
    121141}
    122142
  • trunk/Source/WebCore/dom/CustomElementReactionQueue.h

    r205085 r205340  
    4747
    4848    static void enqueueElementUpgrade(Element&, JSCustomElementInterface&);
     49    static void enqueueElementUpgradeIfDefined(Element&);
    4950    static void enqueueConnectedCallbackIfNeeded(Element&);
    5051    static void enqueueDisconnectedCallbackIfNeeded(Element&);
  • trunk/Source/WebCore/dom/CustomElementRegistry.cpp

    r205315 r205340  
    2929#if ENABLE(CUSTOM_ELEMENTS)
    3030
     31#include "CustomElementReactionQueue.h"
     32#include "DOMWindow.h"
    3133#include "Document.h"
    3234#include "Element.h"
     35#include "ElementTraversal.h"
    3336#include "JSCustomElementInterface.h"
    3437#include "JSDOMPromise.h"
     
    3639#include "QualifiedName.h"
    3740#include "SVGNames.h"
     41#include "ShadowRoot.h"
    3842#include <runtime/JSCJSValueInlines.h>
    3943#include <wtf/text/AtomicString.h>
     
    4145namespace WebCore {
    4246
    43 Ref<CustomElementRegistry> CustomElementRegistry::create()
     47Ref<CustomElementRegistry> CustomElementRegistry::create(DOMWindow& window)
    4448{
    45     return adoptRef(*new CustomElementRegistry());
     49    return adoptRef(*new CustomElementRegistry(window));
    4650}
    4751
    48 CustomElementRegistry::CustomElementRegistry()
     52CustomElementRegistry::CustomElementRegistry(DOMWindow& window)
     53    : m_window(window)
    4954{ }
    5055
    5156CustomElementRegistry::~CustomElementRegistry()
    5257{ }
     58
     59// https://dom.spec.whatwg.org/#concept-shadow-including-tree-order
     60static void enqueueUpgradeInShadowIncludingTreeOrder(ContainerNode& node, JSCustomElementInterface& elementInterface)
     61{
     62    for (Element* element = ElementTraversal::firstWithin(node); element; element = ElementTraversal::next(*element)) {
     63        if (element->isUnresolvedCustomElement() && element->tagQName() == elementInterface.name())
     64            CustomElementReactionQueue::enqueueElementUpgrade(*element, elementInterface);
     65        if (auto* shadowRoot = element->shadowRoot()) {
     66            if (shadowRoot->mode() != ShadowRoot::Mode::UserAgent)
     67                enqueueUpgradeInShadowIncludingTreeOrder(*shadowRoot, elementInterface);
     68        }
     69    }
     70}
    5371
    5472void CustomElementRegistry::addElementDefinition(Ref<JSCustomElementInterface>&& elementInterface)
     
    5977    m_nameMap.add(localName, elementInterface.copyRef());
    6078
    61     auto candidateList = m_upgradeCandidatesMap.find(localName);
    62     if (candidateList == m_upgradeCandidatesMap.end())
    63         return;
     79    if (auto* document = m_window.document())
     80        enqueueUpgradeInShadowIncludingTreeOrder(*document, elementInterface.get());
    6481
    65     Vector<RefPtr<Element>> list(WTFMove(candidateList->value));
    66 
    67     m_upgradeCandidatesMap.remove(localName);
    68 
    69     for (auto& candidate : list) {
    70         ASSERT(candidate);
    71         elementInterface->upgradeElement(*candidate);
    72     }
    73 
    74     // We should not be adding more upgrade candidate for this local name.
    75     ASSERT(!m_upgradeCandidatesMap.contains(localName));
     82    if (auto promise = m_promiseMap.take(localName))
     83        promise.value()->resolve(nullptr);
    7684}
    7785
    78 void CustomElementRegistry::addUpgradeCandidate(Element& candidate)
     86JSCustomElementInterface* CustomElementRegistry::findInterface(const Element& element) const
    7987{
    80     auto result = m_upgradeCandidatesMap.ensure(candidate.localName(), [] {
    81         return Vector<RefPtr<Element>>();
    82     });
    83     auto& nodeVector = result.iterator->value;
    84     ASSERT(!nodeVector.contains(&candidate));
    85     nodeVector.append(&candidate);
     88    return findInterface(element.tagQName());
    8689}
    8790
    8891JSCustomElementInterface* CustomElementRegistry::findInterface(const QualifiedName& name) const
    8992{
     93    ASSERT(!name.hasPrefix());
     94    if (name.namespaceURI() != HTMLNames::xhtmlNamespaceURI)
     95        return nullptr;
    9096    auto it = m_nameMap.find(name.localName());
    9197    return it == m_nameMap.end() || it->value->name() != name ? nullptr : const_cast<JSCustomElementInterface*>(it->value.ptr());
  • trunk/Source/WebCore/dom/CustomElementRegistry.h

    r205315 r205340  
    4444
    4545class CustomElementRegistry;
     46class DOMWindow;
    4647class DeferredWrapper;
    4748class Element;
     
    5152class CustomElementRegistry : public RefCounted<CustomElementRegistry> {
    5253public:
    53     static Ref<CustomElementRegistry> create();
     54    static Ref<CustomElementRegistry> create(DOMWindow&);
    5455    ~CustomElementRegistry();
    5556
    5657    void addElementDefinition(Ref<JSCustomElementInterface>&&);
    57     void addUpgradeCandidate(Element&);
    5858
    5959    bool& elementDefinitionIsRunning() { return m_elementDefinitionIsRunning; }
    6060
     61    JSCustomElementInterface* findInterface(const Element&) const;
    6162    JSCustomElementInterface* findInterface(const QualifiedName&) const;
    6263    JSCustomElementInterface* findInterface(const AtomicString&) const;
     
    6970
    7071private:
    71     CustomElementRegistry();
     72    CustomElementRegistry(DOMWindow&);
    7273
    73     HashMap<AtomicString, Vector<RefPtr<Element>>> m_upgradeCandidatesMap;
     74    DOMWindow& m_window;
    7475    HashMap<AtomicString, Ref<JSCustomElementInterface>> m_nameMap;
    7576    HashMap<const JSC::JSObject*, JSCustomElementInterface*> m_constructorMap;
  • trunk/Source/WebCore/dom/Document.cpp

    r205278 r205340  
    882882
    883883#if ENABLE(CUSTOM_ELEMENTS)
    884 static ALWAYS_INLINE RefPtr<HTMLElement> createUpgradeCandidateElement(Document& document, DOMWindow* window, const QualifiedName& name)
    885 {
    886     if (!window || !RuntimeEnabledFeatures::sharedFeatures().customElementsEnabled())
     884static ALWAYS_INLINE RefPtr<HTMLElement> createUpgradeCandidateElement(Document& document, const QualifiedName& name)
     885{
     886    if (!RuntimeEnabledFeatures::sharedFeatures().customElementsEnabled())
    887887        return nullptr;
    888888
     
    892892    auto element = HTMLElement::create(name, document);
    893893    element->setIsUnresolvedCustomElement();
    894     window->ensureCustomElementRegistry().addUpgradeCandidate(element.get());
    895894    return WTFMove(element);
    896895}
     
    922921
    923922#if ENABLE(CUSTOM_ELEMENTS)
    924     if (auto element = createUpgradeCandidateElement(document, window, qualifiedName))
     923    if (auto element = createUpgradeCandidateElement(document, qualifiedName))
    925924        return WTFMove(element);
    926925#endif
     
    11001099    }
    11011100    // FIXME: Should we also check the equality of prefix between the custom element and name?
    1102     if (auto element = createUpgradeCandidateElement(document, window, name))
     1101    if (auto element = createUpgradeCandidateElement(document, name))
    11031102        return element.releaseNonNull();
    11041103#endif
  • trunk/Source/WebCore/dom/Element.cpp

    r205249 r205340  
    16071607
    16081608#if ENABLE(CUSTOM_ELEMENTS)
    1609     if (becomeConnected && UNLIKELY(isCustomElement()))
    1610         CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded(*this);
     1609    if (becomeConnected) {
     1610        if (UNLIKELY(isUnresolvedCustomElement()))
     1611            CustomElementReactionQueue::enqueueElementUpgradeIfDefined(*this);
     1612        if (UNLIKELY(isCustomElement()))
     1613            CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded(*this);
     1614    }
     1615
    16111616#endif
    16121617
  • trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp

    r204732 r205340  
    679679            element = HTMLElement::create(qualifiedName, ownerDocument);
    680680            element->setIsUnresolvedCustomElement();
    681             window->ensureCustomElementRegistry().addUpgradeCandidate(*element);
    682681        } else
    683682#endif
  • trunk/Source/WebCore/page/DOMWindow.cpp

    r204986 r205340  
    626626{
    627627    if (!m_customElementRegistry)
    628         m_customElementRegistry = CustomElementRegistry::create();
     628        m_customElementRegistry = CustomElementRegistry::create(*this);
    629629    return *m_customElementRegistry;
    630630}
Note: See TracChangeset for help on using the changeset viewer.