Update defineCustomElement according to the spec rewrite
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 5 Mar 2016 01:23:42 +0000 (01:23 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 5 Mar 2016 01:23:42 +0000 (01:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=155010
<rdar://problem/24970878>

Reviewed by Chris Dumez.

Source/WebCore:

Updated the implementation of defineCustomElement and HTMLConstructor per recent rewrite of the spec:
https://w3c.github.io/webcomponents/spec/custom/#dom-document-defineelement
https://w3c.github.io/webcomponents/spec/custom/#htmlelement-constructor

defineCustomElement is now called defineElement and we disallow defining multiple custom elements with
a single class and throw an exception in defineElement.

Test: fast/custom-elements/Document-defineElement.html

* bindings/js/JSDocumentCustom.cpp:
(WebCore::JSDocument::defineElement): Renamed from defineCustomElement. Throw an exception when the interface
already defines another custom element. Also added FIXME's for missing steps.

* bindings/js/JSHTMLElementCustom.cpp:
(WebCore::constructJSHTMLElement): Removed the support for specifying a tag name in the first argument when
a single class defines multiple custom elements since that now results in an exception (in defineElement).

* dom/CustomElementDefinitions.cpp:
(WebCore::CustomElementDefinitions::containsConstructor): Added.
* dom/CustomElementDefinitions.h:
* dom/Document.idl: Renamed defineCustomElement to defineElement.
* html/HTMLElement.idl: Removed the optional tag name from the constructor.

LayoutTests:

Update the tests for the rename and semantics change of defineCustomElement and HTMLElement constructor.

* fast/custom-elements/Document-createElement.html:
* fast/custom-elements/Document-defineCustomElement-expected.txt: Removed.
* fast/custom-elements/Document-defineCustomElement.html: Removed.
* fast/custom-elements/Document-defineElement-expected.txt: Renamed from LayoutTests/fast/custom-elements/Document-defineCustomElement-expected.txt.
* fast/custom-elements/Document-defineElement.html: Renamed from LayoutTests/fast/custom-elements/Document-defineCustomElement.html.
Also added a test case for defining multiple custom elements with a single class, which must throw.
* fast/custom-elements/HTMLElement-constructor-expected.txt:
* fast/custom-elements/HTMLElement-constructor.html:
Removed test cases for the tag name in the first argument as well as ones that associate a single class with multiple tag names.
* fast/custom-elements/parser/parser-constructs-custom-element-in-document-write.html:
* fast/custom-elements/parser/parser-constructs-custom-element-synchronously.html:
* fast/custom-elements/parser/parser-constructs-custom-elements.html:
* fast/custom-elements/parser/parser-fallsback-to-unknown-element.html:
* fast/custom-elements/parser/parser-sets-attributes-and-children.html:
* fast/custom-elements/parser/parser-uses-constructed-element.html:
* fast/custom-elements/parser/parser-uses-registry-of-owner-document.html:

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

22 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/custom-elements/Document-createElement.html
LayoutTests/fast/custom-elements/Document-defineCustomElement-expected.txt [deleted file]
LayoutTests/fast/custom-elements/Document-defineCustomElement.html [deleted file]
LayoutTests/fast/custom-elements/Document-defineElement-expected.txt [new file with mode: 0644]
LayoutTests/fast/custom-elements/Document-defineElement.html [new file with mode: 0644]
LayoutTests/fast/custom-elements/HTMLElement-constructor-expected.txt
LayoutTests/fast/custom-elements/HTMLElement-constructor.html
LayoutTests/fast/custom-elements/parser/parser-constructs-custom-element-in-document-write.html
LayoutTests/fast/custom-elements/parser/parser-constructs-custom-element-synchronously.html
LayoutTests/fast/custom-elements/parser/parser-constructs-custom-elements.html
LayoutTests/fast/custom-elements/parser/parser-fallsback-to-unknown-element.html
LayoutTests/fast/custom-elements/parser/parser-sets-attributes-and-children.html
LayoutTests/fast/custom-elements/parser/parser-uses-constructed-element.html
LayoutTests/fast/custom-elements/parser/parser-uses-registry-of-owner-document.html
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSDocumentCustom.cpp
Source/WebCore/bindings/js/JSHTMLElementCustom.cpp
Source/WebCore/dom/CustomElementDefinitions.cpp
Source/WebCore/dom/CustomElementDefinitions.h
Source/WebCore/dom/Document.idl
Source/WebCore/html/HTMLElement.idl

index 8e305c8..9e7a6e3 100644 (file)
@@ -1,3 +1,30 @@
+2016-03-04  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Update defineCustomElement according to the spec rewrite
+        https://bugs.webkit.org/show_bug.cgi?id=155010
+        <rdar://problem/24970878>
+
+        Reviewed by Chris Dumez.
+
+        Update the tests for the rename and semantics change of defineCustomElement and HTMLElement constructor.
+
+        * fast/custom-elements/Document-createElement.html:
+        * fast/custom-elements/Document-defineCustomElement-expected.txt: Removed.
+        * fast/custom-elements/Document-defineCustomElement.html: Removed.
+        * fast/custom-elements/Document-defineElement-expected.txt: Renamed from LayoutTests/fast/custom-elements/Document-defineCustomElement-expected.txt.
+        * fast/custom-elements/Document-defineElement.html: Renamed from LayoutTests/fast/custom-elements/Document-defineCustomElement.html.
+        Also added a test case for defining multiple custom elements with a single class, which must throw.
+        * fast/custom-elements/HTMLElement-constructor-expected.txt:
+        * fast/custom-elements/HTMLElement-constructor.html:
+        Removed test cases for the tag name in the first argument as well as ones that associate a single class with multiple tag names.
+        * fast/custom-elements/parser/parser-constructs-custom-element-in-document-write.html:
+        * fast/custom-elements/parser/parser-constructs-custom-element-synchronously.html:
+        * fast/custom-elements/parser/parser-constructs-custom-elements.html:
+        * fast/custom-elements/parser/parser-fallsback-to-unknown-element.html:
+        * fast/custom-elements/parser/parser-sets-attributes-and-children.html:
+        * fast/custom-elements/parser/parser-uses-constructed-element.html:
+        * fast/custom-elements/parser/parser-uses-registry-of-owner-document.html:
+
 2016-03-04  Simon Fraser  <simon.fraser@apple.com>
 
         Use larger tiles when possible to reduce per-tile painting overhead
index ccbd099..ae012b0 100644 (file)
@@ -18,7 +18,7 @@ test(function () {
     assert_true(document.createElement('my-custom-element') instanceof HTMLElement);
     assert_false(document.createElement('my-custom-element') instanceof MyCustomElement);
 
-    document.defineCustomElement('my-custom-element', MyCustomElement);
+    document.defineElement('my-custom-element', MyCustomElement);
     var instance = document.createElement('my-custom-element');
     assert_true(instance instanceof MyCustomElement);
     assert_equals(instance.localName, 'my-custom-element');
@@ -33,7 +33,7 @@ test(function () {
             return {foo: 'bar'};
         }
     };
-    document.defineCustomElement('object-custom-element', ObjectCustomElement);
+    document.defineElement('object-custom-element', ObjectCustomElement);
 
     var instance = new ObjectCustomElement;
     assert_true(instance instanceof Object);
@@ -49,7 +49,7 @@ test(function () {
             return document.createTextNode('hello');
         }
     };
-    document.defineCustomElement('text-custom-element', TextCustomElement);
+    document.defineElement('text-custom-element', TextCustomElement);
     assert_true(new TextCustomElement instanceof Text);
     assert_equals(document.createElement('object-custom-element'), null);
 }, 'document.createElement must return null when a custom element constructor returns a Text node');
@@ -64,7 +64,7 @@ test(function () {
             return createdElement;
         }
     };
-    document.defineCustomElement('div-custom-element', DivCustomElement);
+    document.defineElement('div-custom-element', DivCustomElement);
     assert_true(new DivCustomElement instanceof HTMLDivElement);
 
     var instance = document.createElement('div-custom-element');
@@ -82,7 +82,7 @@ test(function () {
                 throw exceptionToThrow;
         }
     };
-    document.defineCustomElement('throw-custom-element', ThrowCustomElement);
+    document.defineElement('throw-custom-element', ThrowCustomElement);
 
     assert_throws(null, function () { new ThrowCustomElement; });
 
diff --git a/LayoutTests/fast/custom-elements/Document-defineCustomElement-expected.txt b/LayoutTests/fast/custom-elements/Document-defineCustomElement-expected.txt
deleted file mode 100644 (file)
index 3eb709f..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-
-PASS Check the existence of defineCustomElement on Document interface 
-PASS document.defineCustomElement should throw with an invalid name 
-PASS document.defineCustomElement should throw with a duplicate name 
-PASS document.defineCustomElement must throw a NotSupportedError when the context object is an associated inert template document 
-PASS document.defineCustomElement must throw a NotSupportedError when the context object is created by DOMImplementation.createHTMLDocument 
-PASS document.defineCustomElement must throw a NotSupportedError when the context object is created by DOMImplementation.createDocument 
-PASS document.defineCustomElement should throw when the element interface is not a constructor 
-PASS document.defineCustomElement should define an instantiatable custom element 
-
diff --git a/LayoutTests/fast/custom-elements/Document-defineCustomElement.html b/LayoutTests/fast/custom-elements/Document-defineCustomElement.html
deleted file mode 100644 (file)
index e37e76e..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<title>Custom Elements: Extensions to Document interface</title>
-<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
-<meta name="assert" content="document.defineCustomElement should define a custom element">
-<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>
-<script>
-
-test(function () {
-    assert_true('defineCustomElement' in Document.prototype, '"defineCustomElement" exists on Document.prototype');
-    assert_true('defineCustomElement' in document, '"defineCustomElement" exists on document');
-}, 'Check the existence of defineCustomElement on Document interface');
-
-test(function () {
-    class MyCustomElement extends HTMLElement {};
-
-    assert_throws({'name': 'SyntaxError'}, function () { document.defineCustomElement(null, MyCustomElement); },
-        'document.defineCustomElement must throw a SyntaxError if the tag name is null');
-    assert_throws({'name': 'SyntaxError'}, function () { document.defineCustomElement('', MyCustomElement); },
-        'document.defineCustomElement must throw a SyntaxError if the tag name is empty');
-    assert_throws({'name': 'SyntaxError'}, function () { document.defineCustomElement('abc', MyCustomElement); },
-        'document.defineCustomElement must throw a SyntaxError if the tag name does not contain "-"');
-    assert_throws({'name': 'SyntaxError'}, function () { document.defineCustomElement('a-Bc', MyCustomElement); },
-        'document.defineCustomElement must throw a SyntaxError if the tag name contains an upper case letter');
-
-    var builtinTagNames = [
-        'annotation-xml',
-        'color-profile',
-        'font-face',
-        'font-face-src',
-        'font-face-uri',
-        'font-face-format',
-        'font-face-name',
-        'missing-glyph'
-    ];
-
-    for (var tagName of builtinTagNames) {
-        assert_throws({'name': 'SyntaxError'}, function () { document.defineCustomElement(tagName, MyCustomElement); },
-            'document.defineCustomElement must throw a SyntaxError if the tag name is "' + tagName + '"');
-    }
-
-}, 'document.defineCustomElement should throw with an invalid name');
-
-test(function () {
-    class SomeCustomElement extends HTMLElement {};
-    class OtherCustomElement extends HTMLElement {};
-
-    document.defineCustomElement('some-custom-element', SomeCustomElement);
-    assert_throws({'name': 'NotSupportedError'}, function () { document.defineCustomElement('some-custom-element', OtherCustomElement); },
-        'document.defineCustomElement must throw a NotSupportedError if the specified tag name is already used');
-
-}, 'document.defineCustomElement should throw with a duplicate name');
-
-test(function () {
-    class SomeCustomElement extends HTMLElement {};
-
-    var templateContentOwnerDocument = document.createElement('template').content.ownerDocument;
-    assert_throws({'name': 'NotSupportedError'}, function () {
-        templateContentOwnerDocument.defineCustomElement('some-custom-element', SomeCustomElement);
-    });
-
-}, 'document.defineCustomElement must throw a NotSupportedError when the context object is an associated inert template document');
-
-test(function () {
-    class SomeCustomElement extends HTMLElement {};
-
-    var windowlessDocument = document.implementation.createHTMLDocument();
-    assert_throws({'name': 'NotSupportedError'}, function () {
-        windowlessDocument.defineCustomElement('some-custom-element', SomeCustomElement);
-    });
-
-}, 'document.defineCustomElement must throw a NotSupportedError when the context object is created by DOMImplementation.createHTMLDocument');
-
-test(function () {
-    class SomeCustomElement extends HTMLElement {};
-
-    var windowlessDocument = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null)
-    assert_throws({'name': 'NotSupportedError'}, function () {
-        windowlessDocument.defineCustomElement('some-custom-element', SomeCustomElement);
-    });
-
-}, 'document.defineCustomElement must throw a NotSupportedError when the context object is created by DOMImplementation.createDocument');
-
-test(function () {
-    assert_throws({'name': 'TypeError'}, function () { document.defineCustomElement('invalid-element', 1); },
-        'document.defineCustomElement must throw a TypeError when the element interface is a number');
-    assert_throws({'name': 'TypeError'}, function () { document.defineCustomElement('invalid-element', '123'); },
-        'document.defineCustomElement must throw a TypeError when the element interface is a string');
-    assert_throws({'name': 'TypeError'}, function () { document.defineCustomElement('invalid-element', {}); },
-        'document.defineCustomElement must throw a TypeError when the element interface is an object');
-    assert_throws({'name': 'TypeError'}, function () { document.defineCustomElement('invalid-element', []); },
-        'document.defineCustomElement must throw a TypeError when the element interface is an array');
-}, 'document.defineCustomElement should throw when the element interface is not a constructor');
-
-test(function () {
-    class MyCustomElement extends HTMLElement {};
-    document.defineCustomElement('my-custom-element', MyCustomElement);
-
-    var instance = new MyCustomElement;
-    assert_true(instance instanceof MyCustomElement,
-        'An instance of a custom HTML element be an instance of the associated interface');
-
-    assert_true(instance instanceof HTMLElement,
-        'An instance of a custom HTML element must inherit from HTMLElement');
-
-    assert_equals(instance.localName, 'my-custom-element',
-        'An instance of a custom element must use the associated tag name');
-
-    assert_equals(instance.namespaceURI, 'http://www.w3.org/1999/xhtml',
-        'A custom element HTML must use HTML namespace');
-
-}, 'document.defineCustomElement should define an instantiatable custom element');
-
-</script>
-</body>
-</html>
diff --git a/LayoutTests/fast/custom-elements/Document-defineElement-expected.txt b/LayoutTests/fast/custom-elements/Document-defineElement-expected.txt
new file mode 100644 (file)
index 0000000..8b7b5aa
--- /dev/null
@@ -0,0 +1,11 @@
+
+PASS Check the existence of defineElement on Document interface 
+PASS document.defineElement should throw with an invalid name 
+PASS document.defineElement should throw when there is already a custom element of the same name 
+PASS document.defineElement should throw when there is already a custom element with the same class 
+PASS document.defineElement must throw a NotSupportedError when the context object is an associated inert template document 
+PASS document.defineElement must throw a NotSupportedError when the context object is created by DOMImplementation.createHTMLDocument 
+PASS document.defineElement must throw a NotSupportedError when the context object is created by DOMImplementation.createDocument 
+PASS document.defineElement should throw when the element interface is not a constructor 
+PASS document.defineElement should define an instantiatable custom element 
+
diff --git a/LayoutTests/fast/custom-elements/Document-defineElement.html b/LayoutTests/fast/custom-elements/Document-defineElement.html
new file mode 100644 (file)
index 0000000..6b6514f
--- /dev/null
@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: Extensions to Document interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="document.defineElement should define a custom element">
+<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>
+<script>
+
+test(function () {
+    assert_true('defineElement' in Document.prototype, '"defineElement" exists on Document.prototype');
+    assert_true('defineElement' in document, '"defineElement" exists on document');
+}, 'Check the existence of defineElement on Document interface');
+
+test(function () {
+    class MyCustomElement extends HTMLElement {};
+
+    assert_throws({'name': 'SyntaxError'}, function () { document.defineElement(null, MyCustomElement); },
+        'document.defineElement must throw a SyntaxError if the tag name is null');
+    assert_throws({'name': 'SyntaxError'}, function () { document.defineElement('', MyCustomElement); },
+        'document.defineElement must throw a SyntaxError if the tag name is empty');
+    assert_throws({'name': 'SyntaxError'}, function () { document.defineElement('abc', MyCustomElement); },
+        'document.defineElement must throw a SyntaxError if the tag name does not contain "-"');
+    assert_throws({'name': 'SyntaxError'}, function () { document.defineElement('a-Bc', MyCustomElement); },
+        'document.defineElement must throw a SyntaxError if the tag name contains an upper case letter');
+
+    var builtinTagNames = [
+        'annotation-xml',
+        'color-profile',
+        'font-face',
+        'font-face-src',
+        'font-face-uri',
+        'font-face-format',
+        'font-face-name',
+        'missing-glyph'
+    ];
+
+    for (var tagName of builtinTagNames) {
+        assert_throws({'name': 'SyntaxError'}, function () { document.defineElement(tagName, MyCustomElement); },
+            'document.defineElement must throw a SyntaxError if the tag name is "' + tagName + '"');
+    }
+
+}, 'document.defineElement should throw with an invalid name');
+
+test(function () {
+    class SomeCustomElement extends HTMLElement {};
+    class OtherCustomElement extends HTMLElement {};
+
+    document.defineElement('some-custom-element', SomeCustomElement);
+    assert_throws({'name': 'NotSupportedError'}, function () { document.defineElement('some-custom-element', OtherCustomElement); },
+        'document.defineElement must throw a NotSupportedError if the specified tag name is already used');
+
+}, 'document.defineElement should throw when there is already a custom element of the same name');
+
+test(function () {
+    class AnotherCustomElement extends HTMLElement {};
+
+    document.defineElement('another-custom-element', AnotherCustomElement);
+    assert_throws({'name': 'NotSupportedError'}, function () { document.defineElement('some-other-element', AnotherCustomElement); },
+        'document.defineElement must throw a NotSupportedError if the specified class already defines an element');
+
+}, 'document.defineElement should throw when there is already a custom element with the same class');
+
+test(function () {
+    class SomeCustomElement extends HTMLElement {};
+
+    var templateContentOwnerDocument = document.createElement('template').content.ownerDocument;
+    assert_throws({'name': 'NotSupportedError'}, function () {
+        templateContentOwnerDocument.defineElement('some-custom-element', SomeCustomElement);
+    });
+
+}, 'document.defineElement must throw a NotSupportedError when the context object is an associated inert template document');
+
+test(function () {
+    class SomeCustomElement extends HTMLElement {};
+
+    var windowlessDocument = document.implementation.createHTMLDocument();
+    assert_throws({'name': 'NotSupportedError'}, function () {
+        windowlessDocument.defineElement('some-custom-element', SomeCustomElement);
+    });
+
+}, 'document.defineElement must throw a NotSupportedError when the context object is created by DOMImplementation.createHTMLDocument');
+
+test(function () {
+    class SomeCustomElement extends HTMLElement {};
+
+    var windowlessDocument = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null)
+    assert_throws({'name': 'NotSupportedError'}, function () {
+        windowlessDocument.defineElement('some-custom-element', SomeCustomElement);
+    });
+
+}, 'document.defineElement must throw a NotSupportedError when the context object is created by DOMImplementation.createDocument');
+
+test(function () {
+    assert_throws({'name': 'TypeError'}, function () { document.defineElement('invalid-element', 1); },
+        'document.defineElement must throw a TypeError when the element interface is a number');
+    assert_throws({'name': 'TypeError'}, function () { document.defineElement('invalid-element', '123'); },
+        'document.defineElement must throw a TypeError when the element interface is a string');
+    assert_throws({'name': 'TypeError'}, function () { document.defineElement('invalid-element', {}); },
+        'document.defineElement must throw a TypeError when the element interface is an object');
+    assert_throws({'name': 'TypeError'}, function () { document.defineElement('invalid-element', []); },
+        'document.defineElement must throw a TypeError when the element interface is an array');
+}, 'document.defineElement should throw when the element interface is not a constructor');
+
+test(function () {
+    class MyCustomElement extends HTMLElement {};
+    document.defineElement('my-custom-element', MyCustomElement);
+
+    var instance = new MyCustomElement;
+    assert_true(instance instanceof MyCustomElement,
+        'An instance of a custom HTML element be an instance of the associated interface');
+
+    assert_true(instance instanceof HTMLElement,
+        'An instance of a custom HTML element must inherit from HTMLElement');
+
+    assert_equals(instance.localName, 'my-custom-element',
+        'An instance of a custom element must use the associated tag name');
+
+    assert_equals(instance.namespaceURI, 'http://www.w3.org/1999/xhtml',
+        'A custom element HTML must use HTML namespace');
+
+}, 'document.defineElement should define an instantiatable custom element');
+
+</script>
+</body>
+</html>
index 628b5a3..cc7bd1f 100644 (file)
@@ -2,7 +2,6 @@
 PASS HTMLElement constructor must throw a TypeError when there is no derived class 
 PASS HTMLElement constructor must throw TypeError when custom element is not well defined 
 PASS HTMLElement constructor must infer the tag name from the element interface 
-PASS HTMLElement constructor must allow associating an element interface with multiple tag names 
 PASS HTMLElement constructor must allow subclassing a custom element 
 PASS HTMLElement constructor must allow subclassing an user-defined subclass of HTMLElement 
 
index 5d5d3a5..e36faec 100644 (file)
 
 test(function () {
     class SomeDefinedElement extends HTMLElement {};
-    document.defineCustomElement('defined-element', SomeDefinedElement);
+    document.defineElement('defined-element', SomeDefinedElement);
     assert_throws({'name': 'TypeError'}, function () { new HTMLElement('defined-element'); });
 }, 'HTMLElement constructor must throw a TypeError when there is no derived class');
 
 test(function () {
     class SomeCustomElement extends HTMLElement {};
     assert_throws({'name': 'TypeError'}, function () { new SomeCustomElement; },
-        'Instantiating a custom element without calling defineCustomElement must throw TypeError');
-
-    class AnotherCustomElement extends HTMLElement {
-        constructor() { super('some-element'); }
-    };
-    document.defineCustomElement('another-element', AnotherCustomElement);
-    assert_throws({'name': 'TypeError'}, function () { new AnotherCustomElement; },
-        'Calling HTMLElement constructor with a mismatching tag name throw TypeError');
-
-    class YetAnotherCustomElement extends HTMLElement {
-        constructor() { super(1); }
-    };
-    document.defineCustomElement('yet-another-element', YetAnotherCustomElement);
-    assert_throws({'name': 'TypeError'}, function () { new YetAnotherCustomElement; },
-        'Calling HTMLElement constructor with a bad tag name throw TypeError');
-
+        'Instantiating a custom element without calling defineElement must throw TypeError');
 }, 'HTMLElement constructor must throw TypeError when custom element is not well defined');
 
 test(function () {
     class CustomElementWithInferredTagName extends HTMLElement {};
-    document.defineCustomElement('inferred-name', CustomElementWithInferredTagName);
+    document.defineElement('inferred-name', CustomElementWithInferredTagName);
 
     var instance = new CustomElementWithInferredTagName;
     assert_true(instance instanceof Element, 'A custom element must inherit from Element');
@@ -59,32 +44,19 @@ test(function () {
 }, 'HTMLElement constructor must infer the tag name from the element interface');
 
 test(function () {
-    class ElementWithMultipleTagNames extends HTMLElement { };
-    document.defineCustomElement('custom-element-1', ElementWithMultipleTagNames);
-    document.defineCustomElement('custom-element-2', ElementWithMultipleTagNames);
-
-    var instance1 = new ElementWithMultipleTagNames('custom-element-1');
-    assert_true(instance1 instanceof ElementWithMultipleTagNames);
-    assert_equals(instance1.localName, 'custom-element-1');
-    assert_equals(instance1.nodeName, 'CUSTOM-ELEMENT-1');
-
-    var instance2 = new ElementWithMultipleTagNames('custom-element-2');
-    assert_true(instance2 instanceof ElementWithMultipleTagNames);
-    assert_equals(instance2.localName, 'custom-element-2');
-    assert_equals(instance2.nodeName, 'CUSTOM-ELEMENT-2');
-
-    assert_throws({'name': 'TypeError'}, function () { new ElementWithMultipleTagNames; },
-        'Instantiating an element interface associated with multiple tag names without specifying the tag name must throw TypeError');
-
-}, 'HTMLElement constructor must allow associating an element interface with multiple tag names');
-
-test(function () {
     class ConcreteCustomElement extends HTMLElement { };
     class SubCustomElement extends ConcreteCustomElement { };
-    document.defineCustomElement('concrete-custom-element', ConcreteCustomElement);
-    document.defineCustomElement('sub-custom-element', SubCustomElement);
+    document.defineElement('concrete-custom-element', ConcreteCustomElement);
+    document.defineElement('sub-custom-element', SubCustomElement);
+
+    var instance = new ConcreteCustomElement();
+    assert_true(instance instanceof ConcreteCustomElement);
+    assert_false(instance instanceof SubCustomElement);
+    assert_equals(instance.localName, 'concrete-custom-element');
+    assert_equals(instance.nodeName, 'CONCRETE-CUSTOM-ELEMENT');
 
     var instance = new SubCustomElement();
+    assert_true(instance instanceof ConcreteCustomElement);
     assert_true(instance instanceof SubCustomElement);
     assert_equals(instance.localName, 'sub-custom-element');
     assert_equals(instance.nodeName, 'SUB-CUSTOM-ELEMENT');
@@ -94,9 +66,10 @@ test(function () {
 test(function () {
     class AbstractCustomElement extends HTMLElement { };
     class ConcreteSubCustomElement extends AbstractCustomElement { };
-    document.defineCustomElement('concrete-sub-custom-element', ConcreteSubCustomElement);
+    document.defineElement('concrete-sub-custom-element', ConcreteSubCustomElement);
 
     var instance = new ConcreteSubCustomElement();
+    assert_true(instance instanceof AbstractCustomElement);
     assert_true(instance instanceof ConcreteSubCustomElement);
     assert_equals(instance.localName, 'concrete-sub-custom-element');
     assert_equals(instance.nodeName, 'CONCRETE-SUB-CUSTOM-ELEMENT');
index c914c2d..c3c163f 100644 (file)
@@ -13,7 +13,7 @@
 <script>
 
 class MyCustomElement extends HTMLElement { }
-document.defineCustomElement('my-custom-element', MyCustomElement);
+document.defineElement('my-custom-element', MyCustomElement);
 
 document.write('<my-custom-element></my-custom-element>');
 
index 814b19a..a00d3b9 100644 (file)
@@ -24,7 +24,7 @@ class MyCustomElement extends HTMLElement {
         containerNextSilbingInConstructor = container.nextSibling;
     }
 };
-document.defineCustomElement('my-custom-element', MyCustomElement);
+document.defineElement('my-custom-element', MyCustomElement);
 
 </script>
 <div id="custom-element-container">
index 24db95d..417f8e7 100644 (file)
@@ -25,7 +25,7 @@ test(function () {
 
 }, 'HTML parser must NOT create a custom element before defineElement is called');
 
-document.defineCustomElement('my-custom-element', MyCustomElement);
+document.defineElement('my-custom-element', MyCustomElement);
 
 </script>
 <my-custom-element id="instance2"></my-custom-element>
index ab1cbb2..cb97200 100644 (file)
@@ -18,7 +18,7 @@ class ReturnsTextNode extends HTMLElement {
         return document.createTextNode('some text');
     }
 };
-document.defineCustomElement('returns-text', ReturnsTextNode);
+document.defineElement('returns-text', ReturnsTextNode);
 
 class ReturnsNonElementObject extends HTMLElement {
     constructor() {
@@ -26,19 +26,19 @@ class ReturnsNonElementObject extends HTMLElement {
         return {};
     }
 };
-document.defineCustomElement('returns-non-element-object', ReturnsNonElementObject);
+document.defineElement('returns-non-element-object', ReturnsNonElementObject);
 
 class LacksSuperCall extends HTMLElement {
     constructor() { }
 };
-document.defineCustomElement('lacks-super-call', LacksSuperCall);
+document.defineElement('lacks-super-call', LacksSuperCall);
 
 class ThrowsException extends HTMLElement {
     constructor() {
         throw 'Bad';
     }
 };
-document.defineCustomElement('throws-exception', ThrowsException);
+document.defineElement('throws-exception', ThrowsException);
 
 </script>
 <returns-text></returns-text>
index 8249512..0f5b1ce 100644 (file)
@@ -22,7 +22,7 @@ class MyCustomElement extends HTMLElement {
         numberOfChildNodesInConstructor = this.childNodes.length;
     }
 };
-document.defineCustomElement('my-custom-element', MyCustomElement);
+document.defineElement('my-custom-element', MyCustomElement);
 
 </script>
 <my-custom-element id="custom-element-id" class="class1 class2">hello <b>world</b></my-custom-element>
index 76a1216..6fea58f 100644 (file)
@@ -25,7 +25,7 @@ class InstantiatesItselfBeforeSuper extends HTMLElement {
         elementCreatedBySuperCall = this;
     }
 };
-document.defineCustomElement('instantiates-itself-before-super', InstantiatesItselfBeforeSuper);
+document.defineElement('instantiates-itself-before-super', InstantiatesItselfBeforeSuper);
 
 let shouldCreateAnotherInstance = true;
 let anotherInstance = undefined;
@@ -42,7 +42,7 @@ class ReturnsAnotherInstance extends HTMLElement {
             return this;
     }
 };
-document.defineCustomElement('returns-another-instance', ReturnsAnotherInstance);
+document.defineElement('returns-another-instance', ReturnsAnotherInstance);
 
 </script>
 <instantiates-itself-before-super></instantiates-itself-before-super>
index fed2011..71a7d98 100644 (file)
@@ -13,7 +13,7 @@
 <script>
 
 class MyCustomElement extends HTMLElement { };
-document.defineCustomElement('my-custom-element', MyCustomElement);
+document.defineElement('my-custom-element', MyCustomElement);
 
 document.write('<template><my-custom-element></my-custom-element></template>');
 
@@ -43,7 +43,7 @@ test(function () {
 }, 'HTML parser must not use the registry of the owner element\'s document inside an iframe');
 
 class ElementInIFrame extends iframe.contentWindow.HTMLElement { };
-iframe.contentDocument.defineCustomElement('element-in-iframe', ElementInIFrame);
+iframe.contentDocument.defineElement('element-in-iframe', ElementInIFrame);
 iframe.contentDocument.body.innerHTML = '<element-in-iframe></element-in-iframe>';
 
 test(function () {
index f7e2d53..dc24ce4 100644 (file)
@@ -1,3 +1,34 @@
+2016-03-04  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Update defineCustomElement according to the spec rewrite
+        https://bugs.webkit.org/show_bug.cgi?id=155010
+        <rdar://problem/24970878>
+
+        Reviewed by Chris Dumez.
+
+        Updated the implementation of defineCustomElement and HTMLConstructor per recent rewrite of the spec:
+        https://w3c.github.io/webcomponents/spec/custom/#dom-document-defineelement
+        https://w3c.github.io/webcomponents/spec/custom/#htmlelement-constructor
+
+        defineCustomElement is now called defineElement and we disallow defining multiple custom elements with
+        a single class and throw an exception in defineElement.
+
+        Test: fast/custom-elements/Document-defineElement.html
+
+        * bindings/js/JSDocumentCustom.cpp:
+        (WebCore::JSDocument::defineElement): Renamed from defineCustomElement. Throw an exception when the interface
+        already defines another custom element. Also added FIXME's for missing steps.
+
+        * bindings/js/JSHTMLElementCustom.cpp:
+        (WebCore::constructJSHTMLElement): Removed the support for specifying a tag name in the first argument when
+        a single class defines multiple custom elements since that now results in an exception (in defineElement).
+
+        * dom/CustomElementDefinitions.cpp:
+        (WebCore::CustomElementDefinitions::containsConstructor): Added.
+        * dom/CustomElementDefinitions.h:
+        * dom/Document.idl: Renamed defineCustomElement to defineElement.
+        * html/HTMLElement.idl: Removed the optional tag name from the constructor.
+
 2016-03-04  Tim Horton  <timothy_horton@apple.com>
 
         Begin implementing <attachment> painting on iOS
index 77034a3..d634887 100644 (file)
@@ -135,7 +135,7 @@ JSValue JSDocument::createTouchList(ExecState& state)
 #endif
 
 #if ENABLE(CUSTOM_ELEMENTS)
-JSValue JSDocument::defineCustomElement(ExecState& state)
+JSValue JSDocument::defineElement(ExecState& state)
 {
     AtomicString tagName(state.argument(0).toString(&state)->toAtomicString(&state));
     if (UNLIKELY(state.hadException()))
@@ -163,16 +163,31 @@ JSValue JSDocument::defineCustomElement(ExecState& state)
         return throwSyntaxError(&state, "Custom element name cannot contain an upper case letter");
     }
 
-    QualifiedName name(nullAtom, tagName, HTMLNames::xhtmlNamespaceURI);
     auto& definitions = document.ensureCustomElementDefinitions();
     if (definitions.findInterface(tagName)) {
         throwNotSupportedError(state, "Cannot define multiple custom elements with the same tag name");
         return jsUndefined();
     }
+
+    if (definitions.containsConstructor(object)) {
+        throwNotSupportedError(state, "Cannot define multiple custom elements with the same class");
+        return jsUndefined();
+    }
+
+    // FIXME: 10. Let prototype be Get(constructor, "prototype"). Rethrow any exceptions.
+    // FIXME: 11. If Type(prototype) is not Object, throw a TypeError exception.
+    // FIXME: 12. Let attachedCallback be Get(prototype, "attachedCallback"). Rethrow any exceptions.
+    // 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.defineElement(name, JSCustomElementInterface::create(object, globalObject()));
     PrivateName uniquePrivateName;
     globalObject()->putDirect(globalObject()->vm(), uniquePrivateName, object);
 
+    // FIXME: 17. Let map be registry's upgrade candidates map.
+    // FIXME: 18. Upgrade a newly-defined element given map and definition.
+
     return jsUndefined();
 }
 #endif
index abe9f72..bd5ab13 100644 (file)
@@ -54,25 +54,8 @@ EncodedJSValue JSC_HOST_CALL constructJSHTMLElement(ExecState* state)
     JSValue newTargetValue = state->thisValue();
     JSObject* newTarget = newTargetValue.getObject();
     QualifiedName fullName = definitions->findName(newTarget);
-    if (fullName == nullQName()) {
-        if (UNLIKELY(state->argumentCount() < 1))
-            return throwVMError(state, createNotEnoughArgumentsError(state));
-    }
-
-    if (state->argumentCount()) {
-        String name;
-        if (!state->argument(0).getString(state, name))
-            return throwVMTypeError(state, "The first argument is not a valid custom element name");
-        
-        auto* interface = definitions->findInterface(name);
-        if (!interface)
-            return throwVMTypeError(state, "The first argument is not a valid custom element name");
-        
-        if (newTarget != interface->constructor())
-            return throwVMTypeError(state, "Attempt to construct a custom element with a wrong interface");
-        
-        fullName = QualifiedName(nullAtom, name, HTMLNames::xhtmlNamespaceURI);
-    }
+    if (fullName == nullQName())
+        return throwVMTypeError(state, "new.target does not define a custom element");
 
     auto* globalObject = jsConstructor->globalObject();
     Structure* baseStructure = getDOMStructure<JSHTMLElement>(vm, *globalObject);
index 9dae68f..9fe19a4 100644 (file)
@@ -94,6 +94,11 @@ JSCustomElementInterface* CustomElementDefinitions::findInterface(const AtomicSt
     return it == m_nameMap.end() ? nullptr : it->value.interface.get();
 }
 
+bool CustomElementDefinitions::containsConstructor(const JSC::JSObject* constructor) const
+{
+    return m_constructorMap.contains(constructor);
+}
+
 const QualifiedName& CustomElementDefinitions::findName(const JSC::JSObject* constructor) const
 {
     auto it = m_constructorMap.find(constructor);
index e4dc2a6..893181f 100644 (file)
@@ -52,6 +52,7 @@ public:
 
     JSCustomElementInterface* findInterface(const QualifiedName&) const;
     JSCustomElementInterface* findInterface(const AtomicString&) const;
+    bool containsConstructor(const JSC::JSObject*) const;
     const QualifiedName& findName(const JSC::JSObject*) const;
 
     enum class NameStatus { Valid, ConflictsWithBuiltinNames, NoHyphen, ContainsUpperCase };
index a669451..b0ffdc4 100644 (file)
 
 #if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT
     [Custom, RaisesException, Conditional=CUSTOM_ELEMENTS]
-    void defineCustomElement(DOMString tagName, CustomElementInterface elementInterface);
+    void defineElement(DOMString localName, Function constructor);
 #endif
 
     // Page visibility API.
index 28ebf55..62d0caa 100644 (file)
@@ -20,7 +20,7 @@
 
 [
 #if defined(ENABLE_CUSTOM_ELEMENTS) && ENABLE_CUSTOM_ELEMENTS
-    CustomConstructor(optional DOMString localName),
+    CustomConstructor(),
 #endif
     JSGenerateToNativeObject,
     JSCustomPushEventHandlerScope,