diff options
Diffstat (limited to 'testing/web-platform/tests/custom-elements/reactions')
57 files changed, 3587 insertions, 0 deletions
diff --git a/testing/web-platform/tests/custom-elements/reactions/Animation.html b/testing/web-platform/tests/custom-elements/reactions/Animation.html new file mode 100644 index 0000000000..f8d3bb86ed --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/Animation.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on Element interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="commitStyles of Animation interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#element"> +<meta name="help" content="https://w3c.github.io/DOM-Parsing/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test(function () { + const element = define_new_custom_element(['style']); + const instance = document.createElement(element.name); + document.body.appendChild(instance); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + + const animation = instance.animate([{'borderColor': 'rgb(0, 0, 255)'}], 1); + animation.commitStyles(); + + const logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_equals(logEntries.last().name, 'style'); + assert_equals(logEntries.last().namespace, null); +}, 'Animation.animate must enqueue an attributeChanged reaction when it adds the observed style attribute'); + +test(function () { + const element = define_new_custom_element(['style']); + const instance = document.createElement(element.name); + document.body.appendChild(instance); + + let animation = instance.animate([{'borderColor': 'rgb(0, 0, 255)'}], 1); + animation.commitStyles(); + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected', 'attributeChanged']); + + animation = instance.animate([{'borderColor': 'rgb(0, 255, 0)'}]); + animation.commitStyles(); + + const logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_equals(logEntries.last().name, 'style'); + assert_equals(logEntries.last().namespace, null); +}, 'Animation.animate must enqueue an attributeChanged reaction when it mutates the observed style attribute'); + +test(function () { + const element = define_new_custom_element([]); + const instance = document.createElement(element.name); + document.body.appendChild(instance); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + + const animation = instance.animate([{'borderColor': 'rgb(0, 0, 255)'}], 1); + animation.commitStyles(); + + assert_array_equals(element.takeLog().types(), []); +}, 'Animation.animate must not enqueue an attributeChanged reaction when it mutates the style attribute but the style attribute is not observed'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/AriaMixin-element-attributes.html b/testing/web-platform/tests/custom-elements/reactions/AriaMixin-element-attributes.html new file mode 100644 index 0000000000..eec6dee03b --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/AriaMixin-element-attributes.html @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on Element interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="Element attributes of AriaAttributes interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#element"> +<meta name="help" content="https://w3c.github.io/DOM-Parsing/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<div id="parentElement"></div> +<script> + +function testElementReflectAttribute(jsAttributeName, contentAttributeName, validValue1, validValue2, name, getParentElement) { + test(function () { + let element = define_new_custom_element([contentAttributeName]); + let instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + parentElement.appendChild(instance); + assert_array_equals(element.takeLog().types(), ['connected']); + instance[jsAttributeName] = validValue1; + let logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + + assert_attribute_log_entry(logEntries.last(), {name: contentAttributeName, oldValue: null, newValue: "", namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when adding ' + contentAttributeName + ' content attribute'); + + test(function () { + let element = define_new_custom_element([contentAttributeName]); + let instance = document.createElement(element.name); + parentElement.appendChild(instance); + instance[jsAttributeName] = validValue1; + assert_array_equals(element.takeLog().types(), ['constructed', 'connected', 'attributeChanged']); + instance[jsAttributeName] = validValue2; + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: contentAttributeName, oldValue: "", newValue: "", namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute'); +} + +const dummy1 = document.createElement('div'); +dummy1.id = 'dummy1'; +document.body.appendChild(dummy1); + +const dummy2 = document.createElement('div'); +dummy2.id = 'dummy2'; +document.body.appendChild(dummy2); + +testElementReflectAttribute('ariaActiveDescendantElement', 'aria-activedescendant', dummy1, dummy2, 'ariaActiveDescendantElement in Element'); +testElementReflectAttribute('ariaControlsElements', 'aria-controls', [dummy1], [dummy2], 'ariaControlsElements in Element'); +testElementReflectAttribute('ariaDescribedByElements', 'aria-describedby', [dummy1], [dummy2], 'ariaDescribedByElements in Element'); +testElementReflectAttribute('ariaDetailsElements', 'aria-details', [dummy1], [dummy2], 'ariaDetailsElements in Element'); +testElementReflectAttribute('ariaErrorMessageElements', 'aria-errormessage', [dummy1], [dummy2], 'ariaErrorMessageElements in Element'); +testElementReflectAttribute('ariaFlowToElements', 'aria-flowto', [dummy1], [dummy2], 'ariaFlowToElements in Element'); +testElementReflectAttribute('ariaLabelledByElements', 'aria-labelledby', [dummy1], [dummy2], 'ariaLabelledByElements in Element') +testElementReflectAttribute('ariaOwnsElements', 'aria-owns', [dummy1], [dummy2], 'ariaOwnsElements in Element') + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/AriaMixin-string-attributes.html b/testing/web-platform/tests/custom-elements/reactions/AriaMixin-string-attributes.html new file mode 100644 index 0000000000..f71bf2daa9 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/AriaMixin-string-attributes.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on Element interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="String attributes of AriaAttributes interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#element"> +<meta name="help" content="https://w3c.github.io/DOM-Parsing/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<div id="container"></div> +<script> + +testReflectAttribute('ariaAtomic', 'aria-atomic', 'foo', 'bar', 'ariaAtomic on Element'); +testReflectAttribute('ariaAutoComplete', 'aria-autocomplete', 'foo', 'bar', 'ariaAutoComplete on Element'); +testReflectAttribute('ariaBusy', 'aria-busy', 'foo', 'bar', 'ariaBusy on Element'); +testReflectAttribute('ariaChecked', 'aria-checked', 'foo', 'bar', 'ariaChecked on Element'); +testReflectAttribute('ariaColCount', 'aria-colcount', 'foo', 'bar', 'ariaColCount on Element'); +testReflectAttribute('ariaColIndex', 'aria-colindex', 'foo', 'bar', 'ariaColIndex on Element'); +testReflectAttribute('ariaColSpan', 'aria-colspan', 'foo', 'bar', 'ariaColSpan on Element'); + +testReflectAttribute('ariaCurrent', 'aria-current', 'foo', 'bar', 'ariaCurrent on Element'); + +testReflectAttribute('ariaDisabled', 'aria-disabled', 'foo', 'bar', 'ariaDisabled on Element'); + +testReflectAttribute('ariaExpanded', 'aria-expanded', 'foo', 'bar', 'ariaExpanded on Element'); + +testReflectAttribute('ariaHasPopup', 'aria-haspopup', 'foo', 'bar', 'ariaHasPopup on Element'); +testReflectAttribute('ariaHidden', 'aria-hidden', 'foo', 'bar', 'ariaHidden on Element'); +testReflectAttribute('ariaInvalid', 'aria-invalid', 'foo', 'bar', 'ariaInvalid on Element'); +testReflectAttribute('ariaKeyShortcuts', 'aria-keyshortcuts', 'foo', 'bar', 'ariaKeyShortcuts on Element'); +testReflectAttribute('ariaLabel', 'aria-label', 'foo', 'bar', 'ariaLabel on Element'); + +testReflectAttribute('ariaLevel', 'aria-level', 'foo', 'bar', 'ariaLevel on Element'); +testReflectAttribute('ariaLive', 'aria-live', 'foo', 'bar', 'ariaLive on Element'); +testReflectAttribute('ariaModal', 'aria-modal', 'foo', 'bar', 'ariaModal on Element'); +testReflectAttribute('ariaMultiLine', 'aria-multiline', 'foo', 'bar', 'ariaMultiLine on Element'); +testReflectAttribute('ariaMultiSelectable', 'aria-multiselectable', 'foo', 'bar', 'ariaMultiSelectable on Element'); +testReflectAttribute('ariaOrientation', 'aria-orientation', 'foo', 'bar', 'ariaOrientation on Element'); + +testReflectAttribute('ariaPlaceholder', 'aria-placeholder', 'foo', 'bar', 'ariaPlaceholder on Element'); +testReflectAttribute('ariaPosInSet', 'aria-posinset', 'foo', 'bar', 'ariaPosInSet on Element'); +testReflectAttribute('ariaPressed', 'aria-pressed', 'foo', 'bar', 'ariaPressed on Element'); +testReflectAttribute('ariaReadOnly', 'aria-readonly', 'foo', 'bar', 'ariaReadOnly on Element'); +testReflectAttribute('ariaRelevant', 'aria-relevant', 'foo', 'bar', 'ariaRelevant on Element'); +testReflectAttribute('ariaRequired', 'aria-required', 'foo', 'bar', 'ariaRequired on Element'); +testReflectAttribute('ariaRoleDescription', 'aria-roledescription', 'foo', 'bar', 'ariaRoleDescription on Element'); +testReflectAttribute('ariaRowCount', 'aria-rowcount', 'foo', 'bar', 'ariaRowCount on Element'); +testReflectAttribute('ariaRowIndex', 'aria-rowindex', 'foo', 'bar', 'ariaRowIndex on Element'); +testReflectAttribute('ariaRowSpan', 'aria-rowspan', 'foo', 'bar', 'ariaRowSpan on Element'); +testReflectAttribute('ariaSelected', 'aria-selected', 'foo', 'bar', 'ariaSelected on Element'); +testReflectAttribute('ariaSetSize', 'aria-setsize', 'foo', 'bar', 'ariaSetSize on Element'); +testReflectAttribute('ariaSort', 'aria-sort', 'foo', 'bar', 'ariaSort on Element'); +testReflectAttribute('ariaValueMax', 'aria-valuemax', 'foo', 'bar', 'ariaValueMax on Element'); +testReflectAttribute('ariaValueMin', 'aria-valuemin', 'foo', 'bar', 'ariaValueMin on Element'); +testReflectAttribute('ariaValueNow', 'aria-valuenow', 'foo', 'bar', 'ariaValueNow on Element'); +testReflectAttribute('ariaValueText', 'aria-valuetext', 'foo', 'bar', 'ariaValueText on Element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/Attr.html b/testing/web-platform/tests/custom-elements/reactions/Attr.html new file mode 100644 index 0000000000..c9fa37f961 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/Attr.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on Attr interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="value of Attr interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +testAttributeMutator(function (element, name, value) { + element.attributes[name].value = value; +}, 'value on Attr'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/CSSStyleDeclaration.html b/testing/web-platform/tests/custom-elements/reactions/CSSStyleDeclaration.html new file mode 100644 index 0000000000..95274d8c75 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/CSSStyleDeclaration.html @@ -0,0 +1,89 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on CSSStyleDeclaration interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="cssText, setProperty, setPropertyValue, setPropertyPriority, removeProperty, cssFloat, and all camel cased attributes of CSSStyleDeclaration interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test_mutating_style_property_value(function (instance, propertyName, idlName, value) { + instance.style.cssText = `${propertyName}: ${value}`; +}, 'cssText on CSSStyleDeclaration'); + +test_mutating_style_property_value(function (instance, propertyName, idlName, value) { + instance.style.setProperty(propertyName, value); +}, 'setProperty on CSSStyleDeclaration'); + +test_mutating_style_property_priority(function (instance, propertyName, idlName, isImportant) { + instance.style.setProperty(propertyName, instance.style[idlName], isImportant ? 'important': ''); +}, 'setProperty on CSSStyleDeclaration'); + +if (CSSStyleDeclaration.prototype.setPropertyValue) { + test_mutating_style_property_value(function (instance, propertyName, idlName, value) { + instance.style.setPropertyValue(propertyName, value); + }, 'setPropertyValue on CSSStyleDeclaration'); +} + +if (CSSStyleDeclaration.prototype.setPropertyPriority) { + test_mutating_style_property_priority(function (instance, propertyName, idlName, isImportant) { + instance.style.setPropertyPriority(propertyName, isImportant ? 'important': ''); + }, 'setPropertyPriority on CSSStyleDeclaration'); +} + +test_removing_style_property_value(function (instance, propertyName, idlName) { + instance.style.removeProperty(propertyName); +}, 'removeProperty on CSSStyleDeclaration'); + +test(function () { + var element = define_new_custom_element(['style']); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + instance.style.cssFloat = 'left'; + assert_equals(instance.getAttribute('style'), 'float: left;'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'style', oldValue: null, newValue: 'float: left;', namespace: null}); +}, 'cssFloat on CSSStyleDeclaration must enqueue an attributeChanged reaction when it adds the observed style attribute'); + +test(function () { + var element = define_new_custom_element([]); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + instance.style.cssFloat = 'left'; + assert_equals(instance.getAttribute('style'), 'float: left;'); + assert_array_equals(element.takeLog().types(), []); +}, 'cssFloat on CSSStyleDeclaration must not enqueue an attributeChanged reaction when it adds the style attribute but the style attribute is not observed'); + +test_mutating_style_property_value(function (instance, propertyName, idlName, value) { + assert_equals(idlName, 'borderWidth'); + instance.style.borderWidth = value; +}, 'A camel case attribute (borderWidth) on CSSStyleDeclaration', + {propertyName: 'border-width', idlName: 'borderWidth', value1: '2px', value2: '4px'}); + +test_mutating_style_property_value(function (instance, propertyName, idlName, value) { + assert_equals(propertyName, 'border-width'); + instance.style['border-width'] = value; +}, 'A dashed property (border-width) on CSSStyleDeclaration', + {propertyName: 'border-width', idlName: 'borderWidth', value1: '1px', value2: '5px'}); + +test_mutating_style_property_value(function (instance, propertyName, idlName, value) { + instance.style.webkitFilter = value; +}, 'A webkit prefixed camel case attribute (webkitFilter) on CSSStyleDeclaration', + {propertyName: 'filter', idlName: 'filter', value1: 'grayscale(20%)', value2: 'grayscale(30%)'}); + +test_mutating_style_property_value(function (instance, propertyName, idlName, value) { + instance.style['-webkit-filter'] = value; +}, 'A webkit prefixed dashed property (-webkit-filter) on CSSStyleDeclaration', + {propertyName: 'filter', idlName: 'filter', value1: 'grayscale(20%)', value2: 'grayscale(30%)'}); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/ChildNode.html b/testing/web-platform/tests/custom-elements/reactions/ChildNode.html new file mode 100644 index 0000000000..f808b67a80 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/ChildNode.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on ChildNode interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="before, after, replaceWith, and remove of ChildNode interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#childnode"> +<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(); +}, 'remove on ChildNode'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/DOMStringMap.html b/testing/web-platform/tests/custom-elements/reactions/DOMStringMap.html new file mode 100644 index 0000000000..5e34dfe2ba --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/DOMStringMap.html @@ -0,0 +1,96 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on DOMStringMap interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="setter and deleter of DOMStringMap interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#domstringmap"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test(function () { + var element = define_new_custom_element(['data-foo']); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + instance.dataset.foo = 'bar'; + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'data-foo', oldValue: null, newValue: 'bar', namespace: null}); +}, 'setter on DOMStringMap must enqueue an attributeChanged reaction when adding an observed data attribute'); + +test(function () { + var element = define_new_custom_element(['data-bar']); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + instance.dataset.foo = 'bar'; + assert_array_equals(element.takeLog().types(), []); +}, 'setter on DOMStringMap must not enqueue an attributeChanged reaction when adding an unobserved data attribute'); + +test(function () { + var element = define_new_custom_element(['data-foo']); + var instance = document.createElement(element.name); + instance.dataset.foo = 'bar'; + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + instance.dataset.foo = 'baz'; + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'data-foo', oldValue: 'bar', newValue: 'baz', namespace: null}); +}, 'setter on DOMStringMap must enqueue an attributeChanged reaction when mutating the value of an observed data attribute'); + +test(function () { + var element = define_new_custom_element(['data-foo']); + var instance = document.createElement(element.name); + instance.dataset.foo = 'bar'; + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + instance.dataset.foo = 'bar'; + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'data-foo', oldValue: 'bar', newValue: 'bar', namespace: null}); +}, 'setter on DOMStringMap must enqueue an attributeChanged reaction when mutating the value of an observed data attribute to the same value'); + +test(function () { + var element = define_new_custom_element(['data-zero']); + var instance = document.createElement(element.name); + instance.dataset.foo = 'bar'; + assert_array_equals(element.takeLog().types(), ['constructed']); + instance.dataset.foo = 'baz'; + assert_array_equals(element.takeLog().types(), []); +}, 'setter on DOMStringMap must not enqueue an attributeChanged reaction when mutating the value of an unobserved data attribute'); + +test(function () { + var element = define_new_custom_element(['data-foo']); + var instance = document.createElement(element.name); + instance.dataset.foo = 'bar'; + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + delete instance.dataset.foo; + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'data-foo', oldValue: 'bar', newValue: null, namespace: null}); +}, 'deleter on DOMStringMap must enqueue an attributeChanged reaction when removing an observed data attribute'); + +test(function () { + var element = define_new_custom_element(['data-bar']); + var instance = document.createElement(element.name); + instance.dataset.foo = 'bar'; + assert_array_equals(element.takeLog().types(), ['constructed']); + delete instance.dataset.foo; + assert_array_equals(element.takeLog().types(), []); +}, 'deleter on DOMStringMap must not enqueue an attributeChanged reaction when removing an unobserved data attribute'); + +test(function () { + var element = define_new_custom_element(['data-foo']); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + delete instance.dataset.foo; + assert_array_equals(element.takeLog().types(), []); +}, 'deleter on DOMStringMap must not enqueue an attributeChanged reaction when it does not remove a data attribute'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/DOMTokenList.html b/testing/web-platform/tests/custom-elements/reactions/DOMTokenList.html new file mode 100644 index 0000000000..14a643c4a8 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/DOMTokenList.html @@ -0,0 +1,210 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on DOMTokenList interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="add, remove, toggle, replace, and the stringifier of DOMTokenList interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<script> + +test(function () { + var element = define_new_custom_element(['class']); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + instance.classList.add('foo'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: null, newValue: 'foo', namespace: null}); +}, 'add on DOMTokenList must enqueue an attributeChanged reaction when adding an attribute'); + +test(function () { + var element = define_new_custom_element(['style']); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + instance.classList.add('foo'); + assert_array_equals(element.takeLog().types(), []); +}, 'add on DOMTokenList must not enqueue an attributeChanged reaction when adding an unobserved attribute'); + +test(function () { + var element = define_new_custom_element(['class']); + var instance = document.createElement(element.name); + instance.setAttribute('class', 'hello'); + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + instance.classList.add('world'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello', newValue: 'hello world', namespace: null}); +}, 'add on DOMTokenList must enqueue an attributeChanged reaction when adding a value to an existing attribute'); + +test(function () { + var element = define_new_custom_element(['contenteditable']); + var instance = document.createElement(element.name); + instance.setAttribute('class', 'hello'); + assert_array_equals(element.takeLog().types(), ['constructed']); + instance.classList.add('world'); + assert_array_equals(element.takeLog().types(), []); +}, 'add on DOMTokenList must not enqueue an attributeChanged reaction when adding a value to an unobserved attribute'); + +test(function () { + var element = define_new_custom_element(['class']); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + instance.classList.add('hello', 'world'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: null, newValue: 'hello world', namespace: null}); +}, 'add on DOMTokenList must enqueue exactly one attributeChanged reaction when adding multiple values to an attribute'); + +test(function () { + var element = define_new_custom_element(['class']); + var instance = document.createElement(element.name); + instance.setAttribute('class', 'hello world'); + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + instance.classList.remove('world'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello world', newValue: 'hello', namespace: null}); +}, 'remove on DOMTokenList must enqueue an attributeChanged reaction when removing a value from an attribute'); + +test(function () { + var element = define_new_custom_element(['class']); + var instance = document.createElement(element.name); + instance.setAttribute('class', 'hello foo world bar'); + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + instance.classList.remove('hello', 'world'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello foo world bar', newValue: 'foo bar', namespace: null}); +}, 'remove on DOMTokenList must enqueue exactly one attributeChanged reaction when removing multiple values to an attribute'); + +test(function () { + var element = define_new_custom_element(['class']); + var instance = document.createElement(element.name); + instance.setAttribute('class', 'hello world'); + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + instance.classList.remove('foo'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello world', newValue: 'hello world', namespace: null}); +}, 'remove on DOMTokenList must enqueue an attributeChanged reaction even when removing a non-existent value from an attribute'); + +test(function () { + var element = define_new_custom_element(['title']); + var instance = document.createElement(element.name); + instance.setAttribute('class', 'hello world'); + assert_array_equals(element.takeLog().types(), ['constructed']); + instance.classList.remove('world'); + assert_array_equals(element.takeLog().types(), []); +}, 'remove on DOMTokenList must not enqueue an attributeChanged reaction when removing a value from an unobserved attribute'); + +test(function () { + var element = define_new_custom_element(['class']); + var instance = document.createElement(element.name); + instance.setAttribute('class', 'hello'); + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + instance.classList.toggle('world'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello', newValue: 'hello world', namespace: null}); +}, 'toggle on DOMTokenList must enqueue an attributeChanged reaction when adding a value to an attribute'); + +test(function () { + var element = define_new_custom_element(['class']); + var instance = document.createElement(element.name); + instance.setAttribute('class', 'hello world'); + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + instance.classList.toggle('world'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'class', oldValue: 'hello world', newValue: 'hello', namespace: null}); +}, 'toggle on DOMTokenList must enqueue an attributeChanged reaction when removing a value from an attribute'); + +test(function () { + var element = define_new_custom_element(['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> diff --git a/testing/web-platform/tests/custom-elements/reactions/Document.html b/testing/web-platform/tests/custom-elements/reactions/Document.html new file mode 100644 index 0000000000..1f05982a90 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/Document.html @@ -0,0 +1,156 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on Document interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="importNode and adoptNode of Document interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#document"> +<meta name="help" content="https://html.spec.whatwg.org/#document"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + const instance = contentDocument.createElement('custom-element'); + assert_array_equals(element.takeLog().types(), ['constructed']); + + const newDoc = contentDocument.implementation.createHTMLDocument(); + newDoc.importNode(instance); + + assert_array_equals(element.takeLog().types(), []); +}, 'importNode on Document must not construct a new custom element when importing a custom element into a window-less document'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + const template = contentDocument.createElement('template'); + template.innerHTML = '<custom-element></custom-element>'; + assert_array_equals(element.takeLog().types(), []); + contentDocument.importNode(template.content, true); + assert_array_equals(element.takeLog().types(), ['constructed']); +}, 'importNode on Document must construct a new custom element when importing a custom element from a template'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + const instance = contentDocument.createElement('custom-element'); + assert_array_equals(element.takeLog().types(), ['constructed']); + + const newDoc = contentDocument.implementation.createHTMLDocument(); + newDoc.adoptNode(instance); + + const logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['adopted']); + assert_equals(logEntries.last().oldDocument, contentDocument); + assert_equals(logEntries.last().newDocument, newDoc); +}, 'adoptNode on Document must enqueue an adopted reaction when importing a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + const instance = contentDocument.createElement('custom-element'); + + const container = contentDocument.createElement('div'); + container.contentEditable = true; + container.appendChild(instance); + contentDocument.body.appendChild(container); + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + + container.focus(); + contentDocument.execCommand('delete', false, null); + + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'execCommand on Document must enqueue a disconnected reaction when deleting a custom element from a contenteditable element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + + contentDocument.title = ''; + const title = contentDocument.querySelector('title'); + const instance = contentDocument.createElement('custom-element'); + title.appendChild(instance); + instance.textContent = 'hello'; + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(title.innerHTML, '<custom-element>hello</custom-element>'); + + title.text = 'world'; + assert_equals(title.innerHTML, 'world'); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'title on Document must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + + const body = contentDocument.body; + body.innerHTML = '<custom-element>hello</custom-element>'; + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(body.innerHTML, '<custom-element>hello</custom-element>'); + + contentDocument.body = contentDocument.createElement('body'); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'body on Document must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + + const instance = contentDocument.createElement('custom-element'); + const body = contentDocument.createElement('body'); + body.appendChild(instance); + + assert_array_equals(element.takeLog().types(), ['constructed']); + assert_equals(body.innerHTML, '<custom-element></custom-element>'); + + contentDocument.body = body; + assert_array_equals(element.takeLog().types(), ['connected']); +}, 'body on Document must enqueue connectedCallback when inserting a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = '<custom-element></custom-element>'; + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + + contentDocument.open(); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'open on Document must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = '<custom-element></custom-element>'; + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + + contentDocument.write(''); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'write on Document must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + contentWindow.document.open(); + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentWindow.document.write('<custom-element></custom-element>'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); +}, 'write on Document must enqueue connectedCallback after constructing a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = '<custom-element></custom-element>'; + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + + contentDocument.writeln(''); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'writeln on Document must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow) { + contentWindow.document.open(); + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentWindow.document.writeln('<custom-element></custom-element>'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); +}, 'writeln on Document must enqueue connectedCallback after constructing a custom element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/Element.html b/testing/web-platform/tests/custom-elements/reactions/Element.html new file mode 100644 index 0000000000..e1576734d0 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/Element.html @@ -0,0 +1,91 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on Element interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="id, className, slot, setAttribute, setAttributeNS, removeAttribute, removeAttributeNS, setAttributeNode, setAttributeNodeNS, removeAttributeNode, insertAdjacentElement, innerHTML, outerHTML, and insertAdjacentHTML of Element interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#element"> +<meta name="help" content="https://w3c.github.io/DOM-Parsing/"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +testReflectAttribute('id', 'id', 'foo', 'bar', 'id on Element'); +testReflectAttribute('className', 'class', 'foo', 'bar', 'className on Element'); +testReflectAttribute('slot', 'slot', 'foo', 'bar', 'slot on Element'); + +testAttributeAdder(function (element, name, value) { + element.setAttribute(name, value); +}, 'setAttribute on Element'); + +testAttributeAdder(function (element, name, value) { + element.setAttributeNS(null, name, value); +}, 'setAttributeNS on Element'); + +testAttributeRemover(function (element, name) { + element.removeAttribute(name); +}, 'removeAttribute on Element'); + +testAttributeRemover(function (element, name) { + element.removeAttributeNS(null, name); +}, 'removeAttributeNS on Element'); + +testAttributeRemover(function (element, name, value) { + if (element.hasAttribute(name)) + element.toggleAttribute(name); +}, 'toggleAttribute (only removes) on Element'); + +testAttributeRemover(function (element, name, value) { + element.toggleAttribute(name, false); +}, 'toggleAttribute (force false) on Element'); + +testAttributeAdder(function (element, name, value) { + var attr = document.createAttribute(name); + attr.value = value; + element.setAttributeNode(attr); +}, 'setAttributeNode on Element'); + +testAttributeAdder(function (element, name, value) { + var attr = document.createAttribute(name); + attr.value = value; + element.setAttributeNodeNS(attr); +}, 'setAttributeNodeNS on Element'); + +testAttributeRemover(function (element, name) { + var attr = element.getAttributeNode(name); + if (attr) + element.removeAttributeNode(element.getAttributeNode(name)); +}, 'removeAttributeNode on Element'); + +testNodeConnector(function (newContainer, element) { + newContainer.insertAdjacentElement('afterBegin', element); +}, 'insertAdjacentElement on Element'); + +testInsertingMarkup(function (newContainer, markup) { + newContainer.innerHTML = markup; +}, 'innerHTML on Element'); + +testNodeDisconnector(function (customElement) { + customElement.parentNode.innerHTML = ''; +}, 'innerHTML on Element'); + +testInsertingMarkup(function (newContainer, markup) { + newContainer.firstChild.outerHTML = markup; +}, 'outerHTML on Element'); + +testNodeDisconnector(function (customElement) { + customElement.outerHTML = ''; +}, 'outerHTML on Element'); + +testInsertingMarkup(function (newContainer, markup) { + newContainer.insertAdjacentHTML('afterBegin', markup); +}, 'insertAdjacentHTML on Element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/ElementContentEditable.html b/testing/web-platform/tests/custom-elements/reactions/ElementContentEditable.html new file mode 100644 index 0000000000..bdb10761cb --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/ElementContentEditable.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on ElementContentEditable interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="contentEditable of ElementContentEditable interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#elementcontenteditable"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +testReflectAttribute('contentEditable', 'contenteditable', 'true', 'false', 'contentEditable on ElementContentEditable'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/HTMLAnchorElement.html b/testing/web-platform/tests/custom-elements/reactions/HTMLAnchorElement.html new file mode 100644 index 0000000000..c6eeb1dce9 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/HTMLAnchorElement.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on HTMLAnchorElement interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="text of HTMLAnchorElement interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<a><custom-element>hello</custom-element></a>`; + const anchor = contentDocument.querySelector('a'); + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(anchor.innerHTML, '<custom-element>hello</custom-element>'); + + anchor.text = 'world'; + assert_equals(anchor.innerHTML, 'world'); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'text on HTMLAnchorElement must enqueue disconnectedCallback when removing a custom element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/HTMLElement.html b/testing/web-platform/tests/custom-elements/reactions/HTMLElement.html new file mode 100644 index 0000000000..0a1d40199e --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/HTMLElement.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on HTMLElement interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="title, lang, translate, dir, hidden, tabIndex, accessKey, draggable, dropzone, contextMenu, spellcheck, popover, innerText, and outerText of HTMLElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#htmlelement"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +testReflectAttribute('title', 'title', 'foo', 'bar', 'title on HTMLElement'); +testReflectAttribute('lang', 'lang', 'en', 'zh', 'lang on HTMLElement'); +testReflectAttributeWithContentValues('translate', 'translate', true, 'yes', false, 'no', 'translate on HTMLElement'); +testReflectAttribute('dir', 'dir', 'ltr', 'rtl', 'dir on HTMLElement'); +testReflectBooleanAttribute('hidden', 'hidden', 'hidden on HTMLElement'); +testReflectAttribute('tabIndex', 'tabindex', '0', '1', 'tabIndex on HTMLElement'); +testReflectAttribute('accessKey', 'accesskey', 'a', 'b', 'accessKey on HTMLElement'); +testReflectAttributeWithContentValues('draggable', 'draggable', true, 'true', false, 'false', 'draggable on HTMLElement'); +testReflectAttributeWithContentValues('spellcheck', 'spellcheck', true, 'true', false, 'false', 'spellcheck on HTMLElement'); +testReflectAttribute('popover', 'popover', 'auto', 'manual', 'popover on HTMLElement'); + +testNodeDisconnector(function (customElement) { + customElement.parentNode.innerText = ''; +}, 'innerText on HTMLElement'); + +testNodeDisconnector(function (customElement) { + customElement.outerText = ''; +}, 'outerText on HTMLElement'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/HTMLOptionElement.html b/testing/web-platform/tests/custom-elements/reactions/HTMLOptionElement.html new file mode 100644 index 0000000000..418ef282b3 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/HTMLOptionElement.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on HTMLOptionElement interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="text of HTMLOptionElement interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<select><option></option></select>`; + const option = contentDocument.querySelector('option'); + const instance = document.createElement('custom-element'); + option.appendChild(instance); + instance.textContent = 'hello'; + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(option.innerHTML, '<custom-element>hello</custom-element>'); + + option.text = 'world'; + assert_equals(option.innerHTML, 'world'); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'text on HTMLOptionElement must enqueue disconnectedCallback when removing a custom element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/HTMLOptionsCollection.html b/testing/web-platform/tests/custom-elements/reactions/HTMLOptionsCollection.html new file mode 100644 index 0000000000..0d64259d06 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/HTMLOptionsCollection.html @@ -0,0 +1,122 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on HTMLOptionsCollection interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="length, the indexed setter, add, and remove of HTMLOptionsCollection interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<select><option></option></select>`; + const option = contentDocument.querySelector('option'); + + const instance = contentDocument.createElement(element.name); + option.appendChild(instance); + instance.textContent = 'hello'; + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(option.innerHTML, '<custom-element>hello</custom-element>'); + + const select = contentDocument.querySelector('select'); + assert_equals(select.options[0], option); + select.options.length = 0; + assert_equals(select.firstChild, null); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'length on HTMLOptionsCollection must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + + const contentDocument = contentWindow.document; + contentDocument.body.innerHTML = `<select></select>`; + const select = contentDocument.querySelector('select'); + + const option = contentDocument.createElement('option'); + const instance = contentDocument.createElement(element.name); + option.appendChild(instance); + instance.textContent = 'hello'; + + assert_array_equals(element.takeLog().types(), ['constructed']); + assert_equals(option.innerHTML, '<custom-element>hello</custom-element>'); + + assert_equals(select.options.length, 0); + select.options[0] = option; + assert_equals(select.options.length, 1); + assert_array_equals(element.takeLog().types(), ['connected']); +}, 'The indexed setter on HTMLOptionsCollection must enqueue connectedCallback when inserting a custom element'); + +test_with_window(function (contentWindow) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + + const contentDocument = contentWindow.document; + contentDocument.body.innerHTML = `<select><option></option></select>`; + const option = contentDocument.querySelector('option'); + + const instance = contentDocument.createElement(element.name); + option.appendChild(instance); + instance.textContent = 'hello'; + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(option.innerHTML, '<custom-element>hello</custom-element>'); + + const select = contentDocument.querySelector('select'); + assert_equals(select.options[0], option); + select.options[0] = null; + assert_equals(select.options.length, 0); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'The indexed setter on HTMLOptionsCollection must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + + const contentDocument = contentWindow.document; + contentDocument.body.innerHTML = `<select></select>`; + const select = contentDocument.querySelector('select'); + + const option = contentDocument.createElement('option'); + const instance = contentDocument.createElement(element.name); + option.appendChild(instance); + instance.textContent = 'hello'; + + assert_array_equals(element.takeLog().types(), ['constructed']); + assert_equals(option.innerHTML, '<custom-element>hello</custom-element>'); + + assert_equals(select.options.length, 0); + select.options.add(option); + assert_equals(select.options.length, 1); + assert_array_equals(element.takeLog().types(), ['connected']); +}, 'add on HTMLOptionsCollection must enqueue connectedCallback when inserting a custom element'); + +test_with_window(function (contentWindow) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + + const contentDocument = contentWindow.document; + contentDocument.body.innerHTML = `<select><option></option></select>`; + const option = contentDocument.querySelector('option'); + + const instance = contentDocument.createElement(element.name); + option.appendChild(instance); + instance.textContent = 'hello'; + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(option.innerHTML, '<custom-element>hello</custom-element>'); + + const select = contentDocument.querySelector('select'); + assert_equals(select.options[0], option); + select.options.remove(0); + assert_equals(select.options.length, 0); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'remove on HTMLOptionsCollection must enqueue disconnectedCallback when removing a custom element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/HTMLOutputElement.html b/testing/web-platform/tests/custom-elements/reactions/HTMLOutputElement.html new file mode 100644 index 0000000000..02e669bc7a --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/HTMLOutputElement.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on HTMLOutputElement interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="value and defaultValue of HTMLOutputElement interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<output><custom-element>hello</custom-element></output>`; + const anchor = contentDocument.querySelector('output'); + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(anchor.innerHTML, '<custom-element>hello</custom-element>'); + + anchor.value = 'world'; + assert_equals(anchor.innerHTML, 'world'); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'value on HTMLOutputElement must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<output><custom-element>hello</custom-element></output>`; + const anchor = contentDocument.querySelector('output'); + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(anchor.innerHTML, '<custom-element>hello</custom-element>'); + + anchor.defaultValue = 'world'; + assert_equals(anchor.innerHTML, 'world'); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'defaultValue on HTMLOutputElement must enqueue disconnectedCallback when removing a custom element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/HTMLSelectElement.html b/testing/web-platform/tests/custom-elements/reactions/HTMLSelectElement.html new file mode 100644 index 0000000000..7c79634f66 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/HTMLSelectElement.html @@ -0,0 +1,122 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on HTMLSelectElement interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="length, add, remove, and the setter of HTMLSelectElement interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<select><option></option></select>`; + const option = contentDocument.querySelector('option'); + + const instance = contentDocument.createElement(element.name); + option.appendChild(instance); + instance.textContent = 'hello'; + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(option.innerHTML, '<custom-element>hello</custom-element>'); + + const select = contentDocument.querySelector('select'); + assert_equals(select.length, 1); + select.length = 0; + assert_equals(select.firstChild, null); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'length on HTMLSelectElement must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + + const contentDocument = contentWindow.document; + contentDocument.body.innerHTML = `<select></select>`; + const select = contentDocument.querySelector('select'); + + const option = contentDocument.createElement('option'); + const instance = contentDocument.createElement(element.name); + option.appendChild(instance); + instance.textContent = 'hello'; + + assert_array_equals(element.takeLog().types(), ['constructed']); + assert_equals(option.innerHTML, '<custom-element>hello</custom-element>'); + + assert_equals(select.options.length, 0); + select[0] = option; + assert_equals(select.options.length, 1); + assert_array_equals(element.takeLog().types(), ['connected']); +}, 'The indexed setter on HTMLSelectElement must enqueue connectedCallback when inserting a custom element'); + +test_with_window(function (contentWindow) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + + const contentDocument = contentWindow.document; + contentDocument.body.innerHTML = `<select><option></option></select>`; + const option = contentDocument.querySelector('option'); + + const instance = contentDocument.createElement(element.name); + option.appendChild(instance); + instance.textContent = 'hello'; + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(option.innerHTML, '<custom-element>hello</custom-element>'); + + const select = contentDocument.querySelector('select'); + assert_equals(select.options[0], option); + select[0] = null; + assert_equals(select.options.length, 0); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'The indexed setter on HTMLSelectElement must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + + const contentDocument = contentWindow.document; + contentDocument.body.innerHTML = `<select></select>`; + const select = contentDocument.querySelector('select'); + + const option = contentDocument.createElement('option'); + const instance = contentDocument.createElement(element.name); + option.appendChild(instance); + instance.textContent = 'hello'; + + assert_array_equals(element.takeLog().types(), ['constructed']); + assert_equals(option.innerHTML, '<custom-element>hello</custom-element>'); + + assert_equals(select.options.length, 0); + select.add(option); + assert_equals(select.options.length, 1); + assert_array_equals(element.takeLog().types(), ['connected']); +}, 'add on HTMLSelectElement must enqueue connectedCallback when inserting a custom element'); + +test_with_window(function (contentWindow) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + + const contentDocument = contentWindow.document; + contentDocument.body.innerHTML = `<select><option></option></select>`; + const option = contentDocument.querySelector('option'); + + const instance = contentDocument.createElement(element.name); + option.appendChild(instance); + instance.textContent = 'hello'; + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(option.innerHTML, '<custom-element>hello</custom-element>'); + + const select = contentDocument.querySelector('select'); + assert_equals(select.options[0], option); + select.remove(0); + assert_equals(select.options.length, 0); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'remove on HTMLSelectElement must enqueue disconnectedCallback when removing a custom element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/HTMLTableElement.html b/testing/web-platform/tests/custom-elements/reactions/HTMLTableElement.html new file mode 100644 index 0000000000..6adf2623d6 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/HTMLTableElement.html @@ -0,0 +1,173 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on HTMLTableElement interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="caption, deleteCaption, thead, deleteTHead, tFoot, deleteTFoot, and deleteRow of HTMLTableElement interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table></table>`; + const table = contentDocument.querySelector('table'); + + const caption = contentDocument.createElement('caption'); + caption.innerHTML = '<custom-element>hello</custom-element>'; + + assert_array_equals(element.takeLog().types(), ['constructed']); + assert_equals(caption.innerHTML, '<custom-element>hello</custom-element>'); + + assert_equals(table.caption, null); + table.caption = caption; + assert_array_equals(element.takeLog().types(), ['connected']); +}, 'caption on HTMLTableElement must enqueue connectedCallback when inserting a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table><caption><custom-element>hello</custom-element></caption></table>`; + const caption = contentDocument.querySelector('caption'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(caption.innerHTML, '<custom-element>hello</custom-element>'); + + const table = contentDocument.querySelector('table'); + assert_equals(table.caption, caption); + const newCaption = contentDocument.createElement('caption'); + table.caption = newCaption; // Chrome doesn't support setting to null. + assert_equals(table.caption, newCaption); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'caption on HTMLTableElement must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table><caption><custom-element>hello</custom-element></caption></table>`; + const caption = contentDocument.querySelector('caption'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(caption.innerHTML, '<custom-element>hello</custom-element>'); + + const table = contentDocument.querySelector('table'); + assert_equals(table.caption, caption); + const newCaption = contentDocument.createElement('caption'); + table.deleteCaption(); + assert_equals(table.caption, null); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'deleteCaption() on HTMLTableElement must enqueue disconnectedCallback when removing a custom element'); + + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table></table>`; + const table = contentDocument.querySelector('table'); + + const thead = contentDocument.createElement('thead'); + thead.innerHTML = '<tr><td><custom-element>hello</custom-element></td></tr>'; + + assert_array_equals(element.takeLog().types(), ['constructed']); + assert_equals(thead.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>'); + + assert_equals(table.tHead, null); + table.tHead = thead; + assert_array_equals(element.takeLog().types(), ['connected']); +}, 'tHead on HTMLTableElement must enqueue connectedCallback when inserting a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table><thead><tr><td><custom-element>hello</custom-element></td></tr></thead></table>`; + const thead = contentDocument.querySelector('thead'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(thead.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>'); + + const table = contentDocument.querySelector('table'); + assert_equals(table.tHead, thead); + const newThead = contentDocument.createElement('thead'); + table.tHead = newThead; // Chrome doesn't support setting to null. + assert_equals(table.tHead, newThead); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'tHead on HTMLTableElement must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table><thead><tr><td><custom-element>hello</custom-element></td></tr></thead></table>`; + const thead = contentDocument.querySelector('thead'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(thead.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>'); + + const table = contentDocument.querySelector('table'); + assert_equals(table.tHead, thead); + table.deleteTHead(); + assert_equals(table.tHead, null); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'deleteTHead() on HTMLTableElement must enqueue disconnectedCallback when removing a custom element'); + + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table></table>`; + const table = contentDocument.querySelector('table'); + + const tfoot = contentDocument.createElement('tfoot'); + tfoot.innerHTML = '<tr><td><custom-element>hello</custom-element></td></tr>'; + + assert_array_equals(element.takeLog().types(), ['constructed']); + assert_equals(tfoot.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>'); + + assert_equals(table.tFoot, null); + table.tFoot = tfoot; + assert_array_equals(element.takeLog().types(), ['connected']); +}, 'tFoot on HTMLTableElement must enqueue connectedCallback when inserting a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table><tfoot><tr><td><custom-element>hello</custom-element></td></tr></tfoot></table>`; + const tfoot = contentDocument.querySelector('tfoot'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(tfoot.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>'); + + const table = contentDocument.querySelector('table'); + assert_equals(table.tFoot, tfoot); + const newThead = contentDocument.createElement('tfoot'); + table.tFoot = newThead; // Chrome doesn't support setting to null. + assert_equals(table.tFoot, newThead); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'tFoot on HTMLTableElement must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table><tfoot><tr><td><custom-element>hello</custom-element></td></tr></tfoot></table>`; + const tfoot = contentDocument.querySelector('tfoot'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(tfoot.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>'); + + const table = contentDocument.querySelector('table'); + assert_equals(table.tFoot, tfoot); + table.deleteTFoot(); + assert_equals(table.tFoot, null); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'deleteTFoot() on HTMLTableElement must enqueue disconnectedCallback when removing a custom element'); + + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table><tr><td><custom-element>hello</custom-element></td></tr></table>`; + const tr = contentDocument.querySelector('tr'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(tr.innerHTML, '<td><custom-element>hello</custom-element></td>'); + + const table = contentDocument.querySelector('table'); + assert_equals(table.rows.length, 1); + assert_equals(table.rows[0], tr); + table.deleteRow(0); + assert_equals(table.rows.length, 0); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'deleteRow() on HTMLTableElement must enqueue disconnectedCallback when removing a custom element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/HTMLTableRowElement.html b/testing/web-platform/tests/custom-elements/reactions/HTMLTableRowElement.html new file mode 100644 index 0000000000..a9a00a5da3 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/HTMLTableRowElement.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on HTMLTableRowElement interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="deleteCell of HTMLTableRowElement interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table><tr><td><custom-element>hello</custom-element></td></tr></table>`; + const td = contentDocument.querySelector('td'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(td.innerHTML, '<custom-element>hello</custom-element>'); + + const table = contentDocument.querySelector('table'); + const row = table.rows[0]; + assert_equals(row.cells[0], td); + row.deleteCell(0); + assert_equals(row.cells.length, 0); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'deleteCell() on HTMLTableRowElement must enqueue disconnectedCallback when removing a custom element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/HTMLTableSectionElement.html b/testing/web-platform/tests/custom-elements/reactions/HTMLTableSectionElement.html new file mode 100644 index 0000000000..cbb0a146e8 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/HTMLTableSectionElement.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on HTMLTableSectionElement interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="deleteRow of HTMLTableSectionElement interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table><thead><tr><td><custom-element>hello</custom-element></td></tr></thead></table>`; + const thead = contentDocument.querySelector('thead'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(thead.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>'); + + const table = contentDocument.querySelector('table'); + assert_equals(table.tHead, thead); + table.tHead.deleteRow(0); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'deleteRow() on HTMLTableSectionElement on thead must enqueue disconnectedCallback when removing a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + contentDocument.body.innerHTML = `<table><tfoot><tr><td><custom-element>hello</custom-element></td></tr></tfoot></table>`; + const tfoot = contentDocument.querySelector('tfoot'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + assert_equals(tfoot.innerHTML, '<tr><td><custom-element>hello</custom-element></td></tr>'); + + const table = contentDocument.querySelector('table'); + assert_equals(table.tFoot, tfoot); + table.tFoot.deleteRow(0); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'deleteRow() on HTMLTableSectionElement on tfoot must enqueue disconnectedCallback when removing a custom element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/HTMLTitleElement.html b/testing/web-platform/tests/custom-elements/reactions/HTMLTitleElement.html new file mode 100644 index 0000000000..6678944c91 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/HTMLTitleElement.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on HTMLTitleElement interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="text of HTMLTitleElement interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + const instance = contentWindow.document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + + contentWindow.document.title = 'hello'; + const titleElement = contentDocument.querySelector('title'); + titleElement.appendChild(instance); + assert_array_equals(element.takeLog().types(), ['connected']); + assert_equals(titleElement.childNodes.length, 2); + + titleElement.text = 'world'; + assert_equals(titleElement.childNodes.length, 1); + assert_array_equals(element.takeLog().types(), ['disconnected']); +}, 'text on HTMLTitleElement must enqueue disconnectedCallback when removing a custom element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/NamedNodeMap.html b/testing/web-platform/tests/custom-elements/reactions/NamedNodeMap.html new file mode 100644 index 0000000000..fa21b3ada9 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/NamedNodeMap.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on NamedNodeMap interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="setNamedItem, setNamedItemNS, removeNameditem, and removeNamedItemNS of NamedNodeMap interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +testAttributeAdder(function (element, name, value) { + var attr = element.ownerDocument.createAttribute(name); + attr.value = value; + element.attributes.setNamedItem(attr); +}, 'setNamedItem on NamedNodeMap'); + +testAttributeAdder(function (element, name, value) { + var attr = element.ownerDocument.createAttribute(name); + attr.value = value; + element.attributes.setNamedItemNS(attr); +}, 'setNamedItemNS on NamedNodeMap'); + +testAttributeRemover(function (element, name) { + element.attributes.removeNamedItem(name); +}, 'removeNamedItem on NamedNodeMap', {onlyExistingAttribute: true}); + +testAttributeRemover(function (element, name) { + element.attributes.removeNamedItemNS(null, name); +}, 'removeNamedItemNS on NamedNodeMap', {onlyExistingAttribute: true}); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/Node.html b/testing/web-platform/tests/custom-elements/reactions/Node.html new file mode 100644 index 0000000000..94da3d020e --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/Node.html @@ -0,0 +1,49 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on Node interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="nodeValue, textContent, normalize, cloneNode, insertBefore, appendChild, replaceChild, and removeChild of Node interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +testAttributeMutator(function (element, name, value) { + element.getAttributeNode(name).nodeValue = value; +}, 'nodeValue on Node'); + +testAttributeMutator(function (element, name, value) { + element.getAttributeNode(name).textContent = value; +}, 'textContent on Node'); + +// FIXME: Add a test for normalize() + +testCloner(function (customElement) { + return customElement.cloneNode(false); +}, 'cloneNode on Node'); + +testNodeConnector(function (newContainer, customElement) { + newContainer.insertBefore(customElement, newContainer.firstChild); +}, 'insertBefore on ChildNode'); + +testNodeConnector(function (newContainer, customElement) { + newContainer.appendChild(customElement); +}, 'appendChild on ChildNode'); + +testNodeConnector(function (newContainer, customElement) { + newContainer.replaceChild(customElement, newContainer.firstChild); +}, 'replaceChild on ChildNode'); + +testNodeDisconnector(function (customElement) { + customElement.parentNode.removeChild(customElement); +}, 'removeChild on ChildNode'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/ParentNode.html b/testing/web-platform/tests/custom-elements/reactions/ParentNode.html new file mode 100644 index 0000000000..b143b5a982 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/ParentNode.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on ParentNode interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="prepend and append of ParentNode interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#parentnode"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +testNodeConnector(function (newContainer, customElement) { + newContainer.prepend(customElement); +}, 'prepend on ParentNode'); + +testNodeConnector(function (newContainer, customElement) { + newContainer.append(customElement); +}, 'append on ParentNode'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/Range.html b/testing/web-platform/tests/custom-elements/reactions/Range.html new file mode 100644 index 0000000000..c4a8252ff6 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/Range.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on Range interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="deleteContents, extractContents, cloneContents, insertNode, and surroundContents of Range interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +testNodeDisconnector(function (customElement) { + var range = document.createRange(); + range.selectNode(customElement); + range.deleteContents(); +}, 'deleteContents on Range'); + +testNodeDisconnector(function (customElement) { + var range = document.createRange(); + range.selectNode(customElement); + range.extractContents(); +}, 'extractContents on Range'); + +testCloner(function (customElement) { + var range = document.createRange(); + range.selectNode(customElement); + range.cloneContents(); +}, 'cloneContents on Range') + +testNodeConnector(function (container, customElement) { + var range = document.createRange(); + range.selectNodeContents(container); + range.insertNode(customElement); +}, 'insertNode on Range'); + +testNodeConnector(function (container, customElement) { + var range = document.createRange(); + range.selectNodeContents(container); + range.surroundContents(customElement); +}, 'surroundContents on Range'); + +testParsingMarkup(function (document, markup) { + var range = document.createRange(); + return range.createContextualFragment(markup); +}, 'createContextualFragment on Range'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/Selection.html b/testing/web-platform/tests/custom-elements/reactions/Selection.html new file mode 100644 index 0000000000..84214201aa --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/Selection.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on Selection interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="deleteFromDocument of Selection interface must have CEReactions"> +<meta name="help" content="http://w3c.github.io/selection-api/#selection-interface"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +testNodeDisconnector(function (customElement, window) { + let selection = window.getSelection(); + let parent = customElement.parentNode; + + // WebKit and Blink "normalizes" selection in selectAllChildren and not select the empty customElement. + // Workaround this orthogonal non-standard behavior by inserting text nodes around the custom element. + parent.prepend(document.createTextNode('start')); + parent.append(document.createTextNode('end')); + + selection.selectAllChildren(parent); + selection.deleteFromDocument(); +}, 'deleteFromDocument on Selection'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/ShadowRoot.html b/testing/web-platform/tests/custom-elements/reactions/ShadowRoot.html new file mode 100644 index 0000000000..9997d9c836 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/ShadowRoot.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<html> +<head> +<title>Custom Elements: CEReactions on ShadowRoot interface</title> +<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> +<meta name="assert" content="innerHTML of ShadowRoot interface must have CEReactions"> +<meta name="help" content="https://dom.spec.whatwg.org/#node"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> +<script src="./resources/reactions.js"></script> +</head> +<body> +<div id="log"></div> +<script> + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + const host = contentDocument.createElement('div'); + const shadowRoot = host.attachShadow({mode: 'closed'}); + shadowRoot.innerHTML = '<custom-element></custom-element>'; + + assert_array_equals(element.takeLog().types(), ['constructed']); +}, 'innerHTML on ShadowRoot must upgrade a custom element'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + const host = contentDocument.createElement('div'); + contentDocument.body.appendChild(host); + const shadowRoot = host.attachShadow({mode: 'closed'}); + shadowRoot.innerHTML = '<custom-element></custom-element>'; + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); +}, 'innerHTML on ShadowRoot must enqueue connectedCallback on newly upgraded custom elements when the shadow root is connected'); + +test_with_window(function (contentWindow, contentDocument) { + const element = define_custom_element_in_window(contentWindow, 'custom-element', []); + const host = contentDocument.createElement('div'); + contentDocument.body.appendChild(host); + + const shadowRoot = host.attachShadow({mode: 'closed'}); + shadowRoot.innerHTML = '<custom-element></custom-element>'; + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + + shadowRoot.innerHTML = ''; + assert_array_equals(element.takeLog().types(), ['disconnected']); + +}, 'innerHTML on ShadowRoot must enqueue disconnectedCallback when removing a custom element'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLAreaElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLAreaElement.html new file mode 100644 index 0000000000..3d53ff87ff --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLAreaElement.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLAreaElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="alt, coords, shape, target, download, ping, rel, + referrerPolicy of HTMLAreaElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-area-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> + +<map name="yellow" id="map"> +</map> +<img usemap="#yellow" src="/images/yellow.png" alt="yellow pic"> + +<script> + +function getParentElement() { + let map = document.getElementById('map'); + return map; +} + +function setAttributes(instance) { + instance.setAttribute('href', '/images/yellow.png'); +} + +testReflectAttributeWithDependentAttributes( + 'alt', 'alt', 'yellow pic', + 'yellow pic2', 'alt on HTMLAreaElement', 'area', + getParentElement, instance => setAttributes(instance), HTMLAreaElement +); +testReflectAttributeWithParentNode( + 'coords', 'coords', '1, 1, 5, 5', + '2, 2, 6, 6', 'coords on HTMLAreaElement', 'area', + getParentElement, HTMLAreaElement +); +testReflectAttributeWithDependentAttributes( + 'shape', 'shape', 'rectangle', + 'default', 'shape on HTMLAreaElement', 'area', + getParentElement, instance => instance.setAttribute('coords', '1, 1, 5, 5'), + HTMLAreaElement +); +testReflectAttributeWithDependentAttributes( + 'target', 'target', '_blank', + '_top', 'target on HTMLAreaElement', 'area', + getParentElement, instance => setAttributes(instance), HTMLAreaElement +); +testReflectAttributeWithDependentAttributes( + 'download', 'download', 'pic1', + 'pic2', 'download on HTMLAreaElement', 'area', + getParentElement, instance => setAttributes(instance), HTMLAreaElement +); +testReflectAttributeWithDependentAttributes( + 'ping', 'ping', 'location.href', + `${location.protocol}\/\/${location.host}`, 'ping on HTMLAreaElement', 'area', + getParentElement, instance => setAttributes(instance), HTMLAreaElement +); +testReflectAttributeWithDependentAttributes( + 'rel', 'rel', 'help', + 'noreferrer', 'rel on HTMLAreaElement', 'area', + getParentElement, instance => setAttributes(instance), HTMLAreaElement +); +testReflectAttributeWithDependentAttributes( + 'referrerPolicy', 'referrerpolicy', 'same-origin', + 'origin', 'referrerPolicy on HTMLAreaElement', 'area', + getParentElement, instance => setAttributes(instance), HTMLAreaElement +); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLBaseElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLBaseElement.html new file mode 100644 index 0000000000..8d8470074c --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLBaseElement.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<head> +<title>Custom Elements: CEReactions on HTMLBaseElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="href, target of HTMLBaseElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-base-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> +<script> + +function getParentElement() { + return document.head; +} + +testReflectAttributeWithParentNode('href', 'href', '/', 'http://example.com/', 'href on HTMLBaseElement', 'base', getParentElement, HTMLBaseElement); +testReflectAttributeWithParentNode('target', 'target', '_blank', '_self', 'target on HTMLBaseElement', 'base', getParentElement, HTMLBaseElement); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLButtonElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLButtonElement.html new file mode 100644 index 0000000000..62f8b7b0c9 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLButtonElement.html @@ -0,0 +1,80 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLButtonElement interface</title> +<meta name="author" title="Zhang Xiaoyu" href="xiaoyux.zhang@intel.com"> +<meta name="assert" content=" autofocus, disabled, formAction, formEnctype, + formMethod, formNoValidate, formTarget, name, type, value + of HTMLButtonElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#htmlbuttonelement"> +<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> +<body> +<script> + +function getParentElement(parentElementName) { + let parentElement = document.createElement(parentElementName); + document.body.appendChild(parentElement); + return parentElement; +} + +function setAttributes(instance) { + instance.setAttribute('type', 'submit'); +} + +testReflectBooleanAttribute( + 'autofocus', 'autofocus', 'autofocus on HTMLButtonElement', + 'button', HTMLButtonElement +); +testReflectBooleanAttribute( + 'disabled', 'disabled','disabled on HTMLButtonElement', + 'button', HTMLButtonElement +); +testReflectAttribute( + 'name', 'name', 'intel', + 'intel1', 'name on HTMLButtonElement', 'button', + HTMLButtonElement +); +testReflectAttribute( + 'value', 'value', 'HTML', + 'CSS', 'value on HTMLButtonElement', 'button', + HTMLButtonElement +); +testReflectAttributeWithParentNode( + 'type', 'type', 'submit', + 'reset', 'type on HTMLButtonElement', 'button', + () => getParentElement('form'), HTMLButtonElement +); +testReflectAttributeWithDependentAttributes( + 'formAction', 'formaction', 'intel.asp', + 'intel1.asp', 'formAction on HTMLButtonElement', 'button', + () => getParentElement('form'), instance => setAttributes(instance), + HTMLButtonElement +); +testReflectAttributeWithDependentAttributes( + 'formEnctype', 'formenctype', 'text/plain', 'multipart/form-data', + 'formEnctype on HTMLButtonElement', 'button', () => getParentElement('form'), + instance => setAttributes(instance), + HTMLButtonElement +); +testReflectAttributeWithDependentAttributes( + 'formMethod', 'formmethod', 'get', + 'post', 'formMethod on HTMLButtonElement', 'button', + () => getParentElement('form'), instance => setAttributes(instance), + HTMLButtonElement +); +testReflectBooleanAttributeWithDependentAttributes( + 'formNoValidate', 'formnovalidate', 'formNoValidate on HTMLButtonElement', + 'button', () => getParentElement('form'), + instance => setAttributes(instance), + HTMLButtonElement +); +testReflectAttributeWithDependentAttributes( + 'formTarget', 'formtarget', '_blank', + '_self', 'formTarget on HTMLButtonElement', 'button', + () => getParentElement('form'), instance => setAttributes(instance), + HTMLButtonElement +); + +</script> +</body> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLCanvasElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLCanvasElement.html new file mode 100644 index 0000000000..6c7119252e --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLCanvasElement.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLCanvasElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="width, height of HTMLCanvasElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-canvas-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> + +<script> + +testReflectAttribute('width', 'width', '15', '20', 'width on HTMLCanvasElement', 'canvas', HTMLCanvasElement); +testReflectAttribute('height', 'height', '23', '45', 'height on HTMLCanvasElement', 'canvas', HTMLCanvasElement); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLDataElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLDataElement.html new file mode 100644 index 0000000000..f078c6aa02 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLDataElement.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLDataElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="value of HTMLDataElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-data-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> + +<script> + +testReflectAttribute('value', 'value', '1234', '2345', 'name on HTMLDataElement', 'data', HTMLDataElement); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLDetailsElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLDetailsElement.html new file mode 100644 index 0000000000..4d81e3e627 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLDetailsElement.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLDetailsElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="open of HTMLDetailsElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-details-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> + +<script> + +testReflectBooleanAttribute('open', 'open', 'open on HTMLDetailsElement', 'details', HTMLDetailsElement); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLEmbedElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLEmbedElement.html new file mode 100644 index 0000000000..923bde7583 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLEmbedElement.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLEmbedElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="src, type, width, height of + HTMLEmbedElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-embed-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> + +<script> + +testReflectAttribute( + 'src', 'src', '/media/movie_5.mp4', + '/media/sound_5.mp3', 'src on HTMLEmbedElement', 'embed', + HTMLEmbedElement +); +testReflectAttribute( + 'type', 'type', 'video/webm', + 'video/mp4', 'type on HTMLEmbedElement', 'embed', + HTMLEmbedElement +); +testReflectAttribute( + 'width', 'width', '100', + '120', 'width on HTMLEmbedElement', 'embed', + HTMLEmbedElement +); +testReflectAttribute( + 'height', 'height', '100', + '120', 'height on HTMLEmbedElement', 'embed', + HTMLEmbedElement +); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLFieldSetElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLFieldSetElement.html new file mode 100644 index 0000000000..517523551b --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLFieldSetElement.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLFieldSetElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="disabled, name of + HTMLFieldSetElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-fieldset-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> + +<body> +<script> + +function getParentElement() { + let form = document.createElement("form"); + document.body.appendChild(form); + return form; +} + +testReflectBooleanAttributeWithParentNode( + 'disabled', 'disabled', 'disabled on HTMLFieldSetElement', + 'fieldset', getParentElement, HTMLFieldSetElement +); +testReflectAttributeWithParentNode( + 'name', 'name', 'fieldset1', + 'fieldset2', 'name on HTMLFieldSetElement', 'fieldset', + getParentElement, HTMLFieldSetElement +); + +</script> +</body> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLImageElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLImageElement.html new file mode 100644 index 0000000000..656e29eb17 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLImageElement.html @@ -0,0 +1,89 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLImageElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="alt, src, srcset, sizes, crossOrigin, useMap, + isMap, width, height, referrerPolicy, decoding of + HTMLImageElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-img-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> + +<map name="yellow"></map> +<map name="green"></map> +<a href="/" id="a"> +</a> +<body> +<script> + +function getParentElement() { + return document.body; +} + +function setAttributes(instance) { + instance.setAttribute('src', '/images/green-1x1.png'); +} + +testReflectAttributeWithDependentAttributes( + 'alt', 'alt', 'image1', + 'image2', 'alt on HTMLImageElement', 'img', + getParentElement, instance => setAttributes(instance), HTMLImageElement +); +testReflectAttributeWithParentNode( + 'src', 'src', '/images/green-1x1.png', + '/images/green-2x2.png', 'src on HTMLImageElement', 'img', + getParentElement, HTMLImageElement +); +testReflectAttributeWithDependentAttributes( + 'srcset', 'srcset', '/images/green.png', + '/images/green-2x2.png', 'srcset on HTMLImageElement', 'img', + getParentElement, instance => setAttributes(instance), HTMLImageElement +); +testReflectAttributeWithDependentAttributes( + 'sizes', 'sizes', '(max-width: 32px) 28px', + '(max-width: 48px) 44px', 'sizes on HTMLImageElement', 'img', + getParentElement, instance => { + instance.setAttribute('src', '/images/green-1x1.png'); + instance.setAttribute('srcset', '/images/green-2x2.png 1x'); + }, HTMLImageElement +); +testReflectAttributeWithDependentAttributes( + 'crossOrigin', 'crossorigin', 'use-credentials', + 'anonymous', 'crossOrigin on HTMLImageElement', 'img', + getParentElement, instance => setAttributes(instance), HTMLImageElement +); +testReflectAttributeWithDependentAttributes( + 'useMap', 'usemap', '#yellow', + '#green', 'useMap on HTMLImageElement', 'img', + getParentElement, instance => setAttributes(instance), HTMLImageElement +); +testReflectBooleanAttributeWithDependentAttributes( + 'isMap', 'ismap', 'isMap on HTMLImageElement', + 'img', () => { return document.getElementById('a') }, + instance => setAttributes(instance), + HTMLImageElement +); +testReflectAttributeWithDependentAttributes( + 'width', 'width', '1', + '2', 'width on HTMLImageElement', 'img', + getParentElement, instance => setAttributes(instance), HTMLImageElement +); +testReflectAttributeWithDependentAttributes( + 'height', 'height', '1', + '2', 'height on HTMLImageElement', 'img', + getParentElement, instance => setAttributes(instance), HTMLImageElement +); +testReflectAttributeWithDependentAttributes( + 'referrerPolicy', 'referrerpolicy', 'same-origin', + 'origin', 'referrerPolicy on HTMLImageElement', 'img', + getParentElement, instance => setAttributes(instance), HTMLImageElement +); +testReflectAttributeWithDependentAttributes( + 'decoding', 'decoding', 'async', + 'sync', 'decoding on HTMLImageElement', 'img', + getParentElement, instance => setAttributes(instance), HTMLImageElement +); + +</script> +</body> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLInputElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLInputElement.html new file mode 100644 index 0000000000..adf43ee74d --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLInputElement.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Custom Elements: CEReactions on HTMLInputElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<link rel="author" title="Wanming Lin" href="mailto:wanming.lin@intel.com"> +<meta name="assert" content="capture of HTMLInputElement interface must have CEReactions"> +<meta name="help" content="https://www.w3.org/TR/html-media-capture/#the-capture-attribute"> +<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> +<body> +<script> +if ('capture' in HTMLInputElement.prototype) { + test(() => { + const element = define_build_in_custom_element(['capture'], HTMLInputElement, 'input'); + const instance = document.createElement('input', { is: element.name }); + + assert_array_equals(element.takeLog().types(), ['constructed']); + instance['capture'] = 'user'; + const logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'capture', oldValue: null, newValue: 'user', namespace: null}); + }, 'capture on HTMLInputElement must enqueue an attributeChanged reaction when adding new attribute'); + + test(() => { + const element = define_build_in_custom_element(['capture'], HTMLInputElement, 'input'); + const instance = document.createElement('input', { is: element.name }); + + instance['capture'] = 'user'; + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + instance['capture'] = 'environment'; + const logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'capture', oldValue: 'user', newValue: 'environment', namespace: null}); + }, 'capture on HTMLInputElement must enqueue an attributeChanged reaction when replacing an existing attribute'); + + test(() => { + const element = define_build_in_custom_element(['capture'], HTMLInputElement, 'input'); + const instance = document.createElement('input', { is: element.name }); + + assert_array_equals(element.takeLog().types(), ['constructed']); + instance['capture'] = 'asdf'; + const logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'capture', oldValue: null, newValue: 'asdf', namespace: null}); + }, 'capture on HTMLInputElement must enqueue an attributeChanged reaction when adding invalid value default'); + + test(() => { + const element = define_build_in_custom_element(['capture'], HTMLInputElement, 'input'); + const instance = document.createElement('input', { is: element.name }); + + instance['capture'] = 'user'; + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + instance['capture'] = ''; + const logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'capture', oldValue: 'user', newValue: '', namespace: null}); + }, 'capture on HTMLInputElement must enqueue an attributeChanged reaction when removing the attribute'); +} else { + // testharness.js doesn't allow a test file with no tests. + test(() => { + }, 'No tests if HTMLInputEement has no "capture" IDL attribute'); +} +</script> +</body> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLLIElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLLIElement.html new file mode 100644 index 0000000000..adba2addf6 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLLIElement.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLLIElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" + content="value of HTMLLIElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-li-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> + +<body> +<script> + +function getParentElement(parentElementName) { + let parentElement = document.createElement(parentElementName); + document.body.appendChild(parentElement); + return parentElement; +} + +testReflectAttributeWithParentNode( + 'value', 'value', '3', + '5', 'value on HTMLLIElement in ol', 'li', + () => getParentElement('ol'), HTMLLIElement +); +testReflectAttributeWithParentNode( + 'value', 'value', '3', + '5', 'value on HTMLLIElement in ul', 'li', + () => getParentElement('ul'), HTMLLIElement +); +testReflectAttributeWithParentNode( + 'value', 'value', '3', + '5', 'value on HTMLLIElement in menu', 'li', + () => getParentElement('menu'), HTMLLIElement +); + +</script> +</body> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLLabelElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLLabelElement.html new file mode 100644 index 0000000000..2fe2741dad --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLLabelElement.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLLabelElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" + content="htmlFor of HTMLLabelElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-label-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> + +<form id="form"> + <input type="radio" name="gender" id="male" value="male"> + <input type="radio" name="gender" id="female" value="female"> +</form> +<script> + +function getParentElement() { + let parentElement = document.getElementById("form"); + return parentElement; +} + +testReflectAttributeWithParentNode( + 'htmlFor', 'for', 'male', + 'female', 'htmlFor on HTMLLabelElement', 'label', + getParentElement, HTMLLabelElement +); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMapElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMapElement.html new file mode 100644 index 0000000000..5b2e674afb --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMapElement.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLMapElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="name of HTMLMapElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-map-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> + +<img usemap="#yellow" src="/images/yellow.png" alt="yellow pic"> +<img usemap="#green" src="/images/green.png" alt="green pic"> +<script> + +testReflectAttribute('name', 'name', 'yellow', 'green', 'name on HTMLMapElement', 'map', HTMLMapElement); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMediaElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMediaElement.html new file mode 100644 index 0000000000..58e002c52c --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMediaElement.html @@ -0,0 +1,107 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLMediaElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="src, crossOrigin, preload, autoplay, loop, + controls, defaultMuted of HTMLMediaElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#media-elements"> +<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> + +<body> +<script> + +function getParentElement() { + return document.body; +} + +function setAttributes(instance, value) { + instance.setAttribute('src', value); +} + +testReflectAttribute( + 'src', 'src', '/media/sound_0.mp3', + '/media/sound_5.mp3', 'src on HTMLMediaElement in audio', 'audio', + HTMLAudioElement +); +testReflectAttributeWithDependentAttributes( + 'crossOrigin', 'crossorigin', 'use-credentials', + 'anonymous', 'crossOrigin on HTMLMediaElement in audio', 'audio', + getParentElement, instance => setAttributes(instance, '/media/sound_5.mp3'), + HTMLAudioElement +); +testReflectAttributeWithDependentAttributes( + 'preload', 'preload', 'auto', + 'none', 'preload on HTMLMediaElement in audio', 'audio', + getParentElement, instance => setAttributes(instance, '/media/sound_5.mp3'), + HTMLAudioElement +); +testReflectBooleanAttributeWithDependentAttributes( + 'autoplay', 'autoplay', 'autoplay on HTMLMediaElement in audio', + 'audio', getParentElement, + instance => setAttributes(instance, '/media/sound_5.mp3'), + HTMLAudioElement +); +testReflectBooleanAttributeWithDependentAttributes( + 'loop', 'loop', 'loop on HTMLMediaElement in audio', + 'audio', getParentElement, + instance => setAttributes(instance, '/media/sound_5.mp3'), HTMLAudioElement +); +testReflectBooleanAttributeWithDependentAttributes( + 'controls', 'controls', 'controls on HTMLMediaElement in audio', + 'audio', getParentElement, + instance => setAttributes(instance, '/media/sound_5.mp3'), + HTMLAudioElement +); +testReflectBooleanAttributeWithDependentAttributes( + 'defaultMuted', 'muted', 'defaultMuted on HTMLMediaElement in audio', + 'audio', getParentElement, + instance => setAttributes(instance, '/media/sound_5.mp3'), + HTMLAudioElement +); + +testReflectAttribute( + 'src', 'src', '/media/video.ogv', + '/media/movie_5.mp4', 'src on HTMLMediaElement in video', 'video', + HTMLVideoElement +); +testReflectAttributeWithDependentAttributes( + 'crossOrigin', 'crossorigin', 'use-credentials', + 'anonymous', 'crossOrigin on HTMLMediaElement in video', 'video', + getParentElement, instance => setAttributes(instance, '/media/movie_5.mp4'), + HTMLVideoElement +); +testReflectAttributeWithDependentAttributes( + 'preload', 'preload', 'auto', + 'none', 'preload on HTMLMediaElement in video', 'video', + getParentElement, instance => setAttributes(instance, '/media/movie_5.mp4'), + HTMLVideoElement +); +testReflectBooleanAttributeWithDependentAttributes( + 'autoplay', 'autoplay', 'autoplay on HTMLMediaElement in video', + 'video', getParentElement, + instance => setAttributes(instance, '/media/movie_5.mp4'), + HTMLVideoElement +); +testReflectBooleanAttributeWithDependentAttributes( + 'loop', 'loop', 'loop on HTMLMediaElement in video', + 'video', getParentElement, + instance => setAttributes(instance, '/media/movie_5.mp4'), + HTMLVideoElement +); +testReflectBooleanAttributeWithDependentAttributes( + 'controls', 'controls', 'controls on HTMLMediaElement in video', + 'video', getParentElement, + instance => setAttributes(instance, '/media/movie_5.mp4'), + HTMLVideoElement +); +testReflectBooleanAttributeWithDependentAttributes( + 'defaultMuted', 'muted', 'defaultMuted on HTMLMediaElement in video', + 'video', getParentElement, + instance => setAttributes(instance, '/media/movie_5.mp4'), + HTMLVideoElement +); + +</script> +</body> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMetaElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMetaElement.html new file mode 100644 index 0000000000..b6e8c06546 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMetaElement.html @@ -0,0 +1,77 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLMetaElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="name, httpEquiv, content of + HTMLMetaElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-meta-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> + +<script> + +function getParentElement() { + return document.head; +} + +function setAttributes(instance, attribute, value) { + instance.setAttribute(attribute, value); +} + +testReflectAttributeWithDependentAttributes( + 'name', 'name', 'description', + 'keywords', 'name on HTMLMetaElement', 'meta', + getParentElement, + instance => setAttributes(instance, 'content', 'HTMLMetaElement'), + HTMLMetaElement +); +testReflectAttributeWithDependentAttributes( + 'content', 'content', 'name1', + 'name2', 'content on HTMLMetaElement', 'meta', + getParentElement, instance => setAttributes(instance, 'name', 'author'), + HTMLMetaElement +); + +test(() => { + let element = define_build_in_custom_element( + ['http-equiv'], HTMLMetaElement, 'meta' + ); + let instance = document.createElement('meta', { is: element.name }); + + assert_array_equals(element.takeLog().types(), ['constructed']); + document.head.appendChild(instance); + assert_array_equals(element.takeLog().types(), ['connected']); + instance['content'] = '300'; + instance['httpEquiv'] = 'refresh'; + let logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), { + name: 'http-equiv', oldValue: null, newValue: 'refresh', namespace: null + }); +}, 'httpEquiv on HTMLMetaElement must enqueue an attributeChanged' + + ' reaction when adding a new attribute'); + +test(() => { + let element = define_build_in_custom_element( + ['http-equiv'], HTMLMetaElement, 'meta' + ); + let instance = document.createElement('meta', { is: element.name }); + document.head.appendChild(instance); + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + instance['content'] = 'text/html; charset=UTF-8'; + instance['httpEquiv'] = 'content-type'; + assert_array_equals(element.takeLog().types(), ['attributeChanged']); + instance['content'] = '300'; + instance['httpEquiv'] = 'refresh'; + let logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), { + name: 'http-equiv', oldValue: 'content-type', + newValue: 'refresh', namespace: null + }); +}, 'httpEquiv on HTMLMetaElement must enqueue an attributeChanged' + + ' reaction when replacing an existing attribute'); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMeterElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMeterElement.html new file mode 100644 index 0000000000..707e56c605 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMeterElement.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLMeterElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="value, min, max, low, high, optimum of + HTMLMeterElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-meter-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> + +<body> +<script> + +function getParentElement() { + return document.body; +} + +function setAttributes(instance) { + instance.setAttribute('value', '0.6'); +} + +testReflectAttribute( + 'value', 'value', '0.3', + '0.4', 'value on HTMLMeterElement', 'meter', + HTMLMeterElement +); +testReflectAttributeWithDependentAttributes( + 'min', 'min', '0.1', + '0.2', 'min on HTMLMeterElement', 'meter', + getParentElement, instance => setAttributes(instance), HTMLMeterElement +); +testReflectAttributeWithDependentAttributes( + 'max', 'max', '2', + '3', 'max on HTMLMeterElement', 'meter', + getParentElement, instance => setAttributes(instance), HTMLMeterElement +); +testReflectAttributeWithDependentAttributes( + 'low', 'low', '0.1', + '0.2', 'low on HTMLMeterElement', 'meter', + getParentElement, instance => setAttributes(instance), HTMLMeterElement +); +testReflectAttributeWithDependentAttributes( + 'high', 'high', '2', + '3', 'high on HTMLMeterElement', 'meter', + getParentElement, instance => setAttributes(instance), HTMLMeterElement +); +testReflectAttributeWithDependentAttributes( + 'optimum', 'optimum', '0.3', + '0.4', 'optimum on HTMLMeterElement', 'meter', + getParentElement, instance => setAttributes(instance), HTMLMeterElement +); + +</script> +</body> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLModElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLModElement.html new file mode 100644 index 0000000000..850fe170a5 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLModElement.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLModElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="cite, dateTime of HTMLModElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#attributes-common-to-ins-and-del-elements"> +<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> + +<script> + +testReflectAttribute('cite', 'cite', '../resources/custom-elements-helpers.js', './resources/reactions.js', 'cite on ins use HTMLModElement', 'ins', HTMLModElement); +testReflectAttribute('dateTime', 'datetime', '2018-12-19 00:00Z', '2018-12-20 00:00Z', 'dateTime on ins use HTMLModElement', 'ins', HTMLModElement); +testReflectAttribute('cite', 'cite', '../resources/custom-elements-helpers.js', './resources/reactions.js', 'cite on del use HTMLModElement', 'del', HTMLModElement); +testReflectAttribute('dateTime', 'datetime', '2018-10-11T01:25-07:00', '2018-10-12T01:25-07:00', 'dateTime on del use HTMLModElement', 'del', HTMLModElement); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLOListElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLOListElement.html new file mode 100644 index 0000000000..b62f31b489 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLOListElement.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLOListElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="reversed, start, type of HTMLOListElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-ol-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> + +<script> + +testReflectBooleanAttribute('reversed', 'reversed', 'reversed on HTMLOListElement', 'ol', HTMLOListElement); +testReflectAttribute('start', 'start', '2', '5', 'start on HTMLOListElement', 'ol', HTMLOListElement); +testReflectAttribute('type', 'type', '1', 'a', 'type on HTMLOListElement', 'ol', HTMLOListElement); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLOptGroupElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLOptGroupElement.html new file mode 100644 index 0000000000..afa31bb465 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLOptGroupElement.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLOptGroupElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="disabled, label of + HTMLOptGroupElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-optgroup-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> + +<body> +<script> + +function getParentElement() { + let element = document.createElement('select'); + document.body.appendChild(element); + return element; +} + +function setAttributes(instance) { + instance.setAttribute('label', 'group1'); +} + +testReflectBooleanAttributeWithDependentAttributes( + 'disabled', 'disabled', 'disabled on HTMLOptGroupElement', + 'optgroup', getParentElement, instance => setAttributes(instance), + HTMLOptGroupElement +); + +testReflectAttributeWithParentNode( + 'label', 'label', 'group1', + 'group2', 'label on HTMLOptGroupElement', 'optgroup', + getParentElement, HTMLOptGroupElement +); + +</script> +</body> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLParamElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLParamElement.html new file mode 100644 index 0000000000..eb3a13962f --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLParamElement.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLParamElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="name, value of HTMLParamElement + interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-param-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> + +<body> +<script> + +function getParentElement() { + let element = document.createElement('object'); + element['type'] = 'image/png'; + element['data'] = '/images/blue.png'; + document.body.appendChild(element); + return element; +} + +function setAttributes(instance, attribute, value) { + instance.setAttribute(attribute, value); +} + +testReflectAttributeWithDependentAttributes( + 'name', 'name', 'image1', + 'image2', 'name on HTMLParamElement', 'param', + getParentElement, instance => setAttributes(instance, 'value', 'blue'), + HTMLParamElement +); +testReflectAttributeWithDependentAttributes( + 'value', 'value', 'blue1', + 'blue2', 'value on HTMLParamElement', 'param', + getParentElement, instance => setAttributes(instance, 'name', 'image'), + HTMLParamElement +); + +</script> +</body> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLProgressElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLProgressElement.html new file mode 100644 index 0000000000..42683f4ede --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLProgressElement.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLProgressElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="value, max of HTMLProgressElement + interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-progress-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> + +<script> + +testReflectAttribute( + 'value', 'value', '0.15', + '0.2', 'value on HTMLProgressElement', 'progress', + HTMLProgressElement +); +testReflectAttribute( + 'max', 'max', '2', + '4', 'max on HTMLProgressElement', 'progress', + HTMLProgressElement +); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLQuoteElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLQuoteElement.html new file mode 100644 index 0000000000..f9c3d7538c --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLQuoteElement.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLQuoteElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="cite of HTMLQuoteElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-blockquote-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> + +<script> + +testReflectAttribute('cite', 'cite', '../resources/custom-elements-helpers.js', './resources/reactions.js', 'cite on blockquote use HTMLQuoteElement', 'blockquote', HTMLQuoteElement); +testReflectAttribute('cite', 'cite', '../resources/custom-elements-helpers.js', './resources/reactions.js', 'cite on q use HTMLQuoteElement', 'q', HTMLQuoteElement); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLSlotElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLSlotElement.html new file mode 100644 index 0000000000..56871873b4 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLSlotElement.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLSlotElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="name of HTMLSlotElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-slot-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> + +<script> + +testReflectAttribute('name', 'name', 'slot1', 'slot2', 'name on HTMLSlotElement', 'slot', HTMLSlotElement); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLSourceElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLSourceElement.html new file mode 100644 index 0000000000..f7d567ebcb --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLSourceElement.html @@ -0,0 +1,191 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLSourceElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="src, type, srcset, sizes, media of + HTMLSourceElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-source-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> + +<video controls id='video' width='5' height='5'></video> +<picture id='pic'> + <img src='/images/green-1x1.png'> +</picture> +<body> +<script> + +function getParentElement(id) { + let element = document.getElementById(id); + return element; +} + +testReflectAttributeWithParentNode( + 'src', 'src', '/media/video.ogv', + '/media/white.mp4', 'src on HTMLSourceElement', 'source', + () => getParentElement('video'), HTMLSourceElement +); +testReflectAttributeWithDependentAttributes( + 'type', 'type', 'video/mp4; codecs="mp4v.20.240, mp4a.40.2"', + 'video/mp4; codecs="mp4v.20.8, mp4a.40.2"', 'type on HTMLSourceElement', + 'source', + () => getParentElement('video'), + instance => instance.setAttribute('src', '/media/white.mp4'), HTMLSourceElement +); + +function testReflectAttributeWithContentValuesAndParentNode( + jsAttributeName, contentAttributeName, validValue1, + contentValue1, validValue2, contentValue2, + name, elementName, getParentElement, + interfaceName) { + + let parentElement = getParentElement(); + + test(() => { + let element = define_build_in_custom_element( + [contentAttributeName], interfaceName, elementName + ); + let instance = document.createElement(elementName, { is: element.name }); + + assert_array_equals(element.takeLog().types(), ['constructed']); + // source element as a child of a picture element, before the img element + parentElement.prepend(instance); + assert_array_equals(element.takeLog().types(), ['connected']); + instance[jsAttributeName] = validValue1; + let logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry( + logEntries.last(), + { name: contentAttributeName, oldValue: null, + newValue: contentValue1, namespace: null + } + ); + }, name + ' must enqueue an attributeChanged reaction when adding a new attribute'); + + test(() => { + let element = define_build_in_custom_element( + [contentAttributeName], interfaceName, elementName + ); + let instance = document.createElement(elementName, { is: element.name }); + // source element as a child of a picture element, before the img element + parentElement.prepend(instance); + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + instance[jsAttributeName] = validValue1; + assert_array_equals(element.takeLog().types(), ['attributeChanged']); + instance[jsAttributeName] = validValue2; + let logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry( + logEntries.last(), + { name: contentAttributeName, oldValue: contentValue1, + newValue: contentValue2, namespace: null + } + ); + }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute'); +} + +function testReflectAttributeWithParentNode( + jsAttributeName, contentAttributeName, validValue1, + validValue2, name, elementName, + getParentElement, interfaceName) { + + testReflectAttributeWithContentValuesAndParentNode( + jsAttributeName, contentAttributeName, validValue1, + validValue1, validValue2, validValue2, + name, elementName, getParentElement, + interfaceName + ); +} + +function testReflectAttributeWithContentValuesAndDependentAttributes( + jsAttributeName, contentAttributeName, validValue1, + contentValue1, validValue2, contentValue2, + name, elementName, getParentElement, + setAttributes, interfaceName) { + + let parentElement = getParentElement(); + + test(() => { + let element = define_build_in_custom_element( + [contentAttributeName], interfaceName, elementName + ); + let instance = document.createElement(elementName, { is: element.name }); + + assert_array_equals(element.takeLog().types(), ['constructed']); + // source element as a child of a picture element, before the img element + parentElement.prepend(instance); + assert_array_equals(element.takeLog().types(), ['connected']); + setAttributes(instance); + instance[jsAttributeName] = validValue1; + let logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry( + logEntries.last(), + { name: contentAttributeName, oldValue: null, + newValue: contentValue1, namespace: null + } + ); + }, name + ' must enqueue an attributeChanged reaction when adding a new attribute'); + + test(() => { + let element = define_build_in_custom_element( + [contentAttributeName], interfaceName, elementName + ); + let instance = document.createElement(elementName, { is: element.name }); + // source element as a child of a picture element, before the img element + parentElement.prepend(instance); + setAttributes(instance); + instance[jsAttributeName] = validValue1; + + assert_array_equals( + element.takeLog().types(), + ['constructed', 'connected', 'attributeChanged'] + ); + instance[jsAttributeName] = validValue2; + let logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry( + logEntries.last(), + { name: contentAttributeName, oldValue: contentValue1, + newValue: contentValue2, namespace: null + } + ); + }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute'); +} + +function testReflectAttributeWithDependentAttributes( + jsAttributeName, contentAttributeName, validValue1, + validValue2, name, elementName, + getParentElement, setAttributes, interfaceName) { + + testReflectAttributeWithContentValuesAndDependentAttributes( + jsAttributeName, contentAttributeName, validValue1, + validValue1, validValue2, validValue2, + name, elementName, getParentElement, + setAttributes, interfaceName); +} + +testReflectAttributeWithParentNode( + 'srcset', 'srcset', '/images/green.png', + '/images/green-1x1.png', 'srcset on HTMLSourceElement', 'source', + () => getParentElement('pic'), HTMLSourceElement +); +testReflectAttributeWithDependentAttributes( + 'sizes', 'sizes', '(max-width: 32px) 28px', + '(max-width: 48px) 44px', 'sizes on HTMLSourceElement', 'source', + () => getParentElement('pic'), + instance => instance.setAttribute('srcset', '/images/green.png 3x'), + HTMLSourceElement +); +testReflectAttributeWithDependentAttributes( + 'media', 'media', '(max-width: 7px)', + '(max-width: 9px)', 'media on HTMLSourceElement', 'source', + () => getParentElement('pic'), + instance => instance.setAttribute('srcset', '/images/green.png 3x'), + HTMLSourceElement +); + +</script> +</body> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLStyleElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLStyleElement.html new file mode 100644 index 0000000000..d68d5cb76d --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLStyleElement.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLStyleElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="media of HTMLStyleElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-style-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> + +<script> + +function getParentElement() { + return document.head; +} + +testReflectAttributeWithParentNode( + 'media', 'media', 'print', + 'screen', 'media on HTMLStyleElement', 'style', + getParentElement, HTMLStyleElement +); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLTableCellElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLTableCellElement.html new file mode 100644 index 0000000000..95a8459df8 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLTableCellElement.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLTableCellElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="colSpan, rowSpan, headers, scope, abbr of + HTMLTableCellElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-td-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> + +<table><tr id="colSpan"></table> +<table><tr id="rowSpan"><tr><tr></table> +<table><tr><th id="id1"><th id="id2"><tr id="td_headers"><tr id="th_headers"></table> +<script> + +function getParentElement(id) { + let parentElement = document.getElementById(id); + return parentElement; +} + +testReflectAttributeWithParentNode( + 'colSpan', 'colspan', '2', + '3', 'colSpan on HTMLTableCellElement in td', 'td', + () => getParentElement('colSpan'), HTMLTableCellElement +); +testReflectAttributeWithParentNode( + 'colSpan', 'colspan', '2', + '3', 'colSpan on HTMLTableCellElement in th', 'th', + () => getParentElement('colSpan'), HTMLTableCellElement +); +testReflectAttributeWithParentNode( + 'rowSpan', 'rowspan', '2', + '3', 'rowSpan on HTMLTableCellElement in td', 'td', + () => getParentElement('rowSpan'), HTMLTableCellElement +); +testReflectAttributeWithParentNode( + 'rowSpan', 'rowspan', '2', + '3', 'rowSpan on HTMLTableCellElement in th', 'th', + () => getParentElement('rowSpan'), HTMLTableCellElement +); +testReflectAttributeWithParentNode( + 'headers', 'headers', 'id1', + 'id2', 'headers on HTMLTableCellElement in td', 'td', + () => getParentElement('td_headers'), HTMLTableCellElement +); +testReflectAttributeWithParentNode( + 'headers', 'headers', 'id1', + 'id2', 'headers on HTMLTableCellElement in th', 'th', + () => getParentElement('th_headers'), HTMLTableCellElement +); +testReflectAttributeWithParentNode( + 'scope', 'scope', 'row', + 'col', 'scope on HTMLTableCellElement in th', 'th', + () => getParentElement('colSpan'), HTMLTableCellElement +); +testReflectAttributeWithParentNode( + 'abbr', 'abbr', 'Model1', + 'Model2', 'abbr on HTMLTableCellElement in th', 'th', + () => getParentElement('colSpan'), HTMLTableCellElement +); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLTableColElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLTableColElement.html new file mode 100644 index 0000000000..8e4d1359d8 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLTableColElement.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLTableColElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="span of HTMLTableColElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-colgroup-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> + +<table id="tbl"></table> +<script> + +function getParentElement() { + let parentElement = document.getElementById('tbl'); + return parentElement; +} + +testReflectAttributeWithParentNode( + 'span', 'span', '2', + '1', 'span on HTMLTableColElement', 'colgroup', + getParentElement, HTMLTableColElement +); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLTimeElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLTimeElement.html new file mode 100644 index 0000000000..b2f4cc8af7 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLTimeElement.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<title>Custom Elements: CEReactions on HTMLTimeElement interface</title> +<link rel="author" title="Intel" href="http://www.intel.com"> +<meta name="assert" content="name of HTMLTimeElement interface must have CEReactions"> +<meta name="help" content="https://html.spec.whatwg.org/#the-time-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> + +<script> + +testReflectAttribute('dateTime', 'datetime', '2018-12-10', '2018-12-12', 'dateTime on HTMLTimeElement', 'time', HTMLTimeElement); + +</script> diff --git a/testing/web-platform/tests/custom-elements/reactions/resources/reactions.js b/testing/web-platform/tests/custom-elements/reactions/resources/reactions.js new file mode 100644 index 0000000000..5ed32a4fa4 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/resources/reactions.js @@ -0,0 +1,452 @@ + +let testNumber = 1; + +function testNodeConnector(testFunction, name) { + let container = document.createElement('div'); + container.appendChild(document.createElement('div')); + document.body.appendChild(container); + + test(function () { + var element = define_new_custom_element(); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(container, instance); + assert_array_equals(element.takeLog().types(), ['connected']); + }, name + ' must enqueue a connected reaction'); + + test(function () { + var element = define_new_custom_element(); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + var newDoc = document.implementation.createHTMLDocument(); + testFunction(container, instance); + assert_array_equals(element.takeLog().types(), ['connected']); + testFunction(newDoc.documentElement, instance); + assert_array_equals(element.takeLog().types(), ['disconnected', 'adopted', 'connected']); + }, name + ' must enqueue a disconnected reaction, an adopted reaction, and a connected reaction when the custom element was in another document'); + + container.parentNode.removeChild(container); +} + +function testNodeDisconnector(testFunction, name) { + let container = document.createElement('div'); + container.appendChild(document.createElement('div')); + document.body.appendChild(container); + + test(function () { + var element = define_new_custom_element(); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + container.appendChild(instance); + assert_array_equals(element.takeLog().types(), ['connected']); + testFunction(instance, window); + assert_array_equals(element.takeLog().types(), ['disconnected']); + }, name + ' must enqueue a disconnected reaction'); + + container.parentNode.removeChild(container); +} + +function testInsertingMarkup(testFunction, name) { + let container = document.createElement('div'); + container.appendChild(document.createElement('div')); + document.body.appendChild(container); + + test(function () { + var element = define_new_custom_element(); + testFunction(container, `<${element.name}></${element.name}>`); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + }, name + ' must enqueue a connected reaction for a newly constructed custom element'); + + test(function () { + var element = define_new_custom_element(['title']); + testFunction(container, `<${element.name} id="hello" title="hi"></${element.name}>`); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['constructed', 'attributeChanged', 'connected']); + assert_attribute_log_entry(logEntries[1], {name: 'title', oldValue: null, newValue: 'hi', namespace: null}); + }, name + ' must enqueue a attributeChanged reaction for a newly constructed custom element'); + + container.parentNode.removeChild(container); +} + +function testParsingMarkup(testFunction, name) { + test(function () { + var element = define_new_custom_element(['id']); + assert_array_equals(element.takeLog().types(), []); + var instance = testFunction(document, `<${element.name} id="hello" class="foo"></${element.name}>`); + assert_equals(Object.getPrototypeOf(instance.querySelector(element.name)), element.class.prototype); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['constructed', 'attributeChanged']); + assert_attribute_log_entry(logEntries[1], {name: 'id', oldValue: null, newValue: 'hello', namespace: null}); + }, name + ' must construct a custom element'); +} + +function testCloner(testFunction, name) { + let container = document.createElement('div'); + container.appendChild(document.createElement('div')); + document.body.appendChild(container); + + test(function () { + var element = define_new_custom_element(['id']); + var instance = document.createElement(element.name); + container.appendChild(instance); + + instance.setAttribute('id', 'foo'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected', 'attributeChanged']); + var newInstance = testFunction(instance); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['constructed', 'attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'id', oldValue: null, newValue: 'foo', namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when cloning an element with an observed attribute'); + + test(function () { + var element = define_new_custom_element(['id']); + var instance = document.createElement(element.name); + container.appendChild(instance); + + instance.setAttribute('lang', 'en'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + var newInstance = testFunction(instance); + assert_array_equals(element.takeLog().types(), ['constructed']); + }, name + ' must not enqueue an attributeChanged reaction when cloning an element with an unobserved attribute'); + + test(function () { + var element = define_new_custom_element(['title', 'class']); + var instance = document.createElement(element.name); + container.appendChild(instance); + + instance.setAttribute('lang', 'en'); + instance.className = 'foo'; + instance.setAttribute('title', 'hello world'); + assert_array_equals(element.takeLog().types(), ['constructed', 'connected', 'attributeChanged', 'attributeChanged']); + var newInstance = testFunction(instance); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['constructed', 'attributeChanged', 'attributeChanged']); + assert_attribute_log_entry(logEntries[1], {name: 'class', oldValue: null, newValue: 'foo', namespace: null}); + assert_attribute_log_entry(logEntries[2], {name: 'title', oldValue: null, newValue: 'hello world', namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when cloning an element only for observed attributes'); +} + +function testReflectAttributeWithContentValues(jsAttributeName, contentAttributeName, validValue1, contentValue1, validValue2, contentValue2, name, elementName, interfaceName) { + test(function () { + if (elementName === undefined) { + var element = define_new_custom_element([contentAttributeName]); + var instance = document.createElement(element.name); + } else { + var element = define_build_in_custom_element([contentAttributeName], interfaceName, elementName); + var instance = document.createElement(elementName, { is: element.name }); + } + assert_array_equals(element.takeLog().types(), ['constructed']); + instance[jsAttributeName] = validValue1; + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + + assert_attribute_log_entry(logEntries.last(), {name: contentAttributeName, oldValue: null, newValue: contentValue1, namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when adding ' + contentAttributeName + ' content attribute'); + + test(function () { + if (elementName === undefined) { + var element = define_new_custom_element([contentAttributeName]); + var instance = document.createElement(element.name); + } else { + var element = define_build_in_custom_element([contentAttributeName], interfaceName, elementName); + var instance = document.createElement(elementName, { is: element.name }); + } + instance[jsAttributeName] = validValue1; + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + instance[jsAttributeName] = validValue2; + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: contentAttributeName, oldValue: contentValue1, newValue: contentValue2, namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute'); +} + +function testReflectAttribute(jsAttributeName, contentAttributeName, validValue1, validValue2, name, elementName, interfaceName) { + testReflectAttributeWithContentValues(jsAttributeName, contentAttributeName, validValue1, validValue1, validValue2, validValue2, name, elementName, interfaceName); +} + +function testReflectBooleanAttribute(jsAttributeName, contentAttributeName, name, elementName, interfaceName) { + testReflectAttributeWithContentValues(jsAttributeName, contentAttributeName, true, '', false, null, name, elementName, interfaceName); +} + +function testReflectAttributeWithContentValuesAndDependentAttributes(jsAttributeName, contentAttributeName, validValue1, contentValue1, validValue2, contentValue2, name, elementName, getParentElement, setAttributes, interfaceName) { + let parentElement = getParentElement(); + + test(() => { + let element = define_build_in_custom_element([contentAttributeName], interfaceName, elementName); + let instance = document.createElement(elementName, { is: element.name }); + + assert_array_equals(element.takeLog().types(), ['constructed']); + parentElement.appendChild(instance); + assert_array_equals(element.takeLog().types(), ['connected']); + setAttributes(instance); + instance[jsAttributeName] = validValue1; + let logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), { name: contentAttributeName, oldValue: null, newValue: contentValue1, namespace: null }); + + }, name + ' must enqueue an attributeChanged reaction when adding a new attribute'); + + test(() => { + let element = define_build_in_custom_element([contentAttributeName], interfaceName, elementName); + let instance = document.createElement(elementName, { is: element.name }); + parentElement.appendChild(instance); + setAttributes(instance); + instance[jsAttributeName] = validValue1; + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected', 'attributeChanged']); + instance[jsAttributeName] = validValue2; + let logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), { name: contentAttributeName, oldValue: contentValue1, newValue: contentValue2, namespace: null }); + + }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute'); +} + +function testReflectAttributeWithDependentAttributes(jsAttributeName, contentAttributeName, validValue1, validValue2, name, elementName, getParentElement, setAttributes, interfaceName) { + testReflectAttributeWithContentValuesAndDependentAttributes(jsAttributeName, contentAttributeName, validValue1, validValue1, validValue2, validValue2, name, elementName, getParentElement, setAttributes, interfaceName); +} + +function testReflectBooleanAttributeWithDependentAttributes(jsAttributeName, contentAttributeName, name, elementName, getParentElement, setAttributes, interfaceName) { + testReflectAttributeWithContentValuesAndDependentAttributes(jsAttributeName, contentAttributeName, true, '', false, null, name, elementName, getParentElement, setAttributes, interfaceName); +} + +function testReflectAttributeWithContentValuesAndParentNode(jsAttributeName, contentAttributeName, validValue1, contentValue1, validValue2, contentValue2, name, elementName, getParentElement, interfaceName) { + let parentElement = getParentElement(); + + test(() => { + let element = define_build_in_custom_element([contentAttributeName], interfaceName, elementName); + let instance = document.createElement(elementName, { is: element.name }); + + assert_array_equals(element.takeLog().types(), ['constructed']); + parentElement.appendChild(instance); + assert_array_equals(element.takeLog().types(), ['connected']); + instance[jsAttributeName] = validValue1; + let logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), { name: contentAttributeName, oldValue: null, newValue: contentValue1, namespace: null }); +}, name + ' must enqueue an attributeChanged reaction when adding a new attribute'); + + test(() => { + let element = define_build_in_custom_element([contentAttributeName], interfaceName, elementName); + let instance = document.createElement(elementName, { is: element.name }); + parentElement.appendChild(instance); + + assert_array_equals(element.takeLog().types(), ['constructed', 'connected']); + instance[jsAttributeName] = validValue1; + assert_array_equals(element.takeLog().types(), ['attributeChanged']); + instance[jsAttributeName] = validValue2; + let logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), { name: contentAttributeName, oldValue: contentValue1, newValue: contentValue2, namespace: null }); + }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute'); +} + +function testReflectAttributeWithParentNode(jsAttributeName, contentAttributeName, validValue1, validValue2, name, elementName, getParentElement, interfaceName) { + testReflectAttributeWithContentValuesAndParentNode(jsAttributeName, contentAttributeName, validValue1, validValue1, validValue2, validValue2, name, elementName, getParentElement, interfaceName); +} + +function testReflectBooleanAttributeWithParentNode(jsAttributeName, contentAttributeName, name, elementName, getParentElement, interfaceName) { + testReflectAttributeWithContentValuesAndParentNode(jsAttributeName, contentAttributeName, true, '', false, null, name, elementName, getParentElement, interfaceName); +} + +function testAttributeAdder(testFunction, name) { + test(function () { + var element = define_new_custom_element(['id']); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(instance, 'id', 'foo'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'id', oldValue: null, newValue: 'foo', namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when adding an attribute'); + + test(function () { + var element = define_new_custom_element(['class']); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(instance, 'data-lang', 'en'); + assert_array_equals(element.takeLog().types(), []); + }, name + ' must not enqueue an attributeChanged reaction when adding an unobserved attribute'); + + test(function () { + var element = define_new_custom_element(['title']); + var instance = document.createElement(element.name); + instance.setAttribute('title', 'hello'); + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + testFunction(instance, 'title', 'world'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'hello', newValue: 'world', namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute'); + + test(function () { + var element = define_new_custom_element([]); + var instance = document.createElement(element.name); + instance.setAttribute('data-lang', 'zh'); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(instance, 'data-lang', 'en'); + assert_array_equals(element.takeLog().types(), []); + }, name + ' must enqueue an attributeChanged reaction when replacing an existing unobserved attribute'); +} + +function testAttributeMutator(testFunction, name) { + test(function () { + var element = define_new_custom_element(['title']); + var instance = document.createElement(element.name); + instance.setAttribute('title', 'hello'); + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + testFunction(instance, 'title', 'world'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'hello', newValue: 'world', namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute'); + + test(function () { + var element = define_new_custom_element(['class']); + var instance = document.createElement(element.name); + instance.setAttribute('data-lang', 'zh'); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(instance, 'data-lang', 'en'); + assert_array_equals(element.takeLog().types(), []); + }, name + ' must not enqueue an attributeChanged reaction when replacing an existing unobserved attribute'); +} + +function testAttributeRemover(testFunction, name, options) { + if (options && !options.onlyExistingAttribute) { + test(function () { + var element = define_new_custom_element(['title']); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(instance, 'title'); + assert_array_equals(element.takeLog().types(), []); + }, name + ' must not enqueue an attributeChanged reaction when removing an attribute that does not exist'); + } + + test(function () { + var element = define_new_custom_element([]); + var instance = document.createElement(element.name); + instance.setAttribute('data-lang', 'hello'); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(instance, 'data-lang'); + assert_array_equals(element.takeLog().types(), []); + }, name + ' must not enqueue an attributeChanged reaction when removing an unobserved attribute'); + + test(function () { + var element = define_new_custom_element(['title']); + var instance = document.createElement(element.name); + instance.setAttribute('title', 'hello'); + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + testFunction(instance, 'title'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'title', oldValue: 'hello', newValue: null, namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when removing an existing attribute'); + + test(function () { + var element = define_new_custom_element([]); + var instance = document.createElement(element.name); + instance.setAttribute('data-lang', 'ja'); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(instance, 'data-lang'); + assert_array_equals(element.takeLog().types(), []); + }, name + ' must not enqueue an attributeChanged reaction when removing an existing unobserved attribute'); +} + +function test_mutating_style_property_value(testFunction, name, options) { + const propertyName = (options || {}).propertyName || 'color'; + const idlName = (options || {}).idlName || 'color'; + const value1 = (options || {}).value1 || 'blue'; + const rule1 = `${propertyName}: ${value1};`; + const value2 = (options || {}).value2 || 'red'; + const rule2 = `${propertyName}: ${value2};`; + + test(function () { + var element = define_new_custom_element(['style']); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(instance, propertyName, idlName, value1); + assert_equals(instance.getAttribute('style'), rule1); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'style', oldValue: null, newValue: rule1, namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when it adds the observed style attribute'); + + test(function () { + var element = define_new_custom_element(['title']); + var instance = document.createElement(element.name); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(instance, propertyName, idlName, value1); + assert_equals(instance.getAttribute('style'), rule1); + assert_array_equals(element.takeLog().types(), []); + }, name + ' must not enqueue an attributeChanged reaction when it adds the style attribute but the style attribute is not observed'); + + test(function () { + var element = define_new_custom_element(['style']); + var instance = document.createElement(element.name); + testFunction(instance, propertyName, idlName, value1); + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + testFunction(instance, propertyName, idlName, value2); + assert_equals(instance.getAttribute('style'), rule2); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'style', oldValue: rule1, newValue: rule2, namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when it mutates the observed style attribute'); + + test(function () { + var element = define_new_custom_element([]); + var instance = document.createElement(element.name); + testFunction(instance, propertyName, idlName, value1); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(instance, propertyName, idlName, value2); + assert_equals(instance.getAttribute('style'), rule2); + assert_array_equals(element.takeLog().types(), []); + }, name + ' must not enqueue an attributeChanged reaction when it mutates the style attribute but the style attribute is not observed'); +} + +function test_removing_style_property_value(testFunction, name) { + test(function () { + var element = define_new_custom_element(['style']); + var instance = document.createElement(element.name); + instance.setAttribute('style', 'color: red; display: none;'); + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + testFunction(instance, 'color', 'color'); + assert_equals(instance.getAttribute('style'), 'display: none;'); // Don't make this empty since browser behaviors are inconsistent now. + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'style', oldValue: 'color: red; display: none;', newValue: 'display: none;', namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when it removes a property from the observed style attribute'); + + test(function () { + var element = define_new_custom_element(['class']); + var instance = document.createElement(element.name); + instance.setAttribute('style', 'color: red; display: none;'); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(instance, 'color', 'color'); + assert_equals(instance.getAttribute('style'), 'display: none;'); // Don't make this empty since browser behaviors are inconsistent now. + assert_array_equals(element.takeLog().types(), []); + }, name + ' must not enqueue an attributeChanged reaction when it removes a property from the style attribute but the style attribute is not observed'); +} + +function test_mutating_style_property_priority(testFunction, name) { + test(function () { + var element = define_new_custom_element(['style']); + var instance = document.createElement(element.name); + instance.setAttribute('style', 'color: red'); + assert_array_equals(element.takeLog().types(), ['constructed', 'attributeChanged']); + testFunction(instance, 'color', 'color', true); + assert_equals(instance.getAttribute('style'), 'color: red !important;'); + var logEntries = element.takeLog(); + assert_array_equals(logEntries.types(), ['attributeChanged']); + assert_attribute_log_entry(logEntries.last(), {name: 'style', oldValue: 'color: red', newValue: 'color: red !important;', namespace: null}); + }, name + ' must enqueue an attributeChanged reaction when it makes a property important and the style attribute is observed'); + + test(function () { + var element = define_new_custom_element(['id']); + var instance = document.createElement(element.name); + instance.setAttribute('style', 'color: red'); + assert_array_equals(element.takeLog().types(), ['constructed']); + testFunction(instance, 'color', 'color', true); + assert_equals(instance.getAttribute('style'), 'color: red !important;'); + assert_array_equals(element.takeLog().types(), []); + }, name + ' must enqueue an attributeChanged reaction when it makes a property important but the style attribute is not observed'); +} diff --git a/testing/web-platform/tests/custom-elements/reactions/with-exceptions.html b/testing/web-platform/tests/custom-elements/reactions/with-exceptions.html new file mode 100644 index 0000000000..131348b1c4 --- /dev/null +++ b/testing/web-platform/tests/custom-elements/reactions/with-exceptions.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Custom Elements: CEReactions interaction with exceptions</title> +<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me"> +<meta name="help" content="https://html.spec.whatwg.org/multipage/#cereactions"> +<meta name="help" content="https://github.com/whatwg/html/pull/3235"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/custom-elements-helpers.js"></script> + +<div id="log"></div> + +<script> +"use strict"; +// Basically from https://github.com/whatwg/html/issues/3217#issuecomment-343633273 +test_with_window((contentWindow, contentDocument) => { + let reactionRan = false; + contentWindow.customElements.define("custom-element", class extends contentWindow.HTMLElement { + disconnectedCallback() { + reactionRan = true; + } + }); + const text = contentDocument.createTextNode(""); + contentDocument.documentElement.appendChild(text); + const element = contentDocument.createElement("custom-element"); + contentDocument.documentElement.appendChild(element); + assert_throws_dom( + "HierarchyRequestError", + contentWindow.DOMException, + () => text.before("", contentDocument.documentElement) + ); + assert_true(reactionRan); +}, "Reaction must run even after the exception is thrown"); +</script> |