<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[205386] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/205386">205386</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2016-09-02 17:09:01 -0700 (Fri, 02 Sep 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Add validations for a synchronously constructed custom element
https://bugs.webkit.org/show_bug.cgi?id=161528

Reviewed by Yusuke Suzuki.

Source/WebCore:

The latest DOM specification has sanity checks when creating an element with the synchronous custom elements flag set
in 6.1.3 through 10:
3. If result does not implement the HTMLElement interface, throw a TypeError.
4. If result's attribute list is not empty, then throw a NotSupportedError.
5. If result has children, then throw a NotSupportedError.
6. If result's parent is not null, then throw a NotSupportedError.
7. If result's node document is not document, then throw a NotSupportedError.
8. If result's namespace is not the HTML namespace, then throw a NotSupportedError.
9. If result's local name is not equal to localName, then throw a NotSupportedError.

Add all these checks to JSCustomElementInterface::constructElement.

Tests: fast/custom-elements/Document-createElement.html

* bindings/js/JSCustomElementInterface.cpp:
(WebCore::JSCustomElementInterface::constructElement): Report the exception thrown during parsing instead of just
clearing and ignoring it.
(WebCore::constructCustomElementSynchronously): Extracted out of constructElement so that we can also catch TypeError
and NotSupportedError we throw in constructElement for the parser.

LayoutTests:

Added test cases for sanity checks in step 6.1. of https://dom.spec.whatwg.org/#concept-create-element
and updated other test cases per those changes.

* fast/custom-elements/Document-createElement-expected.txt:
* fast/custom-elements/Document-createElement.html:
* fast/custom-elements/defined-pseudo-class-expected.txt: Rebaselined now that exceptions thrown while constructing
a custom element is reported in the console.
* fast/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt: Ditto.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsfastcustomelementsDocumentcreateElementexpectedtxt">trunk/LayoutTests/fast/custom-elements/Document-createElement-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastcustomelementsDocumentcreateElementhtml">trunk/LayoutTests/fast/custom-elements/Document-createElement.html</a></li>
<li><a href="#trunkLayoutTestsfastcustomelementsdefinedpseudoclassexpectedtxt">trunk/LayoutTests/fast/custom-elements/defined-pseudo-class-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastcustomelementsparserparserfallsbacktounknownelementexpectedtxt">trunk/LayoutTests/fast/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorebindingsjsJSCustomElementInterfacecpp">trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (205385 => 205386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-09-03 00:04:27 UTC (rev 205385)
+++ trunk/LayoutTests/ChangeLog        2016-09-03 00:09:01 UTC (rev 205386)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2016-09-02  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        Add validations for a synchronously constructed custom element
+        https://bugs.webkit.org/show_bug.cgi?id=161528
+
+        Reviewed by Yusuke Suzuki.
+
+        Added test cases for sanity checks in step 6.1. of https://dom.spec.whatwg.org/#concept-create-element
+        and updated other test cases per those changes.
+
+        * fast/custom-elements/Document-createElement-expected.txt:
+        * fast/custom-elements/Document-createElement.html:
+        * fast/custom-elements/defined-pseudo-class-expected.txt: Rebaselined now that exceptions thrown while constructing
+        a custom element is reported in the console.
+        * fast/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt: Ditto.
+
</ins><span class="cx"> 2016-09-02  Ryan Haddad  &lt;ryanhaddad@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Marking two editing/mac/spelling tests as flaky on mac-wk2.
</span></span></pre></div>
<a id="trunkLayoutTestsfastcustomelementsDocumentcreateElementexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/custom-elements/Document-createElement-expected.txt (205385 => 205386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/custom-elements/Document-createElement-expected.txt        2016-09-03 00:04:27 UTC (rev 205385)
+++ trunk/LayoutTests/fast/custom-elements/Document-createElement-expected.txt        2016-09-03 00:09:01 UTC (rev 205386)
</span><span class="lines">@@ -1,7 +1,37 @@
</span><span class="cx"> 
</span><span class="cx"> PASS document.createElement must create an instance of custom elements 
</span><del>-PASS document.createElement must return null when a custom element constructor returns an object that is not an instance of Node 
-PASS document.createElement must return null when a custom element constructor returns a Text node 
-PASS document.createElement must return an element returned by a custom element constructor 
</del><ins>+PASS document.createElement must throw a TypeError when the result of Construct is not a DOM node 
+PASS document.createElement must throw a TypeError when the result of Construct is a TextNode 
+PASS document.createElement must throw a NotSupportedError when attribute is added by setAttribute during construction 
+PASS document.createElement must throw a NotSupportedError when attribute is added by attributes.setNamedItem during construction 
+PASS document.createElement must not throw a NotSupportedError when attribute is added and removed during construction 
+PASS document.createElement must throw a NotSupportedError when a Text child is added during construction 
+PASS document.createElement must throw a NotSupportedError when a Comment child is added during construction 
+PASS document.createElement must throw a NotSupportedError when an element child is added during construction 
+PASS document.createElement must not throw a NotSupportedError when an element child is added and removed during construction 
+PASS document.createElement must throw a NotSupportedError when the element gets inserted into another element during construction 
+PASS document.createElement must not throw a NotSupportedError when the element is inserted and removed from another element during construction 
+PASS document.createElement must throw a NotSupportedError when the element is adopted into a document of a template element during construction 
+PASS document.createElement must throw a NotSupportedError when the element is inserted into a document of a template element during construction 
+PASS document.createElement must not throw a NotSupportedError when the element is adopted back from a document of a template element during construction 
+PASS document.createElement must throw a NotSupportedError when the element is adopted into a new document during construction 
+PASS document.createElement must throw a NotSupportedError when the element is inserted into a new document during construction 
+PASS document.createElement must not throw a NotSupportedError when the element is adopted back from a new document during construction 
+PASS document.createElement must throw a NotSupportedError when the element is adopted into a cloned document during construction 
+PASS document.createElement must throw a NotSupportedError when the element is inserted into a cloned document during construction 
+PASS document.createElement must not throw a NotSupportedError when the element is adopted back from a cloned document during construction 
+PASS document.createElement must throw a NotSupportedError when the element is adopted into a document created by createHTMLDocument during construction 
+PASS document.createElement must throw a NotSupportedError when the element is inserted into a document created by createHTMLDocument during construction 
+PASS document.createElement must not throw a NotSupportedError when the element is adopted back from a document created by createHTMLDocument during construction 
+PASS document.createElement must throw a NotSupportedError when the element is adopted into a HTML document created by createDocument during construction 
+PASS document.createElement must throw a NotSupportedError when the element is inserted into a HTML document created by createDocument during construction 
+PASS document.createElement must not throw a NotSupportedError when the element is adopted back from a HTML document created by createDocument during construction 
+PASS document.createElement must throw a NotSupportedError when the element is adopted into a document in an iframe during construction 
+PASS document.createElement must throw a NotSupportedError when the element is inserted into a document in an iframe during construction 
+PASS document.createElement must not throw a NotSupportedError when the element is adopted back from a document in an iframe during construction 
+PASS document.createElement must throw a NotSupportedError when the element is adopted into a HTML document fetched by XHR during construction 
+PASS document.createElement must throw a NotSupportedError when the element is inserted into a HTML document fetched by XHR during construction 
+PASS document.createElement must not throw a NotSupportedError when the element is adopted back from a HTML document fetched by XHR during construction 
+PASS document.createElement must throw a NotSupportedError when the local name of the element does not match that of the custom element 
</ins><span class="cx"> PASS document.createElement must re-throw an exception thrown by a custom element constructor 
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsfastcustomelementsDocumentcreateElementhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/custom-elements/Document-createElement.html (205385 => 205386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/custom-elements/Document-createElement.html        2016-09-03 00:04:27 UTC (rev 205385)
+++ trunk/LayoutTests/fast/custom-elements/Document-createElement.html        2016-09-03 00:09:01 UTC (rev 205386)
</span><span class="lines">@@ -7,6 +7,7 @@
</span><span class="cx"> &lt;script src=&quot;../../resources/testharness.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> &lt;script src=&quot;../../resources/testharnessreport.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> &lt;link rel='stylesheet' href='../../resources/testharness.css'&gt;
</span><ins>+&lt;script src=&quot;resources/document-types.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx"> &lt;/head&gt;
</span><span class="cx"> &lt;body&gt;
</span><span class="cx"> &lt;div id=&quot;log&quot;&gt;&lt;/div&gt;
</span><span class="lines">@@ -39,8 +40,8 @@
</span><span class="cx">     assert_true(instance instanceof Object);
</span><span class="cx">     assert_equals(instance.foo, 'bar');
</span><span class="cx"> 
</span><del>-    assert_equals(document.createElement('object-custom-element'), null);
-}, 'document.createElement must return null when a custom element constructor returns an object that is not an instance of Node');
</del><ins>+    assert_throws({name: 'TypeError'}, function () { document.createElement('object-custom-element'); });
+}, 'document.createElement must throw a TypeError when the result of Construct is not a DOM node');
</ins><span class="cx"> 
</span><span class="cx"> test(function () {
</span><span class="cx">     class TextCustomElement extends HTMLElement {
</span><span class="lines">@@ -51,27 +52,196 @@
</span><span class="cx">     };
</span><span class="cx">     customElements.define('text-custom-element', TextCustomElement);
</span><span class="cx">     assert_true(new TextCustomElement instanceof Text);
</span><del>-    assert_equals(document.createElement('object-custom-element'), null);
-}, 'document.createElement must return null when a custom element constructor returns a Text node');
</del><ins>+    assert_throws({name: 'TypeError'}, function () { document.createElement('text-custom-element'); });
+}, 'document.createElement must throw a TypeError when the result of Construct is a TextNode');
</ins><span class="cx"> 
</span><span class="cx"> test(function () {
</span><del>-    var createdElement = null;
</del><ins>+    class ElementWithAttribute extends HTMLElement {
+        constructor()
+        {
+            super();
+            this.setAttribute('id', 'foo');
+        }
+    };
+    customElements.define('element-with-attribute', ElementWithAttribute);
+    assert_true(new ElementWithAttribute instanceof ElementWithAttribute);
+    assert_throws({name: 'NotSupportedError'}, function () { document.createElement('element-with-attribute'); });
+}, 'document.createElement must throw a NotSupportedError when attribute is added by setAttribute during construction');
+
+test(function () {
+    class ElementWithAttrNode extends HTMLElement {
+        constructor()
+        {
+            super();
+            this.attributes.setNamedItem(document.createAttribute('title'));
+        }
+    };
+    customElements.define('element-with-attr-node', ElementWithAttrNode);
+    assert_true(new ElementWithAttrNode instanceof ElementWithAttrNode);
+    assert_throws({name: 'NotSupportedError'}, function () { document.createElement('element-with-attr-node'); });
+}, 'document.createElement must throw a NotSupportedError when attribute is added by attributes.setNamedItem during construction');
+
+test(function () {
+    class ElementWithNoAttributes extends HTMLElement {
+        constructor()
+        {
+            super();
+            this.attributes.setNamedItem(document.createAttribute('title'));
+            this.removeAttribute('title');
+        }
+    };
+    customElements.define('element-with-no-attiributes', ElementWithNoAttributes);
+    assert_true(new ElementWithNoAttributes instanceof ElementWithNoAttributes);
+    assert_true(document.createElement('element-with-no-attiributes') instanceof ElementWithNoAttributes);
+}, 'document.createElement must not throw a NotSupportedError when attribute is added and removed during construction');
+
+test(function () {
+    class ElementWithChildText extends HTMLElement {
+        constructor()
+        {
+            super();
+            this.appendChild(document.createTextNode('hello'));
+        }
+    };
+    customElements.define('element-with-child-text', ElementWithChildText);
+    assert_true(new ElementWithChildText instanceof ElementWithChildText);
+    assert_throws({name: 'NotSupportedError'}, function () { document.createElement('element-with-child-text'); });
+}, 'document.createElement must throw a NotSupportedError when a Text child is added during construction');
+
+test(function () {
+    class ElementWithChildComment extends HTMLElement {
+        constructor()
+        {
+            super();
+            this.appendChild(document.createComment('hello'));
+        }
+    };
+    customElements.define('element-with-child-comment', ElementWithChildComment);
+    assert_true(new ElementWithChildComment instanceof ElementWithChildComment);
+    assert_throws({name: 'NotSupportedError'}, function () { document.createElement('element-with-child-comment'); });
+}, 'document.createElement must throw a NotSupportedError when a Comment child is added during construction');
+
+test(function () {
+    class ElementWithChildElement extends HTMLElement {
+        constructor()
+        {
+            super();
+            this.appendChild(document.createElement('div'));
+        }
+    };
+    customElements.define('element-with-child-element', ElementWithChildElement);
+    assert_true(new ElementWithChildElement instanceof ElementWithChildElement);
+    assert_throws({name: 'NotSupportedError'}, function () { document.createElement('element-with-child-element'); });
+}, 'document.createElement must throw a NotSupportedError when an element child is added during construction');
+
+test(function () {
+    class ElementWithNoChildElements extends HTMLElement {
+        constructor()
+        {
+            super();
+            this.appendChild(document.createElement('div'));
+            this.removeChild(this.firstChild);
+        }
+    };
+    customElements.define('element-with-no-child-elements', ElementWithNoChildElements);
+    assert_true(document.createElement('element-with-no-child-elements') instanceof ElementWithNoChildElements);
+}, 'document.createElement must not throw a NotSupportedError when an element child is added and removed during construction');
+
+test(function () {
+    class ElementWithParent extends HTMLElement {
+        constructor()
+        {
+            super();
+            document.createElement('div').appendChild(this);
+        }
+    };
+    customElements.define('element-with-parent', ElementWithParent);
+    assert_true(new ElementWithParent instanceof ElementWithParent);
+    assert_throws({name: 'NotSupportedError'}, function () { document.createElement('element-with-parent'); });
+}, 'document.createElement must throw a NotSupportedError when the element gets inserted into another element during construction');
+
+test(function () {
+    class ElementWithNoParent extends HTMLElement {
+        constructor()
+        {
+            super();
+            document.createElement('div').appendChild(this);
+            this.parentNode.removeChild(this);
+        }
+    };
+    customElements.define('element-with-no-parent', ElementWithNoParent);
+    assert_true(document.createElement('element-with-no-parent') instanceof ElementWithNoParent);
+}, 'document.createElement must not throw a NotSupportedError when the element is inserted and removed from another element during construction');
+
+DocumentTypes.forEach(function (entry, testNumber) {
+    if (entry.isOwner)
+        return;
+
+    var getDocument = entry.create;
+    var docuemntName = entry.name;
+
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            class ElementWithAdoptCall extends HTMLElement {
+                constructor()
+                {
+                    super();
+                    doc.adoptNode(this);
+                }
+            };
+            var name = 'element-with-adopt-call-' + testNumber;
+            customElements.define(name, ElementWithAdoptCall);
+            assert_true(new ElementWithAdoptCall instanceof ElementWithAdoptCall);
+            assert_throws({name: 'NotSupportedError'}, function () { document.createElement(name); });
+        });
+    }, 'document.createElement must throw a NotSupportedError when the element is adopted into a ' + docuemntName + ' during construction');
+
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            class ElementInsertedIntoAnotherDocument extends HTMLElement {
+                constructor()
+                {
+                    super();
+                    doc.documentElement.appendChild(this);
+                }
+            };
+            var name = 'element-inserted-into-another-document-' + testNumber;
+            customElements.define(name, ElementInsertedIntoAnotherDocument);
+            assert_true(new ElementInsertedIntoAnotherDocument instanceof ElementInsertedIntoAnotherDocument);
+            assert_throws({name: 'NotSupportedError'}, function () { document.createElement(name); });
+        });
+    }, 'document.createElement must throw a NotSupportedError when the element is inserted into a ' + docuemntName + ' during construction');
+
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            class ElementThatGetAdoptedBack extends HTMLElement {
+                constructor()
+                {
+                    super();
+                    doc.adoptNode(this);
+                    document.adoptNode(this);
+                }
+            };
+            var name = 'element-that-get-adopted-back' + testNumber;
+            customElements.define(name, ElementThatGetAdoptedBack);
+            assert_true(document.createElement(name) instanceof ElementThatGetAdoptedBack);
+        });
+    }, 'document.createElement must not throw a NotSupportedError when the element is adopted back from a ' + docuemntName + ' during construction');
+});
+
+test(function () {
</ins><span class="cx">     class DivCustomElement extends HTMLElement {
</span><span class="cx">         constructor()
</span><span class="cx">         {
</span><span class="cx">             super();
</span><del>-            createdElement = document.createElement('div');
-            return createdElement;
</del><ins>+            return document.createElement('div');
</ins><span class="cx">         }
</span><span class="cx">     };
</span><span class="cx">     customElements.define('div-custom-element', DivCustomElement);
</span><span class="cx">     assert_true(new DivCustomElement instanceof HTMLDivElement);
</span><ins>+    assert_throws({name: 'NotSupportedError'}, function () { document.createElement('div-custom-element'); });
+}, 'document.createElement must throw a NotSupportedError when the local name of the element does not match that of the custom element');
</ins><span class="cx"> 
</span><del>-    var instance = document.createElement('div-custom-element');
-    assert_true(instance instanceof HTMLDivElement);
-    assert_equals(instance, createdElement);
-}, 'document.createElement must return an element returned by a custom element constructor');
-
</del><span class="cx"> test(function () {
</span><span class="cx">     var exceptionToThrow = {message: 'exception thrown by a custom constructor'};
</span><span class="cx">     class ThrowCustomElement extends HTMLElement {
</span></span></pre></div>
<a id="trunkLayoutTestsfastcustomelementsdefinedpseudoclassexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/custom-elements/defined-pseudo-class-expected.txt (205385 => 205386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/custom-elements/defined-pseudo-class-expected.txt        2016-09-03 00:04:27 UTC (rev 205385)
+++ trunk/LayoutTests/fast/custom-elements/defined-pseudo-class-expected.txt        2016-09-03 00:09:01 UTC (rev 205386)
</span><span class="lines">@@ -1,4 +1,7 @@
</span><ins>+CONSOLE MESSAGE: line 75: TypeError: The result of constructing a custom element must be a HTMLElement
</ins><span class="cx"> 
</span><ins>+Harness Error (FAIL), message = TypeError: The result of constructing a custom element must be a HTMLElement
+
</ins><span class="cx"> PASS The defined flag of a custom element must not be set if a custom element has not been upgraded yet 
</span><span class="cx"> PASS The defined flag of a custom element must not be set if a custom element has not been upgraded yet even if the element has been defined 
</span><span class="cx"> PASS The defined flag of a custom element must be set when a custom element is successfully upgraded 
</span></span></pre></div>
<a id="trunkLayoutTestsfastcustomelementsparserparserfallsbacktounknownelementexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt (205385 => 205386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt        2016-09-03 00:04:27 UTC (rev 205385)
+++ trunk/LayoutTests/fast/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt        2016-09-03 00:09:01 UTC (rev 205386)
</span><span class="lines">@@ -1,4 +1,10 @@
</span><ins>+CONSOLE MESSAGE: TypeError: The result of constructing a custom element must be a HTMLElement
+CONSOLE MESSAGE: TypeError: The result of constructing a custom element must be a HTMLElement
+CONSOLE MESSAGE: line 32: ReferenceError: Cannot access uninitialized variable.
+CONSOLE MESSAGE: line 38: Bad
</ins><span class="cx"> 
</span><ins>+Harness Error (FAIL), message = Bad
+
</ins><span class="cx"> PASS HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns a Text node 
</span><span class="cx"> PASS HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns non-Element object 
</span><span class="cx"> PASS HTML parser must create a fallback HTMLUnknownElement when a custom element constructor does not call super() 
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (205385 => 205386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-09-03 00:04:27 UTC (rev 205385)
+++ trunk/Source/WebCore/ChangeLog        2016-09-03 00:09:01 UTC (rev 205386)
</span><span class="lines">@@ -1,3 +1,30 @@
</span><ins>+2016-09-02  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        Add validations for a synchronously constructed custom element
+        https://bugs.webkit.org/show_bug.cgi?id=161528
+
+        Reviewed by Yusuke Suzuki.
+
+        The latest DOM specification has sanity checks when creating an element with the synchronous custom elements flag set
+        in 6.1.3 through 10:
+        3. If result does not implement the HTMLElement interface, throw a TypeError.
+        4. If result's attribute list is not empty, then throw a NotSupportedError.
+        5. If result has children, then throw a NotSupportedError.
+        6. If result's parent is not null, then throw a NotSupportedError.
+        7. If result's node document is not document, then throw a NotSupportedError.
+        8. If result's namespace is not the HTML namespace, then throw a NotSupportedError.
+        9. If result's local name is not equal to localName, then throw a NotSupportedError.
+
+        Add all these checks to JSCustomElementInterface::constructElement.
+
+        Tests: fast/custom-elements/Document-createElement.html
+
+        * bindings/js/JSCustomElementInterface.cpp:
+        (WebCore::JSCustomElementInterface::constructElement): Report the exception thrown during parsing instead of just
+        clearing and ignoring it.
+        (WebCore::constructCustomElementSynchronously): Extracted out of constructElement so that we can also catch TypeError
+        and NotSupportedError we throw in constructElement for the parser.
+
</ins><span class="cx"> 2016-09-02  Zalan Bujtas  &lt;zalan@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         ASSERT_NOT_REACHED() is touched in WebCore::valueForLength
</span></span></pre></div>
<a id="trunkSourceWebCorebindingsjsJSCustomElementInterfacecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp (205385 => 205386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp        2016-09-03 00:04:27 UTC (rev 205385)
+++ trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp        2016-09-03 00:09:01 UTC (rev 205386)
</span><span class="lines">@@ -31,6 +31,7 @@
</span><span class="cx"> #if ENABLE(CUSTOM_ELEMENTS)
</span><span class="cx"> 
</span><span class="cx"> #include &quot;DOMWrapperWorld.h&quot;
</span><ins>+#include &quot;JSDOMBinding.h&quot;
</ins><span class="cx"> #include &quot;JSDOMGlobalObject.h&quot;
</span><span class="cx"> #include &quot;JSElement.h&quot;
</span><span class="cx"> #include &quot;JSHTMLElement.h&quot;
</span><span class="lines">@@ -56,7 +57,9 @@
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-RefPtr&lt;Element&gt; JSCustomElementInterface::constructElement(const AtomicString&amp; tagName, ShouldClearException shouldClearException)
</del><ins>+static RefPtr&lt;Element&gt; constructCustomElementSynchronously(Document&amp;, VM&amp;, ExecState&amp;, JSObject* constructor, const AtomicString&amp; localName);
+
+RefPtr&lt;Element&gt; JSCustomElementInterface::constructElement(const AtomicString&amp; localName, ShouldClearException shouldClearException)
</ins><span class="cx"> {
</span><span class="cx">     if (!canInvokeCallback())
</span><span class="cx">         return nullptr;
</span><span class="lines">@@ -63,7 +66,8 @@
</span><span class="cx"> 
</span><span class="cx">     Ref&lt;JSCustomElementInterface&gt; protectedThis(*this);
</span><span class="cx"> 
</span><del>-    JSLockHolder lock(m_isolatedWorld-&gt;vm());
</del><ins>+    VM&amp; vm = m_isolatedWorld-&gt;vm();
+    JSLockHolder lock(vm);
</ins><span class="cx"> 
</span><span class="cx">     if (!m_constructor)
</span><span class="cx">         return nullptr;
</span><span class="lines">@@ -72,11 +76,30 @@
</span><span class="cx">     if (!context)
</span><span class="cx">         return nullptr;
</span><span class="cx">     ASSERT(context-&gt;isDocument());
</span><del>-    JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(context, *m_isolatedWorld);
-    ExecState* state = globalObject-&gt;globalExec();
</del><span class="cx"> 
</span><ins>+    auto&amp; state = *context-&gt;execState();
+    RefPtr&lt;Element&gt; element = constructCustomElementSynchronously(downcast&lt;Document&gt;(*context), vm, state, m_constructor.get(), localName);
+    if (!element) {
+        auto* exception = vm.exception();
+        ASSERT(exception);
+        if (shouldClearException == ShouldClearException::Clear) {
+            state.clearException();
+            reportException(&amp;state, exception);
+        }
+        return nullptr;
+    }
+
+    element-&gt;setCustomElementIsResolved(*this);
+    return element;
+}
+
+// https://dom.spec.whatwg.org/#concept-create-element
+// 6. 1. If the synchronous custom elements flag is set
+static RefPtr&lt;Element&gt; constructCustomElementSynchronously(Document&amp; document, VM&amp; vm, ExecState&amp; state, JSObject* constructor, const AtomicString&amp; localName)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
</ins><span class="cx">     ConstructData constructData;
</span><del>-    ConstructType constructType = m_constructor-&gt;methodTable()-&gt;getConstructData(m_constructor.get(), constructData);
</del><ins>+    ConstructType constructType = constructor-&gt;methodTable()-&gt;getConstructData(constructor, constructData);
</ins><span class="cx">     if (constructType == ConstructType::None) {
</span><span class="cx">         ASSERT_NOT_REACHED();
</span><span class="cx">         return nullptr;
</span><span class="lines">@@ -83,22 +106,43 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     MarkedArgumentBuffer args;
</span><del>-    args.append(jsStringWithCache(state, tagName));
</del><ins>+    args.append(jsStringWithCache(&amp;state, localName));
</ins><span class="cx"> 
</span><del>-    InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionConstruct(context, constructType, constructData);
-    JSValue newElement = construct(state, m_constructor.get(), constructType, constructData, args);
-    InspectorInstrumentation::didCallFunction(cookie, context);
</del><ins>+    InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionConstruct(&amp;document, constructType, constructData);
+    JSValue newElement = construct(&amp;state, constructor, constructType, constructData, args);
+    InspectorInstrumentation::didCallFunction(cookie, &amp;document);
+    if (vm.exception())
+        return nullptr;
</ins><span class="cx"> 
</span><del>-    if (shouldClearException == ShouldClearException::Clear &amp;&amp; state-&gt;hadException())
-        state-&gt;clearException();
</del><ins>+    ASSERT(!newElement.isEmpty());
+    HTMLElement* wrappedElement = JSHTMLElement::toWrapped(newElement);
+    if (!wrappedElement) {
+        throwTypeError(&amp;state, scope, ASCIILiteral(&quot;The result of constructing a custom element must be a HTMLElement&quot;));
+        return nullptr;
+    }
</ins><span class="cx"> 
</span><del>-    if (newElement.isEmpty())
</del><ins>+    if (wrappedElement-&gt;hasAttributes()) {
+        throwNotSupportedError(state, scope, ASCIILiteral(&quot;A newly constructed custom element must not have attributes&quot;));
</ins><span class="cx">         return nullptr;
</span><ins>+    }
+    if (wrappedElement-&gt;hasChildNodes()) {
+        throwNotSupportedError(state, scope, ASCIILiteral(&quot;A newly constructed custom element must not have child nodes&quot;));
+        return nullptr;
+    }
+    if (wrappedElement-&gt;parentNode()) {
+        throwNotSupportedError(state, scope, ASCIILiteral(&quot;A newly constructed custom element must not have a parent node&quot;));
+        return nullptr;
+    }
+    if (&amp;wrappedElement-&gt;document() != &amp;document) {
+        throwNotSupportedError(state, scope, ASCIILiteral(&quot;A newly constructed custom element belongs to a wrong docuemnt&quot;));
+        return nullptr;
+    }
+    ASSERT(wrappedElement-&gt;namespaceURI() == HTMLNames::xhtmlNamespaceURI);
+    if (wrappedElement-&gt;localName() != localName) {
+        throwNotSupportedError(state, scope, ASCIILiteral(&quot;A newly constructed custom element belongs to a wrong docuemnt&quot;));
+        return nullptr;
+    }
</ins><span class="cx"> 
</span><del>-    Element* wrappedElement = JSElement::toWrapped(newElement);
-    if (!wrappedElement)
-        return nullptr;
-    wrappedElement-&gt;setCustomElementIsResolved(*this);
</del><span class="cx">     return wrappedElement;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>