<!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 <rniwa@webkit.org>
+
+ 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 <ryanhaddad@apple.com>
</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"> <script src="../../resources/testharness.js"></script>
</span><span class="cx"> <script src="../../resources/testharnessreport.js"></script>
</span><span class="cx"> <link rel='stylesheet' href='../../resources/testharness.css'>
</span><ins>+<script src="resources/document-types.js"></script>
</ins><span class="cx"> </head>
</span><span class="cx"> <body>
</span><span class="cx"> <div id="log"></div>
</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 <rniwa@webkit.org>
+
+ 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 <zalan@apple.com>
</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 "DOMWrapperWorld.h"
</span><ins>+#include "JSDOMBinding.h"
</ins><span class="cx"> #include "JSDOMGlobalObject.h"
</span><span class="cx"> #include "JSElement.h"
</span><span class="cx"> #include "JSHTMLElement.h"
</span><span class="lines">@@ -56,7 +57,9 @@
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx">
</span><del>-RefPtr<Element> JSCustomElementInterface::constructElement(const AtomicString& tagName, ShouldClearException shouldClearException)
</del><ins>+static RefPtr<Element> constructCustomElementSynchronously(Document&, VM&, ExecState&, JSObject* constructor, const AtomicString& localName);
+
+RefPtr<Element> JSCustomElementInterface::constructElement(const AtomicString& 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<JSCustomElementInterface> protectedThis(*this);
</span><span class="cx">
</span><del>- JSLockHolder lock(m_isolatedWorld->vm());
</del><ins>+ VM& vm = m_isolatedWorld->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->isDocument());
</span><del>- JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(context, *m_isolatedWorld);
- ExecState* state = globalObject->globalExec();
</del><span class="cx">
</span><ins>+ auto& state = *context->execState();
+ RefPtr<Element> element = constructCustomElementSynchronously(downcast<Document>(*context), vm, state, m_constructor.get(), localName);
+ if (!element) {
+ auto* exception = vm.exception();
+ ASSERT(exception);
+ if (shouldClearException == ShouldClearException::Clear) {
+ state.clearException();
+ reportException(&state, exception);
+ }
+ return nullptr;
+ }
+
+ element->setCustomElementIsResolved(*this);
+ return element;
+}
+
+// https://dom.spec.whatwg.org/#concept-create-element
+// 6. 1. If the synchronous custom elements flag is set
+static RefPtr<Element> constructCustomElementSynchronously(Document& document, VM& vm, ExecState& state, JSObject* constructor, const AtomicString& localName)
+{
+ auto scope = DECLARE_THROW_SCOPE(vm);
</ins><span class="cx"> ConstructData constructData;
</span><del>- ConstructType constructType = m_constructor->methodTable()->getConstructData(m_constructor.get(), constructData);
</del><ins>+ ConstructType constructType = constructor->methodTable()->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(&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(&document, constructType, constructData);
+ JSValue newElement = construct(&state, constructor, constructType, constructData, args);
+ InspectorInstrumentation::didCallFunction(cookie, &document);
+ if (vm.exception())
+ return nullptr;
</ins><span class="cx">
</span><del>- if (shouldClearException == ShouldClearException::Clear && state->hadException())
- state->clearException();
</del><ins>+ ASSERT(!newElement.isEmpty());
+ HTMLElement* wrappedElement = JSHTMLElement::toWrapped(newElement);
+ if (!wrappedElement) {
+ throwTypeError(&state, scope, ASCIILiteral("The result of constructing a custom element must be a HTMLElement"));
+ return nullptr;
+ }
</ins><span class="cx">
</span><del>- if (newElement.isEmpty())
</del><ins>+ if (wrappedElement->hasAttributes()) {
+ throwNotSupportedError(state, scope, ASCIILiteral("A newly constructed custom element must not have attributes"));
</ins><span class="cx"> return nullptr;
</span><ins>+ }
+ if (wrappedElement->hasChildNodes()) {
+ throwNotSupportedError(state, scope, ASCIILiteral("A newly constructed custom element must not have child nodes"));
+ return nullptr;
+ }
+ if (wrappedElement->parentNode()) {
+ throwNotSupportedError(state, scope, ASCIILiteral("A newly constructed custom element must not have a parent node"));
+ return nullptr;
+ }
+ if (&wrappedElement->document() != &document) {
+ throwNotSupportedError(state, scope, ASCIILiteral("A newly constructed custom element belongs to a wrong docuemnt"));
+ return nullptr;
+ }
+ ASSERT(wrappedElement->namespaceURI() == HTMLNames::xhtmlNamespaceURI);
+ if (wrappedElement->localName() != localName) {
+ throwNotSupportedError(state, scope, ASCIILiteral("A newly constructed custom element belongs to a wrong docuemnt"));
+ return nullptr;
+ }
</ins><span class="cx">
</span><del>- Element* wrappedElement = JSElement::toWrapped(newElement);
- if (!wrappedElement)
- return nullptr;
- wrappedElement->setCustomElementIsResolved(*this);
</del><span class="cx"> return wrappedElement;
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre>
</div>
</div>
</body>
</html>