| <!DOCTYPE html> |
| <html> |
| <head> |
| <title>Custom Elements: must enqueue an element on the appropriate element queue after checking callback is null and the attribute name</title> |
| <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> |
| <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"> |
| <link rel="help" content="https://html.spec.whatwg.org/multipage/custom-elements.html#enqueue-a-custom-element-callback-reaction"> |
| <link rel="help" content="https://github.com/w3c/webcomponents/issues/760"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="resources/custom-elements-helpers.js"></script> |
| </head> |
| <body> |
| <script> |
| |
| test_with_window((contentWindow, contentDocument) => { |
| class ParentElement extends contentWindow.HTMLElement { |
| connectedCallback() |
| { |
| logs.push('begin'); |
| const child = this.firstChild; |
| child.remove(); |
| logs.push('end'); |
| } |
| } |
| contentWindow.customElements.define('parent-element', ParentElement); |
| |
| const logs = []; |
| class ChildElement extends contentWindow.HTMLElement { |
| connectedCallback() { logs.push('connected'); } |
| disconnectedCallback() { logs.push('disconnected'); } |
| } |
| contentWindow.customElements.define('child-element', ChildElement); |
| |
| const parent = new ParentElement; |
| const child = new ChildElement; |
| parent.appendChild(child); |
| |
| contentDocument.body.appendChild(parent); |
| assert_array_equals(logs, ['begin', 'connected', 'disconnected', 'end']); |
| }, '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'); |
| |
| test_with_window((contentWindow, contentDocument) => { |
| class ParentElement extends contentWindow.HTMLElement { |
| connectedCallback() |
| { |
| logs.push('begin'); |
| const child = this.firstChild; |
| child.remove(); |
| logs.push('end'); |
| } |
| } |
| contentWindow.customElements.define('parent-element', ParentElement); |
| |
| const logs = []; |
| class ChildElement extends contentWindow.HTMLElement { |
| connectedCallback() { logs.push('connected'); } |
| } |
| contentWindow.customElements.define('child-element', ChildElement); |
| |
| const parent = new ParentElement; |
| const child = new ChildElement; |
| parent.appendChild(child); |
| |
| contentDocument.body.appendChild(parent); |
| assert_array_equals(logs, ['begin', 'end', 'connected']); |
| }, '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'); |
| |
| test_with_window((contentWindow, contentDocument) => { |
| class ParentElement extends contentWindow.HTMLElement { |
| disconnectedCallback() |
| { |
| logs.push('begin'); |
| contentDocument.body.appendChild(this.firstChild); |
| logs.push('end'); |
| } |
| } |
| contentWindow.customElements.define('parent-element', ParentElement); |
| |
| const logs = []; |
| class ChildElement extends contentWindow.HTMLElement { |
| connectedCallback() { logs.push('connected'); } |
| disconnectedCallback() { logs.push('disconnected'); } |
| } |
| contentWindow.customElements.define('child-element', ChildElement); |
| |
| const parent = new ParentElement; |
| const child = new ChildElement; |
| parent.appendChild(child); |
| contentDocument.body.appendChild(parent); |
| parent.remove(); |
| assert_array_equals(logs, ['connected', 'begin', 'disconnected', 'connected', 'end']); |
| }, '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'); |
| |
| test_with_window((contentWindow, contentDocument) => { |
| class ParentElement extends contentWindow.HTMLElement { |
| disconnectedCallback() |
| { |
| logs.push('begin'); |
| contentDocument.body.appendChild(this.firstChild); |
| logs.push('end'); |
| } |
| } |
| contentWindow.customElements.define('parent-element', ParentElement); |
| |
| const logs = []; |
| class ChildElement extends contentWindow.HTMLElement { |
| disconnectedCallback() { logs.push('disconnected'); } |
| } |
| contentWindow.customElements.define('child-element', ChildElement); |
| |
| const parent = new ParentElement; |
| const child = new ChildElement; |
| parent.appendChild(child); |
| contentDocument.body.appendChild(parent); |
| parent.remove(); |
| assert_array_equals(logs, ['begin', 'end', 'disconnected']); |
| }, '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'); |
| |
| test_with_window((contentWindow, contentDocument) => { |
| class ParentElement extends contentWindow.HTMLElement { |
| connectedCallback() |
| { |
| logs.push('begin'); |
| document.adoptNode(this.firstChild); |
| logs.push('end'); |
| } |
| } |
| contentWindow.customElements.define('parent-element', ParentElement); |
| |
| const logs = []; |
| class ChildElement extends contentWindow.HTMLElement { |
| adoptedCallback() { logs.push('adopted'); } |
| connectedCallback() { logs.push('connected'); } |
| } |
| contentWindow.customElements.define('child-element', ChildElement); |
| |
| const parent = new ParentElement; |
| const child = new ChildElement; |
| parent.appendChild(child); |
| contentDocument.body.appendChild(parent); |
| assert_array_equals(logs, ['begin', 'connected', 'adopted', 'end']); |
| }, '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'); |
| |
| test_with_window((contentWindow, contentDocument) => { |
| class ParentElement extends contentWindow.HTMLElement { |
| connectedCallback() |
| { |
| logs.push('begin'); |
| document.adoptNode(this.firstChild); |
| logs.push('end'); |
| } |
| } |
| contentWindow.customElements.define('parent-element', ParentElement); |
| |
| const logs = []; |
| class ChildElement extends contentWindow.HTMLElement { |
| connectedCallback() { logs.push('connected'); } |
| } |
| contentWindow.customElements.define('child-element', ChildElement); |
| |
| const parent = new ParentElement; |
| const child = new ChildElement; |
| parent.appendChild(child); |
| contentDocument.body.appendChild(parent); |
| assert_array_equals(logs, ['begin', 'end', 'connected']); |
| }, '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'); |
| |
| test_with_window((contentWindow, contentDocument) => { |
| class ParentElement extends contentWindow.HTMLElement { |
| connectedCallback() |
| { |
| logs.push('begin'); |
| this.firstChild.setAttribute('title', 'foo'); |
| logs.push('end'); |
| } |
| } |
| contentWindow.customElements.define('parent-element', ParentElement); |
| |
| const logs = []; |
| class ChildElement extends contentWindow.HTMLElement { |
| attributeChangedCallback() { logs.push('attributeChanged'); } |
| connectedCallback() { logs.push('connected'); } |
| static get observedAttributes() { return ['title']; } |
| } |
| contentWindow.customElements.define('child-element', ChildElement); |
| |
| const parent = new ParentElement; |
| const child = new ChildElement; |
| parent.appendChild(child); |
| contentDocument.body.appendChild(parent); |
| assert_array_equals(logs, ['begin', 'connected', 'attributeChanged', 'end']); |
| }, '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'); |
| |
| test_with_window((contentWindow, contentDocument) => { |
| class ParentElement extends contentWindow.HTMLElement { |
| connectedCallback() |
| { |
| logs.push('begin'); |
| this.firstChild.setAttribute('lang', 'en'); |
| logs.push('end'); |
| } |
| } |
| contentWindow.customElements.define('parent-element', ParentElement); |
| |
| const logs = []; |
| class ChildElement extends contentWindow.HTMLElement { |
| attributeChangedCallback() { logs.push('attributeChanged'); } |
| connectedCallback() { logs.push('connected'); } |
| static get observedAttributes() { return ['title']; } |
| } |
| contentWindow.customElements.define('child-element', ChildElement); |
| |
| const parent = new ParentElement; |
| const child = new ChildElement; |
| parent.appendChild(child); |
| contentDocument.body.appendChild(parent); |
| assert_array_equals(logs, ['begin', 'end', 'connected']); |
| }, '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'); |
| |
| </script> |
| </body> |
| </html> |