diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /testing/web-platform/tests/custom-elements/v0/custom-element-lifecycle | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-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')
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> |