| <!DOCTYPE html> |
| <html> |
| <head> |
| <title>Custom Elements: The ':defined' pseudo-class applies to elements that are defined.</title> |
| <link rel="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"/> |
| <link rel="help" href="https://w3c.github.io/webcomponents/spec/custom/#the-defined-element-pseudo-class-defined"> |
| <script src="../../resources/testharness.js"></script> |
| <script src="../../resources/testharnessreport.js"></script> |
| </head> |
| <body> |
| <div id="log"></div> |
| <script> |
| setup({allow_uncaught_exception:true}); |
| |
| var upgradeCandidate = document.createElement('my-element'); |
| |
| test(function () { |
| assert_false(upgradeCandidate.matches(':defined')); |
| }, 'The defined flag of a custom element must not be set if a custom element has not been upgraded yet'); |
| |
| class MyElement extends HTMLElement { |
| constructor() { |
| super(); |
| this.matchInsideConstructor = this.matches(':defined'); |
| } |
| } |
| |
| customElements.define('my-element', MyElement); |
| |
| test(function () { |
| assert_false(upgradeCandidate.matches(':defined')); |
| }, 'The defined flag of a custom element must not be set if a custom element has not been upgraded yet even if the element has been defined'); |
| |
| test(function () { |
| document.body.appendChild(upgradeCandidate); |
| assert_true(upgradeCandidate.matches(':defined')); |
| assert_false(!!upgradeCandidate.matchInsideConstructor, 'Upgrading a custom element must set defined flag after invoking the constructor'); |
| }, 'The defined flag of a custom element must be set when a custom element is successfully upgraded'); |
| |
| test(function () { |
| var definedElement = document.createElement('my-element'); |
| assert_true(definedElement.matches(':defined')); |
| assert_true(!!definedElement.matchInsideConstructor); |
| }, 'The defined flag of a custom element must be set inside the HTMLElement constructor'); |
| |
| test(function () { |
| var upgradedElement = document.createElement('my-element').cloneNode(true); |
| assert_true(upgradedElement.matches(':defined')); |
| assert_false(!!upgradedElement.matchInsideConstructor, 'Upgrading a custom element must set defined flag after invoking the constructor'); |
| }, 'The defined flag of an upgraded custom element must be set'); |
| |
| document.write('<my-other-element></my-other-element>'); |
| |
| test(function () { |
| var parserCreatedUnfefinedElement = document.querySelector('my-other-element'); |
| assert_false(parserCreatedUnfefinedElement.matches(':defined')); |
| assert_false(!!parserCreatedUnfefinedElement.matchInsideConstructor); |
| }, 'The defined flag of a custom element created by HTML parser must be unset if there is no matching definition'); |
| |
| document.write('<my-element id="parser-created-defined-element"></my-element>'); |
| |
| test(function () { |
| var parserCreatedDefinedElement = document.getElementById('parser-created-defined-element'); |
| assert_true(parserCreatedDefinedElement.matches(':defined')); |
| assert_true(!!parserCreatedDefinedElement.matchInsideConstructor, |
| 'The defined flag must be set inside HTMLElement constructor when HTMLParser creates a custom element synchronously'); |
| }, 'The defined flag of a custom element created by HTML parser must be set if there is a matching definition'); |
| |
| class ReturnsAnotherNode extends HTMLElement { |
| constructor() { |
| super(); |
| this.matchInsideConstructor = this.matches(':defined'); |
| ReturnsAnotherNode.lastInstance = this; |
| return document.createTextNode(''); |
| } |
| } |
| customElements.define('returns-another-node', ReturnsAnotherNode); |
| |
| var uncaughtError; |
| window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; } |
| document.write('<returns-another-node></returns-another-node>'); |
| window.onerror = null; |
| |
| test(function () { |
| var instance = document.querySelector('returns-another-node'); |
| assert_equals(uncaughtError.name, 'TypeError', 'The HTML parser must report a TypeError when a custom element returns a non-Element node.'); |
| assert_not_equals(instance, ReturnsAnotherNode.lastInstance, 'The element inserted by HTML parser must not be the one returned by super() call'); |
| assert_true(instance instanceof HTMLElement, 'The element inserted by HTML parser must be a HTMLElement'); |
| assert_false(instance instanceof ReturnsAnotherNode, 'The element inserted by HTML parser must be a custom element'); |
| assert_false(instance.matches(':defined'), 'The defined flag must not be set on the element inserted by HTML parser'); |
| assert_true(!!ReturnsAnotherNode.lastInstance.matchInsideConstructor, |
| 'The defined flag must be set inside HTMLElement constructor when HTMLParser creates a custom element synchronously'); |
| }, 'The element inserted by HTML parser must not have the defined flag set if the constructor returns a Text node'); |
| |
| test(function () { |
| var instance = document.createElement('returns-another-node-2'); |
| try { |
| customElements.define('returns-another-node-2', class extends ReturnsAnotherNode {}); |
| } catch (e) { } |
| assert_false(instance.matches(':defined')); |
| assert_false(!!instance.matchInsideConstructor, |
| 'Creating a custom element must leave the defined flag unset when synchronous custom elements flag is not set'); |
| }, 'The defined flag of a custom element must be set after checking the returned result is an instance of HTMLElement when upgrading a custom element'); |
| |
| test(function () { |
| var matchInsideConstructor = false; |
| const theException = {name: 'bad'}; |
| class ThrowsException extends HTMLElement { |
| constructor() { |
| super(); |
| matchInsideConstructor = this.matches(':defined'); |
| throw theException; |
| } |
| }; |
| customElements.define('throws-exception', ThrowsException); |
| var instance; |
| assert_throws_exactly(theException, function () { instance = new ThrowsException; }); |
| assert_true(matchInsideConstructor); |
| }, 'The defined flag of a custom element must be set inside a constructor when constructing a custom element synchronously' |
| + ' even if the constructor threw an exception later'); |
| |
| test(function () { |
| var instance = document.createElement('throws-exception-2'); |
| const theException = {name: 'bad'}; |
| document.body.appendChild(instance); |
| var uncaughtError; |
| window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; } |
| customElements.define('throws-exception-2', class extends HTMLElement { |
| constructor() { |
| throw theException; |
| } |
| }); |
| assert_equals(uncaughtError.name, 'bad'); |
| assert_false(instance.matches(':defined')); |
| }, 'The defined flag of a custom element must not be set when an upgrade of a custom element fails'); |
| |
| </script> |
| </body> |
| </html> |