Title: [197463] trunk
Revision
197463
Author
[email protected]
Date
2016-03-02 13:56:47 -0800 (Wed, 02 Mar 2016)

Log Message

Make HTML parser construct custom elements
https://bugs.webkit.org/show_bug.cgi?id=154908
Source/WebCore:

<rdar://problem/24923735>

Reviewed by Antti Koivisto.

Added the support for instantiating custom elements inside the parser. Based on Jan F2F discussion,
the HTML parser is going to synchronously construct custom elements. When a custom element constructor
throws, the HTML parser creates a HTMLUnknownElement instead.

In our implementation, we pause the parser completely and construct custom elements using the same
mechanism used to run author scripts. It's possible that we may want to apply some optimizations to
to make custom element construction but it's probably a good idea to get semantics right first.

Tests: 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

* bindings/js/JSCustomElementInterface.cpp:
(WebCore::JSCustomElementInterface::constructElement): Added ShouldClearException as an argument
to be used by the HTML parser since the parser can't re-throw to anywhere or fail parsing.

* bindings/js/JSCustomElementInterface.h:
(WebCore::JSCustomElementInterface::ShouldClearException): Added.

* dom/Document.cpp:
(WebCore::createHTMLElementWithNameValidation): Do not clear the exception here since createElement
must re-throw the exception thrown by a custom element constructor.
(WebCore::Document::createElementForBindings):

* dom/make_names.pl:
(printFactoryCppFile): Added ConstructorFunctionMapEntry which contains the constructor function
as well as the qualified name.
(printFactoryHeaderFile): Added a variant of createKnownElement and createElement that takes
AtomicString instead of QualifiedName.

* html/parser/HTMLConstructionSite.cpp:
(WebCore::setAttributes): Added a variant that takes Vector<Attribute>.
(WebCore::HTMLConstructionSite::insertHTMLElementOrFindCustomElementInterface): Added. Returns a
custom element interface when the element doesn't match any builtin element and there is a custom
element definition that matches the specified name.
(WebCore::HTMLConstructionSite::insertCustomElement): Added. Like insertElement but also sets the
attributes on the newly created custom element.
(WebCore::HTMLConstructionSite::createHTMLElementOrFindCustomElementInterface): Extracted from
createHTMLElement. When customElementInterface is not nullptr, we optionally find the custom
element interface and return nullptr.
(WebCore::HTMLConstructionSite::createHTMLElement):
* html/parser/HTMLConstructionSite.h:

* html/parser/HTMLDocumentParser.cpp:
(WebCore::HTMLDocumentParser::runScriptsForPausedTreeBuilder): Create a custom element when there
is a pending custom element to create (i.e. m_customElementToConstruct is not empty).
(WebCore::HTMLDocumentParser::isWaitingForScripts):

* html/parser/HTMLStackItem.h:
(WebCore::HTMLStackItem::create): Added a variant used for custom elements.
(WebCore::HTMLStackItem::HTMLStackItem): Ditto.

* html/parser/HTMLTreeBuilder.cpp:
(WebCore::CustomElementConstructionData::CustomElementConstructionData): Added. It needs to be in
the cpp file to avoid introducing more header dependencies in HTMLTreeBuilder.h.
(WebCore::CustomElementConstructionData::~CustomElementConstructionData): Ditto.
(WebCore::HTMLTreeBuilder::processStartTagForInBody): Use insertGenericHTMLElement when creating
a generic element that could be custom elements.
(WebCore::HTMLTreeBuilder::insertGenericHTMLElement): Added. Create and insert a new element
or set m_customElementToConstruct so that the HTMLDocumentParser will create a custom element later.
(WebCore::HTMLTreeBuilder::didCreateCustomOrCallbackElement): Added. Called by HTMLDocumentParser
when it finishes creating a new custom element.

* html/parser/HTMLTreeBuilder.h:
(WebCore::HTMLTreeBuilder::takeCustomElementConstructionData): Added.
(WebCore::HTMLTreeBuilder::hasParserBlockingScriptWork): Renamed from hasParserBlockingScript.
Checks the existence of m_customElementToConstruct as well as m_scriptToProcess.

LayoutTests:


Reviewed by Antti Koivisto.

Added W3C testharness.js based tests for instantiating custom elements inside the HTML parser.

* fast/custom-elements/parser: Added.
* fast/custom-elements/parser/parser-constructs-custom-elements-expected.txt: Added.
* fast/custom-elements/parser/parser-constructs-custom-elements.html: Added.
* fast/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt: Added.
* fast/custom-elements/parser/parser-fallsback-to-unknown-element.html: Added.
* fast/custom-elements/parser/parser-sets-attributes-and-children-expected.txt: Added.
* fast/custom-elements/parser/parser-sets-attributes-and-children.html: Added.
* fast/custom-elements/parser/parser-uses-constructed-element-expected.txt: Added.
* fast/custom-elements/parser/parser-uses-constructed-element.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (197462 => 197463)


--- trunk/LayoutTests/ChangeLog	2016-03-02 21:42:22 UTC (rev 197462)
+++ trunk/LayoutTests/ChangeLog	2016-03-02 21:56:47 UTC (rev 197463)
@@ -1,3 +1,22 @@
+2016-03-01  Ryosuke Niwa  <[email protected]>
+
+        Make HTML parser construct custom elements
+        https://bugs.webkit.org/show_bug.cgi?id=154908
+
+        Reviewed by Antti Koivisto.
+
+        Added W3C testharness.js based tests for instantiating custom elements inside the HTML parser.
+
+        * fast/custom-elements/parser: Added.
+        * fast/custom-elements/parser/parser-constructs-custom-elements-expected.txt: Added.
+        * fast/custom-elements/parser/parser-constructs-custom-elements.html: Added.
+        * fast/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt: Added.
+        * fast/custom-elements/parser/parser-fallsback-to-unknown-element.html: Added.
+        * fast/custom-elements/parser/parser-sets-attributes-and-children-expected.txt: Added.
+        * fast/custom-elements/parser/parser-sets-attributes-and-children.html: Added.
+        * fast/custom-elements/parser/parser-uses-constructed-element-expected.txt: Added.
+        * fast/custom-elements/parser/parser-uses-constructed-element.html: Added.
+
 2016-03-02  Chris Dumez  <[email protected]>
 
         Align HTMLInputElement.maxLength with the specification

Added: trunk/LayoutTests/fast/custom-elements/parser/parser-constructs-custom-elements-expected.txt (0 => 197463)


--- trunk/LayoutTests/fast/custom-elements/parser/parser-constructs-custom-elements-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/parser/parser-constructs-custom-elements-expected.txt	2016-03-02 21:56:47 UTC (rev 197463)
@@ -0,0 +1,4 @@
+
+PASS HTML parser must NOT create a custom element before defineElement is called 
+PASS HTML parser must create a defined custom element before executing inline scripts 
+

Added: trunk/LayoutTests/fast/custom-elements/parser/parser-constructs-custom-elements.html (0 => 197463)


--- trunk/LayoutTests/fast/custom-elements/parser/parser-constructs-custom-elements.html	                        (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/parser/parser-constructs-custom-elements.html	2016-03-02 21:56:47 UTC (rev 197463)
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: Changes to the HTML parser</title>
+<meta name="author" title="Ryosuke Niwa" href=""
+<meta name="assert" content="HTML parser creates a custom element">
+<script src=""
+<script src=""
+<link rel='stylesheet' href=''>
+</head>
+<body>
+<div id="log"></div>
+<my-custom-element id="instance1"></my-custom-element>
+<script>
+
+class MyCustomElement extends HTMLElement { };
+
+test(function () {
+    var customElement = document.getElementById('instance1');
+
+    assert_true(customElement instanceof HTMLElement, 'An unresolved custom element must be an instance of HTMLElement');
+    assert_false(customElement instanceof MyCustomElement, 'An unresolved custom element must NOT be an instance of that custom element');
+    assert_equals(customElement.localName, 'my-custom-element');
+    assert_equals(customElement.namespaceURI, 'http://www.w3.org/1999/xhtml', 'A custom element HTML must use HTML namespace');
+
+}, 'HTML parser must NOT create a custom element before defineElement is called');
+
+document.defineCustomElement('my-custom-element', MyCustomElement);
+
+</script>
+<my-custom-element id="instance2"></my-custom-element>
+<script>
+
+test(function () {
+    var customElement = document.getElementById('instance2');
+
+    assert_true(customElement instanceof HTMLElement, 'A resolved custom element must be an instance of HTMLElement');
+    assert_false(customElement instanceof HTMLUnknownElement, 'A resolved custom element must NOT be an instance of HTMLUnknownElement');
+    assert_true(customElement instanceof MyCustomElement, 'A resolved custom element must be an instance of that custom element');
+    assert_equals(customElement.localName, 'my-custom-element');
+    assert_equals(customElement.namespaceURI, 'http://www.w3.org/1999/xhtml', 'A custom element HTML must use HTML namespace');
+
+}, 'HTML parser must create a defined custom element before executing inline scripts');
+
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt (0 => 197463)


--- trunk/LayoutTests/fast/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt	2016-03-02 21:56:47 UTC (rev 197463)
@@ -0,0 +1,6 @@
+
+PASS HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns a Text node 
+PASS HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns non-Element object 
+PASS HTML parser must create a fallback HTMLUnknownElement when a custom element constructor does not call super() 
+PASS HTML parser must create a fallback HTMLUnknownElement when a custom element constructor throws an exception 
+

Added: trunk/LayoutTests/fast/custom-elements/parser/parser-fallsback-to-unknown-element.html (0 => 197463)


--- trunk/LayoutTests/fast/custom-elements/parser/parser-fallsback-to-unknown-element.html	                        (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/parser/parser-fallsback-to-unknown-element.html	2016-03-02 21:56:47 UTC (rev 197463)
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: Changes to the HTML parser</title>
+<meta name="author" title="Ryosuke Niwa" href=""
+<meta name="assert" content="HTML parser must fallback to creating a HTMLUnknownElement when a custom element construction fails">
+<script src=""
+<script src=""
+<link rel='stylesheet' href=''>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+class ReturnsTextNode extends HTMLElement {
+    constructor() {
+        super();
+        return document.createTextNode('some text');
+    }
+};
+document.defineCustomElement('returns-text', ReturnsTextNode);
+
+class ReturnsNonElementObject extends HTMLElement {
+    constructor() {
+        super();
+        return {};
+    }
+};
+document.defineCustomElement('returns-non-element-object', ReturnsNonElementObject);
+
+class LacksSuperCall extends HTMLElement {
+    constructor() { }
+};
+document.defineCustomElement('lacks-super-call', LacksSuperCall);
+
+class ThrowsException extends HTMLElement {
+    constructor() {
+        throw 'Bad';
+    }
+};
+document.defineCustomElement('throws-exception', ThrowsException);
+
+</script>
+<returns-text></returns-text>
+<returns-non-element-object></returns-non-element-object>
+<lacks-super-call></lacks-super-call>
+<throws-exception></throws-exception>
+<script>
+
+test(function () {
+    var instance = document.querySelector('returns-text');
+
+    assert_false(instance instanceof ReturnsTextNode, 'HTML parser must NOT instantiate a custom element when the constructor returns a Text node');
+    assert_true(instance instanceof HTMLElement, 'The fallback element created by HTML parser must be an instance of HTMLElement');
+    assert_true(instance instanceof HTMLUnknownElement, 'The fallback element created by HTML parser must be an instance of HTMLUnknownElement');
+
+}, 'HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns a Text node');
+
+test(function () {
+    var instance = document.querySelector('returns-non-element-object');
+
+    assert_false(instance instanceof ReturnsNonElementObject, 'HTML parser must NOT instantiate a custom element when the constructor returns a non-Element object');
+    assert_true(instance instanceof HTMLElement, 'The fallback element created by HTML parser must be an instance of HTMLElement');
+    assert_true(instance instanceof HTMLUnknownElement, 'The fallback element created by HTML parser must be an instance of HTMLUnknownElement');
+
+}, 'HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns non-Element object');
+
+test(function () {
+    var instance = document.querySelector('lacks-super-call');
+
+    assert_false(instance instanceof LacksSuperCall, 'HTML parser must NOT instantiate a custom element when the constructor does not call super()');
+    assert_true(instance instanceof HTMLElement, 'The fallback element created by HTML parser must be an instance of HTMLElement');
+    assert_true(instance instanceof HTMLUnknownElement, 'The fallback element created by HTML parser must be an instance of HTMLUnknownElement');
+
+}, 'HTML parser must create a fallback HTMLUnknownElement when a custom element constructor does not call super()');
+
+test(function () {
+    var instance = document.querySelector('throws-exception');
+
+    assert_false(instance instanceof ThrowsException, 'HTML parser must NOT instantiate a custom element when the constructor throws an exception');
+    assert_true(instance instanceof HTMLElement, 'The fallback element created by HTML parser must be an instance of HTMLElement');
+    assert_true(instance instanceof HTMLUnknownElement, 'The fallback element created by HTML parser must be an instance of HTMLUnknownElement');
+
+}, 'HTML parser must create a fallback HTMLUnknownElement when a custom element constructor throws an exception');
+
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/custom-elements/parser/parser-sets-attributes-and-children-expected.txt (0 => 197463)


--- trunk/LayoutTests/fast/custom-elements/parser/parser-sets-attributes-and-children-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/parser/parser-sets-attributes-and-children-expected.txt	2016-03-02 21:56:47 UTC (rev 197463)
@@ -0,0 +1,6 @@
+hello world
+
+PASS HTML parser must set the attributes 
+PASS HTML parser must append child nodes 
+PASS HTML parser must set the attributes or append children before calling constructor 
+

Added: trunk/LayoutTests/fast/custom-elements/parser/parser-sets-attributes-and-children.html (0 => 197463)


--- trunk/LayoutTests/fast/custom-elements/parser/parser-sets-attributes-and-children.html	                        (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/parser/parser-sets-attributes-and-children.html	2016-03-02 21:56:47 UTC (rev 197463)
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: Changes to the HTML parser</title>
+<meta name="author" title="Ryosuke Niwa" href=""
+<meta name="assert" content="HTML parser must set the attributes and append the children on a custom element">
+<script src=""
+<script src=""
+<link rel='stylesheet' href=''>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+var numberOfAttributesInConstructor;
+var numberOfChildNodesInConstructor;
+
+class MyCustomElement extends HTMLElement {
+    constructor(...args) {
+        super(...args);
+        numberOfAttributesInConstructor = this.attributes.length;
+        numberOfChildNodesInConstructor = this.childNodes.length;
+    }
+};
+document.defineCustomElement('my-custom-element', MyCustomElement);
+
+</script>
+<my-custom-element id="custom-element-id" class="class1 class2">hello <b>world</b></my-custom-element>
+<script>
+
+var customElement = document.querySelector('my-custom-element');
+
+test(function () {
+    assert_equals(customElement.getAttribute('id'), 'custom-element-id', 'HTML parser must preserve the id attribute');
+    assert_equals(customElement.id, 'custom-element-id', 'HTML parser must preserve the semantics of reflect for the id attribute');
+    assert_equals(customElement.getAttribute('class'), 'class1 class2', 'HTML parser must preserve the class attribute');
+    assert_equals(customElement.classList.length, 2, 'HTML parser must initialize classList on custom elements');
+    assert_equals(customElement.classList[0], 'class1', 'HTML parser must initialize classList on custom elements');
+    assert_equals(customElement.classList[1], 'class2', 'HTML parser must initialize classList on custom elements');
+
+    assert_equals(customElement.childNodes.length, 2, 'HTML parser must append child nodes');
+    assert_equals(customElement.classList[0], 'class1', 'HTML parser must initialize classList on custom elements');
+    assert_equals(customElement.classList[1], 'class2', 'HTML parser must initialize classList on custom elements');
+
+}, 'HTML parser must set the attributes');
+
+test(function () {
+    assert_equals(customElement.childNodes.length, 2, 'HTML parser must append child nodes');
+    assert_true(customElement.firstChild instanceof Text, 'HTML parser must append Text node child to a custom element');
+    assert_equals(customElement.firstChild.data, 'hello ', 'HTML parser must append Text node child to a custom element');
+    assert_true(customElement.lastChild instanceof HTMLElement, 'HTML parser must append a builtin element child to a custom element');
+    assert_true(customElement.lastChild.firstChild instanceof Text, 'HTML parser must preserve grandchild nodes of a custom element');
+    assert_equals(customElement.lastChild.firstChild.data, 'world', 'HTML parser must preserve grandchild nodes of a custom element');
+}, 'HTML parser must append child nodes');
+
+test(function () {
+    assert_equals(numberOfAttributesInConstructor, 0, 'HTML parser must not set attributes on a custom element before invoking the constructor');
+    assert_equals(numberOfChildNodesInConstructor, 0, 'HTML parser must not append child nodes to a custom element before invoking the constructor');
+}, 'HTML parser must set the attributes or append children before calling constructor');
+
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/custom-elements/parser/parser-uses-constructed-element-expected.txt (0 => 197463)


--- trunk/LayoutTests/fast/custom-elements/parser/parser-uses-constructed-element-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/parser/parser-uses-constructed-element-expected.txt	2016-03-02 21:56:47 UTC (rev 197463)
@@ -0,0 +1,4 @@
+
+PASS HTML parser must use the returned value of the custom element constructor instead of the one created before super() call 
+PASS HTML parser must use the returned value of the custom element constructor instead using the one created in super() call 
+

Added: trunk/LayoutTests/fast/custom-elements/parser/parser-uses-constructed-element.html (0 => 197463)


--- trunk/LayoutTests/fast/custom-elements/parser/parser-uses-constructed-element.html	                        (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/parser/parser-uses-constructed-element.html	2016-03-02 21:56:47 UTC (rev 197463)
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom Elements: Changes to the HTML parser</title>
+<meta name="author" title="Ryosuke Niwa" href=""
+<meta name="assert" content="HTML parser must construct a custom element instead of upgrading">
+<script src=""
+<script src=""
+<link rel='stylesheet' href=''>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+let anotherElementCreatedBeforeSuperCall = undefined;
+let elementCreatedBySuperCall = undefined;
+let shouldCreateElementBeforeSuperCall = true;
+class InstantiatesItselfBeforeSuper extends HTMLElement {
+    constructor() {
+        if (shouldCreateElementBeforeSuperCall) {
+            shouldCreateElementBeforeSuperCall = false;
+            anotherElementCreatedBeforeSuperCall = new InstantiatesItselfBeforeSuper();
+        }
+        super();
+        elementCreatedBySuperCall = this;
+    }
+};
+document.defineCustomElement('instantiates-itself-before-super', InstantiatesItselfBeforeSuper);
+
+let shouldCreateAnotherInstance = true;
+let anotherInstance = undefined;
+let firstInstance = undefined;
+class ReturnsAnotherInstance extends HTMLElement {
+    constructor() {
+        super();
+        if (shouldCreateAnotherInstance) {
+            shouldCreateAnotherInstance = false;
+            firstInstance = this;
+            anotherInstance = new ReturnsAnotherInstance;
+            return anotherInstance;
+        } else
+            return this;
+    }
+};
+document.defineCustomElement('returns-another-instance', ReturnsAnotherInstance);
+
+</script>
+<instantiates-itself-before-super></instantiates-itself-before-super>
+<returns-another-instance></returns-another-instance>
+<script>
+
+test(function () {
+    var instance = document.querySelector('instantiates-itself-before-super');
+
+    assert_equals(instance, elementCreatedBySuperCall, 'HTML parser must insert the element returned by the custom element constructor');
+    assert_not_equals(instance, anotherElementCreatedBeforeSuperCall, 'HTML parser must not insert another instance of the custom element created before super() call');
+    assert_equals(anotherElementCreatedBeforeSuperCall.parentNode, null, 'HTML parser must not insert another instance of the custom element created before super() call');
+
+}, 'HTML parser must use the returned value of the custom element constructor instead of the one created before super() call');
+
+test(function () {
+    var instance = document.querySelector('returns-another-instance');
+
+    assert_equals(instance, anotherInstance, 'HTML parser must insert the element returned by the custom element constructor');
+    assert_not_equals(instance, firstInstance, 'HTML parser must not insert the element created by super() call if the constructor returned another element');
+    assert_equals(firstInstance.parentNode, null, 'HTML parser must not insert the element created by super() call if the constructor returned another element');
+
+}, 'HTML parser must use the returned value of the custom element constructor instead using the one created in super() call');
+
+</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (197462 => 197463)


--- trunk/Source/WebCore/ChangeLog	2016-03-02 21:42:22 UTC (rev 197462)
+++ trunk/Source/WebCore/ChangeLog	2016-03-02 21:56:47 UTC (rev 197463)
@@ -1,3 +1,80 @@
+2016-03-01  Ryosuke Niwa  <[email protected]>
+
+        Make HTML parser construct custom elements
+        https://bugs.webkit.org/show_bug.cgi?id=154908
+        <rdar://problem/24923735>
+
+        Reviewed by Antti Koivisto.
+
+        Added the support for instantiating custom elements inside the parser. Based on Jan F2F discussion,
+        the HTML parser is going to synchronously construct custom elements. When a custom element constructor
+        throws, the HTML parser creates a HTMLUnknownElement instead.
+
+        In our implementation, we pause the parser completely and construct custom elements using the same
+        mechanism used to run author scripts. It's possible that we may want to apply some optimizations to
+        to make custom element construction but it's probably a good idea to get semantics right first.
+
+        Tests: 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
+
+        * bindings/js/JSCustomElementInterface.cpp:
+        (WebCore::JSCustomElementInterface::constructElement): Added ShouldClearException as an argument
+        to be used by the HTML parser since the parser can't re-throw to anywhere or fail parsing.
+
+        * bindings/js/JSCustomElementInterface.h:
+        (WebCore::JSCustomElementInterface::ShouldClearException): Added.
+
+        * dom/Document.cpp:
+        (WebCore::createHTMLElementWithNameValidation): Do not clear the exception here since createElement
+        must re-throw the exception thrown by a custom element constructor.
+        (WebCore::Document::createElementForBindings):
+
+        * dom/make_names.pl:
+        (printFactoryCppFile): Added ConstructorFunctionMapEntry which contains the constructor function
+        as well as the qualified name.
+        (printFactoryHeaderFile): Added a variant of createKnownElement and createElement that takes
+        AtomicString instead of QualifiedName.
+
+        * html/parser/HTMLConstructionSite.cpp:
+        (WebCore::setAttributes): Added a variant that takes Vector<Attribute>.
+        (WebCore::HTMLConstructionSite::insertHTMLElementOrFindCustomElementInterface): Added. Returns a
+        custom element interface when the element doesn't match any builtin element and there is a custom
+        element definition that matches the specified name.
+        (WebCore::HTMLConstructionSite::insertCustomElement): Added. Like insertElement but also sets the
+        attributes on the newly created custom element.
+        (WebCore::HTMLConstructionSite::createHTMLElementOrFindCustomElementInterface): Extracted from
+        createHTMLElement. When customElementInterface is not nullptr, we optionally find the custom
+        element interface and return nullptr.
+        (WebCore::HTMLConstructionSite::createHTMLElement):
+        * html/parser/HTMLConstructionSite.h:
+
+        * html/parser/HTMLDocumentParser.cpp:
+        (WebCore::HTMLDocumentParser::runScriptsForPausedTreeBuilder): Create a custom element when there
+        is a pending custom element to create (i.e. m_customElementToConstruct is not empty).
+        (WebCore::HTMLDocumentParser::isWaitingForScripts):
+
+        * html/parser/HTMLStackItem.h:
+        (WebCore::HTMLStackItem::create): Added a variant used for custom elements.
+        (WebCore::HTMLStackItem::HTMLStackItem): Ditto.
+
+        * html/parser/HTMLTreeBuilder.cpp:
+        (WebCore::CustomElementConstructionData::CustomElementConstructionData): Added. It needs to be in
+        the cpp file to avoid introducing more header dependencies in HTMLTreeBuilder.h.
+        (WebCore::CustomElementConstructionData::~CustomElementConstructionData): Ditto.
+        (WebCore::HTMLTreeBuilder::processStartTagForInBody): Use insertGenericHTMLElement when creating
+        a generic element that could be custom elements.
+        (WebCore::HTMLTreeBuilder::insertGenericHTMLElement): Added. Create and insert a new element
+        or set m_customElementToConstruct so that the HTMLDocumentParser will create a custom element later.
+        (WebCore::HTMLTreeBuilder::didCreateCustomOrCallbackElement): Added. Called by HTMLDocumentParser
+        when it finishes creating a new custom element.
+
+        * html/parser/HTMLTreeBuilder.h:
+        (WebCore::HTMLTreeBuilder::takeCustomElementConstructionData): Added.
+        (WebCore::HTMLTreeBuilder::hasParserBlockingScriptWork): Renamed from hasParserBlockingScript.
+        Checks the existence of m_customElementToConstruct as well as m_scriptToProcess.
+
 2016-03-02  Zalan Bujtas  <[email protected]>
 
         Use IndentTextOrNot instead of passing isFirstLine/shouldIndentText as bool.

Modified: trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp (197462 => 197463)


--- trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp	2016-03-02 21:42:22 UTC (rev 197462)
+++ trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp	2016-03-02 21:56:47 UTC (rev 197463)
@@ -55,7 +55,7 @@
 {
 }
 
-RefPtr<Element> JSCustomElementInterface::constructElement(const AtomicString& tagName)
+RefPtr<Element> JSCustomElementInterface::constructElement(const AtomicString& tagName, ShouldClearException shouldClearException)
 {
     if (!canInvokeCallback())
         return nullptr;
@@ -88,6 +88,9 @@
     JSValue newElement = construct(state, m_constructor.get(), constructType, constructData, args);
     InspectorInstrumentation::didCallFunction(cookie, context);
 
+    if (shouldClearException == ShouldClearException::Clear && state->hadException())
+        state->clearException();
+
     if (newElement.isEmpty())
         return nullptr;
 

Modified: trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h (197462 => 197463)


--- trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h	2016-03-02 21:42:22 UTC (rev 197462)
+++ trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h	2016-03-02 21:56:47 UTC (rev 197463)
@@ -59,7 +59,8 @@
         return adoptRef(*new JSCustomElementInterface(callback, globalObject));
     }
 
-    RefPtr<Element> constructElement(const AtomicString&);
+    enum class ShouldClearException { Clear, DoNotClear };
+    RefPtr<Element> constructElement(const AtomicString&, ShouldClearException);
 
     ScriptExecutionContext* scriptExecutionContext() const { return ContextDestructionObserver::scriptExecutionContext(); }
     JSC::JSObject* constructor() { return m_constructor.get(); }

Modified: trunk/Source/WebCore/dom/Document.cpp (197462 => 197463)


--- trunk/Source/WebCore/dom/Document.cpp	2016-03-02 21:42:22 UTC (rev 197462)
+++ trunk/Source/WebCore/dom/Document.cpp	2016-03-02 21:56:47 UTC (rev 197463)
@@ -879,35 +879,35 @@
     clearStyleResolver();
 }
 
-static RefPtr<Element> createHTMLElementWithNameValidation(Document& document, const QualifiedName qualifiedName, ExceptionCode& ec)
+static RefPtr<Element> createHTMLElementWithNameValidation(Document& document, const AtomicString& localName, ExceptionCode& ec)
 {
-    RefPtr<HTMLElement> element = HTMLElementFactory::createKnownElement(qualifiedName, document);
+    RefPtr<HTMLElement> element = HTMLElementFactory::createKnownElement(localName, document);
     if (LIKELY(element))
         return element;
 
 #if ENABLE(CUSTOM_ELEMENTS)
     auto* definitions = document.customElementDefinitions();
     if (UNLIKELY(definitions)) {
-        if (auto* interface = definitions->findInterface(qualifiedName))
-            return interface->constructElement(qualifiedName.localName());
+        if (auto* interface = definitions->findInterface(localName))
+            return interface->constructElement(localName, JSCustomElementInterface::ShouldClearException::DoNotClear);
     }
 #endif
 
-    if (UNLIKELY(!Document::isValidName(qualifiedName.localName()))) {
+    if (UNLIKELY(!Document::isValidName(localName))) {
         ec = INVALID_CHARACTER_ERR;
         return nullptr;
     }
 
-    return HTMLUnknownElement::create(qualifiedName, document);
+    return HTMLUnknownElement::create(QualifiedName(nullAtom, localName, xhtmlNamespaceURI), document);
 }
 
 RefPtr<Element> Document::createElementForBindings(const AtomicString& name, ExceptionCode& ec)
 {
     if (isHTMLDocument())
-        return createHTMLElementWithNameValidation(*this, QualifiedName(nullAtom, name.convertToASCIILowercase(), xhtmlNamespaceURI), ec);
+        return createHTMLElementWithNameValidation(*this, name.convertToASCIILowercase(), ec);
 
     if (isXHTMLDocument())
-        return createHTMLElementWithNameValidation(*this, QualifiedName(nullAtom, name, xhtmlNamespaceURI), ec);
+        return createHTMLElementWithNameValidation(*this, name, ec);
 
     if (!isValidName(name)) {
         ec = INVALID_CHARACTER_ERR;

Modified: trunk/Source/WebCore/dom/make_names.pl (197462 => 197463)


--- trunk/Source/WebCore/dom/make_names.pl	2016-03-02 21:42:22 UTC (rev 197462)
+++ trunk/Source/WebCore/dom/make_names.pl	2016-03-02 21:56:47 UTC (rev 197463)
@@ -986,10 +986,28 @@
         $argumentList = "name, document, createdByParser";
     }
 
+    my $lowercaseNamespacePrefix = lc($parameters{namespacePrefix});
+
     printConstructors($F, \%tagConstructorMap);
 
     print F <<END
-static NEVER_INLINE void populate$parameters{namespace}FactoryMap(HashMap<AtomicStringImpl*, $parameters{namespace}ConstructorFunction>& map)
+
+struct ConstructorFunctionMapEntry {
+    ConstructorFunctionMapEntry($parameters{namespace}ConstructorFunction function, const QualifiedName& name)
+        : function(function)
+        , qualifiedName(&name)
+    { }
+
+    ConstructorFunctionMapEntry()
+        : function(nullptr)
+        , qualifiedName(nullptr)
+    { }
+
+    $parameters{namespace}ConstructorFunction function;
+    const QualifiedName* qualifiedName; // Use pointer instead of reference so that emptyValue() in HashMap is cheap to create.
+};
+
+static NEVER_INLINE void populate$parameters{namespace}FactoryMap(HashMap<AtomicStringImpl*, ConstructorFunctionMapEntry>& map)
 {
     struct TableEntry {
         const QualifiedName& name;
@@ -1006,25 +1024,42 @@
     };
 
     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(table); ++i)
-        map.add(table[i].name.localName().impl(), table[i].function);
+        map.add(table[i].name.localName().impl(), ConstructorFunctionMapEntry(table[i].function, table[i].name));
 }
 
-RefPtr<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createKnownElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser)
+
+static ConstructorFunctionMapEntry find$parameters{namespace}ElementConstructorFunction(const AtomicString& localName)
 {
-    static NeverDestroyed<HashMap<AtomicStringImpl*, $parameters{namespace}ConstructorFunction>> functions;
-    if (functions.get().isEmpty())
-        populate$parameters{namespace}FactoryMap(functions);
-    $parameters{namespace}ConstructorFunction function = functions.get().get(name.localName().impl());
-    if (LIKELY(function))
-        return function($argumentList);
+    static NeverDestroyed<HashMap<AtomicStringImpl*, ConstructorFunctionMapEntry>> map;
+    if (map.get().isEmpty())
+        populate$parameters{namespace}FactoryMap(map);
+    return map.get().get(localName.impl());
+}
+
+RefPtr<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createKnownElement(const AtomicString& localName, Document& document$formElementArgumentForDefinition, bool createdByParser)
+{
+    const ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(localName);
+    if (LIKELY(entry.function)) {
+        ASSERT(entry.qualifiedName);
+        const auto& name = *entry.qualifiedName;
+        return entry.function($argumentList);
+    }
     return nullptr;
 }
 
-Ref<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser)
+Ref<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const AtomicString& name, Document& document$formElementArgumentForDefinition, bool createdByParser)
 {
     RefPtr<$parameters{namespace}Element> element = $parameters{namespace}ElementFactory::createKnownElement($argumentList);
     if (LIKELY(element))
         return element.releaseNonNull();
+    return $parameters{fallbackInterfaceName}::create(QualifiedName(nullAtom, name, ${lowercaseNamespacePrefix}NamespaceURI), document);
+}
+
+Ref<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser)
+{
+    const ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(name.localName());
+    if (LIKELY(entry.function))
+        return entry.function($argumentList);
     return $parameters{fallbackInterfaceName}::create(name, document);
 }
 
@@ -1065,12 +1100,15 @@
 END
 ;
 
-print F "        static RefPtr<$parameters{namespace}Element> createKnownElement(const QualifiedName&, Document&";
+print F "        static RefPtr<$parameters{namespace}Element> createKnownElement(const AtomicString& localName, Document&";
 print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML";
 print F ", bool createdByParser = false);\n\n";
-print F "        static Ref<$parameters{namespace}Element> createElement(const QualifiedName&, Document&";
+print F "        static Ref<$parameters{namespace}Element> createElement(const AtomicString& localName, Document&";
 print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML";
 print F ", bool createdByParser = false);\n";
+print F "        static Ref<$parameters{namespace}Element> createElement(const QualifiedName& localName, Document&";
+print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML";
+print F ", bool createdByParser = false);\n";
 
 printf F<<END
     };

Modified: trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp (197462 => 197463)


--- trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp	2016-03-02 21:42:22 UTC (rev 197462)
+++ trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp	2016-03-02 21:56:47 UTC (rev 197463)
@@ -28,6 +28,7 @@
 #include "HTMLTreeBuilder.h"
 
 #include "Comment.h"
+#include "CustomElementDefinitions.h"
 #include "DocumentFragment.h"
 #include "DocumentType.h"
 #include "Frame.h"
@@ -43,6 +44,7 @@
 #include "HTMLPictureElement.h"
 #include "HTMLScriptElement.h"
 #include "HTMLTemplateElement.h"
+#include "HTMLUnknownElement.h"
 #include "NotImplemented.h"
 #include "SVGElement.h"
 #include "Text.h"
@@ -51,13 +53,18 @@
 
 using namespace HTMLNames;
 
-static inline void setAttributes(Element& element, AtomicHTMLToken* token, ParserContentPolicy parserContentPolicy)
+static inline void setAttributes(Element& element, Vector<Attribute>& attributes, ParserContentPolicy parserContentPolicy)
 {
     if (!scriptingContentIsAllowed(parserContentPolicy))
-        element.stripScriptingAttributes(token->attributes());
-    element.parserSetAttributes(token->attributes());
+        element.stripScriptingAttributes(attributes);
+    element.parserSetAttributes(attributes);
 }
 
+static inline void setAttributes(Element& element, AtomicHTMLToken* token, ParserContentPolicy parserContentPolicy)
+{
+    setAttributes(element, token->attributes(), parserContentPolicy);
+}
+
 static bool hasImpliedEndTag(const HTMLStackItem& item)
 {
     return item.hasTagName(ddTag)
@@ -482,6 +489,26 @@
     m_openElements.push(HTMLStackItem::create(element.releaseNonNull(), *token));
 }
 
+#if ENABLE(CUSTOM_ELEMENTS)
+JSCustomElementInterface* HTMLConstructionSite::insertHTMLElementOrFindCustomElementInterface(AtomicHTMLToken* token)
+{
+    JSCustomElementInterface* interface = nullptr;
+    RefPtr<Element> element = createHTMLElementOrFindCustomElementInterface(token, &interface);
+    if (UNLIKELY(interface))
+        return interface;
+    attachLater(&currentNode(), element);
+    m_openElements.push(HTMLStackItem::create(element.releaseNonNull(), *token));
+    return nullptr;
+}
+
+void HTMLConstructionSite::insertCustomElement(Ref<Element>&& element, const AtomicString& localName, Vector<Attribute>& attributes)
+{
+    setAttributes(element.get(), attributes, m_parserContentPolicy);
+    attachLater(&currentNode(), element.ptr());
+    m_openElements.push(HTMLStackItem::create(WTFMove(element), localName, attributes));
+}
+#endif
+
 void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken* token)
 {
     ASSERT(token->type() == HTMLToken::StartTag);
@@ -633,28 +660,53 @@
     return currentNode().document();
 }
 
-Ref<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken* token)
+RefPtr<Element> HTMLConstructionSite::createHTMLElementOrFindCustomElementInterface(
+    AtomicHTMLToken* token, JSCustomElementInterface** customElementInterface)
 {
-    QualifiedName tagName(nullAtom, token->name(), xhtmlNamespaceURI);
+    auto& localName = token->name();
     // FIXME: This can't use HTMLConstructionSite::createElement because we
     // have to pass the current form element.  We should rework form association
     // to occur after construction to allow better code sharing here.
     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#create-an-element-for-the-token
     Document& ownerDocument = ownerDocumentForCurrentNode();
     bool insideTemplateElement = !ownerDocument.frame();
-    Ref<Element> element = HTMLElementFactory::createElement(tagName, ownerDocument, insideTemplateElement ? nullptr : form(), true);
-    
+    RefPtr<Element> element = HTMLElementFactory::createKnownElement(localName, ownerDocument, insideTemplateElement ? nullptr : form(), true);
+    if (UNLIKELY(!element)) {
+
+#if ENABLE(CUSTOM_ELEMENTS)
+        auto* definitions = ownerDocumentForCurrentNode().customElementDefinitions();
+        if (customElementInterface && UNLIKELY(definitions)) {
+            if (auto* interface = definitions->findInterface(localName)) {
+                *customElementInterface = interface;
+                return nullptr;
+            }
+        }
+#else
+        UNUSED_PARAM(customElementInterface);
+#endif
+
+        element = HTMLUnknownElement::create(QualifiedName(nullAtom, localName, xhtmlNamespaceURI), ownerDocumentForCurrentNode());
+    }
+    ASSERT(element);
+
     // FIXME: This is a hack to connect images to pictures before the image has
     // been inserted into the document. It can be removed once asynchronous image
     // loading is working.
-    if (is<HTMLPictureElement>(currentNode()) && is<HTMLImageElement>(element))
-        downcast<HTMLImageElement>(element.get()).setPictureElement(&downcast<HTMLPictureElement>(currentNode()));
+    if (is<HTMLPictureElement>(currentNode()) && is<HTMLImageElement>(*element))
+        downcast<HTMLImageElement>(*element).setPictureElement(&downcast<HTMLPictureElement>(currentNode()));
 
-    setAttributes(element.get(), token, m_parserContentPolicy);
+    setAttributes(*element, token, m_parserContentPolicy);
     ASSERT(element->isHTMLElement());
     return element;
 }
 
+Ref<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken* token)
+{
+    RefPtr<Element> element = createHTMLElementOrFindCustomElementInterface(token, nullptr);
+    ASSERT(element);
+    return element.releaseNonNull();
+}
+
 Ref<HTMLStackItem> HTMLConstructionSite::createElementFromSavedToken(HTMLStackItem* item)
 {
     // NOTE: Moving from item -> token -> item copies the Attribute vector twice!

Modified: trunk/Source/WebCore/html/parser/HTMLConstructionSite.h (197462 => 197463)


--- trunk/Source/WebCore/html/parser/HTMLConstructionSite.h	2016-03-02 21:42:22 UTC (rev 197462)
+++ trunk/Source/WebCore/html/parser/HTMLConstructionSite.h	2016-03-02 21:56:47 UTC (rev 197463)
@@ -84,6 +84,7 @@
 class Document;
 class Element;
 class HTMLFormElement;
+class JSCustomElementInterface;
 
 class HTMLConstructionSite {
     WTF_MAKE_NONCOPYABLE(HTMLConstructionSite);
@@ -103,6 +104,10 @@
     void insertCommentOnDocument(AtomicHTMLToken*);
     void insertCommentOnHTMLHtmlElement(AtomicHTMLToken*);
     void insertHTMLElement(AtomicHTMLToken*);
+#if ENABLE(CUSTOM_ELEMENTS)
+    JSCustomElementInterface* insertHTMLElementOrFindCustomElementInterface(AtomicHTMLToken*);
+    void insertCustomElement(Ref<Element>&&, const AtomicString& localName, Vector<Attribute>&);
+#endif
     void insertSelfClosingHTMLElement(AtomicHTMLToken*);
     void insertFormattingElement(AtomicHTMLToken*);
     void insertHTMLHeadElement(AtomicHTMLToken*);
@@ -194,6 +199,7 @@
 
     void findFosterSite(HTMLConstructionSiteTask&);
 
+    RefPtr<Element> createHTMLElementOrFindCustomElementInterface(AtomicHTMLToken*, JSCustomElementInterface**);
     Ref<Element> createHTMLElement(AtomicHTMLToken*);
     Ref<Element> createElement(AtomicHTMLToken*, const AtomicString& namespaceURI);
 

Modified: trunk/Source/WebCore/html/parser/HTMLDocumentParser.cpp (197462 => 197463)


--- trunk/Source/WebCore/html/parser/HTMLDocumentParser.cpp	2016-03-02 21:42:22 UTC (rev 197462)
+++ trunk/Source/WebCore/html/parser/HTMLDocumentParser.cpp	2016-03-02 21:56:47 UTC (rev 197463)
@@ -34,6 +34,8 @@
 #include "HTMLPreloadScanner.h"
 #include "HTMLScriptRunner.h"
 #include "HTMLTreeBuilder.h"
+#include "HTMLUnknownElement.h"
+#include "JSCustomElementInterface.h"
 
 namespace WebCore {
 
@@ -188,8 +190,24 @@
 {
     ASSERT(scriptingContentIsAllowed(parserContentPolicy()));
 
+#if ENABLE(CUSTOM_ELEMENTS)
+    if (std::unique_ptr<CustomElementConstructionData> constructionData = m_treeBuilder->takeCustomElementConstructionData()) {
+        ASSERT(!m_treeBuilder->hasParserBlockingScriptWork());
+
+        RefPtr<Element> newElement = constructionData->interface->constructElement(constructionData->name, JSCustomElementInterface::ShouldClearException::Clear);
+        if (!newElement) {
+            // FIXME: This call to docuemnt() is wrong for elements inside a template element.
+            newElement = HTMLUnknownElement::create(QualifiedName(nullAtom, constructionData->name, xhtmlNamespaceURI), *document());
+        }
+
+        m_treeBuilder->didCreateCustomOrCallbackElement(newElement.releaseNonNull(), *constructionData);
+        return;
+    }
+#endif
+
     TextPosition scriptStartPosition = TextPosition::belowRangePosition();
     if (auto scriptElement = m_treeBuilder->takeScriptToProcess(scriptStartPosition)) {
+        ASSERT(!m_treeBuilder->hasParserBlockingScriptWork());
         // We will not have a scriptRunner when parsing a DocumentFragment.
         if (m_scriptRunner)
             m_scriptRunner->execute(scriptElement.release(), scriptStartPosition);
@@ -459,7 +477,7 @@
     // The script runner will hold the script until its loaded and run. During
     // any of this time, we want to count ourselves as "waiting for a script" and thus
     // run the preload scanner, as well as delay completion of parsing.
-    bool treeBuilderHasBlockingScript = m_treeBuilder->hasParserBlockingScript();
+    bool treeBuilderHasBlockingScript = m_treeBuilder->hasParserBlockingScriptWork();
     bool scriptRunnerHasBlockingScript = m_scriptRunner && m_scriptRunner->hasParserBlockingScript();
     // Since the parser is paused while a script runner has a blocking script, it should
     // never be possible to end up with both objects holding a blocking script.

Modified: trunk/Source/WebCore/html/parser/HTMLStackItem.h (197462 => 197463)


--- trunk/Source/WebCore/html/parser/HTMLStackItem.h	2016-03-02 21:42:22 UTC (rev 197462)
+++ trunk/Source/WebCore/html/parser/HTMLStackItem.h	2016-03-02 21:56:47 UTC (rev 197463)
@@ -40,6 +40,7 @@
 public:
     // Normal HTMLElementStack and HTMLFormattingElementList items.
     static Ref<HTMLStackItem> create(Ref<Element>&&, AtomicHTMLToken&, const AtomicString& namespaceURI = HTMLNames::xhtmlNamespaceURI);
+    static Ref<HTMLStackItem> create(Ref<Element>&&, const AtomicString&, const Vector<Attribute>&);
 
     // Document fragment or element for parsing context.
     static Ref<HTMLStackItem> create(Element&);
@@ -62,6 +63,7 @@
 
 private:
     HTMLStackItem(Ref<Element>&&, AtomicHTMLToken&, const AtomicString& namespaceURI);
+    HTMLStackItem(Ref<Element>&&, const AtomicString& localName, const AtomicString& namespaceURI, const Vector<Attribute>&);
     explicit HTMLStackItem(Element&);
     explicit HTMLStackItem(DocumentFragment&);
 
@@ -89,6 +91,21 @@
     return adoptRef(*new HTMLStackItem(WTFMove(element), token, namespaceURI));
 }
 
+inline HTMLStackItem::HTMLStackItem(Ref<Element>&& element, const AtomicString& localName, const AtomicString& namespaceURI, const Vector<Attribute>& attributes)
+    : m_node(WTFMove(element))
+    , m_namespaceURI(namespaceURI)
+    , m_localName(localName)
+    , m_attributes(attributes)
+{
+    // FIXME: We should find a way to move the attributes vector in the normal code path instead of copying it.
+}
+
+inline Ref<HTMLStackItem> HTMLStackItem::create(Ref<Element>&& element, const AtomicString& localName, const Vector<Attribute>& attributes)
+{
+    auto& namespaceURI = element.get().namespaceURI();
+    return adoptRef(*new HTMLStackItem(WTFMove(element), localName, namespaceURI, attributes));
+}
+
 inline HTMLStackItem::HTMLStackItem(Element& element)
     : m_node(element)
     , m_namespaceURI(element.namespaceURI())

Modified: trunk/Source/WebCore/html/parser/HTMLTreeBuilder.cpp (197462 => 197463)


--- trunk/Source/WebCore/html/parser/HTMLTreeBuilder.cpp	2016-03-02 21:42:22 UTC (rev 197462)
+++ trunk/Source/WebCore/html/parser/HTMLTreeBuilder.cpp	2016-03-02 21:56:47 UTC (rev 197463)
@@ -34,6 +34,7 @@
 #include "HTMLFormElement.h"
 #include "HTMLOptGroupElement.h"
 #include "HTMLParserIdioms.h"
+#include "JSCustomElementInterface.h"
 #include "LocalizedStrings.h"
 #include "NotImplemented.h"
 #include "XLinkNames.h"
@@ -50,6 +51,19 @@
 
 using namespace HTMLNames;
 
+#if ENABLE(CUSTOM_ELEMENTS)
+
+CustomElementConstructionData::CustomElementConstructionData(Ref<JSCustomElementInterface>&& interface, const AtomicString& name, const Vector<Attribute>& attributes)
+    : interface(WTFMove(interface))
+    , name(name)
+    , attributes(attributes) // FIXME: Avoid copying attributes.
+{ }
+
+CustomElementConstructionData::~CustomElementConstructionData()
+{ }
+
+#endif
+
 namespace {
 
 inline bool isHTMLSpaceOrReplacementCharacter(UChar character)
@@ -896,9 +910,27 @@
     }
 #endif
     m_tree.reconstructTheActiveFormattingElements();
+    insertGenericHTMLElement(token);
+}
+
+inline void HTMLTreeBuilder::insertGenericHTMLElement(AtomicHTMLToken& token)
+{
+#if ENABLE(CUSTOM_ELEMENTS)
+    auto* interface = m_tree.insertHTMLElementOrFindCustomElementInterface(&token);
+    if (UNLIKELY(interface))
+        m_customElementToConstruct = std::make_unique<CustomElementConstructionData>(*interface, token.name(), token.attributes());
+#else
     m_tree.insertHTMLElement(&token);
+#endif
 }
 
+#if ENABLE(CUSTOM_ELEMENTS)
+void HTMLTreeBuilder::didCreateCustomOrCallbackElement(Ref<Element>&& element, CustomElementConstructionData& data)
+{
+    m_tree.insertCustomElement(WTFMove(element), data.name, data.attributes);
+}
+#endif
+
 #if ENABLE(TEMPLATE_ELEMENT)
 
 void HTMLTreeBuilder::processTemplateStartTag(AtomicHTMLToken& token)

Modified: trunk/Source/WebCore/html/parser/HTMLTreeBuilder.h (197462 => 197463)


--- trunk/Source/WebCore/html/parser/HTMLTreeBuilder.h	2016-03-02 21:42:22 UTC (rev 197462)
+++ trunk/Source/WebCore/html/parser/HTMLTreeBuilder.h	2016-03-02 21:56:47 UTC (rev 197463)
@@ -34,8 +34,20 @@
 
 namespace WebCore {
 
+class JSCustomElementInterface;
 class HTMLDocumentParser;
 
+#if ENABLE(CUSTOM_ELEMENTS)
+struct CustomElementConstructionData {
+    CustomElementConstructionData(Ref<JSCustomElementInterface>&&, const AtomicString& name, const Vector<Attribute>&);
+    ~CustomElementConstructionData();
+
+    Ref<JSCustomElementInterface> interface;
+    AtomicString name;
+    Vector<Attribute> attributes;
+};
+#endif
+
 class HTMLTreeBuilder {
     WTF_MAKE_FAST_ALLOCATED;
 public:
@@ -49,11 +61,16 @@
 
     void constructTree(AtomicHTMLToken&);
 
-    bool hasParserBlockingScript() const;
+    bool hasParserBlockingScriptWork() const;
 
     // Must be called to take the parser-blocking script before calling the parser again.
     RefPtr<Element> takeScriptToProcess(TextPosition& scriptStartPosition);
 
+#if ENABLE(CUSTOM_ELEMENTS)
+    std::unique_ptr<CustomElementConstructionData> takeCustomElementConstructionData() { return WTFMove(m_customElementToConstruct); }
+    void didCreateCustomOrCallbackElement(Ref<Element>&&, CustomElementConstructionData&);
+#endif
+
     // Done, close any open tags, etc.
     void finished();
 
@@ -165,6 +182,8 @@
 
     void resetInsertionModeAppropriately();
 
+    void insertGenericHTMLElement(AtomicHTMLToken&);
+
 #if ENABLE(TEMPLATE_ELEMENT)
     void processTemplateStartTag(AtomicHTMLToken&);
     bool processTemplateEndTag(AtomicHTMLToken&);
@@ -204,6 +223,10 @@
     RefPtr<Element> m_scriptToProcess; // <script> tag which needs processing before resuming the parser.
     TextPosition m_scriptToProcessStartPosition; // Starting line number of the script tag needing processing.
 
+#if ENABLE(CUSTOM_ELEMENTS)
+    std::unique_ptr<CustomElementConstructionData> m_customElementToConstruct;
+#endif
+
     bool m_shouldSkipLeadingNewline { false };
 
     bool m_framesetOk { true };
@@ -235,10 +258,15 @@
     return !!m_fragmentContext.fragment();
 }
 
-inline bool HTMLTreeBuilder::hasParserBlockingScript() const
+inline bool HTMLTreeBuilder::hasParserBlockingScriptWork() const
 {
     ASSERT(!m_destroyed);
-    return !!m_scriptToProcess;
+#if ENABLE(CUSTOM_ELEMENTS)
+    ASSERT(!(m_scriptToProcess && m_customElementToConstruct));
+    return m_scriptToProcess || m_customElementToConstruct;
+#else
+    return m_scriptToProcess;
+#endif
 }
 
 inline DocumentFragment* HTMLTreeBuilder::FragmentParsingContext::fragment() const
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to