blob: 0c0c3b08a2ab812f6ba863c503a46bc89b5dfa5e [file] [log] [blame]
<!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>