summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle')
-rw-r--r--testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/enqueuing-and-invoking-callbacks/invoke-callbacks.html60
-rw-r--r--testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attached-callback-move-element-test.html159
-rw-r--r--testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attached-callback-test.html117
-rw-r--r--testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attribute-changed-callback-change-attribute-test.html229
-rw-r--r--testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attribute-changed-callback-remove-attribute-test.html166
-rw-r--r--testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attribute-changed-callback-set-attribute-test.html338
-rw-r--r--testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/created-callback-element-prototype-test.html87
-rw-r--r--testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/created-callback-invocation-order-test.html242
-rw-r--r--testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/created-callback-invocation-test.html167
-rw-r--r--testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/detached-callback-move-element-test.html130
-rw-r--r--testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/detached-callback-no-browsing-context-test.html147
-rw-r--r--testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/detached-callback-with-browsing-context-test.html85
12 files changed, 1927 insertions, 0 deletions
diff --git a/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/enqueuing-and-invoking-callbacks/invoke-callbacks.html b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/enqueuing-and-invoking-callbacks/invoke-callbacks.html
new file mode 100644
index 000000000..97fb73560
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/enqueuing-and-invoking-callbacks/invoke-callbacks.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Invoke CALLBACK with ELEMENT as callback this value</title>
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="Invoke CALLBACK with ELEMENT as callback this value">
+<link rel="help" href="http://www.w3.org/TR/custom-elements/#enqueuing-and-invoking-callbacks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto});
+ var customElement = new GeneratedConstructor();
+
+ doc.body.innerHTML = '<x-a id="x-a"></x-a>';
+ assert_equals(doc.querySelector('#x-a'), proto.createdCallbackThis,
+ '\'this\' value of the created callback should be the custom element');
+}, 'Test \'this\' value inside created callback.');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+
+ assert_equals(doc.querySelector('#x-element'), proto.attachedCallbackThis,
+ '\'this\' value of the attached callback should be the custom element');
+}, 'Test \'this\' value inside attached callback.');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+
+ var customElement = doc.querySelector('#x-element');
+ doc.body.removeChild(customElement);
+ assert_equals(customElement, proto.detachedCallbackThis,
+ '\'this\' value of the detached callback should be the custom element');
+}, 'Test \'this\' value inside detached callback.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-b', {prototype: proto});
+ var customElement = new GeneratedConstructor();
+
+ customElement.setAttribute('class', 'someClass');
+ assert_equals(customElement, proto.attributeChangedCallbackThis,
+ '\'this\' value of the attributeChanged callback should be the custom element');
+}, 'Test \'this\' value inside attributeChanged callback.');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attached-callback-move-element-test.html b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attached-callback-move-element-test.html
new file mode 100644
index 000000000..df4df5535
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attached-callback-move-element-test.html
@@ -0,0 +1,159 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Attached callback of a custom element should be called if element is moved</title>
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="attached callback ... must be enqueued whenever custom element is inserted into a document and this document has a browsing context.">
+<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-a', {prototype: proto});
+ var customElement = doc.createElement('x-a');
+
+ doc.body.appendChild(customElement);
+ assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached ' +
+ 'should not be called in documents that do not have a browsing context');
+
+ var divElement = doc.createElement('div');
+ doc.body.appendChild(divElement);
+ divElement.appendChild(customElement);
+ assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached ' +
+ 'should not be called in documents that do not have a browsing context');
+}, 'Test attached callback if moving custom element inside document ' +
+ 'without browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(docWithBrowsingContext) {
+ var docNoBrowsingContext = newHTMLDocument();
+ var proto1 = newHTMLElementPrototype();
+ docNoBrowsingContext.registerElement('x-b', {prototype: proto1});
+
+ var customElement = docNoBrowsingContext.createElement('x-b');
+ docNoBrowsingContext.body.appendChild(customElement);
+ assert_equals(proto1.attachedCallbackCalledCounter, 0,
+ 'Callback attached should not be called ' +
+ 'in documents that do not have a browsing context');
+
+ var proto2 = newHTMLElementPrototype();
+ docWithBrowsingContext.registerElement('x-b', {prototype: proto2});
+ docWithBrowsingContext.body.appendChild(customElement);
+ assert_equals(proto1.attachedCallbackCalledCounter, 1,
+ 'Callback attached should be called in documents with browsing context');
+ assert_equals(proto2.attachedCallbackCalledCounter, 0,
+ 'Callback attached, defined in receiving document, should not be called');
+}, 'Test attached callback if moving custom element from ' +
+ 'document without browsing context to document with browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(docWithBrowsingContext) {
+ var proto1 = newHTMLElementPrototype();
+ docWithBrowsingContext.registerElement('x-c', {prototype: proto1});
+
+ var customElement = docWithBrowsingContext.createElement('x-c');
+ docWithBrowsingContext.body.appendChild(customElement);
+ assert_equals(proto1.attachedCallbackCalledCounter, 1,
+ 'Callback attached should be called in documents with browsing context');
+
+ var docNoBrowsingContext = newHTMLDocument();
+ var proto2 = newHTMLElementPrototype();
+ docNoBrowsingContext.registerElement('x-c', {prototype: proto2});
+ docNoBrowsingContext.body.appendChild(customElement);
+ assert_equals(proto1.attachedCallbackCalledCounter, 1, 'Callback attached should not be called ' +
+ 'in documents that do not have a browsing context');
+ assert_equals(proto2.attachedCallbackCalledCounter, 0,
+ 'Callback attached, defined in receiving document, should not be called');
+}, 'Test attached callback if moving custom element from ' +
+ 'document with browsing context to document without browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-d', {prototype: proto});
+
+ var customElement = doc.createElement('x-d');
+ doc.body.appendChild(customElement);
+ assert_equals(proto.attachedCallbackCalledCounter, 1,
+ 'Callback attached should be called in documents with browsing context');
+
+ var divElement = doc.createElement('div');
+ doc.body.appendChild(divElement);
+ divElement.appendChild(customElement);
+ assert_equals(proto.attachedCallbackCalledCounter, 2,
+ 'Callback attached should be called in documents with browsing context');
+}, 'Test attached callback if moving custom element inside document ' +
+ 'with browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-e', {prototype: proto});
+
+ var customElement = doc.createElement('x-e');
+ doc.body.appendChild(customElement);
+ assert_equals(proto.attachedCallbackCalledCounter, 1,
+ 'Callback attached should be called in documents with browsing context');
+
+ var divElement = doc.createElement('div');
+ divElement.appendChild(customElement);
+ assert_equals(proto.attachedCallbackCalledCounter, 1,
+ 'Callback attached should not be called if element is not appended to the document');
+
+ doc.body.appendChild(divElement);
+ assert_equals(proto.attachedCallbackCalledCounter, 2,
+ 'Callback attached should be called in documents with browsing context');
+}, 'Test attached callback if indirectly moving custom element inside document ' +
+ 'with browsing context');
+
+
+var moveTest = async_test('Test attached callback if moving custom element from ' +
+ 'document with browsing context to document with browsing context');
+
+moveTest.step(function() {
+ var iframe1 = newIFrame('../../resources/blank.html');
+ iframe1.onload = moveTest.step_func(function() {
+ var doc1 = iframe1.contentDocument;
+
+ // register custom element type
+ var proto1 = newHTMLElementPrototype();
+ doc1.registerElement('x-f', {prototype: proto1});
+
+ // create custom element
+ var customElement = doc1.createElement('x-f');
+ doc1.body.appendChild(customElement);
+ assert_equals(proto1.attachedCallbackCalledCounter, 1,
+ 'Callback attached should be called in documents with browsing context');
+
+ // create second iframe
+ var iframe2 = newIFrame('../../resources/x-element.html');
+ iframe2.onload = moveTest.step_func(function() {
+ var doc2 = iframe2.contentDocument;
+
+ // register custom element type
+ var proto2 = newHTMLElementPrototype();
+ doc2.registerElement('x-f', {prototype: proto2});
+
+ // move element
+ doc2.body.appendChild(customElement);
+ assert_equals(proto1.attachedCallbackCalledCounter, 2,
+ 'Callback attached should be called in documents with browsing context');
+ assert_equals(proto2.attachedCallbackCalledCounter, 0,
+ 'Callback attached, defined in receiving document, should not be called');
+
+ // test clean up
+ iframe1.remove();
+ iframe2.remove();
+ moveTest.done();
+ });
+ });
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attached-callback-test.html b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attached-callback-test.html
new file mode 100644
index 000000000..a4cc0b746
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attached-callback-test.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Attached callback of a custom element should be called </title>
+<meta name="timeout" content="long">
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="attached callback ... must be enqueued whenever custom element is inserted into a document and this document has a browsing context.">
+<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto});
+ var customElement = new GeneratedConstructor();
+ assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached ' +
+ 'should not be called in documents that do not have a browsing context');
+}, 'Test attached callback if custom element is instantiated via constructor. ' +
+ 'Document has no browsing context');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-b', {prototype: proto});
+ doc.body.innerHTML = '<x-b></x-b>';
+ assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached ' +
+ 'should not be called in documents that do not have a browsing context');
+}, 'Test attached callback if custom element is created via innerHTML property. ' +
+ 'Document has no browsing context');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<x-c></x-c>';
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-c', {prototype: proto});
+ assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached ' +
+ 'should not be called in documents that do not have a browsing context');
+}, 'Test attached callback if custom element is created via innerHTML property before ' +
+ 'registration. Document has no browsing context');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<x-d id="x-d"></x-d>';
+ var customElement = doc.querySelector('#x-d');
+
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-d', {prototype: proto});
+
+ customElement.constructor.prototype = proto;
+ assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached should ' +
+ 'not be called for unregistered custom element in document without browsing context');
+}, 'Test attached callback if custom element is unregistered');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ assert_equals(proto.attachedCallbackCalledCounter, 1, 'Callback attached should be ' +
+ 'called in documents with browsing context');
+}, 'Test attached callback. Document has browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+
+ var x = doc.createElement('x-element');
+ assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached should not ' +
+ 'be called before element is added to document with browsing context');
+
+ doc.body.appendChild(x);
+ assert_equals(proto.attachedCallbackCalledCounter, 1, 'Callback attached should be called ' +
+ 'in documents with browsing context');
+}, 'Test attached callback. Registered element is created via document.createElement(). ' +
+ 'Document has browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ var x = doc.createElement('x-element');
+ doc.body.appendChild(x);
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ assert_equals(proto.attachedCallbackCalledCounter, 1, 'Callback attached should ' +
+ 'be called in documents with browsing context');
+}, 'Test attached callback. Unregistered element is created via document.createElement(). ' +
+ 'Document has browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ doc.body.innerHTML = '<x-element></x-element>';
+ assert_equals(proto.attachedCallbackCalledCounter, 1, 'Callback attached should ' +
+ 'be called in documents with browsing context');
+}, 'Test attached callback. Registered element is created via innerHTML property. ' +
+ 'Document has browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ doc.body.innerHTML = '<x-element></x-element>';
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ assert_equals(proto.attachedCallbackCalledCounter, 1, 'Callback attached should ' +
+ 'be called in documents with browsing context');
+}, 'Test attached callback. Unresolved element is created via innerHTML property. ' +
+ 'Document has browsing context');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attribute-changed-callback-change-attribute-test.html b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attribute-changed-callback-change-attribute-test.html
new file mode 100644
index 000000000..cd5f3a4b8
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attribute-changed-callback-change-attribute-test.html
@@ -0,0 +1,229 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test attributeChanged callback is called if custom element attribute value is changed</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="attributeChanged callback must be enqueued whenever custom element's attribute is added, changed or removed">
+<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto});
+ var customElement = new GeneratedConstructor();
+
+ customElement.setAttribute('class', 'someClass');
+ proto.attributeChangedCallbackCalledCounter = 0;
+ customElement.setAttribute('class', 'someClass2');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is called if attribute value is changed by method ' +
+ 'setAttribute(). The custom element is created via constructor');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-b', {prototype: proto});
+
+ doc.body.innerHTML = '<x-b id="x-b" class="oldValue"></x-b>';
+ var customElement = doc.querySelector('#x-b');
+ customElement.setAttribute('class', 'newValue');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+ assert_array_equals(proto.attributeChangedCallbackArgs,
+ ['class', 'oldValue', 'newValue'],
+ 'Unexpected callback attributeChanged arguments');
+}, 'Test attributeChanged callback arguments if attribute value is changed by method ' +
+ 'setAttribute(). The custom element is created via innerHTML property');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-c', {prototype: proto});
+ var customElement = new GeneratedConstructor();
+
+ customElement.setAttribute('class', 'someClass');
+ proto.attributeChangedCallbackCalledCounter = 0;
+ customElement.classList.add('someClass3');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is called if attribute value is changed by method ' +
+ 'classList.add(). The custom element is created via constructor');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-d', {prototype: proto});
+
+ doc.body.innerHTML = '<x-d id="x-d" class="oldValue"></x-d>';
+ var customElement = doc.querySelector('#x-d');
+ customElement.classList.add('newestValue');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+ assert_array_equals(proto.attributeChangedCallbackArgs,
+ ['class', 'oldValue', 'oldValue newestValue'],
+ 'Unexpected callback attributeChanged arguments');
+}, 'Test attributeChanged callback arguments if attribute value is changed by method ' +
+ 'classList.add(). The custom element is created via innerHTML property');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-e', {prototype: proto});
+ var customElement = new GeneratedConstructor();
+
+ customElement.setAttribute('class', 'someClass');
+ proto.attributeChangedCallbackCalledCounter = 0;
+ customElement.id = 'someId';
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is called if attribute value is changed as property. ' +
+ 'The custom element is created via constructor');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-f', {prototype: proto});
+
+ doc.body.innerHTML = '<x-f id="x-f" class="oldValue"></x-f>';
+ var customElement = doc.querySelector('#x-f');
+ customElement.className = 'lastValue';
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+ assert_array_equals(proto.attributeChangedCallbackArgs,
+ ['class', 'oldValue', 'lastValue'],
+ 'Unexpected callback attributeChanged arguments');
+}, 'Test attributeChanged callback arguments if attribute value is changed as property. ' +
+ 'The custom element is created via innerHTML property');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-g', {prototype: proto});
+ var customElement = new GeneratedConstructor();
+
+ customElement.setAttribute('class', 'someClass someSuperClass');
+ proto.attributeChangedCallbackCalledCounter = 0;
+ customElement.classList.toggle('someClass');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is called if attribute value is changed by classList.toggle(). ' +
+ 'The custom element is created via constructor');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-h', {prototype: proto});
+
+ doc.body.innerHTML = '<x-h id="x-h" class="oldValue lastValue"></x-h>';
+ var customElement = doc.querySelector('#x-h');
+ customElement.classList.toggle('lastValue');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+ assert_array_equals(proto.attributeChangedCallbackArgs,
+ ['class', 'oldValue lastValue', 'oldValue'],
+ 'Unexpected callback attributeChanged arguments');
+}, 'Test attributeChanged callback arguments if attribute value is changed by classList.toggle(). ' +
+ 'The custom element is created via innerHTML property');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<x-i id="x-i" class="oldValue"></x-i>';
+ var customElement = doc.querySelector('#x-i');
+ customElement.setAttribute('class', 'newValue');
+ customElement.setAttribute('class', 'newestValue');
+
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-i', {prototype: proto});
+
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 0,
+ 'Callback attributeChanged should not be called');
+
+ customElement.setAttribute('class', 'rightValue');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should not be called');
+ assert_array_equals(proto.attributeChangedCallbackArgs,
+ ['class', 'newestValue', 'rightValue'],
+ 'Unexpected callback attributeChanged arguments');
+}, 'Test attributeChanged callback is not called if custom element is not registered');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+
+ var customElement = doc.querySelector('#x-element');
+ customElement.setAttribute('class', 'firstValue');
+ customElement.setAttribute('class', 'secondValue');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 2, 'Callback ' +
+ 'attributeChanged should be called after call to setAttribute()');
+
+ customElement.classList.add('someClass3');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 3, 'Callback ' +
+ 'attributeChanged should be called after call to classList.add()');
+
+ customElement.id = 'someId';
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 4, 'Callback ' +
+ 'attributeChanged should be called after changing attribute as property');
+}, 'Test attributeChanged callback is called if attribute value is changed. ' +
+ 'The document has browsing context');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ var customElement = doc.querySelector('#x-element');
+
+ customElement.setAttribute('class', 'firstValue');
+ customElement.setAttribute('class', 'secondValue');
+ assert_array_equals(proto.attributeChangedCallbackArgs,
+ ['class', 'firstValue', 'secondValue'],
+ 'Unexpected callback attributeChanged arguments after call to setAttribute()');
+
+ customElement.classList.add('newestValue');
+ assert_array_equals(proto.attributeChangedCallbackArgs,
+ ['class', 'secondValue', 'secondValue newestValue'],
+ 'Unexpected callback attributeChanged arguments after call to classList.add()');
+
+ customElement.className = 'lastValue';
+ assert_array_equals(proto.attributeChangedCallbackArgs,
+ ['class', 'secondValue newestValue', 'lastValue'],
+ 'Unexpected callback attributeChanged arguments after changing attribute as property');
+}, 'Test attributeChanged callback arguments if attribute value is changed. ' +
+ 'The document has browsing context');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var customElement = doc.querySelector('#x-element');
+ customElement.setAttribute('class', 'firstValue');
+ customElement.setAttribute('class', 'secondValue');
+
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ customElement.setAttribute('class', 'thirdValue');
+
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+ assert_array_equals(proto.attributeChangedCallbackArgs,
+ ['class', 'secondValue', 'thirdValue'],
+ 'Unexpected callback attributeChanged arguments after setAttribute() call');
+}, 'Test attributeChanged callback is not called if custom element is not registered. ' +
+ 'The document has browsing context');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attribute-changed-callback-remove-attribute-test.html b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attribute-changed-callback-remove-attribute-test.html
new file mode 100644
index 000000000..fb451b074
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attribute-changed-callback-remove-attribute-test.html
@@ -0,0 +1,166 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test attribute removing to check attributeChanged callback of a custom element</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="attributeChanged callback must be enqueued whenever custom element's attribute is removed">
+<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto});
+
+ var customElement = new GeneratedConstructor();
+ //attributeChangedCallback should be called the first time here
+ customElement.setAttribute('class', 'someClass');
+ //attributeChangedCallback should be called the second time here
+ customElement.removeAttribute('class');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 2, 'Callback attributeChanged should be called ' +
+ 'after setAttribute() and removeAttribute() calls');
+}, 'Test attributeChanged callback if attribute is removed. ' +
+ 'The custom element created via constructor');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ HTML5_ELEMENTS.forEach(function(element) {
+ var obj = doc.createElement(element);
+ var proto = newCustomElementPrototype(obj.constructor.prototype);
+ var GeneratedConstructor = doc.registerElement('x-' + element + '-' + element + '-1', {
+ prototype: proto,
+ extends: element
+ });
+
+ var customElement = new GeneratedConstructor();
+ //attributeChangedCallback should be called the first time here
+ customElement.setAttribute('class', 'someClass');
+ //attributeChangedCallback should be called the second time here
+ customElement.removeAttribute('class');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 2,
+ 'Callback attributeChanged should be called ' +
+ 'after setAttribute() and removeAttribute() calls for "' + element + '"');
+ });
+}, 'Test attributeChanged callback if attribute is removed. ' +
+ 'The custom element created via constructor and extends HTML element');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-b', {prototype: proto});
+
+ doc.body.innerHTML = '<x-b id="x-b" class="theClass"></x-b>';
+ var customElement = doc.querySelector('#x-b');
+ customElement.removeAttribute('class');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called ' +
+ 'after removeAttribute() call');
+}, 'Test attributeChanged callback if attribute is removed. ' +
+ 'The custom element created via innerHTML property');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-c', {prototype: proto});
+
+ doc.body.innerHTML = '<x-c id="x-c" class="theClass"></x-c>';
+ var customElement = doc.querySelector('#x-c');
+ customElement.removeAttribute('class');
+ assert_equals(proto.attributeChangedCallbackArgs[2], null,
+ 'Removing an attribute should invoke ' +
+ 'the attributeChanged callback with a null new value');
+ assert_array_equals(proto.attributeChangedCallbackArgs,
+ ['class', 'theClass', null],
+ 'Unexpected attributeChanged callback arguments');
+}, 'Test attributeChanged callback arguments if attribute is removed. ' +
+ 'The custom element created via innerHTML property');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+
+ doc.body.innerHTML = '<x-d id="x-d" class="theClass"></x-d>';
+ var customElement = doc.querySelector('#x-d');
+ // this should not call or enqueue attributeChangedCallback
+ customElement.setAttribute('class', 'someClass');
+ // this one should not too
+ customElement.removeAttribute('class');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 0,
+ 'Callback attributeChanged should not be called');
+
+ doc.registerElement('x-d', {prototype: proto});
+ // this call invokes attributeChangedCallback
+ customElement.setAttribute('name', 'someName');
+ // and this one
+ customElement.removeAttribute('name');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 2,
+ 'Callback attributeChanged should be called ' +
+ 'after setAttribute() and removeAttribute() calls');
+}, 'Test attributeChanged callback is not called if attribute is removed. ' +
+ 'The custom element created via innerHTML property and unresolved at first');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+
+ var customElement = doc.querySelector('#x-element');
+ customElement.setAttribute('class', 'someClass');
+ customElement.removeAttribute('class');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 2,
+ 'Callback attributeChanged should be called ' +
+ 'after setAttribute() and removeAttribute() calls');
+}, 'Test attributeChanged callback is called if attribute is removed. ' +
+ 'The custom element created via constructor and the document has browsing context');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+
+ doc.body.innerHTML = '<x-element id="x-element" class="theClass"></x-element>';
+ var customElement = doc.querySelector('#x-element');
+ customElement.removeAttribute('class');
+ assert_equals(proto.attributeChangedCallbackArgs[2], null,
+ 'Removing an attribute should invoke ' +
+ 'the attributeChanged callback with a null new value');
+ assert_array_equals(proto.attributeChangedCallbackArgs,
+ ['class', 'theClass', null],
+ 'Unexpected attributeChanged callback arguments');
+}, 'Test attributeChanged callback arguments if attribute is removed. ' +
+ 'The custom element created via innerHTML property and the document has browsing context');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var customElement = doc.querySelector('#x-element');
+ // this should not call or enqueue attributeChangedCallback
+ customElement.setAttribute('name', 'someName');
+ // this one too
+ customElement.removeAttribute('name');
+
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 0,
+ 'Callback attributeChanged should not be called');
+ // this call invokes attributeChangedCallback
+ customElement.setAttribute('class', 'someClass');
+ // this call invokes attributeChangedCallback at second time
+ customElement.removeAttribute('class');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 2,
+ 'Callback attributeChanged should be called ' +
+ 'after setAttribute() and removeAttribute() calls');
+}, 'Test attributeChanged callback if attribute is removed. ' +
+ 'The custom element created via innerHTML property and unresolved at first. ' +
+ 'The document has browsing context');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attribute-changed-callback-set-attribute-test.html b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attribute-changed-callback-set-attribute-test.html
new file mode 100644
index 000000000..1c4ab8618
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/attribute-changed-callback-set-attribute-test.html
@@ -0,0 +1,338 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test attributeChanged callback is called if custom element attribute value is set</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="attributeChanged callback ... must be enqueued whenever custom element's attribute is added, changed or removed.">
+<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto});
+
+ var customElement = new GeneratedConstructor();
+ customElement.setAttribute('class', 'someClass');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is called if attribute value is set by method ' +
+ 'setAttribute(). The custom element is created via constructor.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-b', {prototype: proto});
+
+ var customElement = new GeneratedConstructor();
+ customElement.setAttribute('class', 'someClass');
+ assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
+ 'Unexpected callback attributeChanged arguments');
+}, 'Test attributeChanged callback arguments if attribute value is set by method ' +
+ 'setAttribute(). The custom element is created via constructor.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-c', {prototype: proto});
+
+ var customElement = new GeneratedConstructor();
+ customElement.classList.add('someClass');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is called if attribute value is set by method ' +
+ 'classList.add(). The custom element is created via constructor.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-d', {prototype: proto});
+
+ var customElement = new GeneratedConstructor();
+ customElement.classList.add('someClass');
+ assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
+ 'Unexpected callback attributeChanged arguments');
+}, 'Test attributeChanged callback arguments if attribute value is set by method ' +
+ 'classList.add(). The custom element is created via constructor.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-e', {prototype: proto});
+
+ var customElement = new GeneratedConstructor();
+ customElement.classList.toggle('someClass');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is called if attribute value is set by method ' +
+ 'classList.toggle(). The custom element is created via constructor.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-f', {prototype: proto});
+
+ var customElement = new GeneratedConstructor();
+ customElement.classList.toggle('someClass');
+ assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
+ 'Unexpected callback attributeChanged arguments');
+}, 'Test attributeChanged callback arguments if attribute value is set by method ' +
+ 'classList.toggle(). The custom element is created via constructor.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-g', {prototype: proto});
+
+ var customElement = new GeneratedConstructor();
+ customElement.className = 'someClass';
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is called if attribute value is set as property. ' +
+ 'The custom element is created via constructor.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-h', {prototype: proto});
+
+ var customElement = new GeneratedConstructor();
+ customElement.className = 'someClass';
+ assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
+ 'Unexpected callback attributeChanged arguments');
+}, 'Test attributeChanged callback arguments if attribute value is set as property. '+
+ 'The custom element is created via constructor.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-i', {prototype: proto});
+
+ doc.body.innerHTML = '<x-i id="x-i" class="theClass"></x-i>';
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 0,
+ 'Callback attributeChanged should not be called');
+}, 'Test attributeChanged callback is not called if attribute value is specified in HTML. '+
+ 'The custom element is created via innerHTML property.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-j', {prototype: proto});
+
+ doc.body.innerHTML = '<x-j id="x-j" class="theClass"></x-j>';
+ var customElement = doc.querySelector('#x-j');
+ customElement.setAttribute('class', 'someClass');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is called if attribute value is set by method setAttribute(). '+
+ 'The custom element is created via innerHTML property.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-k', {prototype: proto});
+
+ doc.body.innerHTML = '<x-k id="x-k"></x-k>';
+ var customElement = doc.querySelector('#x-k');
+ customElement.setAttribute('class', 'someClass');
+ assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
+ 'Unexpected callback attributeChanged arguments');
+}, 'Test attributeChanged callback arguments if attribute value is set by method setAttribute(). '+
+ 'The custom element is created via innerHTML property.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+
+ doc.body.innerHTML = '<x-l id="x-l"></x-l>';
+ var customElement = doc.querySelector('#x-l');
+ // this call shouldn't invoke or enqueue the callback
+ customElement.setAttribute('class', 'someClass');
+
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-l', {prototype: proto});
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 0,
+ 'Callback attributeChanged should not be called');
+ // this one should
+ customElement.setAttribute('class', 'someClass2');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is not called if attribute value of unresolved element '+
+ 'is set by method setAttribute().');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ HTML5_ELEMENTS.forEach(function(element) {
+ var obj = doc.createElement(element);
+ var proto = newCustomElementPrototype(obj.constructor.prototype);
+ var GeneratedConstructor = doc.registerElement('x-' + element + '-' + element + '-1', {
+ prototype: proto,
+ extends: element
+ });
+
+ var customElement = new GeneratedConstructor();
+ customElement.setAttribute('class', 'someClass');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+ });
+}, 'Test attributeChanged callback of the custom element that extends some HTML element. ' +
+ 'Test that the callback is called');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ HTML5_ELEMENTS.forEach(function(element) {
+ var obj = doc.createElement(element);
+ var proto = newCustomElementPrototype(obj.constructor.prototype);
+ var GeneratedConstructor = doc.registerElement('x-' + element + '-' + element + '-1-1', {
+ prototype: proto,
+ extends: element
+ });
+
+ var customElement = new GeneratedConstructor();
+ customElement.classList.add('someClass');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+ });
+}, 'Test attributeChanged callback is called if attribute is set by method classList.add(). '+
+ 'The custom element extends some HTML element.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ HTML5_ELEMENTS.forEach(function(element) {
+ var obj = doc.createElement(element);
+ var proto = newCustomElementPrototype(obj.constructor.prototype);
+ var GeneratedConstructor = doc.registerElement('x-' + element + '-' + element + '-2', {
+ prototype: proto,
+ extends: element
+ });
+
+ var customElement = new GeneratedConstructor();
+ customElement.setAttribute('class', 'someClass');
+ assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
+ 'Unexpected callback attributeChanged arguments');
+ });
+}, 'Test attributeChanged callback arguments if attribute value is set by method setAttribute(). '+
+ 'The custom element extends some HTML element.');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+
+ var customElement = doc.querySelector('#x-element');
+ customElement.setAttribute('class', 'someClass');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is called if attribute value is set by method setAttribute(). '+
+ 'The document has browsing context.');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+
+ var customElement = doc.querySelector('#x-element');
+ customElement.setAttribute('class', 'someClass');
+ assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
+ 'Unexpected callback attributeChanged arguments');
+}, 'Test attributeChanged callback arguments if attribute value is set by method setAttribute(). '+
+ 'The document has browsing context.');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+
+ var customElement = doc.querySelector('#x-element');
+ customElement.classList.add('someClass');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is called if attribute value is set by method classList.add(). '+
+ 'The document has browsing context.');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+
+ var customElement = doc.querySelector('#x-element');
+ customElement.classList.add('someClass');
+ assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
+ 'Unexpected callback attributeChanged arguments');
+}, 'Test attributeChanged callback arguments if attribute value is set by method classList.add(). '+
+ 'The document has browsing context.');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var customElement = doc.querySelector('#x-element');
+ // this call shouldn't invoke or enqueue the callback
+ customElement.setAttribute('name', 'someName');
+
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 0,
+ 'Callback attributeChanged should not be called');
+ // this one should
+ customElement.setAttribute('class', 'someClass');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+}, 'Test attributeChanged callback is not called if attribute value of unresolved element '+
+ 'is set by method setAttribute(). The document has browsing context.');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ HTML5_ELEMENTS.forEach(function(element) {
+ var obj = doc.createElement(element);
+ var proto = newCustomElementPrototype(obj.constructor.prototype);
+ var GeneratedConstructor = doc.registerElement('x-' + element + '-' + element + '-1', {
+ prototype: proto,
+ extends: element
+ });
+
+ var customElement = new GeneratedConstructor();
+ customElement.setAttribute('class', 'someClass');
+ assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
+ 'Callback attributeChanged should be called');
+ });
+}, 'Test attributeChanged callback is called if attribute value is set by method setAttribute(). '+
+ 'The document has browsing context. The custom element extends some HTML element.');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ HTML5_ELEMENTS.forEach(function(element) {
+ var obj = doc.createElement(element);
+ var proto = newCustomElementPrototype(obj.constructor.prototype);
+ var GeneratedConstructor = doc.registerElement('x-' + element + '-' + element + '-2', {
+ prototype: proto,
+ extends: element
+ });
+
+ var customElement = new GeneratedConstructor();
+ customElement.setAttribute('class', 'someClass');
+ assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
+ 'Unexpected callback attributeChanged arguments');
+ });
+}, 'Test attributeChanged callback arguments if attribute value is set by method setAttribute(). '+
+ 'The document has browsing context. The custom element extends some HTML element.');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/created-callback-element-prototype-test.html b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/created-callback-element-prototype-test.html
new file mode 100644
index 000000000..188957c1a
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/created-callback-element-prototype-test.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>The custom element prototype must be set just prior to invoking created callback</title>
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="The custom element prototype must be set just prior to invoking callback.">
+<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+
+ doc.body.innerHTML = '<x-a></x-a>';
+ doc.registerElement('x-a', {prototype: proto});
+ assert_equals(proto.createdCallbackThis.constructor.prototype, proto,
+ 'The custom element prototype is incorrect inside created callback');
+}, 'Test custom element prototype inside created callback when custom element is created ' +
+ 'in HTML before registration of a custom element');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+
+ doc.registerElement('x-b', {prototype: proto});
+ doc.body.innerHTML = '<x-b></x-b>';
+ assert_equals(proto.createdCallbackThis.constructor.prototype, proto,
+ 'The custom element prototype is incorrect inside created callback');
+}, 'Test custom element prototype inside created callback when custom element is created ' +
+ 'in HTML after registration of a custom element');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var customElement = doc.createElement('x-c');
+
+ doc.body.appendChild(customElement);
+ doc.registerElement('x-c', {prototype: proto});
+ assert_equals(proto.createdCallbackThis.constructor.prototype, proto,
+ 'The custom element prototype is incorrect inside created callback');
+}, 'Test custom element prototype inside created callback when custom element is created ' +
+ 'via document.createElement() before registration of a custom element');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-d', {prototype: proto});
+
+ var customElement = doc.createElement('x-d');
+ doc.body.appendChild(customElement);
+ assert_equals(proto.createdCallbackThis.constructor.prototype, proto,
+ 'The custom element prototype is incorrect inside created callback');
+}, 'Test custom element prototype inside created callback when custom element is created ' +
+ 'via document.createElement() after registration of a custom element');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-e', {prototype: proto});
+ var customElement = new GeneratedConstructor();
+
+ assert_equals(proto.createdCallbackThis.constructor.prototype, proto,
+ 'The custom element prototype is incorrect inside created callback');
+}, 'Test custom element prototype inside created callback when custom element is created ' +
+ 'via constructor returned by method registerElement');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ assert_equals(proto.createdCallbackThis.constructor.prototype, proto,
+ 'The custom element prototype is incorrect inside created callback');
+}, 'Test custom element prototype inside created callback when custom element is created ' +
+ 'by UA parser before registration of a custom element');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/created-callback-invocation-order-test.html b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/created-callback-invocation-order-test.html
new file mode 100644
index 000000000..b0f2d5ee8
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/created-callback-invocation-order-test.html
@@ -0,0 +1,242 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>All other callbacks must not be enqueued until after the created callback's invocation had started</title>
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="assert" content="All other callbacks must not be enqueued until after the created callback's invocation had started.">
+<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+function newPrototypeWithCallbackLog() {
+ var proto = Object.create(HTMLElement.prototype);
+ proto.callbackLog = [];
+ proto.createdCallback = function() {
+ proto.callbackLog.push('created');
+ };
+ proto.attachedCallback = function() {
+ proto.callbackLog.push('attached');
+ };
+ proto.attributeChangedCallback = function() {
+ proto.callbackLog.push('attributeChanged');
+ };
+ proto.detachedCallback = function() {
+ proto.callbackLog.push('detached');
+ };
+ return proto;
+}
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ var proto = newPrototypeWithCallbackLog();
+ doc.registerElement('x-a', {prototype: proto});
+ doc.body.innerHTML = '<x-a></x-a>';
+
+ assert_equals(proto.callbackLog[0], 'created', 'The callback ' +
+ proto.callbackLog[0] + ' should be enqueued after created callback');
+ assert_in_array('attached', proto.callbackLog, 'The callback ' +
+ 'attached should be called');
+}, 'Test attached callback is enqueued after created callback');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ var proto = newPrototypeWithCallbackLog();
+ doc.registerElement('x-b', {prototype: proto});
+ doc.body.innerHTML = '<x-b id="x-b"></x-b>';
+ var customElement = doc.querySelector('#x-b');
+ customElement.setAttribute('key', 'value');
+
+ assert_equals(proto.callbackLog[0], 'created', 'The callback ' +
+ proto.callbackLog[0] + ' should not be enqueued before created callback');
+ assert_in_array('attributeChanged', proto.callbackLog,
+ 'The callback attributeChanged should be called');
+}, 'Test attributeChanged callback is enqueued after created callback. ' +
+ 'Document has browsing context');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newPrototypeWithCallbackLog();
+ doc.registerElement('x-c', {prototype: proto});
+ doc.body.innerHTML = '<x-c id="x-c"></x-c>';
+ var customElement = doc.querySelector('#x-c');
+ customElement.setAttribute('key', 'value');
+ assert_equals(proto.callbackLog[0], 'created', 'The callback ' +
+ proto.callbackLog[0] + ' should not be enqueued before created callback');
+ assert_in_array('attributeChanged', proto.callbackLog,
+ 'The callback attributeChanged should be called');
+}, 'Test attributeChanged callback is enqueued after created callback. ' +
+ 'Document has no browsing context');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newPrototypeWithCallbackLog();
+ doc.registerElement('x-element', {prototype: proto});
+ var customElement = doc.querySelector('#x-element');
+ doc.body.removeChild(customElement);
+
+ assert_equals(proto.callbackLog[0], 'created', 'The callback ' +
+ proto.callbackLog[0] + ' should not be enqueued before created callback');
+ assert_in_array('detached', proto.callbackLog,
+ 'The callback detached should be called');
+}, 'Test detached callback is enqueued after created callback.');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto1 = newPrototypeWithCallbackLog();
+ proto1.createdCallback = function() {
+ proto1.callbackLog.push('created');
+ var xe = doc.querySelector('#x-e');
+ xe.setAttribute('key', 'value');
+ };
+ var proto2 = newPrototypeWithCallbackLog();
+
+ doc.registerElement('x-d', {prototype: proto1});
+ doc.registerElement('x-e', {prototype: proto2});
+ doc.body.innerHTML = '<x-d><x-e id="x-e"></x-e></x-d>';
+
+ assert_array_equals(proto2.callbackLog, ['created'],
+ 'attributeChanged callback should not be enqueued before created callback');
+}, 'Test attributeChanged callback is not enqueued before created callback started. ' +
+ 'Document has no browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ var proto1 = newPrototypeWithCallbackLog();
+ proto1.createdCallback = function() {
+ proto1.callbackLog.push('created');
+ var xe = doc.querySelector('#x-g');
+ xe.setAttribute('key', 'value');
+ };
+ var proto2 = newPrototypeWithCallbackLog();
+
+ doc.registerElement('x-f', {prototype: proto1});
+ doc.registerElement('x-g', {prototype: proto2});
+ doc.body.innerHTML = '<x-f><x-g id="x-g"></x-g></x-f>';
+
+ assert_array_equals(proto2.callbackLog, ['created', 'attached'],
+ 'attributeChanged callback should not be called before created callback started');
+}, 'Test attributeChanged callback is not enqueued before created callback started. ' +
+ 'Document has browsing context');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newPrototypeWithCallbackLog();
+ proto.createdCallback = function() {
+ proto.callbackLog.push('created');
+ this.setAttribute('key', 'value');
+ };
+ doc.registerElement('x-h', {prototype: proto});
+ doc.body.innerHTML = '<x-h></x-h>';
+
+ assert_array_equals(proto.callbackLog, ['created', 'attributeChanged'],
+ 'attributeChanged callback should be enqueued after created callback');
+}, 'Test attributeChanged callback is enqueued after created callback started. ' +
+ 'Document has no browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(docWithBrowsingContext) {
+ var docNoBrowsingContext = newHTMLDocument();
+
+ var proto1 = newPrototypeWithCallbackLog();
+ var proto2 = newPrototypeWithCallbackLog();
+ proto1.createdCallback = function() {
+ proto1.callbackLog.push('created');
+ var xk = docNoBrowsingContext.querySelector('#x-k');
+ assert_equals(proto2.callbackLog.length, 0, 'Created callback for x-k ' +
+ 'should not be called before created callback for x-i');
+ docWithBrowsingContext.body.appendChild(xk);
+ };
+
+ docNoBrowsingContext.registerElement('x-i', {prototype: proto1});
+ docNoBrowsingContext.registerElement('x-k', {prototype: proto2});
+ docNoBrowsingContext.body.innerHTML = '<x-i><x-k id="x-k"></x-k></x-i>';
+
+ // Though at the moment of inserting <x-k> into docWithBrowsingContext
+ // created callback is not called for <x-k> yet, attached calback is enqueued
+ // anyway. Because specification for setting custom element prototype algorithm reads:
+ // ....
+ // 3. If ELEMENT is in a document and this document has a browsing context:
+ // 1. Enqueue attached callback for ELEMENT
+ //
+ // Changes in the specification will follow, to reflect this exceptional case.
+ assert_array_equals(proto2.callbackLog, ['created', 'attached'],
+ 'attached callback should be enqueued when custom element prototype is set');
+}, 'Test attached callback is enqueued after created callback, but before created callback had started');
+
+
+testInIFrame('../../resources/blank.html', function(docWithBrowsingContext) {
+ var docNoBrowsingContext = newHTMLDocument();
+
+ var proto = newPrototypeWithCallbackLog();
+ proto.createdCallback = function() {
+ proto.callbackLog.push('created');
+ docWithBrowsingContext.body.appendChild(this);
+ };
+
+ docNoBrowsingContext.registerElement('x-l', {prototype: proto});
+ docNoBrowsingContext.body.innerHTML = '<x-l></x-l>';
+ assert_array_equals(proto.callbackLog, ['created', 'attached'],
+ 'attached callback should be enqueued after created callback had started');
+}, 'Test attached callback is enqueued after created callback had started');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ var proto1 = newPrototypeWithCallbackLog();
+ var proto2 = newPrototypeWithCallbackLog();
+ proto1.createdCallback = function() {
+ proto1.callbackLog.push('created');
+ var xn = doc.querySelector('#x-n');
+ assert_equals(proto2.callbackLog.length, 0, 'Created callback for x-n ' +
+ 'should not be called before created callback for x-m');
+ this.removeChild(xn);
+ };
+
+ doc.registerElement('x-m', {prototype: proto1});
+ doc.registerElement('x-n', {prototype: proto2});
+ doc.body.innerHTML = '<x-m><x-n id="x-n"></x-n></x-m>';
+ assert_array_equals(proto2.callbackLog, ['created'],
+ 'detached callback should not be enqueued before created callback had started');
+}, 'Test detached callback is not enqueued before created callback had started');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ var proto = newPrototypeWithCallbackLog();
+ proto.createdCallback = function() {
+ proto.callbackLog.push('created');
+ this.remove();
+ };
+
+ doc.registerElement('x-o', {prototype: proto});
+ doc.body.innerHTML = '<x-o></x-o>';
+
+ assert_array_equals(proto.callbackLog, ['created', 'attached', 'detached'],
+ 'detached callback should be enqueued after created callback had started');
+}, 'Test detached callback is enqueued after created callback had started');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newPrototypeWithCallbackLog();
+ doc.registerElement('x-element', {prototype: proto});
+
+ // Though at the moment of inserting <x-element> into the document
+ // created callback is not called for <x-element> yet, attached calback is enqueued
+ // anyway. Because specification for setting custom element prototype algorithm reads:
+ // ....
+ // 3. If ELEMENT is in a document and this document has a browsing context:
+ // 1. Enqueue attached callback for ELEMENT
+ //
+ // Changes in the specification will follow, to reflect this exceptional case.
+ assert_array_equals(proto.callbackLog, ['created', 'attached'],
+ 'attached callback should be enqueued when custom element prototype is set');
+}, 'Test attached callback is enqueued after created callback after registration ' +
+ 'of custom element type');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/created-callback-invocation-test.html b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/created-callback-invocation-test.html
new file mode 100644
index 000000000..63814a80f
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/created-callback-invocation-test.html
@@ -0,0 +1,167 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Created callback of a custom element should be invoked after custom element instance is created and its definition is registered</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="CREATED callback is invoked after custom element instance is created and its definition is registered">
+<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto});
+ assert_equals(proto.createdCallbackCalledCounter, 0,
+ 'Callback created should not be called before element instance was created');
+ var customElement = new GeneratedConstructor();
+ assert_equals(proto.createdCallbackCalledCounter, 1,
+ 'Callback created should be called after element instance was created');
+}, 'Test created callback when custom element is created by constructor');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-b', {prototype: proto});
+ assert_equals(proto.createdCallbackCalledCounter, 0,
+ 'Callback created should not be called before element instance was created');
+ doc.body.innerHTML = '<x-b></x-b>';
+ assert_equals(proto.createdCallbackCalledCounter, 1,
+ 'Callback created should be called after element instance was created');
+}, 'Test created callback when custom element is created in HTML');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-c', {prototype: proto});
+ assert_equals(proto.createdCallbackCalledCounter, 0,
+ 'Callback created should not be called before element instance was created');
+ doc.body.innerHTML = '<div><x-c></x-c></div>';
+ assert_equals(proto.createdCallbackCalledCounter, 1,
+ 'Callback created should be called after element instance was created');
+}, 'Test created callback when custom element is created in HTML as descendant of ' +
+ 'another element');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-d', {prototype: proto});
+ assert_equals(proto.createdCallbackCalledCounter, 0,
+ 'Callback created should not be called before element instance was created');
+ var customElement = doc.createElement('x-d');
+ assert_equals(proto.createdCallbackCalledCounter, 1,
+ 'Callback created should be called after element instance was created');
+}, 'Test created callback when custom element is created by createElement');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+
+ doc.body.innerHTML = '<x-e></x-e>';
+ doc.registerElement('x-e', {prototype: proto});
+ assert_equals(proto.createdCallbackCalledCounter, 1,
+ 'Callback created should be called after element instance was created');
+}, 'Test created callback when custom element is created in HTML before ' +
+ 'registration of a custom element');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-f', {prototype: proto});
+ doc.body.innerHTML = '<x-f-unresolved id="x-f-unresolved"></x-f-unresolved>';
+ assert_equals(proto.createdCallbackCalledCounter, 0,
+ 'Callback created should not be called if custom element is unresolved');
+
+ var customElement = doc.querySelector('#x-f-unresolved');
+ customElement.constructor.prototype = proto;
+ assert_equals(proto.createdCallbackCalledCounter, 0,
+ 'Created callback should not be called if custom element is unresolved');
+}, 'Test created callback if custom element is unresolved.');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ assert_equals(proto.createdCallbackCalledCounter, 1, 'Callback created should be called');
+}, 'Test created callback is called after custom element is created and registered. ' +
+ 'Document has browsing context');
+
+
+testInIFrame('../../resources/register-and-create-custom-element.html', function(doc) {
+ assert_equals(doc.querySelector('#log').textContent, 'Created callback was called',
+ 'Callback created should be called');
+}, 'Test created callback is called after custom element is registered and created. ' +
+ 'Document has browsing context');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var customElement = doc.createElement('x-g');
+
+ doc.registerElement('x-g', {prototype: proto});
+ assert_equals(proto.createdCallbackCalledCounter, 1,
+ 'Callback created should be called after registration of custom element');
+}, 'Test created callback when custom element is created by createElement '+
+ 'before registration of a custom element');
+
+
+test(function(){
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ var GeneratedConstructor = doc.registerElement('x-h', {prototype: proto});
+
+ var customElement1 = new GeneratedConstructor();
+ assert_equals(proto.createdCallbackCalledCounter, 1, 'Callback created should be called');
+
+ var customElement2 = doc.createElement('x-h');
+ assert_equals(proto.createdCallbackCalledCounter, 2,
+ 'Callback created should be called after element instance was created');
+
+ doc.body.innerHTML = '<x-h></x-h>';
+ assert_equals(proto.createdCallbackCalledCounter, 3,
+ 'Callback created should be called after element instance was created');
+
+ doc.body.innerHTML = '<div><x-h></x-h></div>';
+ assert_equals(proto.createdCallbackCalledCounter, 4,
+ 'Callback created should be called after element instance was created');
+}, 'Test created callback. Create several custom elements using different ways');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+
+ var GeneratedConstructor = doc.registerElement('x-element', {prototype: proto});
+ assert_equals(proto.createdCallbackCalledCounter, 1,
+ 'Callback created should be called for custom element in loaded document');
+
+ var customElement2 = new GeneratedConstructor();
+ assert_equals(proto.createdCallbackCalledCounter, 2,
+ 'Callback created should be called after element instance was created');
+
+ var customElement3 = doc.createElement('x-element');
+ assert_equals(proto.createdCallbackCalledCounter, 3,
+ 'Callback created should be called after element instance was created');
+
+ doc.body.innerHTML = '<x-element></x-element>';
+ assert_equals(proto.createdCallbackCalledCounter, 4,
+ 'Callback created should be called after element instance was created');
+
+ doc.body.innerHTML = '<div><x-element></x-element></div>';
+ assert_equals(proto.createdCallbackCalledCounter, 5,
+ 'Callback created should be called after element instance was created');
+}, 'Test created callback. Create several custom elements using different ways. ' +
+ 'Document has browsing context');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/detached-callback-move-element-test.html b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/detached-callback-move-element-test.html
new file mode 100644
index 000000000..d69eb54db
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/detached-callback-move-element-test.html
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test detached callback of a custom element when moving custom element between different documents</title>
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="detached callback ... must be enqueued whenever custom element is removed from the document and this document has a browsing context.">
+<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-a', {prototype: proto});
+
+ var customElement = doc.createElement('x-a');
+ doc.body.appendChild(customElement);
+
+ var divElement = doc.createElement('div');
+ doc.body.appendChild(divElement);
+ divElement.appendChild(customElement);
+
+ assert_equals(proto.detachedCallbackCalledCounter, 0,
+ 'Callback detached should not be called in document without a browsing context');
+}, 'Test detached callback is not called if moving custom element inside document ' +
+ 'without browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(docWithBrowsingContext) {
+ var docNoBrowsingContext = newHTMLDocument();
+ var proto1 = newHTMLElementPrototype();
+ docNoBrowsingContext.registerElement('x-b', {prototype: proto1});
+
+ var customElement = docNoBrowsingContext.createElement('x-b');
+ docNoBrowsingContext.body.appendChild(customElement);
+ var proto2 = newHTMLElementPrototype();
+ docWithBrowsingContext.registerElement('x-b', {prototype: proto2});
+ docWithBrowsingContext.body.appendChild(customElement);
+
+ assert_equals(proto1.detachedCallbackCalledCounter, 0,
+ 'Callback detached should not be called in document without browsing context');
+ assert_equals(proto2.detachedCallbackCalledCounter, 0,
+ 'Callback detached, defined in receiving document, should not be called');
+}, 'Test detached callback is not called if moving custom element from ' +
+ 'document without browsing context to document with browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(docWithBrowsingContext) {
+ var proto1 = newHTMLElementPrototype();
+ docWithBrowsingContext.registerElement('x-c', {prototype: proto1});
+
+ var customElement = docWithBrowsingContext.createElement('x-c');
+ docWithBrowsingContext.body.appendChild(customElement);
+
+ var docNoBrowsingContext = newHTMLDocument();
+ var proto2 = newHTMLElementPrototype();
+ docNoBrowsingContext.registerElement('x-c', {prototype: proto2});
+ docNoBrowsingContext.body.appendChild(customElement);
+ assert_equals(proto1.detachedCallbackCalledCounter, 1,
+ 'Callback detached should be called in documents with browsing context');
+ assert_equals(proto2.detachedCallbackCalledCounter, 0,
+ 'Callback detached, defined in receiving document, should not be called');
+}, 'Test detached callback if moving custom element from ' +
+ 'document with browsing context to document without browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-d', {prototype: proto});
+
+ var customElement = doc.createElement('x-d');
+ doc.body.appendChild(customElement);
+ var divElement = doc.createElement('div');
+ doc.body.appendChild(divElement);
+ divElement.appendChild(customElement);
+ assert_equals(proto.detachedCallbackCalledCounter, 1,
+ 'Callback detached should be called in documents with browsing context');
+}, 'Test detached callback if moving custom element inside document ' +
+ 'with browsing context');
+
+
+var moveTest = async_test('Test detached callback if moving custom element from ' +
+ 'document with browsing context to document with browsing context');
+
+moveTest.step(function() {
+ var iframe1 = newIFrame('../../resources/blank.html');
+ iframe1.onload = moveTest.step_func(function() {
+ var doc1 = iframe1.contentDocument;
+
+ // register custom element type
+ var proto1 = newHTMLElementPrototype();
+ doc1.registerElement('x-e', {prototype: proto1});
+
+ // create custom element
+ var customElement = doc1.createElement('x-e');
+ doc1.body.appendChild(customElement);
+ assert_equals(proto1.detachedCallbackCalledCounter, 0,
+ 'Callback detached should not be called when element is created');
+
+ // create second iframe
+ var iframe2 = newIFrame('../../resources/x-element.html');
+ iframe2.onload = moveTest.step_func(function() {
+ var doc2 = iframe2.contentDocument;
+
+ // register custom element type
+ var proto2 = newHTMLElementPrototype();
+ doc2.registerElement('x-e', {prototype: proto2});
+
+ // move element
+ doc2.body.appendChild(customElement);
+ assert_equals(proto1.detachedCallbackCalledCounter, 1,
+ 'Callback detached should be called in documents with browsing context');
+ assert_equals(proto2.detachedCallbackCalledCounter, 0,
+ 'Callback detached, defined in receiving document, should not be called');
+
+ // test clean up
+ iframe1.remove();
+ iframe2.remove();
+ moveTest.done();
+ });
+
+ });
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/detached-callback-no-browsing-context-test.html b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/detached-callback-no-browsing-context-test.html
new file mode 100644
index 000000000..2b420d11c
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/detached-callback-no-browsing-context-test.html
@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Detached callback of a custom element should not be called if document has no browsing context</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="detached callback ... must be enqueued whenever custom element is removed from the document and this document has a browsing context.">
+<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-a', {prototype: proto});
+ doc.body.innerHTML = '<x-a id="x-a"></x-a>';
+ var customElement = doc.querySelector('#x-a');
+ doc.body.removeChild(customElement);
+
+ assert_equals(proto.detachedCallbackCalledCounter, 0,
+ 'Callback detached should not be called if the document has no browsing context');
+}, 'Test detached callback if custom element is created via innerHTML property. ' +
+ 'Document has no browsing context');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+ doc.body.innerHTML = '<x-b id="x-b"></x-b>';
+ doc.registerElement('x-b', {prototype: proto});
+ var customElement = doc.querySelector('#x-b');
+ doc.body.removeChild(customElement);
+
+ assert_equals(proto.detachedCallbackCalledCounter, 0,
+ 'Callback detached should not be called if the document has no browsing context');
+}, 'Test detached callback if custom element is via innerHTML property before ' +
+ 'registration of a custom element. Document has no browsing context');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ doc.body.innerHTML = '<x-c id="x-c"></x-c>';
+ var customElement = doc.querySelector('#x-c');
+
+ var proto = newHTMLElementPrototype();
+ customElement.constructor.prototype = proto;
+ doc.body.removeChild(customElement);
+
+ assert_equals(proto.detachedCallbackCalledCounter, 0,
+ 'Callback detached should not be called if the document has no browsing context');
+}, 'Test detached callback if custom element is unregistered. ' +
+ 'Document has no browsing context');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+
+ doc.registerElement('x-d', {prototype: proto});
+ doc.body.innerHTML = '<x-d id="x-d"></x-d>';
+ doc.body.innerHTML = '';
+
+ assert_equals(proto.detachedCallbackCalledCounter, 0,
+ 'Callback detached should not be called if the document has no browsing context');
+}, 'Test detached callback if removing custom element via innerHTML property. ' +
+ 'Document has no browsing context');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+
+ doc.registerElement('x-e', {prototype: proto});
+ doc.body.innerHTML = '<div id="customParent"><x-e id="x-e"></x-e></div>';
+ var parent = doc.querySelector('#customParent');
+ doc.body.removeChild(parent);
+
+ assert_equals(proto.detachedCallbackCalledCounter, 0,
+ 'Callback detached should not be called if the document has no browsing context');
+}, 'Test detached callback if removing perent of custom element. ' +
+ 'Document has no browsing context');
+
+
+test(function() {
+ var doc = newHTMLDocument();
+ var proto = newHTMLElementPrototype();
+
+ doc.registerElement('x-f', {prototype: proto});
+ doc.body.innerHTML = '<div><x-f id="x-f"></x-f></div>';
+ doc.body.innerHTML = '';
+
+ assert_equals(proto.detachedCallbackCalledCounter, 0,
+ 'Callback detached should not be called if the document has no browsing context');
+}, 'Test detached callback if removing perent of custom element via innerHTML property. ' +
+ 'Document has no browsing context');
+
+
+var loseBrowsingContextTest = async_test('Test detached callback is not called ' +
+ 'if document lose browsing context and custom element is removed');
+
+loseBrowsingContextTest.step(function() {
+ var iframe = newIFrame('../../resources/x-element.html');
+ iframe.onload = loseBrowsingContextTest.step_func(function(){
+ var doc = iframe.contentDocument;
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+
+ var customElement = doc.querySelector('#x-element');
+ iframe.remove();
+ customElement.remove();
+
+ assert_equals(proto.detachedCallbackCalledCounter, 0,
+ 'Callback detached should not be called if the document has no browsing context');
+ loseBrowsingContextTest.done();
+ });
+});
+
+
+var navigateTest = async_test('Test detached callback is not called, ' +
+ 'if document\'s window is navigated to another document and custom element is removed');
+
+navigateTest.step(function() {
+ var iframe = newIFrame('../../resources/x-element.html');
+ iframe.onload = navigateTest.step_func(function() {
+ var doc = iframe.contentDocument;
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ customElement = doc.querySelector('#x-element');
+
+ iframe.onload = navigateTest.step_func(function() {
+ customElement.remove();
+ assert_equals(proto.detachedCallbackCalledCounter, 0,
+ 'Callback detached should not be called ' +
+ 'if the document has no browsing context');
+ navigateTest.done();
+ iframe.remove();
+ });
+ iframe.src = '../../resources/blank.html';
+ });
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/detached-callback-with-browsing-context-test.html b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/detached-callback-with-browsing-context-test.html
new file mode 100644
index 000000000..31b06a907
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle/types-of-callbacks/detached-callback-with-browsing-context-test.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Detached callback of a custom element should be called if document has browsing context</title>
+<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
+<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
+<meta name="assert" content="detached callback ... must be enqueued whenever custom element is removed from the document and this document has a browsing context.">
+<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ var customElement = doc.querySelector('#x-element');
+ doc.body.removeChild(customElement);
+ assert_equals(proto.detachedCallbackCalledCounter, 1, 'Callback detached should be ' +
+ 'called if custom element is removed from the document with browsing context');
+}, 'Test detached callback is called if custom element is removed by method removeChild() ' +
+ 'from document with browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ doc.body.innerHTML = '<div id="x-a-parent"><x-a id="x-a"></x-a></div>';
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-a', {prototype: proto});
+ var div = doc.querySelector('#x-a-parent');
+ doc.body.removeChild(div);
+ assert_equals(proto.detachedCallbackCalledCounter, 1, 'Callback detached should be ' +
+ 'called if custom element is removed from the document with browsing context');
+}, 'Test detached callback is called if ancestor node of custom element ' +
+ 'is removed by method removeChild() from document with browsing context');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ var customElement = doc.querySelector('#x-element');
+ var div = doc.createElement('div');
+ doc.body.replaceChild(div, customElement);
+ assert_equals(proto.detachedCallbackCalledCounter, 1, 'Callback detached should be ' +
+ 'called if custom element is removed from the document with browsing context');
+}, 'Test detached callback is called if custom element is removed by method replaceChild() ' +
+ 'from document with browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-b', {prototype: proto});
+ doc.body.innerHTML = '<div id="x-b-parent"><x-b id="x-b"></x-b></div>';
+ var parent = doc.querySelector('#x-b-parent');
+ var replacement = doc.createElement('div');
+ doc.body.replaceChild(replacement, parent);
+ assert_equals(proto.detachedCallbackCalledCounter, 1, 'Callback detached should be ' +
+ 'called if custom element is removed from the document with browsing context');
+}, 'Test detached callback is called if ancestor node of custom element ' +
+ 'is removed by method replaceChild() from document with browsing context');
+
+
+testInIFrame('../../resources/x-element.html', function(doc) {
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-element', {prototype: proto});
+ doc.body.innerHTML = '';
+ assert_equals(proto.detachedCallbackCalledCounter, 1, 'Callback detached should be ' +
+ 'called if custom element is removed from the document with browsing context');
+}, 'Test detached callback is called after changing custom element direct parent ' +
+ 'innerHTML property in the document with browsing context');
+
+
+testInIFrame('../../resources/blank.html', function(doc) {
+ doc.body.innerHTML = '<div><x-c></x-c></div>';
+ var proto = newHTMLElementPrototype();
+ doc.registerElement('x-c', {prototype: proto});
+ doc.body.innerHTML = '';
+ assert_equals(proto.detachedCallbackCalledCounter, 1, 'Callback detached should be ' +
+ 'called if custom element is removed from the document with browsing context');
+}, 'Test detached callback is called after changing custom element ancestor ' +
+ 'innerHTML property in the document with browsing context');
+</script>
+</body>
+</html>