enqueue-custom-element-callback-reactions-inside-another-callback.html (9502B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Custom Elements: must enqueue an element on the appropriate element queue after checking callback is null and the attribute name</title> 5 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> 6 <meta name="assert" content="To enqueue a custom element callback reaction, the callback must be checked of being null and whether the attribute name is observed or not"> 7 <link rel="help" content="https://html.spec.whatwg.org/multipage/custom-elements.html#enqueue-a-custom-element-callback-reaction"> 8 <link rel="help" content="https://github.com/w3c/webcomponents/issues/760"> 9 <script src="/resources/testharness.js"></script> 10 <script src="/resources/testharnessreport.js"></script> 11 <script src="resources/custom-elements-helpers.js"></script> 12 </head> 13 <body> 14 <script> 15 16 test_with_window((contentWindow, contentDocument) => { 17 class ParentElement extends contentWindow.HTMLElement { 18 connectedCallback() 19 { 20 logs.push('begin'); 21 const child = this.firstChild; 22 child.remove(); 23 logs.push('end'); 24 } 25 } 26 contentWindow.customElements.define('parent-element', ParentElement); 27 28 const logs = []; 29 class ChildElement extends contentWindow.HTMLElement { 30 connectedCallback() { logs.push('connected'); } 31 disconnectedCallback() { logs.push('disconnected'); } 32 } 33 contentWindow.customElements.define('child-element', ChildElement); 34 35 const parent = new ParentElement; 36 const child = new ChildElement; 37 parent.appendChild(child); 38 39 contentDocument.body.appendChild(parent); 40 assert_array_equals(logs, ['begin', 'connected', 'disconnected', 'end']); 41 }, 'Disconnecting an element with disconnectedCallback while it has a connectedCallback in its custom element reaction queue must result in connectedCallback getting invoked before the removal completes'); 42 43 test_with_window((contentWindow, contentDocument) => { 44 class ParentElement extends contentWindow.HTMLElement { 45 connectedCallback() 46 { 47 logs.push('begin'); 48 const child = this.firstChild; 49 child.remove(); 50 logs.push('end'); 51 } 52 } 53 contentWindow.customElements.define('parent-element', ParentElement); 54 55 const logs = []; 56 class ChildElement extends contentWindow.HTMLElement { 57 connectedCallback() { logs.push('connected'); } 58 } 59 contentWindow.customElements.define('child-element', ChildElement); 60 61 const parent = new ParentElement; 62 const child = new ChildElement; 63 parent.appendChild(child); 64 65 contentDocument.body.appendChild(parent); 66 assert_array_equals(logs, ['begin', 'end', 'connected']); 67 }, 'Disconnecting an element without disconnectedCallback while it has a connectedCallback in its custom element reaction queue must not result in connectedCallback getting invoked before the removal completes'); 68 69 test_with_window((contentWindow, contentDocument) => { 70 class ParentElement extends contentWindow.HTMLElement { 71 disconnectedCallback() 72 { 73 logs.push('begin'); 74 contentDocument.body.appendChild(this.firstChild); 75 logs.push('end'); 76 } 77 } 78 contentWindow.customElements.define('parent-element', ParentElement); 79 80 const logs = []; 81 class ChildElement extends contentWindow.HTMLElement { 82 connectedCallback() { logs.push('connected'); } 83 disconnectedCallback() { logs.push('disconnected'); } 84 } 85 contentWindow.customElements.define('child-element', ChildElement); 86 87 const parent = new ParentElement; 88 const child = new ChildElement; 89 parent.appendChild(child); 90 contentDocument.body.appendChild(parent); 91 parent.remove(); 92 assert_array_equals(logs, ['connected', 'begin', 'disconnected', 'connected', 'end']); 93 }, 'Connecting a element with connectedCallback while it has a disconnectedCallback in its custom element reaction queue must result in disconnectedCallback getting invoked before the insertion completes'); 94 95 test_with_window((contentWindow, contentDocument) => { 96 class ParentElement extends contentWindow.HTMLElement { 97 disconnectedCallback() 98 { 99 logs.push('begin'); 100 contentDocument.body.appendChild(this.firstChild); 101 logs.push('end'); 102 } 103 } 104 contentWindow.customElements.define('parent-element', ParentElement); 105 106 const logs = []; 107 class ChildElement extends contentWindow.HTMLElement { 108 disconnectedCallback() { logs.push('disconnected'); } 109 } 110 contentWindow.customElements.define('child-element', ChildElement); 111 112 const parent = new ParentElement; 113 const child = new ChildElement; 114 parent.appendChild(child); 115 contentDocument.body.appendChild(parent); 116 parent.remove(); 117 assert_array_equals(logs, ['begin', 'end', 'disconnected']); 118 }, 'Connecting an element without connectedCallback while it has a disconnectedCallback in its custom element reaction queue must not result in disconnectedCallback getting invoked before the insertion completes'); 119 120 test_with_window((contentWindow, contentDocument) => { 121 class ParentElement extends contentWindow.HTMLElement { 122 connectedCallback() 123 { 124 logs.push('begin'); 125 document.adoptNode(this.firstChild); 126 logs.push('end'); 127 } 128 } 129 contentWindow.customElements.define('parent-element', ParentElement); 130 131 const logs = []; 132 class ChildElement extends contentWindow.HTMLElement { 133 adoptedCallback() { logs.push('adopted'); } 134 connectedCallback() { logs.push('connected'); } 135 } 136 contentWindow.customElements.define('child-element', ChildElement); 137 138 const parent = new ParentElement; 139 const child = new ChildElement; 140 parent.appendChild(child); 141 contentDocument.body.appendChild(parent); 142 assert_array_equals(logs, ['begin', 'connected', 'adopted', 'end']); 143 }, 'Adopting an element with adoptingCallback while it has a connectedCallback in its custom element reaction queue must result in connectedCallback getting invoked before the adoption completes'); 144 145 test_with_window((contentWindow, contentDocument) => { 146 class ParentElement extends contentWindow.HTMLElement { 147 connectedCallback() 148 { 149 logs.push('begin'); 150 document.adoptNode(this.firstChild); 151 logs.push('end'); 152 } 153 } 154 contentWindow.customElements.define('parent-element', ParentElement); 155 156 const logs = []; 157 class ChildElement extends contentWindow.HTMLElement { 158 connectedCallback() { logs.push('connected'); } 159 } 160 contentWindow.customElements.define('child-element', ChildElement); 161 162 const parent = new ParentElement; 163 const child = new ChildElement; 164 parent.appendChild(child); 165 contentDocument.body.appendChild(parent); 166 assert_array_equals(logs, ['begin', 'end', 'connected']); 167 }, 'Adopting an element without adoptingCallback while it has a connectedCallback in its custom element reaction queue must not result in connectedCallback getting invoked before the adoption completes'); 168 169 test_with_window((contentWindow, contentDocument) => { 170 class ParentElement extends contentWindow.HTMLElement { 171 connectedCallback() 172 { 173 logs.push('begin'); 174 this.firstChild.setAttribute('title', 'foo'); 175 logs.push('end'); 176 } 177 } 178 contentWindow.customElements.define('parent-element', ParentElement); 179 180 const logs = []; 181 class ChildElement extends contentWindow.HTMLElement { 182 attributeChangedCallback() { logs.push('attributeChanged'); } 183 connectedCallback() { logs.push('connected'); } 184 static get observedAttributes() { return ['title']; } 185 } 186 contentWindow.customElements.define('child-element', ChildElement); 187 188 const parent = new ParentElement; 189 const child = new ChildElement; 190 parent.appendChild(child); 191 contentDocument.body.appendChild(parent); 192 assert_array_equals(logs, ['begin', 'connected', 'attributeChanged', 'end']); 193 }, 'Setting an observed attribute on an element with attributeChangedCallback while it has a connectedCallback in its custom element reaction queue must result in connectedCallback getting invoked before the attribute change completes'); 194 195 test_with_window((contentWindow, contentDocument) => { 196 class ParentElement extends contentWindow.HTMLElement { 197 connectedCallback() 198 { 199 logs.push('begin'); 200 this.firstChild.setAttribute('lang', 'en'); 201 logs.push('end'); 202 } 203 } 204 contentWindow.customElements.define('parent-element', ParentElement); 205 206 const logs = []; 207 class ChildElement extends contentWindow.HTMLElement { 208 attributeChangedCallback() { logs.push('attributeChanged'); } 209 connectedCallback() { logs.push('connected'); } 210 static get observedAttributes() { return ['title']; } 211 } 212 contentWindow.customElements.define('child-element', ChildElement); 213 214 const parent = new ParentElement; 215 const child = new ChildElement; 216 parent.appendChild(child); 217 contentDocument.body.appendChild(parent); 218 assert_array_equals(logs, ['begin', 'end', 'connected']); 219 }, 'Setting an observed attribute on an element with attributeChangedCallback while it has a connectedCallback in its custom element reaction queue must not result in connectedCallback getting invoked before the attribute change completes'); 220 221 </script> 222 </body> 223 </html>