Diff
Modified: trunk/LayoutTests/ChangeLog (208715 => 208716)
--- trunk/LayoutTests/ChangeLog 2016-11-15 00:19:24 UTC (rev 208715)
+++ trunk/LayoutTests/ChangeLog 2016-11-15 00:27:01 UTC (rev 208716)
@@ -1,3 +1,17 @@
+2016-11-14 Ryosuke Niwa <[email protected]>
+
+ document.createElementNS doesn't construct a custom element
+ https://bugs.webkit.org/show_bug.cgi?id=164700
+
+ Reviewed by Darin Adler.
+
+ Added W3C style testharness.js tests for createElementNS and DOMImplementation's createDocument.
+
+ * fast/custom-elements/DOMImplementation-createDocument-expected.txt: Added.
+ * fast/custom-elements/DOMImplementation-createDocument.html: Added.
+ * fast/custom-elements/document-createElementNS-expected.txt: Added.
+ * fast/custom-elements/document-createElementNS.html: Added.
+
2016-11-14 Dean Jackson <[email protected]>
Handle filter() image type in new CSS Parser
Added: trunk/LayoutTests/fast/custom-elements/DOMImplementation-createDocument-expected.txt (0 => 208716)
--- trunk/LayoutTests/fast/custom-elements/DOMImplementation-createDocument-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/DOMImplementation-createDocument-expected.txt 2016-11-15 00:27:01 UTC (rev 208716)
@@ -0,0 +1,3 @@
+
+PASS document.createElement invoke a custom element constructor with no arguments
+
Added: trunk/LayoutTests/fast/custom-elements/DOMImplementation-createDocument.html (0 => 208716)
--- trunk/LayoutTests/fast/custom-elements/DOMImplementation-createDocument.html (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/DOMImplementation-createDocument.html 2016-11-15 00:27:01 UTC (rev 208716)
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: document.implementation.createElement must not instantiate a custom element</title>
+<meta name="author" title="Ryosuke Niwa" href=""
+<meta name="assert" content="document.implementation.createElement must not instantiate a custom element">
+<link rel="help" content="https://dom.spec.whatwg.org/#dom-domimplementation-createdocument">
+<link rel="help" content="https://html.spec.whatwg.org/#look-up-a-custom-element-definition">
+<script src=""
+<script src=""
+<script src=""
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+const xhtmlNamespaceURI = 'http://www.w3.org/1999/xhtml';
+
+test_with_window((contentWindow, contentDocument) => {
+ let constructorCount = 0;
+ class MyCustomElement extends contentWindow.HTMLElement {
+ constructor() {
+ super();
+ constructorCount++;
+ }
+ }
+ contentWindow.customElements.define('my-custom-element', MyCustomElement);
+ const instance = contentDocument.createElement('my-custom-element');
+ assert_true(instance instanceof MyCustomElement);
+ assert_equals(constructorCount, 1);
+
+ const newDocument = contentDocument.implementation.createDocument(xhtmlNamespaceURI, 'my-custom-element', null);
+ assert_equals(constructorCount, 1);
+ assert_true(newDocument.documentElement instanceof contentWindow.HTMLElement);
+ assert_equals(newDocument.documentElement.localName, 'my-custom-element');
+ assert_false(newDocument.documentElement instanceof MyCustomElement);
+}, 'document.createElement invoke a custom element constructor with no arguments');
+
+</script>
+</body>
+</html>
Added: trunk/LayoutTests/fast/custom-elements/document-createElementNS-expected.txt (0 => 208716)
--- trunk/LayoutTests/fast/custom-elements/document-createElementNS-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/document-createElementNS-expected.txt 2016-11-15 00:27:01 UTC (rev 208716)
@@ -0,0 +1,6 @@
+
+PASS document.createElement invoke a custom element constructor with no arguments
+PASS document.createElementNS must create an instance of custom elements
+PASS document.createElementNS must set the prefix after constructing a custom element
+PASS document.createElementNS must use the element returned by the constructor
+
Added: trunk/LayoutTests/fast/custom-elements/document-createElementNS.html (0 => 208716)
--- trunk/LayoutTests/fast/custom-elements/document-createElementNS.html (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/document-createElementNS.html 2016-11-15 00:27:01 UTC (rev 208716)
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: document.createElementNS must create an element with synchronous custom elements flag set</title>
+<meta name="author" title="Ryosuke Niwa" href=""
+<meta name="assert" content="document.createElementNS must create an element with synchronous custom elements flag set">
+<link rel="help" content="https://dom.spec.whatwg.org/#dom-document-createelementns">
+<link rel="help" content="https://dom.spec.whatwg.org/#concept-create-element">
+<script src=""
+<script src=""
+<script src=""
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+const xhtmlNamespaceURI = 'http://www.w3.org/1999/xhtml';
+
+test_with_window((contentWindow, contentDocument) => {
+ let constructorArguments;
+ class MyCustomElement extends contentWindow.HTMLElement {
+ constructor() {
+ constructorArguments = Array.from(arguments);
+ super();
+ }
+ }
+ contentWindow.customElements.define('my-custom-element', MyCustomElement);
+ const instance = contentDocument.createElement('my-custom-element');
+ assert_true(instance instanceof MyCustomElement);
+ assert_array_equals(constructorArguments, []);
+}, 'document.createElement invoke a custom element constructor with no arguments');
+
+test_with_window((contentWindow, contentDocument) => {
+ class MyCustomElement extends contentWindow.HTMLElement {};
+
+ const upgradeCandidate = contentDocument.createElementNS(xhtmlNamespaceURI, 'my-custom-element');
+ assert_true(upgradeCandidate instanceof contentWindow.HTMLElement);
+ assert_false(upgradeCandidate instanceof MyCustomElement);
+
+ contentWindow.customElements.define('my-custom-element', MyCustomElement);
+ const instance = contentDocument.createElementNS(xhtmlNamespaceURI, 'my-custom-element');
+ assert_true(instance instanceof MyCustomElement);
+ assert_equals(instance.localName, 'my-custom-element');
+ assert_equals(instance.namespaceURI, xhtmlNamespaceURI);
+}, 'document.createElementNS must create an instance of custom elements');
+
+test_with_window((contentWindow, contentDocument) => {
+ class MyCustomElement extends contentWindow.HTMLElement {};
+ contentWindow.customElements.define('element-with-prefix', MyCustomElement);
+
+ const instance = contentDocument.createElementNS(xhtmlNamespaceURI, 'foo:element-with-prefix');
+ assert_true(instance instanceof MyCustomElement);
+ assert_equals(instance.localName, 'element-with-prefix');
+ assert_equals(instance.namespaceURI, xhtmlNamespaceURI);
+ assert_equals(instance.prefix, 'foo');
+}, 'document.createElementNS must set the prefix after constructing a custom element');
+
+test_with_window((contentWindow, contentDocument) => {
+ let innerElement;
+ let innerElementPrefix;
+ let outerElement;
+ let outerElementPrefix;
+ class MyCustomElement extends contentWindow.HTMLElement {
+ constructor(isInnerCall) {
+ if (isInnerCall) {
+ innerElement = super();
+ innerElementPrefix = innerElement.prefix;
+ return innerElement;
+ }
+ outerElement = super();
+ outerElementPrefix = outerElement.prefix;
+ return new MyCustomElement(true);
+ }
+ }
+ contentWindow.customElements.define('custom-element', MyCustomElement);
+
+ const instance = contentDocument.createElementNS(xhtmlNamespaceURI, 'foo:custom-element');
+ assert_true(instance instanceof MyCustomElement);
+ assert_equals(instance.localName, 'custom-element');
+ assert_equals(instance.namespaceURI, xhtmlNamespaceURI);
+ assert_equals(instance.prefix, 'foo');
+ assert_equals(instance, innerElement, 'document.createElementNS must create an element with the synchronous custom elements flag set');
+ assert_equals(innerElementPrefix, null, 'HTML constructor must not set prefix');
+ assert_equals(outerElementPrefix, null, 'HTML constructor must not set prefix');
+}, 'document.createElementNS must use the element returned by the constructor');
+
+</script>
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (208715 => 208716)
--- trunk/Source/WebCore/ChangeLog 2016-11-15 00:19:24 UTC (rev 208715)
+++ trunk/Source/WebCore/ChangeLog 2016-11-15 00:27:01 UTC (rev 208716)
@@ -1,3 +1,46 @@
+2016-11-14 Ryosuke Niwa <[email protected]>
+
+ document.createElementNS doesn't construct a custom element
+ https://bugs.webkit.org/show_bug.cgi?id=164700
+
+ Reviewed by Darin Adler.
+
+ Fixed the bug that document.createElementNS doesn't create a custom element or enqueue it to upgrade.
+
+ Also made constructCustomElementSynchronously not call the custom element constructors with the element's
+ local name as the first argument, which was a non-standard behavior added during prototyping.
+
+ Test: fast/custom-elements/DOMImplementation-createDocument.html
+ fast/custom-elements/document-createElementNS.html
+
+ * bindings/js/JSCustomElementInterface.cpp:
+ (WebCore::JSCustomElementInterface::constructElementWithFallback): Added a variant that takes QualifiedName
+ instead of AtomicString.
+ (WebCore::constructCustomElementSynchronously): Don't add the local name as an argument.
+ * bindings/js/JSCustomElementInterface.h:
+
+ * dom/CustomElementRegistry.cpp:
+ (WebCore::CustomElementRegistry::findInterface): Just find the interface based on the local name after
+ checking the namespace URI to be that of the XHTML. We need to ignore the prefix for the purpose of looking
+ up the custom element definition as specified in the latest HTML specification:
+ https://html.spec.whatwg.org/multipage/scripting.html#look-up-a-custom-element-definition
+
+ * dom/DOMImplementation.cpp:
+ (WebCore::DOMImplementation::createDocument): Added an assertion to make sure we don't invoke scripts while
+ constructing the document element.
+
+ * dom/Document.cpp:
+ (WebCore::createUpgradeCandidateElement): Made this function create a HTMLUnknownElement instead of returning
+ nullptr to share more code. Also added a variant which takes QualifiedName.
+ (WebCore::isValidHTMLElementName): Added; helpers for createHTMLElementWithNameValidation to call isValidName
+ on Document with the right argument.
+ (WebCore::createHTMLElementWithNameValidation): Templatized the function to be called with either AtomicString
+ or QualifiedName for the name.
+ (WebCore::createFallbackHTMLElement):
+ (WebCore::Document::createElementNS): Call createHTMLElementWithNameValidation to create a custom element if
+ possible. This function ends up re-validating the element name before creating a HTMLUnknownElement but that
+ shouldn't be a common scenario to matter. In fact, createElementNS is a rarely used API.
+
2016-11-14 Chris Dumez <[email protected]>
Avoid copying attributes vector when constructing a CustomElement in HTMLTreeBuilder
Modified: trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp (208715 => 208716)
--- trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp 2016-11-15 00:19:24 UTC (rev 208715)
+++ trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp 2016-11-15 00:27:01 UTC (rev 208716)
@@ -67,9 +67,24 @@
element->setIsCustomElementUpgradeCandidate();
element->setIsFailedCustomElement(*this);
- return element.get();
+ return WTFMove(element);
}
+Ref<Element> JSCustomElementInterface::constructElementWithFallback(Document& document, const QualifiedName& name)
+{
+ if (auto element = tryToConstructCustomElement(document, name.localName())) {
+ if (name.prefix() != nullAtom)
+ element->setPrefix(name.prefix());
+ return element.releaseNonNull();
+ }
+
+ auto element = HTMLUnknownElement::create(name, document);
+ element->setIsCustomElementUpgradeCandidate();
+ element->setIsFailedCustomElement(*this);
+
+ return WTFMove(element);
+}
+
RefPtr<Element> JSCustomElementInterface::tryToConstructCustomElement(Document& document, const AtomicString& localName)
{
if (!canInvokeCallback())
@@ -110,10 +125,8 @@
return nullptr;
}
+ InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionConstruct(&document, constructType, constructData);
MarkedArgumentBuffer args;
- args.append(jsStringWithCache(&state, localName));
-
- InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionConstruct(&document, constructType, constructData);
JSValue newElement = construct(&state, constructor, constructType, constructData, args);
InspectorInstrumentation::didCallFunction(cookie, &document);
RETURN_IF_EXCEPTION(scope, nullptr);
Modified: trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h (208715 => 208716)
--- trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h 2016-11-15 00:19:24 UTC (rev 208715)
+++ trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h 2016-11-15 00:27:01 UTC (rev 208716)
@@ -59,6 +59,7 @@
}
Ref<Element> constructElementWithFallback(Document&, const AtomicString&);
+ Ref<Element> constructElementWithFallback(Document&, const QualifiedName&);
void upgradeElement(Element&);
Modified: trunk/Source/WebCore/dom/CustomElementRegistry.cpp (208715 => 208716)
--- trunk/Source/WebCore/dom/CustomElementRegistry.cpp 2016-11-15 00:19:24 UTC (rev 208715)
+++ trunk/Source/WebCore/dom/CustomElementRegistry.cpp 2016-11-15 00:27:01 UTC (rev 208716)
@@ -87,11 +87,9 @@
JSCustomElementInterface* CustomElementRegistry::findInterface(const QualifiedName& name) const
{
- ASSERT(!name.hasPrefix());
if (name.namespaceURI() != HTMLNames::xhtmlNamespaceURI)
return nullptr;
- auto it = m_nameMap.find(name.localName());
- return it == m_nameMap.end() || it->value->name() != name ? nullptr : const_cast<JSCustomElementInterface*>(it->value.ptr());
+ return m_nameMap.get(name.localName());
}
JSCustomElementInterface* CustomElementRegistry::findInterface(const AtomicString& name) const
Modified: trunk/Source/WebCore/dom/DOMImplementation.cpp (208715 => 208716)
--- trunk/Source/WebCore/dom/DOMImplementation.cpp 2016-11-15 00:19:24 UTC (rev 208715)
+++ trunk/Source/WebCore/dom/DOMImplementation.cpp 2016-11-15 00:27:01 UTC (rev 208716)
@@ -114,6 +114,7 @@
RefPtr<Element> documentElement;
if (!qualifiedName.isEmpty()) {
+ ASSERT(!document->domWindow()); // If domWindow is not null, createElementNS could find CustomElementRegistry and arbitrary scripts.
auto result = document->createElementNS(namespaceURI, qualifiedName);
if (result.hasException())
return result.releaseException();
Modified: trunk/Source/WebCore/dom/Document.cpp (208715 => 208716)
--- trunk/Source/WebCore/dom/Document.cpp 2016-11-15 00:19:24 UTC (rev 208715)
+++ trunk/Source/WebCore/dom/Document.cpp 2016-11-15 00:27:01 UTC (rev 208716)
@@ -861,22 +861,36 @@
styleScope().clearResolver();
}
-static ALWAYS_INLINE RefPtr<HTMLElement> createUpgradeCandidateElement(Document& document, const QualifiedName& name)
+static ALWAYS_INLINE Ref<HTMLElement> createUpgradeCandidateElement(Document& document, const QualifiedName& name)
{
- if (!RuntimeEnabledFeatures::sharedFeatures().customElementsEnabled())
- return nullptr;
+ if (!RuntimeEnabledFeatures::sharedFeatures().customElementsEnabled()
+ || Document::validateCustomElementName(name.localName()) != CustomElementNameValidationStatus::Valid)
+ return HTMLUnknownElement::create(name, document);
- if (Document::validateCustomElementName(name.localName()) != CustomElementNameValidationStatus::Valid)
- return nullptr;
-
auto element = HTMLElement::create(name, document);
element->setIsCustomElementUpgradeCandidate();
- return WTFMove(element);
+ return element;
}
-static ExceptionOr<Ref<Element>> createHTMLElementWithNameValidation(Document& document, const AtomicString& localName)
+static ALWAYS_INLINE Ref<HTMLElement> createUpgradeCandidateElement(Document& document, const AtomicString& localName)
{
- auto element = HTMLElementFactory::createKnownElement(localName, document);
+ return createUpgradeCandidateElement(document, QualifiedName { nullAtom, localName, xhtmlNamespaceURI });
+}
+
+static inline bool isValidHTMLElementName(const AtomicString& localName)
+{
+ return Document::isValidName(localName);
+}
+
+static inline bool isValidHTMLElementName(const QualifiedName& name)
+{
+ return Document::isValidName(name.localName());
+}
+
+template<typename NameType>
+static ExceptionOr<Ref<Element>> createHTMLElementWithNameValidation(Document& document, const NameType& name)
+{
+ auto element = HTMLElementFactory::createKnownElement(name, document);
if (LIKELY(element))
return Ref<Element> { element.releaseNonNull() };
@@ -883,20 +897,15 @@
if (auto* window = document.domWindow()) {
auto* registry = window->customElementRegistry();
if (UNLIKELY(registry)) {
- if (auto* elementInterface = registry->findInterface(localName))
- return elementInterface->constructElementWithFallback(document, localName);
+ if (auto* elementInterface = registry->findInterface(name))
+ return elementInterface->constructElementWithFallback(document, name);
}
}
- if (UNLIKELY(!Document::isValidName(localName)))
+ if (UNLIKELY(!isValidHTMLElementName(name)))
return Exception { INVALID_CHARACTER_ERR };
- QualifiedName qualifiedName { nullAtom, localName, xhtmlNamespaceURI };
-
- if (auto element = createUpgradeCandidateElement(document, qualifiedName))
- return Ref<Element> { element.releaseNonNull() };
-
- return Ref<Element> { HTMLUnknownElement::create(qualifiedName, document) };
+ return Ref<Element> { createUpgradeCandidateElement(document, name) };
}
ExceptionOr<Ref<Element>> Document::createElementForBindings(const AtomicString& name)
@@ -1055,9 +1064,7 @@
}
}
// FIXME: Should we also check the equality of prefix between the custom element and name?
- if (auto element = createUpgradeCandidateElement(document, name))
- return element.releaseNonNull();
- return HTMLUnknownElement::create(name, document);
+ return createUpgradeCandidateElement(document, name);
}
// FIXME: This should really be in a possible ElementFactory class.
@@ -1159,6 +1166,10 @@
QualifiedName parsedName { parseResult.releaseReturnValue() };
if (!hasValidNamespaceForElements(parsedName))
return Exception { NAMESPACE_ERR };
+
+ if (parsedName.namespaceURI() == xhtmlNamespaceURI)
+ return createHTMLElementWithNameValidation(*this, parsedName);
+
return createElement(parsedName, false);
}