| <!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> |
| <link rel="stylesheet" href="/resources/testharness.css"> |
| </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> |