| OLD | NEW |
| (Empty) | |
| 1 <!DOCTYPE html> |
| 2 <script src="../../../resources/testharness.js"></script> |
| 3 <script src="../../../resources/testharnessreport.js"></script> |
| 4 <body> |
| 5 <template id="expected">A entered |
| 6 A removing parent node |
| 7 A left |
| 8 A inserting parent node |
| 9 A entered |
| 10 A removing parent node |
| 11 A left |
| 12 A inserting parent node |
| 13 A entered |
| 14 B entered |
| 15 B left |
| 16 B entered |
| 17 B setting attribute on C |
| 18 C entered |
| 19 C left |
| 20 C entered |
| 21 C left |
| 22 C entered |
| 23 C@by null->b |
| 24 C setting attribute on B |
| 25 B left |
| 26 B entered |
| 27 B@by null->c |
| 28 B setting attribute on A |
| 29 A@by null->b |
| 30 done |
| 31 done |
| 32 done |
| 33 done |
| 34 done |
| 35 done |
| 36 done</template> |
| 37 <script> |
| 38 test(function () { |
| 39 // Helpers for logging |
| 40 var buffer = []; |
| 41 var indentation = ''; |
| 42 function log(msg) { |
| 43 buffer.push(indentation + msg); |
| 44 } |
| 45 function indented(msg) { |
| 46 log(msg); |
| 47 indentation += ' '; |
| 48 } |
| 49 function unindent() { |
| 50 indentation = indentation.substring(3); |
| 51 log('done'); |
| 52 } |
| 53 |
| 54 // This tests recursion and the processing stack. Specifically: |
| 55 // |
| 56 // (1) Scheduling callbacks for an element that has callbacks |
| 57 // scheduled at an outer level of recursion, but that have not |
| 58 // begun to be processed yet. |
| 59 // |
| 60 // (2) Scheduling callbacks for an element that is in the middle |
| 61 // of processing callbacks at an outer level of recursion. |
| 62 // |
| 63 // (3) Scheduling callbacks for an element that exhaustively |
| 64 // processed callbacks at an outer level of recursion. |
| 65 // |
| 66 // appendChild and remove are used on a subtree containing |
| 67 // multiple custom elements. In this way it is possible to |
| 68 // schedule callbacks for multiple custom elements with one DOM |
| 69 // call. |
| 70 // |
| 71 // The test creates this tree: |
| 72 // |
| 73 // <div> |
| 74 // <x-a></x-a> |
| 75 // <x-b></x-b> |
| 76 // <x-c></x-c> |
| 77 // </div> |
| 78 // |
| 79 // x-a pushes its parent in and out of the document, thus scheduling |
| 80 // work for x-b and x-c at every level of recursion. |
| 81 // |
| 82 // Then x-b processes half its queue before setting an attribute |
| 83 // on x-c. This tests case (1) because x-c has not begun its queue |
| 84 // yet. |
| 85 // |
| 86 // x-c turns around and sets and attribute on x-b. This tests case |
| 87 // (2) because x-b is half way through processing its queue. |
| 88 // |
| 89 // x-b turns around and sets an attribute on x-a. This tests case |
| 90 // (3) because x-a has finished processing its queue. |
| 91 |
| 92 var protoA = Object.create(HTMLElement.prototype); |
| 93 var n = 0; |
| 94 protoA.enteredDocumentCallback = function () { |
| 95 log('A entered'); |
| 96 n++; |
| 97 if (n < 3) { |
| 98 indented('A removing parent node'); |
| 99 this.parentNode.remove(); |
| 100 unindent(); |
| 101 } |
| 102 }; |
| 103 protoA.leftDocumentCallback = function () { |
| 104 log('A left'); |
| 105 indented('A inserting parent node'); |
| 106 document.body.appendChild(this.parentNode); |
| 107 unindent(); |
| 108 }; |
| 109 protoA.attributeChangedCallback = function (name, oldValue, newValue) { |
| 110 log('A@' + name + ' ' + oldValue + '->' + newValue); |
| 111 }; |
| 112 var A = document.register('x-a', {prototype: protoA}); |
| 113 |
| 114 var protoB = Object.create(HTMLElement.prototype); |
| 115 var m = 0; |
| 116 protoB.enteredDocumentCallback = function () { |
| 117 log('B entered'); |
| 118 m++; |
| 119 if (m == 2) { |
| 120 indented('B setting attribute on C'); |
| 121 this.parentNode.querySelector('x-c').setAttribute('by', 'b'); |
| 122 unindent(); |
| 123 } |
| 124 }; |
| 125 protoB.leftDocumentCallback = function () { |
| 126 log('B left'); |
| 127 }; |
| 128 protoB.attributeChangedCallback = function (name, oldValue, newValue) { |
| 129 log('B@' + name + ' ' + oldValue + '->' + newValue); |
| 130 indented('B setting attribute on A'); |
| 131 this.parentNode.querySelector('x-a').setAttribute('by', 'b'); |
| 132 unindent(); |
| 133 }; |
| 134 var B = document.register('x-b', {prototype: protoB}); |
| 135 |
| 136 var protoC = Object.create(HTMLElement.prototype); |
| 137 protoC.enteredDocumentCallback = function () { |
| 138 log('C entered'); |
| 139 }; |
| 140 protoC.leftDocumentCallback = function () { |
| 141 log('C left'); |
| 142 }; |
| 143 protoC.attributeChangedCallback = function (name, oldValue, newValue) { |
| 144 log('C@' + name + ' ' + oldValue + '->' + newValue); |
| 145 indented('C setting attribute on B'); |
| 146 this.parentNode.querySelector('x-b').setAttribute('by', 'c'); |
| 147 unindent(); |
| 148 }; |
| 149 var C = document.register('x-c', {prototype: protoC}); |
| 150 |
| 151 var div = document.createElement('div'); |
| 152 div.innerHTML = '<div><x-a></x-a><x-b></x-b><x-c></x-c></div>'; |
| 153 document.body.appendChild(div); |
| 154 |
| 155 assert_equals(buffer.join('\n'), expected.content.textContent, 'should have
generated an identical log'); |
| 156 }, 'recursively scheduled callbacks'); |
| 157 </script> |
| OLD | NEW |