Title: [205060] trunk
Revision
205060
Author
[email protected]
Date
2016-08-26 17:04:08 -0700 (Fri, 26 Aug 2016)

Log Message

Adopted custom element's callbacks should continue to work
https://bugs.webkit.org/show_bug.cgi?id=161065

Reviewed by Andreas Kling.

Source/WebCore:

When a custom element is adopted into another document, its reaction callbacks need to continue to work.
Because a different document may have its own global object, each custom element needs to remember its
original global object or JSCustomElementInterface. This patch adds the latter to the element rare data.

Tests: fast/custom-elements/connected-callbacks.html
       fast/custom-elements/disconnected-callbacks.html

* bindings/js/JSCustomElementInterface.cpp:
(WebCore::JSCustomElementInterface::constructElement):
(WebCore::JSCustomElementInterface::upgradeElement):
* dom/CustomElementReactionQueue.cpp:
(WebCore::findInterfaceForCustomElement): Deleted.
(WebCore::CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded):
(WebCore::CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded):
(WebCore::CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded):
* dom/Element.cpp:
(WebCore::Element::insertedInto): Invoke callbacks even when the current document is not a HTML document.
(WebCore::Element::removedFrom): Ditto.
(WebCore::Element::setCustomElementIsResolved): Moved from Node. Add the element interface to the rare data.
(WebCore::Element::customElementInterface): Added.
* dom/Element.h:
* dom/ElementRareData.cpp:
* dom/ElementRareData.h:
(WebCore::ElementRareData::customElementInterface): Added.
(WebCore::ElementRareData::setCustomElementInterface): Added.
* dom/Node.h:
((WebCore::Node::setCustomElementIsResolved): Deleted.

LayoutTests:

Added test cases for adopting custom elements into various kinds of documents.

* fast/custom-elements/connected-callbacks-expected.txt:
* fast/custom-elements/connected-callbacks.html:
* fast/custom-elements/defined-pseudo-class-expected.txt:
* fast/custom-elements/defined-pseudo-class.html:
* fast/custom-elements/disconnected-callbacks-expected.txt:
* fast/custom-elements/disconnected-callbacks.html:
* fast/custom-elements/resources/document-types.js: Added.
* fast/custom-elements/resources/empty-html-document.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (205059 => 205060)


--- trunk/LayoutTests/ChangeLog	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/LayoutTests/ChangeLog	2016-08-27 00:04:08 UTC (rev 205060)
@@ -1,3 +1,21 @@
+2016-08-23  Ryosuke Niwa  <[email protected]>
+
+        Adopted custom element's callbacks should continue to work
+        https://bugs.webkit.org/show_bug.cgi?id=161065
+
+        Reviewed by Andreas Kling.
+
+        Added test cases for adopting custom elements into various kinds of documents.
+
+        * fast/custom-elements/connected-callbacks-expected.txt:
+        * fast/custom-elements/connected-callbacks.html:
+        * fast/custom-elements/defined-pseudo-class-expected.txt:
+        * fast/custom-elements/defined-pseudo-class.html:
+        * fast/custom-elements/disconnected-callbacks-expected.txt:
+        * fast/custom-elements/disconnected-callbacks.html:
+        * fast/custom-elements/resources/document-types.js: Added.
+        * fast/custom-elements/resources/empty-html-document.html: Added.
+
 2016-08-26  Ryan Haddad  <[email protected]>
 
         Marking webgl/max-active-contexts-webglcontextlost-prevent-default.html as flaky on mac-wk1.

Modified: trunk/LayoutTests/fast/custom-elements/connected-callbacks-expected.txt (205059 => 205060)


--- trunk/LayoutTests/fast/custom-elements/connected-callbacks-expected.txt	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/LayoutTests/fast/custom-elements/connected-callbacks-expected.txt	2016-08-27 00:04:08 UTC (rev 205060)
@@ -1,10 +1,43 @@
 
 PASS Inserting a custom element into a document must enqueue and invoke connectedCallback 
-PASS Inserting a custom element into a detached node must not enqueue and invoke connectedCallback 
-FAIL Inserting a custom element into a window-less document must enqueue and invoke connectedCallback assert_array_equals: lengths differ, expected 2 got 0
-PASS Inserting an ancestor of a custom element into a document must enqueue and invoke connectedCallback 
-FAIL Inserting an ancestor of custom element into a window-less document must enqueue and invoke connectedCallback assert_array_equals: lengths differ, expected 2 got 0
-PASS Inserting a custom element into a connected shadow tree must enqueue and invoke connectedCallback 
-PASS Inserting the shadow host of a shadow tree with a custom element into a document must enqueue and invoke connectedCallback 
-PASS Inserting a custom element into a detached shadow tree must not enqueue and invoke connectedCallback 
+PASS Inserting an ancestor of custom element into a document must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a shadow tree in a document must enqueue and invoke connectedCallback 
+PASS Inserting the shadow host of a custom element into a document must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a detached shadow tree that belongs to a document must not enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a document of a template element must enqueue and invoke connectedCallback 
+PASS Inserting an ancestor of custom element into a document of a template element must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a shadow tree in a document of a template element must enqueue and invoke connectedCallback 
+PASS Inserting the shadow host of a custom element into a document of a template element must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a detached shadow tree that belongs to a document of a template element must not enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a new document must enqueue and invoke connectedCallback 
+PASS Inserting an ancestor of custom element into a new document must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a shadow tree in a new document must enqueue and invoke connectedCallback 
+PASS Inserting the shadow host of a custom element into a new document must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a detached shadow tree that belongs to a new document must not enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a cloned document must enqueue and invoke connectedCallback 
+PASS Inserting an ancestor of custom element into a cloned document must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a shadow tree in a cloned document must enqueue and invoke connectedCallback 
+PASS Inserting the shadow host of a custom element into a cloned document must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a detached shadow tree that belongs to a cloned document must not enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a document created by createHTMLDocument must enqueue and invoke connectedCallback 
+PASS Inserting an ancestor of custom element into a document created by createHTMLDocument must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a shadow tree in a document created by createHTMLDocument must enqueue and invoke connectedCallback 
+PASS Inserting the shadow host of a custom element into a document created by createHTMLDocument must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a detached shadow tree that belongs to a document created by createHTMLDocument must not enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a HTML document created by createDocument must enqueue and invoke connectedCallback 
+PASS Inserting an ancestor of custom element into a HTML document created by createDocument must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a shadow tree in a HTML document created by createDocument must enqueue and invoke connectedCallback 
+PASS Inserting the shadow host of a custom element into a HTML document created by createDocument must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a detached shadow tree that belongs to a HTML document created by createDocument must not enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a document in an iframe must enqueue and invoke connectedCallback 
+PASS Inserting an ancestor of custom element into a document in an iframe must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a shadow tree in a document in an iframe must enqueue and invoke connectedCallback 
+PASS Inserting the shadow host of a custom element into a document in an iframe must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a detached shadow tree that belongs to a document in an iframe must not enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a HTML document fetched by XHR must enqueue and invoke connectedCallback 
+PASS Inserting an ancestor of custom element into a HTML document fetched by XHR must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a shadow tree in a HTML document fetched by XHR must enqueue and invoke connectedCallback 
+PASS Inserting the shadow host of a custom element into a HTML document fetched by XHR must enqueue and invoke connectedCallback 
+PASS Inserting a custom element into a detached shadow tree that belongs to a HTML document fetched by XHR must not enqueue and invoke connectedCallback 
 
+

Modified: trunk/LayoutTests/fast/custom-elements/connected-callbacks.html (205059 => 205060)


--- trunk/LayoutTests/fast/custom-elements/connected-callbacks.html	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/LayoutTests/fast/custom-elements/connected-callbacks.html	2016-08-27 00:04:08 UTC (rev 205060)
@@ -7,6 +7,7 @@
 <link rel="help" href=""
 <script src=""
 <script src=""
+<script src=""
 <link rel='stylesheet' href=''>
 </head>
 <body>
@@ -20,85 +21,69 @@
 }
 customElements.define('my-custom-element', MyCustomElement);
 
-test(function () {
-    var instance = document.createElement('my-custom-element');
+DocumentTypes.forEach(function (entry) {
+    var documentName = entry.name;
+    var getDocument = entry.create;
 
-    calls = [];
-    document.body.appendChild(instance);
-    assert_array_equals(calls, ['connected', instance]);
-}, 'Inserting a custom element into a document must enqueue and invoke connectedCallback');
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            var instance = document.createElement('my-custom-element');
+            calls = [];
+            doc.documentElement.appendChild(instance);
+            assert_array_equals(calls, ['connected', instance]);
+        });
+    }, 'Inserting a custom element into a ' + documentName + ' must enqueue and invoke connectedCallback');
 
-test(function () {
-    var instance = document.createElement('my-custom-element');
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            var instance = document.createElement('my-custom-element');
+            var parent = document.createElement('div');
+            parent.appendChild(instance);
+            calls = [];
+            doc.documentElement.appendChild(parent);
+            assert_array_equals(calls, ['connected', instance]);
+        });
+    }, 'Inserting an ancestor of custom element into a ' + documentName + ' must enqueue and invoke connectedCallback');
 
-    calls = [];
-    var parent = document.createElement('div');
-    parent.appendChild(instance);
-    assert_array_equals(calls, []);
-}, 'Inserting a custom element into a detached node must not enqueue and invoke connectedCallback');
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            var instance = document.createElement('my-custom-element');
+            var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+            var shadowRoot = host.attachShadow({mode: 'closed'});
+            doc.documentElement.appendChild(host);
 
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var documentWithoutWindow = document.implementation.createHTMLDocument();
+            calls = [];
+            shadowRoot.appendChild(instance);
+            assert_array_equals(calls, ['connected', instance]);
+        });
+    }, 'Inserting a custom element into a shadow tree in a ' + documentName + ' must enqueue and invoke connectedCallback');
 
-    calls = [];
-    documentWithoutWindow.body.appendChild(instance);
-    assert_array_equals(calls, ['connected', instance]);
-}, 'Inserting a custom element into a window-less document must enqueue and invoke connectedCallback');
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            var instance = document.createElement('my-custom-element');
+            var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+            var shadowRoot = host.attachShadow({mode: 'closed'});
+            shadowRoot.appendChild(instance);
 
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var parent = document.createElement('div');
-    parent.appendChild(instance);
+            calls = [];
+            doc.documentElement.appendChild(host);
+            assert_array_equals(calls, ['connected', instance]);
+        });
+    }, 'Inserting the shadow host of a custom element into a ' + documentName + ' must enqueue and invoke connectedCallback');
 
-    calls = [];
-    document.body.appendChild(parent);
-    assert_array_equals(calls, ['connected', instance]);
-}, 'Inserting an ancestor of a custom element into a document must enqueue and invoke connectedCallback');
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            var instance = document.createElement('my-custom-element');
+            var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+            var shadowRoot = host.attachShadow({mode: 'closed'});
 
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var parent = document.createElement('div');
-    parent.appendChild(instance);
-    var documentWithoutWindow = document.implementation.createHTMLDocument();
+            calls = [];
+            shadowRoot.appendChild(instance);
+            assert_array_equals(calls, []);
+        });
+    }, 'Inserting a custom element into a detached shadow tree that belongs to a ' + documentName + ' must not enqueue and invoke connectedCallback');
+});
 
-    calls = [];
-    documentWithoutWindow.body.appendChild(parent);
-    assert_array_equals(calls, ['connected', instance]);
-}, 'Inserting an ancestor of custom element into a window-less document must enqueue and invoke connectedCallback');
-
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var host = document.createElement('div');
-    var shadowRoot = host.attachShadow({mode: 'closed'});
-    document.body.appendChild(host);
-
-    calls = [];
-    shadowRoot.appendChild(instance);
-    assert_array_equals(calls, ['connected', instance]);
-}, 'Inserting a custom element into a connected shadow tree must enqueue and invoke connectedCallback');
-
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var host = document.createElement('div');
-    var shadowRoot = host.attachShadow({mode: 'closed'});
-    shadowRoot.appendChild(instance);
-
-    calls = [];
-    document.body.appendChild(host);
-    assert_array_equals(calls, ['connected', instance]);
-}, 'Inserting the shadow host of a shadow tree with a custom element into a document must enqueue and invoke connectedCallback');
-
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var host = document.createElement('div');
-    var shadowRoot = host.attachShadow({mode: 'closed'});
-
-    calls = [];
-    shadowRoot.appendChild(instance);
-    assert_array_equals(calls, []);
-}, 'Inserting a custom element into a detached shadow tree must not enqueue and invoke connectedCallback');
-
 </script>
 </body>
 </html>

Modified: trunk/LayoutTests/fast/custom-elements/defined-pseudo-class-expected.txt (205059 => 205060)


--- trunk/LayoutTests/fast/custom-elements/defined-pseudo-class-expected.txt	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/LayoutTests/fast/custom-elements/defined-pseudo-class-expected.txt	2016-08-27 00:04:08 UTC (rev 205060)
@@ -1,5 +1,5 @@
 
-PASS The defined flag of a custom element must be set if a custom element has not been upgraded yet 
+PASS The defined flag of a custom element must not be set if a custom element has not been upgraded yet 
 PASS The defined flag of a custom element must be set when a custom element is successfully upgraded 
 PASS The defined flag of a custom element must be set if there is a matching definition 
 PASS The defined flag of a custom element created by HTML parser must be unset if there is no matching definition 

Modified: trunk/LayoutTests/fast/custom-elements/defined-pseudo-class.html (205059 => 205060)


--- trunk/LayoutTests/fast/custom-elements/defined-pseudo-class.html	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/LayoutTests/fast/custom-elements/defined-pseudo-class.html	2016-08-27 00:04:08 UTC (rev 205060)
@@ -16,7 +16,7 @@
 
 test(function () {
     assert_false(upgradeCandidate.matches(':defined'));
-}, 'The defined flag of a custom element must be set if a custom element has not been upgraded yet');
+}, 'The defined flag of a custom element must not be set if a custom element has not been upgraded yet');
 
 var matchInsideConstructor;
 class MyElement extends HTMLElement {

Modified: trunk/LayoutTests/fast/custom-elements/disconnected-callbacks-expected.txt (205059 => 205060)


--- trunk/LayoutTests/fast/custom-elements/disconnected-callbacks-expected.txt	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/LayoutTests/fast/custom-elements/disconnected-callbacks-expected.txt	2016-08-27 00:04:08 UTC (rev 205060)
@@ -1,10 +1,43 @@
 
 PASS Removing a custom element from a document must enqueue and invoke disconnectedCallback 
-PASS Removing a custom element from a detahed node must not enqueue and invoke connectedCallback 
-FAIL Removing a custom element from a window-less document must enqueue and invoke disconnectedCallback assert_array_equals: lengths differ, expected 2 got 0
-PASS Removing an ancestor of a custom element from a document must enqueue and invoke disconnectedCallback 
-FAIL Removing an ancestor of custom element from a a window-less document must enqueue and invoke disconnectedCallback assert_array_equals: lengths differ, expected 2 got 0
-PASS Removing a custom element from a connected shadow tree must enqueue and invoke disconnectedCallback 
-PASS Removing the shadow host of a shadow tree with a custom element from a document must enqueue and invoke disconnectedCallback 
-PASS Removing a custom element from a detached shadow tree must not enqueue and invoke disconnectedCallback 
+PASS Removing an ancestor of custom element from a document must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a shadow tree in a document must enqueue and invoke disconnectedCallback 
+PASS Removing the shadow host of a custom element from adocument must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a detached shadow tree that belongs to a document must not enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a document of a template element must enqueue and invoke disconnectedCallback 
+PASS Removing an ancestor of custom element from a document of a template element must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a shadow tree in a document of a template element must enqueue and invoke disconnectedCallback 
+PASS Removing the shadow host of a custom element from adocument of a template element must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a detached shadow tree that belongs to a document of a template element must not enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a new document must enqueue and invoke disconnectedCallback 
+PASS Removing an ancestor of custom element from a new document must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a shadow tree in a new document must enqueue and invoke disconnectedCallback 
+PASS Removing the shadow host of a custom element from anew document must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a detached shadow tree that belongs to a new document must not enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a cloned document must enqueue and invoke disconnectedCallback 
+PASS Removing an ancestor of custom element from a cloned document must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a shadow tree in a cloned document must enqueue and invoke disconnectedCallback 
+PASS Removing the shadow host of a custom element from acloned document must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a detached shadow tree that belongs to a cloned document must not enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a document created by createHTMLDocument must enqueue and invoke disconnectedCallback 
+PASS Removing an ancestor of custom element from a document created by createHTMLDocument must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a shadow tree in a document created by createHTMLDocument must enqueue and invoke disconnectedCallback 
+PASS Removing the shadow host of a custom element from adocument created by createHTMLDocument must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a detached shadow tree that belongs to a document created by createHTMLDocument must not enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a HTML document created by createDocument must enqueue and invoke disconnectedCallback 
+PASS Removing an ancestor of custom element from a HTML document created by createDocument must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a shadow tree in a HTML document created by createDocument must enqueue and invoke disconnectedCallback 
+PASS Removing the shadow host of a custom element from aHTML document created by createDocument must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a detached shadow tree that belongs to a HTML document created by createDocument must not enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a document in an iframe must enqueue and invoke disconnectedCallback 
+PASS Removing an ancestor of custom element from a document in an iframe must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a shadow tree in a document in an iframe must enqueue and invoke disconnectedCallback 
+PASS Removing the shadow host of a custom element from adocument in an iframe must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a detached shadow tree that belongs to a document in an iframe must not enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a HTML document fetched by XHR must enqueue and invoke disconnectedCallback 
+PASS Removing an ancestor of custom element from a HTML document fetched by XHR must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a shadow tree in a HTML document fetched by XHR must enqueue and invoke disconnectedCallback 
+PASS Removing the shadow host of a custom element from aHTML document fetched by XHR must enqueue and invoke disconnectedCallback 
+PASS Removing a custom element from a detached shadow tree that belongs to a HTML document fetched by XHR must not enqueue and invoke disconnectedCallback 
 
+

Modified: trunk/LayoutTests/fast/custom-elements/disconnected-callbacks.html (205059 => 205060)


--- trunk/LayoutTests/fast/custom-elements/disconnected-callbacks.html	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/LayoutTests/fast/custom-elements/disconnected-callbacks.html	2016-08-27 00:04:08 UTC (rev 205060)
@@ -7,6 +7,7 @@
 <link rel="help" href=""
 <script src=""
 <script src=""
+<script src=""
 <link rel='stylesheet' href=''>
 </head>
 <body>
@@ -20,94 +21,74 @@
 }
 customElements.define('my-custom-element', MyCustomElement);
 
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    document.body.appendChild(instance);
+DocumentTypes.forEach(function (entry) {
+    var documentName = entry.name;
+    var getDocument = entry.create;
 
-    calls = [];
-    document.body.removeChild(instance);
-    assert_array_equals(calls, ['disconnected', instance]);
-}, 'Removing a custom element from a document must enqueue and invoke disconnectedCallback');
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            var instance = document.createElement('my-custom-element');
+            doc.documentElement.appendChild(instance);
+            calls = [];
+            doc.documentElement.removeChild(instance);
+            assert_array_equals(calls, ['disconnected', instance]);
+        });
+    }, 'Removing a custom element from a ' + documentName + ' must enqueue and invoke disconnectedCallback');
 
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var parent = document.createElement('div');
-    parent.appendChild(instance);
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            var instance = document.createElement('my-custom-element');
+            var parent = document.createElement('div');
+            parent.appendChild(instance);
+            doc.documentElement.appendChild(parent);
+            calls = [];
+            doc.documentElement.removeChild(parent);
+            assert_array_equals(calls, ['disconnected', instance]);
+        });
+    }, 'Removing an ancestor of custom element from a ' + documentName + ' must enqueue and invoke disconnectedCallback');
 
-    calls = [];
-    parent.removeChild(instance);
-    assert_array_equals(calls, []);
-}, 'Removing a custom element from a detahed node must not enqueue and invoke connectedCallback');
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            var instance = document.createElement('my-custom-element');
+            var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+            var shadowRoot = host.attachShadow({mode: 'closed'});
+            doc.documentElement.appendChild(host);
+            shadowRoot.appendChild(instance);
 
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var documentWithoutWindow = document.implementation.createHTMLDocument();
-    documentWithoutWindow.body.appendChild(instance);
+            calls = [];
+            shadowRoot.removeChild(instance);
+            assert_array_equals(calls, ['disconnected', instance]);
+        });
+    }, 'Removing a custom element from a shadow tree in a ' + documentName + ' must enqueue and invoke disconnectedCallback');
 
-    calls = [];
-    documentWithoutWindow.body.removeChild(instance);
-    assert_array_equals(calls, ['disconnected', instance]);
-}, 'Removing a custom element from a window-less document must enqueue and invoke disconnectedCallback');
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            var instance = document.createElement('my-custom-element');
+            var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+            var shadowRoot = host.attachShadow({mode: 'closed'});
+            shadowRoot.appendChild(instance);
+            doc.documentElement.appendChild(host);
 
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var parent = document.createElement('div');
-    parent.appendChild(instance);
-    document.body.appendChild(parent);
+            calls = [];
+            doc.documentElement.removeChild(host);
+            assert_array_equals(calls, ['disconnected', instance]);
+        });
+    }, 'Removing the shadow host of a custom element from a' + documentName + ' must enqueue and invoke disconnectedCallback');
 
-    calls = [];
-    document.body.removeChild(parent);
-    assert_array_equals(calls, ['disconnected', instance]);
-}, 'Removing an ancestor of a custom element from a document must enqueue and invoke disconnectedCallback');
+    promise_test(function () {
+        return getDocument().then(function (doc) {
+            var instance = document.createElement('my-custom-element');
+            var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+            var shadowRoot = host.attachShadow({mode: 'closed'});
+            shadowRoot.appendChild(instance);
 
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var parent = document.createElement('div');
-    parent.appendChild(instance);
-    var documentWithoutWindow = document.implementation.createHTMLDocument();
-    documentWithoutWindow.body.appendChild(parent);
+            calls = [];
+            shadowRoot.removeChild(instance);
+            assert_array_equals(calls, []);
+        });
+    }, 'Removing a custom element from a detached shadow tree that belongs to a ' + documentName + ' must not enqueue and invoke disconnectedCallback');
+});
 
-    calls = [];
-    documentWithoutWindow.body.removeChild(parent);
-    assert_array_equals(calls, ['disconnected', instance]);
-}, 'Removing an ancestor of custom element from a a window-less document must enqueue and invoke disconnectedCallback');
-
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var host = document.createElement('div');
-    document.body.appendChild(host);
-    var shadowRoot = host.attachShadow({mode: 'closed'});
-    shadowRoot.appendChild(instance);
-
-    calls = [];
-    shadowRoot.removeChild(instance);
-    assert_array_equals(calls, ['disconnected', instance]);
-}, 'Removing a custom element from a connected shadow tree must enqueue and invoke disconnectedCallback');
-
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var host = document.createElement('div');
-    var shadowRoot = host.attachShadow({mode: 'closed'});
-    shadowRoot.appendChild(instance);
-    document.body.appendChild(host);
-
-    calls = [];
-    document.body.removeChild(host);
-    assert_array_equals(calls, ['disconnected', instance]);
-}, 'Removing the shadow host of a shadow tree with a custom element from a document must enqueue and invoke disconnectedCallback');
-
-test(function () {
-    var instance = document.createElement('my-custom-element');
-    var host = document.createElement('div');
-    var shadowRoot = host.attachShadow({mode: 'closed'});
-    shadowRoot.appendChild(instance);
-
-    calls = [];
-    shadowRoot.removeChild(instance);
-    assert_array_equals(calls, []);
-}, 'Removing a custom element from a detached shadow tree must not enqueue and invoke disconnectedCallback');
-
-
 </script>
 </body>
 </html>

Added: trunk/LayoutTests/fast/custom-elements/resources/document-types.js (0 => 205060)


--- trunk/LayoutTests/fast/custom-elements/resources/document-types.js	                        (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/resources/document-types.js	2016-08-27 00:04:08 UTC (rev 205060)
@@ -0,0 +1,74 @@
+const DocumentTypes = [
+    {
+        name: 'document',
+        create: function () { return Promise.resolve(document); }
+    },
+    {
+        name: 'document of a template element',
+        create: function () {
+            return new Promise(function (resolve) {
+                var template = document.createElementNS('http://www.w3.org/1999/xhtml', 'template');
+                var doc = template.content.ownerDocument;
+                if (!doc.documentElement)
+                    doc.appendChild(doc.createElement('html'));
+                resolve(doc);
+            });
+        }
+    },
+    {
+        name: 'new document',
+        create: function () {
+            return new Promise(function (resolve) {
+                var doc = new Document();
+                doc.appendChild(doc.createElement('html'));
+                resolve(doc);
+            });
+        }
+    },
+    {
+        name: 'cloned document',
+        create: function () {
+            return new Promise(function (resolve) {
+                var doc = document.cloneNode(false);
+                doc.appendChild(doc.createElement('html'));
+                resolve(doc);
+            });
+        }
+    },
+    {
+        name: 'document created by createHTMLDocument',
+        create: function () {
+            return Promise.resolve(document.implementation.createHTMLDocument());
+        }
+    },
+    {
+        name: 'HTML document created by createDocument',
+        create: function () {
+            return Promise.resolve(document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null));
+        }
+    },
+    {
+        name: 'document in an iframe',
+        create: function () {
+            return new Promise(function (resolve, reject) {
+                var iframe = document.createElement('iframe');
+                iframe._onload_ = function () { resolve(iframe.contentDocument); }
+                iframe._onerror_ = function () { reject('Failed to load an empty iframe'); }
+                document.body.appendChild(iframe);
+            });
+        }
+    },
+    {
+        name: 'HTML document fetched by XHR',
+        create: function () {
+            return new Promise(function (resolve, reject) {
+                var xhr = new XMLHttpRequest();
+                xhr.open('GET', 'resources/empty-html-document.html');
+                xhr.overrideMimeType('text/xml');
+                xhr._onload_ = function () { resolve(xhr.responseXML); }
+                xhr._onerror_ = function () { reject('Failed to fetch the document'); }
+                xhr.send();
+            });
+        }
+    }
+];
\ No newline at end of file

Added: trunk/LayoutTests/fast/custom-elements/resources/empty-html-document.html (0 => 205060)


--- trunk/LayoutTests/fast/custom-elements/resources/empty-html-document.html	                        (rev 0)
+++ trunk/LayoutTests/fast/custom-elements/resources/empty-html-document.html	2016-08-27 00:04:08 UTC (rev 205060)
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<body>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (205059 => 205060)


--- trunk/Source/WebCore/ChangeLog	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/Source/WebCore/ChangeLog	2016-08-27 00:04:08 UTC (rev 205060)
@@ -1,3 +1,38 @@
+2016-08-23  Ryosuke Niwa  <[email protected]>
+
+        Adopted custom element's callbacks should continue to work
+        https://bugs.webkit.org/show_bug.cgi?id=161065
+
+        Reviewed by Andreas Kling.
+
+        When a custom element is adopted into another document, its reaction callbacks need to continue to work.
+        Because a different document may have its own global object, each custom element needs to remember its
+        original global object or JSCustomElementInterface. This patch adds the latter to the element rare data.
+
+        Tests: fast/custom-elements/connected-callbacks.html
+               fast/custom-elements/disconnected-callbacks.html
+
+        * bindings/js/JSCustomElementInterface.cpp:
+        (WebCore::JSCustomElementInterface::constructElement):
+        (WebCore::JSCustomElementInterface::upgradeElement):
+        * dom/CustomElementReactionQueue.cpp:
+        (WebCore::findInterfaceForCustomElement): Deleted.
+        (WebCore::CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded):
+        (WebCore::CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded):
+        (WebCore::CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded):
+        * dom/Element.cpp:
+        (WebCore::Element::insertedInto): Invoke callbacks even when the current document is not a HTML document.
+        (WebCore::Element::removedFrom): Ditto.
+        (WebCore::Element::setCustomElementIsResolved): Moved from Node. Add the element interface to the rare data.
+        (WebCore::Element::customElementInterface): Added.
+        * dom/Element.h:
+        * dom/ElementRareData.cpp:
+        * dom/ElementRareData.h:
+        (WebCore::ElementRareData::customElementInterface): Added.
+        (WebCore::ElementRareData::setCustomElementInterface): Added.
+        * dom/Node.h:
+        ((WebCore::Node::setCustomElementIsResolved): Deleted.
+
 2016-08-26  Zalan Bujtas  <[email protected]>
 
         ASSERT_NOT_REACHED() is touched in WebCore::minimumValueForLength

Modified: trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp (205059 => 205060)


--- trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp	2016-08-27 00:04:08 UTC (rev 205060)
@@ -98,7 +98,7 @@
     Element* wrappedElement = JSElement::toWrapped(newElement);
     if (!wrappedElement)
         return nullptr;
-    wrappedElement->setCustomElementIsResolved();
+    wrappedElement->setCustomElementIsResolved(*this);
     return wrappedElement;
 }
 
@@ -147,8 +147,7 @@
         throwInvalidStateError(*state, "Custom element constructor failed to upgrade an element");
         return;
     }
-    wrappedElement->setCustomElementIsResolved();
-    ASSERT(wrappedElement->isCustomElement());
+    wrappedElement->setCustomElementIsResolved(*this);
 }
 
 void JSCustomElementInterface::invokeCallback(Element& element, JSObject* callback, const WTF::Function<void(ExecState*, MarkedArgumentBuffer&)>& addArguments)

Modified: trunk/Source/WebCore/dom/CustomElementReactionQueue.cpp (205059 => 205060)


--- trunk/Source/WebCore/dom/CustomElementReactionQueue.cpp	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/Source/WebCore/dom/CustomElementReactionQueue.cpp	2016-08-27 00:04:08 UTC (rev 205060)
@@ -106,23 +106,9 @@
         queue->m_items.append({CustomElementReactionQueueItem::Type::ElementUpgrade, element, elementInterface});
 }
 
-static JSCustomElementInterface* findInterfaceForCustomElement(Element& element)
-{
-    ASSERT(element.isCustomElement());
-    auto* window = element.document().domWindow();
-    if (!window)
-        return nullptr;
-
-    auto* registry = window->customElementRegistry();
-    if (!registry)
-        return nullptr;
-
-    return registry->findInterface(element.tagQName());
-}
-
 void CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded(Element& element)
 {
-    auto* elementInterface = findInterfaceForCustomElement(element);
+    auto* elementInterface = element.customElementInterface();
     if (!elementInterface)
         return;
 
@@ -132,7 +118,7 @@
 
 void CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded(Element& element)
 {
-    auto* elementInterface = findInterfaceForCustomElement(element);
+    auto* elementInterface = element.customElementInterface();
     if (!elementInterface)
         return;
 
@@ -142,7 +128,7 @@
 
 void CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)
 {
-    auto* elementInterface = findInterfaceForCustomElement(element);
+    auto* elementInterface = element.customElementInterface();
     if (!elementInterface || !elementInterface->observesAttribute(attributeName.localName()))
         return;
 

Modified: trunk/Source/WebCore/dom/Element.cpp (205059 => 205060)


--- trunk/Source/WebCore/dom/Element.cpp	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/Source/WebCore/dom/Element.cpp	2016-08-27 00:04:08 UTC (rev 205060)
@@ -1574,7 +1574,8 @@
     // This element is new to the shadow tree (and its tree scope) only if the parent into which this element
     // or its ancestor is inserted belongs to the same tree scope as this element's.
     TreeScope* newScope = &insertionPoint.treeScope();
-    HTMLDocument* newDocument = !wasInDocument && inDocument() && is<HTMLDocument>(newScope->documentScope()) ? &downcast<HTMLDocument>(newScope->documentScope()) : nullptr;
+    bool becomeConnected = !wasInDocument && inDocument();
+    HTMLDocument* newDocument = becomeConnected && is<HTMLDocument>(newScope->documentScope()) ? &downcast<HTMLDocument>(newScope->documentScope()) : nullptr;
     if (newScope != &treeScope())
         newScope = nullptr;
 
@@ -1600,7 +1601,7 @@
     }
 
 #if ENABLE(CUSTOM_ELEMENTS)
-    if (newDocument && UNLIKELY(isCustomElement()))
+    if (becomeConnected && UNLIKELY(isCustomElement()))
         CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded(*this);
 #endif
 
@@ -1622,7 +1623,8 @@
 
     if (insertionPoint.isInTreeScope()) {
         TreeScope* oldScope = &insertionPoint.treeScope();
-        HTMLDocument* oldDocument = inDocument() && is<HTMLDocument>(oldScope->documentScope()) ? &downcast<HTMLDocument>(oldScope->documentScope()) : nullptr;
+        bool becomeDisconnected = inDocument();
+        HTMLDocument* oldDocument = becomeDisconnected && is<HTMLDocument>(oldScope->documentScope()) ? &downcast<HTMLDocument>(oldScope->documentScope()) : nullptr;
 
         // ContainerNode::removeBetween always sets the removed chid's tree scope to Document's but InTreeScope flag is unset in Node::removedFrom.
         // So this element has been removed from the old tree scope only if InTreeScope flag is set and this element's tree scope is Document's.
@@ -1651,7 +1653,7 @@
         }
 
 #if ENABLE(CUSTOM_ELEMENTS)
-        if (oldDocument && UNLIKELY(isCustomElement()))
+        if (becomeDisconnected && UNLIKELY(isCustomElement()))
             CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded(*this);
 #endif
     }
@@ -1821,6 +1823,27 @@
     return *shadowRoot;
 }
 
+    
+#if ENABLE(CUSTOM_ELEMENTS)
+
+void Element::setCustomElementIsResolved(JSCustomElementInterface& elementInterface)
+{
+    clearFlag(IsEditingTextOrUnresolvedCustomElementFlag);
+    setFlag(IsCustomElement);
+    ensureElementRareData().setCustomElementInterface(elementInterface);
+}
+
+JSCustomElementInterface* Element::customElementInterface() const
+{
+    ASSERT(isCustomElement());
+    if (!hasRareData())
+        return nullptr;
+    return elementRareData()->customElementInterface();
+}
+
+#endif
+
+
 const AtomicString& Element::shadowPseudoId() const
 {
     return pseudo();

Modified: trunk/Source/WebCore/dom/Element.h (205059 => 205060)


--- trunk/Source/WebCore/dom/Element.h	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/Source/WebCore/dom/Element.h	2016-08-27 00:04:08 UTC (rev 205060)
@@ -44,6 +44,7 @@
 class ElementRareData;
 class HTMLDocument;
 class IntSize;
+class JSCustomElementInterface;
 class KeyboardEvent;
 class Locale;
 class PlatformKeyboardEvent;
@@ -272,6 +273,11 @@
     ShadowRoot* userAgentShadowRoot() const;
     WEBCORE_EXPORT ShadowRoot& ensureUserAgentShadowRoot();
 
+#if ENABLE(CUSTOM_ELEMENTS)
+    void setCustomElementIsResolved(JSCustomElementInterface&);
+    JSCustomElementInterface* customElementInterface() const;
+#endif
+
     // FIXME: this should not be virtual, do not override this.
     virtual const AtomicString& shadowPseudoId() const;
 

Modified: trunk/Source/WebCore/dom/ElementRareData.cpp (205059 => 205060)


--- trunk/Source/WebCore/dom/ElementRareData.cpp	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/Source/WebCore/dom/ElementRareData.cpp	2016-08-27 00:04:08 UTC (rev 205060)
@@ -44,7 +44,11 @@
     RegionOversetState regionOversetState;
     LayoutSize sizeForResizing;
     IntPoint savedLayerScrollPosition;
+#if ENABLE(CUSTOM_ELEMENTS)
+    void* pointers[8];
+#else
     void* pointers[7];
+#endif
 };
 
 static_assert(sizeof(ElementRareData) == sizeof(SameSizeAsElementRareData), "ElementRareData should stay small");

Modified: trunk/Source/WebCore/dom/ElementRareData.h (205059 => 205060)


--- trunk/Source/WebCore/dom/ElementRareData.h	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/Source/WebCore/dom/ElementRareData.h	2016-08-27 00:04:08 UTC (rev 205060)
@@ -24,6 +24,7 @@
 
 #include "DOMTokenList.h"
 #include "DatasetDOMStringMap.h"
+#include "JSCustomElementInterface.h"
 #include "NamedNodeMap.h"
 #include "NodeRareData.h"
 #include "PseudoElement.h"
@@ -93,6 +94,11 @@
     ShadowRoot* shadowRoot() const { return m_shadowRoot.get(); }
     void setShadowRoot(RefPtr<ShadowRoot>&& shadowRoot) { m_shadowRoot = WTFMove(shadowRoot); }
 
+#if ENABLE(CUSTOM_ELEMENTS)
+    JSCustomElementInterface* customElementInterface() { return m_customElementInterface.get(); }
+    void setCustomElementInterface(JSCustomElementInterface& customElementInterface) { m_customElementInterface = &customElementInterface; }
+#endif
+
     NamedNodeMap* attributeMap() const { return m_attributeMap.get(); }
     void setAttributeMap(std::unique_ptr<NamedNodeMap> attributeMap) { m_attributeMap = WTFMove(attributeMap); }
 
@@ -149,6 +155,9 @@
     std::unique_ptr<DatasetDOMStringMap> m_dataset;
     std::unique_ptr<DOMTokenList> m_classList;
     RefPtr<ShadowRoot> m_shadowRoot;
+#if ENABLE(CUSTOM_ELEMENTS)
+    RefPtr<JSCustomElementInterface> m_customElementInterface;
+#endif
     std::unique_ptr<NamedNodeMap> m_attributeMap;
 
     RefPtr<PseudoElement> m_beforePseudoElement;

Modified: trunk/Source/WebCore/dom/Node.h (205059 => 205060)


--- trunk/Source/WebCore/dom/Node.h	2016-08-26 23:47:40 UTC (rev 205059)
+++ trunk/Source/WebCore/dom/Node.h	2016-08-27 00:04:08 UTC (rev 205060)
@@ -267,7 +267,6 @@
 
     bool isUnresolvedCustomElement() const { return isElementNode() && getFlag(IsEditingTextOrUnresolvedCustomElementFlag); }
     void setIsUnresolvedCustomElement() { setFlag(IsEditingTextOrUnresolvedCustomElementFlag); }
-    void setCustomElementIsResolved();
 #endif
 
     // Returns null, a child of ShadowRoot, or a legacy shadow root.
@@ -769,16 +768,6 @@
     return parentNode();
 }
 
-#if ENABLE(CUSTOM_ELEMENTS)
-
-inline void Node::setCustomElementIsResolved()
-{
-    clearFlag(IsEditingTextOrUnresolvedCustomElementFlag);
-    setFlag(IsCustomElement);
-}
-
-#endif
-
 } // namespace WebCore
 
 #if ENABLE(TREE_DEBUGGING)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to