1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
<!DOCTYPE html>
<html>
<head>
<title>Custom Elements: Upgrading</title>
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
<meta name="assert" content="Node.prototype.cloneNode should upgrade a custom element">
<link rel="help" href="https://html.spec.whatwg.org/#upgrades">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/custom-elements-helpers.js"></script>
</head>
<body>
<div id="log"></div>
<script>
setup({allow_uncaught_exception:true});
test(function () {
class MyCustomElement extends HTMLElement {}
customElements.define('my-custom-element', MyCustomElement);
var instance = document.createElement('my-custom-element');
assert_true(instance instanceof HTMLElement);
assert_true(instance instanceof MyCustomElement);
var clone = instance.cloneNode(false);
assert_not_equals(instance, clone);
assert_true(clone instanceof HTMLElement,
'A cloned custom element must be an instance of HTMLElement');
assert_true(clone instanceof MyCustomElement,
'A cloned custom element must be an instance of the custom element');
}, 'Node.prototype.cloneNode(false) must be able to clone a custom element');
test(function () {
class AutonomousCustomElement extends HTMLElement {};
class IsCustomElement extends HTMLElement {};
customElements.define('autonomous-custom-element', AutonomousCustomElement);
customElements.define('is-custom-element', IsCustomElement);
var instance = document.createElement('autonomous-custom-element', { is: "is-custom-element"});
assert_true(instance instanceof HTMLElement);
assert_true(instance instanceof AutonomousCustomElement);
var clone = instance.cloneNode(false);
assert_not_equals(instance, clone);
assert_true(clone instanceof HTMLElement,
'A cloned custom element must be an instance of HTMLElement');
assert_true(clone instanceof AutonomousCustomElement,
'A cloned custom element must be an instance of the custom element');
}, 'Node.prototype.cloneNode(false) must be able to clone as a autonomous custom element when it contains is attribute');
test_with_window(function (contentWindow) {
var contentDocument = contentWindow.document;
class MyCustomElement extends contentWindow.HTMLElement {}
contentWindow.customElements.define('my-custom-element', MyCustomElement);
var instance = contentDocument.createElement('my-custom-element');
assert_true(instance instanceof contentWindow.HTMLElement);
assert_true(instance instanceof MyCustomElement);
var clone = instance.cloneNode(false);
assert_not_equals(instance, clone);
assert_true(clone instanceof contentWindow.HTMLElement,
'A cloned custom element must be an instance of HTMLElement');
assert_true(clone instanceof MyCustomElement,
'A cloned custom element must be an instance of the custom element');
}, 'Node.prototype.cloneNode(false) must be able to clone a custom element inside an iframe');
test_with_window(function (contentWindow) {
var contentDocument = contentWindow.document;
class MyCustomElement extends contentWindow.HTMLElement { }
contentWindow.customElements.define('my-custom-element', MyCustomElement);
var instance = contentDocument.createElement('my-custom-element');
var container = contentDocument.createElement('div');
container.appendChild(instance);
var containerClone = container.cloneNode(true);
assert_true(containerClone instanceof contentWindow.HTMLDivElement);
var clone = containerClone.firstChild;
assert_not_equals(instance, clone);
assert_true(clone instanceof contentWindow.HTMLElement,
'A cloned custom element must be an instance of HTMLElement');
assert_true(clone instanceof MyCustomElement,
'A cloned custom element must be an instance of the custom element');
}, 'Node.prototype.cloneNode(true) must be able to clone a descendent custom element');
test_with_window(function (contentWindow) {
var parentNodeInConstructor;
var previousSiblingInConstructor;
var nextSiblingInConstructor;
class MyCustomElement extends contentWindow.HTMLElement {
constructor() {
super();
parentNodeInConstructor = this.parentNode;
previousSiblingInConstructor = this.previousSibling;
nextSiblingInConstructor = this.nextSibling;
}
}
contentWindow.customElements.define('my-custom-element', MyCustomElement);
var contentDocument = contentWindow.document;
var instance = contentDocument.createElement('my-custom-element');
var siblingBeforeInstance = contentDocument.createElement('b');
var siblingAfterInstance = contentDocument.createElement('a');
var container = contentDocument.createElement('div');
container.appendChild(siblingBeforeInstance);
container.appendChild(instance);
container.appendChild(siblingAfterInstance);
var containerClone = container.cloneNode(true);
assert_equals(parentNodeInConstructor, containerClone,
'An upgraded element must have its parentNode set before the custom element constructor is called');
assert_equals(previousSiblingInConstructor, containerClone.firstChild,
'An upgraded element must have its previousSibling set before the custom element constructor is called');
assert_equals(nextSiblingInConstructor, containerClone.lastChild,
'An upgraded element must have its nextSibling set before the custom element constructor is called');
}, 'Node.prototype.cloneNode(true) must set parentNode, previousSibling, and nextSibling before upgrading custom elements');
// The error reporting isn't clear yet when multiple globals involved in custom
// element, see w3c/webcomponents#635, so using test_with_window is not a good
// idea here.
test(function () {
class MyCustomElement extends HTMLElement {
constructor(doNotCreateItself) {
super();
if (!doNotCreateItself)
new MyCustomElement(true);
}
}
customElements.define('my-custom-element-constructed-after-super', MyCustomElement);
var instance = new MyCustomElement(false);
var uncaughtError;
window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
instance.cloneNode(false);
assert_equals(uncaughtError.name, 'TypeError');
}, 'HTMLElement constructor must throw an TypeError when the top of the construction stack is marked AlreadyConstructed'
+ ' due to a custom element constructor constructing itself after super() call');
test(function () {
class MyCustomElement extends HTMLElement {
constructor(doNotCreateItself) {
if (!doNotCreateItself)
new MyCustomElement(true);
super();
}
}
customElements.define('my-custom-element-constructed-before-super', MyCustomElement);
var instance = new MyCustomElement(false);
var uncaughtError;
window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
instance.cloneNode(false);
assert_equals(uncaughtError.name, 'TypeError');
}, 'HTMLElement constructor must throw an TypeError when the top of the construction stack is marked AlreadyConstructed'
+ ' due to a custom element constructor constructing itself before super() call');
test(function () {
var returnSpan = false;
class MyCustomElement extends HTMLElement {
constructor() {
super();
if (returnSpan)
return document.createElement('span');
}
}
customElements.define('my-custom-element-return-another', MyCustomElement);
var instance = new MyCustomElement(false);
returnSpan = true;
var uncaughtError;
window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
instance.cloneNode(false);
assert_equals(uncaughtError.name, 'TypeError');
}, 'Upgrading a custom element must throw TypeError when the custom element\'s constructor returns another element');
test(function () {
var instance = document.createElement('my-custom-element-throw-exception');
document.body.appendChild(instance);
var calls = [];
class MyCustomElement extends HTMLElement {
constructor() {
super();
calls.push(this);
throw 'bad';
}
}
var uncaughtError;
window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
customElements.define('my-custom-element-throw-exception', MyCustomElement);
assert_equals(uncaughtError, 'bad');
assert_array_equals(calls, [instance]);
document.body.removeChild(instance);
document.body.appendChild(instance);
assert_array_equals(calls, [instance]);
}, 'Inserting an element must not try to upgrade a custom element when it had already failed to upgrade once');
</script>
</body>
</html>
|