Changeset 197917 in webkit


Ignore:
Timestamp:
Mar 9, 2016 6:33:12 PM (8 years ago)
Author:
rniwa@webkit.org
Message:

defineElement should upgrade existing unresolved custom elements
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.
Location:
trunk
Files:
2 added
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r197915 r197917  
     12016-03-09  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        defineElement should upgrade existing unresolved custom elements
     4        https://bugs.webkit.org/show_bug.cgi?id=155107
     5
     6        Reviewed by Darin Adler.
     7
     8        Added W3C style testharness.js tests for asynchronously defining custom elements.
     9
     10        * fast/custom-elements/upgrading/Node-cloneNode.html:
     11        * fast/custom-elements/upgrading/upgrading-parser-created-element-expected.txt: Added.
     12        * fast/custom-elements/upgrading/upgrading-parser-created-element.html: Added.
     13
    1142016-03-09  Saam Barati  <sbarati@apple.com>
    215
  • trunk/LayoutTests/fast/custom-elements/upgrading/Node-cloneNode.html

    r197634 r197917  
    22<html>
    33<head>
    4 <title>Custom Elements: Extensions to Document interface</title>
     4<title>Custom Elements: Upgrading</title>
    55<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
    66<meta name="assert" content="Node.prototype.cloneNode should upgrade a custom element">
  • trunk/Source/WebCore/ChangeLog

    r197916 r197917  
     12016-03-09  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        defineElement should upgrade existing unresolved custom elements
     4        https://bugs.webkit.org/show_bug.cgi?id=155107
     5
     6        Reviewed by Darin Adler.
     7
     8        Added the support for upgrading existing unresolved custom elements when defineElement is called.
     9
     10        The current implementation upgrades elements in the order they were created and has the issue that
     11        it keeps accumulating all elements with a hyphen in its name until defineElement is called as
     12        documented in https://github.com/w3c/webcomponents/issues/419
     13
     14        This patch re-purposes IsEditingTextFlag to indicate that the node is an unresolved custom element.
     15        Since isEditingText() is only called in textRendererIsNeeded only on Text nodes, it's mutually
     16        exclusive with isUnresolvedCustomElement().
     17
     18        The list of unresolved custom elements is kept in m_upgradeCandidatesMap, a hash map of element names
     19        to the list of unresolved elements with that name.
     20
     21        In addition, added the logic to use HTMLElement as the interface for unresolved custom element instead
     22        of HTMLUnknownElement.
     23
     24        Test: fast/custom-elements/upgrading/upgrading-parser-created-element.html
     25
     26        * bindings/js/JSCustomElementInterface.cpp:
     27        (WebCore::JSCustomElementInterface::upgradeElement): Clear the flag.
     28        * bindings/js/JSDocumentCustom.cpp:
     29        (WebCore::JSDocument::defineElement): Set the unique private name to keep the interface alive before
     30        calling addElementDefinition as the call can now invoke author scripts.
     31        * dom/CustomElementDefinitions.cpp:
     32        (WebCore::CustomElementDefinitions::addElementDefinition): Upgrade existing unresolved elements kept
     33        in m_upgradeCandidatesMap.
     34        (WebCore::CustomElementDefinitions::addUpgradeCandidate): Added.
     35        * dom/CustomElementDefinitions.h:
     36        * dom/Document.cpp:
     37        (WebCore::createHTMLElementWithNameValidation): Added the code to add the unresolved custom elements
     38        to the upgrade candidates map. Also instantiate it as HTMLElement instead of HTMLUnknownElement.
     39        (WebCore::createFallbackHTMLElement): Ditto.
     40        * dom/Node.h:
     41        (WebCore::Node::setIsCustomElement):
     42        (WebCore::Node::isUnresolvedCustomElement): Added.
     43        (WebCore::Node::setIsUnresolvedCustomElement): Added.
     44        (WebCore::Node::setCustomElementIsResolved): Added. Clears IsEditingTextOrUnresolvedCustomElementFlag
     45        and sets IsCustomElement.
     46        (WebCore::Node::isEditingText): Check both IsEditingTextOrUnresolvedCustomElementFlag and IsTextFlag
     47        for safety even though it's currently only used in textRendererIsNeeded which takes Text&.
     48        * dom/make_names.pl:
     49        (defaultParametersHash): Added customElementInterfaceName as a parameter.
     50        (printWrapperFactoryCppFile): Generate the code to use customElementInterfaceName when the element
     51        for which the wrapper is created has isUnresolvedCustomElement flag set.
     52        * html/HTMLTagNames.in: Use HTMLElement for unresolved custom elements.
     53        * html/parser/HTMLConstructionSite.cpp:
     54        (WebCore::HTMLConstructionSite::createHTMLElementOrFindCustomElementInterface): Added the code to add
     55        the unresolved custom elements to the upgrade candidates map. Also instantiate it as HTMLElement instead
     56        of HTMLUnknownElement.
     57
    1582016-03-09  Enrica Casucci  <enrica@apple.com>
    259
  • trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp

    r197634 r197917  
    105105void JSCustomElementInterface::upgradeElement(Element& element)
    106106{
    107     ASSERT(element.isCustomElement());
     107    ASSERT(element.isUnresolvedCustomElement());
    108108    if (!canInvokeCallback())
    109109        return;
     
    148148        return;
    149149    }
     150    wrappedElement->setCustomElementIsResolved();
    150151    ASSERT(wrappedElement->isCustomElement());
    151152}
  • trunk/Source/WebCore/bindings/js/JSDocumentCustom.cpp

    r197614 r197917  
    181181    // FIXME: 14. Let attributeChangedCallback be Get(prototype, "attributeChangedCallback"). Rethrow any exceptions.
    182182
     183    PrivateName uniquePrivateName;
     184    globalObject()->putDirect(globalObject()->vm(), uniquePrivateName, object);
     185
    183186    QualifiedName name(nullAtom, tagName, HTMLNames::xhtmlNamespaceURI);
    184187    definitions.addElementDefinition(JSCustomElementInterface::create(name, object, globalObject()));
    185     PrivateName uniquePrivateName;
    186     globalObject()->putDirect(globalObject()->vm(), uniquePrivateName, object);
    187188
    188189    // FIXME: 17. Let map be registry's upgrade candidates map.
  • trunk/Source/WebCore/dom/CustomElementDefinitions.cpp

    r197630 r197917  
    7575    ASSERT(!m_nameMap.contains(localName));
    7676    m_constructorMap.add(interface->constructor(), interface.ptr());
    77     m_nameMap.add(localName, WTFMove(interface));
     77    m_nameMap.add(localName, interface.copyRef());
     78
     79    auto candidateList = m_upgradeCandidatesMap.find(localName);
     80    if (candidateList == m_upgradeCandidatesMap.end())
     81        return;
     82
     83    Vector<RefPtr<Element>> list(WTFMove(candidateList->value));
     84
     85    m_upgradeCandidatesMap.remove(localName);
     86
     87    for (auto& candidate : list) {
     88        ASSERT(candidate);
     89        interface->upgradeElement(*candidate);
     90    }
     91
     92    // We should not be adding more upgrade candidate for this local name.
     93    ASSERT(!m_upgradeCandidatesMap.contains(localName));
     94}
     95
     96void CustomElementDefinitions::addUpgradeCandidate(Element& candidate)
     97{
     98    auto result = m_upgradeCandidatesMap.ensure(candidate.localName(), [] {
     99        return Vector<RefPtr<Element>>();
     100    });
     101    auto& nodeVector = result.iterator->value;
     102    ASSERT(!nodeVector.contains(&candidate));
     103    nodeVector.append(&candidate);
    78104}
    79105
  • trunk/Source/WebCore/dom/CustomElementDefinitions.h

    r197612 r197917  
    5050public:
    5151    void addElementDefinition(Ref<JSCustomElementInterface>&&);
     52    void addUpgradeCandidate(Element&);
    5253
    5354    JSCustomElementInterface* findInterface(const QualifiedName&) const;
     
    6061
    6162private:
     63    HashMap<AtomicString, Vector<RefPtr<Element>>> m_upgradeCandidatesMap;
    6264    HashMap<AtomicString, RefPtr<JSCustomElementInterface>> m_nameMap;
    6365    HashMap<const JSC::JSObject*, JSCustomElementInterface*> m_constructorMap;
  • trunk/Source/WebCore/dom/Document.cpp

    r197804 r197917  
    900900    }
    901901
    902     return HTMLUnknownElement::create(QualifiedName(nullAtom, localName, xhtmlNamespaceURI), document);
     902    QualifiedName qualifiedName(nullAtom, localName, xhtmlNamespaceURI);
     903
     904#if ENABLE(CUSTOM_ELEMENTS)
     905    if (CustomElementDefinitions::checkName(localName) == CustomElementDefinitions::NameStatus::Valid) {
     906        Ref<HTMLElement> element = HTMLElement::create(qualifiedName, document);
     907        element->setIsUnresolvedCustomElement();
     908        document.ensureCustomElementDefinitions().addUpgradeCandidate(element.get());
     909        return WTFMove(element);
     910    }
     911#endif
     912
     913    return HTMLUnknownElement::create(qualifiedName, document);
    903914}
    904915
     
    10811092        if (auto* interface = definitions->findInterface(name)) {
    10821093            Ref<HTMLElement> element = HTMLElement::create(name, document);
    1083             element->setIsCustomElement(); // Pre-upgrade element is still considered a custom element.
     1094            element->setIsUnresolvedCustomElement();
    10841095            LifecycleCallbackQueue::enqueueElementUpgrade(element.get(), *interface);
    10851096            return element;
    10861097        }
     1098    }
     1099    // FIXME: Should we also check the equality of prefix between the custom element and name?
     1100    if (CustomElementDefinitions::checkName(name.localName()) == CustomElementDefinitions::NameStatus::Valid) {
     1101        Ref<HTMLElement> element = HTMLElement::create(name, document);
     1102        element->setIsUnresolvedCustomElement();
     1103        document.ensureCustomElementDefinitions().addUpgradeCandidate(element.get());
     1104        return element;
    10871105    }
    10881106#endif
  • trunk/Source/WebCore/dom/Node.h

    r197887 r197917  
    267267#if ENABLE(CUSTOM_ELEMENTS)
    268268    bool isCustomElement() const { return getFlag(IsCustomElement); }
    269     void setIsCustomElement() { return setFlag(IsCustomElement); }
     269    void setIsCustomElement() { setFlag(IsCustomElement); }
     270
     271    bool isUnresolvedCustomElement() const { return isElementNode() && getFlag(IsEditingTextOrUnresolvedCustomElementFlag); }
     272    void setIsUnresolvedCustomElement() { setFlag(IsEditingTextOrUnresolvedCustomElementFlag); }
     273    void setCustomElementIsResolved();
    270274#endif
    271275
     
    321325    bool childNeedsStyleRecalc() const { return getFlag(ChildNeedsStyleRecalcFlag); }
    322326    bool styleIsAffectedByPreviousSibling() const { return getFlag(StyleIsAffectedByPreviousSibling); }
    323     bool isEditingText() const { return getFlag(IsEditingTextFlag); }
     327    bool isEditingText() const { return getFlag(IsTextFlag) && getFlag(IsEditingTextOrUnresolvedCustomElementFlag); }
    324328
    325329    void setChildNeedsStyleRecalc() { setFlag(ChildNeedsStyleRecalcFlag); }
     
    596600
    597601        StyleChangeMask = 1 << nodeStyleChangeShift | 1 << (nodeStyleChangeShift + 1) | 1 << (nodeStyleChangeShift + 2),
    598         IsEditingTextFlag = 1 << 17,
     602        IsEditingTextOrUnresolvedCustomElementFlag = 1 << 17,
    599603        IsNamedFlowContentNodeFlag = 1 << 18,
    600604        HasSyntheticAttrChildNodesFlag = 1 << 19,
     
    635639        CreateSVGElement = CreateStyledElement | IsSVGFlag | HasCustomStyleResolveCallbacksFlag,
    636640        CreateDocument = CreateContainer | InDocumentFlag,
    637         CreateEditingText = CreateText | IsEditingTextFlag,
     641        CreateEditingText = CreateText | IsEditingTextOrUnresolvedCustomElementFlag,
    638642        CreateMathMLElement = CreateStyledElement | IsMathMLFlag
    639643    };
     
    770774}
    771775
     776#if ENABLE(CUSTOM_ELEMENTS)
     777
     778inline void Node::setCustomElementIsResolved()
     779{
     780    clearFlag(IsEditingTextOrUnresolvedCustomElementFlag);
     781    setFlag(IsCustomElement);
     782}
     783
     784#endif
     785
    772786} // namespace WebCore
    773787
  • trunk/Source/WebCore/dom/make_names.pl

    r197634 r197917  
    217217        'fallbackInterfaceName' => '',
    218218        'fallbackJSInterfaceName' => '',
     219        'customElementInterfaceName' => '',
    219220    );
    220221}
     
    13191320    if (auto function = functions.get().get(element->localName().impl()))
    13201321        return function(globalObject, element);
     1322END
     1323;
     1324
     1325    if ($parameters{customElementInterfaceName}) {
     1326        print F <<END
     1327#if ENABLE(CUSTOM_ELEMENTS)
     1328    if (element->isUnresolvedCustomElement())
     1329        return CREATE_DOM_WRAPPER(globalObject, $parameters{customElementInterfaceName}, element.get());
     1330#endif
     1331END
     1332;
     1333    }
     1334
     1335    print F <<END
    13211336    return CREATE_DOM_WRAPPER(globalObject, $parameters{fallbackJSInterfaceName}, element.get());
    13221337}
  • trunk/Source/WebCore/html/HTMLTagNames.in

    r195627 r197917  
    33namespaceURI="http://www.w3.org/1999/xhtml"
    44fallbackInterfaceName="HTMLUnknownElement"
     5customElementInterfaceName="HTMLElement"
    56
    67a interfaceName=HTMLAnchorElement
  • trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp

    r197463 r197917  
    673673    RefPtr<Element> element = HTMLElementFactory::createKnownElement(localName, ownerDocument, insideTemplateElement ? nullptr : form(), true);
    674674    if (UNLIKELY(!element)) {
    675 
    676675#if ENABLE(CUSTOM_ELEMENTS)
    677         auto* definitions = ownerDocumentForCurrentNode().customElementDefinitions();
    678         if (customElementInterface && UNLIKELY(definitions)) {
    679             if (auto* interface = definitions->findInterface(localName)) {
    680                 *customElementInterface = interface;
    681                 return nullptr;
     676        if (customElementInterface) {
     677            auto* definitions = ownerDocument.customElementDefinitions();
     678            if (UNLIKELY(definitions)) {
     679                if (auto* interface = definitions->findInterface(localName)) {
     680                    *customElementInterface = interface;
     681                    return nullptr;
     682                }
    682683            }
    683684        }
     
    686687#endif
    687688
    688         element = HTMLUnknownElement::create(QualifiedName(nullAtom, localName, xhtmlNamespaceURI), ownerDocumentForCurrentNode());
     689        QualifiedName qualifiedName(nullAtom, localName, xhtmlNamespaceURI);
     690#if ENABLE(CUSTOM_ELEMENTS)
     691        if (CustomElementDefinitions::checkName(localName) == CustomElementDefinitions::NameStatus::Valid) {
     692            element = HTMLElement::create(qualifiedName, ownerDocument);
     693            element->setIsUnresolvedCustomElement();
     694            ownerDocument.ensureCustomElementDefinitions().addUpgradeCandidate(*element);
     695        } else
     696#endif
     697            element = HTMLUnknownElement::create(qualifiedName, ownerDocument);
    689698    }
    690699    ASSERT(element);
Note: See TracChangeset for help on using the changeset viewer.