Diff
Modified: trunk/LayoutTests/ChangeLog (197633 => 197634)
--- trunk/LayoutTests/ChangeLog 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/LayoutTests/ChangeLog 2016-03-06 05:07:47 UTC (rev 197634)
@@ -1,3 +1,15 @@
+2016-03-05 Ryosuke Niwa <rn...@webkit.org>
+
+ Add the support for upgrading custom elements in cloneNode
+ https://bugs.webkit.org/show_bug.cgi?id=155062
+
+ Reviewed by Antti Koivisto.
+
+ Added test cases for upgrading elements with Node.prototype.cloneNode.
+
+ * fast/custom-elements/lifecycle-callback-timing-expected.txt:
+ * fast/custom-elements/lifecycle-callback-timing.html:
+
2016-03-05 Sam Weinig <s...@webkit.org>
Update tests to match the latest version of the HTML5 spec.
Modified: trunk/LayoutTests/fast/custom-elements/lifecycle-callback-timing-expected.txt (197633 => 197634)
--- trunk/LayoutTests/fast/custom-elements/lifecycle-callback-timing-expected.txt 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/LayoutTests/fast/custom-elements/lifecycle-callback-timing-expected.txt 2016-03-06 05:07:47 UTC (rev 197634)
@@ -1,3 +1,4 @@
PASS setAttribute and removeAttribute must enqueue and invoke attributeChangedCallback
+PASS Calling Node.prototype.cloneNode(false) must push a new element queue to the processing stack
Modified: trunk/LayoutTests/fast/custom-elements/lifecycle-callback-timing.html (197633 => 197634)
--- trunk/LayoutTests/fast/custom-elements/lifecycle-callback-timing.html 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/LayoutTests/fast/custom-elements/lifecycle-callback-timing.html 2016-03-06 05:07:47 UTC (rev 197634)
@@ -47,6 +47,42 @@
}, 'setAttribute and removeAttribute must enqueue and invoke attributeChangedCallback');
+test(function () {
+ var shouldCloneAnotherInstance = false;
+ var anotherInstanceClone;
+ var log = [];
+
+ class SelfCloningElement extends HTMLElement {
+ constructor() {
+ super();
+ log.push([this, 'begin']);
+ if (shouldCloneAnotherInstance) {
+ shouldCloneAnotherInstance = false;
+ anotherInstanceClone = anotherInstance.cloneNode(false);
+ }
+ log.push([this, 'end']);
+ }
+ }
+ document.defineElement('self-cloning-element', SelfCloningElement);
+
+ var instance = document.createElement('self-cloning-element');
+ var anotherInstance = document.createElement('self-cloning-element');
+ shouldCloneAnotherInstance = true;
+
+ assert_equals(log.length, 4);
+ var instanceClone = instance.cloneNode(false);
+
+ assert_equals(log.length, 8);
+ assert_array_equals(log[0], [instance, 'begin']);
+ assert_array_equals(log[1], [instance, 'end']);
+ assert_array_equals(log[2], [anotherInstance, 'begin']);
+ assert_array_equals(log[3], [anotherInstance, 'end']);
+ assert_array_equals(log[4], [instanceClone, 'begin']);
+ assert_array_equals(log[5], [anotherInstanceClone, 'begin']);
+ assert_array_equals(log[6], [anotherInstanceClone, 'end']);
+ assert_array_equals(log[7], [instanceClone, 'end']);
+}, 'Calling Node.prototype.cloneNode(false) must push a new element queue to the processing stack');
+
</script>
</body>
</html>
Added: trunk/LayoutTests/fast/custom-elements/upgrading/Node-cloneNode-expected.txt (0 => 197634)
--- trunk/LayoutTests/fast/custom-elements/upgrading/Node-cloneNode-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/upgrading/Node-cloneNode-expected.txt 2016-03-06 05:07:47 UTC (rev 197634)
@@ -0,0 +1,9 @@
+
+PASS Node.prototype.cloneNode(false) must be able to clone a custom element
+PASS Node.prototype.cloneNode(false) must be able to clone a custom element inside an iframe
+PASS Node.prototype.cloneNode(true) must be able to clone a descendent custom element
+PASS Node.prototype.cloneNode(true) must be able to clone a descendent custom element
+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 InvalidStateError when the custom element's constructor returns another element
+
Added: trunk/LayoutTests/fast/custom-elements/upgrading/Node-cloneNode.html (0 => 197634)
--- trunk/LayoutTests/fast/custom-elements/upgrading/Node-cloneNode.html (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/upgrading/Node-cloneNode.html 2016-03-06 05:07:47 UTC (rev 197634)
@@ -0,0 +1,169 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: Extensions to Document interface</title>
+<meta name="author" title="Ryosuke Niwa" href=""
+<meta name="assert" content="Node.prototype.cloneNode should upgrade a custom element">
+<link rel="help" href=""
+<script src=""
+<script src=""
+<link rel='stylesheet' href=''>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+function withNewDocumentWithABrowsingContext(callback) {
+ var iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+ try {
+ callback(iframe.contentWindow, iframe.contentDocument);
+ } finally {
+ document.body.removeChild(iframe);
+ }
+}
+
+test(function () {
+ class MyCustomElement extends HTMLElement {}
+ document.defineElement('my-custom-element', MyCustomElement);
+
+ var instance = document.createElement('my-custom-element');
+ assert_true(instance instanceof HTMLElement);
+ assert_true(instance instanceof MyCustomElement);
+
+ var clone = instance.cloneNode(false);
+ assert_not_equals(instance, clone);
+ assert_true(clone instanceof HTMLElement,
+ 'A cloned custom element must be an instance of HTMLElement');
+ assert_true(clone instanceof MyCustomElement,
+ 'A cloned custom element must be an instance of the custom element');
+}, 'Node.prototype.cloneNode(false) must be able to clone a custom element');
+
+test(function () {
+ withNewDocumentWithABrowsingContext(function (contentWindow, contentDocument) {
+ class MyCustomElement extends contentWindow.HTMLElement {}
+ contentDocument.defineElement('my-custom-element', MyCustomElement);
+
+ var instance = contentDocument.createElement('my-custom-element');
+ assert_true(instance instanceof contentWindow.HTMLElement);
+ assert_true(instance instanceof MyCustomElement);
+
+ var clone = instance.cloneNode(false);
+ assert_not_equals(instance, clone);
+ assert_true(clone instanceof contentWindow.HTMLElement,
+ 'A cloned custom element must be an instance of HTMLElement');
+ assert_true(clone instanceof MyCustomElement,
+ 'A cloned custom element must be an instance of the custom element');
+ });
+}, 'Node.prototype.cloneNode(false) must be able to clone a custom element inside an iframe');
+
+test(function () {
+ withNewDocumentWithABrowsingContext(function (contentWindow, contentDocument) {
+ class MyCustomElement extends contentWindow.HTMLElement { }
+ contentDocument.defineElement('my-custom-element', MyCustomElement);
+
+ var instance = contentDocument.createElement('my-custom-element');
+ var container = contentDocument.createElement('div');
+ container.appendChild(instance);
+
+ var containerClone = container.cloneNode(true);
+ assert_true(containerClone instanceof contentWindow.HTMLDivElement);
+
+ var clone = containerClone.firstChild;
+ assert_not_equals(instance, clone);
+ assert_true(clone instanceof contentWindow.HTMLElement,
+ 'A cloned custom element must be an instance of HTMLElement');
+ assert_true(clone instanceof MyCustomElement,
+ 'A cloned custom element must be an instance of the custom element');
+ });
+}, 'Node.prototype.cloneNode(true) must be able to clone a descendent custom element');
+
+test(function () {
+ withNewDocumentWithABrowsingContext(function (contentWindow, contentDocument) {
+ var parentNodeInConstructor;
+ var previousSiblingInConstructor;
+ var nextSiblingInConstructor;
+ class MyCustomElement extends contentWindow.HTMLElement {
+ constructor() {
+ super();
+ parentNodeInConstructor = this.parentNode;
+ previousSiblingInConstructor = this.previousSibling;
+ nextSiblingInConstructor = this.nextSibling;
+ }
+ }
+ contentDocument.defineElement('my-custom-element', MyCustomElement);
+
+ var instance = contentDocument.createElement('my-custom-element');
+ var siblingBeforeInstance = contentDocument.createElement('b');
+ var siblingAfterInstance = contentDocument.createElement('a');
+ var container = contentDocument.createElement('div');
+ container.appendChild(siblingBeforeInstance);
+ container.appendChild(instance);
+ container.appendChild(siblingAfterInstance);
+
+ var containerClone = container.cloneNode(true);
+
+ assert_equals(parentNodeInConstructor, containerClone,
+ 'An upgraded element must have its parentNode set before the custom element constructor is called');
+ assert_equals(previousSiblingInConstructor, containerClone.firstChild,
+ 'An upgraded element must have its previousSibling set before the custom element constructor is called');
+ assert_equals(nextSiblingInConstructor, containerClone.lastChild,
+ 'An upgraded element must have its nextSibling set before the custom element constructor is called');
+ });
+}, 'Node.prototype.cloneNode(true) must be able to clone a descendent custom element');
+
+test(function () {
+ withNewDocumentWithABrowsingContext(function (contentWindow, contentDocument) {
+ class MyCustomElement extends contentWindow.HTMLElement {
+ constructor(doNotCreateItself) {
+ super();
+ if (!doNotCreateItself)
+ new MyCustomElement(true);
+ }
+ }
+ contentDocument.defineElement('my-custom-element', MyCustomElement);
+
+ var instance = contentDocument.createElement('my-custom-element');
+ assert_throws({'name': 'InvalidStateError'}, function () { instance.cloneNode(false); });
+ });
+}, '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 () {
+ withNewDocumentWithABrowsingContext(function (contentWindow, contentDocument) {
+ class MyCustomElement extends contentWindow.HTMLElement {
+ constructor(doNotCreateItself) {
+ if (!doNotCreateItself)
+ new MyCustomElement(true);
+ super();
+ }
+ }
+ contentDocument.defineElement('my-custom-element', MyCustomElement);
+
+ var instance = contentDocument.createElement('my-custom-element');
+ assert_throws({'name': 'InvalidStateError'}, function () { instance.cloneNode(false); });
+ });
+}, '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 () {
+ withNewDocumentWithABrowsingContext(function (contentWindow, contentDocument) {
+ var cloning = false;
+ class MyCustomElement extends contentWindow.HTMLElement {
+ constructor() {
+ super();
+ if (cloning)
+ return contentDocument.createElement('span');
+ }
+ }
+ contentDocument.defineElement('my-custom-element', MyCustomElement);
+
+ var instance = contentDocument.createElement('my-custom-element');
+ cloning = true;
+ assert_throws({'name': 'InvalidStateError'}, function () { instance.cloneNode(false); });
+ });
+}, 'Upgrading a custom element must throw InvalidStateError when the custom element\'s constructor returns another element');
+
+</script>
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (197633 => 197634)
--- trunk/Source/WebCore/ChangeLog 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/Source/WebCore/ChangeLog 2016-03-06 05:07:47 UTC (rev 197634)
@@ -1,3 +1,52 @@
+2016-03-05 Ryosuke Niwa <rn...@webkit.org>
+
+ Add the support for upgrading custom elements in cloneNode
+ https://bugs.webkit.org/show_bug.cgi?id=155062
+
+ Reviewed by Antti Koivisto.
+
+ Implement https://w3c.github.io/webcomponents/spec/custom/#upgrading and steps 6 through 11 in
+ https://w3c.github.io/webcomponents/spec/custom/#htmlelement-constructor to support upgrading elements
+ created by Node.prototype.cloneNode.
+
+ Tests: fast/custom-elements/lifecycle-callback-timing.html
+ fast/custom-elements/upgrading/Node-cloneNode.html
+
+ * bindings/js/JSCustomElementInterface.cpp:
+ (WebCore::JSCustomElementInterface::upgradeElement): Added. Implements
+ https://w3c.github.io/webcomponents/spec/custom/#dfn-upgrade-a-custom-element
+ (WebCore::JSCustomElementInterface::didUpgradeLastElementInConstructionStack): Added. Implements step 10
+ "Replace the last entry in definition's construction stacka with an already constructed marker."
+ in https://w3c.github.io/webcomponents/spec/custom/#dom-htmlelement-constructor
+ * bindings/js/JSCustomElementInterface.h:
+ (WebCore::JSCustomElementInterface::isUpgradingElement):
+ (WebCore::JSCustomElementInterface::lastElementInConstructionStack):
+ (WebCore::JSCustomElementInterface): Added m_constructionStack. This is the construction stack:
+ https://w3c.github.io/webcomponents/spec/custom/#dfn-element-definition-construction-stack
+ * bindings/js/JSDOMBinding.cpp:
+ (WebCore::throwInvalidStateError): Added.
+ * bindings/js/JSDOMBinding.h:
+ * bindings/js/JSHTMLElementCustom.cpp:
+ (WebCore::constructJSHTMLElement): Implement the upgrading case in:
+ https://w3c.github.io/webcomponents/spec/custom/#htmlelement-constructor
+ * dom/Document.cpp:
+ (WebCore::createFallbackHTMLElement): Added. Enqueues upgrades of custom elements (enqueueElementUpgrade
+ currently does nothing if there is no InvokesCustomElementLifecycleCallbacks; e.g. in other DOM APIs).
+ This function implements https://w3c.github.io/webcomponents/spec/custom/#dfn-element-upgrade-algorithm
+ (WebCore::Document::createElement):
+ * dom/LifecycleCallbackQueue.cpp:
+ (WebCore::LifecycleQueueItem::LifecycleQueueItem): Added a generic constructor.
+ (WebCore::LifecycleQueueItem::invoke): Call upgradeElement when m_type is Type::ElementUpgrade.
+ (WebCore::LifecycleCallbackQueue::enqueueElementUpgrade): Added.
+ * dom/LifecycleCallbackQueue.h:
+ * dom/Node.idl: Added InvokesCustomElementLifecycleCallbacks on cloneNode.
+ * dom/make_names.pl:
+ (printFactoryCppFile): Added a variant of createKnownElement which takes QualifiedName. Also directly call
+ find(HTML|SVG|MathML)ElementConstructorFunction in createElement that takes AtomicString to avoid an extra
+ function call.
+ (printFactoryHeaderFile): Added a function declaration for createKnownElement that takes QualifiedName and
+ outdented class and function declarations to match the modern code style guideline.
+
2016-03-05 Tim Horton <timothy_hor...@apple.com>
Create a DOMHTMLVideoElement when wrapping <video> elements
Modified: trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp (197633 => 197634)
--- trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp 2016-03-06 05:07:47 UTC (rev 197634)
@@ -102,6 +102,54 @@
return wrappedElement;
}
+void JSCustomElementInterface::upgradeElement(Element& element)
+{
+ ASSERT(element.isCustomElement());
+ if (!canInvokeCallback())
+ return;
+
+ Ref<JSCustomElementInterface> protect(*this);
+ JSLockHolder lock(m_isolatedWorld->vm());
+
+ if (!m_constructor)
+ return;
+
+ ScriptExecutionContext* context = scriptExecutionContext();
+ if (!context)
+ return;
+ ASSERT(context->isDocument());
+ JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(context, *m_isolatedWorld);
+ ExecState* state = globalObject->globalExec();
+ if (state->hadException())
+ return;
+
+ ConstructData constructData;
+ ConstructType constructType = m_constructor->methodTable()->getConstructData(m_constructor.get(), constructData);
+ if (constructType == ConstructType::None) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ m_constructionStack.append(&element);
+
+ MarkedArgumentBuffer args;
+ InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionConstruct(context, constructType, constructData);
+ JSValue returnedElement = construct(state, m_constructor.get(), constructType, constructData, args);
+ InspectorInstrumentation::didCallFunction(cookie, context);
+
+ m_constructionStack.removeLast();
+
+ if (state->hadException())
+ return;
+
+ Element* wrappedElement = JSElement::toWrapped(returnedElement);
+ if (!wrappedElement || wrappedElement != &element) {
+ throwInvalidStateError(*state, "Custom element constructor failed to upgrade an element");
+ return;
+ }
+ ASSERT(wrappedElement->isCustomElement());
+}
+
void JSCustomElementInterface::attributeChanged(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)
{
if (!canInvokeCallback())
@@ -145,6 +193,11 @@
if (exception)
reportException(state, exception);
}
+
+void JSCustomElementInterface::didUpgradeLastElementInConstructionStack()
+{
+ m_constructionStack.last() = nullptr;
+}
} // namespace WebCore
Modified: trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h (197633 => 197634)
--- trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h 2016-03-06 05:07:47 UTC (rev 197634)
@@ -63,6 +63,8 @@
enum class ShouldClearException { Clear, DoNotClear };
RefPtr<Element> constructElement(const AtomicString&, ShouldClearException);
+ void upgradeElement(Element&);
+
void attributeChanged(Element&, const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue);
ScriptExecutionContext* scriptExecutionContext() const { return ContextDestructionObserver::scriptExecutionContext(); }
@@ -70,6 +72,10 @@
const QualifiedName& name() const { return m_name; }
+ bool isUpgradingElement() const { return !m_constructionStack.isEmpty(); }
+ Element* lastElementInConstructionStack() const { return m_constructionStack.last().get(); }
+ void didUpgradeLastElementInConstructionStack();
+
virtual ~JSCustomElementInterface();
private:
@@ -78,6 +84,7 @@
QualifiedName m_name;
mutable JSC::Weak<JSC::JSObject> m_constructor;
RefPtr<DOMWrapperWorld> m_isolatedWorld;
+ Vector<RefPtr<Element>, 1> m_constructionStack;
};
} // namespace WebCore
Modified: trunk/Source/WebCore/bindings/js/JSDOMBinding.cpp (197633 => 197634)
--- trunk/Source/WebCore/bindings/js/JSDOMBinding.cpp 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/Source/WebCore/bindings/js/JSDOMBinding.cpp 2016-03-06 05:07:47 UTC (rev 197634)
@@ -642,6 +642,13 @@
state.vm().throwException(&state, createDOMException(&state, NOT_SUPPORTED_ERR, &messageString));
}
+void throwInvalidStateError(JSC::ExecState& state, const char* message)
+{
+ ASSERT(!state.hadException());
+ String messageString(message);
+ state.vm().throwException(&state, createDOMException(&state, INVALID_STATE_ERR, &messageString));
+}
+
JSC::EncodedJSValue throwArgumentMustBeEnumError(JSC::ExecState& state, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName, const char* expectedValues)
{
StringBuilder builder;
Modified: trunk/Source/WebCore/bindings/js/JSDOMBinding.h (197633 => 197634)
--- trunk/Source/WebCore/bindings/js/JSDOMBinding.h 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/Source/WebCore/bindings/js/JSDOMBinding.h 2016-03-06 05:07:47 UTC (rev 197634)
@@ -85,6 +85,7 @@
WEBCORE_EXPORT void reportDeprecatedSetterError(JSC::ExecState&, const char* interfaceName, const char* attributeName);
void throwNotSupportedError(JSC::ExecState&, const char* message);
+void throwInvalidStateError(JSC::ExecState&, const char* message);
void throwArrayElementTypeError(JSC::ExecState&);
void throwAttributeTypeError(JSC::ExecState&, const char* interfaceName, const char* attributeName, const char* expectedType);
WEBCORE_EXPORT void throwSequenceTypeError(JSC::ExecState&);
Modified: trunk/Source/WebCore/bindings/js/JSHTMLElementCustom.cpp (197633 => 197634)
--- trunk/Source/WebCore/bindings/js/JSHTMLElementCustom.cpp 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/Source/WebCore/bindings/js/JSHTMLElementCustom.cpp 2016-03-06 05:07:47 UTC (rev 197634)
@@ -29,6 +29,7 @@
#include "CustomElementDefinitions.h"
#include "Document.h"
#include "HTMLFormElement.h"
+#include "JSNodeCustom.h"
#include <runtime/InternalFunction.h>
#include <runtime/JSWithScope.h>
@@ -57,16 +58,40 @@
if (!interface)
return throwVMTypeError(state, "new.target does not define a custom element");
- auto* globalObject = jsConstructor->globalObject();
- Structure* baseStructure = getDOMStructure<JSHTMLElement>(vm, *globalObject);
- auto* newElementStructure = InternalFunction::createSubclassStructure(state, newTargetValue, baseStructure);
- if (UNLIKELY(state->hadException()))
+ if (!interface->isUpgradingElement()) {
+ auto* globalObject = jsConstructor->globalObject();
+ Structure* baseStructure = getDOMStructure<JSHTMLElement>(vm, *globalObject);
+ auto* newElementStructure = InternalFunction::createSubclassStructure(state, newTargetValue, baseStructure);
+ if (UNLIKELY(state->hadException()))
+ return JSValue::encode(jsUndefined());
+
+ Ref<HTMLElement> element = HTMLElement::create(interface->name(), document);
+ auto* jsElement = JSHTMLElement::create(newElementStructure, globalObject, element.get());
+ cacheWrapper(globalObject->world(), element.ptr(), jsElement);
+ return JSValue::encode(jsElement);
+ }
+
+ Element* elementToUpgrade = interface->lastElementInConstructionStack();
+ if (!elementToUpgrade) {
+ throwInvalidStateError(*state, "Cannot instantiate a custom element inside its own constrcutor during upgrades");
return JSValue::encode(jsUndefined());
+ }
- Ref<HTMLElement> element = HTMLElement::create(interface->name(), document);
- auto* jsElement = JSHTMLElement::create(newElementStructure, globalObject, element.get());
- cacheWrapper(globalObject->world(), element.ptr(), jsElement);
- return JSValue::encode(jsElement);
+ JSValue elementWrapperValue = toJS(state, jsConstructor->globalObject(), elementToUpgrade);
+ ASSERT(elementWrapperValue.isObject());
+
+ JSValue newPrototype = newTarget->get(state, vm.propertyNames->prototype);
+ if (state->hadException())
+ return JSValue::encode(jsUndefined());
+
+ JSObject* elementWrapperObject = asObject(elementWrapperValue);
+ JSObject::setPrototype(elementWrapperObject, state, newPrototype, true /* shouldThrowIfCantSet */);
+ if (state->hadException())
+ return JSValue::encode(jsUndefined());
+
+ interface->didUpgradeLastElementInConstructionStack();
+
+ return JSValue::encode(elementWrapperValue);
}
#endif
Modified: trunk/Source/WebCore/dom/Document.cpp (197633 => 197634)
--- trunk/Source/WebCore/dom/Document.cpp 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/Source/WebCore/dom/Document.cpp 2016-03-06 05:07:47 UTC (rev 197634)
@@ -103,6 +103,7 @@
#include "JSModuleLoader.h"
#include "KeyboardEvent.h"
#include "Language.h"
+#include "LifecycleCallbackQueue.h"
#include "LoaderStrategy.h"
#include "Logging.h"
#include "MainFrame.h"
@@ -1072,15 +1073,33 @@
return hasValidNamespaceForElements(qName);
}
+static Ref<HTMLElement> createFallbackHTMLElement(Document& document, const QualifiedName& name)
+{
+#if ENABLE(CUSTOM_ELEMENTS)
+ auto* definitions = document.customElementDefinitions();
+ 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.
+ LifecycleCallbackQueue::enqueueElementUpgrade(element.get(), *interface);
+ return element;
+ }
+ }
+#endif
+ return HTMLUnknownElement::create(name, document);
+}
+
// FIXME: This should really be in a possible ElementFactory class.
Ref<Element> Document::createElement(const QualifiedName& name, bool createdByParser)
{
RefPtr<Element> element;
// FIXME: Use registered namespaces and look up in a hash to find the right factory.
- if (name.namespaceURI() == xhtmlNamespaceURI)
- element = HTMLElementFactory::createElement(name, *this, nullptr, createdByParser);
- else if (name.namespaceURI() == SVGNames::svgNamespaceURI)
+ if (name.namespaceURI() == xhtmlNamespaceURI) {
+ element = HTMLElementFactory::createKnownElement(name, *this, nullptr, createdByParser);
+ if (UNLIKELY(!element))
+ element = createFallbackHTMLElement(*this, name);
+ } else if (name.namespaceURI() == SVGNames::svgNamespaceURI)
element = SVGElementFactory::createElement(name, *this, createdByParser);
#if ENABLE(MATHML)
else if (name.namespaceURI() == MathMLNames::mathmlNamespaceURI)
Modified: trunk/Source/WebCore/dom/LifecycleCallbackQueue.cpp (197633 => 197634)
--- trunk/Source/WebCore/dom/LifecycleCallbackQueue.cpp 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/Source/WebCore/dom/LifecycleCallbackQueue.cpp 2016-03-06 05:07:47 UTC (rev 197634)
@@ -33,6 +33,7 @@
#include "JSCustomElementInterface.h"
#include "JSDOMBinding.h"
#include <heap/Heap.h>
+#include <wtf/Optional.h>
#include <wtf/Ref.h>
namespace WebCore {
@@ -40,9 +41,16 @@
class LifecycleQueueItem {
public:
enum class Type {
+ ElementUpgrade,
AttributeChanged,
};
+ LifecycleQueueItem(Type type, Element& element, JSCustomElementInterface& interface)
+ : m_type(type)
+ , m_element(element)
+ , m_interface(interface)
+ { }
+
LifecycleQueueItem(Element& element, JSCustomElementInterface& interface, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)
: m_type(Type::AttributeChanged)
, m_element(element)
@@ -54,14 +62,22 @@
void invoke()
{
- m_interface->attributeChanged(m_element.get(), m_attributeName, m_oldValue, m_newValue);
+ switch (m_type) {
+ case Type::ElementUpgrade:
+ m_interface->upgradeElement(m_element.get());
+ break;
+ case Type::AttributeChanged:
+ ASSERT(m_attributeName);
+ m_interface->attributeChanged(m_element.get(), m_attributeName.value(), m_oldValue, m_newValue);
+ break;
+ }
}
private:
Type m_type;
Ref<Element> m_element;
Ref<JSCustomElementInterface> m_interface;
- QualifiedName m_attributeName;
+ Optional<QualifiedName> m_attributeName;
AtomicString m_oldValue;
AtomicString m_newValue;
};
@@ -74,6 +90,12 @@
ASSERT(m_items.isEmpty());
}
+void LifecycleCallbackQueue::enqueueElementUpgrade(Element& element, JSCustomElementInterface& interface)
+{
+ if (auto* queue = CustomElementLifecycleProcessingStack::ensureCurrentQueue())
+ queue->m_items.append(LifecycleQueueItem(LifecycleQueueItem::Type::ElementUpgrade, element, interface));
+}
+
void LifecycleCallbackQueue::enqueueAttributeChangedCallback(Element& element, JSCustomElementInterface& interface,
const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)
{
Modified: trunk/Source/WebCore/dom/LifecycleCallbackQueue.h (197633 => 197634)
--- trunk/Source/WebCore/dom/LifecycleCallbackQueue.h 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/Source/WebCore/dom/LifecycleCallbackQueue.h 2016-03-06 05:07:47 UTC (rev 197634)
@@ -46,6 +46,8 @@
LifecycleCallbackQueue();
~LifecycleCallbackQueue();
+ static void enqueueElementUpgrade(Element&, JSCustomElementInterface&);
+
static void enqueueAttributeChangedCallback(Element&, JSCustomElementInterface&,
const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue);
Modified: trunk/Source/WebCore/dom/Node.idl (197633 => 197634)
--- trunk/Source/WebCore/dom/Node.idl 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/Source/WebCore/dom/Node.idl 2016-03-06 05:07:47 UTC (rev 197634)
@@ -70,7 +70,10 @@
[Custom, RaisesException] Node appendChild([CustomReturn] Node newChild);
boolean hasChildNodes();
- [NewObject, RaisesException, ImplementedAs=cloneNodeForBindings] Node cloneNode([Default=Undefined] optional boolean deep);
+
+ [NewObject, RaisesException, ImplementedAs=cloneNodeForBindings, InvokesCustomElementLifecycleCallbacks]
+ Node cloneNode([Default=Undefined] optional boolean deep);
+
void normalize();
// Introduced in DOM Level 2:
Modified: trunk/Source/WebCore/dom/make_names.pl (197633 => 197634)
--- trunk/Source/WebCore/dom/make_names.pl 2016-03-06 05:02:49 UTC (rev 197633)
+++ trunk/Source/WebCore/dom/make_names.pl 2016-03-06 05:07:47 UTC (rev 197634)
@@ -1047,14 +1047,25 @@
return nullptr;
}
-Ref<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const AtomicString& name, Document& document$formElementArgumentForDefinition, bool createdByParser)
+RefPtr<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createKnownElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser)
{
- RefPtr<$parameters{namespace}Element> element = $parameters{namespace}ElementFactory::createKnownElement($argumentList);
- if (LIKELY(element))
- return element.releaseNonNull();
- return $parameters{fallbackInterfaceName}::create(QualifiedName(nullAtom, name, ${lowercaseNamespacePrefix}NamespaceURI), document);
+ const ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(name.localName());
+ if (LIKELY(entry.function))
+ return entry.function($argumentList);
+ return nullptr;
}
+Ref<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const AtomicString& localName, Document& document$formElementArgumentForDefinition, bool createdByParser)
+{
+ const ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(localName);
+ if (LIKELY(entry.function)) {
+ ASSERT(entry.qualifiedName);
+ const auto& name = *entry.qualifiedName;
+ return entry.function($argumentList);
+ }
+ return $parameters{fallbackInterfaceName}::create(QualifiedName(nullAtom, localName, ${lowercaseNamespacePrefix}NamespaceURI), document);
+}
+
Ref<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser)
{
const ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(name.localName());
@@ -1089,29 +1100,36 @@
namespace WebCore {
- class Document;
- class HTMLFormElement;
- class QualifiedName;
+class Document;
+class HTMLFormElement;
+class QualifiedName;
- class $parameters{namespace}Element;
+class $parameters{namespace}Element;
- class $parameters{namespace}ElementFactory {
- public:
+class $parameters{namespace}ElementFactory {
+public:
END
;
-print F " static RefPtr<$parameters{namespace}Element> createKnownElement(const AtomicString& localName, Document&";
+print F "static RefPtr<$parameters{namespace}Element> createKnownElement(const AtomicString&, Document&";
print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML";
-print F ", bool createdByParser = false);\n\n";
-print F " static Ref<$parameters{namespace}Element> createElement(const AtomicString& localName, Document&";
+print F ", bool createdByParser = false);\n";
+
+print F "static RefPtr<$parameters{namespace}Element> createKnownElement(const QualifiedName&, Document&";
print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML";
print F ", bool createdByParser = false);\n";
-print F " static Ref<$parameters{namespace}Element> createElement(const QualifiedName& localName, Document&";
+
+print F "static Ref<$parameters{namespace}Element> createElement(const AtomicString&, Document&";
print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML";
print F ", bool createdByParser = false);\n";
+print F "static Ref<$parameters{namespace}Element> createElement(const QualifiedName&, Document&";
+print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML";
+print F ", bool createdByParser = false);\n";
+
printf F<<END
- };
+};
+
}
#endif // $parameters{namespace}ElementFactory_h