import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

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

View file

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom Elements: HTMLElement must allow subclassing</title>
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
<meta name="assert" content="HTMLElement must allow subclassing">
<link rel="help" href="https://html.spec.whatwg.org/#html-element-constructors">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function () {
customElements.define('html-custom-element', HTMLElement);
assert_throws({'name': 'TypeError'}, function () { new HTMLElement(); });
}, 'HTMLElement constructor must throw a TypeError when NewTarget is equal to itself');
test(function () {
customElements.define('html-proxy-custom-element', new Proxy(HTMLElement, {}));
assert_throws({'name': 'TypeError'}, function () { new HTMLElement(); });
}, 'HTMLElement constructor must throw a TypeError when NewTarget is equal to itself via a Proxy object');
test(function () {
class SomeCustomElement extends HTMLElement {};
assert_throws({'name': 'TypeError'}, function () { new SomeCustomElement; });
}, 'HTMLElement constructor must throw TypeError when it has not been defined by customElements.define');
test(function () {
class CustomElementWithInferredTagName extends HTMLElement {};
customElements.define('inferred-name', CustomElementWithInferredTagName);
var instance = new CustomElementWithInferredTagName;
assert_true(instance instanceof Element, 'A custom element must inherit from Element');
assert_true(instance instanceof Node, 'A custom element must inherit from Node');
assert_equals(instance.localName, 'inferred-name');
assert_equals(instance.nodeName, 'INFERRED-NAME');
assert_equals(instance.namespaceURI, 'http://www.w3.org/1999/xhtml', 'A custom HTML element must use HTML namespace');
document.body.appendChild(instance);
assert_equals(document.body.lastChild, instance,
'document.body.appendChild must be able to insert a custom element');
assert_equals(document.querySelector('inferred-name'), instance,
'document.querySelector must be able to find a custom element by its tag name');
}, 'HTMLElement constructor must infer the tag name from the element interface');
test(function () {
class ConcreteCustomElement extends HTMLElement { };
class SubCustomElement extends ConcreteCustomElement { };
customElements.define('concrete-custom-element', ConcreteCustomElement);
customElements.define('sub-custom-element', SubCustomElement);
var instance = new ConcreteCustomElement();
assert_true(instance instanceof ConcreteCustomElement);
assert_false(instance instanceof SubCustomElement);
assert_equals(instance.localName, 'concrete-custom-element');
assert_equals(instance.nodeName, 'CONCRETE-CUSTOM-ELEMENT');
var instance = new SubCustomElement();
assert_true(instance instanceof ConcreteCustomElement);
assert_true(instance instanceof SubCustomElement);
assert_equals(instance.localName, 'sub-custom-element');
assert_equals(instance.nodeName, 'SUB-CUSTOM-ELEMENT');
}, 'HTMLElement constructor must allow subclassing a custom element');
test(function () {
class AbstractCustomElement extends HTMLElement { };
class ConcreteSubCustomElement extends AbstractCustomElement { };
customElements.define('concrete-sub-custom-element', ConcreteSubCustomElement);
var instance = new ConcreteSubCustomElement();
assert_true(instance instanceof AbstractCustomElement);
assert_true(instance instanceof ConcreteSubCustomElement);
assert_equals(instance.localName, 'concrete-sub-custom-element');
assert_equals(instance.nodeName, 'CONCRETE-SUB-CUSTOM-ELEMENT');
}, 'HTMLElement constructor must allow subclassing an user-defined subclass of HTMLElement');
</script>
</body>
</html>

View file

@ -0,0 +1,9 @@
@alsemenov
@deepak-sa
@domenic
@dominiccooney
@hayatoito
@kojiishi
@rniwa
@sgrekhov
@takayoshikochi

View file

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

View file

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

View file

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

View file

@ -0,0 +1,243 @@
<!DOCTYPE html>
<title>Custom Elements: Element definition</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<div id="log"></div>
<iframe id="iframe"></iframe>
<script>
'use strict';
(() => {
// Element definition
// https://html.spec.whatwg.org/multipage/scripting.html#element-definition
// Use window from iframe to isolate the test.
const testWindow = iframe.contentDocument.defaultView;
const customElements = testWindow.customElements;
let testable = false;
test(() => {
assert_true('customElements' in testWindow, '"window.customElements" exists');
assert_true('define' in customElements, '"window.customElements.define" exists');
testable = true;
}, '"window.customElements.define" should exists');
if (!testable)
return;
const expectTypeError = TypeError.prototype;
// Following errors are DOMException, not JavaScript errors.
const expectSyntaxError = 'SYNTAX_ERR';
const expectNotSupportedError = 'NOT_SUPPORTED_ERR';
// 1. If IsConstructor(constructor) is false,
// then throw a TypeError and abort these steps.
test(() => {
assert_throws(expectTypeError, () => {
customElements.define();
});
}, 'If no arguments, should throw a TypeError');
test(() => {
assert_throws(expectTypeError, () => {
customElements.define('test-define-one-arg');
});
}, 'If one argument, should throw a TypeError');
[
[ 'undefined', undefined ],
[ 'null', null ],
[ 'object', {} ],
[ 'string', 'string' ],
[ 'arrow function', () => {} ], // IsConstructor returns false for arrow functions
[ 'method', ({ m() { } }).m ], // IsConstructor returns false for methods
].forEach(t => {
test(() => {
assert_throws(expectTypeError, () => {
customElements.define(`test-define-constructor-${t[0]}`, t[1]);
});
}, `If constructor is ${t[0]}, should throw a TypeError`);
});
// 2. If name is not a valid custom element name,
// then throw a SyntaxError and abort these steps.
let validCustomElementNames = [
// [a-z] (PCENChar)* '-' (PCENChar)*
// https://html.spec.whatwg.org/multipage/scripting.html#valid-custom-element-name
'a-',
'a-a',
'aa-',
'aa-a',
'a-.-_',
'a-0123456789',
'a-\u6F22\u5B57', // Two CJK Unified Ideographs
'a-\uD840\uDC0B', // Surrogate pair U+2000B
];
let invalidCustomElementNames = [
undefined,
null,
'',
'-',
'a',
'input',
'mycustomelement',
'A',
'A-',
'0-',
'a-A',
'a-Z',
'A-a',
'a-a\u00D7',
'a-a\u3000',
'a-a\uDB80\uDC00', // Surrogate pair U+F0000
// name must not be any of the hyphen-containing element names.
'annotation-xml',
'color-profile',
'font-face',
'font-face-src',
'font-face-uri',
'font-face-format',
'font-face-name',
'missing-glyph',
];
validCustomElementNames.forEach(name => {
test(() => {
customElements.define(name, class {});
}, `Element names: defining an element named ${name} should succeed`);
});
invalidCustomElementNames.forEach(name => {
test(() => {
assert_throws(expectSyntaxError, () => {
customElements.define(name, class {});
});
}, `Element names: defining an element named ${name} should throw a SyntaxError`);
});
// 3. If this CustomElementRegistry contains an entry with name name,
// then throw a NotSupportedError and abort these steps.
test(() => {
customElements.define('test-define-dup-name', class {});
assert_throws(expectNotSupportedError, () => {
customElements.define('test-define-dup-name', class {});
});
}, 'If the name is already defined, should throw a NotSupportedError');
// 5. If this CustomElementRegistry contains an entry with constructor constructor,
// then throw a NotSupportedError and abort these steps.
test(() => {
class TestDupConstructor {};
customElements.define('test-define-dup-constructor', TestDupConstructor);
assert_throws(expectNotSupportedError, () => {
customElements.define('test-define-dup-ctor2', TestDupConstructor);
});
}, 'If the constructor is already defined, should throw a NotSupportedError');
// 9.1. If extends is a valid custom element name,
// then throw a NotSupportedError.
validCustomElementNames.forEach(name => {
test(() => {
assert_throws(expectNotSupportedError, () => {
customElements.define('test-define-extend-valid-name', class {}, { extends: name });
});
}, `If extends is ${name}, should throw a NotSupportedError`);
});
// 9.2. If the element interface for extends and the HTML namespace is HTMLUnknownElement
// (e.g., if extends does not indicate an element definition in this specification),
// then throw a NotSupportedError.
[
// https://html.spec.whatwg.org/multipage/dom.html#elements-in-the-dom:htmlunknownelement
'bgsound',
'blink',
'isindex',
'multicol',
'nextid',
'spacer',
'elementnametobeunknownelement',
].forEach(name => {
test(() => {
assert_throws(expectNotSupportedError, () => {
customElements.define('test-define-extend-' + name, class {}, { extends: name });
});
}, `If extends is ${name}, should throw a NotSupportedError`);
});
// 12.1. Let prototype be Get(constructor, "prototype"). Rethrow any exceptions.
function assert_rethrown(func, description) {
assert_throws({ name: 'rethrown' }, func, description);
}
function throw_rethrown_error() {
const e = new Error('check this is rethrown');
e.name = 'rethrown';
throw e;
}
test(() => {
// Hack for prototype to throw while IsConstructor is true.
const BadConstructor = (function () { }).bind({});
Object.defineProperty(BadConstructor, 'prototype', {
get() { throw_rethrown_error(); }
});
assert_rethrown(() => {
customElements.define('test-define-constructor-prototype-rethrow', BadConstructor);
});
}, 'If constructor.prototype throws, should rethrow');
// 12.2. If Type(prototype) is not Object,
// then throw a TypeError exception.
test(() => {
const c = (function () { }).bind({}); // prototype is undefined.
assert_throws(expectTypeError, () => {
customElements.define('test-define-constructor-prototype-undefined', c);
});
}, 'If Type(constructor.prototype) is undefined, should throw a TypeError');
test(() => {
function c() {};
c.prototype = 'string';
assert_throws(expectTypeError, () => {
customElements.define('test-define-constructor-prototype-string', c);
});
}, 'If Type(constructor.prototype) is string, should throw a TypeError');
// 12.3. Let lifecycleCallbacks be a map with the four keys "connectedCallback",
// "disconnectedCallback", "adoptedCallback", and "attributeChangedCallback",
// each of which belongs to an entry whose value is null.
// 12.4. For each of the four keys callbackName in lifecycleCallbacks:
// 12.4.1. Let callbackValue be Get(prototype, callbackName). Rethrow any exceptions.
// 12.4.2. If callbackValue is not undefined, then set the value of the entry in
// lifecycleCallbacks with key callbackName to the result of converting callbackValue
// to the Web IDL Function callback type. Rethrow any exceptions from the conversion.
[
'connectedCallback',
'disconnectedCallback',
'adoptedCallback',
'attributeChangedCallback',
].forEach(name => {
test(() => {
class C {
get [name]() { throw_rethrown_error(); }
}
assert_rethrown(() => {
customElements.define(`test-define-${name.toLowerCase()}-rethrow`, C);
});
}, `If constructor.prototype.${name} throws, should rethrow`);
[
{ name: 'undefined', value: undefined, success: true },
{ name: 'function', value: function () { }, success: true },
{ name: 'null', value: null, success: false },
{ name: 'object', value: {}, success: false },
{ name: 'integer', value: 1, success: false },
].forEach(data => {
test(() => {
class C { };
C.prototype[name] = data.value;
if (data.success) {
customElements.define(`test-define-${name.toLowerCase()}-${data.name}`, C);
} else {
assert_throws(expectTypeError, () => {
customElements.define(`test-define-${name.toLowerCase()}-${data.name}`, C);
});
}
}, `If constructor.prototype.${name} is ${data.name}, should ${data.success ? 'succeed' : 'throw a TypeError'}`);
});
});
})();
</script>
</body>

View file

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

View file

@ -0,0 +1,124 @@
<!DOCTYPE html>
<title>Custom Elements: [HTMLConstructor] derives prototype from NewTarget</title>
<meta name="author" title="Domenic Denicola" href="mailto:d@domenic.me">
<meta name="help" content="https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/custom-elements-helpers.js"></script>
<script>
"use strict";
test_with_window(w => {
let afterDefinition = false;
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);
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;
}
});
w.customElements.define("test-element", TestElement);
beforeDefinition = true;
new TestElement();
}, "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 () {
assert_throws({ name: "prototype throws" }, () => {
return Reflect.construct(w.HTMLElement, [], new.target);
});
}).bind({});
Object.defineProperty(TestElement, "prototype", {
get() {
if (throws) {
throw { name: "prototype throws" };
}
return {};
}
});
w.customElements.define("test-element", TestElement);
throws = true;
new TestElement();
}, "Rethrow any exceptions thrown while getting the prototype");
test_with_window(w => {
for (const notAnObject of [null, undefined, 5, "string"]) {
// We have to return an object during define(), but not during super()
let returnNotAnObject = false;
const TestElement = (function () {
const o = Reflect.construct(w.HTMLElement, [], new.target);
assert_equals(Object.getPrototypeOf(o), window.HTMLElement,
"Must use the HTMLElement from the realm of NewTarget");
assert_not_equals(Object.getPrototypeOf(o), w.HTMLElement,
"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 : {};
}
});
w.customElements.define("test-element", TestElement);
returnNotAnObject = true;
new TestElement();
}
}, "If prototype is not object, derives the fallback from NewTarget's realm (autonomous custom elements)");
test_with_window(w => {
for (const notAnObject of [null, undefined, 5, "string"]) {
// We have to return an object during define(), but not during super()
let returnNotAnObject = false;
const TestElement = (function () {
const o = Reflect.construct(w.HTMLParagraphElement, [], new.target);
assert_equals(Object.getPrototypeOf(o), window.HTMLParagraphElement,
"Must use the HTMLParagraphElement from the realm of NewTarget");
assert_not_equals(Object.getPrototypeOf(o), w.HTMLParagraphElement,
"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 : {};
}
});
w.customElements.define("test-element", TestElement, { extends: "p" });
returnNotAnObject = true;
new TestElement();
}
}, "If prototype is not object, derives the fallback from NewTarget's realm (customized built-in elements)");
</script>

View file

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

View file

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

View file

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

View file

@ -0,0 +1,217 @@
<!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');
assert_array_equals(element.takeLog().types(), []);
}, 'remove on DOMTokenList must not enqueue an attributeChanged reaction 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>

View file

@ -0,0 +1,47 @@
<!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="importNode and adoptNode of Document interface must have CEReactions">
<meta name="help" content="https://dom.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(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();
newDoc.importNode(instance);
var logEntries = element.takeLog();
assert_array_equals(logEntries.types(), ['constructed']);
assert_equals(logEntries.last().oldDocument, document);
assert_equals(logEntries.last().newDocument, newDoc);
}, 'importNode on Document must construct a new custom element when importing a custom element');
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();
newDoc.adoptNode(instance);
var logEntries = element.takeLog();
assert_array_equals(logEntries.types(), ['adopted']);
assert_equals(logEntries.last().oldDocument, document);
assert_equals(logEntries.last().newDocument, newDoc);
}, 'adoptNode on Document must enqueue an adopted reaction when importing a custom element');
</script>
</body>
</html>

View file

@ -0,0 +1,61 @@
<!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="id, className, slot, setAttribute, setAttributeNS, removeAttribute, removeAttributeNS, setAttributeNode, setAttributeNodeNS, removeAttributeNode, and insertAdjacentElement of Element interface must have CEReactions">
<meta name="help" content="https://dom.spec.whatwg.org/#element">
<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);
});
</script>
</body>
</html>

View file

@ -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');
testAttributeRemover(function (element, name) {
element.attributes.removeNamedItemNS(null, name);
}, 'removeNamedItemNS on NamedNodeMap');
</script>
</body>
</html>

View file

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

View file

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

View file

@ -0,0 +1,49 @@
<!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);
}, 'insertNode on Range');
</script>
</body>
</html>

View file

@ -0,0 +1,217 @@
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);
assert_array_equals(element.takeLog().types(), ['disconnected']);
}, name + ' must enqueue a disconnected reaction');
container.parentNode.removeChild(container);
}
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 testReflectAttribute(jsAttributeName, contentAttributeName, validValue1, validValue2, 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: validValue1, 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: validValue1, newValue: validValue2, namespace: null});
}, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute');
}
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) {
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');
}

View file

@ -0,0 +1,162 @@
function create_window_in_test(t, srcdoc) {
let p = new Promise((resolve) => {
let f = document.createElement('iframe');
f.srcdoc = srcdoc ? srcdoc : '';
f.onload = (event) => {
let w = f.contentWindow;
t.add_cleanup(() => f.parentNode && f.remove());
resolve(w);
};
document.body.appendChild(f);
});
return p;
}
function test_with_window(f, name, srcdoc) {
promise_test((t) => {
return create_window_in_test(t, srcdoc)
.then((w) => {
f(w);
});
}, name);
}
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,
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,
}
];
}

View file

@ -0,0 +1,5 @@
<!DOCTYPE html>
<html>
<body>
</body>
</html>

View file

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<title>First char allowed for custom element type </title>
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="The custom element type identifies a custom element interface and is a sequence of characters that must match the NCName production">
<meta name="timeout" content="long">
<link rel="help" href="https://dvcs.w3.org/hg/webcomponents/raw-file/default/spec/custom/index.html#concepts">
<link rel="help" href="http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
function composeName(charCode) {
return String.fromCharCode(charCode) + '-x' + charCode.toString(16);
}
// NCName definition from http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName
// NCName ::= (Letter | '_') (NCNameChar)*
// NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
// Letter ::= BaseChar | Ideographic
test(function() {
testCharCode(getCharCode('_'), composeName, checkValidName);
}, 'Registering valid custom element name starting with \'_\' char');
test(function() {
baseCharsSingle.testEach(composeName, checkValidName);
baseCharsRanges.testEach(composeName, checkValidName);
}, 'Registering valid custom element name starting with base char');
test(function() {
ideographicCharsSingle.testEach(composeName, checkValidName);
ideographicCharsRanges.testEach(composeName, checkValidName);
}, 'Registering valid custom element name starting with ideographic char');
test(function() {
(new CharsArray(['.', '-', ':', '+', '='])).testEach(composeName, checkInvalidName);
}, 'Registering custom element starting with invalid character (general) should fail');
test(function() {
combiningChars.testEach(composeName, checkInvalidName);
}, 'Registering custom element starting with invalid character (combining char) should fail');
test(function() {
extenderChars.testEach(composeName, checkInvalidName);
}, 'Registering custom element starting with invalid character (extender char) should fail');
test(function() {
digitCharsRanges.testEach(composeName, checkInvalidName);
}, 'Registering custom element starting with invalid character (digit char) should fail');
</script>
</body>
</html>

View file

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<title>Chars allowed for custom element type </title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="The custom element type identifies a custom element interface and is a sequence of characters that must match the NCName production">
<meta name="timeout" content="long">
<link rel="help" href="https://dvcs.w3.org/hg/webcomponents/raw-file/default/spec/custom/index.html#concepts">
<link rel="help" href="http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
function composeName(charCode) {
return 'x-' + String.fromCharCode(charCode) + '-x' + charCode.toString(16);
}
// NCName definition from http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName
// NCName ::= (Letter | '_') (NCNameChar)*
// NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
// Letter ::= BaseChar | Ideographic
test(function() {
baseCharsSingle.testEach(composeName, checkValidName);
baseCharsRanges.testEach(composeName, checkValidName);
}, 'Registering valid custom element types with base char');
test(function() {
ideographicCharsSingle.testEach(composeName, checkValidName);
ideographicCharsRanges.testEach(composeName, checkValidName);
}, 'Registering valid custom element types with ideographic char');
test(function() {
digitCharsRanges.testEach(composeName, checkValidName);
}, 'Registering valid custom element types with digit chars');
test(function() {
(new CharsArray(['.', '-', '_'])).testEach(composeName, checkValidName)
}, 'Registering valid custom element types with characters \'.\', \'-\', \'_\'');
test(function() {
combiningChars.testEach(composeName, checkValidName);
}, 'Registering valid custom element types with combining char');
test(function() {
extenderChars.testEach(composeName, checkValidName);
}, 'Registering valid custom element types with extender char');
</script>
</body>
</html>

View file

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html>
<head>
<title>The custom element type is a sequence of characters that must match the NCName production and contain a minus character</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="The custom element type is a sequence of characters that must match the NCName production and contain a U+002D HYPHEN-MINUS character">
<link rel="help" href="https://dvcs.w3.org/hg/webcomponents/raw-file/default/spec/custom/index.html#concepts">
<link rel="help" href="http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var validNames = ['x-frame', 'xx-frame', 'x--frame', '_-frame', 'x-', 'x--',
'x-1', 'x-_', '_-_', '__-', '_-1', '_-..'];
HTML5_ELEMENTS.forEach(function(value) {
validNames.push('x-' + value);
validNames.push('Y-' + value.toUpperCase());
});
validNames.forEach(function(value) {
try {
doc.registerElement(value);
} catch (e) {
assert_unreached('Exception should not be thrown in case of attempt ' +
'to register a custom element with a name \'' + value + '\'');
}
});
}, 'Registering valid custom element types');
test(function() {
var doc = newHTMLDocument();
var invalidNames = ['xframe', 'x_frame', 'x.frame', 'x1frame', '-xframe', '1-frame',
'1x-frame', '.-frame', '_frame', 'x-f!rame', 'x-:frame'];
invalidNames.forEach(function(value) {
assert_throws('SyntaxError', function() {
doc.registerElement(value);
}, 'Exception should be thrown in case of attempt to register element ' +
'with the name \'' + value + '\'');
});
}, 'Registering invalid custom element types should fail');
test(function() {
var doc = newHTMLDocument();
var forbiddenNames = ['annotation-xml',
'color-profile',
'font-face',
'font-face-src',
'font-face-uri',
'font-face-format',
'font-face-name',
'missing-glyph'];
forbiddenNames.forEach(function(value) {
assert_throws('SyntaxError', function() {
doc.registerElement(value);
}, 'Exception should be thrown in case of attempt to register element ' +
'with the name \'' + value + '\'');
});
}, 'Registering forbidden custom element types should fail');
</script>
</body>
</html>

View file

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<title>When an HTML Document is loaded in a browsing context, a new registry must be created and associated with this document</title>
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="When an HTML Document is loaded in a browsing context, a new registry must be created and associated with this document.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#creating-and-passing-registries">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
testInIFrame('../resources/blank.html', function(doc) {
try {
doc.registerElement('x-element');
} catch (e) {
assert_unreached('Unexpected exception, while registering a valid custom element');
}
}, 'Document, loaded into browsing context, must have a new empty registry');
testInIFrame('../resources/blank.html', function(loadedDocument) {
var createdDocument = document.implementation.createHTMLDocument('Test Document');
// Let's check that loadedDocument and createdDocument use different registeries.
createdDocument.registerElement('x-element');
try {
loadedDocument.registerElement('x-element');
} catch (e) {
assert_unreached('Unexpected exception while registering a custom element ' +
'in a document, which has it\'s own registry');
}
}, 'Document, loaded into browsing context, must have a new empty registry, ' +
'which is different from other documents\' registries');
</script>
</body>
</html>

View file

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html>
<head>
<title>New document without browsing context must not have a registry</title>
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="In all other cases, new documents must not have a registry.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#creating-and-passing-registries">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = document.implementation.createDocument(null, 'test', null);
assert_throws(
'NotSupportedError',
function() { doc.registerElement('x-element'); },
'Registering valid custom element in a document ' +
'without registry should fail');
}, 'Document of type other than HTML, not loaded into browsing context, must not have a registry');
async_test(function(t) {
var request = new XMLHttpRequest();
request.onreadystatechange = t.step_func(function() {
if (request.readyState == 4){
assert_equals(200, request.status, 'Test document is not loaded correctly');
var doc = request.response;
assert_true(doc instanceof HTMLDocument,
'XMLHttpRequest\'s asynchronous response should be HTML document');
assert_throws(
'NotSupportedError',
function() { doc.registerElement('x-element'); },
'Registering valid custom element in ' +
'an XMLHttpRequest\'s response document should fail');
t.done();
}
});
request.open('GET', '../resources/blank.html', true);
request.responseType = 'document';
request.send();
}, 'XMLHttpRequest\'s asynchronous response HTML document must not have a registry');
</script>
</body>
</html>

View file

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html>
<head>
<title>Document, created with createHTMLDocument or createDocument with HTML namespace, should share registry with the associated document</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="When DOMImplementation's createDocument method is invoked with namespace set to HTML Namespace or when the createHTMLDocument method is invoked, use the registry of the associated document to the new instance.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#creating-and-passing-registries">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var name = 'x-frame';
var GeneratedConstructor = doc.registerElement(name);
var doc2 = doc.implementation.createHTMLDocument('Document 2');
assert_throws(
'NotSupportedError',
function() { doc2.registerElement(name); },
'Registering a custom element type name that is already registered in a shared ' +
'registry should throw an exception');
var xframe = doc2.createElement(name);
assert_true(xframe instanceof GeneratedConstructor,
'Created element should be x-frame instance');
}, 'Document created by createHTMLDocument should share an existing registry');
test(function() {
var doc = newHTMLDocument();
var name = 'x-frame-1';
var GeneratedConstructor = doc.registerElement(name);
var doc2 = doc.implementation.createDocument(HTML_NAMESPACE, 'html', null);
assert_throws(
'NotSupportedError',
function() { doc2.registerElement(name); },
'Exception should be thrown for custom element, ' +
'which is already registered in shared registry');
var xframe = doc2.createElement(name);
assert_true(xframe instanceof GeneratedConstructor,
'Created element should be x-frame instance');
}, 'Document created by createDocument with HTML namespace should share an existing registry');
</script>
</body>
</html>

View file

@ -0,0 +1,95 @@
<!DOCTYPE html>
<html>
<head>
<title>When creating an import, use the registry of the master document</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="When creating an import, use the registry of the master document.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#creating-and-passing-registries">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
var test1 = async_test('Registry of the imported document should be shared with master document. ' +
'Import is asynchronous');
test1.step(function() {
var iframe = newIFrame('../resources/import-master-async.html');
document.body.appendChild(iframe);
iframe.onload = test1.step_func(function() {
var doc = iframe.contentDocument;
var link = doc.querySelector('link[rel=import]');
link.onload = test1.step_func(function() {
try {
var doc2 = link.import;
var name = 'x-frame';
doc.registerElement(name);
assert_throws(
'NotSupportedError',
function() { doc2.registerElement(name); },
'Registering a custom element type name that is already registered in a shared ' +
'registry should throw an exception');
test1.done();
} finally {
iframe.remove();
}
});
});
});
var test2 = async_test('Registry of the master document should be shared with imported document. ' +
'Import is asynchronous');
test2.step(function() {
var iframe = newIFrame('../resources/import-master-async.html');
document.body.appendChild(iframe);
iframe.onload = test2.step_func(function() {
var doc = iframe.contentDocument;
var link = doc.querySelector('link[rel=import]');
link.onload = test2.step_func(function() {
try {
var doc2 = link.import;
var name = 'x-frame';
doc2.registerElement(name);
assert_throws(
'NotSupportedError',
function() { doc.registerElement(name); },
'Registering a custom element type name that is already registered in a shared ' +
'registry should throw an exception');
test2.done();
} finally {
iframe.remove();
}
});
});
});
testInIFrame('../resources/import-master.html', function(doc) {
var link = doc.querySelector('link[rel=import]');
var doc2 = link.import;
var name = 'x-frame';
doc.registerElement(name);
assert_throws(
'NotSupportedError',
function() { doc2.registerElement(name); },
'Registering a custom element type name that is already registered in a shared ' +
'registry should throw an exception');
}, 'Registry of the imported document should be shared with master document. Import is syncronous');
testInIFrame('../resources/import-master.html', function(doc) {
var link = doc.querySelector('link[rel=import]');
var doc2 = link.import;
var name = 'x-frame';
doc2.registerElement(name);
assert_throws(
'NotSupportedError',
function() { doc.registerElement(name); },
'Registering a custom element type name that is already registered in a shared ' +
'registry should throw an exception');
}, 'Registry of the master document should be shared with imported document. Import is syncronous');
</script>
</body>
</html>

View file

@ -0,0 +1,60 @@
<!DOCTYPE html>
<html>
<head>
<title>Invoke CALLBACK with ELEMENT as callback this value</title>
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Invoke CALLBACK with ELEMENT as callback this value">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#enqueuing-and-invoking-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto});
var customElement = new GeneratedConstructor();
doc.body.innerHTML = '<x-a id="x-a"></x-a>';
assert_equals(doc.querySelector('#x-a'), proto.createdCallbackThis,
'\'this\' value of the created callback should be the custom element');
}, 'Test \'this\' value inside created callback.');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
assert_equals(doc.querySelector('#x-element'), proto.attachedCallbackThis,
'\'this\' value of the attached callback should be the custom element');
}, 'Test \'this\' value inside attached callback.');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
var customElement = doc.querySelector('#x-element');
doc.body.removeChild(customElement);
assert_equals(customElement, proto.detachedCallbackThis,
'\'this\' value of the detached callback should be the custom element');
}, 'Test \'this\' value inside detached callback.');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-b', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.setAttribute('class', 'someClass');
assert_equals(customElement, proto.attributeChangedCallbackThis,
'\'this\' value of the attributeChanged callback should be the custom element');
}, 'Test \'this\' value inside attributeChanged callback.');
</script>
</body>
</html>

View file

@ -0,0 +1,159 @@
<!DOCTYPE html>
<html>
<head>
<title>Attached callback of a custom element should be called if element is moved</title>
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="attached callback ... must be enqueued whenever custom element is inserted into a document and this document has a browsing context.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-a', {prototype: proto});
var customElement = doc.createElement('x-a');
doc.body.appendChild(customElement);
assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached ' +
'should not be called in documents that do not have a browsing context');
var divElement = doc.createElement('div');
doc.body.appendChild(divElement);
divElement.appendChild(customElement);
assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached ' +
'should not be called in documents that do not have a browsing context');
}, 'Test attached callback if moving custom element inside document ' +
'without browsing context');
testInIFrame('../../resources/blank.html', function(docWithBrowsingContext) {
var docNoBrowsingContext = newHTMLDocument();
var proto1 = newHTMLElementPrototype();
docNoBrowsingContext.registerElement('x-b', {prototype: proto1});
var customElement = docNoBrowsingContext.createElement('x-b');
docNoBrowsingContext.body.appendChild(customElement);
assert_equals(proto1.attachedCallbackCalledCounter, 0,
'Callback attached should not be called ' +
'in documents that do not have a browsing context');
var proto2 = newHTMLElementPrototype();
docWithBrowsingContext.registerElement('x-b', {prototype: proto2});
docWithBrowsingContext.body.appendChild(customElement);
assert_equals(proto1.attachedCallbackCalledCounter, 1,
'Callback attached should be called in documents with browsing context');
assert_equals(proto2.attachedCallbackCalledCounter, 0,
'Callback attached, defined in receiving document, should not be called');
}, 'Test attached callback if moving custom element from ' +
'document without browsing context to document with browsing context');
testInIFrame('../../resources/blank.html', function(docWithBrowsingContext) {
var proto1 = newHTMLElementPrototype();
docWithBrowsingContext.registerElement('x-c', {prototype: proto1});
var customElement = docWithBrowsingContext.createElement('x-c');
docWithBrowsingContext.body.appendChild(customElement);
assert_equals(proto1.attachedCallbackCalledCounter, 1,
'Callback attached should be called in documents with browsing context');
var docNoBrowsingContext = newHTMLDocument();
var proto2 = newHTMLElementPrototype();
docNoBrowsingContext.registerElement('x-c', {prototype: proto2});
docNoBrowsingContext.body.appendChild(customElement);
assert_equals(proto1.attachedCallbackCalledCounter, 1, 'Callback attached should not be called ' +
'in documents that do not have a browsing context');
assert_equals(proto2.attachedCallbackCalledCounter, 0,
'Callback attached, defined in receiving document, should not be called');
}, 'Test attached callback if moving custom element from ' +
'document with browsing context to document without browsing context');
testInIFrame('../../resources/blank.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-d', {prototype: proto});
var customElement = doc.createElement('x-d');
doc.body.appendChild(customElement);
assert_equals(proto.attachedCallbackCalledCounter, 1,
'Callback attached should be called in documents with browsing context');
var divElement = doc.createElement('div');
doc.body.appendChild(divElement);
divElement.appendChild(customElement);
assert_equals(proto.attachedCallbackCalledCounter, 2,
'Callback attached should be called in documents with browsing context');
}, 'Test attached callback if moving custom element inside document ' +
'with browsing context');
testInIFrame('../../resources/blank.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-e', {prototype: proto});
var customElement = doc.createElement('x-e');
doc.body.appendChild(customElement);
assert_equals(proto.attachedCallbackCalledCounter, 1,
'Callback attached should be called in documents with browsing context');
var divElement = doc.createElement('div');
divElement.appendChild(customElement);
assert_equals(proto.attachedCallbackCalledCounter, 1,
'Callback attached should not be called if element is not appended to the document');
doc.body.appendChild(divElement);
assert_equals(proto.attachedCallbackCalledCounter, 2,
'Callback attached should be called in documents with browsing context');
}, 'Test attached callback if indirectly moving custom element inside document ' +
'with browsing context');
var moveTest = async_test('Test attached callback if moving custom element from ' +
'document with browsing context to document with browsing context');
moveTest.step(function() {
var iframe1 = newIFrame('../../resources/blank.html');
iframe1.onload = moveTest.step_func(function() {
var doc1 = iframe1.contentDocument;
// register custom element type
var proto1 = newHTMLElementPrototype();
doc1.registerElement('x-f', {prototype: proto1});
// create custom element
var customElement = doc1.createElement('x-f');
doc1.body.appendChild(customElement);
assert_equals(proto1.attachedCallbackCalledCounter, 1,
'Callback attached should be called in documents with browsing context');
// create second iframe
var iframe2 = newIFrame('../../resources/x-element.html');
iframe2.onload = moveTest.step_func(function() {
var doc2 = iframe2.contentDocument;
// register custom element type
var proto2 = newHTMLElementPrototype();
doc2.registerElement('x-f', {prototype: proto2});
// move element
doc2.body.appendChild(customElement);
assert_equals(proto1.attachedCallbackCalledCounter, 2,
'Callback attached should be called in documents with browsing context');
assert_equals(proto2.attachedCallbackCalledCounter, 0,
'Callback attached, defined in receiving document, should not be called');
// test clean up
iframe1.remove();
iframe2.remove();
moveTest.done();
});
});
});
</script>
</body>
</html>

View file

@ -0,0 +1,117 @@
<!DOCTYPE html>
<html>
<head>
<title>Attached callback of a custom element should be called </title>
<meta name="timeout" content="long">
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="attached callback ... must be enqueued whenever custom element is inserted into a document and this document has a browsing context.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto});
var customElement = new GeneratedConstructor();
assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached ' +
'should not be called in documents that do not have a browsing context');
}, 'Test attached callback if custom element is instantiated via constructor. ' +
'Document has no browsing context');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-b', {prototype: proto});
doc.body.innerHTML = '<x-b></x-b>';
assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached ' +
'should not be called in documents that do not have a browsing context');
}, 'Test attached callback if custom element is created via innerHTML property. ' +
'Document has no browsing context');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-c></x-c>';
var proto = newHTMLElementPrototype();
doc.registerElement('x-c', {prototype: proto});
assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached ' +
'should not be called in documents that do not have a browsing context');
}, 'Test attached callback if custom element is created via innerHTML property before ' +
'registration. Document has no browsing context');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-d id="x-d"></x-d>';
var customElement = doc.querySelector('#x-d');
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-d', {prototype: proto});
customElement.constructor.prototype = proto;
assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached should ' +
'not be called for unregistered custom element in document without browsing context');
}, 'Test attached callback if custom element is unregistered');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
assert_equals(proto.attachedCallbackCalledCounter, 1, 'Callback attached should be ' +
'called in documents with browsing context');
}, 'Test attached callback. Document has browsing context');
testInIFrame('../../resources/blank.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
var x = doc.createElement('x-element');
assert_equals(proto.attachedCallbackCalledCounter, 0, 'Callback attached should not ' +
'be called before element is added to document with browsing context');
doc.body.appendChild(x);
assert_equals(proto.attachedCallbackCalledCounter, 1, 'Callback attached should be called ' +
'in documents with browsing context');
}, 'Test attached callback. Registered element is created via document.createElement(). ' +
'Document has browsing context');
testInIFrame('../../resources/blank.html', function(doc) {
var x = doc.createElement('x-element');
doc.body.appendChild(x);
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
assert_equals(proto.attachedCallbackCalledCounter, 1, 'Callback attached should ' +
'be called in documents with browsing context');
}, 'Test attached callback. Unregistered element is created via document.createElement(). ' +
'Document has browsing context');
testInIFrame('../../resources/blank.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
doc.body.innerHTML = '<x-element></x-element>';
assert_equals(proto.attachedCallbackCalledCounter, 1, 'Callback attached should ' +
'be called in documents with browsing context');
}, 'Test attached callback. Registered element is created via innerHTML property. ' +
'Document has browsing context');
testInIFrame('../../resources/blank.html', function(doc) {
doc.body.innerHTML = '<x-element></x-element>';
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
assert_equals(proto.attachedCallbackCalledCounter, 1, 'Callback attached should ' +
'be called in documents with browsing context');
}, 'Test attached callback. Unresolved element is created via innerHTML property. ' +
'Document has browsing context');
</script>
</body>
</html>

View file

@ -0,0 +1,229 @@
<!DOCTYPE html>
<html>
<head>
<title>Test attributeChanged callback is called if custom element attribute value is changed</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="attributeChanged callback must be enqueued whenever custom element's attribute is added, changed or removed">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.setAttribute('class', 'someClass');
proto.attributeChangedCallbackCalledCounter = 0;
customElement.setAttribute('class', 'someClass2');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is called if attribute value is changed by method ' +
'setAttribute(). The custom element is created via constructor');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-b', {prototype: proto});
doc.body.innerHTML = '<x-b id="x-b" class="oldValue"></x-b>';
var customElement = doc.querySelector('#x-b');
customElement.setAttribute('class', 'newValue');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
assert_array_equals(proto.attributeChangedCallbackArgs,
['class', 'oldValue', 'newValue'],
'Unexpected callback attributeChanged arguments');
}, 'Test attributeChanged callback arguments if attribute value is changed by method ' +
'setAttribute(). The custom element is created via innerHTML property');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-c', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.setAttribute('class', 'someClass');
proto.attributeChangedCallbackCalledCounter = 0;
customElement.classList.add('someClass3');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is called if attribute value is changed by method ' +
'classList.add(). The custom element is created via constructor');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-d', {prototype: proto});
doc.body.innerHTML = '<x-d id="x-d" class="oldValue"></x-d>';
var customElement = doc.querySelector('#x-d');
customElement.classList.add('newestValue');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
assert_array_equals(proto.attributeChangedCallbackArgs,
['class', 'oldValue', 'oldValue newestValue'],
'Unexpected callback attributeChanged arguments');
}, 'Test attributeChanged callback arguments if attribute value is changed by method ' +
'classList.add(). The custom element is created via innerHTML property');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-e', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.setAttribute('class', 'someClass');
proto.attributeChangedCallbackCalledCounter = 0;
customElement.id = 'someId';
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is called if attribute value is changed as property. ' +
'The custom element is created via constructor');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-f', {prototype: proto});
doc.body.innerHTML = '<x-f id="x-f" class="oldValue"></x-f>';
var customElement = doc.querySelector('#x-f');
customElement.className = 'lastValue';
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
assert_array_equals(proto.attributeChangedCallbackArgs,
['class', 'oldValue', 'lastValue'],
'Unexpected callback attributeChanged arguments');
}, 'Test attributeChanged callback arguments if attribute value is changed as property. ' +
'The custom element is created via innerHTML property');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-g', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.setAttribute('class', 'someClass someSuperClass');
proto.attributeChangedCallbackCalledCounter = 0;
customElement.classList.toggle('someClass');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is called if attribute value is changed by classList.toggle(). ' +
'The custom element is created via constructor');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-h', {prototype: proto});
doc.body.innerHTML = '<x-h id="x-h" class="oldValue lastValue"></x-h>';
var customElement = doc.querySelector('#x-h');
customElement.classList.toggle('lastValue');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
assert_array_equals(proto.attributeChangedCallbackArgs,
['class', 'oldValue lastValue', 'oldValue'],
'Unexpected callback attributeChanged arguments');
}, 'Test attributeChanged callback arguments if attribute value is changed by classList.toggle(). ' +
'The custom element is created via innerHTML property');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-i id="x-i" class="oldValue"></x-i>';
var customElement = doc.querySelector('#x-i');
customElement.setAttribute('class', 'newValue');
customElement.setAttribute('class', 'newestValue');
var proto = newHTMLElementPrototype();
doc.registerElement('x-i', {prototype: proto});
assert_equals(proto.attributeChangedCallbackCalledCounter, 0,
'Callback attributeChanged should not be called');
customElement.setAttribute('class', 'rightValue');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should not be called');
assert_array_equals(proto.attributeChangedCallbackArgs,
['class', 'newestValue', 'rightValue'],
'Unexpected callback attributeChanged arguments');
}, 'Test attributeChanged callback is not called if custom element is not registered');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
var customElement = doc.querySelector('#x-element');
customElement.setAttribute('class', 'firstValue');
customElement.setAttribute('class', 'secondValue');
assert_equals(proto.attributeChangedCallbackCalledCounter, 2, 'Callback ' +
'attributeChanged should be called after call to setAttribute()');
customElement.classList.add('someClass3');
assert_equals(proto.attributeChangedCallbackCalledCounter, 3, 'Callback ' +
'attributeChanged should be called after call to classList.add()');
customElement.id = 'someId';
assert_equals(proto.attributeChangedCallbackCalledCounter, 4, 'Callback ' +
'attributeChanged should be called after changing attribute as property');
}, 'Test attributeChanged callback is called if attribute value is changed. ' +
'The document has browsing context');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
var customElement = doc.querySelector('#x-element');
customElement.setAttribute('class', 'firstValue');
customElement.setAttribute('class', 'secondValue');
assert_array_equals(proto.attributeChangedCallbackArgs,
['class', 'firstValue', 'secondValue'],
'Unexpected callback attributeChanged arguments after call to setAttribute()');
customElement.classList.add('newestValue');
assert_array_equals(proto.attributeChangedCallbackArgs,
['class', 'secondValue', 'secondValue newestValue'],
'Unexpected callback attributeChanged arguments after call to classList.add()');
customElement.className = 'lastValue';
assert_array_equals(proto.attributeChangedCallbackArgs,
['class', 'secondValue newestValue', 'lastValue'],
'Unexpected callback attributeChanged arguments after changing attribute as property');
}, 'Test attributeChanged callback arguments if attribute value is changed. ' +
'The document has browsing context');
testInIFrame('../../resources/x-element.html', function(doc) {
var customElement = doc.querySelector('#x-element');
customElement.setAttribute('class', 'firstValue');
customElement.setAttribute('class', 'secondValue');
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
customElement.setAttribute('class', 'thirdValue');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
assert_array_equals(proto.attributeChangedCallbackArgs,
['class', 'secondValue', 'thirdValue'],
'Unexpected callback attributeChanged arguments after setAttribute() call');
}, 'Test attributeChanged callback is not called if custom element is not registered. ' +
'The document has browsing context');
</script>
</body>
</html>

View file

@ -0,0 +1,166 @@
<!DOCTYPE html>
<html>
<head>
<title>Test attribute removing to check attributeChanged callback of a custom element</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="attributeChanged callback must be enqueued whenever custom element's attribute is removed">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto});
var customElement = new GeneratedConstructor();
//attributeChangedCallback should be called the first time here
customElement.setAttribute('class', 'someClass');
//attributeChangedCallback should be called the second time here
customElement.removeAttribute('class');
assert_equals(proto.attributeChangedCallbackCalledCounter, 2, 'Callback attributeChanged should be called ' +
'after setAttribute() and removeAttribute() calls');
}, 'Test attributeChanged callback if attribute is removed. ' +
'The custom element created via constructor');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(element) {
var obj = doc.createElement(element);
var proto = newCustomElementPrototype(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement('x-' + element + '-' + element + '-1', {
prototype: proto,
extends: element
});
var customElement = new GeneratedConstructor();
//attributeChangedCallback should be called the first time here
customElement.setAttribute('class', 'someClass');
//attributeChangedCallback should be called the second time here
customElement.removeAttribute('class');
assert_equals(proto.attributeChangedCallbackCalledCounter, 2,
'Callback attributeChanged should be called ' +
'after setAttribute() and removeAttribute() calls for "' + element + '"');
});
}, 'Test attributeChanged callback if attribute is removed. ' +
'The custom element created via constructor and extends HTML element');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-b', {prototype: proto});
doc.body.innerHTML = '<x-b id="x-b" class="theClass"></x-b>';
var customElement = doc.querySelector('#x-b');
customElement.removeAttribute('class');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called ' +
'after removeAttribute() call');
}, 'Test attributeChanged callback if attribute is removed. ' +
'The custom element created via innerHTML property');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-c', {prototype: proto});
doc.body.innerHTML = '<x-c id="x-c" class="theClass"></x-c>';
var customElement = doc.querySelector('#x-c');
customElement.removeAttribute('class');
assert_equals(proto.attributeChangedCallbackArgs[2], null,
'Removing an attribute should invoke ' +
'the attributeChanged callback with a null new value');
assert_array_equals(proto.attributeChangedCallbackArgs,
['class', 'theClass', null],
'Unexpected attributeChanged callback arguments');
}, 'Test attributeChanged callback arguments if attribute is removed. ' +
'The custom element created via innerHTML property');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.body.innerHTML = '<x-d id="x-d" class="theClass"></x-d>';
var customElement = doc.querySelector('#x-d');
// this should not call or enqueue attributeChangedCallback
customElement.setAttribute('class', 'someClass');
// this one should not too
customElement.removeAttribute('class');
assert_equals(proto.attributeChangedCallbackCalledCounter, 0,
'Callback attributeChanged should not be called');
doc.registerElement('x-d', {prototype: proto});
// this call invokes attributeChangedCallback
customElement.setAttribute('name', 'someName');
// and this one
customElement.removeAttribute('name');
assert_equals(proto.attributeChangedCallbackCalledCounter, 2,
'Callback attributeChanged should be called ' +
'after setAttribute() and removeAttribute() calls');
}, 'Test attributeChanged callback is not called if attribute is removed. ' +
'The custom element created via innerHTML property and unresolved at first');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
var customElement = doc.querySelector('#x-element');
customElement.setAttribute('class', 'someClass');
customElement.removeAttribute('class');
assert_equals(proto.attributeChangedCallbackCalledCounter, 2,
'Callback attributeChanged should be called ' +
'after setAttribute() and removeAttribute() calls');
}, 'Test attributeChanged callback is called if attribute is removed. ' +
'The custom element created via constructor and the document has browsing context');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
doc.body.innerHTML = '<x-element id="x-element" class="theClass"></x-element>';
var customElement = doc.querySelector('#x-element');
customElement.removeAttribute('class');
assert_equals(proto.attributeChangedCallbackArgs[2], null,
'Removing an attribute should invoke ' +
'the attributeChanged callback with a null new value');
assert_array_equals(proto.attributeChangedCallbackArgs,
['class', 'theClass', null],
'Unexpected attributeChanged callback arguments');
}, 'Test attributeChanged callback arguments if attribute is removed. ' +
'The custom element created via innerHTML property and the document has browsing context');
testInIFrame('../../resources/x-element.html', function(doc) {
var customElement = doc.querySelector('#x-element');
// this should not call or enqueue attributeChangedCallback
customElement.setAttribute('name', 'someName');
// this one too
customElement.removeAttribute('name');
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
assert_equals(proto.attributeChangedCallbackCalledCounter, 0,
'Callback attributeChanged should not be called');
// this call invokes attributeChangedCallback
customElement.setAttribute('class', 'someClass');
// this call invokes attributeChangedCallback at second time
customElement.removeAttribute('class');
assert_equals(proto.attributeChangedCallbackCalledCounter, 2,
'Callback attributeChanged should be called ' +
'after setAttribute() and removeAttribute() calls');
}, 'Test attributeChanged callback if attribute is removed. ' +
'The custom element created via innerHTML property and unresolved at first. ' +
'The document has browsing context');
</script>
</body>
</html>

View file

@ -0,0 +1,338 @@
<!DOCTYPE html>
<html>
<head>
<title>Test attributeChanged callback is called if custom element attribute value is set</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="attributeChanged callback ... must be enqueued whenever custom element's attribute is added, changed or removed.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.setAttribute('class', 'someClass');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is called if attribute value is set by method ' +
'setAttribute(). The custom element is created via constructor.');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-b', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.setAttribute('class', 'someClass');
assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
'Unexpected callback attributeChanged arguments');
}, 'Test attributeChanged callback arguments if attribute value is set by method ' +
'setAttribute(). The custom element is created via constructor.');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-c', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.classList.add('someClass');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is called if attribute value is set by method ' +
'classList.add(). The custom element is created via constructor.');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-d', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.classList.add('someClass');
assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
'Unexpected callback attributeChanged arguments');
}, 'Test attributeChanged callback arguments if attribute value is set by method ' +
'classList.add(). The custom element is created via constructor.');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-e', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.classList.toggle('someClass');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is called if attribute value is set by method ' +
'classList.toggle(). The custom element is created via constructor.');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-f', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.classList.toggle('someClass');
assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
'Unexpected callback attributeChanged arguments');
}, 'Test attributeChanged callback arguments if attribute value is set by method ' +
'classList.toggle(). The custom element is created via constructor.');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-g', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.className = 'someClass';
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is called if attribute value is set as property. ' +
'The custom element is created via constructor.');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-h', {prototype: proto});
var customElement = new GeneratedConstructor();
customElement.className = 'someClass';
assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
'Unexpected callback attributeChanged arguments');
}, 'Test attributeChanged callback arguments if attribute value is set as property. '+
'The custom element is created via constructor.');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-i', {prototype: proto});
doc.body.innerHTML = '<x-i id="x-i" class="theClass"></x-i>';
assert_equals(proto.attributeChangedCallbackCalledCounter, 0,
'Callback attributeChanged should not be called');
}, 'Test attributeChanged callback is not called if attribute value is specified in HTML. '+
'The custom element is created via innerHTML property.');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-j', {prototype: proto});
doc.body.innerHTML = '<x-j id="x-j" class="theClass"></x-j>';
var customElement = doc.querySelector('#x-j');
customElement.setAttribute('class', 'someClass');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is called if attribute value is set by method setAttribute(). '+
'The custom element is created via innerHTML property.');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-k', {prototype: proto});
doc.body.innerHTML = '<x-k id="x-k"></x-k>';
var customElement = doc.querySelector('#x-k');
customElement.setAttribute('class', 'someClass');
assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
'Unexpected callback attributeChanged arguments');
}, 'Test attributeChanged callback arguments if attribute value is set by method setAttribute(). '+
'The custom element is created via innerHTML property.');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-l id="x-l"></x-l>';
var customElement = doc.querySelector('#x-l');
// this call shouldn't invoke or enqueue the callback
customElement.setAttribute('class', 'someClass');
var proto = newHTMLElementPrototype();
doc.registerElement('x-l', {prototype: proto});
assert_equals(proto.attributeChangedCallbackCalledCounter, 0,
'Callback attributeChanged should not be called');
// this one should
customElement.setAttribute('class', 'someClass2');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is not called if attribute value of unresolved element '+
'is set by method setAttribute().');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(element) {
var obj = doc.createElement(element);
var proto = newCustomElementPrototype(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement('x-' + element + '-' + element + '-1', {
prototype: proto,
extends: element
});
var customElement = new GeneratedConstructor();
customElement.setAttribute('class', 'someClass');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
});
}, 'Test attributeChanged callback of the custom element that extends some HTML element. ' +
'Test that the callback is called');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(element) {
var obj = doc.createElement(element);
var proto = newCustomElementPrototype(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement('x-' + element + '-' + element + '-1-1', {
prototype: proto,
extends: element
});
var customElement = new GeneratedConstructor();
customElement.classList.add('someClass');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
});
}, 'Test attributeChanged callback is called if attribute is set by method classList.add(). '+
'The custom element extends some HTML element.');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(element) {
var obj = doc.createElement(element);
var proto = newCustomElementPrototype(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement('x-' + element + '-' + element + '-2', {
prototype: proto,
extends: element
});
var customElement = new GeneratedConstructor();
customElement.setAttribute('class', 'someClass');
assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
'Unexpected callback attributeChanged arguments');
});
}, 'Test attributeChanged callback arguments if attribute value is set by method setAttribute(). '+
'The custom element extends some HTML element.');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
var customElement = doc.querySelector('#x-element');
customElement.setAttribute('class', 'someClass');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is called if attribute value is set by method setAttribute(). '+
'The document has browsing context.');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
var customElement = doc.querySelector('#x-element');
customElement.setAttribute('class', 'someClass');
assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
'Unexpected callback attributeChanged arguments');
}, 'Test attributeChanged callback arguments if attribute value is set by method setAttribute(). '+
'The document has browsing context.');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
var customElement = doc.querySelector('#x-element');
customElement.classList.add('someClass');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is called if attribute value is set by method classList.add(). '+
'The document has browsing context.');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
var customElement = doc.querySelector('#x-element');
customElement.classList.add('someClass');
assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
'Unexpected callback attributeChanged arguments');
}, 'Test attributeChanged callback arguments if attribute value is set by method classList.add(). '+
'The document has browsing context.');
testInIFrame('../../resources/x-element.html', function(doc) {
var customElement = doc.querySelector('#x-element');
// this call shouldn't invoke or enqueue the callback
customElement.setAttribute('name', 'someName');
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
assert_equals(proto.attributeChangedCallbackCalledCounter, 0,
'Callback attributeChanged should not be called');
// this one should
customElement.setAttribute('class', 'someClass');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
}, 'Test attributeChanged callback is not called if attribute value of unresolved element '+
'is set by method setAttribute(). The document has browsing context.');
testInIFrame('../../resources/x-element.html', function(doc) {
HTML5_ELEMENTS.forEach(function(element) {
var obj = doc.createElement(element);
var proto = newCustomElementPrototype(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement('x-' + element + '-' + element + '-1', {
prototype: proto,
extends: element
});
var customElement = new GeneratedConstructor();
customElement.setAttribute('class', 'someClass');
assert_equals(proto.attributeChangedCallbackCalledCounter, 1,
'Callback attributeChanged should be called');
});
}, 'Test attributeChanged callback is called if attribute value is set by method setAttribute(). '+
'The document has browsing context. The custom element extends some HTML element.');
testInIFrame('../../resources/x-element.html', function(doc) {
HTML5_ELEMENTS.forEach(function(element) {
var obj = doc.createElement(element);
var proto = newCustomElementPrototype(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement('x-' + element + '-' + element + '-2', {
prototype: proto,
extends: element
});
var customElement = new GeneratedConstructor();
customElement.setAttribute('class', 'someClass');
assert_array_equals(proto.attributeChangedCallbackArgs, ['class', null, 'someClass'],
'Unexpected callback attributeChanged arguments');
});
}, 'Test attributeChanged callback arguments if attribute value is set by method setAttribute(). '+
'The document has browsing context. The custom element extends some HTML element.');
</script>
</body>
</html>

View file

@ -0,0 +1,87 @@
<!DOCTYPE html>
<html>
<head>
<title>The custom element prototype must be set just prior to invoking created callback</title>
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="The custom element prototype must be set just prior to invoking callback.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.body.innerHTML = '<x-a></x-a>';
doc.registerElement('x-a', {prototype: proto});
assert_equals(proto.createdCallbackThis.constructor.prototype, proto,
'The custom element prototype is incorrect inside created callback');
}, 'Test custom element prototype inside created callback when custom element is created ' +
'in HTML before registration of a custom element');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-b', {prototype: proto});
doc.body.innerHTML = '<x-b></x-b>';
assert_equals(proto.createdCallbackThis.constructor.prototype, proto,
'The custom element prototype is incorrect inside created callback');
}, 'Test custom element prototype inside created callback when custom element is created ' +
'in HTML after registration of a custom element');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var customElement = doc.createElement('x-c');
doc.body.appendChild(customElement);
doc.registerElement('x-c', {prototype: proto});
assert_equals(proto.createdCallbackThis.constructor.prototype, proto,
'The custom element prototype is incorrect inside created callback');
}, 'Test custom element prototype inside created callback when custom element is created ' +
'via document.createElement() before registration of a custom element');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-d', {prototype: proto});
var customElement = doc.createElement('x-d');
doc.body.appendChild(customElement);
assert_equals(proto.createdCallbackThis.constructor.prototype, proto,
'The custom element prototype is incorrect inside created callback');
}, 'Test custom element prototype inside created callback when custom element is created ' +
'via document.createElement() after registration of a custom element');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-e', {prototype: proto});
var customElement = new GeneratedConstructor();
assert_equals(proto.createdCallbackThis.constructor.prototype, proto,
'The custom element prototype is incorrect inside created callback');
}, 'Test custom element prototype inside created callback when custom element is created ' +
'via constructor returned by method registerElement');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
assert_equals(proto.createdCallbackThis.constructor.prototype, proto,
'The custom element prototype is incorrect inside created callback');
}, 'Test custom element prototype inside created callback when custom element is created ' +
'by UA parser before registration of a custom element');
</script>
</body>
</html>

View file

@ -0,0 +1,242 @@
<!DOCTYPE html>
<html>
<head>
<title>All other callbacks must not be enqueued until after the created callback's invocation had started</title>
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="All other callbacks must not be enqueued until after the created callback's invocation had started.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
function newPrototypeWithCallbackLog() {
var proto = Object.create(HTMLElement.prototype);
proto.callbackLog = [];
proto.createdCallback = function() {
proto.callbackLog.push('created');
};
proto.attachedCallback = function() {
proto.callbackLog.push('attached');
};
proto.attributeChangedCallback = function() {
proto.callbackLog.push('attributeChanged');
};
proto.detachedCallback = function() {
proto.callbackLog.push('detached');
};
return proto;
}
testInIFrame('../../resources/blank.html', function(doc) {
var proto = newPrototypeWithCallbackLog();
doc.registerElement('x-a', {prototype: proto});
doc.body.innerHTML = '<x-a></x-a>';
assert_equals(proto.callbackLog[0], 'created', 'The callback ' +
proto.callbackLog[0] + ' should be enqueued after created callback');
assert_in_array('attached', proto.callbackLog, 'The callback ' +
'attached should be called');
}, 'Test attached callback is enqueued after created callback');
testInIFrame('../../resources/blank.html', function(doc) {
var proto = newPrototypeWithCallbackLog();
doc.registerElement('x-b', {prototype: proto});
doc.body.innerHTML = '<x-b id="x-b"></x-b>';
var customElement = doc.querySelector('#x-b');
customElement.setAttribute('key', 'value');
assert_equals(proto.callbackLog[0], 'created', 'The callback ' +
proto.callbackLog[0] + ' should not be enqueued before created callback');
assert_in_array('attributeChanged', proto.callbackLog,
'The callback attributeChanged should be called');
}, 'Test attributeChanged callback is enqueued after created callback. ' +
'Document has browsing context');
test(function() {
var doc = newHTMLDocument();
var proto = newPrototypeWithCallbackLog();
doc.registerElement('x-c', {prototype: proto});
doc.body.innerHTML = '<x-c id="x-c"></x-c>';
var customElement = doc.querySelector('#x-c');
customElement.setAttribute('key', 'value');
assert_equals(proto.callbackLog[0], 'created', 'The callback ' +
proto.callbackLog[0] + ' should not be enqueued before created callback');
assert_in_array('attributeChanged', proto.callbackLog,
'The callback attributeChanged should be called');
}, 'Test attributeChanged callback is enqueued after created callback. ' +
'Document has no browsing context');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newPrototypeWithCallbackLog();
doc.registerElement('x-element', {prototype: proto});
var customElement = doc.querySelector('#x-element');
doc.body.removeChild(customElement);
assert_equals(proto.callbackLog[0], 'created', 'The callback ' +
proto.callbackLog[0] + ' should not be enqueued before created callback');
assert_in_array('detached', proto.callbackLog,
'The callback detached should be called');
}, 'Test detached callback is enqueued after created callback.');
test(function() {
var doc = newHTMLDocument();
var proto1 = newPrototypeWithCallbackLog();
proto1.createdCallback = function() {
proto1.callbackLog.push('created');
var xe = doc.querySelector('#x-e');
xe.setAttribute('key', 'value');
};
var proto2 = newPrototypeWithCallbackLog();
doc.registerElement('x-d', {prototype: proto1});
doc.registerElement('x-e', {prototype: proto2});
doc.body.innerHTML = '<x-d><x-e id="x-e"></x-e></x-d>';
assert_array_equals(proto2.callbackLog, ['created'],
'attributeChanged callback should not be enqueued before created callback');
}, 'Test attributeChanged callback is not enqueued before created callback started. ' +
'Document has no browsing context');
testInIFrame('../../resources/blank.html', function(doc) {
var proto1 = newPrototypeWithCallbackLog();
proto1.createdCallback = function() {
proto1.callbackLog.push('created');
var xe = doc.querySelector('#x-g');
xe.setAttribute('key', 'value');
};
var proto2 = newPrototypeWithCallbackLog();
doc.registerElement('x-f', {prototype: proto1});
doc.registerElement('x-g', {prototype: proto2});
doc.body.innerHTML = '<x-f><x-g id="x-g"></x-g></x-f>';
assert_array_equals(proto2.callbackLog, ['created', 'attached'],
'attributeChanged callback should not be called before created callback started');
}, 'Test attributeChanged callback is not enqueued before created callback started. ' +
'Document has browsing context');
test(function() {
var doc = newHTMLDocument();
var proto = newPrototypeWithCallbackLog();
proto.createdCallback = function() {
proto.callbackLog.push('created');
this.setAttribute('key', 'value');
};
doc.registerElement('x-h', {prototype: proto});
doc.body.innerHTML = '<x-h></x-h>';
assert_array_equals(proto.callbackLog, ['created', 'attributeChanged'],
'attributeChanged callback should be enqueued after created callback');
}, 'Test attributeChanged callback is enqueued after created callback started. ' +
'Document has no browsing context');
testInIFrame('../../resources/blank.html', function(docWithBrowsingContext) {
var docNoBrowsingContext = newHTMLDocument();
var proto1 = newPrototypeWithCallbackLog();
var proto2 = newPrototypeWithCallbackLog();
proto1.createdCallback = function() {
proto1.callbackLog.push('created');
var xk = docNoBrowsingContext.querySelector('#x-k');
assert_equals(proto2.callbackLog.length, 0, 'Created callback for x-k ' +
'should not be called before created callback for x-i');
docWithBrowsingContext.body.appendChild(xk);
};
docNoBrowsingContext.registerElement('x-i', {prototype: proto1});
docNoBrowsingContext.registerElement('x-k', {prototype: proto2});
docNoBrowsingContext.body.innerHTML = '<x-i><x-k id="x-k"></x-k></x-i>';
// Though at the moment of inserting <x-k> into docWithBrowsingContext
// created callback is not called for <x-k> yet, attached calback is enqueued
// anyway. Because specification for setting custom element prototype algorithm reads:
// ....
// 3. If ELEMENT is in a document and this document has a browsing context:
// 1. Enqueue attached callback for ELEMENT
//
// Changes in the specification will follow, to reflect this exceptional case.
assert_array_equals(proto2.callbackLog, ['created', 'attached'],
'attached callback should be enqueued when custom element prototype is set');
}, 'Test attached callback is enqueued after created callback, but before created callback had started');
testInIFrame('../../resources/blank.html', function(docWithBrowsingContext) {
var docNoBrowsingContext = newHTMLDocument();
var proto = newPrototypeWithCallbackLog();
proto.createdCallback = function() {
proto.callbackLog.push('created');
docWithBrowsingContext.body.appendChild(this);
};
docNoBrowsingContext.registerElement('x-l', {prototype: proto});
docNoBrowsingContext.body.innerHTML = '<x-l></x-l>';
assert_array_equals(proto.callbackLog, ['created', 'attached'],
'attached callback should be enqueued after created callback had started');
}, 'Test attached callback is enqueued after created callback had started');
testInIFrame('../../resources/blank.html', function(doc) {
var proto1 = newPrototypeWithCallbackLog();
var proto2 = newPrototypeWithCallbackLog();
proto1.createdCallback = function() {
proto1.callbackLog.push('created');
var xn = doc.querySelector('#x-n');
assert_equals(proto2.callbackLog.length, 0, 'Created callback for x-n ' +
'should not be called before created callback for x-m');
this.removeChild(xn);
};
doc.registerElement('x-m', {prototype: proto1});
doc.registerElement('x-n', {prototype: proto2});
doc.body.innerHTML = '<x-m><x-n id="x-n"></x-n></x-m>';
assert_array_equals(proto2.callbackLog, ['created'],
'detached callback should not be enqueued before created callback had started');
}, 'Test detached callback is not enqueued before created callback had started');
testInIFrame('../../resources/blank.html', function(doc) {
var proto = newPrototypeWithCallbackLog();
proto.createdCallback = function() {
proto.callbackLog.push('created');
this.remove();
};
doc.registerElement('x-o', {prototype: proto});
doc.body.innerHTML = '<x-o></x-o>';
assert_array_equals(proto.callbackLog, ['created', 'attached', 'detached'],
'detached callback should be enqueued after created callback had started');
}, 'Test detached callback is enqueued after created callback had started');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newPrototypeWithCallbackLog();
doc.registerElement('x-element', {prototype: proto});
// Though at the moment of inserting <x-element> into the document
// created callback is not called for <x-element> yet, attached calback is enqueued
// anyway. Because specification for setting custom element prototype algorithm reads:
// ....
// 3. If ELEMENT is in a document and this document has a browsing context:
// 1. Enqueue attached callback for ELEMENT
//
// Changes in the specification will follow, to reflect this exceptional case.
assert_array_equals(proto.callbackLog, ['created', 'attached'],
'attached callback should be enqueued when custom element prototype is set');
}, 'Test attached callback is enqueued after created callback after registration ' +
'of custom element type');
</script>
</body>
</html>

View file

@ -0,0 +1,167 @@
<!DOCTYPE html>
<html>
<head>
<title>Created callback of a custom element should be invoked after custom element instance is created and its definition is registered</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="CREATED callback is invoked after custom element instance is created and its definition is registered">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto});
assert_equals(proto.createdCallbackCalledCounter, 0,
'Callback created should not be called before element instance was created');
var customElement = new GeneratedConstructor();
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be called after element instance was created');
}, 'Test created callback when custom element is created by constructor');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-b', {prototype: proto});
assert_equals(proto.createdCallbackCalledCounter, 0,
'Callback created should not be called before element instance was created');
doc.body.innerHTML = '<x-b></x-b>';
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be called after element instance was created');
}, 'Test created callback when custom element is created in HTML');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-c', {prototype: proto});
assert_equals(proto.createdCallbackCalledCounter, 0,
'Callback created should not be called before element instance was created');
doc.body.innerHTML = '<div><x-c></x-c></div>';
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be called after element instance was created');
}, 'Test created callback when custom element is created in HTML as descendant of ' +
'another element');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-d', {prototype: proto});
assert_equals(proto.createdCallbackCalledCounter, 0,
'Callback created should not be called before element instance was created');
var customElement = doc.createElement('x-d');
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be called after element instance was created');
}, 'Test created callback when custom element is created by createElement');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.body.innerHTML = '<x-e></x-e>';
doc.registerElement('x-e', {prototype: proto});
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be called after element instance was created');
}, 'Test created callback when custom element is created in HTML before ' +
'registration of a custom element');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-f', {prototype: proto});
doc.body.innerHTML = '<x-f-unresolved id="x-f-unresolved"></x-f-unresolved>';
assert_equals(proto.createdCallbackCalledCounter, 0,
'Callback created should not be called if custom element is unresolved');
var customElement = doc.querySelector('#x-f-unresolved');
customElement.constructor.prototype = proto;
assert_equals(proto.createdCallbackCalledCounter, 0,
'Created callback should not be called if custom element is unresolved');
}, 'Test created callback if custom element is unresolved.');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
assert_equals(proto.createdCallbackCalledCounter, 1, 'Callback created should be called');
}, 'Test created callback is called after custom element is created and registered. ' +
'Document has browsing context');
testInIFrame('../../resources/register-and-create-custom-element.html', function(doc) {
assert_equals(doc.querySelector('#log').textContent, 'Created callback was called',
'Callback created should be called');
}, 'Test created callback is called after custom element is registered and created. ' +
'Document has browsing context');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var customElement = doc.createElement('x-g');
doc.registerElement('x-g', {prototype: proto});
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be called after registration of custom element');
}, 'Test created callback when custom element is created by createElement '+
'before registration of a custom element');
test(function(){
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-h', {prototype: proto});
var customElement1 = new GeneratedConstructor();
assert_equals(proto.createdCallbackCalledCounter, 1, 'Callback created should be called');
var customElement2 = doc.createElement('x-h');
assert_equals(proto.createdCallbackCalledCounter, 2,
'Callback created should be called after element instance was created');
doc.body.innerHTML = '<x-h></x-h>';
assert_equals(proto.createdCallbackCalledCounter, 3,
'Callback created should be called after element instance was created');
doc.body.innerHTML = '<div><x-h></x-h></div>';
assert_equals(proto.createdCallbackCalledCounter, 4,
'Callback created should be called after element instance was created');
}, 'Test created callback. Create several custom elements using different ways');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
var GeneratedConstructor = doc.registerElement('x-element', {prototype: proto});
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be called for custom element in loaded document');
var customElement2 = new GeneratedConstructor();
assert_equals(proto.createdCallbackCalledCounter, 2,
'Callback created should be called after element instance was created');
var customElement3 = doc.createElement('x-element');
assert_equals(proto.createdCallbackCalledCounter, 3,
'Callback created should be called after element instance was created');
doc.body.innerHTML = '<x-element></x-element>';
assert_equals(proto.createdCallbackCalledCounter, 4,
'Callback created should be called after element instance was created');
doc.body.innerHTML = '<div><x-element></x-element></div>';
assert_equals(proto.createdCallbackCalledCounter, 5,
'Callback created should be called after element instance was created');
}, 'Test created callback. Create several custom elements using different ways. ' +
'Document has browsing context');
</script>
</body>
</html>

View file

@ -0,0 +1,130 @@
<!DOCTYPE html>
<html>
<head>
<title>Test detached callback of a custom element when moving custom element between different documents</title>
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="detached callback ... must be enqueued whenever custom element is removed from the document and this document has a browsing context.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-a', {prototype: proto});
var customElement = doc.createElement('x-a');
doc.body.appendChild(customElement);
var divElement = doc.createElement('div');
doc.body.appendChild(divElement);
divElement.appendChild(customElement);
assert_equals(proto.detachedCallbackCalledCounter, 0,
'Callback detached should not be called in document without a browsing context');
}, 'Test detached callback is not called if moving custom element inside document ' +
'without browsing context');
testInIFrame('../../resources/blank.html', function(docWithBrowsingContext) {
var docNoBrowsingContext = newHTMLDocument();
var proto1 = newHTMLElementPrototype();
docNoBrowsingContext.registerElement('x-b', {prototype: proto1});
var customElement = docNoBrowsingContext.createElement('x-b');
docNoBrowsingContext.body.appendChild(customElement);
var proto2 = newHTMLElementPrototype();
docWithBrowsingContext.registerElement('x-b', {prototype: proto2});
docWithBrowsingContext.body.appendChild(customElement);
assert_equals(proto1.detachedCallbackCalledCounter, 0,
'Callback detached should not be called in document without browsing context');
assert_equals(proto2.detachedCallbackCalledCounter, 0,
'Callback detached, defined in receiving document, should not be called');
}, 'Test detached callback is not called if moving custom element from ' +
'document without browsing context to document with browsing context');
testInIFrame('../../resources/blank.html', function(docWithBrowsingContext) {
var proto1 = newHTMLElementPrototype();
docWithBrowsingContext.registerElement('x-c', {prototype: proto1});
var customElement = docWithBrowsingContext.createElement('x-c');
docWithBrowsingContext.body.appendChild(customElement);
var docNoBrowsingContext = newHTMLDocument();
var proto2 = newHTMLElementPrototype();
docNoBrowsingContext.registerElement('x-c', {prototype: proto2});
docNoBrowsingContext.body.appendChild(customElement);
assert_equals(proto1.detachedCallbackCalledCounter, 1,
'Callback detached should be called in documents with browsing context');
assert_equals(proto2.detachedCallbackCalledCounter, 0,
'Callback detached, defined in receiving document, should not be called');
}, 'Test detached callback if moving custom element from ' +
'document with browsing context to document without browsing context');
testInIFrame('../../resources/blank.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-d', {prototype: proto});
var customElement = doc.createElement('x-d');
doc.body.appendChild(customElement);
var divElement = doc.createElement('div');
doc.body.appendChild(divElement);
divElement.appendChild(customElement);
assert_equals(proto.detachedCallbackCalledCounter, 1,
'Callback detached should be called in documents with browsing context');
}, 'Test detached callback if moving custom element inside document ' +
'with browsing context');
var moveTest = async_test('Test detached callback if moving custom element from ' +
'document with browsing context to document with browsing context');
moveTest.step(function() {
var iframe1 = newIFrame('../../resources/blank.html');
iframe1.onload = moveTest.step_func(function() {
var doc1 = iframe1.contentDocument;
// register custom element type
var proto1 = newHTMLElementPrototype();
doc1.registerElement('x-e', {prototype: proto1});
// create custom element
var customElement = doc1.createElement('x-e');
doc1.body.appendChild(customElement);
assert_equals(proto1.detachedCallbackCalledCounter, 0,
'Callback detached should not be called when element is created');
// create second iframe
var iframe2 = newIFrame('../../resources/x-element.html');
iframe2.onload = moveTest.step_func(function() {
var doc2 = iframe2.contentDocument;
// register custom element type
var proto2 = newHTMLElementPrototype();
doc2.registerElement('x-e', {prototype: proto2});
// move element
doc2.body.appendChild(customElement);
assert_equals(proto1.detachedCallbackCalledCounter, 1,
'Callback detached should be called in documents with browsing context');
assert_equals(proto2.detachedCallbackCalledCounter, 0,
'Callback detached, defined in receiving document, should not be called');
// test clean up
iframe1.remove();
iframe2.remove();
moveTest.done();
});
});
});
</script>
</body>
</html>

View file

@ -0,0 +1,147 @@
<!DOCTYPE html>
<html>
<head>
<title>Detached callback of a custom element should not be called if document has no browsing context</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="detached callback ... must be enqueued whenever custom element is removed from the document and this document has a browsing context.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-a', {prototype: proto});
doc.body.innerHTML = '<x-a id="x-a"></x-a>';
var customElement = doc.querySelector('#x-a');
doc.body.removeChild(customElement);
assert_equals(proto.detachedCallbackCalledCounter, 0,
'Callback detached should not be called if the document has no browsing context');
}, 'Test detached callback if custom element is created via innerHTML property. ' +
'Document has no browsing context');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.body.innerHTML = '<x-b id="x-b"></x-b>';
doc.registerElement('x-b', {prototype: proto});
var customElement = doc.querySelector('#x-b');
doc.body.removeChild(customElement);
assert_equals(proto.detachedCallbackCalledCounter, 0,
'Callback detached should not be called if the document has no browsing context');
}, 'Test detached callback if custom element is via innerHTML property before ' +
'registration of a custom element. Document has no browsing context');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-c id="x-c"></x-c>';
var customElement = doc.querySelector('#x-c');
var proto = newHTMLElementPrototype();
customElement.constructor.prototype = proto;
doc.body.removeChild(customElement);
assert_equals(proto.detachedCallbackCalledCounter, 0,
'Callback detached should not be called if the document has no browsing context');
}, 'Test detached callback if custom element is unregistered. ' +
'Document has no browsing context');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-d', {prototype: proto});
doc.body.innerHTML = '<x-d id="x-d"></x-d>';
doc.body.innerHTML = '';
assert_equals(proto.detachedCallbackCalledCounter, 0,
'Callback detached should not be called if the document has no browsing context');
}, 'Test detached callback if removing custom element via innerHTML property. ' +
'Document has no browsing context');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-e', {prototype: proto});
doc.body.innerHTML = '<div id="customParent"><x-e id="x-e"></x-e></div>';
var parent = doc.querySelector('#customParent');
doc.body.removeChild(parent);
assert_equals(proto.detachedCallbackCalledCounter, 0,
'Callback detached should not be called if the document has no browsing context');
}, 'Test detached callback if removing perent of custom element. ' +
'Document has no browsing context');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
doc.registerElement('x-f', {prototype: proto});
doc.body.innerHTML = '<div><x-f id="x-f"></x-f></div>';
doc.body.innerHTML = '';
assert_equals(proto.detachedCallbackCalledCounter, 0,
'Callback detached should not be called if the document has no browsing context');
}, 'Test detached callback if removing perent of custom element via innerHTML property. ' +
'Document has no browsing context');
var loseBrowsingContextTest = async_test('Test detached callback is not called ' +
'if document lose browsing context and custom element is removed');
loseBrowsingContextTest.step(function() {
var iframe = newIFrame('../../resources/x-element.html');
iframe.onload = loseBrowsingContextTest.step_func(function(){
var doc = iframe.contentDocument;
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
var customElement = doc.querySelector('#x-element');
iframe.remove();
customElement.remove();
assert_equals(proto.detachedCallbackCalledCounter, 0,
'Callback detached should not be called if the document has no browsing context');
loseBrowsingContextTest.done();
});
});
var navigateTest = async_test('Test detached callback is not called, ' +
'if document\'s window is navigated to another document and custom element is removed');
navigateTest.step(function() {
var iframe = newIFrame('../../resources/x-element.html');
iframe.onload = navigateTest.step_func(function() {
var doc = iframe.contentDocument;
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
customElement = doc.querySelector('#x-element');
iframe.onload = navigateTest.step_func(function() {
customElement.remove();
assert_equals(proto.detachedCallbackCalledCounter, 0,
'Callback detached should not be called ' +
'if the document has no browsing context');
navigateTest.done();
iframe.remove();
});
iframe.src = '../../resources/blank.html';
});
});
</script>
</body>
</html>

View file

@ -0,0 +1,85 @@
<!DOCTYPE html>
<html>
<head>
<title>Detached callback of a custom element should be called if document has browsing context</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="detached callback ... must be enqueued whenever custom element is removed from the document and this document has a browsing context.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
var customElement = doc.querySelector('#x-element');
doc.body.removeChild(customElement);
assert_equals(proto.detachedCallbackCalledCounter, 1, 'Callback detached should be ' +
'called if custom element is removed from the document with browsing context');
}, 'Test detached callback is called if custom element is removed by method removeChild() ' +
'from document with browsing context');
testInIFrame('../../resources/blank.html', function(doc) {
doc.body.innerHTML = '<div id="x-a-parent"><x-a id="x-a"></x-a></div>';
var proto = newHTMLElementPrototype();
doc.registerElement('x-a', {prototype: proto});
var div = doc.querySelector('#x-a-parent');
doc.body.removeChild(div);
assert_equals(proto.detachedCallbackCalledCounter, 1, 'Callback detached should be ' +
'called if custom element is removed from the document with browsing context');
}, 'Test detached callback is called if ancestor node of custom element ' +
'is removed by method removeChild() from document with browsing context');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
var customElement = doc.querySelector('#x-element');
var div = doc.createElement('div');
doc.body.replaceChild(div, customElement);
assert_equals(proto.detachedCallbackCalledCounter, 1, 'Callback detached should be ' +
'called if custom element is removed from the document with browsing context');
}, 'Test detached callback is called if custom element is removed by method replaceChild() ' +
'from document with browsing context');
testInIFrame('../../resources/blank.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-b', {prototype: proto});
doc.body.innerHTML = '<div id="x-b-parent"><x-b id="x-b"></x-b></div>';
var parent = doc.querySelector('#x-b-parent');
var replacement = doc.createElement('div');
doc.body.replaceChild(replacement, parent);
assert_equals(proto.detachedCallbackCalledCounter, 1, 'Callback detached should be ' +
'called if custom element is removed from the document with browsing context');
}, 'Test detached callback is called if ancestor node of custom element ' +
'is removed by method replaceChild() from document with browsing context');
testInIFrame('../../resources/x-element.html', function(doc) {
var proto = newHTMLElementPrototype();
doc.registerElement('x-element', {prototype: proto});
doc.body.innerHTML = '';
assert_equals(proto.detachedCallbackCalledCounter, 1, 'Callback detached should be ' +
'called if custom element is removed from the document with browsing context');
}, 'Test detached callback is called after changing custom element direct parent ' +
'innerHTML property in the document with browsing context');
testInIFrame('../../resources/blank.html', function(doc) {
doc.body.innerHTML = '<div><x-c></x-c></div>';
var proto = newHTMLElementPrototype();
doc.registerElement('x-c', {prototype: proto});
doc.body.innerHTML = '';
assert_equals(proto.detachedCallbackCalledCounter, 1, 'Callback detached should be ' +
'called if custom element is removed from the document with browsing context');
}, 'Test detached callback is called after changing custom element ancestor ' +
'innerHTML property in the document with browsing context');
</script>
</body>
</html>

View file

@ -0,0 +1,158 @@
<!DOCTYPE html>
<html>
<head>
<title>Changing IS attribute of the custom element must not affect this element's custom element type, after element is instantiated</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="After a custom element is instantiated, changing the value of the IS attribute must not affect this element's custom element type">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#instantiating-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('x-a');
var customElement = new GeneratedConstructor();
doc.registerElement('x-b');
customElement.setAttribute('is', 'x-b');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be x-a');
}, 'Test custom element type, after assigning IS attribute value. ' +
'Element is created by constructor');
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('x-c');
doc.registerElement('x-d');
doc.body.innerHTML = '<x-c id="x-c"></x-c>';
var customElement = doc.querySelector('#x-c');
customElement.setAttribute('is', 'x-d');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be x-c');
}, 'Test custom element type, after assigning IS attribute value. ' +
'Element is created via innerHTML property');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-e id="x-e"></x-e>';
var customElement = doc.querySelector('#x-e');
customElement.setAttribute('is', 'x-f');
var GeneratedConstructor = doc.registerElement('x-e');
doc.registerElement('x-f');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be x-e');
}, 'Test custom element type, after assigning IS attribute value to unresolved element. ' +
'Element is created via innerHTML property');
testInIFrame('../resources/x-element.html', function(doc) {
var GeneratedConstructor = doc.registerElement('x-element');
doc.registerElement('y-element');
var customElement = doc.querySelector('#x-element');
customElement.setAttribute('is', 'y-element');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be x-element');
}, 'Test custom element type, after assigning IS attribute value. ' +
'Element is defined in loaded HTML document');
testInIFrame('../resources/x-element.html', function(doc) {
var customElement = doc.querySelector('#x-element');
customElement.setAttribute('is', 'y-element');
var GeneratedConstructor = doc.registerElement('x-element');
doc.registerElement('y-element');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be x-element');
}, 'Test custom element type, after assigning IS attribute value to unresolved element. ' +
'Element is defined in loaded HTML document');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
if (HTML5_DOCUMENT_ELEMENTS.indexOf(tagName) !== -1) {
return;
}
var name = 'y-' + tagName;
var obj = doc.createElement(tagName);
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {prototype: proto, extends: tagName});
if (HTML5_TABLE_ELEMENTS.indexOf(tagName) !== -1) {
doc.body.innerHTML =
'<table>' +
'<' + tagName + ' id="custom-element" is="' + name + '"></' + tagName + '>' +
'</table>';
} else {
doc.body.innerHTML =
'<' + tagName + ' id="custom-element" is="' + name + '"></' + tagName + '>';
}
var customElement = doc.querySelector('#custom-element');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be '+ name);
var name2 = 'y-a-' + tagName;
doc.registerElement(name2);
customElement.setAttribute('is', name2);
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be ' + name);
});
}, 'Test custom element type after changing IS attribute value. ' +
'Element is HTML5 element with IS attribute referring to custom element type');
test(function() {
var doc = newHTMLDocument();
var localName = 'z-a';
var obj = doc.createElement('a');
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(localName, {prototype: proto, extends: 'a'});
doc.body.innerHTML = '<a id="custom-element" is="' + localName + '"></a>';
var customElement = doc.querySelector('#custom-element');
HTML5_ELEMENTS.forEach(function(tagName) {
var name = 'z-a-' + tagName;
var htmlElement = doc.createElement(tagName);
var htmlElementProto = Object.create(htmlElement.constructor.prototype);
doc.registerElement(name, {prototype: htmlElementProto, extends: tagName});
customElement.setAttribute('is', name);
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be ' + localName);
});
}, 'Test custom element type after changing IS attribute value several times. ' +
'Element is HTML5 element with IS attribute referring to custom element type');
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('x-g');
doc.registerElement('x-h');
doc.body.innerHTML = '<x-g id="x-g" is="x-h"></x-g>';
var customElement = doc.querySelector('#x-g');
customElement.removeAttribute('is');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be x-g');
}, 'Test custom element type, after removing IS attribute value. ' +
'Element is created via innerHTML property');
test(function() {
var doc = newHTMLDocument();
var obj = doc.createElement('a');
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement('x-i', {prototype: proto, extends: 'a'});
doc.body.innerHTML = '<a id="x-i" is="x-i"></a>';
var customElement = doc.querySelector('#x-i');
customElement.removeAttribute('is');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be x-i');
}, 'Test custom element type, after removing IS attribute value. ' +
'Element is HTML5 element with IS attribute referring to custom element type');
</script>
</body>
</html>

View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom element constructor sets value of IS attribute to custom element type, if it is not equal to name</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If TYPE is not the same as NAME, set the value of ELEMENT's IS attribute to TYPE">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#instantiating-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
if (HTML5_DOCUMENT_ELEMENTS.indexOf(tagName) !== -1) {
return;
}
var obj = doc.createElement(tagName);
var name = 'x-a-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {prototype: proto, extends: tagName});
var customElement = new GeneratedConstructor();
assert_equals(customElement.getAttribute('is'), name,
'Value of the IS attribute should be set to type');
});
}, 'Test that the constructor of a type extension sets the IS attribute value to the type');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
if (HTML5_DOCUMENT_ELEMENTS.indexOf(tagName) !== -1) {
return;
}
var name = 'x-b-' + tagName;
var GeneratedConstructor = doc.registerElement(name);
var customElement = new GeneratedConstructor();
assert_false(customElement.hasAttribute('is'),
'IS attribute should not present if local name is the same as type');
});
}, 'Test that the constructor of a custom element does not set the IS attribute if local name is the same as type');
</script>
</body>
</html>

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom element constructor sets local name to the name from custom element definition</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="Set ELEMENT's local name to NAME">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#instantiating-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('x-a');
var customElement = new GeneratedConstructor();
assert_equals(customElement.localName, 'x-a',
'Custom element local name should be equal to the name in custom element definition');
}, 'Custom element constructor sets local name to the name from custom element definition');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-b-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {
prototype: proto,
extends: tagName
});
var customElement = new GeneratedConstructor();
assert_equals(customElement.localName, tagName,
'Custom element local name should be equal to the name in custom element definition');
});
}, 'Custom element constructor sets local name to the name from custom element definition. ' +
'Test constructor of extended HTML element');
</script>
</body>
</html>

View file

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom element constructor sets local namespace to the namespace from custom element definition</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Custom element constructor sets custom element namespace to the namespace in custom element definition">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#instantiating-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('x-a');
var customElement = new GeneratedConstructor();
assert_equals(customElement.namespaceURI, HTML_NAMESPACE,
'Custom element namespace should be equal to namespace in custom element definition');
}, 'Custom element constructor sets namespace to the namespace from custom element definition');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-b-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {
prototype: proto,
extends: tagName
});
var customElement = new GeneratedConstructor();
assert_equals(customElement.namespaceURI, HTML_NAMESPACE,
'Custom element namespace should be equal to namespace in custom element definition');
});
}, 'Custom element constructor sets namespace to the namespace from custom element definition. ' +
'Test constructor of extended HTML element');
</script>
</body>
</html>

View file

@ -0,0 +1,100 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom element constructor sets owner document to the document, where custom element type is registered</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Custom element constructor sets custom element node document to the document, where custom element type is registered">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#instantiating-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('x-a');
var customElement = new GeneratedConstructor();
assert_equals(customElement.ownerDocument, doc,
'Custom element owner document should be the document, where custom element ' +
'type is registered');
}, 'Custom element constructor sets owner document to the document, where custom element ' +
'type is registered');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-b-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {
prototype: proto,
extends: tagName
});
var customElement = new GeneratedConstructor();
assert_equals(customElement.ownerDocument, doc,
'Custom element owner document should be the document, where custom element ' +
'type is registered');
});
}, 'Custom element constructor sets owner document to the document, where custom element ' +
'type is registered. Test constructor of extended HTML element');
test(function() {
var doc = newHTMLDocument();
var sharedRegistryDocument = doc.implementation.createHTMLDocument('Document 2');
var name = 'x-c';
var GeneratedConstructor = doc.registerElement(name);
var customElement = new GeneratedConstructor();
assert_equals(customElement.ownerDocument, doc,
'Custom element owner document should be the document, where custom element ' +
'type is registered');
var name2 = 'x-d';
var GeneratedConstructor2 = sharedRegistryDocument.registerElement(name2);
var customElement2 = new GeneratedConstructor2();
assert_equals(customElement2.ownerDocument, sharedRegistryDocument,
'Custom element owner document should be the document, where custom element ' +
'type is registered');
}, 'Custom element constructor sets owner document to the document, where custom element ' +
'type is registered. Test different documents with shared registry');
test(function() {
var doc = newHTMLDocument();
var sharedRegistryDocument = doc.implementation.createHTMLDocument('Document 2');
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-e-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {
prototype: proto,
extends: tagName
});
var customElement = new GeneratedConstructor();
assert_equals(customElement.ownerDocument, doc,
'Custom element owner document should be the document, where custom element ' +
'type is registered');
var obj2 = sharedRegistryDocument.createElement(tagName);
var name2 = 'x-f-' + tagName;
var proto2 = Object.create(obj2.constructor.prototype);
var GeneratedConstructor2 = sharedRegistryDocument.registerElement(name2, {
prototype: proto2,
extends: tagName
});
var customElement2 = new GeneratedConstructor2();
assert_equals(customElement2.ownerDocument, sharedRegistryDocument,
'Custom element owner document should be the document, where custom element ' +
'type is registered');
});
}, 'Custom element constructor sets owner document to the document, where custom element ' +
'type is registered. Test constructor of extended HTML element for different documents ' +
'with shared registry');
</script>
</body>
</html>

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom element constructor prototype is the prototype object specified in element definition</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Custom element constructor prototype is the prototype object specified in element definition">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = Object.create(SVGElement.prototype);
var GeneratedConstructor = doc.registerElement('x-a', {
prototype: proto,
extends: 'p'
});
assert_true(GeneratedConstructor.prototype === proto,
'Custom element constructor must have the prototype specified in registerElement()');
}, 'If custom element type is registered with prototype, the custom element ' +
'constructor should have the prototype specified in registerElement() call');
</script>
</body>
</html>

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom element prototype is the prototype object specified in element definition</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Custom element prototype is the prototype object specified in element definition">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#instantiating-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = Object.create(SVGElement.prototype);
var GeneratedConstructor = doc.registerElement('x-a', {
prototype: proto,
extends: 'p'
});
var customElement = new GeneratedConstructor();
assert_true(Object.getPrototypeOf(customElement) === proto,
'Custom element instance must have the prototype specified in registerElement()');
}, 'If custom element type is registered with prototype, the custom element ' +
'instance should have the prototype specified in registerElement() call');
</script>
</body>
</html>

View file

@ -0,0 +1,73 @@
<!DOCTYPE html>
<html>
<head>
<title>Instantiation of custom element: custom element type is given as the value of the IS attribute</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="The custom element type is given to a custom element at the time of its instantation in one of the two ways: ... 2. As the value of the IS attribute of the custom element.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#instantiating-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
if (HTML5_DOCUMENT_ELEMENTS.indexOf(tagName) !== -1) {
return;
}
var obj = doc.createElement(tagName);
var name = 'x-a-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {prototype: proto, extends: tagName});
if (HTML5_TABLE_ELEMENTS.indexOf(tagName) !== -1) {
doc.body.innerHTML =
'<table>' +
'<' + tagName + ' id="custom-element" is="' + name + '"></' + tagName + '>' +
'</table>';
} else {
doc.body.innerHTML =
'<' + tagName + ' id="custom-element" is="' + name + '"></' + tagName + '>';
}
var customElement = doc.querySelector('#custom-element');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the type, specified as value of IS attribute');
});
}, 'Instantiation of custom element: custom element type is given as the value of ' +
'the IS attribute');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
if (HTML5_DOCUMENT_ELEMENTS.indexOf(tagName) !== -1) {
return;
}
var obj = doc.createElement(tagName);
var name = 'x-b-' + tagName;
if (HTML5_TABLE_ELEMENTS.indexOf(tagName) !== -1) {
doc.body.innerHTML =
'<table>' +
'<' + tagName + ' id="custom-element" is="' + name + '"></' + tagName + '>' +
'</table>';
} else {
doc.body.innerHTML =
'<' + tagName + ' id="custom-element" is="' + name + '"></' + tagName + '>';
}
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {prototype: proto, extends: tagName});
var customElement = doc.querySelector('#custom-element');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the type, specified as value of IS attribute');
});
}, 'Instantiation of custom element: custom element type is given as the value ' +
'of the IS attribute. Custom element is unresolved at first');
</script>
</body>
</html>

View file

@ -0,0 +1,101 @@
<!DOCTYPE html>
<html>
<head>
<title>Instantiation of custom element: the custom tag must win over the type extension</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="If both types of custom element types are provided at the time of element's instantiation, the custom tag must win over the type extension">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#instantiating-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('x-a');
doc.registerElement('x-b');
doc.body.innerHTML = '<x-a id="x-a" is="x-b"></x-a>';
var customElement = doc.querySelector('#x-a');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the type, specified in the local name of the element');
}, 'Custom element type must be taken from the local name of the element even ' +
'if IS attribute provided.');
test(function() {
var doc = newHTMLDocument();
doc.registerElement('x-d');
doc.body.innerHTML = '<x-c id="x-c" is="x-d"></x-c>';
var customElement = doc.querySelector('#x-c');
var GeneratedConstructor = doc.registerElement('x-c');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the type, specified in the local name of the element');
}, 'Custom element type must be taken from the local name of the element even ' +
'if IS attribute provided. Custom element is unresolved at first');
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('x-f');
doc.body.innerHTML = '<x-f id="x-f" is="x-e"></x-f>';
var customElement = doc.querySelector('#x-f');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the type, specified in local name of the element');
doc.registerElement('x-e');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the type, specified in local name of the element');
}, 'Custom element type must be taken from the local name of the element even if IS ' +
'attribute provided. There\'s no definition for the value of IS attribute at first');
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('x-element');
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-d-' + tagName;
doc.registerElement(name, {
prototype: Object.create(obj.constructor.prototype),
extends: tagName
});
doc.body.innerHTML = '<x-element id="x-element" is="' + name + '"></x-element>';
var customElement = doc.querySelector('#x-element');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the local name of the custom element');
});
}, 'Custom element type must be taken from the local name of the element even ' +
'if IS attribute provided. IS attribute refers to another custom element type, ' +
'which extends HTML5 elements');
test(function() {
var doc = newHTMLDocument();
doc.registerElement('y-element');
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-e-' + tagName;
var id = 'x-e-' + tagName;
doc.registerElement(name, {
prototype: Object.create(obj.constructor.prototype),
extends: tagName
});
doc.body.innerHTML = '<' + name + ' id="' + id + '" is="y-element"></' + name + '>';
var customElement = doc.querySelector('#' + id);
// We have <x-e-a is='y-element'>. Custom element type for this will be
// HTMLElement, not x-e-a (for x-e-a there should be <a is='x-e-a'>...)
assert_class_string(customElement, 'HTMLElement',
'Custom element type should be HTMLElement');
});
}, 'Custom element type must be taken from the local name of the custom element even ' +
'if IS attribute provided. The element extends HTML5 elements, IS attribute refers ' +
'to another custom element type.');
</script>
</body>
</html>

View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<title>Instantiation of custom element: custom element type is given via the local name of the custom element</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="The custom element type is given to a custom element at the time of its instantation in one of the two ways: 1. As the local name of the custom element.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#instantiating-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('x-a');
doc.body.innerHTML = '<x-a id="x-a"></x-a>';
var customElement = doc.querySelector('#x-a');
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the type, specified by the local name of ' +
'the custom element');
}, 'Test custom element type, which is given via the local name of the custom element. ' +
'Custom element created via innerHTML property');
testInIFrame('../resources/x-element.html', function(doc) {
var GeneratedConstructor = doc.registerElement('x-element');
var xelement = doc.querySelector('#x-element');
assert_equals(Object.getPrototypeOf(xelement), GeneratedConstructor.prototype,
'Custom element type should be the type, specified by the local name of ' +
'the custom element');
}, 'Test custom element type, which is given via the local name of the custom element. ' +
'Custom element is defined in loaded HTML document');
</script>
</body>
</html>

View file

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<title>Document.createElement() and Document.createElementNS() create custom element of type, specified by localName argument</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If an element definition with matching localName, namespace, and TYPE is not registered with token's document, set TYPE to localName">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#extensions-to-document-interface-to-instantiate">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var name1 = 'x-a';
var name2 = 'x-b';
var GeneratedConstructor = doc.registerElement(name1);
var customElement = doc.createElement(name1, name2);
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the local name of the custom element');
}, 'Test Document.createElement() creates custom element of type, ' +
'specified by localName argument, if an element definition with matching localName, ' +
'namespace, and type is not registered');
test(function() {
var doc = newHTMLDocument();
var name1 = 'x-c';
var name2 = 'x-d';
var GeneratedConstructor = doc.registerElement(name1);
var customElement = doc.createElementNS(HTML_NAMESPACE, name1, name2);
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the local name of the custom element');
}, 'Test Document.createElementNS() creates custom element of type, ' +
'specified by localName argument, if an element definition with matching ' +
'localName, namespace, and type is not registered');
</script>
</body>
</html>

View file

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<head>
<title>Document.createElement() and Document.createElementNS() create custom element of type, specified by typeExtension argument</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If an element definition with matching localName, namespace, and TYPE is registered then typeExtension is a TYPE">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#extensions-to-document-interface-to-instantiate">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var name1 = 'x-a';
var GeneratedConstructor1 = doc.registerElement(name1);
var name2 = 'x-b';
var GeneratedConstructor2 = doc.registerElement(name2);
var customElement = doc.createElement(name1, name2);
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor2.prototype,
'Custom element type should be the type extension of the custom element');
}, 'Test Document.createElement() creates custom element of type, ' +
'specified by typeExtension argument');
test(function() {
var doc = newHTMLDocument();
var name1 = 'x-c';
var GeneratedConstructor1 = doc.registerElement(name1);
var name2 = 'x-d';
var GeneratedConstructor2 = doc.registerElement(name2);
var customElement = doc.createElementNS(HTML_NAMESPACE, name1, name2);
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor2.prototype,
'Custom element type should be the type extension of the custom element');
}, 'Test Document.createElementNS() creates custom element of type, ' +
'specified by typeExtension argument');
test(function() {
var doc = newHTMLDocument();
var name1 = 'x-e';
var name2 = 'x-f';
var GeneratedConstructor2 = doc.registerElement(name2);
var customElement = doc.createElement(name1, name2);
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor2.prototype,
'Custom element type should be the type extension of the custom element');
}, 'Test Document.createElement() creates custom element of type, ' +
'specified by typeExtension argument. Definition for localName is absent');
test(function() {
var doc = newHTMLDocument();
var name1 = 'x-g';
var name2 = 'x-h';
var GeneratedConstructor2 = doc.registerElement(name2);
var customElement = doc.createElementNS(HTML_NAMESPACE, name1, name2);
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor2.prototype,
'Custom element type should be the type extension of the custom element');
}, 'Test Document.createElementNS() creates custom element of type, ' +
'specified by typeExtension argument. Definition for localName is absent');
</script>
</body>
</html>

View file

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html>
<head>
<title>Document.createElement() and Document.createElementNS() set IS attribute to type</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If TYPE is not the same as localName, set the value of ELEMENT's IS attribute to TYPE">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#extensions-to-document-interface-to-instantiate">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-a-' + tagName;
var proto = Object.create(obj.constructor.prototype);
doc.registerElement(name, {prototype: proto, extends: tagName});
var customElement = doc.createElement(tagName, name);
assert_equals(customElement.getAttribute('is'), name,
'Value of the IS attribute should be set to type by Document.createElement()');
});
}, 'Test Document.createElement() sets the element\'s IS attribute value to type, ' +
'if type is not the same as localName');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var name = 'x-b-' + tagName;
var customElement = doc.createElement(tagName, name);
assert_equals(customElement.getAttribute('is'), name,
'Value of the IS attribute should be set to type by Document.createElement()');
});
}, 'Test Document.createElement() sets the element\'s IS attribute value to type, ' +
'if type is not the same as localName and an element definition with matching ' +
'localName, namespace, and type is not registered');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-c-' + tagName;
var proto = Object.create(obj.constructor.prototype);
doc.registerElement(name, {prototype: proto, extends: tagName});
var customElement = doc.createElementNS(HTML_NAMESPACE, tagName, name);
assert_equals(customElement.getAttribute('is'), name,
'Value of the IS attribute should be set to type by Document.createElementNS()');
});
}, 'Test Document.createElementNS() sets the element\'s IS attribute value to type, ' +
'if type is not the same as localName');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var name = 'x-d-' + tagName;
var customElement = doc.createElementNS(HTML_NAMESPACE, tagName, name);
assert_equals(customElement.getAttribute('is'), name,
'Value of the IS attribute should be set to type by Document.createElementNS()');
});
}, 'Test Document.createElementNS() sets the element\'s IS attribute value to type, ' +
'if type is not the same as localNameand and an element definition with matching ' +
'localName, namespace, and type is not registered ');
</script>
</body>
</html>

View file

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html>
<head>
<title>Document.createElement() sets custom element namespace to HTML Namespace</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Namespace for createElement is HTML Namespace">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#extensions-to-document-interface-to-instantiate">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var name = 'x-a';
doc.registerElement(name);
var customElement = doc.createElement(name);
assert_equals(customElement.namespaceURI, HTML_NAMESPACE,
'Custom element namespace should be HTML Namespace');
}, 'Test Document.createElement() sets custom element namespace to HTML Namespace');
test(function() {
var doc = newHTMLDocument();
var name = 'x-b';
var customElement = doc.createElement(name);
assert_equals(customElement.namespaceURI, HTML_NAMESPACE,
'Custom element namespace should be HTML Namespace');
}, 'Test Document.createElement() sets custom element namespace to HTML Namespace ' +
'and an element definition with matching localName, namespace, and type is not registered');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-c-' + tagName;
var proto = Object.create(obj.constructor.prototype);
doc.registerElement(name, {
prototype: Object.create(proto),
extends: tagName
});
var customElement = doc.createElement(tagName, name);
assert_equals(customElement.namespaceURI, HTML_NAMESPACE,
'Custom element namespace for the element extending ' + tagName +
' should be HTML Namespace');
});
}, 'Document.createElement() sets custom element namespace to HTML Namespace. ' +
'Custom element is extending standard HTML tag');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var name = 'x-d-' + tagName;
var customElement = doc.createElement(tagName, name);
assert_equals(customElement.namespaceURI, HTML_NAMESPACE,
'Custom element namespace for the element with tag name ' + tagName +
' and type name ' + name + ' should be HTML Namespace');
});
}, 'Document.createElement() sets custom element namespace to HTML Namespace. ' +
'Document.createElement() is called with standard HTML tag name and ' +
'type without registered custom element of such type');
</script>
</body>
</html>

View file

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>Document.createElement() and Document.createElementNS() create custom element of type, specified by typeExtension argument</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Let TYPE be typeExtension, or localName if typeExtension is not present">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#extensions-to-document-interface-to-instantiate">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-a-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {prototype: proto, extends: tagName});
var customElement = doc.createElement(tagName, name);
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be ' + name);
});
}, 'Test Document.createElement() creates custom element of type, ' +
'specified by typeExtension argument');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-b-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {prototype: proto, extends: tagName});
var customElement = doc.createElementNS(HTML_NAMESPACE, tagName, name);
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be ' + name);
});
}, 'Test Document.createElementNS() creates custom element of type, ' +
'specified by typeExtension argument');
</script>
</body>
</html>

View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<title>Document.createElement() and Document.createElementNS() create custom element of type, specified by localName argument</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If an element definition with matching localName, namespace, and TYPE is not registered with token's document, set TYPE to localName">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#extensions-to-document-interface-to-instantiate">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test (function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-a-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var customElement = doc.createElement(tagName, name);
assert_equals(Object.getPrototypeOf(customElement), Object.getPrototypeOf(obj),
'Unregistered custom element type should be a local name');
var GeneratedConstructor = doc.registerElement(name, {prototype: proto, extends: tagName});
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Registered custom element type should be the type extension');
});
}, 'If typeExtension is unresolved when createElement called then local name is a type');
test (function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-b-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var customElement = doc.createElementNS(HTML_NAMESPACE, tagName, name);
assert_equals(Object.getPrototypeOf(customElement), Object.getPrototypeOf(obj),
'Custom element type should be a local name');
var GeneratedConstructor = doc.registerElement(name, {prototype: proto, extends: tagName});
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the type extension');
});
}, 'If typeExtension is unresolved when createElementNS called then local name is a type');
</script>
</body>
</html>

View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<title>Document.createElement() and Document.createElementNS() create custom element of type, specified by single localName argument</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Let TYPE be typeExtension, or localName if typeExtension is not present">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#extensions-to-document-interface-to-instantiate">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var name = 'x-a';
var GeneratedConstructor = doc.registerElement(name);
var customElement = doc.createElement(name);
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the local name of the custom element');
}, 'Test Document.createElement() creates custom element of type, ' +
'specified by single localName argument');
test(function() {
var doc = newHTMLDocument();
var name = 'x-b';
var GeneratedConstructor = doc.registerElement(name);
var customElement = doc.createElementNS(HTML_NAMESPACE, name);
assert_equals(Object.getPrototypeOf(customElement), GeneratedConstructor.prototype,
'Custom element type should be the local name of the custom element');
}, 'Test Document.createElementNS() creates custom element of type, ' +
'specified by localName argument. Argument typeExtension is not passed');
</script>
</body>
</html>

View file

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html>
<head>
<title>Document.createElementNS() must enqueue created callback for registered custom element type</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Document.createElementNS() must enqueue created callback for registered custom element type">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#extensions-to-document-interface-to-instantiate">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var name = 'x-a';
doc.registerElement(name, {prototype: proto});
var customElement = doc.createElementNS(HTML_NAMESPACE, name);
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be enqueued by Document.createElementNS()');
}, 'Test Document.createElementNS() without typeExtension argument enqueues created callback');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var name = 'x-b';
doc.registerElement(name, {prototype: proto});
var customElement = doc.createElementNS(HTML_NAMESPACE, name, name);
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be enqueued by Document.createElementNS()');
}, 'Test Document.createElementNS() with typeExtension argument enqueues created callback');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var name = 'x-c';
var customElement = doc.createElementNS(HTML_NAMESPACE, name);
assert_equals(proto.createdCallbackCalledCounter, 0,
'Document.createElementNS() should not enqueue created callback ' +
'for unresolved custom element');
doc.registerElement(name, {prototype: proto});
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be called after custom element is registered');
}, 'Document.createElementNS() should not enqueue created callback ' +
'for unresolved custom element');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-d-' + tagName;
var proto = newCustomElementPrototype(Object.create(obj.constructor.prototype));
doc.registerElement(name, {prototype: proto, extends: tagName});
var customElement = doc.createElementNS(HTML_NAMESPACE, tagName, name);
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be enqueued by Document.createElementNS()');
});
}, 'Test Document.createElementNS() enqueues created callback for custom elements ' +
'that are extensions of HTML5 elements');
</script>
</body>
</html>

View file

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html>
<head>
<title>Document.createElement() must enqueue created callback for registered custom element type</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Document.createElement() must enqueue created callback for registered custom element type">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#extensions-to-document-interface-to-instantiate">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#types-of-callbacks">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var name = 'x-a';
doc.registerElement(name, {prototype: proto});
var customElement = doc.createElement(name);
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be enqueued by Document.createElement()');
}, 'Test Document.createElement() without typeExtension argument enqueues created callback');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var name = 'x-b';
doc.registerElement(name, {prototype: proto});
var customElement = doc.createElement(name, name);
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be enqueued by Document.createElement()');
}, 'Test Document.createElement() with typeExtension argument enqueues created callback');
test(function() {
var doc = newHTMLDocument();
var proto = newHTMLElementPrototype();
var name = 'x-c';
var customElement = doc.createElement(name);
assert_equals(proto.createdCallbackCalledCounter, 0,
'Document.createElement() should not enqueue created callback ' +
'for unresolved custom element');
doc.registerElement(name, {prototype: proto});
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be called after custom element is registered');
}, 'Document.createElement() should not enqueue created callback ' +
'for unresolved custom element');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-d-' + tagName;
var proto = newCustomElementPrototype(Object.create(obj.constructor.prototype));
doc.registerElement(name, {prototype: proto, extends: tagName});
var customElement = doc.createElement(tagName, name);
assert_equals(proto.createdCallbackCalledCounter, 1,
'Callback created should be enqueued by Document.createElement()');
});
}, 'Test Document.createElement() enqueues created callback for custom elements ' +
'that are extensions of HTML5 elements');
</script>
</body>
</html>

View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<title>If prototype has a non-configurable property named constructor, Document.registerElement() throws NotSupportedError</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If PROTOTYPE has a non-configurable property named constructor, throw a NotSupportedError and stop">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#instantiating-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = Object.create(HTMLElement.prototype);
Object.defineProperty(proto, 'constructor', {configurable: false});
assert_throws('NotSupportedError', function() {
doc.registerElement('x-a', {prototype: proto});
}, 'Exception should be thrown in case of attempt to register element ' +
'with a non-configurable property named constructor');
}, 'Test Document.registerElement() throws NotSupportedError ' +
'if prototype has a non-configurable property named constructor');
test(function() {
var doc = newHTMLDocument();
var proto = Object.create(HTMLElement.prototype);
Object.defineProperty(proto, 'constructor', {configurable: true});
try {
doc.registerElement('x-b', {prototype: proto});
} catch (e) {
assert_unreached('Exception should not be thrown in case of attempt to register ' +
'element with a configurable property named constructor');
}
}, 'Test Document.registerElement() accepts prototype with a configurable ' +
'property named constructor without throwing errors');
</script>
</body>
</html>

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head>
<title>If prototype is already an interface prototype object, Document.registerElement() throws a NotSupportedError</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If PROTOTYPE is already an interface prototype object for any interface object, throw a NotSupportedError and stop">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#instantiating-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-a-' + tagName;
assert_throws('NotSupportedError', function() {
doc.registerElement(name, {prototype: obj.constructor.prototype});
}, 'Exception should be thrown in case of attempt to register element ' +
'if prototype is already an interface prototype object (' + name + ')');
});
}, 'Test Document.registerElement() throws NotSupportedError ' +
'if prototype is already an interface prototype object');
test(function() {
var doc = newHTMLDocument();
var proto = Object.create(HTMLElement.prototype);
doc.registerElement('x-b', {
prototype: proto
});
assert_throws('NotSupportedError', function() {
doc.registerElement('x-b', {
prototype: proto
});
}, 'Exception should be thrown if registring custom element type with already used prototype');
}, 'Test Document.registerElement() throws NotSupportedError ' +
'if prototype is already used for another custom element type');
</script>
</body>
</html>

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Custom element's type is immutable.</title>
<meta name="author" title="Bon-Yong Lee" href="mailto:bylee78@gmail.com">
<meta name="assert" content="After a custom element is instantiated, changing the value of the is attribute must not affect this element's custom element type.">
<link rel="help" href="http://w3c.github.io/webcomponents/spec/custom/#instantiating-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script type="text/javascript">
test(function() {
var CustomButton = document.registerElement('custom-button', {
prototype: Object.create(HTMLButtonElement.prototype),
extends: 'button'
});
var customButton = document.createElement('button', 'custom-button');
assert_true(customButton instanceof CustomButton,
'A custom element is of the custom element type after ' +
'instantiation');
customButton.setAttribute('is', 'dirty');
assert_equals('dirty', customButton.getAttribute('is'),
'An attribute must be changed by method "setAttribute"');
assert_true(customButton instanceof CustomButton,
'A custom element is of the original custom element type even ' +
'after changing the \'is\' attribute');
}, 'After a custom element is instantiated, changing the value of the is attribute must not affect this element\'s custom element type.');
</script>

View file

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<title>Default namespace is HTML namespace</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Default namespace is HTML namespace">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('x-a');
var customElement = new GeneratedConstructor();
assert_equals(customElement.namespaceURI, HTML_NAMESPACE,
'Custom element namespace should be HTML namespace');
}, 'Default namespace is HTML namespace');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-b-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {
prototype: proto,
extends: tagName
});
var customElement = new GeneratedConstructor();
assert_equals(customElement.namespaceURI, HTML_NAMESPACE,
'Custom element namespace should be HTML namespace');
});
}, 'Default namespace is HTML namespace. Test constructor of extended HTML element');
</script>
</body>
</html>

View file

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html>
<head>
<title>Check duplicate definition</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If there already exists a definition with the same TYPE, set ERROR to DuplicateDefinition and stop">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var name = 'x-a';
doc.registerElement(name);
assert_throws('NotSupportedError', function() {
doc.registerElement(name);
}, 'Exception should be thrown if definition with the same type already exists');
}, 'Check duplicate definition');
test(function() {
var doc = newHTMLDocument();
var name = 'x-b';
doc.registerElement(name);
HTML5_ELEMENTS.forEach(function(tagName) {
assert_throws('NotSupportedError', function() {
doc.registerElement(name, {
extends: tagName
});
}, 'Exception should be thrown if definition with the same type already exists');
});
}, 'Check duplicate definition. Specify constructor');
test(function() {
var doc = newHTMLDocument();
var name = 'x-c';
doc.registerElement(name, {
prototype: Object.create(HTMLAnchorElement.prototype),
extends: 'a'
});
HTML5_ELEMENTS.forEach(function(tagName) {
assert_throws('NotSupportedError', function() {
doc.registerElement(name, {
extends: tagName
});
}, 'Exception should be thrown if definition with the same type already exists');
});
}, 'Check duplicate definition. Test different prototypes and extends');
</script>
</body>
</html>

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<title>If TYPE is an invalid custom element type, throw SyntaxError</title>
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="If TYPE is an invalid custom element type, set ERROR to InvalidType and stop.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
assert_throws('SyntaxError', function() { doc.registerElement('1xa2'); },
'Registering invalid custom element type should throw SyntaxError');
}, 'Registering invalid custom element type should throw SyntaxError');
</script>
</body>
</html>

View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom element local name should be converted to lower case if document is an HTML document</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If DOCUMENT is an HTML document, convert NAME to lowercase">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-a-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {
prototype: proto,
extends: tagName.toUpperCase()
});
var customElement = new GeneratedConstructor();
assert_equals(customElement.localName, tagName, 'Local name should be lowercased');
});
}, 'Custom element local name should be lowercased if document is an HTML document');
</script>
</body>
</html>

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<title>If NAME is null then localName must be set to TYPE</title>
<meta name="author" title="Vasiliy Degtyarev" href="mailto:vasya@unipro.ru">
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If NAME is null then localName must be set to TYPE">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var name = 'x-a';
var proto = Object.create(HTMLElement.prototype);
var GeneratedConstructor = doc.registerElement(name, {prototype: proto});
var customElement = new GeneratedConstructor();
assert_equals(customElement.localName, name, 'LocalName should be a type in case of ' +
'attempt to register a custom element and local name is not provided');
}, 'If NAME is not specified then localName must be set to TYPE');
test(function() {
var doc = newHTMLDocument();
var name = 'x-b';
var proto = Object.create(HTMLElement.prototype);
var GeneratedConstructor = doc.registerElement(name, {prototype: proto, extends: null});
var customElement = new GeneratedConstructor();
assert_equals(customElement.localName, name, 'LocalName should be a type in case of ' +
'attempt to register a custom element and name is null');
}, 'If NAME is null then localName must be set to TYPE');
</script>
</body>
</html>

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<title>If element interface for name doesn't exists then error must be thrown</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If NAME was provided and is not null and if element interface for the name and namespace does not exist or is an interface for a custom element, set ERROR to InvalidName and stop">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
doc.registerElement('x-a');
assert_throws('NotSupportedError', function() {
doc.registerElement('x-b', {extends: 'x-a'});
}, 'Exception should be thrown in case of attempt to register ' +
'a custom element which extends another custom element');
}, 'Exception should be thrown in case of attempt to register ' +
'a custom element which extends another custom element');
</script>
</body>
</html>

View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<title>If namespace is SVG namespace and name is null then error must be thrown</title>
<meta name="author" title="Vasiliy Degtyarev" href="mailto:vasya@unipro.ru">
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If namespace is SVG namespace and name is null then error must be thrown">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = Object.create(SVGElement.prototype);
assert_throws('NotSupportedError', function() {
doc.registerElement('x-svg-a', {prototype: proto});
}, 'Exception should be thrown in case of attempt to register ' +
'a custom element with SVG namespace and name is not specified');
}, 'Error should be thrown if namespace is SVG and local name is not specified');
test(function() {
var doc = newHTMLDocument();
var proto = Object.create(SVGElement.prototype);
assert_throws('NotSupportedError', function() {
doc.registerElement('x-svg-b', {prototype: proto, extends: null});
}, 'Exception should be thrown in case of attempt to register ' +
'a custom element with SVG namespace and name is null');
}, 'Error should be thrown if namespace is SVG and local name is null');
</script>
</body>
</html>

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<title>For SVG prototype namespace is SVG namespace</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If PROTOTYPE's interface inherits from SVGElement, set NAMESPACE to SVG Namespace">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var proto = Object.create(SVGElement.prototype);
var GeneratedConstructor = doc.registerElement('x-a', {prototype: proto, extends: 'a'});
var customElement = new GeneratedConstructor();
assert_equals(customElement.namespaceURI, SVG_NAMESPACE,
'Custom element namespace should be SVG namespace');
}, 'For SVG prototype namespace is SVG namespace');
</script>
</body>
</html>

View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom element type should be converted to lower case</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Custom element type should be lowercased">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('X-A');
var customElement = new GeneratedConstructor();
assert_equals(customElement.localName, 'x-a', 'Custom element type should be lowercased');
}, 'Custom element type should be lowercased. Test constructor');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var obj = doc.createElement(tagName);
var name = 'x-a-' + tagName;
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {
prototype: proto,
extends: tagName.toUpperCase()
});
var customElement = new GeneratedConstructor();
assert_equals(customElement.localName, tagName, 'Local name should be lowercased');
});
}, 'Custom element type should be lowercased. Test constructor of extended HTML element');
</script>
</body>
</html>

View file

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<title>If document has no registry NotSupportedError is thrown</title>
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="If REGISTRY does not exist, set ERROR to NoRegistry and stop.">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#dfn-element-registration-algorithm">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = document.implementation.createDocument(null, 'test', null);
assert_throws('NotSupportedError', function(){
doc.registerElement('x-a');
}, 'Registering valid custom element in document ' +
'without registry should throw NotSupportedError');
}, 'Registering valid custom element without options in document ' +
'without registry should throw NotSupportedError');
test(function() {
var doc = document.implementation.createDocument(null, 'test', null);
var proto = Object.create(HTMLElement.prototype);
assert_throws('NotSupportedError', function(){
doc.registerElement('x-b', { prototype: proto, extends: 'a'});
}, 'Registering valid custom element in document ' +
'without registry should throw NotSupportedError');
}, 'Registering valid custom element with options in document ' +
'without registry should throw NotSupportedError');
</script>
</body>
</html>

View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom element local name is the lowercased value of the EXTENDS property, supplied to Document.registerElement()</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="Custom element local name is the lowercased value of the EXTENDS property">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#extensions-to-document-interface-to-register">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
var name = 'x-' + tagName;
var obj = doc.createElement(tagName);
var proto = Object.create(obj.constructor.prototype);
var GeneratedConstructor = doc.registerElement(name, {
prototype: proto,
extends: tagName
});
var customElement = new GeneratedConstructor();
assert_equals(customElement.localName, tagName.toLowerCase(),
'Custom element local name should be a lowercased value of the EXTENDS property, ' +
'supplied to Document.registerElement()');
});
}, 'Custom element local name is the lowercased value of the EXTENDS property, ' +
'supplied to Document.registerElement()');
</script>
</body>
</html>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<title>Test prototype object of a custom element</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="If PROTOTYPE is null, let PROTOTYPE be the result of invoking Object.create with HTMLElement's interface prototype object as only argument">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#extensions-to-document-interface-to-register">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var GeneratedConstructor = doc.registerElement('x-a');
var customElement = new GeneratedConstructor();
assert_class_string(customElement, 'HTMLElement',
'Custom element should be a HTMLElement, ' +
'if its type is registered without prototype');
}, 'Custom element should have HTMLElement prototype, ' +
'if its type is registered without prototype');
</script>
</body>
</html>

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<title>The :unresolved pseudoclass reference file</title>
<link rel="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<style>
body {
background-color: white;
}
</style>
<body>
<p>Test passes if x-element background below is red</p>
<x-element style="background-color: red;">
x-element
</x-element>
</body>
</html>

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<title>The :unresolved pseudoclass matching custom tag</title>
<link rel="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="The :unresolved pseudoclass must match all custom elements whose created callback has not yet been invoked">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#unresolved-element-pseudoclass">
<link rel="match" href="unresolved-element-pseudoclass-css-test-custom-tag-ref.html">
<style>
:unresolved {
background-color: red;
}
body {
background-color: white;
}
</style>
<body>
<p>Test passes if x-element background below is red</p>
<x-element>
x-element
</x-element>
</body>
</html>

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<title>The :unresolved pseudoclass reference file</title>
<link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<style>
body {
background-color: white;
}
</style>
<body>
<p>Test passes if x-element background below is yellow</p>
<x-element style="background-color: yellow;">
x-element
</x-element>
</body>
</html>

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<title>The :unresolved pseudoclass matching custom tag</title>
<link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="The :unresolved pseudoclass must match all custom elements whose created callback has not yet been invoked">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#unresolved-element-pseudoclass">
<link rel="match" href="unresolved-element-pseudoclass-css-test-registered-custom-tag-ref.html">
<style>
:unresolved {
background-color: red;
}
x-element {
background-color: yellow;
}
body {
background-color: white;
}
</style>
<body onload="document.registerElement('x-element');">
<p>Test passes if x-element background below is yellow</p>
<x-element>
x-element
</x-element>
</body>
</html>

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<title>The :unresolved pseudoclass reference file</title>
<link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<style>
body {
background-color: white;
}
</style>
<body>
<p>Test passes if x-element background below is yellow</p>
<a is="x-element" style="background-color: yellow;">
x-element
</a>
</body>
</html>

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<title>The :unresolved pseudoclass matching type extension</title>
<link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="The :unresolved pseudoclass must match all custom elements whose created callback has not yet been invoked">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#unresolved-element-pseudoclass">
<link rel="match" href="unresolved-element-pseudoclass-css-test-registered-type-extension-ref.html">
<style>
:unresolved {
background-color: red;
}
a {
background-color: yellow;
}
body {
background-color: white;
}
</style>
<script>
function registerXElement() {
var obj = document.createElement('a');
var proto = Object.create(obj.constructor.prototype);
document.registerElement('x-element', { prototype: proto, extends: 'a'});
}
</script>
<body onload="registerXElement();">
<p>Test passes if x-element background below is yellow</p>
<a is="x-element">
x-element
</a>
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<title>The :unresolved pseudoclass reference file</title>
<link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<style>
body {
background-color: white;
}
</style>
<body>
<p>Test passes if x-element background below is red</p>
<a is="x-element" style="background-color: red;">
x-element
</a>
</body>
</html>

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<title>The :unresolved pseudoclass matching type extension</title>
<link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="The :unresolved pseudoclass must match all custom elements whose created callback has not yet been invoked">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#unresolved-element-pseudoclass">
<link rel="match" href="unresolved-element-pseudoclass-css-test-type-extension-ref.html">
<style>
:unresolved {
background-color: red;
}
body {
background-color: white;
}
</style>
<body>
<p>Test passes if x-element background below is red</p>
<a is="x-element">
x-element
</a>
</body>
</html>

View file

@ -0,0 +1,190 @@
<!DOCTYPE html>
<html>
<head>
<title>The :unresolved pseudoclass matching with Document.querySelectorAll()</title>
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="assert" content="The :unresolved pseudoclass must match all custom elements whose created callback has not yet been invoked">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#unresolved-element-pseudoclass">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-a></x-a>';
var queryResult = doc.querySelectorAll(':unresolved');
assert_equals(queryResult.length, 1,
'Unresolved custom element should be accessible by :unresolved pseudoclass');
assert_equals(queryResult.item(0).localName, 'x-a',
'Document.querySelectorAll(\':unresolved\') should return x-a element');
}, 'Test that single unresolved custom element is accessible ' +
'by Document.querySelectorAll(\':unresolved\')');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-b></x-b>';
var queryResult = doc.querySelectorAll(':unresolved');
assert_equals(queryResult.length, 1,
'Unresolved custom element should be accessible by :unresolved pseudoclass');
assert_equals(queryResult.item(0).localName, 'x-b',
'Document.querySelectorAll(\':unresolved\') should return x-b element');
doc.registerElement('x-b');
queryResult = doc.querySelectorAll(':unresolved');
assert_equals(queryResult.length, 0,
'Registered custom element should not be accessible by :unresolved pseudoclass');
}, 'Test that single registered custom element is not accessible by :unresolved');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-c></x-c><x-d></x-d>';
var queryResult = doc.querySelectorAll(':unresolved');
assert_equals(queryResult.length, 2,
'All unresolved custom element should be accessible by :unresolved pseudoclass');
assert_equals(queryResult.item(0).localName, 'x-c',
'First custom element returned by Document.querySelectorAll(\':unresolved\') ' +
'should be x-c');
assert_equals(queryResult.item(1).localName, 'x-d',
'Second custom element returned by Document.querySelectorAll(\':unresolved\') ' +
'should be x-d');
doc.registerElement('x-c');
queryResult = doc.querySelectorAll(':unresolved');
assert_equals(queryResult.length, 1,
'Only unresolved custom elements should be accessible by :unresolved pseudoclass');
assert_equals(queryResult.item(0).localName, 'x-d',
'Custom element returned by Document.querySelectorAll(\':unresolved\') ' +
'should be x-d');
}, 'If there are more than one unresolved custom element then all of them accessible ' +
'by Document.querySelectorAll(\':unresolved\')');
test(function() {
var doc = newHTMLDocument();
var customElement = doc.createElement('x-e');
doc.body.appendChild(customElement);
var queryResult = doc.querySelectorAll(':unresolved');
assert_equals(queryResult.length, 1,
'Unresolved custom element should be accessible by :unresolved pseudoclass');
assert_equals(queryResult.item(0).localName, 'x-e',
'Custom element returned by Document.querySelectorAll(\':unresolved\') ' +
'should be x-e');
}, 'Unresolved custom element, created via Document.createElement(), should be ' +
'accessible by Document.querySelectorAll(\':unresolved\')');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-f><x-g></x-g></x-f>';
var queryResult = doc.querySelectorAll(':unresolved');
assert_equals(queryResult.length, 2,
'All unresolved custom element should be accessible by :unresolved pseudoclass');
assert_equals(queryResult.item(0).localName, 'x-f',
'First custom element returned by Document.querySelectorAll(\':unresolved\') ' +
'should be x-f');
assert_equals(queryResult.item(1).localName, 'x-g',
'Second custom element returned by Document.querySelectorAll(\':unresolved\') ' +
'should be x-g');
doc.registerElement('x-g');
queryResult = doc.querySelectorAll(':unresolved');
assert_equals(queryResult.length, 1,
'Only unresolved custom elements should be accessible by :unresolved pseudoclass');
assert_equals(queryResult.item(0).localName, 'x-f',
'Custom element returned by Document.querySelectorAll(\':unresolved\') ' +
'should be x-f');
}, 'All unresolved custom element including nested ones are accessible ' +
'by Document.querySelectorAll(\':unresolved\')');
testInIFrame('../../resources/x-element.html', function(doc) {
var queryResult = doc.querySelectorAll(':unresolved');
assert_equals(queryResult.length, 1,
'Unresolved custom element should be accessible by ' +
'Document.querySelectorAll(\':unresolved\')');
assert_equals(queryResult.item(0).localName, 'x-element',
'Custom element returned by Document.querySelectorAll(\':unresolved\') ' +
'should be x-element');
}, 'Unresolved custom element should be accessible by ' +
'Document.querySelectorAll(\':unresolved\') in a loaded document');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
if (HTML5_DOCUMENT_ELEMENTS.indexOf(tagName) === -1) {
var obj = doc.createElement(tagName);
var name = 'x-h-' + tagName;
var id = 'x-h-' + tagName + '-id';
if (HTML5_TABLE_ELEMENTS.indexOf(tagName) !== -1) {
doc.body.innerHTML =
'<table>' +
'<' + tagName + ' id="' + id + '" is="' + name + '"></' + tagName + '>' +
'</table>';
} else {
doc.body.innerHTML =
'<' + tagName + ' id="' + id + '" is="' + name + '"></' + tagName + '>';
}
var queryResult = doc.querySelectorAll(':unresolved');
assert_not_equals(queryResult, null,
'Unresolved custom element should be accessible by :unresolved pseudoclass');
assert_equals(queryResult.item(0).id, id,
'ID of element returned by Document.querySelectorAll(\':unresolved\') ' +
'should be ' + id);
var proto = Object.create(obj.constructor.prototype);
doc.registerElement(name, {prototype: proto, extends: tagName});
var queryResult2 = doc.querySelectorAll(':unresolved');
assert_equals(queryResult2.length, 0,
'Registered custom element should not be accessible by :unresolved pseudoclass');
}
});
}, 'Test that Document.querySelectorAll(\':unresolved\') returns unresolved custom elements, ' +
'extending HTML elements by IS attribute');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-i><a is="x-j"></a></x-i><x-k></x-k><b is="x-l"/>';
var queryResult = doc.querySelectorAll(':unresolved');
assert_equals(queryResult.length, 4,
'All unresolved custom element should be accessible by :unresolved pseudoclass');
var elementNames = [
queryResult.item(0).localName,
queryResult.item(1).localName,
queryResult.item(2).localName,
queryResult.item(3).localName];
assert_array_equals(elementNames, ['x-i', 'a', 'x-k', 'b'],
'Document.querySelectorAll(\':unresolved\') return unexpected result');
var isAttributes = [
queryResult.item(0).getAttribute('is'),
queryResult.item(1).getAttribute('is'),
queryResult.item(2).getAttribute('is'),
queryResult.item(3).getAttribute('is')];
assert_array_equals(isAttributes, [null, 'x-j', null, 'x-l'],
'Document.querySelectorAll(\':unresolved\') return unexpected result');
}, 'Test Document.querySelectorAll(\':unresolved\') returns mix of custom elements of different types');
</script>
</body>
</html>

View file

@ -0,0 +1,161 @@
<!DOCTYPE html>
<html>
<head>
<title>The :unresolved pseudoclass matching with Document.querySelector()</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="The :unresolved pseudoclass must match all custom elements whose created callback has not yet been invoked">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#unresolved-element-pseudoclass">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-a></x-a>';
var customElement = doc.querySelector(':unresolved');
assert_not_equals(customElement, null,
'Unresolved custom element should be accessible by :unresolved');
assert_equals(customElement.localName, 'x-a',
'Custom element returned by Document.querySelector(\':unresolved\') should be x-a');
}, 'Test that unresolved custom element is accessible by Document.querySelector(\':unresolved\')');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-b></x-b>';
var customElement = doc.querySelector(':unresolved');
assert_not_equals(customElement, null,
'Unresolved custom element should be accessible by :unresolved');
assert_equals(customElement.localName, 'x-b',
'Custom element returned by Document.querySelector(\':unresolved\') should be x-b');
doc.registerElement('x-b');
customElement = doc.querySelector(':unresolved');
assert_equals(customElement, null,
'Registered custom element should not be accessible by :unresolved pseudoclass');
}, 'Test that registered custom element are not accessible by :unresolved');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-c></x-c><x-d></x-d>';
var customElement = doc.querySelector(':unresolved');
assert_not_equals(customElement, null,
'Unresolved custom element should be accessible by :unresolved pseudoclass');
assert_equals(customElement.localName, 'x-c',
'Custom element returned by Document.querySelector(\':unresolved\') should be x-c');
doc.registerElement('x-c');
customElement = doc.querySelector(':unresolved');
assert_not_equals(customElement, null,
'Unresolved custom elements should be accessible by :unresolved pseudoclass');
assert_equals(customElement.localName, 'x-d',
'Custom element returned by Document.querySelector(\':unresolved\') should be x-d');
}, 'If there are more than one unresolved custom element, all of them should be ' +
'accessible by :unresolved pseudoclass');
test(function() {
var doc = newHTMLDocument();
var customElement = doc.createElement('x-e');
doc.body.appendChild(customElement);
var queryResult = doc.querySelector(':unresolved');
assert_not_equals(queryResult, null,
'Unresolved custom element should be accessible by :unresolved');
assert_equals(queryResult.localName, 'x-e',
'Custom element returned by Document.querySelector(\':unresolved\') should be x-e');
}, 'Unresolved custom element, created via Document.createElement(), should be ' +
'accessible by Document.querySelector(\':unresolved\')');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<div><x-f></x-f><div>';
var customElement = doc.querySelector(':unresolved');
assert_not_equals(customElement, null,
'Unresolved custom element should be accessible by :unresolved');
assert_equals(customElement.localName, 'x-f',
'Custom element returned by Document.querySelector(\':unresolved\') should be x-f');
}, 'Unresolved custom element inside div element should be accessible by ' +
':unresolved pseudoclass');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-h><x-g></x-g></x-h>';
var customElement = doc.querySelector(':unresolved');
assert_not_equals(customElement, null,
'Unresolved custom element should be accessible by :unresolved pseudoclass');
assert_equals(customElement.localName, 'x-h',
'Custom element returned by Document.querySelector(\':unresolved\') ' +
'should be x-h');
doc.registerElement('x-h');
customElement = doc.querySelector(':unresolved');
assert_not_equals(customElement, null,
'Unresolved custom element should be accessible by :unresolved pseudoclass');
assert_equals(customElement.localName, 'x-g',
'Custom element returned by Document.querySelector(\':unresolved\') ' +
'should be x-g');
}, 'All unresolved custom element including nested ones should be accessible ' +
'by Document.querySelector(\':unresolved\')');
testInIFrame('../../resources/x-element.html', function(doc) {
var customElement = doc.querySelector(':unresolved');
assert_not_equals(customElement, null,
'Unresolved custom element should be accessible by :unresolved');
assert_equals(customElement.localName, 'x-element',
'Custom element returned by Document.querySelector(\':unresolved\') should be x-element');
}, 'Document.querySelector(): Unresolved custom element should be accessible by :unresolved ' +
'in loaded document');
test(function() {
var doc = newHTMLDocument();
HTML5_ELEMENTS.forEach(function(tagName) {
if (HTML5_DOCUMENT_ELEMENTS.indexOf(tagName) === -1) {
var obj = doc.createElement(tagName);
var name = 'x-i-' + tagName;
var id = 'x-i-' + tagName + '-id';
if (HTML5_TABLE_ELEMENTS.indexOf(tagName) !== -1) {
doc.body.innerHTML =
'<table>' +
'<' + tagName + ' id="' + id + '" is="' + name + '"></' + tagName + '>' +
'</table>';
} else {
doc.body.innerHTML =
'<' + tagName + ' id="' + id + '" is="' + name + '"></' + tagName + '>';
}
var customElement = doc.querySelector(':unresolved');
assert_not_equals(customElement, null,
'Unresolved custom element should be accessible by :unresolved pseudoclass');
assert_equals(customElement.id, id,
'ID of element returned by Document.querySelector(\':unresolved\') ' +
'should be ' + id);
var proto = Object.create(obj.constructor.prototype);
doc.registerElement(name, {prototype: proto, extends: tagName});
var customElement2 = doc.querySelector(':unresolved');
assert_equals(customElement2, null,
'Registered custom element should not be accessible by :unresolved pseudoclass');
}
});
}, 'Test that Document.querySelector(\':unresolved\') returns custom element, ' +
'extending HTML elements by IS attribute');
</script>
</body>
</html>

View file

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html>
<head>
<title>Unresolved element interface must be HTMLElement, if the namespace is HTML Namespace</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="When an unresolved element is created, it's element interface must be HTMLElement, if the namespace is HTML Namespace">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML = '<x-a id="x-a"></x-a>';
var customElement = doc.querySelector('#x-a');
assert_not_equals(customElement, null, 'Unregistered custom element should not be null');
assert_class_string(customElement, 'HTMLElement',
'Unresolved custom element must be a HTML element');
}, 'Test interface of unresolved element, created via innerHTML property');
test(function() {
var doc = newHTMLDocument();
var customElement = doc.createElement('x-b');
assert_class_string(customElement, 'HTMLElement',
'Unresolved custom element must be a HTML element');
}, 'Test interface of unresolved element, created by Document.createElement');
test(function() {
var doc = newHTMLDocument();
var customElement = doc.createElementNS(HTML_NAMESPACE, 'x-c');
assert_class_string(customElement, 'HTMLElement',
'Unresolved custom element must be a HTML element');
}, 'Test interface of unresolved element, created by Document.createElementNS');
testInIFrame('../resources/x-element.html', function(doc) {
var customElement = doc.getElementById('x-element');
assert_not_equals(customElement, null, 'Unregistered custom element should not be null');
assert_class_string(customElement, 'HTMLElement',
'Unresolved custom element must be a HTML element');
}, 'Test unresolved element interface in loaded HTML document');
</script>
</body>
</html>

View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<title>Unresolved element interface must be HTMLUnknownElement, if the namespace is neither HTML Namespace nor SVG Namespace</title>
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="When an unresolved element is created, it's element interface must be HTMLUnknownElement, if the namespace is neither HTML Namespace nor SVG Namespace">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML =
'<math xmlns="' + MATHML_NAMESPACE + '">' +
'<x-a id="x-a"></x-a>' +
'</math>';
var xa = doc.querySelector('#x-a');
assert_not_equals(xa, null, 'Unregistered custom element should not be null');
// According https://code.google.com/p/chromium/issues/detail?id=336377
// expected class string is Element
assert_class_string(xa, 'Element', 'Unresolved custom element must be an Element');
}, 'Test interface of unresolved element with MathML namespace, created via innerHTML property');
test(function() {
var doc = newHTMLDocument();
var xa = doc.createElementNS(MATHML_NAMESPACE, 'x-b');
assert_class_string(xa, 'Element',
'Unresolved custom element must be a HTMLUnknownElement');
}, 'Test interface of unresolved element with MathML namespace, ' +
'created by Document.createElementNS');
testInIFrame('../resources/x-mathml-element.html', function(doc) {
var customElement = doc.getElementById('x-math-element');
assert_not_equals(customElement, null, 'Unregistered custom element should not be null');
assert_class_string(customElement, 'Element',
'Unresolved custom element must be a Element');
}, 'Test interface of unresolved element in loaded HTML document with embedded MathML elements');
</script>
</body>
</html>

View file

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>Unresolved element interface must be SVGElement, if the namespace is SVG Namespace</title>
<meta name="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
<meta name="author" title="Vasiliy Degtyarev" href="mailto:vasya@unipro.ru">
<meta name="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<meta name="assert" content="When an unresolved element is created, it's element interface must be SVGElement if the namespace is SVG Namespace">
<link rel="help" href="http://www.w3.org/TR/custom-elements/#registering-custom-elements">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
</head>
<body>
<div id="log"></div>
<script>
test(function() {
var doc = newHTMLDocument();
var xsvg = doc.createElementNS(SVG_NAMESPACE, 'x-svg');
assert_class_string(xsvg, 'SVGElement', 'Unresolved custom element must be a SVG element');
}, 'Test interface of unresolved element with valid name, created by Document.createElementNS()');
test(function() {
var doc = newHTMLDocument();
doc.body.innerHTML =
'<svg xmlns=' + SVG_NAMESPACE + ' version="1.1">' +
'<x-svg-a id="x-svg"></x-svg-a>' +
'</svg>';
var xsvg = doc.querySelector('#x-svg');
assert_class_string(xsvg, 'SVGElement', 'Unresolved custom element must be a SVG element');
}, 'Test interface of unresolved element with valid name, created via innerHTML property');
testInIFrame('../resources/x-svg-element.html', function(doc) {
var xsvg = doc.getElementById('x-svg-element');
assert_not_equals(xsvg, null, 'Unresolved custom element should not be null');
assert_class_string(xsvg, 'SVGElement',
'Unresolved custom element must be a SVG element');
}, 'Test interface of unresolved element in loaded HTML document with embedded SVG elements');
</script>
</body>
</html>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>Blank document</title>
<link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Import Master Document (asynchronous)</title>
<link rel="import" href="import.html" async>
</head>
<body>
<p>Master document body</p>
</body>
</html>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Import Master Document</title>
<link rel="import" href="import.html">
</head>
<body>
<p>Master document body</p>
</body>
</html>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>Import Document</title>
</head>
<body>
<p>Import Document body</p>
</body>
</html>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>Register and create custom element</title>
<link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
<link rel="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
</head>
<body>
<div id="log"></div>
<script>
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
document.querySelector('#log').textContent = 'Created callback was called';
};
document.registerElement('x-element', {prototype: proto});
</script>
<x-element id="x-element"></x-element>
</body>
</html>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>x-element custom element</title>
<link rel="author" title="Sergey G. Grekhov" href="mailto:sgrekhov@unipro.ru">
</head>
<body>
<x-element id="x-element"></x-element>
</body>
</html>

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom element in MathML namespace</title>
<link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
</head>
<body>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<x-math-element id="x-math-element"></x-math-element>
</math>
</body>
</html>

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>SVG custom element</title>
<link rel="author" title="Aleksei Yu. Semenov" href="mailto:a.semenov@unipro.ru">
</head>
<body>
<svg height="100" width="100">
<x-svg-element id="x-svg-element"></x-element>
</svg>
</body>
</html>

View file

@ -0,0 +1,286 @@
/*
Distributed under both the W3C Test Suite License [1] and the W3C
3-clause BSD License [2]. To contribute to a W3C Test Suite, see the
policies and contribution forms [3].
[1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license
[2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license
[3] http://www.w3.org/2004/10/27-testcases
*/
"use strict";
var HTML5_ELEMENTS = [ 'a', 'abbr', 'address', 'area', 'article', 'aside',
'audio', 'b', 'base', 'bdi', 'bdo', 'blockquote', 'body', 'br',
'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup',
'command', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div',
'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure',
'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header',
'hgroup', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd',
'keygen', 'label', 'legend', 'li', 'link', 'map', 'mark', 'menu',
'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup',
'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 'rp', 'rt',
'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source',
'span', 'strong', 'style', 'sub', 'table', 'tbody', 'td', 'textarea',
'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul',
'var', 'video', 'wbr' ];
var HTML5_DOCUMENT_ELEMENTS = [ 'html', 'head', 'body' ];
var HTML5_TABLE_ELEMENTS = [ 'caption', 'col', 'colgroup', 'tbody', 'td',
'tfoot', 'th', 'thead', 'tr' ];
var EXTENDER_CHARS = [ 0x00B7, 0x02D0, 0x02D1, 0x0387, 0x0640, 0x0E46, 0x0EC6,
0x3005, 0x3031, 0x3032, 0x3033, 0x3034, 0x3035, 0x309D, 0x309E, 0x30FC,
0x30FD, 0x30FE ];
var COMBINING_CHARS = [ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306,
0x0307, 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F,
0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, 0x0318,
0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, 0x0320, 0x0321,
0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, 0x0328, 0x0329, 0x032A,
0x032B, 0x032C, 0x032D, 0x032E, 0x032F, 0x0330, 0x0331, 0x0332, 0x0333,
0x0334, 0x0335, 0x0336, 0x0337, 0x0338, 0x0339, 0x033A, 0x033B, 0x033C,
0x033D, 0x033E, 0x033F, 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345,
0x0360, 0x0361, 0x0483, 0x0484, 0x0485, 0x0486, 0x0591, 0x0592, 0x0593,
0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x05A0, 0x05A1, 0x05A3,
0x05A4, 0x05A5, 0x05A6, 0x05A7, 0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC,
0x05AD, 0x05AE, 0x05AF, 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5,
0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x05BB, 0x05BC, 0x05BD, 0x05BF, 0x05C1,
0x05C2, 0x05C4, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x0651,
0x0652, 0x0670, 0x06D6, 0x06D7, 0x06D8, 0x06D9, 0x06DA, 0x06DB, 0x06DC,
0x06DD, 0x06DE, 0x06DF, 0x06E0, 0x06E1, 0x06E2, 0x06E3, 0x06E4, 0x06E7,
0x06E8, 0x06EA, 0x06EB, 0x06EC, 0x06ED, 0x0901, 0x0902, 0x0903, 0x093C,
0x093E, 0x093F, 0x0940, 0x0941, 0x0942, 0x0943, 0x0944, 0x0945, 0x0946,
0x0947, 0x0948, 0x0949, 0x094A, 0x094B, 0x094C, 0x094D, 0x0951, 0x0952,
0x0953, 0x0954, 0x0962, 0x0963, 0x0981, 0x0982, 0x0983, 0x09BC, 0x09BE,
0x09BF, 0x09C0, 0x09C1, 0x09C2, 0x09C3, 0x09C4, 0x09C7, 0x09C8, 0x09CB,
0x09CC, 0x09CD, 0x09D7, 0x09E2, 0x09E3, 0x0A02, 0x0A3C, 0x0A3E, 0x0A3F,
0x0A40, 0x0A41, 0x0A42, 0x0A47, 0x0A48, 0x0A4B, 0x0A4C, 0x0A4D, 0x0A70,
0x0A71, 0x0A81, 0x0A82, 0x0A83, 0x0ABC, 0x0ABE, 0x0ABF, 0x0AC0, 0x0AC1,
0x0AC2, 0x0AC3, 0x0AC4, 0x0AC5, 0x0AC7, 0x0AC8, 0x0AC9, 0x0ACB, 0x0ACC,
0x0ACD, 0x0B01, 0x0B02, 0x0B03, 0x0B3C, 0x0B3E, 0x0B3F, 0x0B40, 0x0B41,
0x0B42, 0x0B43, 0x0B47, 0x0B48, 0x0B4B, 0x0B4C, 0x0B4D, 0x0B56, 0x0B57,
0x0B82, 0x0B83, 0x0BBE, 0x0BBF, 0x0BC0, 0x0BC1, 0x0BC2, 0x0BC6, 0x0BC7,
0x0BC8, 0x0BCA, 0x0BCB, 0x0BCC, 0x0BCD, 0x0BD7, 0x0C01, 0x0C02, 0x0C03,
0x0C3E, 0x0C3F, 0x0C40, 0x0C41, 0x0C42, 0x0C43, 0x0C44, 0x0C46, 0x0C47,
0x0C48, 0x0C4A, 0x0C4B, 0x0C4C, 0x0C4D, 0x0C55, 0x0C56, 0x0C82, 0x0C83,
0x0CBE, 0x0CBF, 0x0CC0, 0x0CC1, 0x0CC2, 0x0CC3, 0x0CC4, 0x0CC6, 0x0CC7,
0x0CC8, 0x0CCA, 0x0CCB, 0x0CCC, 0x0CCD, 0x0CD5, 0x0CD6, 0x0D02, 0x0D03,
0x0D3E, 0x0D3F, 0x0D40, 0x0D41, 0x0D42, 0x0D43, 0x0D46, 0x0D47, 0x0D48,
0x0D4A, 0x0D4B, 0x0D4C, 0x0D4D, 0x0D57, 0x0E31, 0x0E34, 0x0E35, 0x0E36,
0x0E37, 0x0E38, 0x0E39, 0x0E3A, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B,
0x0E4C, 0x0E4D, 0x0E4E, 0x0EB1, 0x0EB4, 0x0EB5, 0x0EB6, 0x0EB7, 0x0EB8,
0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0EC9, 0x0ECA, 0x0ECB, 0x0ECC, 0x0ECD,
0x0F18, 0x0F19, 0x0F35, 0x0F37, 0x0F39, 0x0F3E, 0x0F3F, 0x0F71, 0x0F72,
0x0F73, 0x0F74, 0x0F75, 0x0F76, 0x0F77, 0x0F78, 0x0F79, 0x0F7A, 0x0F7B,
0x0F7C, 0x0F7D, 0x0F7E, 0x0F7F, 0x0F80, 0x0F81, 0x0F82, 0x0F83, 0x0F84,
0x0F86, 0x0F87, 0x0F88, 0x0F89, 0x0F8A, 0x0F8B, 0x0F90, 0x0F91, 0x0F92,
0x0F93, 0x0F94, 0x0F95, 0x0F97, 0x0F99, 0x0F9A, 0x0F9B, 0x0F9C, 0x0F9D,
0x0F9E, 0x0F9F, 0x0FA0, 0x0FA1, 0x0FA2, 0x0FA3, 0x0FA4, 0x0FA5, 0x0FA6,
0x0FA7, 0x0FA8, 0x0FA9, 0x0FAA, 0x0FAB, 0x0FAC, 0x0FAD, 0x0FB1, 0x0FB2,
0x0FB3, 0x0FB4, 0x0FB5, 0x0FB6, 0x0FB7, 0x0FB9, 0x20D0, 0x20D1, 0x20D2,
0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7, 0x20D8, 0x20D9, 0x20DA, 0x20DB,
0x20DC, 0x20E1, 0x302A, 0x302B, 0x302C, 0x302D, 0x302E, 0x302F, 0x3099,
0x309A ];
var BASE_CHARS_SINGLE = [ 0x0386, 0x038C, 0x03DA, 0x03DC, 0x03DE, 0x03E0,
0x0559, 0x06D5, 0x093D, 0x09B2, 0x0A5E, 0x0A8D, 0x0ABD, 0x0AE0, 0x0B3D,
0x0B9C, 0x0CDE, 0x0E30, 0x0E84, 0x0E8A, 0x0E8D, 0x0EA5, 0x0EA7, 0x0EB0,
0x0EBD, 0x1100, 0x1109, 0x113C, 0x113E, 0x1140, 0x114C, 0x114E, 0x1150,
0x1159, 0x1163, 0x1165, 0x1167, 0x1169, 0x1175, 0x119E, 0x11A8, 0x11AB,
0x11BA, 0x11EB, 0x11F0, 0x11F9, 0x1F59, 0x1F5B, 0x1F5D, 0x1FBE, 0x2126,
0x212E ];
var BASE_CHARS_RANGES = [ 0x0041, 0x005A, 0x0061, 0x007A, 0x00C0, 0x00D6,
0x00D8, 0x00F6, 0x00F8, 0x00FF, 0x0100, 0x0131, 0x0134, 0x013E, 0x0141,
0x0148, 0x014A, 0x017E, 0x0180, 0x01C3, 0x01CD, 0x01F0, 0x01F4, 0x01F5,
0x01FA, 0x0217, 0x0250, 0x02A8, 0x02BB, 0x02C1, 0x0388, 0x038A, 0x038E,
0x03A1, 0x03A3, 0x03CE, 0x03D0, 0x03D6, 0x03E2, 0x03F3, 0x0401, 0x040C,
0x040E, 0x044F, 0x0451, 0x045C, 0x045E, 0x0481, 0x0490, 0x04C4, 0x04C7,
0x04C8, 0x04CB, 0x04CC, 0x04D0, 0x04EB, 0x04EE, 0x04F5, 0x04F8, 0x04F9,
0x0531, 0x0556, 0x0561, 0x0586, 0x05D0, 0x05EA, 0x05F0, 0x05F2, 0x0621,
0x063A, 0x0641, 0x064A, 0x0671, 0x06B7, 0x06BA, 0x06BE, 0x06C0, 0x06CE,
0x06D0, 0x06D3, 0x06E5, 0x06E6, 0x0905, 0x0939, 0x0958, 0x0961, 0x0985,
0x098C, 0x098F, 0x0990, 0x0993, 0x09A8, 0x09AA, 0x09B0, 0x09B6, 0x09B9,
0x09DC, 0x09DD, 0x09DF, 0x09E1, 0x09F0, 0x09F1, 0x0A05, 0x0A0A, 0x0A0F,
0x0A10, 0x0A13, 0x0A28, 0x0A2A, 0x0A30, 0x0A32, 0x0A33, 0x0A35, 0x0A36,
0x0A38, 0x0A39, 0x0A59, 0x0A5C, 0x0A72, 0x0A74, 0x0A85, 0x0A8B, 0x0A8F,
0x0A91, 0x0A93, 0x0AA8, 0x0AAA, 0x0AB0, 0x0AB2, 0x0AB3, 0x0AB5, 0x0AB9,
0x0B05, 0x0B0C, 0x0B0F, 0x0B10, 0x0B13, 0x0B28, 0x0B2A, 0x0B30, 0x0B32,
0x0B33, 0x0B36, 0x0B39, 0x0B5C, 0x0B5D, 0x0B5F, 0x0B61, 0x0B85, 0x0B8A,
0x0B8E, 0x0B90, 0x0B92, 0x0B95, 0x0B99, 0x0B9A, 0x0B9E, 0x0B9F, 0x0BA3,
0x0BA4, 0x0BA8, 0x0BAA, 0x0BAE, 0x0BB5, 0x0BB7, 0x0BB9, 0x0C05, 0x0C0C,
0x0C0E, 0x0C10, 0x0C12, 0x0C28, 0x0C2A, 0x0C33, 0x0C35, 0x0C39, 0x0C60,
0x0C61, 0x0C85, 0x0C8C, 0x0C8E, 0x0C90, 0x0C92, 0x0CA8, 0x0CAA, 0x0CB3,
0x0CB5, 0x0CB9, 0x0CE0, 0x0CE1, 0x0D05, 0x0D0C, 0x0D0E, 0x0D10, 0x0D12,
0x0D28, 0x0D2A, 0x0D39, 0x0D60, 0x0D61, 0x0E01, 0x0E2E, 0x0E32, 0x0E33,
0x0E40, 0x0E45, 0x0E81, 0x0E82, 0x0E87, 0x0E88, 0x0E94, 0x0E97, 0x0E99,
0x0E9F, 0x0EA1, 0x0EA3, 0x0EAA, 0x0EAB, 0x0EAD, 0x0EAE, 0x0EB2, 0x0EB3,
0x0EC0, 0x0EC4, 0x0F40, 0x0F47, 0x0F49, 0x0F69, 0x10A0, 0x10C5, 0x10D0,
0x10F6, 0x1102, 0x1103, 0x1105, 0x1107, 0x110B, 0x110C, 0x110E, 0x1112,
0x1154, 0x1155, 0x115F, 0x1161, 0x116D, 0x116E, 0x1172, 0x1173, 0x11AE,
0x11AF, 0x11B7, 0x11B8, 0x11BC, 0x11C2, 0x1E00, 0x1E9B, 0x1EA0, 0x1EF9,
0x1F00, 0x1F15, 0x1F18, 0x1F1D, 0x1F20, 0x1F45, 0x1F48, 0x1F4D, 0x1F50,
0x1F57, 0x1F5F, 0x1F7D, 0x1F80, 0x1FB4, 0x1FB6, 0x1FBC, 0x1FC2, 0x1FC4,
0x1FC6, 0x1FCC, 0x1FD0, 0x1FD3, 0x1FD6, 0x1FDB, 0x1FE0, 0x1FEC, 0x1FF2,
0x1FF4, 0x1FF6, 0x1FFC, 0x212A, 0x212B, 0x2180, 0x2182, 0x3041, 0x3094,
0x30A1, 0x30FA, 0x3105, 0x312C, 0xAC00, 0xD7A3 ];
var IDEOGRAPHIC_CHARS_SINGLE = [ 0x3007 ];
var IDEOGRAPHIC_CHARS_RANGES = [ 0x3021, 0x3029, 0x4E00, 0x9FA5 ];
var DIGIT_CHARS_RANGES = [ 0x0030, 0x0039, 0x0660, 0x0669, 0x06F0, 0x06F9,
0x0966, 0x096F, 0x09E6, 0x09EF, 0x0A66, 0x0A6F, 0x0AE6, 0x0AEF, 0x0B66,
0x0B6F, 0x0BE7, 0x0BEF, 0x0C66, 0x0C6F, 0x0CE6, 0x0CEF, 0x0D66, 0x0D6F,
0x0E50, 0x0E59, 0x0ED0, 0x0ED9, 0x0F20, 0x0F29 ];
function CharsArray(array) {
this.array = array;
}
CharsArray.prototype.testEach = function(namingFunction, checkFunction) {
if (this.array != null) {
this.array.forEach(function(value) {
checkFunction(namingFunction(getCharCode(value)));
});
}
};
function CharRangesArray(array) {
this.array = array;
}
CharRangesArray.prototype.testEach = function(namingFunction, checkFunction) {
if (this.array != null) {
for (var i = 0; i < this.array.length; i += 2) {
var rangeStart = getCharCode(this.array[i]);
var rangeEnd = getCharCode(this.array[i+1]);
for (var c = rangeStart; c <= rangeEnd; c++) {
checkFunction(namingFunction(c));
}
}
}
};
function testCharCode(charCode, namingFunction, checkFunction) {
checkFunction(namingFunction(charCode));
}
var extenderChars = new CharsArray(EXTENDER_CHARS);
var combiningChars = new CharsArray(COMBINING_CHARS);
var baseCharsSingle = new CharsArray(BASE_CHARS_SINGLE);
var baseCharsRanges = new CharRangesArray(BASE_CHARS_RANGES);
var ideographicCharsSingle = new CharsArray(IDEOGRAPHIC_CHARS_SINGLE);
var ideographicCharsRanges = new CharRangesArray(IDEOGRAPHIC_CHARS_RANGES);
var digitCharsRanges = new CharRangesArray(DIGIT_CHARS_RANGES);
// Helper function, which verifies that given custom element name is valid
function checkValidName(name) {
var doc = newHTMLDocument();
try {
doc.registerElement(name);
} catch (e) {
assert_unreached('The custom element name \'' + name +
'\' should be registered without errors');
}
}
// Helper function, which verifies that given custom element name is invalid
function checkInvalidName(name) {
var doc = newHTMLDocument();
assert_throws('SyntaxError', function() {
doc.registerElement(name);
}, 'Registering invalid custom element name \'' + name + '\' should fail');
}
// Helper function to extract character code from given object
// expected input: either charater code or one character long string.
function getCharCode(c) {
if (typeof(c) === 'string') {
assert_equals(1, c.length, 'Error in test: input string should be one character long');
c = c.charCodeAt(0);
}
assert_equals('number', typeof(c), 'Error in test: unexpected type for charater code');
return c;
}
var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
function newHTMLDocument() {
return document.implementation.createHTMLDocument('Test Document');
}
// Creates new iframe and loads given url into it.
// Returns reference to created iframe.
function newIFrame(url) {
assert_not_equals(url, null, 'argument url should not be null');
var iframe = document.createElement('iframe');
iframe.src = url;
document.body.appendChild(iframe);
return iframe;
}
// Creates new iframe and loads given url into it.
// Function f is bound to the iframe's onload event.
// Function f receives iframe's contentDocument as argument.
// The iframe is disposed after function f is executed.
function testInIFrame(url, f, testName, testProps) {
var t = async_test(testName, testProps);
t.step(function() {
var iframe = newIFrame(url);
iframe.onload = t.step_func(function() {
try {
f(iframe.contentDocument);
t.done();
} finally {
iframe.remove();
}
});
});
}
// Helper function to create a prototype for custom element
// with predefined callbacks
function newHTMLElementPrototype() {
return newCustomElementPrototype(HTMLElement.prototype);
}
// Helper function to create a prototype for custom element
// with predefined callbacks
function newCustomElementPrototype(parent) {
var proto = Object.create(parent);
proto.createdCallbackThis = null;
proto.createdCallbackCalledCounter = 0;
proto.attachedCallbackThis = null;
proto.attachedCallbackCalledCounter = 0;
proto.detachedCallbackThis = null;
proto.detachedCallbackCalledCounter = 0;
proto.attributeChangedCallbackThis = null;
proto.attributeChangedCallbackCalledCounter = 0;
proto.attributeChangedCallbackArgs = null;
proto.createdCallback = function() {
proto.createdCallbackThis = this;
proto.createdCallbackCalledCounter++;
};
proto.attachedCallback = function() {
proto.attachedCallbackThis = this;
proto.attachedCallbackCalledCounter++;
};
proto.detachedCallback = function() {
proto.detachedCallbackThis = this;
proto.detachedCallbackCalledCounter++;
};
proto.attributeChangedCallback = function(arg1, arg2, arg3) {
proto.attributeChangedCallbackThis = this;
proto.attributeChangedCallbackCalledCounter++;
proto.attributeChangedCallbackArgs = [arg1, arg2, arg3];
};
return proto;
}