2016-01-15 02:59:03 +00:00
|
|
|
/*
|
2016-08-11 02:08:17 +00:00
|
|
|
* Copyright (C) 2015, 2016 Apple Inc. All rights reserved.
|
2016-01-15 02:59:03 +00:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
|
|
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
2016-08-22 19:45:01 +00:00
|
|
|
#include "CustomElementRegistry.h"
|
2016-01-15 02:59:03 +00:00
|
|
|
|
2016-09-02 06:17:17 +00:00
|
|
|
#include "CustomElementReactionQueue.h"
|
|
|
|
#include "DOMWindow.h"
|
2016-01-15 02:59:03 +00:00
|
|
|
#include "Document.h"
|
|
|
|
#include "JSCustomElementInterface.h"
|
2019-10-04 19:07:22 +00:00
|
|
|
#include "JSDOMPromiseDeferred.h"
|
2016-01-15 02:59:03 +00:00
|
|
|
#include "MathMLNames.h"
|
|
|
|
#include "QualifiedName.h"
|
2016-09-02 06:17:17 +00:00
|
|
|
#include "ShadowRoot.h"
|
2018-08-02 19:25:23 +00:00
|
|
|
#include "TypedElementDescendantIterator.h"
|
2018-02-07 05:20:34 +00:00
|
|
|
#include <JavaScriptCore/JSCJSValueInlines.h>
|
2019-06-17 01:48:13 +00:00
|
|
|
#include <wtf/text/AtomString.h>
|
2016-01-15 02:59:03 +00:00
|
|
|
|
|
|
|
namespace WebCore {
|
|
|
|
|
2018-08-04 09:02:44 +00:00
|
|
|
Ref<CustomElementRegistry> CustomElementRegistry::create(DOMWindow& window, ScriptExecutionContext* scriptExecutionContext)
|
2016-08-11 02:08:17 +00:00
|
|
|
{
|
2018-08-04 09:02:44 +00:00
|
|
|
return adoptRef(*new CustomElementRegistry(window, scriptExecutionContext));
|
2016-08-11 02:08:17 +00:00
|
|
|
}
|
|
|
|
|
2018-08-04 09:02:44 +00:00
|
|
|
CustomElementRegistry::CustomElementRegistry(DOMWindow& window, ScriptExecutionContext* scriptExecutionContext)
|
|
|
|
: ContextDestructionObserver(scriptExecutionContext)
|
|
|
|
, m_window(window)
|
2017-10-19 23:48:45 +00:00
|
|
|
{
|
|
|
|
}
|
2016-08-11 02:08:17 +00:00
|
|
|
|
2017-10-19 23:48:45 +00:00
|
|
|
CustomElementRegistry::~CustomElementRegistry() = default;
|
2016-08-11 02:08:17 +00:00
|
|
|
|
2016-09-02 06:17:17 +00:00
|
|
|
// https://dom.spec.whatwg.org/#concept-shadow-including-tree-order
|
|
|
|
static void enqueueUpgradeInShadowIncludingTreeOrder(ContainerNode& node, JSCustomElementInterface& elementInterface)
|
|
|
|
{
|
|
|
|
for (Element* element = ElementTraversal::firstWithin(node); element; element = ElementTraversal::next(*element)) {
|
Update the semantics of defined-ness of custom elements per spec changes
https://bugs.webkit.org/show_bug.cgi?id=161570
Reviewed by Darin Adler.
Source/WebCore:
This patch adds the notion of a custom element that failed to construct or upgrade so that :defined
doesn't apply to such an element. We also set the defined flag inside the HTMLElement constructor in
the case of synchronous construction instead of waiting for the custom element constructor to finish.
https://dom.spec.whatwg.org/#concept-create-element
Conceptually, there are four distinct states for an element:
1. The element is a built-in element
2. The element is a custom element yet to be defined (an upgrade candidate).
3. The element is a well-defined custom element (constructed or upgraded).
4. The element has failed to construct or upgrade as a custom element (because the custom element
constructor threw an exception or returned an unexpected object).
In the latest DOM/HTML specifications, these states are called as 1. "uncustomized", 2. "undefined",
3. "custom", and 4. "failed": https://dom.spec.whatwg.org/#concept-element-defined
This patch refactors Node flags to introduce these distinct states as the following:
1. Neither IsCustomElement nor IsEditingTextOrUnresolvedCustomElementFlag is set.
2. IsCustomElement and IsEditingTextOrUnresolvedCustomElementFlag are set.
isCustomElementUpgradeCandidate() and isUndefinedCustomElement() return true.
3. IsCustomElement is set and IsEditingTextOrUnresolvedCustomElementFlag is unset.
isDefinedCustomElement() returns true.
4. IsCustomElement is unset and IsEditingTextOrUnresolvedCustomElementFlag is set.
isFailedCustomElement() and isUndefinedCustomElement() return true.
Per a spec change, this patch also makes :defined applied to a synchronously constructed custom element
immediately after super() call in the constructor. When the constructor throws an exception or fails to
return the right element, the HTML parser marks the fallback element with setIsUndefinedCustomElement.
Tests: fast/custom-elements/defined-pseudo-class.html
fast/custom-elements/defined-rule.html
fast/custom-elements/upgrading/Node-cloneNode.html
* bindings/js/JSCustomElementInterface.cpp:
(WebCore::JSCustomElementInterface::constructElement): Don't set :defined flag here since that's done
in the HTMLElement constructor now.
(WebCore::JSCustomElementInterface::upgradeElement): Mark the element as failed-to-upgrade as needed.
* bindings/js/JSElementCustom.cpp:
(WebCore::toJSNewlyCreated):
* bindings/js/JSHTMLElementCustom.cpp:
(WebCore::constructJSHTMLElement):
* css/SelectorCheckerTestFunctions.h:
(WebCore::isDefinedElement):
* dom/CustomElementReactionQueue.cpp:
(WebCore::CustomElementReactionQueue::enqueueElementUpgradeIfDefined): Enqueue custom element reactions
only if the element is well defined (successfully constructed or upgraded).
(WebCore::CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded): Ditto.
(WebCore::CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded): Ditto.
(WebCore::CustomElementReactionQueue::enqueueAdoptedCallbackIfNeeded): Ditto.
(WebCore::CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded): Ditto.
* dom/CustomElementRegistry.cpp:
(WebCore::enqueueUpgradeInShadowIncludingTreeOrder):
* dom/Document.cpp:
(WebCore::createUpgradeCandidateElement):
(WebCore::createFallbackHTMLElement):
* dom/Element.cpp:
(WebCore::Element::attributeChanged):
(WebCore::Element::didMoveToNewDocument):
(WebCore::Element::insertedInto):
(WebCore::Element::removedFrom):
(WebCore::Element::setCustomElementIsResolved): Deleted.
(WebCore::Element::setIsDefinedCustomElement): Renamed from setCustomElementIsResolved.
(WebCore::Element::setIsFailedCustomElement): Added.
(WebCore::Element::setIsCustomElementUpgradeCandidate): Added.
(WebCore::Element::customElementInterface):
* dom/Element.h:
* dom/Node.h:
(WebCore::Node::setIsCustomElement): Deleted.
(WebCore::Node::isUndefinedCustomElement): Renamed from isUnresolvedCustomElement.
(WebCore::Node::setIsUnresolvedCustomElement): Deleted.
(WebCore::Node::isCustomElementUpgradeCandidate): Added.
(WebCore::Node::isDefinedCustomElement): Renamed from isCustomElement.
(WebCore::Node::isFailedCustomElement): Added.
* dom/make_names.pl:
(printWrapperFactoryCppFile): Use the HTMLElement wrapper on upgrade candidates. When a custom element
failed to upgrade, the HTMLElement constructor would have created the wrapper so we never run this code.
* html/parser/HTMLConstructionSite.cpp:
(WebCore::HTMLConstructionSite::createHTMLElementOrFindCustomElementInterface):
* html/parser/HTMLDocumentParser.cpp:
(WebCore::HTMLDocumentParser::runScriptsForPausedTreeBuilder): Mark the HTMLUnknownElement created when
the custom element constructor failed to run successfully as a failed custom element so that :define
wouldn't apply to this element.
LayoutTests:
Added a new test cases to defined-pseudo-class.html, defined-rule.html, and Node-cloneNode.html
and rebaselined the tests.
* fast/custom-elements/defined-pseudo-class-expected.txt:
* fast/custom-elements/defined-pseudo-class.html:
(MyElement): Made matchInsideConstructor an instance variable so that there won't be inter-test dependency.
Added test cases for :defined not being not applying to a failed-to-upgrade custom element. Finally, updated
test expectation to reflect the fact :defined now applies inside custom element constructors immediately after
super() call.
* fast/custom-elements/defined-rule.html: Added a test case for :defined not applying to a failed-to-upgrade
custom element. Also adjusted the height of the last box so that the green box is still 100px by 100px.
* fast/custom-elements/upgrading/Node-cloneNode-expected.txt:
* fast/custom-elements/upgrading/Node-cloneNode.html: Added a test to make sure we don't try to upgrade
a custom element for the second time when the first attempt resulted in the constructor throwing an exception.
Canonical link: https://commits.webkit.org/179748@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@205416 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2016-09-04 05:09:28 +00:00
|
|
|
if (element->isCustomElementUpgradeCandidate() && element->tagQName() == elementInterface.name())
|
2016-10-25 06:18:13 +00:00
|
|
|
element->enqueueToUpgrade(elementInterface);
|
2016-09-02 06:17:17 +00:00
|
|
|
if (auto* shadowRoot = element->shadowRoot()) {
|
2016-10-27 20:23:06 +00:00
|
|
|
if (shadowRoot->mode() != ShadowRootMode::UserAgent)
|
2016-09-02 06:17:17 +00:00
|
|
|
enqueueUpgradeInShadowIncludingTreeOrder(*shadowRoot, elementInterface);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-25 21:22:12 +00:00
|
|
|
RefPtr<DeferredPromise> CustomElementRegistry::addElementDefinition(Ref<JSCustomElementInterface>&& elementInterface)
|
2016-01-15 02:59:03 +00:00
|
|
|
{
|
2019-06-17 01:48:13 +00:00
|
|
|
AtomString localName = elementInterface->name().localName();
|
2016-03-05 08:31:38 +00:00
|
|
|
ASSERT(!m_nameMap.contains(localName));
|
2016-06-28 11:49:21 +00:00
|
|
|
m_constructorMap.add(elementInterface->constructor(), elementInterface.ptr());
|
|
|
|
m_nameMap.add(localName, elementInterface.copyRef());
|
2016-03-10 02:33:12 +00:00
|
|
|
|
2016-09-02 06:17:17 +00:00
|
|
|
if (auto* document = m_window.document())
|
|
|
|
enqueueUpgradeInShadowIncludingTreeOrder(*document, elementInterface.get());
|
2016-09-03 23:25:47 +00:00
|
|
|
|
2020-08-26 02:25:04 +00:00
|
|
|
return m_promiseMap.take(localName);
|
2016-03-10 02:33:12 +00:00
|
|
|
}
|
|
|
|
|
2016-09-02 06:17:17 +00:00
|
|
|
JSCustomElementInterface* CustomElementRegistry::findInterface(const Element& element) const
|
2016-03-10 02:33:12 +00:00
|
|
|
{
|
2016-09-02 06:17:17 +00:00
|
|
|
return findInterface(element.tagQName());
|
2016-01-15 02:59:03 +00:00
|
|
|
}
|
|
|
|
|
2016-08-22 19:45:01 +00:00
|
|
|
JSCustomElementInterface* CustomElementRegistry::findInterface(const QualifiedName& name) const
|
2016-01-25 17:23:42 +00:00
|
|
|
{
|
2016-09-02 06:17:17 +00:00
|
|
|
if (name.namespaceURI() != HTMLNames::xhtmlNamespaceURI)
|
|
|
|
return nullptr;
|
2016-11-15 00:27:01 +00:00
|
|
|
return m_nameMap.get(name.localName());
|
2016-01-25 17:23:42 +00:00
|
|
|
}
|
|
|
|
|
2019-06-17 01:48:13 +00:00
|
|
|
JSCustomElementInterface* CustomElementRegistry::findInterface(const AtomString& name) const
|
2016-01-15 02:59:03 +00:00
|
|
|
{
|
2016-03-06 04:25:32 +00:00
|
|
|
return m_nameMap.get(name);
|
2016-01-15 02:59:03 +00:00
|
|
|
}
|
|
|
|
|
2016-08-22 19:45:01 +00:00
|
|
|
JSCustomElementInterface* CustomElementRegistry::findInterface(const JSC::JSObject* constructor) const
|
2016-03-05 01:23:42 +00:00
|
|
|
{
|
2016-03-06 04:25:32 +00:00
|
|
|
return m_constructorMap.get(constructor);
|
2016-03-05 01:23:42 +00:00
|
|
|
}
|
|
|
|
|
2016-08-22 19:45:01 +00:00
|
|
|
bool CustomElementRegistry::containsConstructor(const JSC::JSObject* constructor) const
|
2016-01-15 02:59:03 +00:00
|
|
|
{
|
2016-03-05 08:31:38 +00:00
|
|
|
return m_constructorMap.contains(constructor);
|
2016-01-15 02:59:03 +00:00
|
|
|
}
|
|
|
|
|
2019-06-17 01:48:13 +00:00
|
|
|
JSC::JSValue CustomElementRegistry::get(const AtomString& name)
|
2016-08-31 05:18:54 +00:00
|
|
|
{
|
|
|
|
if (auto* elementInterface = m_nameMap.get(name))
|
|
|
|
return elementInterface->constructor();
|
|
|
|
return JSC::jsUndefined();
|
|
|
|
}
|
|
|
|
|
2018-08-02 19:25:23 +00:00
|
|
|
static void upgradeElementsInShadowIncludingDescendants(ContainerNode& root)
|
|
|
|
{
|
|
|
|
for (auto& element : descendantsOfType<Element>(root)) {
|
|
|
|
if (element.isCustomElementUpgradeCandidate())
|
2020-08-28 02:55:21 +00:00
|
|
|
CustomElementReactionQueue::tryToUpgradeElement(element);
|
2018-08-02 19:25:23 +00:00
|
|
|
if (auto* shadowRoot = element.shadowRoot())
|
|
|
|
upgradeElementsInShadowIncludingDescendants(*shadowRoot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CustomElementRegistry::upgrade(Node& root)
|
|
|
|
{
|
|
|
|
if (!is<ContainerNode>(root))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (is<Element>(root) && downcast<Element>(root).isCustomElementUpgradeCandidate())
|
2020-08-28 02:55:21 +00:00
|
|
|
CustomElementReactionQueue::tryToUpgradeElement(downcast<Element>(root));
|
2018-08-02 19:25:23 +00:00
|
|
|
|
|
|
|
upgradeElementsInShadowIncludingDescendants(downcast<ContainerNode>(root));
|
|
|
|
}
|
|
|
|
|
2016-01-15 02:59:03 +00:00
|
|
|
}
|