| <!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> |