defineElement should upgrade existing unresolved custom elements
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Mar 2016 02:33:12 +0000 (02:33 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Mar 2016 02:33:12 +0000 (02:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=155107

Reviewed by Darin Adler.

Source/WebCore:

Added the support for upgrading existing unresolved custom elements when defineElement is called.

The current implementation upgrades elements in the order they were created and has the issue that
it keeps accumulating all elements with a hyphen in its name until defineElement is called as
documented in https://github.com/w3c/webcomponents/issues/419

This patch re-purposes IsEditingTextFlag to indicate that the node is an unresolved custom element.
Since isEditingText() is only called in textRendererIsNeeded only on Text nodes, it's mutually
exclusive with isUnresolvedCustomElement().

The list of unresolved custom elements is kept in m_upgradeCandidatesMap, a hash map of element names
to the list of unresolved elements with that name.

In addition, added the logic to use HTMLElement as the interface for unresolved custom element instead
of HTMLUnknownElement.

Test: fast/custom-elements/upgrading/upgrading-parser-created-element.html

* bindings/js/JSCustomElementInterface.cpp:
(WebCore::JSCustomElementInterface::upgradeElement): Clear the flag.
* bindings/js/JSDocumentCustom.cpp:
(WebCore::JSDocument::defineElement): Set the unique private name to keep the interface alive before
calling addElementDefinition as the call can now invoke author scripts.
* dom/CustomElementDefinitions.cpp:
(WebCore::CustomElementDefinitions::addElementDefinition): Upgrade existing unresolved elements kept
in m_upgradeCandidatesMap.
(WebCore::CustomElementDefinitions::addUpgradeCandidate): Added.
* dom/CustomElementDefinitions.h:
* dom/Document.cpp:
(WebCore::createHTMLElementWithNameValidation): Added the code to add the unresolved custom elements
to the upgrade candidates map. Also instantiate it as HTMLElement instead of HTMLUnknownElement.
(WebCore::createFallbackHTMLElement): Ditto.
* dom/Node.h:
(WebCore::Node::setIsCustomElement):
(WebCore::Node::isUnresolvedCustomElement): Added.
(WebCore::Node::setIsUnresolvedCustomElement): Added.
(WebCore::Node::setCustomElementIsResolved): Added. Clears IsEditingTextOrUnresolvedCustomElementFlag
and sets IsCustomElement.
(WebCore::Node::isEditingText): Check both IsEditingTextOrUnresolvedCustomElementFlag and IsTextFlag
for safety even though it's currently only used in textRendererIsNeeded which takes Text&.
* dom/make_names.pl:
(defaultParametersHash): Added customElementInterfaceName as a parameter.
(printWrapperFactoryCppFile): Generate the code to use customElementInterfaceName when the element
for which the wrapper is created has isUnresolvedCustomElement flag set.
* html/HTMLTagNames.in: Use HTMLElement for unresolved custom elements.
* html/parser/HTMLConstructionSite.cpp:
(WebCore::HTMLConstructionSite::createHTMLElementOrFindCustomElementInterface): Added the code to add
the unresolved custom elements to the upgrade candidates map. Also instantiate it as HTMLElement instead
of HTMLUnknownElement.

LayoutTests:

Added W3C style testharness.js tests for asynchronously defining custom elements.

* fast/custom-elements/upgrading/Node-cloneNode.html:
* fast/custom-elements/upgrading/upgrading-parser-created-element-expected.txt: Added.
* fast/custom-elements/upgrading/upgrading-parser-created-element.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@197917 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/custom-elements/upgrading/Node-cloneNode.html
LayoutTests/fast/custom-elements/upgrading/upgrading-parser-created-element-expected.txt [new file with mode: 0644]
LayoutTests/fast/custom-elements/upgrading/upgrading-parser-created-element.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSCustomElementInterface.cpp
Source/WebCore/bindings/js/JSDocumentCustom.cpp
Source/WebCore/dom/CustomElementDefinitions.cpp
Source/WebCore/dom/CustomElementDefinitions.h
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Node.h
Source/WebCore/dom/make_names.pl
Source/WebCore/html/HTMLTagNames.in
Source/WebCore/html/parser/HTMLConstructionSite.cpp

index 5718153..23cfe53 100644 (file)
@@ -1,3 +1,16 @@
+2016-03-09  Ryosuke Niwa  <rniwa@webkit.org>
+
+        defineElement should upgrade existing unresolved custom elements
+        https://bugs.webkit.org/show_bug.cgi?id=155107
+
+        Reviewed by Darin Adler.
+
+        Added W3C style testharness.js tests for asynchronously defining custom elements.
+
+        * fast/custom-elements/upgrading/Node-cloneNode.html:
+        * fast/custom-elements/upgrading/upgrading-parser-created-element-expected.txt: Added.
+        * fast/custom-elements/upgrading/upgrading-parser-created-element.html: Added.
+
 2016-03-09  Saam Barati  <sbarati@apple.com>
 
         ES6: Implement lexical scoping for function definitions in strict mode
index 9f4fcc3..95aadad 100644 (file)
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html>
 <head>
-<title>Custom Elements: Extensions to Document interface</title>
+<title>Custom Elements: Upgrading</title>
 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
 <meta name="assert" content="Node.prototype.cloneNode should upgrade a custom element">
 <link rel="help" href="https://w3c.github.io/webcomponents/spec/custom/#upgrading">
diff --git a/LayoutTests/fast/custom-elements/upgrading/upgrading-parser-created-element-expected.txt b/LayoutTests/fast/custom-elements/upgrading/upgrading-parser-created-element-expected.txt
new file mode 100644 (file)
index 0000000..680cbf6
--- /dev/null
@@ -0,0 +1,6 @@
+
+PASS Element.prototype.createElement must add an unresolved custom element to the upgrade candidates map 
+PASS HTMLElement constructor must throw an InvalidStateError when the top of the construction stack is marked AlreadyConstructed due to a custom element constructor constructing itself after super() call 
+PASS HTMLElement constructor must throw an InvalidStateError when the top of the construction stack is marked AlreadyConstructed due to a custom element constructor constructing itself before super() call 
+PASS Upgrading a custom element must throw an InvalidStateError when the returned element is not SameValue as the upgraded element 
+
diff --git a/LayoutTests/fast/custom-elements/upgrading/upgrading-parser-created-element.html b/LayoutTests/fast/custom-elements/upgrading/upgrading-parser-created-element.html
new file mode 100644 (file)
index 0000000..61e827d
--- /dev/null
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: Upgrading unresolved elements</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="HTML parser must add an unresolved custom element to the upgrade candidates map">
+<link rel="help" href="https://w3c.github.io/webcomponents/spec/custom/#upgrading">
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<link rel='stylesheet' href='../../../resources/testharness.css'>
+</head>
+<body>
+<div id="log"></div>
+<my-custom-element></my-custom-element>
+<instantiates-itself-after-super></instantiates-itself-after-super>
+<instantiates-itself-before-super></instantiates-itself-before-super>
+<my-other-element id="instance"></my-other-element>
+<my-other-element id="otherInstance"></my-other-element>
+<script>
+
+test(function () {
+    class MyCustomElement extends HTMLElement { }
+
+    var instance = document.querySelector('my-custom-element');
+    assert_true(instance instanceof HTMLElement);
+    assert_false(instance instanceof HTMLUnknownElement,
+        'an unresolved custom element should not be an instance of HTMLUnknownElement');
+    assert_false(instance instanceof MyCustomElement);
+
+    document.defineElement('my-custom-element', MyCustomElement);
+
+    assert_true(instance instanceof HTMLElement);
+    assert_true(instance instanceof MyCustomElement,
+        'Calling defineElement must upgrade existing custom elements');
+
+}, 'Element.prototype.createElement must add an unresolved custom element to the upgrade candidates map');
+
+
+test(function () {
+    class InstantiatesItselfAfterSuper extends HTMLElement {
+        constructor(doNotCreateItself) {
+            super();
+            if (!doNotCreateItself)
+                new InstantiatesItselfAfterSuper(true);
+        }
+    }
+
+    assert_throws({'name': 'InvalidStateError'}, function () {
+        document.defineElement('instantiates-itself-after-super', InstantiatesItselfAfterSuper);
+    });
+}, 'HTMLElement constructor must throw an InvalidStateError when the top of the construction stack is marked AlreadyConstructed'
+    + ' due to a custom element constructor constructing itself after super() call');
+
+test(function () {
+    class InstantiatesItselfBeforeSuper extends HTMLElement {
+        constructor(doNotCreateItself) {
+            if (!doNotCreateItself)
+                new InstantiatesItselfBeforeSuper(true);
+            super();
+        }
+    }
+
+    assert_throws({'name': 'InvalidStateError'}, function () {
+        document.defineElement('instantiates-itself-before-super', InstantiatesItselfBeforeSuper);
+    });
+}, 'HTMLElement constructor must throw an InvalidStateError when the top of the construction stack is marked AlreadyConstructed'
+    + ' due to a custom element constructor constructing itself before super() call');
+
+test(function () {
+    class MyOtherElement extends HTMLElement {
+        constructor() {
+            super();
+            if (this == instance)
+                return otherInstance;
+        }
+    }
+    var instance = document.getElementById('instance');
+    var otherInstance = document.getElementById('otherInstance');
+
+    assert_false(instance instanceof MyOtherElement);
+    assert_false(otherInstance instanceof MyOtherElement);
+
+    assert_throws({'name': 'InvalidStateError'}, function () {
+        document.defineElement('my-other-element', MyOtherElement);
+    });
+
+    assert_true(document.createElement('my-other-element') instanceof MyOtherElement,
+        'Upgrading of custom elements must happen after the definition was added to the registry.');
+}, 'Upgrading a custom element must throw an InvalidStateError when the returned element is not SameValue as the upgraded element');
+
+</script>
+</body>
+</html>
index f7d564f..3d73db9 100644 (file)
@@ -1,3 +1,60 @@
+2016-03-09  Ryosuke Niwa  <rniwa@webkit.org>
+
+        defineElement should upgrade existing unresolved custom elements
+        https://bugs.webkit.org/show_bug.cgi?id=155107
+
+        Reviewed by Darin Adler.
+
+        Added the support for upgrading existing unresolved custom elements when defineElement is called.
+
+        The current implementation upgrades elements in the order they were created and has the issue that
+        it keeps accumulating all elements with a hyphen in its name until defineElement is called as
+        documented in https://github.com/w3c/webcomponents/issues/419
+
+        This patch re-purposes IsEditingTextFlag to indicate that the node is an unresolved custom element.
+        Since isEditingText() is only called in textRendererIsNeeded only on Text nodes, it's mutually
+        exclusive with isUnresolvedCustomElement().
+
+        The list of unresolved custom elements is kept in m_upgradeCandidatesMap, a hash map of element names
+        to the list of unresolved elements with that name.
+
+        In addition, added the logic to use HTMLElement as the interface for unresolved custom element instead
+        of HTMLUnknownElement.
+
+        Test: fast/custom-elements/upgrading/upgrading-parser-created-element.html
+
+        * bindings/js/JSCustomElementInterface.cpp:
+        (WebCore::JSCustomElementInterface::upgradeElement): Clear the flag.
+        * bindings/js/JSDocumentCustom.cpp:
+        (WebCore::JSDocument::defineElement): Set the unique private name to keep the interface alive before
+        calling addElementDefinition as the call can now invoke author scripts.
+        * dom/CustomElementDefinitions.cpp:
+        (WebCore::CustomElementDefinitions::addElementDefinition): Upgrade existing unresolved elements kept
+        in m_upgradeCandidatesMap.
+        (WebCore::CustomElementDefinitions::addUpgradeCandidate): Added.
+        * dom/CustomElementDefinitions.h:
+        * dom/Document.cpp:
+        (WebCore::createHTMLElementWithNameValidation): Added the code to add the unresolved custom elements
+        to the upgrade candidates map. Also instantiate it as HTMLElement instead of HTMLUnknownElement.
+        (WebCore::createFallbackHTMLElement): Ditto.
+        * dom/Node.h:
+        (WebCore::Node::setIsCustomElement):
+        (WebCore::Node::isUnresolvedCustomElement): Added.
+        (WebCore::Node::setIsUnresolvedCustomElement): Added.
+        (WebCore::Node::setCustomElementIsResolved): Added. Clears IsEditingTextOrUnresolvedCustomElementFlag
+        and sets IsCustomElement.
+        (WebCore::Node::isEditingText): Check both IsEditingTextOrUnresolvedCustomElementFlag and IsTextFlag
+        for safety even though it's currently only used in textRendererIsNeeded which takes Text&.
+        * dom/make_names.pl:
+        (defaultParametersHash): Added customElementInterfaceName as a parameter.
+        (printWrapperFactoryCppFile): Generate the code to use customElementInterfaceName when the element
+        for which the wrapper is created has isUnresolvedCustomElement flag set.
+        * html/HTMLTagNames.in: Use HTMLElement for unresolved custom elements.
+        * html/parser/HTMLConstructionSite.cpp:
+        (WebCore::HTMLConstructionSite::createHTMLElementOrFindCustomElementInterface): Added the code to add
+        the unresolved custom elements to the upgrade candidates map. Also instantiate it as HTMLElement instead
+        of HTMLUnknownElement. 
+
 2016-03-09  Enrica Casucci  <enrica@apple.com>
 
         Retrieve additional context for some data detector link for preview and action menu.
index 4823a81..18f3941 100644 (file)
@@ -104,7 +104,7 @@ RefPtr<Element> JSCustomElementInterface::constructElement(const AtomicString& t
 
 void JSCustomElementInterface::upgradeElement(Element& element)
 {
-    ASSERT(element.isCustomElement());
+    ASSERT(element.isUnresolvedCustomElement());
     if (!canInvokeCallback())
         return;
 
@@ -147,6 +147,7 @@ void JSCustomElementInterface::upgradeElement(Element& element)
         throwInvalidStateError(*state, "Custom element constructor failed to upgrade an element");
         return;
     }
+    wrappedElement->setCustomElementIsResolved();
     ASSERT(wrappedElement->isCustomElement());
 }
 
index 4b5f4a5..2ea8bb3 100644 (file)
@@ -180,11 +180,12 @@ JSValue JSDocument::defineElement(ExecState& state)
     // FIXME: 13. Let detachedCallback be Get(prototype, "detachedCallback"). Rethrow any exceptions.
     // FIXME: 14. Let attributeChangedCallback be Get(prototype, "attributeChangedCallback"). Rethrow any exceptions.
 
-    QualifiedName name(nullAtom, tagName, HTMLNames::xhtmlNamespaceURI);
-    definitions.addElementDefinition(JSCustomElementInterface::create(name, object, globalObject()));
     PrivateName uniquePrivateName;
     globalObject()->putDirect(globalObject()->vm(), uniquePrivateName, object);
 
+    QualifiedName name(nullAtom, tagName, HTMLNames::xhtmlNamespaceURI);
+    definitions.addElementDefinition(JSCustomElementInterface::create(name, object, globalObject()));
+
     // FIXME: 17. Let map be registry's upgrade candidates map.
     // FIXME: 18. Upgrade a newly-defined element given map and definition.
 
index 06d1920..2e44187 100644 (file)
@@ -74,7 +74,33 @@ void CustomElementDefinitions::addElementDefinition(Ref<JSCustomElementInterface
     AtomicString localName = interface->name().localName();
     ASSERT(!m_nameMap.contains(localName));
     m_constructorMap.add(interface->constructor(), interface.ptr());
-    m_nameMap.add(localName, WTFMove(interface));
+    m_nameMap.add(localName, interface.copyRef());
+
+    auto candidateList = m_upgradeCandidatesMap.find(localName);
+    if (candidateList == m_upgradeCandidatesMap.end())
+        return;
+
+    Vector<RefPtr<Element>> list(WTFMove(candidateList->value));
+
+    m_upgradeCandidatesMap.remove(localName);
+
+    for (auto& candidate : list) {
+        ASSERT(candidate);
+        interface->upgradeElement(*candidate);
+    }
+
+    // We should not be adding more upgrade candidate for this local name.
+    ASSERT(!m_upgradeCandidatesMap.contains(localName));
+}
+
+void CustomElementDefinitions::addUpgradeCandidate(Element& candidate)
+{
+    auto result = m_upgradeCandidatesMap.ensure(candidate.localName(), [] {
+        return Vector<RefPtr<Element>>();
+    });
+    auto& nodeVector = result.iterator->value;
+    ASSERT(!nodeVector.contains(&candidate));
+    nodeVector.append(&candidate);
 }
 
 JSCustomElementInterface* CustomElementDefinitions::findInterface(const QualifiedName& name) const
index d2498f4..6de0fdc 100644 (file)
@@ -49,6 +49,7 @@ class CustomElementDefinitions {
     WTF_MAKE_FAST_ALLOCATED;
 public:
     void addElementDefinition(Ref<JSCustomElementInterface>&&);
+    void addUpgradeCandidate(Element&);
 
     JSCustomElementInterface* findInterface(const QualifiedName&) const;
     JSCustomElementInterface* findInterface(const AtomicString&) const;
@@ -59,6 +60,7 @@ public:
     static NameStatus checkName(const AtomicString& tagName);
 
 private:
+    HashMap<AtomicString, Vector<RefPtr<Element>>> m_upgradeCandidatesMap;
     HashMap<AtomicString, RefPtr<JSCustomElementInterface>> m_nameMap;
     HashMap<const JSC::JSObject*, JSCustomElementInterface*> m_constructorMap;
 };
index c1d43fc..4ec2f41 100644 (file)
@@ -899,7 +899,18 @@ static RefPtr<Element> createHTMLElementWithNameValidation(Document& document, c
         return nullptr;
     }
 
-    return HTMLUnknownElement::create(QualifiedName(nullAtom, localName, xhtmlNamespaceURI), document);
+    QualifiedName qualifiedName(nullAtom, localName, xhtmlNamespaceURI);
+
+#if ENABLE(CUSTOM_ELEMENTS)
+    if (CustomElementDefinitions::checkName(localName) == CustomElementDefinitions::NameStatus::Valid) {
+        Ref<HTMLElement> element = HTMLElement::create(qualifiedName, document);
+        element->setIsUnresolvedCustomElement();
+        document.ensureCustomElementDefinitions().addUpgradeCandidate(element.get());
+        return WTFMove(element);
+    }
+#endif
+
+    return HTMLUnknownElement::create(qualifiedName, document);
 }
 
 RefPtr<Element> Document::createElementForBindings(const AtomicString& name, ExceptionCode& ec)
@@ -1080,11 +1091,18 @@ static Ref<HTMLElement> createFallbackHTMLElement(Document& document, const Qual
     if (UNLIKELY(definitions)) {
         if (auto* interface = definitions->findInterface(name)) {
             Ref<HTMLElement> element = HTMLElement::create(name, document);
-            element->setIsCustomElement(); // Pre-upgrade element is still considered a custom element.
+            element->setIsUnresolvedCustomElement();
             LifecycleCallbackQueue::enqueueElementUpgrade(element.get(), *interface);
             return element;
         }
     }
+    // FIXME: Should we also check the equality of prefix between the custom element and name?
+    if (CustomElementDefinitions::checkName(name.localName()) == CustomElementDefinitions::NameStatus::Valid) {
+        Ref<HTMLElement> element = HTMLElement::create(name, document);
+        element->setIsUnresolvedCustomElement();
+        document.ensureCustomElementDefinitions().addUpgradeCandidate(element.get());
+        return element;
+    }
 #endif
     return HTMLUnknownElement::create(name, document);
 }
index e435160..114ab8c 100644 (file)
@@ -266,7 +266,11 @@ public:
 
 #if ENABLE(CUSTOM_ELEMENTS)
     bool isCustomElement() const { return getFlag(IsCustomElement); }
-    void setIsCustomElement() { return setFlag(IsCustomElement); }
+    void setIsCustomElement() { setFlag(IsCustomElement); }
+
+    bool isUnresolvedCustomElement() const { return isElementNode() && getFlag(IsEditingTextOrUnresolvedCustomElementFlag); }
+    void setIsUnresolvedCustomElement() { setFlag(IsEditingTextOrUnresolvedCustomElementFlag); }
+    void setCustomElementIsResolved();
 #endif
 
     // Returns null, a child of ShadowRoot, or a legacy shadow root.
@@ -320,7 +324,7 @@ public:
     StyleChangeType styleChangeType() const { return static_cast<StyleChangeType>(m_nodeFlags & StyleChangeMask); }
     bool childNeedsStyleRecalc() const { return getFlag(ChildNeedsStyleRecalcFlag); }
     bool styleIsAffectedByPreviousSibling() const { return getFlag(StyleIsAffectedByPreviousSibling); }
-    bool isEditingText() const { return getFlag(IsEditingTextFlag); }
+    bool isEditingText() const { return getFlag(IsTextFlag) && getFlag(IsEditingTextOrUnresolvedCustomElementFlag); }
 
     void setChildNeedsStyleRecalc() { setFlag(ChildNeedsStyleRecalcFlag); }
     void clearChildNeedsStyleRecalc() { m_nodeFlags &= ~(ChildNeedsStyleRecalcFlag | DirectChildNeedsStyleRecalcFlag); }
@@ -595,7 +599,7 @@ protected:
         IsParsingChildrenFinishedFlag = 1 << 13, // Element
 
         StyleChangeMask = 1 << nodeStyleChangeShift | 1 << (nodeStyleChangeShift + 1) | 1 << (nodeStyleChangeShift + 2),
-        IsEditingTextFlag = 1 << 17,
+        IsEditingTextOrUnresolvedCustomElementFlag = 1 << 17,
         IsNamedFlowContentNodeFlag = 1 << 18,
         HasSyntheticAttrChildNodesFlag = 1 << 19,
         HasCustomStyleResolveCallbacksFlag = 1 << 20,
@@ -634,7 +638,7 @@ protected:
         CreateHTMLElement = CreateStyledElement | IsHTMLFlag,
         CreateSVGElement = CreateStyledElement | IsSVGFlag | HasCustomStyleResolveCallbacksFlag,
         CreateDocument = CreateContainer | InDocumentFlag,
-        CreateEditingText = CreateText | IsEditingTextFlag,
+        CreateEditingText = CreateText | IsEditingTextOrUnresolvedCustomElementFlag,
         CreateMathMLElement = CreateStyledElement | IsMathMLFlag
     };
     Node(Document&, ConstructionType);
@@ -769,6 +773,16 @@ inline ContainerNode* Node::parentNodeGuaranteedHostFree() const
     return parentNode();
 }
 
+#if ENABLE(CUSTOM_ELEMENTS)
+
+inline void Node::setCustomElementIsResolved()
+{
+    clearFlag(IsEditingTextOrUnresolvedCustomElementFlag);
+    setFlag(IsCustomElement);
+}
+
+#endif
+
 } // namespace WebCore
 
 #if ENABLE(TREE_DEBUGGING)
index 8de96d4..14e8329 100755 (executable)
@@ -216,6 +216,7 @@ sub defaultParametersHash
         'attrsNullNamespace' => 0,
         'fallbackInterfaceName' => '',
         'fallbackJSInterfaceName' => '',
+        'customElementInterfaceName' => '',
     );
 }
 
@@ -1318,6 +1319,20 @@ JSDOMObject* createJS$parameters{namespace}Wrapper(JSDOMGlobalObject* globalObje
         populate$parameters{namespace}WrapperMap(functions);
     if (auto function = functions.get().get(element->localName().impl()))
         return function(globalObject, element);
+END
+;
+
+    if ($parameters{customElementInterfaceName}) {
+        print F <<END
+#if ENABLE(CUSTOM_ELEMENTS)
+    if (element->isUnresolvedCustomElement())
+        return CREATE_DOM_WRAPPER(globalObject, $parameters{customElementInterfaceName}, element.get());
+#endif
+END
+;
+    }
+
+    print F <<END
     return CREATE_DOM_WRAPPER(globalObject, $parameters{fallbackJSInterfaceName}, element.get());
 }
 
index 151fa17..dcc5e39 100644 (file)
@@ -2,6 +2,7 @@ namespace="HTML"
 namespacePrefix="xhtml"
 namespaceURI="http://www.w3.org/1999/xhtml"
 fallbackInterfaceName="HTMLUnknownElement"
+customElementInterfaceName="HTMLElement"
 
 a interfaceName=HTMLAnchorElement
 abbr interfaceName=HTMLElement
index dbb6ab8..9e4f2cc 100644 (file)
@@ -672,20 +672,29 @@ RefPtr<Element> HTMLConstructionSite::createHTMLElementOrFindCustomElementInterf
     bool insideTemplateElement = !ownerDocument.frame();
     RefPtr<Element> element = HTMLElementFactory::createKnownElement(localName, ownerDocument, insideTemplateElement ? nullptr : form(), true);
     if (UNLIKELY(!element)) {
-
 #if ENABLE(CUSTOM_ELEMENTS)
-        auto* definitions = ownerDocumentForCurrentNode().customElementDefinitions();
-        if (customElementInterface && UNLIKELY(definitions)) {
-            if (auto* interface = definitions->findInterface(localName)) {
-                *customElementInterface = interface;
-                return nullptr;
+        if (customElementInterface) {
+            auto* definitions = ownerDocument.customElementDefinitions();
+            if (UNLIKELY(definitions)) {
+                if (auto* interface = definitions->findInterface(localName)) {
+                    *customElementInterface = interface;
+                    return nullptr;
+                }
             }
         }
 #else
         UNUSED_PARAM(customElementInterface);
 #endif
 
-        element = HTMLUnknownElement::create(QualifiedName(nullAtom, localName, xhtmlNamespaceURI), ownerDocumentForCurrentNode());
+        QualifiedName qualifiedName(nullAtom, localName, xhtmlNamespaceURI);
+#if ENABLE(CUSTOM_ELEMENTS)
+        if (CustomElementDefinitions::checkName(localName) == CustomElementDefinitions::NameStatus::Valid) {
+            element = HTMLElement::create(qualifiedName, ownerDocument);
+            element->setIsUnresolvedCustomElement();
+            ownerDocument.ensureCustomElementDefinitions().addUpgradeCandidate(*element);
+        } else
+#endif
+            element = HTMLUnknownElement::create(qualifiedName, ownerDocument);
     }
     ASSERT(element);