mirror of
https://github.com/servo/servo.git
synced 2025-08-25 23:28:21 +01:00
Update web-platform-tests to revision e8bfc205e36ad699601212cd50083870bad9a75d
This commit is contained in:
parent
65dd6d4340
commit
ccdb0a3458
1428 changed files with 118036 additions and 9786 deletions
|
@ -0,0 +1,580 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CustomElementRegistry interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="CustomElementRegistry interface must exist">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test(function () {
|
||||
assert_true('define' in CustomElementRegistry.prototype, '"define" exists on CustomElementRegistry.prototype');
|
||||
assert_true('define' in customElements, '"define" exists on window.customElements');
|
||||
}, 'CustomElementRegistry interface must have define as a method');
|
||||
|
||||
test(function () {
|
||||
assert_throws({'name': 'TypeError'}, function () { customElements.define('badname', 1); },
|
||||
'customElements.define must throw a TypeError when the element interface is a number');
|
||||
assert_throws({'name': 'TypeError'}, function () { customElements.define('badname', '123'); },
|
||||
'customElements.define must throw a TypeError when the element interface is a string');
|
||||
assert_throws({'name': 'TypeError'}, function () { customElements.define('badname', {}); },
|
||||
'customElements.define must throw a TypeError when the element interface is an object');
|
||||
assert_throws({'name': 'TypeError'}, function () { customElements.define('badname', []); },
|
||||
'customElements.define must throw a TypeError when the element interface is an array');
|
||||
}, 'customElements.define must throw when the element interface is not a constructor');
|
||||
|
||||
test(function () {
|
||||
customElements.define('custom-html-element', HTMLElement);
|
||||
}, 'customElements.define must not throw the constructor is HTMLElement');
|
||||
|
||||
test(function () {
|
||||
class MyCustomElement extends HTMLElement {};
|
||||
|
||||
assert_throws({'name': 'SyntaxError'}, function () { customElements.define(null, MyCustomElement); },
|
||||
'customElements.define must throw a SyntaxError if the tag name is null');
|
||||
assert_throws({'name': 'SyntaxError'}, function () { customElements.define('', MyCustomElement); },
|
||||
'customElements.define must throw a SyntaxError if the tag name is empty');
|
||||
assert_throws({'name': 'SyntaxError'}, function () { customElements.define('abc', MyCustomElement); },
|
||||
'customElements.define must throw a SyntaxError if the tag name does not contain "-"');
|
||||
assert_throws({'name': 'SyntaxError'}, function () { customElements.define('a-Bc', MyCustomElement); },
|
||||
'customElements.define must throw a SyntaxError if the tag name contains an upper case letter');
|
||||
|
||||
var builtinTagNames = [
|
||||
'annotation-xml',
|
||||
'color-profile',
|
||||
'font-face',
|
||||
'font-face-src',
|
||||
'font-face-uri',
|
||||
'font-face-format',
|
||||
'font-face-name',
|
||||
'missing-glyph'
|
||||
];
|
||||
|
||||
for (var tagName of builtinTagNames) {
|
||||
assert_throws({'name': 'SyntaxError'}, function () { customElements.define(tagName, MyCustomElement); },
|
||||
'customElements.define must throw a SyntaxError if the tag name is "' + tagName + '"');
|
||||
}
|
||||
|
||||
}, 'customElements.define must throw with an invalid name');
|
||||
|
||||
test(function () {
|
||||
class SomeCustomElement extends HTMLElement {};
|
||||
|
||||
var calls = [];
|
||||
var OtherCustomElement = new Proxy(class extends HTMLElement {}, {
|
||||
get: function (target, name) {
|
||||
calls.push(name);
|
||||
return target[name];
|
||||
}
|
||||
})
|
||||
|
||||
customElements.define('some-custom-element', SomeCustomElement);
|
||||
assert_throws({'name': 'NotSupportedError'}, function () { customElements.define('some-custom-element', OtherCustomElement); },
|
||||
'customElements.define must throw a NotSupportedError if the specified tag name is already used');
|
||||
assert_array_equals(calls, [], 'customElements.define must validate the custom element name before getting the prototype of the constructor');
|
||||
|
||||
}, 'customElements.define must throw when there is already a custom element of the same name');
|
||||
|
||||
test(function () {
|
||||
class AnotherCustomElement extends HTMLElement {};
|
||||
|
||||
customElements.define('another-custom-element', AnotherCustomElement);
|
||||
assert_throws({'name': 'NotSupportedError'}, function () { customElements.define('some-other-element', AnotherCustomElement); },
|
||||
'customElements.define must throw a NotSupportedError if the specified class already defines an element');
|
||||
|
||||
}, 'customElements.define must throw a NotSupportedError when there is already a custom element with the same class');
|
||||
|
||||
test(function () {
|
||||
var outerCalls = [];
|
||||
var OuterCustomElement = new Proxy(class extends HTMLElement { }, {
|
||||
get: function (target, name) {
|
||||
outerCalls.push(name);
|
||||
customElements.define('inner-custom-element', InnerCustomElement);
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
var innerCalls = [];
|
||||
var InnerCustomElement = new Proxy(class extends HTMLElement { }, {
|
||||
get: function (target, name) {
|
||||
outerCalls.push(name);
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
|
||||
assert_throws({'name': 'NotSupportedError'}, function () { customElements.define('outer-custom-element', OuterCustomElement); },
|
||||
'customElements.define must throw a NotSupportedError if the specified class already defines an element');
|
||||
assert_array_equals(outerCalls, ['prototype'], 'customElements.define must get "prototype"');
|
||||
assert_array_equals(innerCalls, [],
|
||||
'customElements.define must throw a NotSupportedError when element definition is running flag is set'
|
||||
+ ' before getting the prototype of the constructor');
|
||||
|
||||
}, 'customElements.define must throw a NotSupportedError when element definition is running flag is set');
|
||||
|
||||
test(function () {
|
||||
var calls = [];
|
||||
var ElementWithBadInnerConstructor = new Proxy(class extends HTMLElement { }, {
|
||||
get: function (target, name) {
|
||||
calls.push(name);
|
||||
customElements.define('inner-custom-element', 1);
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
|
||||
assert_throws({'name': 'TypeError'}, function () {
|
||||
customElements.define('element-with-bad-inner-constructor', ElementWithBadInnerConstructor);
|
||||
}, 'customElements.define must throw a NotSupportedError if IsConstructor(constructor) is false');
|
||||
|
||||
assert_array_equals(calls, ['prototype'], 'customElements.define must get "prototype"');
|
||||
}, 'customElements.define must check IsConstructor on the constructor before checking the element definition is running flag');
|
||||
|
||||
test(function () {
|
||||
var calls = [];
|
||||
var ElementWithBadInnerName = new Proxy(class extends HTMLElement { }, {
|
||||
get: function (target, name) {
|
||||
calls.push(name);
|
||||
customElements.define('badname', class extends HTMLElement {});
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
|
||||
assert_throws({'name': 'SyntaxError'}, function () {
|
||||
customElements.define('element-with-bad-inner-name', ElementWithBadInnerName);
|
||||
}, 'customElements.define must throw a SyntaxError if the specified name is not a valid custom element name');
|
||||
|
||||
assert_array_equals(calls, ['prototype'], 'customElements.define must get "prototype"');
|
||||
}, 'customElements.define must validate the custom element name before checking the element definition is running flag');
|
||||
|
||||
test(function () {
|
||||
var unresolvedElement = document.createElement('constructor-calls-define');
|
||||
document.body.appendChild(unresolvedElement);
|
||||
var elementUpgradedDuringUpgrade = document.createElement('defined-during-upgrade');
|
||||
document.body.appendChild(elementUpgradedDuringUpgrade);
|
||||
|
||||
var DefinedDuringUpgrade = class extends HTMLElement { };
|
||||
|
||||
class ConstructorCallsDefine extends HTMLElement {
|
||||
constructor() {
|
||||
customElements.define('defined-during-upgrade', DefinedDuringUpgrade);
|
||||
assert_false(unresolvedElement instanceof ConstructorCallsDefine);
|
||||
assert_true(elementUpgradedDuringUpgrade instanceof DefinedDuringUpgrade);
|
||||
super();
|
||||
assert_true(unresolvedElement instanceof ConstructorCallsDefine);
|
||||
assert_true(elementUpgradedDuringUpgrade instanceof DefinedDuringUpgrade);
|
||||
}
|
||||
}
|
||||
|
||||
assert_false(unresolvedElement instanceof ConstructorCallsDefine);
|
||||
assert_false(elementUpgradedDuringUpgrade instanceof DefinedDuringUpgrade);
|
||||
|
||||
customElements.define('constructor-calls-define', ConstructorCallsDefine);
|
||||
}, 'customElements.define unset the element definition is running flag before upgrading custom elements');
|
||||
|
||||
(function () {
|
||||
var testCase = async_test('customElements.define must not throw'
|
||||
+' when defining another custom element in a different global object during Get(constructor, "prototype")', {timeout: 100});
|
||||
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.onload = function () {
|
||||
testCase.step(function () {
|
||||
var InnerCustomElement = class extends iframe.contentWindow.HTMLElement {};
|
||||
var calls = [];
|
||||
var proxy = new Proxy(class extends HTMLElement { }, {
|
||||
get: function (target, name) {
|
||||
calls.push(name);
|
||||
iframe.contentWindow.customElements.define('another-custom-element', InnerCustomElement);
|
||||
return target[name];
|
||||
}
|
||||
})
|
||||
customElements.define('element-with-inner-element-define', proxy);
|
||||
assert_array_equals(calls, ['prototype'], 'customElements.define must get "prototype"');
|
||||
assert_true(iframe.contentDocument.createElement('another-custom-element') instanceof InnerCustomElement);
|
||||
});
|
||||
document.body.removeChild(iframe);
|
||||
testCase.done();
|
||||
}
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
})();
|
||||
|
||||
test(function () {
|
||||
var calls = [];
|
||||
var ElementWithBadInnerName = new Proxy(class extends HTMLElement { }, {
|
||||
get: function (target, name) {
|
||||
calls.push(name);
|
||||
customElements.define('badname', class extends HTMLElement {});
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
|
||||
assert_throws({'name': 'SyntaxError'}, function () {
|
||||
customElements.define('element-with-bad-inner-name', ElementWithBadInnerName);
|
||||
}, 'customElements.define must throw a SyntaxError if the specified name is not a valid custom element name');
|
||||
|
||||
assert_array_equals(calls, ['prototype'], 'customElements.define must get "prototype"');
|
||||
}, '');
|
||||
|
||||
test(function () {
|
||||
var calls = [];
|
||||
var proxy = new Proxy(class extends HTMLElement { }, {
|
||||
get: function (target, name) {
|
||||
calls.push(name);
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
customElements.define('proxy-element', proxy);
|
||||
assert_array_equals(calls, ['prototype']);
|
||||
}, 'customElements.define must get "prototype" property of the constructor');
|
||||
|
||||
test(function () {
|
||||
var proxy = new Proxy(class extends HTMLElement { }, {
|
||||
get: function (target, name) {
|
||||
throw {name: 'expectedError'};
|
||||
}
|
||||
});
|
||||
assert_throws({'name': 'expectedError'}, function () { customElements.define('element-with-string-prototype', proxy); });
|
||||
}, 'customElements.define must rethrow an exception thrown while getting "prototype" property of the constructor');
|
||||
|
||||
test(function () {
|
||||
var returnedValue;
|
||||
var proxy = new Proxy(class extends HTMLElement { }, {
|
||||
get: function (target, name) { return returnedValue; }
|
||||
});
|
||||
|
||||
returnedValue = null;
|
||||
assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-string-prototype', proxy); },
|
||||
'customElements.define must throw when "prototype" property of the constructor is null');
|
||||
returnedValue = undefined;
|
||||
assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-string-prototype', proxy); },
|
||||
'customElements.define must throw when "prototype" property of the constructor is undefined');
|
||||
returnedValue = 'hello';
|
||||
assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-string-prototype', proxy); },
|
||||
'customElements.define must throw when "prototype" property of the constructor is a string');
|
||||
returnedValue = 1;
|
||||
assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-string-prototype', proxy); },
|
||||
'customElements.define must throw when "prototype" property of the constructor is a number');
|
||||
|
||||
}, 'customElements.define must throw when "prototype" property of the constructor is not an object');
|
||||
|
||||
test(function () {
|
||||
var constructor = function () {}
|
||||
var calls = [];
|
||||
constructor.prototype = new Proxy(constructor.prototype, {
|
||||
get: function (target, name) {
|
||||
calls.push(name);
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
customElements.define('element-with-proxy-prototype', constructor);
|
||||
assert_array_equals(calls, ['connectedCallback', 'disconnectedCallback', 'adoptedCallback', 'attributeChangedCallback']);
|
||||
}, 'customElements.define must get callbacks of the constructor prototype');
|
||||
|
||||
test(function () {
|
||||
var constructor = function () {}
|
||||
var calls = [];
|
||||
constructor.prototype = new Proxy(constructor.prototype, {
|
||||
get: function (target, name) {
|
||||
calls.push(name);
|
||||
if (name == 'disconnectedCallback')
|
||||
throw {name: 'expectedError'};
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
assert_throws({'name': 'expectedError'}, function () { customElements.define('element-with-throwing-callback', constructor); });
|
||||
assert_array_equals(calls, ['connectedCallback', 'disconnectedCallback'],
|
||||
'customElements.define must not get callbacks after one of the get throws');
|
||||
}, 'customElements.define must rethrow an exception thrown while getting callbacks on the constructor prototype');
|
||||
|
||||
test(function () {
|
||||
var constructor = function () {}
|
||||
var calls = [];
|
||||
constructor.prototype = new Proxy(constructor.prototype, {
|
||||
get: function (target, name) {
|
||||
calls.push(name);
|
||||
if (name == 'adoptedCallback')
|
||||
return 1;
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-throwing-callback', constructor); });
|
||||
assert_array_equals(calls, ['connectedCallback', 'disconnectedCallback', 'adoptedCallback'],
|
||||
'customElements.define must not get callbacks after one of the conversion throws');
|
||||
}, 'customElements.define must rethrow an exception thrown while converting a callback value to Function callback type');
|
||||
|
||||
test(function () {
|
||||
var constructor = function () {}
|
||||
constructor.prototype.attributeChangedCallback = function () { };
|
||||
var prototypeCalls = [];
|
||||
var callOrder = 0;
|
||||
constructor.prototype = new Proxy(constructor.prototype, {
|
||||
get: function (target, name) {
|
||||
if (name == 'prototype' || name == 'observedAttributes')
|
||||
throw 'Unexpected access to observedAttributes';
|
||||
prototypeCalls.push(callOrder++);
|
||||
prototypeCalls.push(name);
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
var constructorCalls = [];
|
||||
var proxy = new Proxy(constructor, {
|
||||
get: function (target, name) {
|
||||
constructorCalls.push(callOrder++);
|
||||
constructorCalls.push(name);
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
customElements.define('element-with-attribute-changed-callback', proxy);
|
||||
assert_array_equals(prototypeCalls, [1, 'connectedCallback', 2, 'disconnectedCallback', 3, 'adoptedCallback', 4, 'attributeChangedCallback']);
|
||||
assert_array_equals(constructorCalls, [0, 'prototype', 5, 'observedAttributes']);
|
||||
}, 'customElements.define must get "observedAttributes" property on the constructor prototype when "attributeChangedCallback" is present');
|
||||
|
||||
test(function () {
|
||||
var constructor = function () {}
|
||||
constructor.prototype.attributeChangedCallback = function () { };
|
||||
var calls = [];
|
||||
var proxy = new Proxy(constructor, {
|
||||
get: function (target, name) {
|
||||
calls.push(name);
|
||||
if (name == 'observedAttributes')
|
||||
throw {name: 'expectedError'};
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
assert_throws({'name': 'expectedError'}, function () { customElements.define('element-with-throwing-observed-attributes', proxy); });
|
||||
assert_array_equals(calls, ['prototype', 'observedAttributes'],
|
||||
'customElements.define must get "prototype" and "observedAttributes" on the constructor');
|
||||
}, 'customElements.define must rethrow an exception thrown while getting observedAttributes on the constructor prototype');
|
||||
|
||||
test(function () {
|
||||
var constructor = function () {}
|
||||
constructor.prototype.attributeChangedCallback = function () { };
|
||||
var calls = [];
|
||||
var proxy = new Proxy(constructor, {
|
||||
get: function (target, name) {
|
||||
calls.push(name);
|
||||
if (name == 'observedAttributes')
|
||||
return 1;
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-invalid-observed-attributes', proxy); });
|
||||
assert_array_equals(calls, ['prototype', 'observedAttributes'],
|
||||
'customElements.define must get "prototype" and "observedAttributes" on the constructor');
|
||||
}, 'customElements.define must rethrow an exception thrown while converting the value of observedAttributes to sequence<DOMString>');
|
||||
|
||||
test(function () {
|
||||
var constructor = function () {}
|
||||
constructor.prototype.attributeChangedCallback = function () { };
|
||||
constructor.observedAttributes = {[Symbol.iterator]: function *() {
|
||||
yield 'foo';
|
||||
throw {name: 'SomeError'};
|
||||
}};
|
||||
assert_throws({'name': 'SomeError'}, function () { customElements.define('element-with-generator-observed-attributes', constructor); });
|
||||
}, 'customElements.define must rethrow an exception thrown while iterating over observedAttributes to sequence<DOMString>');
|
||||
|
||||
test(function () {
|
||||
var constructor = function () {}
|
||||
constructor.prototype.attributeChangedCallback = function () { };
|
||||
constructor.observedAttributes = {[Symbol.iterator]: 1};
|
||||
assert_throws({'name': 'TypeError'}, function () { customElements.define('element-with-observed-attributes-with-uncallable-iterator', constructor); });
|
||||
}, 'customElements.define must rethrow an exception thrown while retrieving Symbol.iterator on observedAttributes');
|
||||
|
||||
test(function () {
|
||||
var constructor = function () {}
|
||||
constructor.observedAttributes = 1;
|
||||
customElements.define('element-without-callback-with-invalid-observed-attributes', constructor);
|
||||
}, 'customElements.define must not throw even if "observedAttributes" fails to convert if "attributeChangedCallback" is not defined');
|
||||
|
||||
test(function () {
|
||||
class MyCustomElement extends HTMLElement {};
|
||||
customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
var instance = new MyCustomElement;
|
||||
assert_true(instance instanceof MyCustomElement,
|
||||
'An instance of a custom HTML element be an instance of the associated interface');
|
||||
|
||||
assert_true(instance instanceof HTMLElement,
|
||||
'An instance of a custom HTML element must inherit from HTMLElement');
|
||||
|
||||
assert_equals(instance.localName, 'my-custom-element',
|
||||
'An instance of a custom element must use the associated tag name');
|
||||
|
||||
assert_equals(instance.namespaceURI, 'http://www.w3.org/1999/xhtml',
|
||||
'A custom element HTML must use HTML namespace');
|
||||
|
||||
}, 'customElements.define must define an instantiatable custom element');
|
||||
|
||||
test(function () {
|
||||
var disconnectedElement = document.createElement('some-custom');
|
||||
var connectedElementBeforeShadowHost = document.createElement('some-custom');
|
||||
var connectedElementAfterShadowHost = document.createElement('some-custom');
|
||||
var elementInShadowTree = document.createElement('some-custom');
|
||||
var childElementOfShadowHost = document.createElement('some-custom');
|
||||
var customShadowHost = document.createElement('some-custom');
|
||||
var elementInNestedShadowTree = document.createElement('some-custom');
|
||||
|
||||
var container = document.createElement('div');
|
||||
var shadowHost = document.createElement('div');
|
||||
var shadowRoot = shadowHost.attachShadow({mode: 'closed'});
|
||||
container.appendChild(connectedElementBeforeShadowHost);
|
||||
container.appendChild(shadowHost);
|
||||
container.appendChild(connectedElementAfterShadowHost);
|
||||
shadowHost.appendChild(childElementOfShadowHost);
|
||||
shadowRoot.appendChild(elementInShadowTree);
|
||||
shadowRoot.appendChild(customShadowHost);
|
||||
|
||||
var innerShadowRoot = customShadowHost.attachShadow({mode: 'closed'});
|
||||
innerShadowRoot.appendChild(elementInNestedShadowTree);
|
||||
|
||||
var calls = [];
|
||||
class SomeCustomElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
calls.push(this);
|
||||
}
|
||||
};
|
||||
|
||||
document.body.appendChild(container);
|
||||
customElements.define('some-custom', SomeCustomElement);
|
||||
assert_array_equals(calls, [connectedElementBeforeShadowHost, elementInShadowTree, customShadowHost, elementInNestedShadowTree, childElementOfShadowHost, connectedElementAfterShadowHost]);
|
||||
}, 'customElements.define must upgrade elements in the shadow-including tree order');
|
||||
|
||||
test(function () {
|
||||
assert_true('get' in CustomElementRegistry.prototype, '"get" exists on CustomElementRegistry.prototype');
|
||||
assert_true('get' in customElements, '"get" exists on window.customElements');
|
||||
}, 'CustomElementRegistry interface must have get as a method');
|
||||
|
||||
test(function () {
|
||||
assert_equals(customElements.get('a-b'), undefined);
|
||||
}, 'customElements.get must return undefined when the registry does not contain an entry with the given name');
|
||||
|
||||
test(function () {
|
||||
assert_equals(customElements.get('html'), undefined);
|
||||
assert_equals(customElements.get('span'), undefined);
|
||||
assert_equals(customElements.get('div'), undefined);
|
||||
assert_equals(customElements.get('g'), undefined);
|
||||
assert_equals(customElements.get('ab'), undefined);
|
||||
}, 'customElements.get must return undefined when the registry does not contain an entry with the given name even if the name was not a valid custom element name');
|
||||
|
||||
test(function () {
|
||||
assert_equals(customElements.get('existing-custom-element'), undefined);
|
||||
class ExistingCustomElement extends HTMLElement {};
|
||||
customElements.define('existing-custom-element', ExistingCustomElement);
|
||||
assert_equals(customElements.get('existing-custom-element'), ExistingCustomElement);
|
||||
}, 'customElements.get return the constructor of the entry with the given name when there is a matching entry.');
|
||||
|
||||
test(function () {
|
||||
assert_true(customElements.whenDefined('some-name') instanceof Promise);
|
||||
}, 'customElements.whenDefined must return a promise for a valid custom element name');
|
||||
|
||||
test(function () {
|
||||
assert_equals(customElements.whenDefined('some-name'), customElements.whenDefined('some-name'));
|
||||
}, 'customElements.whenDefined must return the same promise each time invoked for a valid custom element name which has not been defined');
|
||||
|
||||
promise_test(function () {
|
||||
var resolved = false;
|
||||
var rejected = false;
|
||||
customElements.whenDefined('a-b').then(function () { resolved = true; }, function () { rejected = true; });
|
||||
return Promise.resolve().then(function () {
|
||||
assert_false(resolved, 'The promise returned by "whenDefined" must not be resolved until a custom element is defined');
|
||||
assert_false(rejected, 'The promise returned by "whenDefined" must not be rejected until a custom element is defined');
|
||||
});
|
||||
}, 'customElements.whenDefined must return an unresolved promise when the registry does not contain the entry with the given name')
|
||||
|
||||
promise_test(function () {
|
||||
var promise = customElements.whenDefined('badname');
|
||||
promise.then(function (value) { promise.resolved = value; }, function (value) { promise.rejected = value; });
|
||||
|
||||
assert_false('resolved' in promise, 'The promise returned by "whenDefined" must not be resolved until the end of the next microtask');
|
||||
assert_false('rejected' in promise, 'The promise returned by "whenDefined" must not be rejected until the end of the next microtask');
|
||||
|
||||
return Promise.resolve().then(function () {
|
||||
assert_false('resolved' in promise, 'The promise returned by "whenDefined" must be resolved when a custom element is defined');
|
||||
assert_true('rejected' in promise, 'The promise returned by "whenDefined" must not be rejected when a custom element is defined');
|
||||
});
|
||||
}, 'customElements.whenDefined must return a rejected promise when the given name is not a valid custom element name');
|
||||
|
||||
promise_test(function () {
|
||||
customElements.define('preexisting-custom-element', class extends HTMLElement { });
|
||||
|
||||
var promise = customElements.whenDefined('preexisting-custom-element');
|
||||
promise.then(function (value) { promise.resolved = value; }, function (value) { promise.rejected = value; });
|
||||
|
||||
assert_false('resolved' in promise, 'The promise returned by "whenDefined" must not be resolved until the end of the next microtask');
|
||||
assert_false('rejected' in promise, 'The promise returned by "whenDefined" must not be rejected until the end of the next microtask');
|
||||
|
||||
return Promise.resolve().then(function () {
|
||||
assert_true('resolved' in promise, 'The promise returned by "whenDefined" must be resolved when a custom element is defined');
|
||||
assert_equals(promise.resolved, undefined,
|
||||
'The promise returned by "whenDefined" must be resolved with "undefined" when a custom element is defined');
|
||||
assert_false('rejected' in promise, 'The promise returned by "whenDefined" must not be rejected when a custom element is defined');
|
||||
});
|
||||
}, 'customElements.whenDefined must return a resolved promise when the registry contains the entry with the given name');
|
||||
|
||||
promise_test(function () {
|
||||
class AnotherExistingCustomElement extends HTMLElement {};
|
||||
customElements.define('another-existing-custom-element', AnotherExistingCustomElement);
|
||||
|
||||
var promise1 = customElements.whenDefined('another-existing-custom-element');
|
||||
var promise2 = customElements.whenDefined('another-existing-custom-element');
|
||||
promise1.then(function (value) { promise1.resolved = value; }, function (value) { promise1.rejected = value; });
|
||||
promise2.then(function (value) { promise2.resolved = value; }, function (value) { promise2.rejected = value; });
|
||||
|
||||
assert_not_equals(promise1, promise2);
|
||||
assert_false('resolved' in promise1, 'The promise returned by "whenDefined" must not be resolved until the end of the next microtask');
|
||||
assert_false('resolved' in promise2, 'The promise returned by "whenDefined" must not be resolved until the end of the next microtask');
|
||||
assert_false('rejected' in promise1, 'The promise returned by "whenDefined" must not be rejected until the end of the next microtask');
|
||||
assert_false('rejected' in promise2, 'The promise returned by "whenDefined" must not be rejected until the end of the next microtask');
|
||||
|
||||
return Promise.resolve().then(function () {
|
||||
assert_true('resolved' in promise1, 'The promise returned by "whenDefined" must be resolved when a custom element is defined');
|
||||
assert_equals(promise1.resolved, undefined, 'The promise returned by "whenDefined" must be resolved with "undefined" when a custom element is defined');
|
||||
assert_false('rejected' in promise1, 'The promise returned by "whenDefined" must not be rejected when a custom element is defined');
|
||||
|
||||
assert_true('resolved' in promise2, 'The promise returned by "whenDefined" must be resolved when a custom element is defined');
|
||||
assert_equals(promise2.resolved, undefined, 'The promise returned by "whenDefined" must be resolved with "undefined" when a custom element is defined');
|
||||
assert_false('rejected' in promise2, 'The promise returned by "whenDefined" must not be rejected when a custom element is defined');
|
||||
});
|
||||
}, 'customElements.whenDefined must return a new resolved promise each time invoked when the registry contains the entry with the given name');
|
||||
|
||||
promise_test(function () {
|
||||
var promise = customElements.whenDefined('element-defined-after-whendefined');
|
||||
promise.then(function (value) { promise.resolved = value; }, function (value) { promise.rejected = value; });
|
||||
|
||||
assert_false('resolved' in promise, 'The promise returned by "whenDefined" must not be resolved until the end of the next microtask');
|
||||
assert_false('rejected' in promise, 'The promise returned by "whenDefined" must not be rejected until the end of the next microtask');
|
||||
|
||||
var promiseAfterDefine;
|
||||
return Promise.resolve().then(function () {
|
||||
assert_false('resolved' in promise, 'The promise returned by "whenDefined" must not be resolved until the element is defined');
|
||||
assert_false('rejected' in promise, 'The promise returned by "whenDefined" must not be rejected until the element is defined');
|
||||
assert_equals(customElements.whenDefined('element-defined-after-whendefined'), promise,
|
||||
'"whenDefined" must return the same unresolved promise before the custom element is defined');
|
||||
customElements.define('element-defined-after-whendefined', class extends HTMLElement { });
|
||||
assert_false('resolved' in promise, 'The promise returned by "whenDefined" must not be resolved until the end of the next microtask');
|
||||
assert_false('rejected' in promise, 'The promise returned by "whenDefined" must not be rejected until the end of the next microtask');
|
||||
|
||||
promiseAfterDefine = customElements.whenDefined('element-defined-after-whendefined');
|
||||
promiseAfterDefine.then(function (value) { promiseAfterDefine.resolved = value; }, function (value) { promiseAfterDefine.rejected = value; });
|
||||
assert_not_equals(promiseAfterDefine, promise, '"whenDefined" must return a resolved promise once the custom element is defined');
|
||||
assert_false('resolved' in promiseAfterDefine, 'The promise returned by "whenDefined" must not be resolved until the end of the next microtask');
|
||||
assert_false('rejected' in promiseAfterDefine, 'The promise returned by "whenDefined" must not be rejected until the end of the next microtask');
|
||||
}).then(function () {
|
||||
assert_true('resolved' in promise, 'The promise returned by "whenDefined" must be resolved when a custom element is defined');
|
||||
assert_equals(promise.resolved, undefined,
|
||||
'The promise returned by "whenDefined" must be resolved with "undefined" when a custom element is defined');
|
||||
assert_false('rejected' in promise, 'The promise returned by "whenDefined" must not be rejected when a custom element is defined');
|
||||
|
||||
assert_true('resolved' in promiseAfterDefine, 'The promise returned by "whenDefined" must be resolved when a custom element is defined');
|
||||
assert_equals(promiseAfterDefine.resolved, undefined,
|
||||
'The promise returned by "whenDefined" must be resolved with "undefined" when a custom element is defined');
|
||||
assert_false('rejected' in promiseAfterDefine, 'The promise returned by "whenDefined" must not be rejected when a custom element is defined');
|
||||
});
|
||||
}, 'A promise returned by customElements.whenDefined must be resolved by "define"');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,332 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: document.createElement should create an element with synchronous custom elements flag set</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="document.createElement should create an element with synchronous custom elements flag set">
|
||||
<link rel="help" content="https://dom.spec.whatwg.org/#dom-document-createelement">
|
||||
<link rel="help" content="https://dom.spec.whatwg.org/#concept-create-element">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/custom-elements-helper.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
setup({allow_uncaught_exception:true});
|
||||
|
||||
test(function () {
|
||||
class MyCustomElement extends HTMLElement {};
|
||||
|
||||
assert_true(document.createElement('my-custom-element') instanceof HTMLElement);
|
||||
assert_false(document.createElement('my-custom-element') instanceof MyCustomElement);
|
||||
|
||||
customElements.define('my-custom-element', MyCustomElement);
|
||||
var instance = document.createElement('my-custom-element');
|
||||
assert_true(instance instanceof MyCustomElement);
|
||||
assert_equals(instance.localName, 'my-custom-element');
|
||||
assert_equals(instance.namespaceURI, 'http://www.w3.org/1999/xhtml', 'A custom element HTML must use HTML namespace');
|
||||
|
||||
}, 'document.createElement must create an instance of custom elements');
|
||||
|
||||
function assert_reports(expected, testFunction, message) {
|
||||
var uncaughtError = null;
|
||||
window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
|
||||
testFunction();
|
||||
if (typeof(expected) == 'string')
|
||||
assert_equals(uncaughtError, expected, message);
|
||||
else if (expected && 'name' in expected)
|
||||
assert_equals(uncaughtError.name, expected.name, message);
|
||||
else
|
||||
assert_equals(uncaughtError, expected, message);
|
||||
window.onerror = null;
|
||||
}
|
||||
|
||||
function assert_not_reports(testFunction, message) {
|
||||
assert_reports(null, testFunction, message);
|
||||
}
|
||||
|
||||
test(function () {
|
||||
class ObjectCustomElement extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
return {foo: 'bar'};
|
||||
}
|
||||
};
|
||||
customElements.define('object-custom-element', ObjectCustomElement);
|
||||
|
||||
var instance = new ObjectCustomElement;
|
||||
assert_true(instance instanceof Object);
|
||||
assert_equals(instance.foo, 'bar');
|
||||
|
||||
var instance;
|
||||
assert_reports({name: 'TypeError'}, function () { instance = document.createElement('object-custom-element'); });
|
||||
assert_equals(instance.localName, 'object-custom-element');
|
||||
assert_true(instance instanceof HTMLUnknownElement);
|
||||
}, 'document.createElement must report a TypeError when the result of Construct is not a DOM node');
|
||||
|
||||
test(function () {
|
||||
class TextCustomElement extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
return document.createTextNode('hello');
|
||||
}
|
||||
};
|
||||
customElements.define('text-custom-element', TextCustomElement);
|
||||
assert_true(new TextCustomElement instanceof Text);
|
||||
var instance;
|
||||
assert_reports({name: 'TypeError'}, function () { instance = document.createElement('text-custom-element'); });
|
||||
assert_equals(instance.localName, 'text-custom-element');
|
||||
assert_true(instance instanceof HTMLUnknownElement);
|
||||
}, 'document.createElement must report a TypeError when the result of Construct is a TextNode');
|
||||
|
||||
test(function () {
|
||||
class ElementWithAttribute extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.setAttribute('id', 'foo');
|
||||
}
|
||||
};
|
||||
customElements.define('element-with-attribute', ElementWithAttribute);
|
||||
assert_true(new ElementWithAttribute instanceof ElementWithAttribute);
|
||||
var instance;
|
||||
assert_reports({name: 'NotSupportedError'}, function () { instance = document.createElement('element-with-attribute'); });
|
||||
assert_equals(instance.localName, 'element-with-attribute');
|
||||
assert_true(instance instanceof HTMLUnknownElement);
|
||||
}, 'document.createElement must report a NotSupportedError when attribute is added by setAttribute during construction');
|
||||
|
||||
test(function () {
|
||||
class ElementWithAttrNode extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.attributes.setNamedItem(document.createAttribute('title'));
|
||||
}
|
||||
};
|
||||
customElements.define('element-with-attr-node', ElementWithAttrNode);
|
||||
assert_true(new ElementWithAttrNode instanceof ElementWithAttrNode);
|
||||
var instance;
|
||||
assert_reports({name: 'NotSupportedError'}, function () { instance = document.createElement('element-with-attr-node'); });
|
||||
assert_equals(instance.localName, 'element-with-attr-node');
|
||||
assert_true(instance instanceof HTMLUnknownElement);
|
||||
}, 'document.createElement must report a NotSupportedError when attribute is added by attributes.setNamedItem during construction');
|
||||
|
||||
test(function () {
|
||||
class ElementWithNoAttributes extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.attributes.setNamedItem(document.createAttribute('title'));
|
||||
this.removeAttribute('title');
|
||||
}
|
||||
};
|
||||
customElements.define('element-with-no-attiributes', ElementWithNoAttributes);
|
||||
assert_true(new ElementWithNoAttributes instanceof ElementWithNoAttributes);
|
||||
var instance;
|
||||
assert_not_reports(function () { instance = document.createElement('element-with-no-attiributes'); });
|
||||
assert_true(instance instanceof ElementWithNoAttributes);
|
||||
}, 'document.createElement must not report a NotSupportedError when attribute is added and removed during construction');
|
||||
|
||||
test(function () {
|
||||
class ElementWithChildText extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.appendChild(document.createTextNode('hello'));
|
||||
}
|
||||
};
|
||||
customElements.define('element-with-child-text', ElementWithChildText);
|
||||
assert_true(new ElementWithChildText instanceof ElementWithChildText);
|
||||
var instance;
|
||||
assert_reports({name: 'NotSupportedError'}, function () { instance = document.createElement('element-with-child-text'); });
|
||||
assert_equals(instance.localName, 'element-with-child-text');
|
||||
assert_true(instance instanceof HTMLUnknownElement);
|
||||
}, 'document.createElement must report a NotSupportedError when a Text child is added during construction');
|
||||
|
||||
test(function () {
|
||||
class ElementWithChildComment extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.appendChild(document.createComment('hello'));
|
||||
}
|
||||
};
|
||||
customElements.define('element-with-child-comment', ElementWithChildComment);
|
||||
assert_true(new ElementWithChildComment instanceof ElementWithChildComment);
|
||||
var instance;
|
||||
assert_reports({name: 'NotSupportedError'}, function () { instance = document.createElement('element-with-child-comment'); });
|
||||
assert_equals(instance.localName, 'element-with-child-comment');
|
||||
assert_true(instance instanceof HTMLUnknownElement);
|
||||
}, 'document.createElement must report a NotSupportedError when a Comment child is added during construction');
|
||||
|
||||
test(function () {
|
||||
class ElementWithChildElement extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.appendChild(document.createElement('div'));
|
||||
}
|
||||
};
|
||||
customElements.define('element-with-child-element', ElementWithChildElement);
|
||||
assert_true(new ElementWithChildElement instanceof ElementWithChildElement);
|
||||
var instance;
|
||||
assert_reports({name: 'NotSupportedError'}, function () { instance = document.createElement('element-with-child-element'); });
|
||||
assert_equals(instance.localName, 'element-with-child-element');
|
||||
assert_true(instance instanceof HTMLUnknownElement);
|
||||
}, 'document.createElement must report a NotSupportedError when an element child is added during construction');
|
||||
|
||||
test(function () {
|
||||
class ElementWithNoChildElements extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.appendChild(document.createElement('div'));
|
||||
this.removeChild(this.firstChild);
|
||||
}
|
||||
};
|
||||
customElements.define('element-with-no-child-elements', ElementWithNoChildElements);
|
||||
var instance;
|
||||
assert_not_reports(function () { instance = document.createElement('element-with-no-child-elements'); });
|
||||
assert_true(instance instanceof ElementWithNoChildElements);
|
||||
}, 'document.createElement must not report a NotSupportedError when an element child is added and removed during construction');
|
||||
|
||||
test(function () {
|
||||
class ElementWithParent extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
document.createElement('div').appendChild(this);
|
||||
}
|
||||
};
|
||||
customElements.define('element-with-parent', ElementWithParent);
|
||||
assert_true(new ElementWithParent instanceof ElementWithParent);
|
||||
var instance;
|
||||
assert_reports({name: 'NotSupportedError'}, function () { instance = document.createElement('element-with-parent'); });
|
||||
assert_equals(instance.localName, 'element-with-parent');
|
||||
assert_true(instance instanceof HTMLUnknownElement);
|
||||
}, 'document.createElement must report a NotSupportedError when the element gets inserted into another element during construction');
|
||||
|
||||
test(function () {
|
||||
class ElementWithNoParent extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
document.createElement('div').appendChild(this);
|
||||
this.parentNode.removeChild(this);
|
||||
}
|
||||
};
|
||||
customElements.define('element-with-no-parent', ElementWithNoParent);
|
||||
var instance;
|
||||
assert_not_reports(function () { instance = document.createElement('element-with-no-parent'); });
|
||||
assert_true(instance instanceof ElementWithNoParent);
|
||||
}, 'document.createElement must not report a NotSupportedError when the element is inserted and removed from another element during construction');
|
||||
|
||||
document_types().forEach(function (entry, testNumber) {
|
||||
if (entry.isOwner)
|
||||
return;
|
||||
|
||||
var getDocument = entry.create;
|
||||
var docuemntName = entry.name;
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
class ElementWithAdoptCall extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
doc.adoptNode(this);
|
||||
}
|
||||
};
|
||||
var name = 'element-with-adopt-call-' + testNumber;
|
||||
customElements.define(name, ElementWithAdoptCall);
|
||||
assert_true(new ElementWithAdoptCall instanceof ElementWithAdoptCall);
|
||||
var instance;
|
||||
assert_reports({name: 'NotSupportedError'}, function () { instance = document.createElement(name); });
|
||||
assert_equals(instance.localName, name);
|
||||
assert_true(instance instanceof HTMLUnknownElement);
|
||||
});
|
||||
}, 'document.createElement must report a NotSupportedError when the element is adopted into a ' + docuemntName + ' during construction');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
class ElementInsertedIntoAnotherDocument extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
doc.documentElement.appendChild(this);
|
||||
}
|
||||
};
|
||||
var name = 'element-inserted-into-another-document-' + testNumber;
|
||||
customElements.define(name, ElementInsertedIntoAnotherDocument);
|
||||
assert_true(new ElementInsertedIntoAnotherDocument instanceof ElementInsertedIntoAnotherDocument);
|
||||
var instance;
|
||||
assert_reports({name: 'NotSupportedError'}, function () { instance = document.createElement(name); });
|
||||
assert_equals(instance.localName, name);
|
||||
assert_true(instance instanceof HTMLUnknownElement);
|
||||
});
|
||||
}, 'document.createElement must report a NotSupportedError when the element is inserted into a ' + docuemntName + ' during construction');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
class ElementThatGetAdoptedBack extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
doc.adoptNode(this);
|
||||
document.adoptNode(this);
|
||||
}
|
||||
};
|
||||
var name = 'element-that-get-adopted-back' + testNumber;
|
||||
customElements.define(name, ElementThatGetAdoptedBack);
|
||||
var instance;
|
||||
assert_not_reports(function () { instance = document.createElement(name); });
|
||||
assert_true(instance instanceof ElementThatGetAdoptedBack);
|
||||
});
|
||||
}, 'document.createElement must not report a NotSupportedError when the element is adopted back from a ' + docuemntName + ' during construction');
|
||||
});
|
||||
|
||||
test(function () {
|
||||
class DivCustomElement extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
return document.createElement('div');
|
||||
}
|
||||
};
|
||||
customElements.define('div-custom-element', DivCustomElement);
|
||||
assert_true(new DivCustomElement instanceof HTMLDivElement);
|
||||
var instance;
|
||||
assert_reports({name: 'NotSupportedError'}, function () { instance = document.createElement('div-custom-element'); });
|
||||
assert_equals(instance.localName, 'div-custom-element');
|
||||
assert_true(instance instanceof HTMLUnknownElement);
|
||||
}, 'document.createElement must report a NotSupportedError when the local name of the element does not match that of the custom element');
|
||||
|
||||
test(function () {
|
||||
var exceptionToThrow = {name: 'exception thrown by a custom constructor'};
|
||||
class ThrowCustomElement extends HTMLElement {
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
if (exceptionToThrow)
|
||||
throw exceptionToThrow;
|
||||
}
|
||||
};
|
||||
customElements.define('throw-custom-element', ThrowCustomElement);
|
||||
|
||||
assert_throws(exceptionToThrow, function () { new ThrowCustomElement; });
|
||||
var instance;
|
||||
assert_reports(exceptionToThrow, function () { instance = document.createElement('throw-custom-element'); });
|
||||
assert_equals(instance.localName, 'throw-custom-element');
|
||||
assert_true(instance instanceof HTMLUnknownElement);
|
||||
|
||||
exceptionToThrow = false;
|
||||
var instance = document.createElement('throw-custom-element');
|
||||
assert_true(instance instanceof ThrowCustomElement);
|
||||
assert_equals(instance.localName, 'throw-custom-element');
|
||||
|
||||
}, 'document.createElement must report an exception thrown by a custom element constructor');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,135 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: adoptedCallback</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="adoptedCallback must be enqueued whenever custom element is adopted into a new document">
|
||||
<link rel="help" href="https://w3c.github.io/webcomponents/spec/custom/#dfn-connected-callback">
|
||||
<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>
|
||||
|
||||
var calls = [];
|
||||
class MyCustomElement extends HTMLElement {
|
||||
connectedCallback() { calls.push('connected'); }
|
||||
adoptedCallback(oldDocument, newDocument) { calls.push('adopted'); calls.push(oldDocument); calls.push(newDocument); }
|
||||
disconnectedCallback() { calls.push('disconnected'); }
|
||||
}
|
||||
customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
test(function () {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
calls = [];
|
||||
document.body.appendChild(instance);
|
||||
assert_array_equals(calls, ['connected']);
|
||||
}, 'Inserting a custom element into the owner document must not enqueue and invoke adoptedCallback');
|
||||
|
||||
document_types().forEach(function (entry) {
|
||||
if (entry.isOwner)
|
||||
return;
|
||||
|
||||
var documentName = entry.name;
|
||||
var getDocument = entry.create;
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
calls = [];
|
||||
doc.documentElement.appendChild(instance);
|
||||
assert_array_equals(calls, ['adopted', document, doc, 'connected']);
|
||||
});
|
||||
}, 'Inserting a custom element into ' + documentName + ' must enqueue and invoke adoptedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
document.body.appendChild(instance);
|
||||
calls = [];
|
||||
doc.documentElement.appendChild(instance);
|
||||
assert_array_equals(calls, ['disconnected', 'adopted', document, doc, 'connected']);
|
||||
});
|
||||
}, 'Moving a custom element from the owner document into ' + documentName + ' must enqueue and invoke adoptedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var parent = document.createElement('div');
|
||||
parent.appendChild(instance);
|
||||
calls = [];
|
||||
doc.documentElement.appendChild(parent);
|
||||
assert_array_equals(calls, ['adopted', document, doc, 'connected']);
|
||||
});
|
||||
}, 'Inserting an ancestor of custom element into ' + documentName + ' must enqueue and invoke adoptedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var parent = document.createElement('div');
|
||||
parent.appendChild(instance);
|
||||
document.body.appendChild(parent);
|
||||
calls = [];
|
||||
doc.documentElement.appendChild(parent);
|
||||
assert_array_equals(calls, ['disconnected', 'adopted', document, doc, 'connected']);
|
||||
});
|
||||
}, 'Moving an ancestor of custom element from the owner document into ' + documentName + ' must enqueue and invoke adoptedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
var shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
doc.documentElement.appendChild(host);
|
||||
|
||||
calls = [];
|
||||
shadowRoot.appendChild(instance);
|
||||
assert_array_equals(calls, ['adopted', document, doc, 'connected']);
|
||||
});
|
||||
}, 'Inserting a custom element into a shadow tree in ' + documentName + ' must enqueue and invoke adoptedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var host = document.createElement('div');
|
||||
var shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
shadowRoot.appendChild(instance);
|
||||
|
||||
calls = [];
|
||||
doc.documentElement.appendChild(host);
|
||||
assert_array_equals(calls, ['adopted', document, doc, 'connected']);
|
||||
});
|
||||
}, 'Inserting the shadow host of a custom element into ' + documentName + ' must enqueue and invoke adoptedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var host = document.createElement('div');
|
||||
var shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
shadowRoot.appendChild(instance);
|
||||
document.body.appendChild(host);
|
||||
|
||||
calls = [];
|
||||
doc.documentElement.appendChild(host);
|
||||
assert_array_equals(calls, ['disconnected', 'adopted', document, doc, 'connected']);
|
||||
});
|
||||
}, 'Moving the shadow host of a custom element from the owner document into ' + documentName + ' must enqueue and invoke adoptedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
var shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
|
||||
calls = [];
|
||||
shadowRoot.appendChild(instance);
|
||||
assert_array_equals(calls, ['adopted', document, doc]);
|
||||
});
|
||||
}, 'Inserting a custom element into a detached shadow tree that belongs to ' + documentName + ' must enqueue and invoke adoptedCallback');
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,223 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: attributeChangedCallback</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="attributeChangedCallback must be enqueued whenever custom element's attribute is added, changed or removed">
|
||||
<link rel="help" href="https://w3c.github.io/webcomponents/spec/custom/#dfn-attribute-changed-callback">
|
||||
<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>
|
||||
|
||||
var customElement = define_new_custom_element(['title', 'id', 'r']);
|
||||
|
||||
test(function () {
|
||||
const instance = document.createElement(customElement.name);
|
||||
assert_array_equals(customElement.takeLog().types(), ['constructed']);
|
||||
|
||||
instance.setAttribute('title', 'foo');
|
||||
assert_equals(instance.getAttribute('title'), 'foo');
|
||||
var logEntries = customElement.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: null, newValue: 'foo', namespace: null});
|
||||
|
||||
instance.removeAttribute('title');
|
||||
assert_equals(instance.getAttribute('title'), null);
|
||||
var logEntries = customElement.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'foo', newValue: null, namespace: null});
|
||||
}, 'setAttribute and removeAttribute must enqueue and invoke attributeChangedCallback');
|
||||
|
||||
test(function () {
|
||||
var instance = document.createElement(customElement.name);
|
||||
assert_array_equals(customElement.takeLog().types(), ['constructed']);
|
||||
|
||||
instance.setAttributeNS('http://www.w3.org/2000/svg', 'title', 'hello');
|
||||
assert_equals(instance.getAttribute('title'), 'hello');
|
||||
var logEntries = customElement.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: null, newValue: 'hello', namespace: 'http://www.w3.org/2000/svg'});
|
||||
|
||||
instance.removeAttributeNS('http://www.w3.org/2000/svg', 'title');
|
||||
assert_equals(instance.getAttribute('title'), null);
|
||||
var logEntries = customElement.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'hello', newValue: null, namespace: 'http://www.w3.org/2000/svg'});
|
||||
}, 'setAttributeNS and removeAttributeNS must enqueue and invoke attributeChangedCallback');
|
||||
|
||||
test(function () {
|
||||
var instance = document.createElement(customElement.name);
|
||||
assert_array_equals(customElement.takeLog().types(), ['constructed']);
|
||||
|
||||
var attr = document.createAttribute('id');
|
||||
attr.value = 'bar';
|
||||
instance.setAttributeNode(attr);
|
||||
|
||||
assert_equals(instance.getAttribute('id'), 'bar');
|
||||
var logEntries = customElement.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'id', oldValue: null, newValue: 'bar', namespace: null});
|
||||
|
||||
instance.removeAttributeNode(attr);
|
||||
assert_equals(instance.getAttribute('id'), null);
|
||||
var logEntries = customElement.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'id', oldValue: 'bar', newValue: null, namespace: null});
|
||||
}, 'setAttributeNode and removeAttributeNode must enqueue and invoke attributeChangedCallback for an HTML attribute');
|
||||
|
||||
test(function () {
|
||||
const instance = document.createElement(customElement.name);
|
||||
assert_array_equals(customElement.takeLog().types(), ['constructed']);
|
||||
|
||||
const attr = document.createAttributeNS('http://www.w3.org/2000/svg', 'r');
|
||||
attr.value = '100';
|
||||
instance.setAttributeNode(attr);
|
||||
|
||||
assert_equals(instance.getAttribute('r'), '100');
|
||||
var logEntries = customElement.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'r', oldValue: null, newValue: '100', namespace: 'http://www.w3.org/2000/svg'});
|
||||
|
||||
instance.removeAttributeNode(attr);
|
||||
assert_equals(instance.getAttribute('r'), null);
|
||||
var logEntries = customElement.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'r', oldValue: '100', newValue: null, namespace: 'http://www.w3.org/2000/svg'});
|
||||
}, 'setAttributeNode and removeAttributeNS must enqueue and invoke attributeChangedCallback for an SVG attribute');
|
||||
|
||||
test(function () {
|
||||
const callsToOld = [];
|
||||
const callsToNew = [];
|
||||
class CustomElement extends HTMLElement { }
|
||||
CustomElement.prototype.attributeChangedCallback = function (...args) {
|
||||
callsToOld.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
CustomElement.observedAttributes = ['title'];
|
||||
customElements.define('element-with-mutated-attribute-changed-callback', CustomElement);
|
||||
CustomElement.prototype.attributeChangedCallback = function (...args) {
|
||||
callsToNew.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
|
||||
const instance = document.createElement('element-with-mutated-attribute-changed-callback');
|
||||
instance.setAttribute('title', 'hi');
|
||||
assert_equals(instance.getAttribute('title'), 'hi');
|
||||
assert_array_equals(callsToNew, []);
|
||||
assert_equals(callsToOld.length, 1);
|
||||
assert_attribute_log_entry(callsToOld[0], {name: 'title', oldValue: null, newValue: 'hi', namespace: null});
|
||||
}, 'Mutating attributeChangedCallback after calling customElements.define must not affect the callback being invoked');
|
||||
|
||||
test(function () {
|
||||
const calls = [];
|
||||
class CustomElement extends HTMLElement {
|
||||
attributeChangedCallback(...args) {
|
||||
calls.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
}
|
||||
CustomElement.observedAttributes = ['title'];
|
||||
customElements.define('element-not-observing-id-attribute', CustomElement);
|
||||
|
||||
const instance = document.createElement('element-not-observing-id-attribute');
|
||||
assert_equals(calls.length, 0);
|
||||
instance.setAttribute('title', 'hi');
|
||||
assert_equals(calls.length, 1);
|
||||
assert_attribute_log_entry(calls[0], {name: 'title', oldValue: null, newValue: 'hi', namespace: null});
|
||||
instance.setAttribute('id', 'some');
|
||||
assert_equals(calls.length, 1);
|
||||
assert_attribute_log_entry(calls[0], {name: 'title', oldValue: null, newValue: 'hi', namespace: null});
|
||||
}, 'attributedChangedCallback must not be invoked when the observed attributes does not contain the attribute');
|
||||
|
||||
test(function () {
|
||||
const calls = [];
|
||||
class CustomElement extends HTMLElement { }
|
||||
CustomElement.prototype.attributeChangedCallback = function (...args) {
|
||||
calls.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
CustomElement.observedAttributes = ['title', 'lang'];
|
||||
customElements.define('element-with-mutated-observed-attributes', CustomElement);
|
||||
CustomElement.observedAttributes = ['title', 'id'];
|
||||
|
||||
const instance = document.createElement('element-with-mutated-observed-attributes');
|
||||
instance.setAttribute('title', 'hi');
|
||||
assert_equals(calls.length, 1);
|
||||
assert_attribute_log_entry(calls[0], {name: 'title', oldValue: null, newValue: 'hi', namespace: null});
|
||||
|
||||
instance.setAttribute('id', 'some');
|
||||
assert_equals(calls.length, 1);
|
||||
|
||||
instance.setAttribute('lang', 'en');
|
||||
assert_equals(calls.length, 2);
|
||||
assert_attribute_log_entry(calls[1], {name: 'lang', oldValue: null, newValue: 'en', namespace: null});
|
||||
}, 'Mutating observedAttributes after calling customElements.define must not affect the set of attributes for which attributedChangedCallback is invoked');
|
||||
|
||||
test(function () {
|
||||
var calls = [];
|
||||
class CustomElement extends HTMLElement { }
|
||||
CustomElement.prototype.attributeChangedCallback = function (...args) {
|
||||
calls.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
CustomElement.observedAttributes = { [Symbol.iterator]: function *() { yield 'lang'; yield 'style'; } };
|
||||
customElements.define('element-with-generator-observed-attributes', CustomElement);
|
||||
|
||||
var instance = document.createElement('element-with-generator-observed-attributes');
|
||||
instance.setAttribute('lang', 'en');
|
||||
assert_equals(calls.length, 1);
|
||||
assert_attribute_log_entry(calls[0], {name: 'lang', oldValue: null, newValue: 'en', namespace: null});
|
||||
|
||||
instance.setAttribute('lang', 'ja');
|
||||
assert_equals(calls.length, 2);
|
||||
assert_attribute_log_entry(calls[1], {name: 'lang', oldValue: 'en', newValue: 'ja', namespace: null});
|
||||
|
||||
instance.setAttribute('title', 'hello');
|
||||
assert_equals(calls.length, 2);
|
||||
|
||||
instance.setAttribute('style', 'font-size: 2rem');
|
||||
assert_equals(calls.length, 3);
|
||||
assert_attribute_log_entry(calls[2], {name: 'style', oldValue: null, newValue: 'font-size: 2rem', namespace: null});
|
||||
}, 'attributedChangedCallback must be enqueued for attributes specified in a non-Array iterable observedAttributes');
|
||||
|
||||
test(function () {
|
||||
var calls = [];
|
||||
class CustomElement extends HTMLElement { }
|
||||
CustomElement.prototype.attributeChangedCallback = function (...args) {
|
||||
calls.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
CustomElement.observedAttributes = ['style'];
|
||||
customElements.define('element-with-style-attribute-observation', CustomElement);
|
||||
|
||||
var instance = document.createElement('element-with-style-attribute-observation');
|
||||
assert_equals(calls.length, 0);
|
||||
|
||||
instance.style.fontSize = '10px';
|
||||
assert_equals(calls.length, 1);
|
||||
assert_attribute_log_entry(calls[0], {name: 'style', oldValue: null, newValue: 'font-size: 10px;', namespace: null});
|
||||
|
||||
instance.style.fontSize = '20px';
|
||||
assert_equals(calls.length, 2);
|
||||
assert_attribute_log_entry(calls[1], {name: 'style', oldValue: 'font-size: 10px;', newValue: 'font-size: 20px;', namespace: null});
|
||||
|
||||
}, 'attributedChangedCallback must be enqueued for style attribute change by mutating inline style declaration');
|
||||
|
||||
test(function () {
|
||||
var calls = [];
|
||||
class CustomElement extends HTMLElement { }
|
||||
CustomElement.prototype.attributeChangedCallback = function (...args) {
|
||||
calls.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
CustomElement.observedAttributes = ['title'];
|
||||
customElements.define('element-with-no-style-attribute-observation', CustomElement);
|
||||
|
||||
var instance = document.createElement('element-with-no-style-attribute-observation');
|
||||
assert_equals(calls.length, 0);
|
||||
instance.style.fontSize = '10px';
|
||||
assert_equals(calls.length, 0);
|
||||
instance.title = 'hello';
|
||||
assert_attribute_log_entry(calls[0], {name: 'title', oldValue: null, newValue: 'hello', namespace: null});
|
||||
}, 'attributedChangedCallback must not be enqueued when mutating inline style declaration if the style attribute is not observed');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,88 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: connectedCallback</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="connectedCallback must be enqueued whenever custom element is inserted into a document">
|
||||
<link rel="help" href="https://w3c.github.io/webcomponents/spec/custom/#dfn-connected-callback">
|
||||
<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>
|
||||
|
||||
var calls = [];
|
||||
class MyCustomElement extends HTMLElement {
|
||||
connectedCallback() { calls.push('connected', this); }
|
||||
disconnectedCallback() { calls.push('disconnected', this); }
|
||||
}
|
||||
customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
document_types().forEach(function (entry) {
|
||||
var documentName = entry.name;
|
||||
var getDocument = entry.create;
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
calls = [];
|
||||
doc.documentElement.appendChild(instance);
|
||||
assert_array_equals(calls, ['connected', instance]);
|
||||
});
|
||||
}, 'Inserting a custom element into ' + documentName + ' must enqueue and invoke connectedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var parent = document.createElement('div');
|
||||
parent.appendChild(instance);
|
||||
calls = [];
|
||||
doc.documentElement.appendChild(parent);
|
||||
assert_array_equals(calls, ['connected', instance]);
|
||||
});
|
||||
}, 'Inserting an ancestor of custom element into ' + documentName + ' must enqueue and invoke connectedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
var shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
doc.documentElement.appendChild(host);
|
||||
|
||||
calls = [];
|
||||
shadowRoot.appendChild(instance);
|
||||
assert_array_equals(calls, ['connected', instance]);
|
||||
});
|
||||
}, 'Inserting a custom element into a shadow tree in ' + documentName + ' must enqueue and invoke connectedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
var shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
shadowRoot.appendChild(instance);
|
||||
|
||||
calls = [];
|
||||
doc.documentElement.appendChild(host);
|
||||
assert_array_equals(calls, ['connected', instance]);
|
||||
});
|
||||
}, 'Inserting the shadow host of a custom element into ' + documentName + ' must enqueue and invoke connectedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
var shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
|
||||
calls = [];
|
||||
shadowRoot.appendChild(instance);
|
||||
assert_array_equals(calls, []);
|
||||
});
|
||||
}, 'Inserting a custom element into a detached shadow tree that belongs to ' + documentName + ' must not enqueue and invoke connectedCallback');
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,157 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: Each element must have its own custom element reaction queue</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="Each element must have its own custom element reaction queue">
|
||||
<meta name="help" content="https://html.spec.whatwg.org/multipage/scripting.html#custom-element-reaction-queue">
|
||||
<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>
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.write('<test-element id="first-element">');
|
||||
contentDocument.write('<test-element id="second-element">');
|
||||
|
||||
const element1 = contentDocument.getElementById('first-element');
|
||||
const element2 = contentDocument.getElementById('second-element');
|
||||
assert_equals(Object.getPrototypeOf(element1), contentWindow.HTMLElement.prototype);
|
||||
assert_equals(Object.getPrototypeOf(element2), contentWindow.HTMLElement.prototype);
|
||||
|
||||
let log = [];
|
||||
class TestElement extends contentWindow.HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
log.push(create_constructor_log(this));
|
||||
}
|
||||
connectedCallback(...args) {
|
||||
log.push(create_connected_callback_log(this, ...args));
|
||||
}
|
||||
attributeChangedCallback(...args) {
|
||||
log.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
static get observedAttributes() { return ['id']; }
|
||||
}
|
||||
contentWindow.customElements.define('test-element', TestElement);
|
||||
assert_equals(Object.getPrototypeOf(element1), TestElement.prototype);
|
||||
assert_equals(Object.getPrototypeOf(element2), TestElement.prototype);
|
||||
|
||||
assert_equals(log.length, 6);
|
||||
assert_constructor_log_entry(log[0], element1);
|
||||
assert_attribute_log_entry(log[1], {name: 'id', oldValue: null, newValue: 'first-element', namespace: null});
|
||||
assert_connected_log_entry(log[2], element1);
|
||||
assert_constructor_log_entry(log[3], element2);
|
||||
assert_attribute_log_entry(log[4], {name: 'id', oldValue: null, newValue: 'second-element', namespace: null});
|
||||
assert_connected_log_entry(log[5], element2);
|
||||
}, 'Upgrading a custom element must invoke attributeChangedCallback and connectedCallback before start upgrading another element');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.write('<test-element id="first-element">');
|
||||
contentDocument.write('<test-element id="second-element">');
|
||||
|
||||
const element1 = contentDocument.getElementById('first-element');
|
||||
const element2 = contentDocument.getElementById('second-element');
|
||||
assert_equals(Object.getPrototypeOf(element1), contentWindow.HTMLElement.prototype);
|
||||
assert_equals(Object.getPrototypeOf(element2), contentWindow.HTMLElement.prototype);
|
||||
|
||||
let log = [];
|
||||
class TestElement extends contentWindow.HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
log.push(create_constructor_log(this));
|
||||
if (this == element1) {
|
||||
element2.setAttribute('title', 'hi');
|
||||
element2.removeAttribute('title');
|
||||
element2.setAttribute('class', 'foo');
|
||||
}
|
||||
}
|
||||
connectedCallback(...args) {
|
||||
log.push(create_connected_callback_log(this, ...args));
|
||||
}
|
||||
attributeChangedCallback(...args) {
|
||||
log.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
static get observedAttributes() { return ['id', 'class', 'title']; }
|
||||
}
|
||||
contentWindow.customElements.define('test-element', TestElement);
|
||||
assert_equals(Object.getPrototypeOf(element1), TestElement.prototype);
|
||||
assert_equals(Object.getPrototypeOf(element2), TestElement.prototype);
|
||||
|
||||
assert_equals(log.length, 7);
|
||||
assert_constructor_log_entry(log[0], element1);
|
||||
assert_attribute_log_entry(log[1], {name: 'id', oldValue: null, newValue: 'first-element', namespace: null});
|
||||
assert_connected_log_entry(log[2], element1);
|
||||
assert_constructor_log_entry(log[3], element2);
|
||||
assert_attribute_log_entry(log[4], {name: 'id', oldValue: null, newValue: 'second-element', namespace: null});
|
||||
assert_attribute_log_entry(log[5], {name: 'class', oldValue: null, newValue: 'foo', namespace: null});
|
||||
assert_connected_log_entry(log[6], element2);
|
||||
}, 'Mutating a undefined custom element while upgrading a custom element must not enqueue or invoke reactions on the mutated element');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
let log = [];
|
||||
let element1;
|
||||
let element2;
|
||||
class TestElement extends contentWindow.HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
log.push(create_constructor_log(this));
|
||||
}
|
||||
adoptedCallback(...args) {
|
||||
log.push(create_adopted_callback_log(this, ...args));
|
||||
if (this == element1)
|
||||
element3.setAttribute('id', 'foo');
|
||||
}
|
||||
connectedCallback(...args) {
|
||||
log.push(create_connected_callback_log(this, ...args));
|
||||
}
|
||||
attributeChangedCallback(...args) {
|
||||
log.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
static get observedAttributes() { return ['id', 'class']; }
|
||||
}
|
||||
|
||||
contentWindow.customElements.define('test-element', TestElement);
|
||||
|
||||
let contentDocument = contentWindow.document;
|
||||
element1 = contentDocument.createElement('test-element');
|
||||
element2 = contentDocument.createElement('test-element');
|
||||
element3 = contentDocument.createElement('test-element');
|
||||
assert_equals(Object.getPrototypeOf(element1), TestElement.prototype);
|
||||
assert_equals(Object.getPrototypeOf(element2), TestElement.prototype);
|
||||
assert_equals(Object.getPrototypeOf(element3), TestElement.prototype);
|
||||
|
||||
assert_equals(log.length, 3);
|
||||
assert_constructor_log_entry(log[0], element1);
|
||||
assert_constructor_log_entry(log[1], element2);
|
||||
assert_constructor_log_entry(log[2], element3);
|
||||
log = [];
|
||||
|
||||
const container = contentDocument.createElement('div');
|
||||
container.appendChild(element1);
|
||||
container.appendChild(element2);
|
||||
container.appendChild(element3);
|
||||
|
||||
const anotherDocument = document.implementation.createHTMLDocument();
|
||||
anotherDocument.documentElement.appendChild(container);
|
||||
|
||||
assert_equals(log.length, 7);
|
||||
assert_adopted_log_entry(log[0], element1);
|
||||
assert_adopted_log_entry(log[1], element3);
|
||||
assert_connected_log_entry(log[2], element3);
|
||||
assert_attribute_log_entry(log[3], {name: 'id', oldValue: null, newValue: 'foo', namespace: null});
|
||||
assert_connected_log_entry(log[4], element1);
|
||||
assert_adopted_log_entry(log[5], element2);
|
||||
assert_connected_log_entry(log[6], element2);
|
||||
|
||||
}, 'Mutating another custom element inside adopted callback must invoke all pending callbacks on the mutated element');
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,93 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: disconnectedCallback</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="disconnectedCallback must be enqueued whenever custom element is removed from a document">
|
||||
<link rel="help" href="https://w3c.github.io/webcomponents/spec/custom/#dfn-connected-callback">
|
||||
<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>
|
||||
|
||||
var calls = [];
|
||||
class MyCustomElement extends HTMLElement {
|
||||
connectedCallback() { calls.push('connected', this); }
|
||||
disconnectedCallback() { calls.push('disconnected', this); }
|
||||
}
|
||||
customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
document_types().forEach(function (entry) {
|
||||
var documentName = entry.name;
|
||||
var getDocument = entry.create;
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
doc.documentElement.appendChild(instance);
|
||||
calls = [];
|
||||
doc.documentElement.removeChild(instance);
|
||||
assert_array_equals(calls, ['disconnected', instance]);
|
||||
});
|
||||
}, 'Removing a custom element from ' + documentName + ' must enqueue and invoke disconnectedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var parent = document.createElement('div');
|
||||
parent.appendChild(instance);
|
||||
doc.documentElement.appendChild(parent);
|
||||
calls = [];
|
||||
doc.documentElement.removeChild(parent);
|
||||
assert_array_equals(calls, ['disconnected', instance]);
|
||||
});
|
||||
}, 'Removing an ancestor of custom element from ' + documentName + ' must enqueue and invoke disconnectedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
var shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
doc.documentElement.appendChild(host);
|
||||
shadowRoot.appendChild(instance);
|
||||
|
||||
calls = [];
|
||||
shadowRoot.removeChild(instance);
|
||||
assert_array_equals(calls, ['disconnected', instance]);
|
||||
});
|
||||
}, 'Removing a custom element from a shadow tree in ' + documentName + ' must enqueue and invoke disconnectedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
var shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
shadowRoot.appendChild(instance);
|
||||
doc.documentElement.appendChild(host);
|
||||
|
||||
calls = [];
|
||||
doc.documentElement.removeChild(host);
|
||||
assert_array_equals(calls, ['disconnected', instance]);
|
||||
});
|
||||
}, 'Removing the shadow host of a custom element from a' + documentName + ' must enqueue and invoke disconnectedCallback');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var host = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
var shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
shadowRoot.appendChild(instance);
|
||||
|
||||
calls = [];
|
||||
shadowRoot.removeChild(instance);
|
||||
assert_array_equals(calls, []);
|
||||
});
|
||||
}, 'Removing a custom element from a detached shadow tree that belongs to ' + documentName + ' must not enqueue and invoke disconnectedCallback');
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -5,120 +5,124 @@
|
|||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
test_with_window(w => {
|
||||
let afterDefinition = false;
|
||||
let beforeDefinition = true;
|
||||
const proto1 = { "proto": "number one" };
|
||||
const proto2 = { "proto": "number two" };
|
||||
|
||||
const TestElement = (function () {
|
||||
assert_throws({ name: "prototype throws" }, () => {
|
||||
const o = Reflect.construct(w.HTMLElement, [], new.target);
|
||||
function TestElement() {
|
||||
const o = Reflect.construct(w.HTMLElement, [], new.target);
|
||||
assert_equals(Object.getPrototypeOf(o), proto2,
|
||||
"Must use the value returned from new.target.prototype");
|
||||
assert_not_equals(Object.getPrototypeOf(o), proto1,
|
||||
"Must not use the prototype stored at definition time");
|
||||
}
|
||||
|
||||
assert_equals(Object.getPrototypeOf(o), proto2,
|
||||
"Must use the value returned from new.target.prototype");
|
||||
assert_not_equals(Object.getPrototypeOf(o), proto1,
|
||||
"Must not use the prototype stored at definition time");
|
||||
});
|
||||
}).bind({});
|
||||
|
||||
Object.defineProperty(TestElement, "prototype", {
|
||||
get() {
|
||||
return beforeDefinition ? proto1 : proto2;
|
||||
}
|
||||
const ElementWithDynamicPrototype = new Proxy(TestElement, {
|
||||
get: function (target, name) {
|
||||
if (name == "prototype")
|
||||
return beforeDefinition ? proto1 : proto2;
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
|
||||
w.customElements.define("test-element", TestElement);
|
||||
|
||||
beforeDefinition = true;
|
||||
new TestElement();
|
||||
w.customElements.define("test-element", ElementWithDynamicPrototype);
|
||||
|
||||
beforeDefinition = false;
|
||||
new ElementWithDynamicPrototype();
|
||||
}, "Use NewTarget's prototype, not the one stored at definition time");
|
||||
|
||||
test_with_window(w => {
|
||||
// We have to not throw during define(), but throw during super()
|
||||
let throws = false;
|
||||
|
||||
const TestElement = (function () {
|
||||
function TestElement() {
|
||||
throws = true;
|
||||
assert_throws({ name: "prototype throws" }, () => {
|
||||
return Reflect.construct(w.HTMLElement, [], new.target);
|
||||
Reflect.construct(w.HTMLElement, [], new.target);
|
||||
});
|
||||
}).bind({});
|
||||
}
|
||||
|
||||
Object.defineProperty(TestElement, "prototype", {
|
||||
get() {
|
||||
if (throws) {
|
||||
const ElementWithDynamicPrototype = new Proxy(TestElement, {
|
||||
get: function (target, name) {
|
||||
if (throws && name == "prototype")
|
||||
throw { name: "prototype throws" };
|
||||
}
|
||||
return {};
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
|
||||
w.customElements.define("test-element", TestElement);
|
||||
w.customElements.define("test-element", ElementWithDynamicPrototype);
|
||||
|
||||
throws = true;
|
||||
new TestElement();
|
||||
new ElementWithDynamicPrototype();
|
||||
|
||||
}, "Rethrow any exceptions thrown while getting the prototype");
|
||||
|
||||
test_with_window(w => {
|
||||
for (const notAnObject of [null, undefined, 5, "string"]) {
|
||||
[null, undefined, 5, "string"].forEach(function (notAnObject) {
|
||||
test_with_window(w => {
|
||||
// We have to return an object during define(), but not during super()
|
||||
let returnNotAnObject = false;
|
||||
|
||||
const TestElement = (function () {
|
||||
function TestElement() {
|
||||
const o = Reflect.construct(w.HTMLElement, [], new.target);
|
||||
|
||||
assert_equals(Object.getPrototypeOf(o), window.HTMLElement,
|
||||
assert_equals(Object.getPrototypeOf(new.target), window.Function.prototype);
|
||||
assert_equals(Object.getPrototypeOf(o), window.HTMLElement.prototype,
|
||||
"Must use the HTMLElement from the realm of NewTarget");
|
||||
assert_not_equals(Object.getPrototypeOf(o), w.HTMLElement,
|
||||
assert_not_equals(Object.getPrototypeOf(o), w.HTMLElement.prototype,
|
||||
"Must not use the HTMLElement from the realm of the active function object (w.HTMLElement)");
|
||||
|
||||
return o;
|
||||
}).bind({});
|
||||
}
|
||||
|
||||
Object.defineProperty(TestElement, "prototype", {
|
||||
get() {
|
||||
return returnNotAnObject ? notAnObject : {};
|
||||
const ElementWithDynamicPrototype = new Proxy(TestElement, {
|
||||
get: function (target, name) {
|
||||
if (name == "prototype")
|
||||
return returnNotAnObject ? notAnObject : {};
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
|
||||
w.customElements.define("test-element", TestElement);
|
||||
w.customElements.define("test-element", ElementWithDynamicPrototype);
|
||||
|
||||
returnNotAnObject = true;
|
||||
new TestElement();
|
||||
}
|
||||
}, "If prototype is not object, derives the fallback from NewTarget's realm (autonomous custom elements)");
|
||||
new ElementWithDynamicPrototype();
|
||||
}, "If prototype is not object (" + notAnObject + "), derives the fallback from NewTarget's realm (autonomous custom elements)");
|
||||
});
|
||||
|
||||
test_with_window(w => {
|
||||
for (const notAnObject of [null, undefined, 5, "string"]) {
|
||||
[null, undefined, 5, "string"].forEach(function (notAnObject) {
|
||||
test_with_window(w => {
|
||||
// We have to return an object during define(), but not during super()
|
||||
let returnNotAnObject = false;
|
||||
|
||||
const TestElement = (function () {
|
||||
function TestElement() {
|
||||
const o = Reflect.construct(w.HTMLParagraphElement, [], new.target);
|
||||
|
||||
assert_equals(Object.getPrototypeOf(o), window.HTMLParagraphElement,
|
||||
assert_equals(Object.getPrototypeOf(o), window.HTMLParagraphElement.prototype,
|
||||
"Must use the HTMLParagraphElement from the realm of NewTarget");
|
||||
assert_not_equals(Object.getPrototypeOf(o), w.HTMLParagraphElement,
|
||||
assert_not_equals(Object.getPrototypeOf(o), w.HTMLParagraphElement.prototype,
|
||||
"Must not use the HTMLParagraphElement from the realm of the active function object (w.HTMLParagraphElement)");
|
||||
|
||||
return o;
|
||||
}).bind({});
|
||||
}
|
||||
|
||||
Object.defineProperty(TestElement, "prototype", {
|
||||
get() {
|
||||
return returnNotAnObject ? notAnObject : {};
|
||||
const ElementWithDynamicPrototype = new Proxy(TestElement, {
|
||||
get: function (target, name) {
|
||||
if (name == "prototype")
|
||||
return returnNotAnObject ? notAnObject : {};
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
|
||||
w.customElements.define("test-element", TestElement, { extends: "p" });
|
||||
w.customElements.define("test-element", ElementWithDynamicPrototype, { extends: "p" });
|
||||
|
||||
returnNotAnObject = true;
|
||||
new TestElement();
|
||||
}
|
||||
}, "If prototype is not object, derives the fallback from NewTarget's realm (customized built-in elements)");
|
||||
new ElementWithDynamicPrototype();
|
||||
}, "If prototype is not object (" + notAnObject + "), derives the fallback from NewTarget's realm (customized built-in elements)");
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: Changes to the HTML parser</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="HTML parser must construct custom elements inside document.write">
|
||||
<link rel="help" href="https://html.spec.whatwg.org/#create-an-element-for-the-token">
|
||||
<link rel="help" href="https://dom.spec.whatwg.org/#concept-create-element">
|
||||
<link rel="help" href="https://html.spec.whatwg.org/#document.write()">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
class MyCustomElement extends HTMLElement { }
|
||||
customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
document.write('<my-custom-element></my-custom-element>');
|
||||
|
||||
test(function () {
|
||||
var instance = document.querySelector('my-custom-element');
|
||||
|
||||
assert_true(instance instanceof HTMLElement);
|
||||
assert_true(instance instanceof MyCustomElement);
|
||||
|
||||
}, 'HTML parser must instantiate custom elements inside document.write');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: Changes to the HTML parser</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="HTML parser must construct a custom element synchronously">
|
||||
<link rel="help" href="https://html.spec.whatwg.org/#create-an-element-for-the-token">
|
||||
<link rel="help" href="https://dom.spec.whatwg.org/#concept-create-element">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
var childElementCountInConstructor;
|
||||
var containerChildNodesInConstructor = [];
|
||||
var containerNextSiblingInConstructor;
|
||||
class MyCustomElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
var container = document.getElementById('custom-element-container');
|
||||
for (var i = 0; i < container.childNodes.length; i++)
|
||||
containerChildNodesInConstructor.push(container.childNodes[i]);
|
||||
containerNextSiblingInConstructor = container.nextSibling;
|
||||
}
|
||||
};
|
||||
customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
</script>
|
||||
<div id="custom-element-container">
|
||||
<span id="custom-element-previous-element"></span>
|
||||
<my-custom-element></my-custom-element>
|
||||
<div id="custom-element-next-element"></div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
test(function () {
|
||||
var instance = document.querySelector('my-custom-element');
|
||||
|
||||
assert_equals(containerChildNodesInConstructor.length, 3);
|
||||
assert_equals(containerChildNodesInConstructor[0], instance.parentNode.firstChild);
|
||||
assert_equals(containerChildNodesInConstructor[1], document.getElementById('custom-element-previous-element'));
|
||||
assert_equals(containerChildNodesInConstructor[2], instance.previousSibling);
|
||||
assert_equals(containerNextSiblingInConstructor, null);
|
||||
|
||||
}, 'HTML parser must only append nodes that appear before a custom element before instantiating the custom element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,48 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: Changes to the HTML parser</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="HTML parser creates a custom element">
|
||||
<link rel="help" href="https://html.spec.whatwg.org/#create-an-element-for-the-token">
|
||||
<link rel="help" href="https://dom.spec.whatwg.org/#concept-create-element">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<my-custom-element id="instance1"></my-custom-element>
|
||||
<script>
|
||||
|
||||
class MyCustomElement extends HTMLElement { };
|
||||
|
||||
test(function () {
|
||||
var customElement = document.getElementById('instance1');
|
||||
|
||||
assert_true(customElement instanceof HTMLElement, 'An unresolved custom element must be an instance of HTMLElement');
|
||||
assert_false(customElement instanceof MyCustomElement, 'An unresolved custom element must NOT be an instance of that custom element');
|
||||
assert_equals(customElement.localName, 'my-custom-element');
|
||||
assert_equals(customElement.namespaceURI, 'http://www.w3.org/1999/xhtml', 'A custom element HTML must use HTML namespace');
|
||||
|
||||
}, 'HTML parser must NOT create a custom element before customElements.define is called');
|
||||
|
||||
customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
</script>
|
||||
<my-custom-element id="instance2"></my-custom-element>
|
||||
<script>
|
||||
|
||||
test(function () {
|
||||
var customElement = document.getElementById('instance2');
|
||||
|
||||
assert_true(customElement instanceof HTMLElement, 'A resolved custom element must be an instance of HTMLElement');
|
||||
assert_false(customElement instanceof HTMLUnknownElement, 'A resolved custom element must NOT be an instance of HTMLUnknownElement');
|
||||
assert_true(customElement instanceof MyCustomElement, 'A resolved custom element must be an instance of that custom element');
|
||||
assert_equals(customElement.localName, 'my-custom-element');
|
||||
assert_equals(customElement.namespaceURI, 'http://www.w3.org/1999/xhtml', 'A custom element HTML must use HTML namespace');
|
||||
|
||||
}, 'HTML parser must create a defined custom element before executing inline scripts');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,91 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: Changes to the HTML parser</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="HTML parser must fallback to creating a HTMLUnknownElement when a custom element construction fails">
|
||||
<link rel="help" href="https://html.spec.whatwg.org/#create-an-element-for-the-token">
|
||||
<link rel="help" href="https://dom.spec.whatwg.org/#concept-create-element">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
setup({allow_uncaught_exception:true});
|
||||
|
||||
class ReturnsTextNode extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
return document.createTextNode('some text');
|
||||
}
|
||||
};
|
||||
customElements.define('returns-text', ReturnsTextNode);
|
||||
|
||||
class ReturnsNonElementObject extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
return {};
|
||||
}
|
||||
};
|
||||
customElements.define('returns-non-element-object', ReturnsNonElementObject);
|
||||
|
||||
class LacksSuperCall extends HTMLElement {
|
||||
constructor() { }
|
||||
};
|
||||
customElements.define('lacks-super-call', LacksSuperCall);
|
||||
|
||||
class ThrowsException extends HTMLElement {
|
||||
constructor() {
|
||||
throw 'Bad';
|
||||
}
|
||||
};
|
||||
customElements.define('throws-exception', ThrowsException);
|
||||
|
||||
</script>
|
||||
<returns-text></returns-text>
|
||||
<returns-non-element-object></returns-non-element-object>
|
||||
<lacks-super-call></lacks-super-call>
|
||||
<throws-exception></throws-exception>
|
||||
<script>
|
||||
|
||||
test(function () {
|
||||
var instance = document.querySelector('returns-text');
|
||||
|
||||
assert_false(instance instanceof ReturnsTextNode, 'HTML parser must NOT instantiate a custom element when the constructor returns a Text node');
|
||||
assert_true(instance instanceof HTMLElement, 'The fallback element created by HTML parser must be an instance of HTMLElement');
|
||||
assert_true(instance instanceof HTMLUnknownElement, 'The fallback element created by HTML parser must be an instance of HTMLUnknownElement');
|
||||
|
||||
}, 'HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns a Text node');
|
||||
|
||||
test(function () {
|
||||
var instance = document.querySelector('returns-non-element-object');
|
||||
|
||||
assert_false(instance instanceof ReturnsNonElementObject, 'HTML parser must NOT instantiate a custom element when the constructor returns a non-Element object');
|
||||
assert_true(instance instanceof HTMLElement, 'The fallback element created by HTML parser must be an instance of HTMLElement');
|
||||
assert_true(instance instanceof HTMLUnknownElement, 'The fallback element created by HTML parser must be an instance of HTMLUnknownElement');
|
||||
|
||||
}, 'HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns non-Element object');
|
||||
|
||||
test(function () {
|
||||
var instance = document.querySelector('lacks-super-call');
|
||||
|
||||
assert_false(instance instanceof LacksSuperCall, 'HTML parser must NOT instantiate a custom element when the constructor does not call super()');
|
||||
assert_true(instance instanceof HTMLElement, 'The fallback element created by HTML parser must be an instance of HTMLElement');
|
||||
assert_true(instance instanceof HTMLUnknownElement, 'The fallback element created by HTML parser must be an instance of HTMLUnknownElement');
|
||||
|
||||
}, 'HTML parser must create a fallback HTMLUnknownElement when a custom element constructor does not call super()');
|
||||
|
||||
test(function () {
|
||||
var instance = document.querySelector('throws-exception');
|
||||
|
||||
assert_false(instance instanceof ThrowsException, 'HTML parser must NOT instantiate a custom element when the constructor throws an exception');
|
||||
assert_true(instance instanceof HTMLElement, 'The fallback element created by HTML parser must be an instance of HTMLElement');
|
||||
assert_true(instance instanceof HTMLUnknownElement, 'The fallback element created by HTML parser must be an instance of HTMLUnknownElement');
|
||||
|
||||
}, 'HTML parser must create a fallback HTMLUnknownElement when a custom element constructor throws an exception');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,59 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: Changes to the HTML parser</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="HTML parser must set the attributes and append the children on a custom element">
|
||||
<link rel="help" href="https://html.spec.whatwg.org/#create-an-element-for-the-token">
|
||||
<link rel="help" href="https://dom.spec.whatwg.org/#concept-create-element">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
var numberOfAttributesInConstructor;
|
||||
var numberOfChildNodesInConstructor;
|
||||
|
||||
class MyCustomElement extends HTMLElement {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
numberOfAttributesInConstructor = this.attributes.length;
|
||||
numberOfChildNodesInConstructor = this.childNodes.length;
|
||||
}
|
||||
};
|
||||
customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
</script>
|
||||
<my-custom-element id="custom-element-id" class="class1 class2">hello <b>world</b></my-custom-element>
|
||||
<script>
|
||||
|
||||
var customElement = document.querySelector('my-custom-element');
|
||||
|
||||
test(function () {
|
||||
assert_equals(customElement.getAttribute('id'), 'custom-element-id', 'HTML parser must preserve the id attribute');
|
||||
assert_equals(customElement.id, 'custom-element-id', 'HTML parser must preserve the semantics of reflect for the id attribute');
|
||||
assert_equals(customElement.getAttribute('class'), 'class1 class2', 'HTML parser must preserve the class attribute');
|
||||
assert_equals(customElement.classList.length, 2, 'HTML parser must initialize classList on custom elements');
|
||||
assert_equals(customElement.classList[0], 'class1', 'HTML parser must initialize classList on custom elements');
|
||||
assert_equals(customElement.classList[1], 'class2', 'HTML parser must initialize classList on custom elements');
|
||||
}, 'HTML parser must set the attributes');
|
||||
|
||||
test(function () {
|
||||
assert_equals(customElement.childNodes.length, 2, 'HTML parser must append child nodes');
|
||||
assert_true(customElement.firstChild instanceof Text, 'HTML parser must append Text node child to a custom element');
|
||||
assert_equals(customElement.firstChild.data, 'hello ', 'HTML parser must append Text node child to a custom element');
|
||||
assert_true(customElement.lastChild instanceof HTMLElement, 'HTML parser must append a builtin element child to a custom element');
|
||||
assert_true(customElement.lastChild.firstChild instanceof Text, 'HTML parser must preserve grandchild nodes of a custom element');
|
||||
assert_equals(customElement.lastChild.firstChild.data, 'world', 'HTML parser must preserve grandchild nodes of a custom element');
|
||||
}, 'HTML parser must append child nodes');
|
||||
|
||||
test(function () {
|
||||
assert_equals(numberOfAttributesInConstructor, 0, 'HTML parser must not set attributes on a custom element before invoking the constructor');
|
||||
assert_equals(numberOfChildNodesInConstructor, 0, 'HTML parser must not append child nodes to a custom element before invoking the constructor');
|
||||
}, 'HTML parser must set the attributes or append children before calling constructor');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,75 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: HTML parser must construct a custom element instead of upgrading</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="HTML parser must construct a custom element instead of upgrading">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<link rel="help" href="https://html.spec.whatwg.org/#create-an-element-for-the-token">
|
||||
<link rel="help" href="https://dom.spec.whatwg.org/#concept-create-element">
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
let anotherElementCreatedBeforeSuperCall = undefined;
|
||||
let elementCreatedBySuperCall = undefined;
|
||||
let shouldCreateElementBeforeSuperCall = true;
|
||||
class InstantiatesItselfBeforeSuper extends HTMLElement {
|
||||
constructor() {
|
||||
if (shouldCreateElementBeforeSuperCall) {
|
||||
shouldCreateElementBeforeSuperCall = false;
|
||||
anotherElementCreatedBeforeSuperCall = new InstantiatesItselfBeforeSuper();
|
||||
}
|
||||
super();
|
||||
elementCreatedBySuperCall = this;
|
||||
}
|
||||
};
|
||||
customElements.define('instantiates-itself-before-super', InstantiatesItselfBeforeSuper);
|
||||
|
||||
let shouldCreateAnotherInstance = true;
|
||||
let anotherInstance = undefined;
|
||||
let firstInstance = undefined;
|
||||
class ReturnsAnotherInstance extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
if (shouldCreateAnotherInstance) {
|
||||
shouldCreateAnotherInstance = false;
|
||||
firstInstance = this;
|
||||
anotherInstance = new ReturnsAnotherInstance;
|
||||
return anotherInstance;
|
||||
} else
|
||||
return this;
|
||||
}
|
||||
};
|
||||
customElements.define('returns-another-instance', ReturnsAnotherInstance);
|
||||
|
||||
</script>
|
||||
<instantiates-itself-before-super></instantiates-itself-before-super>
|
||||
<returns-another-instance></returns-another-instance>
|
||||
<script>
|
||||
|
||||
test(function () {
|
||||
var instance = document.querySelector('instantiates-itself-before-super');
|
||||
|
||||
assert_equals(instance instanceof InstantiatesItselfBeforeSuper, 'HTML parser must insert the synchronously constructed custom element');
|
||||
assert_equals(instance, elementCreatedBySuperCall, 'HTML parser must insert the element returned by the custom element constructor');
|
||||
assert_not_equals(instance, anotherElementCreatedBeforeSuperCall, 'HTML parser must not insert another instance of the custom element created before super() call');
|
||||
assert_equals(anotherElementCreatedBeforeSuperCall.parentNode, null, 'HTML parser must not insert another instance of the custom element created before super() call');
|
||||
|
||||
}, 'HTML parser must use the returned value of the custom element constructor instead of the one created before super() call');
|
||||
|
||||
test(function () {
|
||||
var instance = document.querySelector('returns-another-instance');
|
||||
|
||||
assert_equals(instance instanceof ReturnsAnotherInstance, 'HTML parser must insert the synchronously constructed custom element');
|
||||
assert_equals(instance, anotherInstance, 'HTML parser must insert the element returned by the custom element constructor');
|
||||
assert_not_equals(instance, firstInstance, 'HTML parser must not insert the element created by super() call if the constructor returned another element');
|
||||
assert_equals(firstInstance.parentNode, null, 'HTML parser must not insert the element created by super() call if the constructor returned another element');
|
||||
|
||||
}, 'HTML parser must use the returned value of the custom element constructor instead using the one created in super() call');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,126 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: HTML parser must use the owner document's custom element registry</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="HTML parser must use the owner document's custom element registry">
|
||||
<link rel="help" href="https://html.spec.whatwg.org/#create-an-element-for-the-token">
|
||||
<link rel="help" href="https://dom.spec.whatwg.org/#concept-create-element">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
class MyCustomElement extends HTMLElement { };
|
||||
customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
document.write('<template><my-custom-element></my-custom-element></template>');
|
||||
|
||||
test(function () {
|
||||
var template = document.querySelector('template');
|
||||
var instance = template.content.firstChild;
|
||||
|
||||
assert_true(instance instanceof HTMLElement,
|
||||
'A custom element inside a template element must be an instance of HTMLElement');
|
||||
assert_false(instance instanceof MyCustomElement,
|
||||
'A custom element must not be instantiated inside a template element using the registry of the template element\'s owner document');
|
||||
assert_equals(instance.ownerDocument, template.content.ownerDocument,
|
||||
'Custom elements inside a template must use the appropriate template contents owner document as the owner document');
|
||||
|
||||
}, 'HTML parser must not instantiate custom elements inside template elements');
|
||||
|
||||
var iframe = document.createElement('iframe');
|
||||
document.body.appendChild(iframe);
|
||||
iframe.contentDocument.body.innerHTML = '<my-custom-element></my-custom-element>';
|
||||
|
||||
test(function () {
|
||||
var instance = iframe.contentDocument.querySelector('my-custom-element');
|
||||
|
||||
assert_true(instance instanceof iframe.contentWindow.HTMLElement);
|
||||
assert_false(instance instanceof MyCustomElement);
|
||||
|
||||
}, 'HTML parser must not use the registry of the owner element\'s document inside an iframe');
|
||||
|
||||
class ElementInIFrame extends iframe.contentWindow.HTMLElement { };
|
||||
iframe.contentWindow.customElements.define('element-in-iframe', ElementInIFrame);
|
||||
iframe.contentDocument.body.innerHTML = '<element-in-iframe></element-in-iframe>';
|
||||
|
||||
test(function () {
|
||||
var instance = iframe.contentDocument.querySelector('element-in-iframe');
|
||||
|
||||
assert_true(instance instanceof iframe.contentWindow.HTMLElement, 'A custom element inside an iframe must be an instance of HTMLElement');
|
||||
assert_true(instance instanceof ElementInIFrame,
|
||||
'A custom element must be instantiated inside an iframe using the registry of the content document');
|
||||
assert_equals(instance.ownerDocument, iframe.contentDocument,
|
||||
'The owner document of custom elements inside an iframe must be the content document of the iframe');
|
||||
|
||||
}, 'HTML parser must use the registry of the content document inside an iframe');
|
||||
|
||||
document.write('<element-in-iframe></element-in-iframe>');
|
||||
|
||||
test(function () {
|
||||
var instance = document.querySelector('element-in-iframe');
|
||||
|
||||
assert_true(instance instanceof HTMLElement);
|
||||
assert_false(instance instanceof ElementInIFrame);
|
||||
|
||||
}, 'HTML parser must not instantiate a custom element defined inside an frame in frame element\'s owner document');
|
||||
|
||||
document.body.removeChild(iframe);
|
||||
|
||||
test(function () {
|
||||
var windowlessDocument = document.implementation.createHTMLDocument();
|
||||
windowlessDocument.open();
|
||||
windowlessDocument.write('<my-custom-element></my-custom-element>');
|
||||
windowlessDocument.close();
|
||||
|
||||
var instance = windowlessDocument.querySelector('my-custom-element');
|
||||
|
||||
assert_true(instance instanceof HTMLElement);
|
||||
assert_false(instance instanceof MyCustomElement);
|
||||
|
||||
}, 'HTML parser must use the registry of window.document in a document created by document.implementation.createHTMLDocument()');
|
||||
|
||||
test(function () {
|
||||
var windowlessDocument = document.implementation.createDocument ('http://www.w3.org/1999/xhtml', 'html', null);
|
||||
windowlessDocument.documentElement.innerHTML = '<my-custom-element></my-custom-element>';
|
||||
|
||||
var instance = windowlessDocument.querySelector('my-custom-element');
|
||||
assert_true(instance instanceof HTMLElement);
|
||||
assert_false(instance instanceof MyCustomElement);
|
||||
|
||||
}, 'HTML parser must use the registry of window.document in a document created by document.implementation.createXHTMLDocument()');
|
||||
|
||||
test(function () {
|
||||
var windowlessDocument = new Document;
|
||||
windowlessDocument.appendChild(windowlessDocument.createElement('html'));
|
||||
windowlessDocument.documentElement.innerHTML = '<my-custom-element></my-custom-element>';
|
||||
|
||||
var instance = windowlessDocument.querySelector('my-custom-element');
|
||||
|
||||
assert_true(instance instanceof Element);
|
||||
assert_false(instance instanceof MyCustomElement);
|
||||
|
||||
}, 'HTML parser must use the registry of window.document in a document created by new Document');
|
||||
|
||||
promise_test(function () {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', '../resources/empty-html-document.html');
|
||||
xhr.overrideMimeType('text/xml');
|
||||
xhr.onload = function () { resolve(xhr.responseXML); }
|
||||
xhr.onerror = function () { reject('Failed to fetch the document'); }
|
||||
xhr.send();
|
||||
}).then(function (doc) {
|
||||
doc.documentElement.innerHTML = '<my-custom-element></my-custom-element>';
|
||||
var instance = doc.querySelector('my-custom-element');
|
||||
assert_true(instance instanceof Element);
|
||||
assert_false(instance instanceof MyCustomElement);
|
||||
});
|
||||
}, 'HTML parser must use the registry of window.document in a document created by XMLHttpRequest');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,88 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: Custom element reactions must be invoked before returning to author scripts</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="Custom element reactions must be invoked before returning to author scripts">
|
||||
<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#invoke-custom-element-reactions">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
class MyCustomElement extends HTMLElement {
|
||||
attributeChangedCallback(...args) {
|
||||
this.handler(...args);
|
||||
}
|
||||
|
||||
handler() { }
|
||||
}
|
||||
MyCustomElement.observedAttributes = ['data-title', 'title'];
|
||||
customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
test(function () {
|
||||
var instance = document.createElement('my-custom-element');
|
||||
var anotherInstance = document.createElement('my-custom-element');
|
||||
|
||||
var callbackOrder = [];
|
||||
instance.handler = function () {
|
||||
callbackOrder.push([this, 'begin']);
|
||||
anotherInstance.setAttribute('data-title', 'baz');
|
||||
callbackOrder.push([this, 'end']);
|
||||
}
|
||||
anotherInstance.handler = function () {
|
||||
callbackOrder.push([this, 'begin']);
|
||||
callbackOrder.push([this, 'end']);
|
||||
}
|
||||
|
||||
instance.setAttribute('title', 'foo');
|
||||
assert_equals(callbackOrder.length, 4);
|
||||
|
||||
assert_array_equals(callbackOrder[0], [instance, 'begin']);
|
||||
assert_array_equals(callbackOrder[1], [anotherInstance, 'begin']);
|
||||
assert_array_equals(callbackOrder[2], [anotherInstance, 'end']);
|
||||
assert_array_equals(callbackOrder[3], [instance, 'end']);
|
||||
|
||||
}, 'setAttribute and removeAttribute must enqueue and invoke attributeChangedCallback');
|
||||
|
||||
test(function () {
|
||||
var shouldCloneAnotherInstance = false;
|
||||
var anotherInstanceClone;
|
||||
var log = [];
|
||||
|
||||
class SelfCloningElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
log.push([this, 'begin']);
|
||||
if (shouldCloneAnotherInstance) {
|
||||
shouldCloneAnotherInstance = false;
|
||||
anotherInstanceClone = anotherInstance.cloneNode(false);
|
||||
}
|
||||
log.push([this, 'end']);
|
||||
}
|
||||
}
|
||||
customElements.define('self-cloning-element', SelfCloningElement);
|
||||
|
||||
var instance = document.createElement('self-cloning-element');
|
||||
var anotherInstance = document.createElement('self-cloning-element');
|
||||
shouldCloneAnotherInstance = true;
|
||||
|
||||
assert_equals(log.length, 4);
|
||||
var instanceClone = instance.cloneNode(false);
|
||||
|
||||
assert_equals(log.length, 8);
|
||||
assert_array_equals(log[0], [instance, 'begin']);
|
||||
assert_array_equals(log[1], [instance, 'end']);
|
||||
assert_array_equals(log[2], [anotherInstance, 'begin']);
|
||||
assert_array_equals(log[3], [anotherInstance, 'end']);
|
||||
assert_array_equals(log[4], [instanceClone, 'begin']);
|
||||
assert_array_equals(log[5], [anotherInstanceClone, 'begin']);
|
||||
assert_array_equals(log[6], [anotherInstanceClone, 'end']);
|
||||
assert_array_equals(log[7], [instanceClone, 'end']);
|
||||
}, 'Calling Node.prototype.cloneNode(false) must push a new element queue to the processing stack');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on Attr interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="value of Attr interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
testAttributeMutator(function (element, name, value) {
|
||||
element.attributes[name].value = value;
|
||||
}, 'value on Attr');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,85 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on CSSStyleDeclaration interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="cssText, setProperty, setPropertyValue, setPropertyPriority, removeProperty, cssFloat, and all camel cased attributes of CSSStyleDeclaration interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test_mutating_style_property_value(function (instance, propertyName, idlName, value) {
|
||||
instance.style.cssText = `${propertyName}: ${value}`;
|
||||
}, 'cssText on CSSStyleDeclaration');
|
||||
|
||||
test_mutating_style_property_value(function (instance, propertyName, idlName, value) {
|
||||
instance.style.setProperty(propertyName, value);
|
||||
}, 'setProperty on CSSStyleDeclaration');
|
||||
|
||||
test_mutating_style_property_priority(function (instance, propertyName, idlName, isImportant) {
|
||||
instance.style.setProperty(propertyName, instance.style[idlName], isImportant ? 'important': '');
|
||||
}, 'setProperty on CSSStyleDeclaration');
|
||||
|
||||
test_mutating_style_property_value(function (instance, propertyName, idlName, value) {
|
||||
instance.style.setPropertyValue(propertyName, value);
|
||||
}, 'setPropertyValue on CSSStyleDeclaration');
|
||||
|
||||
test_mutating_style_property_priority(function (instance, propertyName, idlName, isImportant) {
|
||||
instance.style.setPropertyPriority(propertyName, isImportant ? 'important': '');
|
||||
}, 'setPropertyPriority on CSSStyleDeclaration');
|
||||
|
||||
test_removing_style_property_value(function (instance, propertyName, idlName) {
|
||||
instance.style.removeProperty(propertyName);
|
||||
}, 'removeProperty on CSSStyleDeclaration');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['style']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.style.cssFloat = 'left';
|
||||
assert_equals(instance.getAttribute('style'), 'float: left;');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'style', oldValue: null, newValue: 'float: left;', namespace: null});
|
||||
}, 'cssFloat on CSSStyleDeclaration must enqueue an attributeChanged reaction when it adds the observed style attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element([]);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.style.cssFloat = 'left';
|
||||
assert_equals(instance.getAttribute('style'), 'float: left;');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'cssFloat on CSSStyleDeclaration must not enqueue an attributeChanged reaction when it adds the style attribute but the style attribute is not observed');
|
||||
|
||||
test_mutating_style_property_value(function (instance, propertyName, idlName, value) {
|
||||
assert_equals(idlName, 'borderWidth');
|
||||
instance.style.borderWidth = value;
|
||||
}, 'A camel case attribute (borderWidth) on CSSStyleDeclaration',
|
||||
{propertyName: 'border-width', idlName: 'borderWidth', value1: '2px', value2: '4px'});
|
||||
|
||||
test_mutating_style_property_value(function (instance, propertyName, idlName, value) {
|
||||
assert_equals(propertyName, 'border-width');
|
||||
instance.style['border-width'] = value;
|
||||
}, 'A dashed property (border-width) on CSSStyleDeclaration',
|
||||
{propertyName: 'border-width', idlName: 'borderWidth', value1: '1px', value2: '5px'});
|
||||
|
||||
test_mutating_style_property_value(function (instance, propertyName, idlName, value) {
|
||||
instance.style.webkitFilter = value;
|
||||
}, 'A webkit prefixed camel case attribute (webkitFilter) on CSSStyleDeclaration',
|
||||
{propertyName: 'filter', idlName: 'filter', value1: 'grayscale(20%)', value2: 'grayscale(30%)'});
|
||||
|
||||
test_mutating_style_property_value(function (instance, propertyName, idlName, value) {
|
||||
instance.style['-webkit-filter'] = value;
|
||||
}, 'A webkit prefixed dashed property (-webkit-filter) on CSSStyleDeclaration',
|
||||
{propertyName: 'filter', idlName: 'filter', value1: 'grayscale(20%)', value2: 'grayscale(30%)'});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on ChildNode interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="before, after, after, replaceWith, and remove of ChildNode interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#parentnode">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
testNodeConnector(function (newContainer, customElement) {
|
||||
newContainer.firstChild.before(customElement);
|
||||
}, 'before on ChildNode');
|
||||
|
||||
testNodeConnector(function (newContainer, customElement) {
|
||||
newContainer.firstChild.after(customElement);
|
||||
}, 'after on ChildNode');
|
||||
|
||||
testNodeConnector(function (newContainer, customElement) {
|
||||
newContainer.firstChild.replaceWith(customElement);
|
||||
}, 'replaceWith on ChildNode');
|
||||
|
||||
testNodeDisconnector(function (customElement) {
|
||||
customElement.remove();
|
||||
}, 'replaceWith on ChildNode');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,96 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on DOMStringMap interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="setter and deleter of DOMStringMap interface must have CEReactions">
|
||||
<meta name="help" content="https://html.spec.whatwg.org/#domstringmap">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['data-foo']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.dataset.foo = 'bar';
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'data-foo', oldValue: null, newValue: 'bar', namespace: null});
|
||||
}, 'setter on DOMStringMap must enqueue an attributeChanged reaction when adding an observed data attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['data-bar']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.dataset.foo = 'bar';
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'setter on DOMStringMap must not enqueue an attributeChanged reaction when adding an unobserved data attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['data-foo']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.dataset.foo = 'bar';
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance.dataset.foo = 'baz';
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'data-foo', oldValue: 'bar', newValue: 'baz', namespace: null});
|
||||
}, 'setter on DOMStringMap must enqueue an attributeChanged reaction when mutating the value of an observed data attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['data-foo']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.dataset.foo = 'bar';
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance.dataset.foo = 'bar';
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'data-foo', oldValue: 'bar', newValue: 'bar', namespace: null});
|
||||
}, 'setter on DOMStringMap must enqueue an attributeChanged reaction when mutating the value of an observed data attribute to the same value');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['data-zero']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.dataset.foo = 'bar';
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.dataset.foo = 'baz';
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'setter on DOMStringMap must not enqueue an attributeChanged reaction when mutating the value of an unobserved data attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['data-foo']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.dataset.foo = 'bar';
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
delete instance.dataset.foo;
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'data-foo', oldValue: 'bar', newValue: null, namespace: null});
|
||||
}, 'deleter on DOMStringMap must enqueue an attributeChanged reaction when removing an observed data attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['data-bar']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.dataset.foo = 'bar';
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
delete instance.dataset.foo;
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'deleter on DOMStringMap must not enqueue an attributeChanged reaction when removing an unobserved data attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['data-foo']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
delete instance.dataset.foo;
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'deleter on DOMStringMap must not enqueue an attributeChanged reaction when it does not remove a data attribute');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,219 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on DOMTokenList interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="add, remove, toggle, replace, and the stringifier of DOMTokenList interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.classList.add('foo');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: null, newValue: 'foo', namespace: null});
|
||||
}, 'add on DOMTokenList must enqueue an attributeChanged reaction when adding an attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['style']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.classList.add('foo');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'add on DOMTokenList must not enqueue an attributeChanged reaction when adding an unobserved attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance.classList.add('world');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello', newValue: 'hello world', namespace: null});
|
||||
}, 'add on DOMTokenList must enqueue an attributeChanged reaction when adding a value to an existing attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['contenteditable']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.classList.add('world');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'add on DOMTokenList must not enqueue an attributeChanged reaction when adding a value to an unobserved attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.classList.add('hello', 'world');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: null, newValue: 'hello world', namespace: null});
|
||||
}, 'add on DOMTokenList must enqueue exactly one attributeChanged reaction when adding multiple values to an attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello world');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance.classList.remove('world');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello world', newValue: 'hello', namespace: null});
|
||||
}, 'remove on DOMTokenList must enqueue an attributeChanged reaction when removing a value from an attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello foo world bar');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance.classList.remove('hello', 'world');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello foo world bar', newValue: 'foo bar', namespace: null});
|
||||
}, 'remove on DOMTokenList must enqueue exactly one attributeChanged reaction when removing multiple values to an attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello world');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance.classList.remove('foo');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello world', newValue: 'hello world', namespace: null});
|
||||
}, 'remove on DOMTokenList must enqueue an attributeChanged reaction even when removing a non-existent value from an attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['title']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello world');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.classList.remove('world');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'remove on DOMTokenList must not enqueue an attributeChanged reaction when removing a value from an unobserved attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance.classList.toggle('world');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello', newValue: 'hello world', namespace: null});
|
||||
}, 'toggle on DOMTokenList must enqueue an attributeChanged reaction when adding a value to an attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello world');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance.classList.toggle('world');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello world', newValue: 'hello', namespace: null});
|
||||
}, 'toggle on DOMTokenList must enqueue an attributeChanged reaction when removing a value from an attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['lang']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello world');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.classList.toggle('world');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'remove on DOMTokenList must not enqueue an attributeChanged reaction when removing a value from an unobserved attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance.classList.replace('hello', 'world');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello', newValue: 'world', namespace: null});
|
||||
}, 'replace on DOMTokenList must enqueue an attributeChanged reaction when replacing a value in an attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello world');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance.classList.replace('foo', 'bar');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'replace on DOMTokenList must not enqueue an attributeChanged reaction when the token to replace does not exist in the attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['title']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.classList.replace('hello', 'world');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'replace on DOMTokenList must not enqueue an attributeChanged reaction when replacing a value in an unobserved attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.classList = 'hello';
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: null, newValue: 'hello', namespace: null});
|
||||
}, 'the stringifier of DOMTokenList must enqueue an attributeChanged reaction when adding an observed attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['id']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.classList = 'hello';
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'the stringifier of DOMTokenList must not enqueue an attributeChanged reaction when adding an unobserved attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance.classList = 'world';
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello', newValue: 'world', namespace: null});
|
||||
}, 'the stringifier of DOMTokenList must enqueue an attributeChanged reaction when mutating the value of an observed attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element([]);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance.classList = 'world';
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'the stringifier of DOMTokenList must not enqueue an attributeChanged reaction when mutating the value of an unobserved attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('class', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance.classList = 'hello';
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello', newValue: 'hello', namespace: null});
|
||||
}, 'the stringifier of DOMTokenList must enqueue an attributeChanged reaction when the setter is called with the original value of the attribute');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,154 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on Document interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="importNode and adoptNode of Document interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#document">
|
||||
<meta name="help" content="https://html.spec.whatwg.org/#document">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
const instance = contentDocument.createElement('custom-element');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
|
||||
const newDoc = contentDocument.implementation.createHTMLDocument();
|
||||
newDoc.importNode(instance);
|
||||
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, 'importNode on Document must not construct a new custom element when importing a custom element into a window-less document');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
const template = contentDocument.createElement('template');
|
||||
template.innerHTML = '<custom-element></custom-element>';
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
contentDocument.importNode(template.content, true);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
}, 'importNode on Document must construct a new custom element when importing a custom element from a template');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
const instance = contentDocument.createElement('custom-element');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
|
||||
const newDoc = contentDocument.implementation.createHTMLDocument();
|
||||
newDoc.adoptNode(instance);
|
||||
|
||||
const logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['adopted']);
|
||||
assert_equals(logEntries.last().oldDocument, contentDocument);
|
||||
assert_equals(logEntries.last().newDocument, newDoc);
|
||||
}, 'adoptNode on Document must enqueue an adopted reaction when importing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
const instance = contentDocument.createElement('custom-element');
|
||||
|
||||
const container = contentDocument.createElement('div');
|
||||
container.contentEditable = true;
|
||||
container.appendChild(instance);
|
||||
contentDocument.body.appendChild(container);
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
|
||||
container.focus();
|
||||
contentDocument.execCommand('delete', false, null);
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'execCommand on Document must enqueue a disconnected reaction when deleting a custom element from a contenteditable element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
|
||||
contentDocument.title = '';
|
||||
const title = contentDocument.querySelector('title');
|
||||
const instance = contentDocument.createElement('custom-element');
|
||||
title.appendChild(instance);
|
||||
instance.textContent = 'hello';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(title.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
title.text = 'world';
|
||||
assert_equals(title.innerHTML, 'world');
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'title on Document must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
|
||||
const body = contentDocument.body;
|
||||
body.innerHTML = '<custom-element>hello</custom-element>';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(body.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
contentDocument.body = contentDocument.createElement('body');
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'body on Document must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
|
||||
const instance = contentDocument.createElement('custom-element');
|
||||
const body = contentDocument.createElement('body');
|
||||
body.appendChild(instance);
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
assert_equals(body.innerHTML, '<custom-element></custom-element>');
|
||||
|
||||
contentDocument.body = body;
|
||||
assert_array_equals(element.takeLog().types(), ['connected']);
|
||||
}, 'body on Document must enqueue connectedCallback when inserting a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = '<custom-element></custom-element>';
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
|
||||
contentDocument.open();
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'open on Document must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = '<custom-element></custom-element>';
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
|
||||
contentDocument.write('');
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'write on Document must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentWindow.document.write('<custom-element></custom-element>');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
}, 'write on Document must enqueue connectedCallback after constructing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = '<custom-element></custom-element>';
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
|
||||
contentDocument.writeln('');
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'writeln on Document must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentWindow.document.writeln('<custom-element></custom-element>');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
}, 'writeln on Document must enqueue connectedCallback after constructing a custom element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,82 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on Element interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="id, className, slot, setAttribute, setAttributeNS, removeAttribute, removeAttributeNS, setAttributeNode, setAttributeNodeNS, removeAttributeNode, insertAdjacentElement, innerHTML, outerHTML, and insertAdjacentHTML of Element interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#element">
|
||||
<meta name="help" content="https://w3c.github.io/DOM-Parsing/">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
testReflectAttribute('id', 'id', 'foo', 'bar', 'id on Element');
|
||||
testReflectAttribute('className', 'class', 'foo', 'bar', 'className on Element');
|
||||
testReflectAttribute('slot', 'slot', 'foo', 'bar', 'slot on Element');
|
||||
|
||||
testAttributeAdder(function (element, name, value) {
|
||||
element.setAttribute(name, value);
|
||||
}, 'setAttribute on Element');
|
||||
|
||||
testAttributeAdder(function (element, name, value) {
|
||||
element.setAttributeNS(null, name, value);
|
||||
}, 'setAttributeNS on Element');
|
||||
|
||||
testAttributeRemover(function (element, name) {
|
||||
element.removeAttribute(name);
|
||||
}, 'removeAttribute on Element');
|
||||
|
||||
testAttributeRemover(function (element, name) {
|
||||
element.removeAttributeNS(null, name);
|
||||
}, 'removeAttributeNS on Element');
|
||||
|
||||
testAttributeAdder(function (element, name, value) {
|
||||
var attr = document.createAttribute(name);
|
||||
attr.value = value;
|
||||
element.setAttributeNode(attr);
|
||||
}, 'setAttributeNode on Element');
|
||||
|
||||
testAttributeAdder(function (element, name, value) {
|
||||
var attr = document.createAttribute(name);
|
||||
attr.value = value;
|
||||
element.setAttributeNodeNS(attr);
|
||||
}, 'setAttributeNodeNS on Element');
|
||||
|
||||
testAttributeRemover(function (element, name) {
|
||||
var attr = element.getAttributeNode(name);
|
||||
if (attr)
|
||||
element.removeAttributeNode(element.getAttributeNode(name));
|
||||
}, 'removeAttributeNode on Element');
|
||||
|
||||
testNodeConnector(function (newContainer, element) {
|
||||
newContainer.insertAdjacentElement('afterBegin', element);
|
||||
}, 'insertAdjacentElement on Element');
|
||||
|
||||
testInsertingMarkup(function (newContainer, markup) {
|
||||
newContainer.innerHTML = markup;
|
||||
}, 'innerHTML on Element');
|
||||
|
||||
testNodeDisconnector(function (customElement) {
|
||||
customElement.parentNode.innerHTML = '';
|
||||
}, 'innerHTML on Element');
|
||||
|
||||
testInsertingMarkup(function (newContainer, markup) {
|
||||
newContainer.firstChild.outerHTML = markup;
|
||||
}, 'outerHTML on Element');
|
||||
|
||||
testNodeDisconnector(function (customElement) {
|
||||
customElement.outerHTML = '';
|
||||
}, 'outerHTML on Element');
|
||||
|
||||
testInsertingMarkup(function (newContainer, markup) {
|
||||
newContainer.insertAdjacentHTML('afterBegin', markup);
|
||||
}, 'insertAdjacentHTML on Element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on ElementContentEditable interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="contentEditable of ElementContentEditable interface must have CEReactions">
|
||||
<meta name="help" content="https://html.spec.whatwg.org/#elementcontenteditable">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
testReflectAttribute('contentEditable', 'contenteditable', 'true', 'false', 'contentEditable on ElementContentEditable');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on HTMLAnchorElement interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="text of HTMLAnchorElement interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<a><custom-element>hello</custom-element></a>`;
|
||||
const anchor = contentDocument.querySelector('a');
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(anchor.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
anchor.text = 'world';
|
||||
assert_equals(anchor.innerHTML, 'world');
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'text on HTMLAnchorElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on HTMLElement interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="title, lang, translate, dir, hidden, tabIndex, accessKey, draggable, dropzone, contextMenu, spellcheck, innerText, and outerText of HTMLElement interface must have CEReactions">
|
||||
<meta name="help" content="https://html.spec.whatwg.org/#htmlelement">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
testReflectAttribute('title', 'title', 'foo', 'bar', 'title on HTMLElement');
|
||||
testReflectAttribute('lang', 'lang', 'en', 'zh', 'lang on HTMLElement');
|
||||
testReflectAttributeWithContentValues('translate', 'translate', true, 'yes', false, 'no', 'translate on HTMLElement');
|
||||
testReflectAttribute('dir', 'dir', 'ltr', 'rtl', 'dir on HTMLElement');
|
||||
testReflectBooleanAttribute('hidden', 'hidden', 'hidden on HTMLElement');
|
||||
testReflectAttribute('tabIndex', 'tabindex', '0', '1', 'tabIndex on HTMLElement');
|
||||
testReflectAttribute('accessKey', 'accesskey', 'a', 'b', 'accessKey on HTMLElement');
|
||||
testReflectAttributeWithContentValues('draggable', 'draggable', true, 'true', false, 'false', 'draggable on HTMLElement');
|
||||
testReflectAttribute('dropzone', 'dropzone', 'copy', 'move', 'dropzone on HTMLElement');
|
||||
testReflectAttribute('contextMenu', 'contextmenu', 'menu1', 'menu2', 'contextMenu on HTMLElement');
|
||||
testReflectAttributeWithContentValues('spellcheck', 'spellcheck', true, 'true', false, 'false', 'spellcheck on HTMLElement');
|
||||
|
||||
testNodeDisconnector(function (customElement) {
|
||||
customElement.parentNode.innerText = '';
|
||||
}, 'innerText on HTMLElement');
|
||||
|
||||
if ('outerText' in HTMLElement.prototype) {
|
||||
// Not yet to be in the standard but all but Gecko supports this property: https://github.com/whatwg/html/issues/668
|
||||
testNodeDisconnector(function (customElement) {
|
||||
customElement.outerText = '';
|
||||
}, 'outerText on HTMLElement');
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on HTMLOptionElement interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="text of HTMLOptionElement interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<select><option></option></select>`;
|
||||
const option = contentDocument.querySelector('option');
|
||||
const instance = document.createElement('custom-element');
|
||||
option.appendChild(instance);
|
||||
instance.textContent = 'hello';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(option.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
option.text = 'world';
|
||||
assert_equals(option.innerHTML, 'world');
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'text on HTMLOptionElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,122 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on HTMLOptionsCollection interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="length, the indexed setter, add, and remove of HTMLOptionsCollection interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<select><option></option></select>`;
|
||||
const option = contentDocument.querySelector('option');
|
||||
|
||||
const instance = contentDocument.createElement(element.name);
|
||||
option.appendChild(instance);
|
||||
instance.textContent = 'hello';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(option.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
const select = contentDocument.querySelector('select');
|
||||
assert_equals(select.options[0], option);
|
||||
select.options.length = 0;
|
||||
assert_equals(select.firstChild, null);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'length on HTMLOptionsCollection must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.body.innerHTML = `<select></select>`;
|
||||
const select = contentDocument.querySelector('select');
|
||||
|
||||
const option = contentDocument.createElement('option');
|
||||
const instance = contentDocument.createElement(element.name);
|
||||
option.appendChild(instance);
|
||||
instance.textContent = 'hello';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
assert_equals(option.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
assert_equals(select.options.length, 0);
|
||||
select.options[0] = option;
|
||||
assert_equals(select.options.length, 1);
|
||||
assert_array_equals(element.takeLog().types(), ['connected']);
|
||||
}, 'The indexed setter on HTMLOptionsCollection must enqueue connectedCallback when inserting a custom element');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.body.innerHTML = `<select><option></option></select>`;
|
||||
const option = contentDocument.querySelector('option');
|
||||
|
||||
const instance = contentDocument.createElement(element.name);
|
||||
option.appendChild(instance);
|
||||
instance.textContent = 'hello';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(option.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
const select = contentDocument.querySelector('select');
|
||||
assert_equals(select.options[0], option);
|
||||
select.options[0] = null;
|
||||
assert_equals(select.options.length, 0);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'The indexed setter on HTMLOptionsCollection must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.body.innerHTML = `<select></select>`;
|
||||
const select = contentDocument.querySelector('select');
|
||||
|
||||
const option = contentDocument.createElement('option');
|
||||
const instance = contentDocument.createElement(element.name);
|
||||
option.appendChild(instance);
|
||||
instance.textContent = 'hello';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
assert_equals(option.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
assert_equals(select.options.length, 0);
|
||||
select.options.add(option);
|
||||
assert_equals(select.options.length, 1);
|
||||
assert_array_equals(element.takeLog().types(), ['connected']);
|
||||
}, 'add on HTMLOptionsCollection must enqueue connectedCallback when inserting a custom element');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.body.innerHTML = `<select><option></option></select>`;
|
||||
const option = contentDocument.querySelector('option');
|
||||
|
||||
const instance = contentDocument.createElement(element.name);
|
||||
option.appendChild(instance);
|
||||
instance.textContent = 'hello';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(option.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
const select = contentDocument.querySelector('select');
|
||||
assert_equals(select.options[0], option);
|
||||
select.options.remove(0);
|
||||
assert_equals(select.options.length, 0);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'remove on HTMLOptionsCollection must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on HTMLOutputElement interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="value and defaultValue of HTMLOutputElement interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<output><custom-element>hello</custom-element></output>`;
|
||||
const anchor = contentDocument.querySelector('output');
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(anchor.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
anchor.value = 'world';
|
||||
assert_equals(anchor.innerHTML, 'world');
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'value on HTMLOutputElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<output><custom-element>hello</custom-element></output>`;
|
||||
const anchor = contentDocument.querySelector('output');
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(anchor.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
anchor.defaultValue = 'world';
|
||||
assert_equals(anchor.innerHTML, 'world');
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'defaultValue on HTMLOutputElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,122 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on HTMLSelectElement interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="length, add, remove, and the setter of HTMLSelectElement interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<select><option></option></select>`;
|
||||
const option = contentDocument.querySelector('option');
|
||||
|
||||
const instance = contentDocument.createElement(element.name);
|
||||
option.appendChild(instance);
|
||||
instance.textContent = 'hello';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(option.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
const select = contentDocument.querySelector('select');
|
||||
assert_equals(select.length, 1);
|
||||
select.length = 0;
|
||||
assert_equals(select.firstChild, null);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'length on HTMLSelectElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.body.innerHTML = `<select></select>`;
|
||||
const select = contentDocument.querySelector('select');
|
||||
|
||||
const option = contentDocument.createElement('option');
|
||||
const instance = contentDocument.createElement(element.name);
|
||||
option.appendChild(instance);
|
||||
instance.textContent = 'hello';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
assert_equals(option.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
assert_equals(select.options.length, 0);
|
||||
select[0] = option;
|
||||
assert_equals(select.options.length, 1);
|
||||
assert_array_equals(element.takeLog().types(), ['connected']);
|
||||
}, 'The indexed setter on HTMLSelectElement must enqueue connectedCallback when inserting a custom element');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.body.innerHTML = `<select><option></option></select>`;
|
||||
const option = contentDocument.querySelector('option');
|
||||
|
||||
const instance = contentDocument.createElement(element.name);
|
||||
option.appendChild(instance);
|
||||
instance.textContent = 'hello';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(option.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
const select = contentDocument.querySelector('select');
|
||||
assert_equals(select.options[0], option);
|
||||
select[0] = null;
|
||||
assert_equals(select.options.length, 0);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'The indexed setter on HTMLSelectElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.body.innerHTML = `<select></select>`;
|
||||
const select = contentDocument.querySelector('select');
|
||||
|
||||
const option = contentDocument.createElement('option');
|
||||
const instance = contentDocument.createElement(element.name);
|
||||
option.appendChild(instance);
|
||||
instance.textContent = 'hello';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
assert_equals(option.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
assert_equals(select.options.length, 0);
|
||||
select.add(option);
|
||||
assert_equals(select.options.length, 1);
|
||||
assert_array_equals(element.takeLog().types(), ['connected']);
|
||||
}, 'add on HTMLSelectElement must enqueue connectedCallback when inserting a custom element');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.body.innerHTML = `<select><option></option></select>`;
|
||||
const option = contentDocument.querySelector('option');
|
||||
|
||||
const instance = contentDocument.createElement(element.name);
|
||||
option.appendChild(instance);
|
||||
instance.textContent = 'hello';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(option.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
const select = contentDocument.querySelector('select');
|
||||
assert_equals(select.options[0], option);
|
||||
select.remove(0);
|
||||
assert_equals(select.options.length, 0);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'remove on HTMLSelectElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,173 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on HTMLTableElement interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="caption, deleteCaption, thead, deleteTHead, tFoot, deleteTFoot, and deleteRow of HTMLTableElement interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table></table>`;
|
||||
const table = contentDocument.querySelector('table');
|
||||
|
||||
const caption = contentDocument.createElement('caption');
|
||||
caption.innerHTML = '<custom-element>hello</custom-element>';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
assert_equals(caption.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
assert_equals(table.caption, null);
|
||||
table.caption = caption;
|
||||
assert_array_equals(element.takeLog().types(), ['connected']);
|
||||
}, 'caption on HTMLTableElement must enqueue connectedCallback when inserting a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table><caption><custom-element>hello</custom-element></caption></table>`;
|
||||
const caption = contentDocument.querySelector('caption');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(caption.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
const table = contentDocument.querySelector('table');
|
||||
assert_equals(table.caption, caption);
|
||||
const newCaption = contentDocument.createElement('caption');
|
||||
table.caption = newCaption; // Chrome doesn't support setting to null.
|
||||
assert_equals(table.caption, newCaption);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'caption on HTMLTableElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table><caption><custom-element>hello</custom-element></caption></table>`;
|
||||
const caption = contentDocument.querySelector('caption');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(caption.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
const table = contentDocument.querySelector('table');
|
||||
assert_equals(table.caption, caption);
|
||||
const newCaption = contentDocument.createElement('caption');
|
||||
table.deleteCaption();
|
||||
assert_equals(table.caption, null);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'deleteCaption() on HTMLTableElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table></table>`;
|
||||
const table = contentDocument.querySelector('table');
|
||||
|
||||
const thead = contentDocument.createElement('thead');
|
||||
thead.innerHTML = '<tr><td><custom-element>hello</custom-element></td></tr>';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
assert_equals(thead.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>');
|
||||
|
||||
assert_equals(table.tHead, null);
|
||||
table.tHead = thead;
|
||||
assert_array_equals(element.takeLog().types(), ['connected']);
|
||||
}, 'tHead on HTMLTableElement must enqueue connectedCallback when inserting a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table><thead><tr><td><custom-element>hello</custom-element></td></tr></thead></table>`;
|
||||
const thead = contentDocument.querySelector('thead');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(thead.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>');
|
||||
|
||||
const table = contentDocument.querySelector('table');
|
||||
assert_equals(table.tHead, thead);
|
||||
const newThead = contentDocument.createElement('thead');
|
||||
table.tHead = newThead; // Chrome doesn't support setting to null.
|
||||
assert_equals(table.tHead, newThead);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'tHead on HTMLTableElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table><thead><tr><td><custom-element>hello</custom-element></td></tr></thead></table>`;
|
||||
const thead = contentDocument.querySelector('thead');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(thead.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>');
|
||||
|
||||
const table = contentDocument.querySelector('table');
|
||||
assert_equals(table.tHead, thead);
|
||||
table.deleteTHead();
|
||||
assert_equals(table.tHead, null);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'deleteTHead() on HTMLTableElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table></table>`;
|
||||
const table = contentDocument.querySelector('table');
|
||||
|
||||
const tfoot = contentDocument.createElement('tfoot');
|
||||
tfoot.innerHTML = '<tr><td><custom-element>hello</custom-element></td></tr>';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
assert_equals(tfoot.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>');
|
||||
|
||||
assert_equals(table.tFoot, null);
|
||||
table.tFoot = tfoot;
|
||||
assert_array_equals(element.takeLog().types(), ['connected']);
|
||||
}, 'tFoot on HTMLTableElement must enqueue connectedCallback when inserting a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table><tfoot><tr><td><custom-element>hello</custom-element></td></tr></tfoot></table>`;
|
||||
const tfoot = contentDocument.querySelector('tfoot');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(tfoot.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>');
|
||||
|
||||
const table = contentDocument.querySelector('table');
|
||||
assert_equals(table.tFoot, tfoot);
|
||||
const newThead = contentDocument.createElement('tfoot');
|
||||
table.tFoot = newThead; // Chrome doesn't support setting to null.
|
||||
assert_equals(table.tFoot, newThead);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'tFoot on HTMLTableElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table><tfoot><tr><td><custom-element>hello</custom-element></td></tr></tfoot></table>`;
|
||||
const tfoot = contentDocument.querySelector('tfoot');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(tfoot.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>');
|
||||
|
||||
const table = contentDocument.querySelector('table');
|
||||
assert_equals(table.tFoot, tfoot);
|
||||
table.deleteTFoot();
|
||||
assert_equals(table.tFoot, null);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'deleteTFoot() on HTMLTableElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table><tr><td><custom-element>hello</custom-element></td></tr></table>`;
|
||||
const tr = contentDocument.querySelector('tr');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(tr.innerHTML, '<td><custom-element>hello</custom-element></td>');
|
||||
|
||||
const table = contentDocument.querySelector('table');
|
||||
assert_equals(table.rows.length, 1);
|
||||
assert_equals(table.rows[0], tr);
|
||||
table.deleteRow(0);
|
||||
assert_equals(table.rows.length, 0);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'deleteRow() on HTMLTableElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on HTMLTableRowElement interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="deleteCell of HTMLTableRowElement interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table><tr><td><custom-element>hello</custom-element></td></tr></table>`;
|
||||
const td = contentDocument.querySelector('td');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(td.innerHTML, '<custom-element>hello</custom-element>');
|
||||
|
||||
const table = contentDocument.querySelector('table');
|
||||
const row = table.rows[0];
|
||||
assert_equals(row.cells[0], td);
|
||||
row.deleteCell(0);
|
||||
assert_equals(row.cells.length, 0);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'deleteCell() on HTMLTableRowElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on HTMLTableSectionElement interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="deleteRow of HTMLTableSectionElement interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table><thead><tr><td><custom-element>hello</custom-element></td></tr></thead></table>`;
|
||||
const thead = contentDocument.querySelector('thead');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(thead.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>');
|
||||
|
||||
const table = contentDocument.querySelector('table');
|
||||
assert_equals(table.tHead, thead);
|
||||
table.tHead.deleteRow(0);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'deleteRow() on HTMLTableSectionElement on thead must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
contentDocument.body.innerHTML = `<table><tfoot><tr><td><custom-element>hello</custom-element></td></tr></tfoot></table>`;
|
||||
const tfoot = contentDocument.querySelector('tfoot');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
assert_equals(tfoot.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>');
|
||||
|
||||
const table = contentDocument.querySelector('table');
|
||||
assert_equals(table.tFoot, tfoot);
|
||||
table.tFoot.deleteRow(0);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'deleteRow() on HTMLTableSectionElement on tfoot must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on HTMLTitleElement interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="text of HTMLTitleElement interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
const instance = contentWindow.document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
|
||||
contentWindow.document.title = 'hello';
|
||||
const titleElement = contentDocument.querySelector('title');
|
||||
titleElement.appendChild(instance);
|
||||
assert_array_equals(element.takeLog().types(), ['connected']);
|
||||
assert_equals(titleElement.childNodes.length, 2);
|
||||
|
||||
titleElement.text = 'world';
|
||||
assert_equals(titleElement.childNodes.length, 1);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, 'text on HTMLTitleElement must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,39 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on NamedNodeMap interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="setNamedItem, setNamedItemNS, removeNameditem, and removeNamedItemNS of NamedNodeMap interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
testAttributeAdder(function (element, name, value) {
|
||||
var attr = element.ownerDocument.createAttribute(name);
|
||||
attr.value = value;
|
||||
element.attributes.setNamedItem(attr);
|
||||
}, 'setNamedItem on NamedNodeMap');
|
||||
|
||||
testAttributeAdder(function (element, name, value) {
|
||||
var attr = element.ownerDocument.createAttribute(name);
|
||||
attr.value = value;
|
||||
element.attributes.setNamedItemNS(attr);
|
||||
}, 'setNamedItemNS on NamedNodeMap');
|
||||
|
||||
testAttributeRemover(function (element, name) {
|
||||
element.attributes.removeNamedItem(name);
|
||||
}, 'removeNamedItem on NamedNodeMap', {onlyExistingAttribute: true});
|
||||
|
||||
testAttributeRemover(function (element, name) {
|
||||
element.attributes.removeNamedItemNS(null, name);
|
||||
}, 'removeNamedItemNS on NamedNodeMap', {onlyExistingAttribute: true});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on Node interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="nodeValue, textContent, normalize, cloneNode, insertBefore, appendChild, replaceChild, and removeChild of Node interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
testAttributeMutator(function (element, name, value) {
|
||||
element.getAttributeNode(name).nodeValue = value;
|
||||
}, 'nodeValue on Node');
|
||||
|
||||
testAttributeMutator(function (element, name, value) {
|
||||
element.getAttributeNode(name).textContent = value;
|
||||
}, 'textContent on Node');
|
||||
|
||||
// FIXME: Add a test for normalize()
|
||||
|
||||
testCloner(function (customElement) {
|
||||
return customElement.cloneNode(false);
|
||||
}, 'cloneNode on Node');
|
||||
|
||||
testNodeConnector(function (newContainer, customElement) {
|
||||
newContainer.insertBefore(customElement, newContainer.firstChild);
|
||||
}, 'insertBefore on ChildNode');
|
||||
|
||||
testNodeConnector(function (newContainer, customElement) {
|
||||
newContainer.appendChild(customElement);
|
||||
}, 'appendChild on ChildNode');
|
||||
|
||||
testNodeConnector(function (newContainer, customElement) {
|
||||
newContainer.replaceChild(customElement, newContainer.firstChild);
|
||||
}, 'replaceChild on ChildNode');
|
||||
|
||||
testNodeDisconnector(function (customElement) {
|
||||
customElement.parentNode.removeChild(customElement);
|
||||
}, 'removeChild on ChildNode');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on ParentNode interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="prepend and append of ParentNode interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#parentnode">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
testNodeConnector(function (newContainer, customElement) {
|
||||
newContainer.prepend(customElement);
|
||||
}, 'prepend on ParentNode');
|
||||
|
||||
testNodeConnector(function (newContainer, customElement) {
|
||||
newContainer.append(customElement);
|
||||
}, 'append on ParentNode');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on Range interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="deleteContents, extractContents, cloneContents, insertNode, and surroundContents of Range interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
testNodeDisconnector(function (customElement) {
|
||||
var range = document.createRange();
|
||||
range.selectNode(customElement);
|
||||
range.deleteContents();
|
||||
}, 'deleteContents on Range');
|
||||
|
||||
testNodeDisconnector(function (customElement) {
|
||||
var range = document.createRange();
|
||||
range.selectNode(customElement);
|
||||
range.extractContents();
|
||||
}, 'extractContents on Range');
|
||||
|
||||
testCloner(function (customElement) {
|
||||
var range = document.createRange();
|
||||
range.selectNode(customElement);
|
||||
range.cloneContents();
|
||||
}, 'cloneContents on Range')
|
||||
|
||||
testNodeConnector(function (container, customElement) {
|
||||
var range = document.createRange();
|
||||
range.selectNodeContents(container);
|
||||
range.insertNode(customElement);
|
||||
}, 'insertNode on Range');
|
||||
|
||||
testNodeConnector(function (container, customElement) {
|
||||
var range = document.createRange();
|
||||
range.selectNodeContents(container);
|
||||
range.surroundContents(customElement);
|
||||
}, 'surroundContents on Range');
|
||||
|
||||
testParsingMarkup(function (document, markup) {
|
||||
var range = document.createRange();
|
||||
return range.createContextualFragment(markup);
|
||||
}, 'createContextualFragment on Range');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on Selection interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="deleteFromDocument of Selection interface must have CEReactions">
|
||||
<meta name="help" content="http://w3c.github.io/selection-api/#selection-interface">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
testNodeDisconnector(function (customElement, window) {
|
||||
let selection = window.getSelection();
|
||||
let parent = customElement.parentNode;
|
||||
|
||||
// WebKit and Blink "normalizes" selection in selectAllChildren and not select the empty customElement.
|
||||
// Workaround this orthogonal non-standard behavior by inserting text nodes around the custom element.
|
||||
parent.prepend(document.createTextNode('start'));
|
||||
parent.append(document.createTextNode('end'));
|
||||
|
||||
selection.selectAllChildren(parent);
|
||||
selection.deleteFromDocument();
|
||||
}, 'deleteFromDocument on Selection');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: CEReactions on ShadowRoot interface</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="innerHTML of ShadowRoot interface must have CEReactions">
|
||||
<meta name="help" content="https://dom.spec.whatwg.org/#node">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../resources/custom-elements-helpers.js"></script>
|
||||
<script src="./resources/reactions.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
const host = contentDocument.createElement('div');
|
||||
const shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
shadowRoot.innerHTML = '<custom-element></custom-element>';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
}, 'innerHTML on ShadowRoot must upgrade a custom element');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
const host = contentDocument.createElement('div');
|
||||
contentDocument.body.appendChild(host);
|
||||
const shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
shadowRoot.innerHTML = '<custom-element></custom-element>';
|
||||
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
}, 'innerHTML on ShadowRoot must enqueue connectedCallback on newly upgraded custom elements when the shadow root is connected');
|
||||
|
||||
test_with_window(function (contentWindow, contentDocument) {
|
||||
const element = define_custom_element_in_window(contentWindow, 'custom-element', []);
|
||||
const host = contentDocument.createElement('div');
|
||||
contentDocument.body.appendChild(host);
|
||||
|
||||
const shadowRoot = host.attachShadow({mode: 'closed'});
|
||||
shadowRoot.innerHTML = '<custom-element></custom-element>';
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
|
||||
shadowRoot.innerHTML = '';
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
|
||||
}, 'innerHTML on ShadowRoot must enqueue disconnectedCallback when removing a custom element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,361 @@
|
|||
|
||||
let testNumber = 1;
|
||||
|
||||
function testNodeConnector(testFunction, name) {
|
||||
let container = document.createElement('div');
|
||||
container.appendChild(document.createElement('div'));
|
||||
document.body.appendChild(container);
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element();
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(container, instance);
|
||||
assert_array_equals(element.takeLog().types(), ['connected']);
|
||||
}, name + ' must enqueue a connected reaction');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element();
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
var newDoc = document.implementation.createHTMLDocument();
|
||||
testFunction(container, instance);
|
||||
assert_array_equals(element.takeLog().types(), ['connected']);
|
||||
testFunction(newDoc.documentElement, instance);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected', 'adopted', 'connected']);
|
||||
}, name + ' must enqueue a disconnected reaction, an adopted reaction, and a connected reaction when the custom element was in another document');
|
||||
|
||||
container.parentNode.removeChild(container);
|
||||
}
|
||||
|
||||
function testNodeDisconnector(testFunction, name) {
|
||||
let container = document.createElement('div');
|
||||
container.appendChild(document.createElement('div'));
|
||||
document.body.appendChild(container);
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element();
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
container.appendChild(instance);
|
||||
assert_array_equals(element.takeLog().types(), ['connected']);
|
||||
testFunction(instance, window);
|
||||
assert_array_equals(element.takeLog().types(), ['disconnected']);
|
||||
}, name + ' must enqueue a disconnected reaction');
|
||||
|
||||
container.parentNode.removeChild(container);
|
||||
}
|
||||
|
||||
function testInsertingMarkup(testFunction, name) {
|
||||
let container = document.createElement('div');
|
||||
container.appendChild(document.createElement('div'));
|
||||
document.body.appendChild(container);
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element();
|
||||
testFunction(container, `<${element.name}></${element.name}>`);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
}, name + ' must enqueue a connected reaction for a newly constructed custom element');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['title']);
|
||||
testFunction(container, `<${element.name} id="hello" title="hi"></${element.name}>`);
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['constructed', 'attributeChanged', 'connected']);
|
||||
assert_attribute_log_entry(logEntries[1], {name: 'title', oldValue: null, newValue: 'hi', namespace: null});
|
||||
}, name + ' must enqueue a attributeChanged reaction for a newly constructed custom element');
|
||||
|
||||
container.parentNode.removeChild(container);
|
||||
}
|
||||
|
||||
function testParsingMarkup(testFunction, name) {
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['id']);
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
var instance = testFunction(document, `<${element.name} id="hello" class="foo"></${element.name}>`);
|
||||
assert_equals(Object.getPrototypeOf(instance.querySelector(element.name)), element.class.prototype);
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['constructed', 'attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries[1], {name: 'id', oldValue: null, newValue: 'hello', namespace: null});
|
||||
}, name + ' must construct a custom element');
|
||||
}
|
||||
|
||||
function testCloner(testFunction, name) {
|
||||
let container = document.createElement('div');
|
||||
container.appendChild(document.createElement('div'));
|
||||
document.body.appendChild(container);
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['id']);
|
||||
var instance = document.createElement(element.name);
|
||||
container.appendChild(instance);
|
||||
|
||||
instance.setAttribute('id', 'foo');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected', 'attributeChanged']);
|
||||
var newInstance = testFunction(instance);
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['constructed', 'attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'id', oldValue: null, newValue: 'foo', namespace: null});
|
||||
}, name + ' must enqueue an attributeChanged reaction when cloning an element with an observed attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['id']);
|
||||
var instance = document.createElement(element.name);
|
||||
container.appendChild(instance);
|
||||
|
||||
instance.setAttribute('lang', 'en');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
|
||||
var newInstance = testFunction(instance);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
}, name + ' must not enqueue an attributeChanged reaction when cloning an element with an unobserved attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['title', 'class']);
|
||||
var instance = document.createElement(element.name);
|
||||
container.appendChild(instance);
|
||||
|
||||
instance.setAttribute('lang', 'en');
|
||||
instance.className = 'foo';
|
||||
instance.setAttribute('title', 'hello world');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'connected', 'attributeChanged', 'attributeChanged']);
|
||||
var newInstance = testFunction(instance);
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['constructed', 'attributeChanged', 'attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries[1], {name: 'class', oldValue: null, newValue: 'foo', namespace: null});
|
||||
assert_attribute_log_entry(logEntries[2], {name: 'title', oldValue: null, newValue: 'hello world', namespace: null});
|
||||
}, name + ' must enqueue an attributeChanged reaction when cloning an element only for observed attributes');
|
||||
}
|
||||
|
||||
function testReflectAttributeWithContentValues(jsAttributeName, contentAttributeName, validValue1, contentValue1, validValue2, contentValue2, name) {
|
||||
test(function () {
|
||||
var element = define_new_custom_element([contentAttributeName]);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
instance[jsAttributeName] = validValue1;
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
|
||||
assert_attribute_log_entry(logEntries.last(), {name: contentAttributeName, oldValue: null, newValue: contentValue1, namespace: null});
|
||||
}, name + ' must enqueue an attributeChanged reaction when adding ' + contentAttributeName + ' content attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element([contentAttributeName]);
|
||||
var instance = document.createElement(element.name);
|
||||
instance[jsAttributeName] = validValue1;
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
instance[jsAttributeName] = validValue2;
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: contentAttributeName, oldValue: contentValue1, newValue: contentValue2, namespace: null});
|
||||
}, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute');
|
||||
}
|
||||
|
||||
function testReflectAttribute(jsAttributeName, contentAttributeName, validValue1, validValue2, name) {
|
||||
testReflectAttributeWithContentValues(jsAttributeName, contentAttributeName, validValue1, validValue1, validValue2, validValue2, name);
|
||||
}
|
||||
|
||||
function testReflectBooleanAttribute(jsAttributeName, contentAttributeName, name) {
|
||||
testReflectAttributeWithContentValues(jsAttributeName, contentAttributeName, true, '', false, null, name);
|
||||
}
|
||||
|
||||
function testAttributeAdder(testFunction, name) {
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['id']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(instance, 'id', 'foo');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'id', oldValue: null, newValue: 'foo', namespace: null});
|
||||
}, name + ' must enqueue an attributeChanged reaction when adding an attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(instance, 'data-lang', 'en');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, name + ' must not enqueue an attributeChanged reaction when adding an unobserved attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['title']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('title', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
testFunction(instance, 'title', 'world');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'hello', newValue: 'world', namespace: null});
|
||||
}, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element([]);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('data-lang', 'zh');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(instance, 'data-lang', 'en');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, name + ' must enqueue an attributeChanged reaction when replacing an existing unobserved attribute');
|
||||
}
|
||||
|
||||
function testAttributeMutator(testFunction, name) {
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['title']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('title', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
testFunction(instance, 'title', 'world');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'hello', newValue: 'world', namespace: null});
|
||||
}, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('data-lang', 'zh');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(instance, 'data-lang', 'en');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, name + ' must not enqueue an attributeChanged reaction when replacing an existing unobserved attribute');
|
||||
}
|
||||
|
||||
function testAttributeRemover(testFunction, name, options) {
|
||||
if (options && !options.onlyExistingAttribute) {
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['title']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(instance, 'title');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, name + ' must not enqueue an attributeChanged reaction when removing an attribute that does not exist');
|
||||
}
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element([]);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('data-lang', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(instance, 'data-lang');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, name + ' must not enqueue an attributeChanged reaction when removing an unobserved attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['title']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('title', 'hello');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
testFunction(instance, 'title');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'hello', newValue: null, namespace: null});
|
||||
}, name + ' must enqueue an attributeChanged reaction when removing an existing attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element([]);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('data-lang', 'ja');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(instance, 'data-lang');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, name + ' must not enqueue an attributeChanged reaction when removing an existing unobserved attribute');
|
||||
}
|
||||
|
||||
function test_mutating_style_property_value(testFunction, name, options) {
|
||||
const propertyName = (options || {}).propertyName || 'color';
|
||||
const idlName = (options || {}).idlName || 'color';
|
||||
const value1 = (options || {}).value1 || 'blue';
|
||||
const rule1 = `${propertyName}: ${value1};`;
|
||||
const value2 = (options || {}).value2 || 'red';
|
||||
const rule2 = `${propertyName}: ${value2};`;
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['style']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(instance, propertyName, idlName, value1);
|
||||
assert_equals(instance.getAttribute('style'), rule1);
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'style', oldValue: null, newValue: rule1, namespace: null});
|
||||
}, name + ' must enqueue an attributeChanged reaction when it adds the observed style attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['title']);
|
||||
var instance = document.createElement(element.name);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(instance, propertyName, idlName, value1);
|
||||
assert_equals(instance.getAttribute('style'), rule1);
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, name + ' must not enqueue an attributeChanged reaction when it adds the style attribute but the style attribute is not observed');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['style']);
|
||||
var instance = document.createElement(element.name);
|
||||
testFunction(instance, propertyName, idlName, value1);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
testFunction(instance, propertyName, idlName, value2);
|
||||
assert_equals(instance.getAttribute('style'), rule2);
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'style', oldValue: rule1, newValue: rule2, namespace: null});
|
||||
}, name + ' must enqueue an attributeChanged reaction when it mutates the observed style attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element([]);
|
||||
var instance = document.createElement(element.name);
|
||||
testFunction(instance, propertyName, idlName, value1);
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(instance, propertyName, idlName, value2);
|
||||
assert_equals(instance.getAttribute('style'), rule2);
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, name + ' must not enqueue an attributeChanged reaction when it mutates the style attribute but the style attribute is not observed');
|
||||
}
|
||||
|
||||
function test_removing_style_property_value(testFunction, name) {
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['style']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('style', 'color: red; display: none;');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
testFunction(instance, 'color', 'color');
|
||||
assert_equals(instance.getAttribute('style'), 'display: none;'); // Don't make this empty since browser behaviors are inconsistent now.
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'style', oldValue: 'color: red; display: none;', newValue: 'display: none;', namespace: null});
|
||||
}, name + ' must enqueue an attributeChanged reaction when it removes a property from the observed style attribute');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['class']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('style', 'color: red; display: none;');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(instance, 'color', 'color');
|
||||
assert_equals(instance.getAttribute('style'), 'display: none;'); // Don't make this empty since browser behaviors are inconsistent now.
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, name + ' must not enqueue an attributeChanged reaction when it removes a property from the style attribute but the style attribute is not observed');
|
||||
}
|
||||
|
||||
function test_mutating_style_property_priority(testFunction, name) {
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['style']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('style', 'color: red');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']);
|
||||
testFunction(instance, 'color', 'color', true);
|
||||
assert_equals(instance.getAttribute('style'), 'color: red !important;');
|
||||
var logEntries = element.takeLog();
|
||||
assert_array_equals(logEntries.types(), ['attributeChanged']);
|
||||
assert_attribute_log_entry(logEntries.last(), {name: 'style', oldValue: 'color: red', newValue: 'color: red !important;', namespace: null});
|
||||
}, name + ' must enqueue an attributeChanged reaction when it makes a property important and the style attribute is observed');
|
||||
|
||||
test(function () {
|
||||
var element = define_new_custom_element(['id']);
|
||||
var instance = document.createElement(element.name);
|
||||
instance.setAttribute('style', 'color: red');
|
||||
assert_array_equals(element.takeLog().types(), ['constructed']);
|
||||
testFunction(instance, 'color', 'color', true);
|
||||
assert_equals(instance.getAttribute('style'), 'color: red !important;');
|
||||
assert_array_equals(element.takeLog().types(), []);
|
||||
}, name + ' must enqueue an attributeChanged reaction when it makes a property important but the style attribute is not observed');
|
||||
}
|
|
@ -16,7 +16,215 @@ function test_with_window(f, name, srcdoc) {
|
|||
promise_test((t) => {
|
||||
return create_window_in_test(t, srcdoc)
|
||||
.then((w) => {
|
||||
f(w);
|
||||
f(w, w.document);
|
||||
});
|
||||
}, name);
|
||||
}
|
||||
|
||||
function define_custom_element_in_window(window, name, observedAttributes) {
|
||||
let log = [];
|
||||
|
||||
class CustomElement extends window.HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
log.push(create_constructor_log(this));
|
||||
}
|
||||
attributeChangedCallback(...args) {
|
||||
log.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
connectedCallback() { log.push(create_connected_callback_log(this)); }
|
||||
disconnectedCallback() { log.push(create_disconnected_callback_log(this)); }
|
||||
adoptedCallback(oldDocument, newDocument) { log.push({type: 'adopted', element: this, oldDocument: oldDocument, newDocument: newDocument}); }
|
||||
}
|
||||
CustomElement.observedAttributes = observedAttributes;
|
||||
|
||||
window.customElements.define(name, CustomElement);
|
||||
|
||||
return {
|
||||
name: name,
|
||||
class: CustomElement,
|
||||
takeLog: function () {
|
||||
let currentLog = log; log = [];
|
||||
currentLog.types = () => currentLog.map((entry) => entry.type);
|
||||
currentLog.last = () => currentLog[currentLog.length - 1];
|
||||
return currentLog;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function create_constructor_log(element) {
|
||||
return {type: 'constructed', element: element};
|
||||
}
|
||||
|
||||
function assert_constructor_log_entry(log, element) {
|
||||
assert_equals(log.type, 'constructed');
|
||||
assert_equals(log.element, element);
|
||||
}
|
||||
|
||||
function create_connected_callback_log(element) {
|
||||
return {type: 'connected', element: element};
|
||||
}
|
||||
|
||||
function assert_connected_log_entry(log, element) {
|
||||
assert_equals(log.type, 'connected');
|
||||
assert_equals(log.element, element);
|
||||
}
|
||||
|
||||
function create_disconnected_callback_log(element) {
|
||||
return {type: 'disconnected', element: element};
|
||||
}
|
||||
|
||||
function assert_disconnected_log_entry(log, element) {
|
||||
assert_equals(log.type, 'disconnected');
|
||||
assert_equals(log.element, element);
|
||||
}
|
||||
|
||||
function assert_adopted_log_entry(log, element) {
|
||||
assert_equals(log.type, 'adopted');
|
||||
assert_equals(log.element, element);
|
||||
}
|
||||
|
||||
function create_adopted_callback_log(element) {
|
||||
return {type: 'adopted', element: element};
|
||||
}
|
||||
|
||||
function create_attribute_changed_callback_log(element, name, oldValue, newValue, namespace) {
|
||||
return {
|
||||
type: 'attributeChanged',
|
||||
element: element,
|
||||
name: name,
|
||||
namespace: namespace,
|
||||
oldValue: oldValue,
|
||||
newValue: newValue,
|
||||
actualValue: element.getAttributeNS(namespace, name)
|
||||
};
|
||||
}
|
||||
|
||||
function assert_attribute_log_entry(log, expected) {
|
||||
assert_equals(log.type, 'attributeChanged');
|
||||
assert_equals(log.name, expected.name);
|
||||
assert_equals(log.oldValue, expected.oldValue);
|
||||
assert_equals(log.newValue, expected.newValue);
|
||||
assert_equals(log.actualValue, expected.newValue);
|
||||
assert_equals(log.namespace, expected.namespace);
|
||||
}
|
||||
|
||||
|
||||
function define_new_custom_element(observedAttributes) {
|
||||
let log = [];
|
||||
let name = 'custom-element-' + define_new_custom_element._element_number++;
|
||||
|
||||
class CustomElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
log.push({type: 'constructed', element: this});
|
||||
}
|
||||
attributeChangedCallback(...args) {
|
||||
log.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
connectedCallback() { log.push({type: 'connected', element: this}); }
|
||||
disconnectedCallback() { log.push({type: 'disconnected', element: this}); }
|
||||
adoptedCallback(oldDocument, newDocument) { log.push({type: 'adopted', element: this, oldDocument: oldDocument, newDocument: newDocument}); }
|
||||
}
|
||||
CustomElement.observedAttributes = observedAttributes;
|
||||
|
||||
customElements.define(name, CustomElement);
|
||||
|
||||
return {
|
||||
name: name,
|
||||
class: CustomElement,
|
||||
takeLog: function () {
|
||||
let currentLog = log; log = [];
|
||||
currentLog.types = () => currentLog.map((entry) => entry.type);
|
||||
currentLog.last = () => currentLog[currentLog.length - 1];
|
||||
return currentLog;
|
||||
}
|
||||
};
|
||||
}
|
||||
define_new_custom_element._element_number = 1;
|
||||
|
||||
function document_types() {
|
||||
return [
|
||||
{
|
||||
name: 'the document',
|
||||
create: function () { return Promise.resolve(document); },
|
||||
isOwner: true,
|
||||
hasBrowsingContext: true,
|
||||
},
|
||||
{
|
||||
name: 'the document of the template elements',
|
||||
create: function () {
|
||||
return new Promise(function (resolve) {
|
||||
var template = document.createElementNS('http://www.w3.org/1999/xhtml', 'template');
|
||||
var doc = template.content.ownerDocument;
|
||||
if (!doc.documentElement)
|
||||
doc.appendChild(doc.createElement('html'));
|
||||
resolve(doc);
|
||||
});
|
||||
},
|
||||
hasBrowsingContext: false,
|
||||
},
|
||||
{
|
||||
name: 'a new document',
|
||||
create: function () {
|
||||
return new Promise(function (resolve) {
|
||||
var doc = new Document();
|
||||
doc.appendChild(doc.createElement('html'));
|
||||
resolve(doc);
|
||||
});
|
||||
},
|
||||
hasBrowsingContext: false,
|
||||
},
|
||||
{
|
||||
name: 'a cloned document',
|
||||
create: function () {
|
||||
return new Promise(function (resolve) {
|
||||
var doc = document.cloneNode(false);
|
||||
doc.appendChild(doc.createElement('html'));
|
||||
resolve(doc);
|
||||
});
|
||||
},
|
||||
hasBrowsingContext: false,
|
||||
},
|
||||
{
|
||||
name: 'a document created by createHTMLDocument',
|
||||
create: function () {
|
||||
return Promise.resolve(document.implementation.createHTMLDocument());
|
||||
},
|
||||
hasBrowsingContext: false,
|
||||
},
|
||||
{
|
||||
name: 'an HTML document created by createDocument',
|
||||
create: function () {
|
||||
return Promise.resolve(document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null));
|
||||
},
|
||||
hasBrowsingContext: false,
|
||||
},
|
||||
{
|
||||
name: 'the document of an iframe',
|
||||
create: function () {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.onload = function () { resolve(iframe.contentDocument); }
|
||||
iframe.onerror = function () { reject('Failed to load an empty iframe'); }
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
},
|
||||
hasBrowsingContext: true,
|
||||
},
|
||||
{
|
||||
name: 'an HTML document fetched by XHR',
|
||||
create: function () {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'resources/empty-html-document.html');
|
||||
xhr.overrideMimeType('text/xml');
|
||||
xhr.onload = function () { resolve(xhr.responseXML); }
|
||||
xhr.onerror = function () { reject('Failed to fetch the document'); }
|
||||
xhr.send();
|
||||
});
|
||||
},
|
||||
hasBrowsingContext: false,
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
190
tests/wpt/web-platform-tests/custom-elements/upgrading.html
Normal file
190
tests/wpt/web-platform-tests/custom-elements/upgrading.html
Normal file
|
@ -0,0 +1,190 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: Enqueue a custom element upgrade reaction</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="Enqueue a custom element upgrade reaction must upgrade a custom element">
|
||||
<link rel="help" href="https://dom.spec.whatwg.org/#concept-create-element">
|
||||
<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#concept-try-upgrade">
|
||||
<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#enqueue-a-custom-element-upgrade-reaction">
|
||||
<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>
|
||||
|
||||
class PredefinedCustomElement extends HTMLElement {}
|
||||
customElements.define('predefined-custom-element', PredefinedCustomElement);
|
||||
|
||||
var customElementNumber = 1;
|
||||
function generateNextCustomElementName() { return 'custom-' + customElementNumber++; }
|
||||
|
||||
// Tests for documents without a browsing context.
|
||||
document_types().filter(function (entry) { return !entry.isOwner && !entry.hasBrowsingContext; }).forEach(function (entry) {
|
||||
var documentName = entry.name;
|
||||
var getDocument = entry.create;
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
assert_false(doc.createElement('predefined-custom-element') instanceof PredefinedCustomElement);
|
||||
});
|
||||
}, 'Creating an element in ' + documentName + ' must not enqueue a custom element upgrade reaction'
|
||||
+ ' because the document does not have a browsing context');
|
||||
|
||||
promise_test(function () {
|
||||
var name = generateNextCustomElementName();
|
||||
var unresolvedElement = document.createElement(name);
|
||||
|
||||
assert_equals(unresolvedElement.__proto__, HTMLElement.prototype,
|
||||
'[[Prototype]] internal slot of the unresolved custom element must be the HTMLElement prototype');
|
||||
|
||||
return getDocument().then(function (doc) {
|
||||
var unresolvedElementInDoc = doc.createElement(name);
|
||||
var prototype = (unresolvedElementInDoc.namespaceURI == 'http://www.w3.org/1999/xhtml' ? HTMLElement : Element).prototype;
|
||||
|
||||
assert_equals(unresolvedElementInDoc.__proto__, prototype,
|
||||
'[[Prototype]] internal slot of the unresolved custom element must be the ' + prototype.toString() + ' prototype');
|
||||
var someCustomElement = class extends HTMLElement {};
|
||||
customElements.define(name, someCustomElement);
|
||||
assert_equals(unresolvedElementInDoc.__proto__, prototype, '"define" must not upgrade a disconnected unresolved custom elements');
|
||||
doc.documentElement.appendChild(unresolvedElementInDoc);
|
||||
assert_equals(unresolvedElementInDoc.__proto__, prototype,
|
||||
'Inserting an element into a document without a browsing context must not enqueue a custom element upgrade reaction');
|
||||
});
|
||||
}, 'Creating an element in ' + documentName + ' and inserting into the document must not enqueue a custom element upgrade reaction');
|
||||
|
||||
promise_test(function () {
|
||||
var name = generateNextCustomElementName();
|
||||
var unresolvedElement = document.createElement(name);
|
||||
|
||||
assert_equals(unresolvedElement.__proto__, HTMLElement.prototype,
|
||||
'[[Prototype]] internal slot of the unresolved custom element must be the HTMLElement prototype');
|
||||
|
||||
return getDocument().then(function (doc) {
|
||||
var unresolvedElementInDoc = doc.createElement(name);
|
||||
var prototype = (unresolvedElementInDoc.namespaceURI == 'http://www.w3.org/1999/xhtml' ? HTMLElement : Element).prototype;
|
||||
|
||||
assert_equals(unresolvedElementInDoc.__proto__, prototype,
|
||||
'[[Prototype]] internal slot of the unresolved custom element must be the ' + prototype.toString() + ' prototype');
|
||||
var someCustomElement = class extends HTMLElement {};
|
||||
customElements.define(name, someCustomElement);
|
||||
assert_equals(unresolvedElementInDoc.__proto__, prototype, '"define" must not upgrade a disconnected unresolved custom elements');
|
||||
document.body.appendChild(unresolvedElementInDoc);
|
||||
|
||||
if (unresolvedElementInDoc.namespaceURI == 'http://www.w3.org/1999/xhtml') {
|
||||
assert_equals(unresolvedElementInDoc.__proto__, someCustomElement.prototype,
|
||||
'Inserting an element into a document with a browsing context must enqueue a custom element upgrade reaction');
|
||||
} else {
|
||||
assert_equals(unresolvedElementInDoc.__proto__, prototype,
|
||||
'Looking up a custom element definition must return null if the element is not in the HTML namespace');
|
||||
}
|
||||
});
|
||||
}, 'Creating an element in ' + documentName + ' and adopting back to a document with browsing context must enqueue a custom element upgrade reaction');
|
||||
|
||||
});
|
||||
|
||||
// Tests for documents with a browsing context.
|
||||
document_types().filter(function (entry) { return !entry.isOwner && entry.hasBrowsingContext; }).forEach(function (entry) {
|
||||
var documentName = entry.name;
|
||||
var getDocument = entry.create;
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
assert_false(doc.createElement('predefined-custom-element') instanceof PredefinedCustomElement);
|
||||
});
|
||||
}, 'Creating an element in ' + documentName + ' must not enqueue a custom element upgrade reaction if there is no matching definition');
|
||||
|
||||
promise_test(function () {
|
||||
return getDocument().then(function (doc) {
|
||||
var docWindow = doc.defaultView;
|
||||
class DistinctPredefinedCustomElement extends docWindow.HTMLElement { };
|
||||
docWindow.customElements.define('predefined-custom-element', DistinctPredefinedCustomElement);
|
||||
assert_true(doc.createElement('predefined-custom-element') instanceof DistinctPredefinedCustomElement);
|
||||
});
|
||||
}, 'Creating an element in ' + documentName + ' must enqueue a custom element upgrade reaction if there is a matching definition');
|
||||
|
||||
promise_test(function () {
|
||||
var unresolvedElement = document.createElement('unresolved-element');
|
||||
return getDocument().then(function (doc) {
|
||||
var docWindow = doc.defaultView;
|
||||
class UnresolvedElement extends docWindow.HTMLElement { };
|
||||
var unresolvedElementInDoc = doc.createElement('unresolved-element');
|
||||
|
||||
assert_equals(unresolvedElement.__proto__, HTMLElement.prototype);
|
||||
assert_equals(unresolvedElementInDoc.__proto__, docWindow.HTMLElement.prototype);
|
||||
|
||||
docWindow.customElements.define('unresolved-element', UnresolvedElement);
|
||||
|
||||
assert_equals(unresolvedElement.__proto__, HTMLElement.prototype);
|
||||
assert_equals(unresolvedElementInDoc.__proto__, docWindow.HTMLElement.prototype);
|
||||
|
||||
});
|
||||
}, '"define" in ' + documentName + ' must not enqueue a custom element upgrade reaction on a disconnected unresolved custom element');
|
||||
|
||||
promise_test(function () {
|
||||
var unresolvedElement = document.createElement('unresolved-element');
|
||||
return getDocument().then(function (doc) {
|
||||
var docWindow = doc.defaultView;
|
||||
class UnresolvedElement extends docWindow.HTMLElement { };
|
||||
var unresolvedElementInDoc = doc.createElement('unresolved-element');
|
||||
|
||||
assert_equals(unresolvedElement.__proto__, HTMLElement.prototype);
|
||||
assert_equals(unresolvedElementInDoc.__proto__, docWindow.HTMLElement.prototype);
|
||||
|
||||
docWindow.customElements.define('unresolved-element', UnresolvedElement);
|
||||
doc.documentElement.appendChild(unresolvedElementInDoc);
|
||||
|
||||
assert_equals(unresolvedElement.__proto__, HTMLElement.prototype);
|
||||
assert_equals(unresolvedElementInDoc.__proto__, UnresolvedElement.prototype);
|
||||
});
|
||||
}, 'Inserting an unresolved custom element into ' + documentName + ' must enqueue a custom element upgrade reaction');
|
||||
|
||||
promise_test(function () {
|
||||
var unresolvedElement = document.createElement('unresolved-element');
|
||||
return getDocument().then(function (doc) {
|
||||
var docWindow = doc.defaultView;
|
||||
class UnresolvedElement extends docWindow.HTMLElement { };
|
||||
var unresolvedElementInDoc = doc.createElement('unresolved-element');
|
||||
doc.documentElement.appendChild(unresolvedElementInDoc);
|
||||
|
||||
assert_equals(unresolvedElement.__proto__, HTMLElement.prototype);
|
||||
assert_equals(unresolvedElementInDoc.__proto__, docWindow.HTMLElement.prototype);
|
||||
|
||||
docWindow.customElements.define('unresolved-element', UnresolvedElement);
|
||||
|
||||
assert_equals(unresolvedElement.__proto__, HTMLElement.prototype);
|
||||
assert_equals(unresolvedElementInDoc.__proto__, UnresolvedElement.prototype);
|
||||
});
|
||||
}, '"define" in ' + documentName + ' must enqueue a custom element upgrade reaction on a connected unresolved custom element');
|
||||
|
||||
promise_test(function () {
|
||||
var unresolvedElement = document.createElement('unresolved-element');
|
||||
return getDocument().then(function (doc) {
|
||||
var docWindow = doc.defaultView;
|
||||
class UnresolvedElement extends docWindow.HTMLElement { };
|
||||
assert_false(unresolvedElement instanceof UnresolvedElement);
|
||||
docWindow.customElements.define('unresolved-element', UnresolvedElement);
|
||||
doc.adoptNode(unresolvedElement);
|
||||
assert_false(unresolvedElement instanceof UnresolvedElement);
|
||||
});
|
||||
}, 'Adopting (and leaving disconnceted) an unresolved custom element into ' + documentName + ' must not enqueue a custom element upgrade reaction');
|
||||
|
||||
promise_test(function () {
|
||||
var unresolvedElement = document.createElement('unresolved-element');
|
||||
return getDocument().then(function (doc) {
|
||||
var docWindow = doc.defaultView;
|
||||
class UnresolvedElement extends docWindow.HTMLElement { };
|
||||
assert_false(unresolvedElement instanceof UnresolvedElement);
|
||||
docWindow.customElements.define('unresolved-element', UnresolvedElement);
|
||||
doc.documentElement.appendChild(unresolvedElement);
|
||||
assert_true(unresolvedElement instanceof UnresolvedElement);
|
||||
});
|
||||
}, 'Adopting and inserting an unresolved custom element into ' + documentName + ' must enqueue a custom element upgrade reaction');
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,185 @@
|
|||
<!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>
|
||||
|
||||
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_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');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
class MyCustomElement extends contentWindow.HTMLElement {
|
||||
constructor(doNotCreateItself) {
|
||||
super();
|
||||
if (!doNotCreateItself)
|
||||
new MyCustomElement(true);
|
||||
}
|
||||
}
|
||||
contentWindow.customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
var instance = new MyCustomElement(false);
|
||||
var uncaughtError;
|
||||
contentWindow.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
|
||||
instance.cloneNode(false);
|
||||
assert_equals(uncaughtError.name, 'InvalidStateError');
|
||||
}, 'HTMLElement constructor must throw an InvalidStateError when the top of the construction stack is marked AlreadyConstructed'
|
||||
+ ' due to a custom element constructor constructing itself after super() call');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
class MyCustomElement extends contentWindow.HTMLElement {
|
||||
constructor(doNotCreateItself) {
|
||||
if (!doNotCreateItself)
|
||||
new MyCustomElement(true);
|
||||
super();
|
||||
}
|
||||
}
|
||||
contentWindow.customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
var instance = new MyCustomElement(false);
|
||||
var uncaughtError;
|
||||
contentWindow.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
|
||||
instance.cloneNode(false);
|
||||
assert_equals(uncaughtError.name, 'InvalidStateError');
|
||||
}, 'HTMLElement constructor must throw an InvalidStateError when the top of the construction stack is marked AlreadyConstructed'
|
||||
+ ' due to a custom element constructor constructing itself before super() call');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
var contentDocument = contentWindow.document;
|
||||
var returnSpan = false;
|
||||
class MyCustomElement extends contentWindow.HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
if (returnSpan)
|
||||
return contentDocument.createElement('span');
|
||||
}
|
||||
}
|
||||
contentWindow.customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
var instance = new MyCustomElement(false);
|
||||
returnSpan = true;
|
||||
var uncaughtError;
|
||||
contentWindow.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
|
||||
instance.cloneNode(false);
|
||||
assert_equals(uncaughtError.name, 'InvalidStateError');
|
||||
}, 'Upgrading a custom element must throw InvalidStateError when the custom element\'s constructor returns another element');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
var contentDocument = contentWindow.document;
|
||||
var instance = contentDocument.createElement('my-custom-element');
|
||||
contentDocument.body.appendChild(instance);
|
||||
|
||||
var calls = [];
|
||||
class MyCustomElement extends contentWindow.HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
calls.push(this);
|
||||
throw 'bad';
|
||||
}
|
||||
}
|
||||
|
||||
var uncaughtError;
|
||||
contentWindow.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
|
||||
contentWindow.customElements.define('my-custom-element', MyCustomElement);
|
||||
assert_equals(uncaughtError, 'bad');
|
||||
|
||||
assert_array_equals(calls, [instance]);
|
||||
contentDocument.body.removeChild(instance);
|
||||
contentDocument.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>
|
|
@ -0,0 +1,158 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: Upgrading custom elements should enqueue attributeChanged and connected callbacks</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="Upgrading custom elements should enqueue attributeChanged and connected callbacksml">
|
||||
<meta name="help" content="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_with_window(function (contentWindow) {
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.write('<test-element id="some" title="This is a test">');
|
||||
|
||||
const undefinedElement = contentDocument.querySelector('test-element');
|
||||
assert_equals(Object.getPrototypeOf(undefinedElement), contentWindow.HTMLElement.prototype);
|
||||
|
||||
let log = [];
|
||||
class TestElement extends contentWindow.HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
log.push(create_constructor_log(this));
|
||||
}
|
||||
attributeChangedCallback(...args) {
|
||||
log.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
static get observedAttributes() { return ['id', 'title']; }
|
||||
}
|
||||
contentWindow.customElements.define('test-element', TestElement);
|
||||
assert_equals(Object.getPrototypeOf(undefinedElement), TestElement.prototype);
|
||||
|
||||
assert_equals(log.length, 3);
|
||||
assert_constructor_log_entry(log[0], undefinedElement);
|
||||
assert_attribute_log_entry(log[1], {name: 'id', oldValue: null, newValue: 'some', namespace: null});
|
||||
assert_attribute_log_entry(log[2], {name: 'title', oldValue: null, newValue: 'This is a test', namespace: null});
|
||||
}, 'Upgrading a custom element must enqueue attributeChangedCallback on each attribute');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.write('<test-element id="some" title="This is a test" class="foo">');
|
||||
|
||||
const undefinedElement = contentDocument.querySelector('test-element');
|
||||
assert_equals(Object.getPrototypeOf(undefinedElement), contentWindow.HTMLElement.prototype);
|
||||
|
||||
let log = [];
|
||||
class TestElement extends contentWindow.HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
log.push(create_constructor_log(this));
|
||||
}
|
||||
attributeChangedCallback(...args) {
|
||||
log.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
static get observedAttributes() { return ['class', 'id']; }
|
||||
}
|
||||
contentWindow.customElements.define('test-element', TestElement);
|
||||
assert_equals(Object.getPrototypeOf(undefinedElement), TestElement.prototype);
|
||||
|
||||
assert_equals(log.length, 3);
|
||||
assert_constructor_log_entry(log[0], undefinedElement);
|
||||
assert_attribute_log_entry(log[1], {name: 'id', oldValue: null, newValue: 'some', namespace: null});
|
||||
assert_attribute_log_entry(log[2], {name: 'class', oldValue: null, newValue: 'foo', namespace: null});
|
||||
}, 'Upgrading a custom element not must enqueue attributeChangedCallback on unobserved attributes');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.write('<test-element id="some" title="This is a test" class="foo">');
|
||||
|
||||
const undefinedElement = contentDocument.querySelector('test-element');
|
||||
assert_equals(Object.getPrototypeOf(undefinedElement), contentWindow.HTMLElement.prototype);
|
||||
|
||||
let log = [];
|
||||
class TestElement extends contentWindow.HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
log.push(create_constructor_log(this));
|
||||
}
|
||||
connectedCallback(...args) {
|
||||
log.push(create_connected_callback_log(this, ...args));
|
||||
}
|
||||
}
|
||||
contentWindow.customElements.define('test-element', TestElement);
|
||||
assert_equals(Object.getPrototypeOf(undefinedElement), TestElement.prototype);
|
||||
|
||||
assert_equals(log.length, 2);
|
||||
assert_constructor_log_entry(log[0], undefinedElement);
|
||||
assert_connected_log_entry(log[1], undefinedElement);
|
||||
}, 'Upgrading a custom element must enqueue connectedCallback if the element in the document');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.write('<test-element id="some" title="This is a test" class="foo">');
|
||||
|
||||
const undefinedElement = contentDocument.querySelector('test-element');
|
||||
assert_equals(Object.getPrototypeOf(undefinedElement), contentWindow.HTMLElement.prototype);
|
||||
|
||||
let log = [];
|
||||
class TestElement extends contentWindow.HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
log.push(create_constructor_log(this));
|
||||
}
|
||||
connectedCallback(...args) {
|
||||
log.push(create_connected_callback_log(this, ...args));
|
||||
}
|
||||
attributeChangedCallback(...args) {
|
||||
log.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
static get observedAttributes() { return ['class', 'id']; }
|
||||
}
|
||||
contentWindow.customElements.define('test-element', TestElement);
|
||||
assert_equals(Object.getPrototypeOf(undefinedElement), TestElement.prototype);
|
||||
|
||||
assert_equals(log.length, 4);
|
||||
assert_constructor_log_entry(log[0], undefinedElement);
|
||||
assert_attribute_log_entry(log[1], {name: 'id', oldValue: null, newValue: 'some', namespace: null});
|
||||
assert_attribute_log_entry(log[2], {name: 'class', oldValue: null, newValue: 'foo', namespace: null});
|
||||
assert_connected_log_entry(log[3], undefinedElement);
|
||||
}, 'Upgrading a custom element must enqueue attributeChangedCallback before connectedCallback');
|
||||
|
||||
test_with_window(function (contentWindow) {
|
||||
const contentDocument = contentWindow.document;
|
||||
contentDocument.write('<test-element id="some" title="This is a test" class="foo">');
|
||||
|
||||
const undefinedElement = contentDocument.querySelector('test-element');
|
||||
assert_equals(Object.getPrototypeOf(undefinedElement), contentWindow.HTMLElement.prototype);
|
||||
|
||||
let log = [];
|
||||
class TestElement extends contentWindow.HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
log.push(create_constructor_log(this));
|
||||
throw 'Exception thrown as a part of test';
|
||||
}
|
||||
connectedCallback(...args) {
|
||||
log.push(create_connected_callback_log(this, ...args));
|
||||
}
|
||||
attributeChangedCallback(...args) {
|
||||
log.push(create_attribute_changed_callback_log(this, ...args));
|
||||
}
|
||||
static get observedAttributes() { return ['class', 'id']; }
|
||||
}
|
||||
contentWindow.customElements.define('test-element', TestElement);
|
||||
assert_equals(Object.getPrototypeOf(undefinedElement), TestElement.prototype);
|
||||
|
||||
assert_equals(log.length, 1);
|
||||
assert_constructor_log_entry(log[0], undefinedElement);
|
||||
}, 'Upgrading a custom element must not invoke attributeChangedCallback and connectedCallback when the element failed to upgrade');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,97 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Elements: Upgrading unresolved elements</title>
|
||||
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
||||
<meta name="assert" content="HTML parser must add an unresolved custom element to the upgrade candidates map">
|
||||
<link rel="help" href="https://html.spec.whatwg.org/#upgrades">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<my-custom-element></my-custom-element>
|
||||
<instantiates-itself-after-super></instantiates-itself-after-super>
|
||||
<instantiates-itself-before-super></instantiates-itself-before-super>
|
||||
<my-other-element id="instance"></my-other-element>
|
||||
<my-other-element id="otherInstance"></my-other-element>
|
||||
<script>
|
||||
|
||||
setup({allow_uncaught_exception:true});
|
||||
|
||||
test(function () {
|
||||
class MyCustomElement extends HTMLElement { }
|
||||
|
||||
var instance = document.querySelector('my-custom-element');
|
||||
assert_true(instance instanceof HTMLElement);
|
||||
assert_false(instance instanceof HTMLUnknownElement,
|
||||
'an unresolved custom element should not be an instance of HTMLUnknownElement');
|
||||
assert_false(instance instanceof MyCustomElement);
|
||||
|
||||
customElements.define('my-custom-element', MyCustomElement);
|
||||
|
||||
assert_true(instance instanceof HTMLElement);
|
||||
assert_true(instance instanceof MyCustomElement,
|
||||
'Calling customElements.define must upgrade existing custom elements');
|
||||
|
||||
}, 'Element.prototype.createElement must add an unresolved custom element to the upgrade candidates map');
|
||||
|
||||
test(function () {
|
||||
class InstantiatesItselfAfterSuper extends HTMLElement {
|
||||
constructor(doNotCreateItself) {
|
||||
super();
|
||||
if (!doNotCreateItself)
|
||||
new InstantiatesItselfAfterSuper(true);
|
||||
}
|
||||
}
|
||||
|
||||
var uncaughtError;
|
||||
window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
|
||||
customElements.define('instantiates-itself-after-super', InstantiatesItselfAfterSuper);
|
||||
assert_equals(uncaughtError.name, 'InvalidStateError');
|
||||
}, 'HTMLElement constructor must throw an InvalidStateError when the top of the construction stack is marked AlreadyConstructed'
|
||||
+ ' due to a custom element constructor constructing itself after super() call');
|
||||
|
||||
test(function () {
|
||||
class InstantiatesItselfBeforeSuper extends HTMLElement {
|
||||
constructor(doNotCreateItself) {
|
||||
if (!doNotCreateItself)
|
||||
new InstantiatesItselfBeforeSuper(true);
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
var uncaughtError;
|
||||
window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
|
||||
customElements.define('instantiates-itself-before-super', InstantiatesItselfBeforeSuper);
|
||||
assert_equals(uncaughtError.name, 'InvalidStateError');
|
||||
}, 'HTMLElement constructor must throw an InvalidStateError when the top of the construction stack is marked AlreadyConstructed'
|
||||
+ ' due to a custom element constructor constructing itself before super() call');
|
||||
|
||||
test(function () {
|
||||
class MyOtherElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
if (this == instance)
|
||||
return otherInstance;
|
||||
}
|
||||
}
|
||||
var instance = document.getElementById('instance');
|
||||
var otherInstance = document.getElementById('otherInstance');
|
||||
|
||||
assert_false(instance instanceof MyOtherElement);
|
||||
assert_false(otherInstance instanceof MyOtherElement);
|
||||
|
||||
var uncaughtError;
|
||||
window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; }
|
||||
customElements.define('my-other-element', MyOtherElement);
|
||||
assert_equals(uncaughtError.name, 'InvalidStateError');
|
||||
|
||||
assert_true(document.createElement('my-other-element') instanceof MyOtherElement,
|
||||
'Upgrading of custom elements must happen after the definition was added to the registry.');
|
||||
|
||||
}, 'Upgrading a custom element must throw an InvalidStateError when the returned element is not SameValue as the upgraded element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue