diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/css/cssom | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/css/cssom')
215 files changed, 9914 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/cssom/CSS-namespace-object-class-string.html b/testing/web-platform/tests/css/cssom/CSS-namespace-object-class-string.html new file mode 100644 index 0000000000..3145467073 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSS-namespace-object-class-string.html @@ -0,0 +1,47 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM - Symbol.toStringTag value of CSS namespace object</title> +<link rel="help" href="https://webidl.spec.whatwg.org/#es-namespaces"> +<link rel="help" href="https://drafts.csswg.org/cssom/#namespacedef-css"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +"use strict"; + +test(() => { + assert_own_property(CSS, Symbol.toStringTag); + + const propDesc = Object.getOwnPropertyDescriptor(CSS, Symbol.toStringTag); + assert_equals(propDesc.value, "CSS", "value"); + assert_equals(propDesc.writable, false, "writable"); + assert_equals(propDesc.enumerable, false, "enumerable"); + assert_equals(propDesc.configurable, true, "configurable"); +}, "@@toStringTag exists on the namespace object with the appropriate descriptor"); + +test(() => { + assert_equals(CSS.toString(), "[object CSS]"); + assert_equals(Object.prototype.toString.call(CSS), "[object CSS]"); +}, "Object.prototype.toString applied to the namespace object"); + +test(t => { + assert_own_property(CSS, Symbol.toStringTag, "Precondition: @@toStringTag on the namespace object"); + t.add_cleanup(() => { + Object.defineProperty(CSS, Symbol.toStringTag, { value: "CSS" }); + }); + + Object.defineProperty(CSS, Symbol.toStringTag, { value: "Test" }); + assert_equals(CSS.toString(), "[object Test]"); + assert_equals(Object.prototype.toString.call(CSS), "[object Test]"); +}, "Object.prototype.toString applied after modifying the namespace object's @@toStringTag"); + +test(t => { + assert_own_property(CSS, Symbol.toStringTag, "Precondition: @@toStringTag on the namespace object"); + t.add_cleanup(() => { + Object.defineProperty(CSS, Symbol.toStringTag, { value: "CSS" }); + }); + + assert_true(delete CSS[Symbol.toStringTag]); + assert_equals(CSS.toString(), "[object Object]"); + assert_equals(Object.prototype.toString.call(CSS), "[object Object]"); +}, "Object.prototype.toString applied after deleting @@toStringTag"); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSConditionRule-conditionText.html b/testing/web-platform/tests/css/cssom/CSSConditionRule-conditionText.html new file mode 100644 index 0000000000..bccfc3135a --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSConditionRule-conditionText.html @@ -0,0 +1,23 @@ +<!doctype html> +<title>CSSConditionRule.conditionText</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="help" href="https://drafts.csswg.org/css-conditional-3/#cssconditionrule"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +@media not all { + :root { color: lime } +} +</style> +<script> +test(function(t) { + let rule = document.styleSheets[0].cssRules[0]; + assert_true(rule instanceof CSSConditionRule); + assert_equals(rule.conditionText, "not all"); + rule.conditionText = 1; + assert_equals(rule.conditionText, "not all"); + rule.conditionText = "all"; + assert_equals(rule.conditionText, "not all"); + assert_not_equals(getComputedStyle(document.documentElement).color, "rgb(0, 255, 0)"); +}); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSContainerRule.tentative.html b/testing/web-platform/tests/css/cssom/CSSContainerRule.tentative.html new file mode 100644 index 0000000000..4e01c0b470 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSContainerRule.tentative.html @@ -0,0 +1,35 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSContainerRule</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-contain-3/#the-csscontainerrule-interface"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<style id="sheet"></style> +<script> + const kTests = [ + { + rule: "@container name (min-width: 100px) {}", + name: "name", + query: "(min-width: 100px)", + }, + { + rule: "@container (min-width: 100px) {}", + name: "", + query: "(min-width: 100px)", + }, + ]; + + const sheet = document.getElementById("sheet").sheet; + for (let { rule, name, query } of kTests) { + test(function() { + sheet.insertRule(rule, 0); + let r = sheet.cssRules[0]; + assert_true(r instanceof CSSContainerRule); + assert_equals(r.containerName, name, "containerName"); + assert_equals(r.containerQuery, query, "containerQuery"); + assert_equals(r.conditionText, name ? `${name} ${query}` : query, "conditionText"); + }, rule) + } +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSCounterStyleRule.html b/testing/web-platform/tests/css/cssom/CSSCounterStyleRule.html new file mode 100644 index 0000000000..afae84b0b2 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSCounterStyleRule.html @@ -0,0 +1,21 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM - CSSCounterStyleRule interface</title> +<link rel="help" href="https://drafts.csswg.org/css-counter-styles-3/#csscounterstylerule"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @counter-style foo { + system: cyclic; + symbols: ‣; + suffix: " "; + } +</style> +<script> +test(function() { + // TODO: The exact serialization isn't well specified (the ordering of the + // descriptors isn't defined). + let rule = document.styleSheets[0].cssRules[0]; + assert_false(rule.cssText.includes('\n')); +}, "CSSCounterStyleRule.cssText doesn't serialize with newlines"); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSFontFaceRule.html b/testing/web-platform/tests/css/cssom/CSSFontFaceRule.html new file mode 100644 index 0000000000..438c30df9e --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSFontFaceRule.html @@ -0,0 +1,21 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM - CSSFontFaceRule interface</title> +<link rel="help" href="https://drafts.csswg.org/css-fonts-4/#cssfontfacerule"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @font-face { + src: local("foo"); + font-family: foo; + font-weight: bold; + } +</style> +<script> +test(function() { + // TODO: The exact serialization isn't consistent across browsers + // (Firefox doesn't preserve ordering of the descriptors, WebKit/Blink does). + let rule = document.styleSheets[0].cssRules[0]; + assert_false(rule.cssText.includes('\n')); +}, "CSSFontFaceRule.cssText doesn't serialize with newlines"); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSFontFeatureValuesRule.html b/testing/web-platform/tests/css/cssom/CSSFontFeatureValuesRule.html new file mode 100644 index 0000000000..a0ea64a503 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSFontFeatureValuesRule.html @@ -0,0 +1,192 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<title>CSSOM - CSSFontFeatureValuesRule interface</title> +<link + rel="help" + href="https://drafts.csswg.org/css-fonts-4/#om-fontfeaturevalues" +/> +<link rel="author" title="Dominik Röttsches" href="drott@chromium.org" /> +<meta + name="assert" + content="CSSFontFeatureValuesRule interface is accessible and supports read and write access." +/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style></style> +<body></body> +<script> + let rule_initial = ` + @font-feature-values test_family { + @annotation { + the_first: 6; + } + @styleset { + yo: 7; + del: 4; + di: 10 9 4 5; + } + } + `; + + let rule_added = ` + @font-feature-values second_family { + @annotation { + yabab: 2; + yogog: 4; + bidib: 5; + } + } + `; + + let rule_overlap = ` + @font-feature-values test_family { + @annotation { + baric: 17; + } + @ornaments { + fooic: 1; + } + } + `; + + function resetStateAndTest(testFunction) { + var sheet = document.styleSheets[0]; + while (sheet.cssRules.length > 0) sheet.deleteRule(0); + sheet.insertRule(rule_initial); + testFunction(); + } + + test(function () { + resetStateAndTest(() => { + let rule = document.styleSheets[0].cssRules[0]; + assert_equals(rule.type, 14); + assert_equals(rule.annotation.size, 1); + assert_equals(rule.styleset.size, 3); + assert_array_equals(rule.styleset.get("yo"), [7]); + assert_array_equals(rule.styleset.get("del"), [4]); + assert_array_equals(rule.styleset.get("di"), [10, 9, 4, 5]); + assert_array_equals(rule.annotation.get("the_first"), [6]); + assert_equals(rule.stylistic.size, 0); + assert_equals(rule.swash.size, 0); + assert_equals(rule.ornaments.size, 0); + assert_equals(rule.characterVariant.size, 0); + assert_equals(rule.fontFamily, "test_family"); + }); + }, "CSSFontFeatureValuesRule is correctly parsed and accessible via CSSOM."); + + test(function () { + resetStateAndTest(() => { + let family_override = "test_changed_family"; + let rule = document.styleSheets[0].cssRules[0]; + rule.fontFamily = family_override; + let read_rule = document.styleSheets[0].cssRules[0]; + assert_equals(read_rule.fontFamily, family_override); + }); + }, "CSSFontFeatureValuesRule family is settable and readable."); + + test(function () { + resetStateAndTest(() => { + let rule = document.styleSheets[0].cssRules[0]; + let override_number = 43; + assert_equals(rule.styleset.get("di").length, 4); + rule.styleset.set("di", override_number); + assert_array_equals( + document.styleSheets[0].cssRules[0].styleset.get("di"), + [override_number] + ); + assert_equals(rule.styleset.size, 3); + assert_equals( + document.styleSheets[0].cssRules[0].styleset.get("di").length, + 1 + ); + }); + }, "CSSFontFeatureValuesMap entries are settable to single values."); + + test(function () { + resetStateAndTest(() => { + let rule = document.styleSheets[0].cssRules[0]; + let override_sequence = [43, 22]; + assert_equals( + document.styleSheets[0].cssRules[0].styleset.get("di").length, + 4 + ); + rule.styleset.set("di", override_sequence); + assert_array_equals( + document.styleSheets[0].cssRules[0].styleset.get("di"), + override_sequence + ); + assert_equals(document.styleSheets[0].cssRules[0].styleset.size, 3); + assert_equals( + document.styleSheets[0].cssRules[0].styleset.get("di").length, + 2 + ); + }); + }, "CSSFontFeatureValuesMap entries are settable to sequences of numbers."); + + test(function () { + resetStateAndTest(() => { + document.styleSheets[0].insertRule(rule_added, 1); + assert_equals(document.styleSheets[0].cssRules[1].type, 14); + assert_equals(document.styleSheets[0].cssRules[1].annotation.size, 3); + }); + }, "New rules can be added."); + + test(function () { + resetStateAndTest(() => { + document.styleSheets[0].insertRule(rule_added, 1); + assert_equals(document.styleSheets[0].cssRules[1].annotation.size, 3); + document.styleSheets[0].cssRules[1].annotation.delete("yogog"); + assert_equals(document.styleSheets[0].cssRules[1].annotation.size, 2); + }); + }, "Deleting single entries is possible."); + + test(function () { + resetStateAndTest(() => { + document.styleSheets[0].insertRule(rule_added, 1); + assert_equals(document.styleSheets[0].cssRules[1].annotation.size, 3); + document.styleSheets[0].cssRules[1].annotation.clear(); + assert_equals(document.styleSheets[0].cssRules[1].annotation.size, 0); + }); + }, "Clearing all entries is possible."); + + test(function () { + resetStateAndTest(() => { + assert_equals(document.styleSheets[0].cssRules.length, 1); + document.styleSheets[0].insertRule(rule_overlap, 1); + assert_equals(document.styleSheets[0].cssRules.length, 2); + + // Force updating internal state. + getComputedStyle(document.body).borderTop; + + assert_equals( + document.styleSheets[0].cssRules[0].annotation.size, + 1, + "1 annotation." + ); + assert_equals( + document.styleSheets[0].cssRules[0].styleset.size, + 3, + "3 entries in styleset." + ); + assert_equals( + document.styleSheets[0].cssRules[0].ornaments.size, + 0, + "No ornament entries." + ); + assert_false( + document.styleSheets[0].cssRules[0].annotation.has("baric"), + "Annotation must not contain 'baric'." + ); + assert_false( + document.styleSheets[0].cssRules[0].ornaments.has("fooic"), + "Ornaments must not contain 'fooic'." + ); + + assert_equals(document.styleSheets[0].cssRules[1].annotation.size, 1); + assert_equals(document.styleSheets[0].cssRules[1].ornaments.size, 1); + assert_false( + document.styleSheets[0].cssRules[1].annotation.has("the_first") + ); + }); + }, "Multiple rules for the same family are kept separate in CSSOM."); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSGroupingRule-cssRules.html b/testing/web-platform/tests/css/cssom/CSSGroupingRule-cssRules.html new file mode 100644 index 0000000000..e4afd5e560 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSGroupingRule-cssRules.html @@ -0,0 +1,54 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>CSSOM - CSSGroupingRule - cssRules</title> + <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssgroupingrule-interface"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + function create(t) { + var style = document.createElement('style'); + style.appendChild(document.createTextNode('@media all { * {} }')); + document.head.appendChild(style); + t.add_cleanup(function() { + document.head.removeChild(style); + }); + + assert_true(!!style.sheet, 'setup - sheet defined'); + assert_equals( + style.sheet.cssRules.length, 1, 'setup - grouping rule created' + ); + assert_equals( + style.sheet.cssRules[0].cssRules.length, 1, 'setup - rule created' + ); + return style.sheet.cssRules[0]; + } + + test(function (t) { + var groupingRule = create(t); + groupingRule.cssRules.wptMarker = 'wpt'; + + // `insertRule` is used to prompt non-conforming implementations to + // create a new CSSRuleList object. Its behavior is verified by a + // dedicated test and should not influence the result of this + // particular test. + try { + groupingRule.insertRule('.foo {}', 0); + groupingRule.insertRule('.bar {}', 0); + groupingRule.insertRule('.baz {}', 0); + } catch (err) {} + + assert_equals(groupingRule.cssRules.wptMarker, 'wpt'); + + try { + groupingRule.deleteRule('.foo {}', 0); + groupingRule.deleteRule('.bar {}', 0); + groupingRule.deleteRule('.baz {}', 0); + } catch (err) {} + + assert_equals(groupingRule.cssRules.wptMarker, 'wpt'); + }, '[SameObject] is honored'); + </script> +</head> +</html> diff --git a/testing/web-platform/tests/css/cssom/CSSGroupingRule-insertRule.html b/testing/web-platform/tests/css/cssom/CSSGroupingRule-insertRule.html new file mode 100644 index 0000000000..d131e3975d --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSGroupingRule-insertRule.html @@ -0,0 +1,118 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>CSSOM - CSSGroupingRule - insertRule</title> + <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssgroupingrule-interface"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + function create(t) { + var style = document.createElement('style'); + style.appendChild(document.createTextNode('@media all { * {} }')); + document.head.appendChild(style); + t.add_cleanup(function() { + document.head.removeChild(style); + }); + + assert_true(!!style.sheet, 'setup - sheet defined'); + assert_equals( + style.sheet.cssRules.length, 1, 'setup - grouping rule created' + ); + assert_equals( + style.sheet.cssRules[0].cssRules.length, 1, 'setup - rule created' + ); + return style.sheet.cssRules[0]; + } + + test(function (t) { + var groupingRule = create(t); + var first = groupingRule.cssRules[0].cssText; + var result; + + result = groupingRule.insertRule('.foo {}', 0); + + assert_equals(groupingRule.cssRules.length, 2); + assert_not_equals(groupingRule.cssRules[0].cssText, first); + assert_equals(groupingRule.cssRules[1].cssText, first); + assert_equals(result, 0, 'result'); + }, 'index before first'); + + test(function (t) { + var groupingRule = create(t); + var first = groupingRule.cssRules[0].cssText; + var result; + + result = groupingRule.insertRule('.foo {}', 1); + + assert_equals(groupingRule.cssRules.length, 2); + assert_equals(groupingRule.cssRules[0].cssText, first); + assert_not_equals(groupingRule.cssRules[1].cssText, first); + assert_equals(result, 1); + }, 'index after final'); + + test(function (t) { + var groupingRule = create(t); + var first = groupingRule.cssRules[0].cssText; + var result; + + result = groupingRule.insertRule('.foo {}'); + + assert_equals(groupingRule.cssRules.length, 2); + assert_not_equals(groupingRule.cssRules[0].cssText, first); + assert_equals(groupingRule.cssRules[1].cssText, first); + assert_equals(result, 0); + }, 'index not specified'); + + test(function (t) { + var groupingRule = create(t); + var first = groupingRule.cssRules[0].cssText; + + assert_throws_dom('IndexSizeError', function() { + // The syntax error is intentional; it verifies that the insertion + // index is validated prior to the CSS text. + groupingRule.insertRule('???', 2); + }); + + assert_equals(groupingRule.cssRules.length, 1); + assert_equals(groupingRule.cssRules[0].cssText, first); + }, 'index exceeds length'); + + test(function (t) { + var groupingRule = create(t); + var first = groupingRule.cssRules[0].cssText; + + assert_throws_dom('SyntaxError', function() { + groupingRule.insertRule('???', 0); + }); + + assert_equals(groupingRule.cssRules.length, 1); + assert_equals(groupingRule.cssRules[0].cssText, first); + }, 'CSS parsing error'); + + test(function (t) { + var groupingRule = create(t); + var first = groupingRule.cssRules[0].cssText; + + assert_throws_dom('HierarchyRequestError', function() { + groupingRule.insertRule('@import url("foo.css");', 0); + }); + + assert_equals(groupingRule.cssRules.length, 1); + assert_equals(groupingRule.cssRules[0].cssText, first); + }, 'constraint violation'); + + test(function (t) { + var groupingRule = create(t); + var first = groupingRule.cssRules[0].cssText; + + assert_throws_dom('HierarchyRequestError', function() { + groupingRule.insertRule('@namespace url(http://www.w3.org/1999/xhtml);', 0); + }); + + assert_equals(groupingRule.cssRules.length, 1); + assert_equals(groupingRule.cssRules[0].cssText, first); + }, 'disallowed namespace rule'); + </script> +</head> +</html> diff --git a/testing/web-platform/tests/css/cssom/CSSKeyframeRule.html b/testing/web-platform/tests/css/cssom/CSSKeyframeRule.html new file mode 100644 index 0000000000..3f6d182186 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSKeyframeRule.html @@ -0,0 +1,52 @@ +<!doctype html> +<meta charset="utf-8"> +<title></title> +<link rel="help" href="https://drafts.csswg.org/css-animations/#interface-csskeyframerule"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style type="text/css" id="styleElement"> + div { animation: 3s slidein; } + @keyframes slidein { + from { + margin-left: 100%; + width: 300%; + } + + to { + margin-left: 0%; + width: 100%; + } + } +</style> +<script> + var styleSheet = document.getElementById("styleElement").sheet; + var keyframesRule = styleSheet.cssRules[1]; + var fromRule = keyframesRule.cssRules[0]; + var toRule = keyframesRule.cssRules[1]; + + test(function() { + assert_equals(keyframesRule.name, "slidein"); + assert_equals(typeof fromRule.style, "object"); + assert_equals(fromRule.style.marginLeft, "100%"); + assert_equals(fromRule.style.width, "300%"); + + assert_equals(typeof toRule.style, "object"); + assert_equals(toRule.style.marginLeft, "0%"); + assert_equals(toRule.style.width, "100%"); + + toRule.style.marginLeft = "-5%"; + toRule.style.width = "50%"; + + assert_equals(fromRule.style.marginLeft, "100%"); + assert_equals(fromRule.style.width, "300%"); + assert_equals(toRule.style.marginLeft, "-5%"); + assert_equals(toRule.style.width, "50%"); + }, "CSSKeyframeRule: style property"); + + test(function() { + fromRule.style = "margin-left: 50%; width: 100%;"; + + assert_equals(fromRule.style.marginLeft, "50%", "margin-left"); + assert_equals(fromRule.style.width, "100%", "width"); + }, "CSSKeyframeRule: style property has [PutForwards]"); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSKeyframesRule.html b/testing/web-platform/tests/css/cssom/CSSKeyframesRule.html new file mode 100644 index 0000000000..a1adac383f --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSKeyframesRule.html @@ -0,0 +1,85 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>CSSOM - CSSKeyframesRule interface</title> + <link rel="help" href="https://drafts.csswg.org/css-animations/#interface-csskeyframesrule"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style> + @keyframes foo { + 0% { top: 0px; } + 100% { top: 200px; } + } + @keyframes empty {} + @keyframes indexed-access { + 0% { top: 0px; } + 100% { top: 200px; } + } + </style> + + <script> + test(function () { + var keyframe = document.styleSheets[0].cssRules[0]; + assert_equals(keyframe.name, "foo", "CSSKeyframesRule name attribute"); + assert_equals(keyframe.cssRules.length, 2, "CSSKeyframesRule cssRule length attribute"); + assert_equals(keyframe.cssRules[0].cssText, "0% { top: 0px; }", "CSSKeyframesRule cssRule cssText attribute"); + assert_equals(keyframe.cssRules[1].cssText, "100% { top: 200px; }", "CSSKeyframesRule cssRule cssText attribute"); + + keyframe.appendRule("50% { top: 100px; }"); + assert_equals(keyframe.cssRules.length, 3, "CSSKeyframesRule cssRule length attribute after appendRule function"); + assert_equals(keyframe.cssRules[0].cssText, "0% { top: 0px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function"); + assert_equals(keyframe.cssRules[1].cssText, "100% { top: 200px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function"); + assert_equals(keyframe.cssRules[2].cssText, "50% { top: 100px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function"); + + keyframe.appendRule("0% { top: 50px; }"); + assert_equals(keyframe.cssRules.length, 4, "CSSKeyframesRule cssRule length attribute after appendRule function"); + assert_equals(keyframe.cssRules[0].cssText, "0% { top: 0px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function"); + assert_equals(keyframe.cssRules[1].cssText, "100% { top: 200px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function"); + assert_equals(keyframe.cssRules[2].cssText, "50% { top: 100px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function"); + assert_equals(keyframe.cssRules[3].cssText, "0% { top: 50px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function"); + + var find1 = keyframe.findRule("50%"); + assert_equals(find1.cssText, "50% { top: 100px; }", "CSSKeyframesRule findRule function"); + var find2 = keyframe.findRule("0%"); + assert_equals(find2.cssText, "0% { top: 50px; }", "CSSKeyframesRule findRule function"); + var find3 = keyframe.findRule("70%"); + assert_equals(find3, null, "CSSKeyframesRule findRule function"); + + keyframe.deleteRule("100%"); + assert_equals(keyframe.cssRules.length, 3, "CSSKeyframesRule cssRule length attribute after deleteRule function"); + assert_equals(keyframe.cssRules[0].cssText, "0% { top: 0px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function"); + assert_equals(keyframe.cssRules[1].cssText, "50% { top: 100px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function"); + assert_equals(keyframe.cssRules[2].cssText, "0% { top: 50px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function"); + assert_equals(keyframe.cssRules[3], undefined, "CSSKeyframesRule cssRule cssText attribute after deleteRule function"); + + keyframe.deleteRule("80%"); + assert_equals(keyframe.cssRules.length, 3, "CSSKeyframesRule cssRule length attribute after deleteRule function"); + assert_equals(keyframe.cssRules[0].cssText, "0% { top: 0px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function"); + assert_equals(keyframe.cssRules[1].cssText, "50% { top: 100px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function"); + assert_equals(keyframe.cssRules[2].cssText, "0% { top: 50px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function"); + assert_equals(keyframe.cssRules[3], undefined, "CSSKeyframesRule cssRule cssText attribute after deleteRule function"); + + var empty = document.styleSheets[0].cssRules[1]; + empty.name = "bar"; + assert_equals(empty.name, "bar", "CSSKeyframesRule name setter"); + assert_equals(empty.cssText.replace(/\s/g, ""), "@keyframesbar{}", "CSSKeyframesRule cssText attribute"); + + empty.name = "initial"; + assert_equals(empty.name, "initial", "CSSKeyframesRule name setter, CSS-wide keyword"); + assert_equals(empty.cssText.replace(/\s/g, ""), "@keyframes\"initial\"{}", "CSSKeyframesRule cssText attribute with CSS-wide keyword name"); + + empty.name = "none"; + assert_equals(empty.name, "none", "CSSKeyframesRule name setter, 'none'"); + assert_equals(empty.cssText.replace(/\s/g, ""), "@keyframes\"none\"{}", "CSSKeyframesRule cssText attribute with 'none' name"); + }, 'name, cssRules, appendRule, findRule, deleteRule'); + + test(function () { + const keyframes = document.styleSheets[0].cssRules[2]; + assert_equals(keyframes[0].cssText, "0% { top: 0px; }", "CSSKeyframesRule indexed getter [0]"); + assert_equals(keyframes[1].cssText, "100% { top: 200px; }", "CSSKeyframesRule indexed getter [1]"); + assert_equals(keyframes.length, 2, "CSSKeyframesRule.length"); + }, 'indexed getter, length'); + </script> +</head> +</html> diff --git a/testing/web-platform/tests/css/cssom/CSSNamespaceRule.html b/testing/web-platform/tests/css/cssom/CSSNamespaceRule.html new file mode 100644 index 0000000000..fbbaa3c569 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSNamespaceRule.html @@ -0,0 +1,32 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>CSSOM - CSSNamespaceRule interface</title> + <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssnamespacerule-interface"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style> + @namespace svg url(http://servo); + @namespace url(http://servo1); + @namespace svg url("http://servo2"); + </style> + + <script> + test(function () { + var rules = document.styleSheets[0].cssRules; + assert_equals(rules[0].prefix, "svg", "CSSNamespaceRule prefix attribute"); + assert_equals(rules[0].namespaceURI, "http://servo", "CSSNamespaceRule namespaceURI attribute"); + assert_equals(rules[0].cssText, "@namespace svg url(\"http://servo\");", "CSSNamespaceRule cssText attribute"); + + assert_equals(rules[1].prefix, "", "CSSNamespaceRule prefix attribute"); + assert_equals(rules[1].namespaceURI, "http://servo1", "CSSNamespaceRule namespaceURI attribute"); + assert_equals(rules[1].cssText, "@namespace url(\"http://servo1\");", "CSSNamespaceRule cssText attribute"); + + assert_equals(rules[2].prefix, "svg", "CSSNamespaceRule prefix attribute"); + assert_equals(rules[2].namespaceURI, "http://servo2", "CSSNamespaceRule namespaceURI attribute"); + assert_equals(rules[2].cssText, "@namespace svg url(\"http://servo2\");", "CSSNamespaceRule cssText attribute"); + }); + </script> +</head> +</html> diff --git a/testing/web-platform/tests/css/cssom/CSSRuleList.html b/testing/web-platform/tests/css/cssom/CSSRuleList.html new file mode 100644 index 0000000000..4605b74964 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSRuleList.html @@ -0,0 +1,27 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>CSSOM - CSSRuleList interface</title> + <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssrulelist-interface"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style> + body { width: 50%; } + #foo { height: 100px; } + </style> + + <script> + test(function () { + var ruleList = document.styleSheets[0].cssRules; + assert_equals(ruleList.length, 2, "CSSRuleList length attribute"); + assert_equals(ruleList[0].cssText, "body { width: 50%; }", "CSSRuleList indexed getter"); + assert_equals(ruleList[1].cssText, "#foo { height: 100px; }", "CSSRuleList indexed getter"); + assert_equals(ruleList[2], undefined, "CSSRuleList indexed getter"); + assert_equals(ruleList.item(0).cssText, "body { width: 50%; }", "CSSRuleList item function"); + assert_equals(ruleList.item(1).cssText, "#foo { height: 100px; }", "CSSRuleList item function"); + assert_equals(ruleList.item(2), null, "CSSRuleList item function"); + }); + </script> +</head> +</html> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText-namespace.html b/testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText-namespace.html new file mode 100644 index 0000000000..4da0a333e9 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText-namespace.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CSSOM StyleRule selectorText property setter with namespaces</title> +<link rel="help" href="https://drafts.csswg.org/cssom-1/#dom-cssstylerule-selectortext"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> + +<style type="text/css" id="styleElement"> +@namespace url(http://www.w3.org/1999/xhtml); +@namespace svg url(http://www.w3.org/2000/svg); + +svg|*.style0 { background-color: rgb(0, 0, 255) !important; } +svg|*.style1 { background-color: rgb(255, 0, 255); } +</style> + +<span> + <p></p> + + <svg height="30" width="200" id="container" class="style1" lang="zh-CN" language segment="42 43"> + <text x="0" y="15">SVG text</text> + </svg> +</span> + +<script> + var styleSheet = document.getElementById("styleElement").sheet; + var rule = styleSheet.cssRules[2]; + + var divContainerStyle = getComputedStyle(document.getElementById("container")); + + const originalStyleSelector = "svg|*.style0"; + + var assertColors = function(selectorMatches) { + assert_equals(divContainerStyle.getPropertyValue('background-color'), selectorMatches ? "rgb(0, 0, 255)" : "rgb(255, 0, 255)") + }; + + [ + {selector: ".style1", isMatch: false, }, + {selector: "svg|*.style1 ", isMatch: true, normalizedSelector: "svg|*.style1"}, + {selector: "*|*.style1 ", isMatch: true, normalizedSelector: "*|*.style1"}, + {selector: " *.style1 ", isMatch: false, normalizedSelector: ".style1"}, + {selector: "p", isMatch: false}, + ].forEach(function(testCase) { + test(function() { + // Check if starting with the default value. + assert_equals(rule.selectorText, originalStyleSelector); + + this.add_cleanup(function() { rule.selectorText = originalStyleSelector; }); + + assertColors(false); + + rule.selectorText = testCase.selector; + + var expectedSelector = testCase.normalizedSelector ? testCase.normalizedSelector : testCase.selector; + + assert_equals(rule.selectorText, expectedSelector); + + assertColors(testCase.isMatch); + }, "CSSStyleRule: selectorText value: |" + testCase.selector + "| isMatch: " + testCase.isMatch); + }); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText.html b/testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText.html new file mode 100644 index 0000000000..e29db52ec6 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText.html @@ -0,0 +1,158 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CSSOM StyleRule selectorText property setter</title> +<link rel="help" href="https://drafts.csswg.org/cssom-1/#dom-cssstylerule-selectortext"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> + +<style type="text/css" id="styleElement"> + .style0 { background-color: rgb(0, 0, 255) !important; } + .style1 { background-color: rgb(255, 0, 255); } +</style> + +<span> + <p></p> + <div id="container" class="style1" lang="zh-CN" language segment="42 43"> + </div> +</span> + +<script> + var styleSheet = document.getElementById("styleElement").sheet; + var rule = styleSheet.cssRules[0]; + + var divContainerStyle = getComputedStyle(document.getElementById("container")); + + const originalStyleSelector = ".style0"; + + var assertColors = function(selectorMatches) { + assert_equals(divContainerStyle.backgroundColor, selectorMatches ? "rgb(0, 0, 255)" : "rgb(255, 0, 255)") + }; + + test(function() { + assert_equals(typeof rule.selectorText, "string"); + assert_equals(rule.selectorText, originalStyleSelector); + }, "CSSStyleRule: Can read selectorText value."); + + [ // Invalid selector values. + "", + " ", + "!!", + "123", + "-", + "$", + ":", + "::", + ":::", + "::gibberish", + ":gibberish", + ".", + "#", + "[]", + "[", + "()", + "(", + "{}", + "{", + ].forEach(function(selector) { + test(function() { + assert_equals(rule.selectorText, originalStyleSelector); + + this.add_cleanup(function() { rule.selectorText = originalStyleSelector; }); + + rule.selectorText = selector; + + assert_equals(rule.selectorText, originalStyleSelector); + }, "CSSStyleRule: Invalid CSS selector: " + selector); + }); + + + [ // Valid selector values. + {selector: "#container", isMatch: true}, + {selector: "#container ", isMatch: true, normalizedSelector: "#container"}, + {selector: " #container ", isMatch: true, normalizedSelector: "#container"}, + {selector: ".style1", isMatch: true}, + {selector: "div.style1", isMatch: true}, + {selector: "div:not(#non-existing-id)", isMatch: true}, + {selector: "div", isMatch: true}, + {selector: "*", isMatch: true}, + + {selector: "#no-match", isMatch: false}, + {selector: "ÇĞıİ", isMatch: false}, + {selector: "🤓", isMatch: false}, + + {selector: "[language]", isMatch: true}, + {selector: "[language-no]", isMatch: false}, + {selector: "[lang=\"zh-CN\"]", isMatch: true}, + {selector: "[lang=\"ab-CD\"]", isMatch: false}, + {selector: "[segment~=\"43\"]", isMatch: true}, + {selector: "[segment~=\"42\"]", isMatch: true}, + {selector: "[lang|=\"zh\"]", isMatch: true}, + {selector: "[lang|=\"zh-CN\"]", isMatch: true}, + {selector: "[lang|=\"ab\"]", isMatch: false}, + {selector: "[lang|=\"z\"]", isMatch: false}, + {selector: "[lang^=\"z\"]", isMatch: true}, + {selector: "[lang^=\"ab\"]", isMatch: false}, + {selector: "[segment$=\"43\"]", isMatch: true}, + {selector: "[segment$=\"3\"]", isMatch: true}, + {selector: "[segment$=\"42\"]", isMatch: false}, + {selector: "[lang*=\"-\"]", isMatch: true}, + {selector: "[lang*=\"h-\"]", isMatch: true}, + {selector: "[lang*=\"ab\"]", isMatch: false}, + + {selector: "*|div", isMatch: true, normalizedSelector: "div"}, + {selector: "|div", isMatch: false}, + {selector: "*|a", isMatch: false, normalizedSelector: "a"}, + {selector: "*|*", isMatch: true, normalizedSelector: "*"}, + {selector: "[*|lang]", isMatch: true, normalizedSelector: "[*|lang]"}, + {selector: "[|lang]", isMatch: true, normalizedSelector: "[lang]"}, + + {selector: ":active", isMatch: false}, + {selector: ":not(:active)", isMatch: true}, + {selector: "*:not(:active)", isMatch: true, normalizedSelector: ":not(:active)"}, + {selector: "div:not(:active)", isMatch: true}, + {selector: "div:active", isMatch: false}, + + {selector: "span div", isMatch: true}, + {selector: "span div ", isMatch: true, normalizedSelector: "span div"}, + {selector: "span > div", isMatch: true}, + {selector: "div div", isMatch: false}, + {selector: "div > div", isMatch: false}, + {selector: "p + div", isMatch: true}, + {selector: "span + div", isMatch: false}, + {selector: "p ~ div", isMatch: true}, + {selector: "span ~ div", isMatch: false}, + + {selector: ":lang(zh-CN)", isMatch: true}, + {selector: ":lang(zh)", isMatch: true}, + {selector: ":lang(tr-AZ)", isMatch: false}, + + {selector: "::after", isMatch: false, normalizedSelector: "::after"}, + {selector: ":after", isMatch: false, normalizedSelector: "::after"}, + {selector: "::before", isMatch: false, normalizedSelector: "::before"}, + {selector: ":before", isMatch: false, normalizedSelector: "::before"}, + {selector: "::first-letter", isMatch: false, normalizedSelector: "::first-letter"}, + {selector: ":first-letter", isMatch: false, normalizedSelector: "::first-letter"}, + {selector: "::first-line", isMatch: false, normalizedSelector: "::first-line"}, + {selector: ":first-line", isMatch: false, normalizedSelector: "::first-line"}, + + {selector: "div:focus:not([lang=\"zh-CN\"])", isMatch: false}, + {selector: "div[lang=\"zh-CN\"]:not(:focus)", isMatch: true}, + ].forEach(function(testCase) { + test(function() { + // Check if starting with the default value. + assert_equals(rule.selectorText, originalStyleSelector); + + this.add_cleanup(function() { rule.selectorText = originalStyleSelector; }); + + assertColors(false); + + rule.selectorText = testCase.selector; + + var expectedSelector = testCase.normalizedSelector ? testCase.normalizedSelector : testCase.selector; + + assert_equals(rule.selectorText, expectedSelector); + + assertColors(testCase.isMatch); + }, "CSSStyleRule: selectorText value: |" + testCase.selector + "| isMatch: " + testCase.isMatch); + }); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleRule.html b/testing/web-platform/tests/css/cssom/CSSStyleRule.html new file mode 100644 index 0000000000..3acdfb1285 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleRule.html @@ -0,0 +1,113 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSSOM CSSRule CSSStyleRule interface</title> + <link rel="author" title="Letitia Lew" href="mailto:lew.letitia@gmail.com"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#css-rules"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstylerule-interface"> + <meta name="flags" content="dom"> + <meta name="assert" content="All properties for this CSSStyleRule instance of CSSRule are initialized correctly"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <style id="styleElement" type="text/css"> + div { margin: 10px; padding: 0px; } + </style> +</head> +<body> + <div id="log"></div> + + <script type="text/javascript"> + var rule; + setup(function() { + var styleSheet = document.getElementById("styleElement").sheet; + var ruleList = styleSheet.cssRules; + rule = ruleList[0]; + }); + + test(function() { + assert_true(rule instanceof CSSRule); + assert_true(rule instanceof CSSStyleRule); + }, "CSSRule and CSSStyleRule types"); + + test(function() { + assert_equals(rule.STYLE_RULE, 1); + assert_equals(rule.IMPORT_RULE, 3); + assert_equals(rule.MEDIA_RULE, 4); + assert_equals(rule.FONT_FACE_RULE, 5); + assert_equals(rule.PAGE_RULE, 6); + assert_equals(rule.NAMESPACE_RULE, 10); + assert_idl_attribute(rule, "type"); + assert_equals(typeof rule.type, "number"); + }, "Type of CSSRule#type and constant values"); + + test(function() { + assert_true(rule instanceof CSSRule); + assert_idl_attribute(rule, "cssText"); + assert_idl_attribute(rule, "parentRule"); + assert_idl_attribute(rule, "parentStyleSheet"); + }, "Existence of CSSRule attributes"); + + test(function() { + assert_readonly(rule, "type"); + assert_readonly(rule, "parentRule"); + assert_readonly(rule, "parentStyleSheet"); + }, "Writability of CSSRule attributes"); + + test(function() { + assert_equals(rule.type, rule.STYLE_RULE); + assert_equals(typeof rule.cssText, "string"); + assert_equals(rule.cssText, "div { margin: 10px; padding: 0px; }"); + assert_equals(rule.parentRule, null); + assert_true(rule.parentStyleSheet instanceof CSSStyleSheet); + }, "Values of CSSRule attributes"); + + test(function() { + assert_idl_attribute(rule, "selectorText"); + assert_equals(typeof rule.selectorText, "string"); + assert_idl_attribute(rule, "style"); + }, "Existence and type of CSSStyleRule attributes"); + + test(function() { + // CSSStyleRule.style has PutForwards=cssText and SameObject. + var initial = rule.style.cssText; + var style = rule.style; + + rule.style = ""; + assert_equals(rule.style.cssText, ""); + assert_equals(rule.style, style); + + rule.style = "margin: 42px;"; + assert_equals(rule.style.margin, "42px"); + assert_equals(rule.style, style); + + rule.style = initial; + assert_equals(rule.style, style); + }, "Assigning to CSSStyleRule.style assigns to cssText; CSSStyleRule.style returns the same object"); + + test(function() { + assert_equals(rule.selectorText, "div"); + assert_true(rule.style instanceof CSSStyleDeclaration); + }, "Values of CSSStyleRule attributes"); + + test(function() { + assert_equals(rule.style.margin, "10px"); + assert_equals(rule.style.padding, "0px"); + + rule.style.padding = "5px"; + rule.style.border = "1px solid"; + + assert_equals(rule.style.padding, "5px"); + assert_equals(rule.style.border, "1px solid"); + }, "Mutability of CSSStyleRule's style attribute"); + + test(function() { + rule.style = "margin: 15px; padding: 2px;"; + + assert_equals(rule.style.margin, "15px", "margin"); + assert_equals(rule.style.padding, "2px", "padding"); + }, "CSSStyleRule's style has [PutForwards]"); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-baseURL.tentative.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-baseURL.tentative.html new file mode 100644 index 0000000000..8997a59e9c --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-baseURL.tentative.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<title>CSSStyleSheet baseURL</title> +<link rel="author" title="Erik Nordin" href="mailto:enordin@mozilla.com"> +<link rel="help" href="https://github.com/WICG/construct-stylesheets/issues/95#issuecomment-593545252"> +<div id="target"></div> +<script src='/resources/testharness.js'></script> +<script src='/resources/testharnessreport.js'></script> +<script> + +function currentLocation() { + const sections = location.href.split("/") + sections.pop(); + return sections.join("/"); +} + +test(() => { + const span = document.createElement("span"); + target.appendChild(span); + span.attachShadow({ mode: "open" }) + const shadowDiv = document.createElement("div"); + span.shadowRoot.appendChild(shadowDiv); + + const fileName = "example.png" + const baseURL = `${location.origin}/custom/path/`; + const fullURL = `${baseURL}${fileName}`; + + const sheet = new CSSStyleSheet({ baseURL }); + span.shadowRoot.adoptedStyleSheets = [sheet]; + + sheet.replaceSync(`* { background-image: url("${fileName}"); }`); + const styleFromRelative = getComputedStyle(shadowDiv).backgroundImage; + + sheet.replaceSync(`* { background-image: url("${fullURL}"); }`); + const styleFromFull = getComputedStyle(shadowDiv).backgroundImage; + + assert_equals(styleFromRelative, styleFromFull); +}, "Constructing sheet with custom base URL ueses that URL for CSS rules"); + +test(() => { + const span = document.createElement("span"); + target.appendChild(span); + span.attachShadow({ mode: "open" }) + const shadowDiv = document.createElement("div"); + span.shadowRoot.appendChild(shadowDiv); + + const fileName = "example.png" + const baseURL = "custom/path/"; + const fullURL = `${currentLocation()}/${baseURL}${fileName}`; + + const sheet = new CSSStyleSheet({ baseURL }); + span.shadowRoot.adoptedStyleSheets = [sheet]; + + sheet.replaceSync(`* { background-image: url("${fileName}"); }`); + const styleFromRelative = getComputedStyle(shadowDiv).backgroundImage; + + sheet.replaceSync(`* { background-image: url("${fullURL}"); }`); + const styleFromFull = getComputedStyle(shadowDiv).backgroundImage; + + assert_equals(styleFromRelative, styleFromFull); +}, "Constructing sheet with relative URL adds to the constructor document's base URL"); + +test(() => { + assert_throws_dom("NotAllowedError", () => { new CSSStyleSheet({ baseURL: "https://test:test/"}) }); +}, "Constructing sheet with invalid base URL throws a NotAllowedError"); + +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat-ref.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat-ref.html new file mode 100644 index 0000000000..1c6b6784c9 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat-ref.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Adoptedstylesheets.concat should work when starting empty</title> +<link rel="author" href="mailto:masonf@chromium.org"> + +<span>This should be green</span><br> +<span>This should be green</span> +<style> + span { + background-color:green; + } +</style> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat.html new file mode 100644 index 0000000000..3ba9992845 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Adoptedstylesheets.concat should work when starting empty</title> +<link rel="author" href="mailto:masonf@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/cssom/#extensions-to-the-document-or-shadow-root-interface"> +<link rel="match" href="CSSStyleSheet-constructable-concat-ref.html"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<span>This should be green</span> +<div id=host></div> + +<script> +test(function() { + const sheet = new CSSStyleSheet(); + sheet.replaceSync('span {background-color:green;}'); + assert_equals(document.adoptedStyleSheets.length,0); + document.adoptedStyleSheets = document.adoptedStyleSheets.concat([sheet]); + assert_equals(document.adoptedStyleSheets.length,1); + + const host = document.getElementById('host'); + const shadow = host.attachShadow({mode: 'open'}); + shadow.innerHTML = '<span>This should be green</span>'; + assert_equals(shadow.adoptedStyleSheets.length,0); + shadow.adoptedStyleSheets = shadow.adoptedStyleSheets.concat([sheet]); + assert_equals(shadow.adoptedStyleSheets.length,1); +}, "adoptedStyleSheets should allow .concat on empty starting values"); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-cssRules.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-cssRules.html new file mode 100644 index 0000000000..72b0e15c8b --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-cssRules.html @@ -0,0 +1,22 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSStyleSheet.replace/replaceSync() doesn't change cssRules object</title> +<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> +<link rel="author" href="https://mozilla.org" title="Mozilla"> +<link rel="help" href="https://drafts.csswg.org/cssom/#extensions-to-the-document-or-shadow-root-interface"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1752392"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<span>Should be green</span> +<script> +promise_test(async function() { + const sheet = new CSSStyleSheet(); + let rules = sheet.cssRules; + sheet.replaceSync('span {color:blue;}'); + assert_equals(rules, sheet.cssRules, "Rules should be the same after replaceSync"); + await sheet.replace('span {color: lime;}'); + assert_equals(rules, sheet.cssRules, "Rules should be the same after replace()"); + document.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(document.querySelector("span")).color, "rgb(0, 255, 0)", "Sheet should apply"); +}, "cssRules doesn't change on replace / replaceSync"); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disabled-regular-sheet-insertion.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disabled-regular-sheet-insertion.html new file mode 100644 index 0000000000..ef4ea14806 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disabled-regular-sheet-insertion.html @@ -0,0 +1,22 @@ +<!doctype html> +<title>Shouldn't crash / assert when inserting a stylesheet after there are disabled constructable sheets</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="help" href="https://wicg.github.io/construct-stylesheets/"> +<script src = '/resources/testharness.js'></script> +<script src = '/resources/testharnessreport.js'></script> +<div id="host"></div> +<script> +test(function() { + let sheet = new CSSStyleSheet({ disabled: true }); + sheet.replaceSync("div { color: red }"); + + let root = document.getElementById("host").attachShadow({ mode: "open" }); + root.adoptedStyleSheets = [sheet]; + let style = document.createElement("style"); + root.innerHTML = ` + <style>div { color: green }</style> + <div>Should be green</div> + `; + assert_equals(getComputedStyle(root.querySelector("div")).color, "rgb(0, 128, 0)", "Should insert the sheet at the right position and not crash"); +}); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html new file mode 100644 index 0000000000..d49a084fbe --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<title>CSSStyleSheet Disallow Import Rules</title> +<link rel="author" title="Erik Nordin" href="mailto:enordin@mozilla.com"> +<link rel="help" href="https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-597733392"> +<div id="target"></div> +<script src='/resources/testharness.js'></script> +<script src='/resources/testharnessreport.js'></script> +<script> + +const greenStyleText = ".green { color: green; }"; +const import_text = '@import url("support/constructable-import.css");'; + +function attachShadowDiv(host) { + const shadowRoot = host.attachShadow({mode: 'open'}); + const shadowDiv = document.createElement("div"); + shadowRoot.appendChild(shadowDiv); + return shadowDiv; +} + +test(() => { + assert_throws_dom("SyntaxError", () => { (new CSSStyleSheet).insertRule(import_text) }); +}, 'Inserting an @import rule through insertRule on a constructed stylesheet throws an exception'); + +promise_test(async t => { + const importUrl = "support/constructable-import.css"; + const sheet = new CSSStyleSheet(); + + sheet.replaceSync(import_text); + await sheet.replace(import_text); + assert_throws_dom("SyntaxError", () => { sheet.insertRule(import_text) }); + + const timeAfterImportsInvoked = performance.now(); + + let link = document.createElement("link"); + t.add_cleanup(() => { link.remove(); }); + link.rel = "stylesheet"; + link.href = importUrl; + + await new Promise((resolve, reject) => { + link.addEventListener("load", resolve); + link.addEventListener("error", reject); + document.body.appendChild(link); + }); + + let entries = window.performance.getEntriesByType('resource').filter(entry => entry.name.includes(importUrl)); + assert_equals(entries.length, 1, "There should be only one entry for the import URL"); + assert_greater_than_equal(entries[0].startTime, timeAfterImportsInvoked, "The entry's start time should be after all throws"); +}, "@import rules should not trigger any loads.") + +promise_test(() => { + const span = document.createElement("span"); + target.appendChild(span); + const shadowDiv = attachShadowDiv(span); + shadowDiv.classList.add("green"); + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + const sheet_promise = sheet.replace(`${import_text} ${greenStyleText}`); + return sheet_promise.then((sheet) => { + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules[0].cssText, greenStyleText); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 128, 0)"); + }).catch((reason) => { + assert_unreached(`Promise was rejected (${reason}) when it should have been resolved`); + }); +}, '@import rules are not parsed in CSSStyleSheet.replace'); + +test(() => { + const span = document.createElement("span"); + target.appendChild(span); + const shadowDiv = attachShadowDiv(span); + shadowDiv.classList.add("green"); + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + // Replace and assert that the imported rule is applied. + const sheet_promise = sheet.replaceSync(`${import_text} ${greenStyleText}`); + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules[0].cssText, greenStyleText); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 128, 0)"); +}, '@import rules are not parsed in CSSStyleSheet.replaceSync'); + +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-duplicate.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-duplicate.html new file mode 100644 index 0000000000..579f5d0fce --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-duplicate.html @@ -0,0 +1,84 @@ +<!doctype html> +<title>Cascade order of a stylesheet for duplicate sheets.</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="help" href="https://wicg.github.io/construct-stylesheets/"> +<script src = '/resources/testharness.js'></script> +<script src = '/resources/testharnessreport.js'></script> +<div id="target"></div> +<script> + +function attachShadowDiv(host) { + const shadowRoot = host.attachShadow({mode: 'open'}); + const shadowDiv = document.createElement("div"); + shadowRoot.appendChild(shadowDiv); + return shadowDiv; +} + +function blueSheetsWithIncreasingZIndex(n) { + let sheets = []; + for (let i = 0; i < n; ++i) { + sheets.push(new CSSStyleSheet()); + sheets[i].replaceSync("div { z-index: " + i + "; color: blue; }"); + } + return sheets; +} + +let sheets = []; + +test(function() { + sheets = blueSheetsWithIncreasingZIndex(2); + + document.adoptedStyleSheets = [sheets[1], sheets[0], sheets[1]]; + assert_equals(getComputedStyle(document.querySelector("div")).zIndex, "1", "duplicate stylesheet should take right position in the cascade"); + + document.adoptedStyleSheets = []; +}, "Duplicate stylesheets have the right cascade position in the Document"); + +test(function() { + sheets = blueSheetsWithIncreasingZIndex(2); + + const sheet = new CSSStyleSheet(); + sheet.replaceSync("div { color: red; }"); + + document.adoptedStyleSheets = [sheets[1], sheets[0]]; + assert_equals(getComputedStyle(document.querySelector("div")).zIndex, "0", "backmost stylesheet should take precedence"); + assert_equals(getComputedStyle(document.querySelector("div")).color, "rgb(0, 0, 255)", "backmost stylesheet should take precedence"); + + document.adoptedStyleSheets = [sheets[1], sheets[0], sheets[1], sheet]; + assert_equals(getComputedStyle(document.querySelector("div")).zIndex, "1", "duplicate stylesheet should take the right position in the cascade"); + assert_equals(getComputedStyle(document.querySelector("div")).color, "rgb(255, 0, 0)", "backmost stylesheet should take precedence"); + + document.adoptedStyleSheets = []; +}, "Appending duplicate stylesheets yields the correct cascade position in the Document"); + +test(function() { + sheets = blueSheetsWithIncreasingZIndex(2); + + const span = document.createElement("span"); + target.appendChild(span); + attachShadowDiv(span); + + span.shadowRoot.adoptedStyleSheets = [sheets[1], sheets[0], sheets[1]]; + assert_equals(getComputedStyle(span.shadowRoot.querySelector("div")).zIndex, "1", "duplicate stylesheet should take right position in the cascade"); +}, "Duplicate stylesheets have the right cascade position in the ShadowRoot"); + +test(function() { + sheets = blueSheetsWithIncreasingZIndex(2); + + const sheet = new CSSStyleSheet(); + sheet.replaceSync("div { color: red; }"); + + const span = document.createElement("span"); + target.appendChild(span); + attachShadowDiv(span); + + span.shadowRoot.adoptedStyleSheets = [sheets[1], sheets[0]]; + assert_equals(getComputedStyle(span.shadowRoot.querySelector("div")).zIndex, "0", "backmost stylesheet should take precedence"); + assert_equals(getComputedStyle(span.shadowRoot.querySelector("div")).color, "rgb(0, 0, 255)", "backmost stylesheet should take precedence"); + + span.shadowRoot.adoptedStyleSheets = [sheets[1], sheets[0], sheets[1], sheet]; + assert_equals(getComputedStyle(span.shadowRoot.querySelector("div")).zIndex, "1", "duplicate stylesheet should take right position in the cascade"); + assert_equals(getComputedStyle(span.shadowRoot.querySelector("div")).color, "rgb(255, 0, 0)", "backmost stylesheet should take precedence"); +}, "Appending duplicate stylesheets yields the correct cascade position in the ShadowRoot"); + +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-replace-on-regular-sheet.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-replace-on-regular-sheet.html new file mode 100644 index 0000000000..76bee452a6 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-replace-on-regular-sheet.html @@ -0,0 +1,42 @@ +<!doctype html> +<title>replace / replaceSync on non-constructed stylesheet</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="help" href="https://wicg.github.io/construct-stylesheets/"> +<script src = '/resources/testharness.js'></script> +<script src = '/resources/testharnessreport.js'></script> +<style id="style"> + @import url("support/constructable-import.css"); + :root { background-color: lime } +</style> +<script> + +promise_test(async () => { + await new Promise(resolve => window.addEventListener("load", resolve)); + assert_equals(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 255, 0)", "precondition") + let sheet = document.styleSheets[0]; + let childSheet = sheet.cssRules[0].styleSheet; + assert_throws_dom("NotAllowedError", () => sheet.replaceSync(":root { background-color: red }"), "replaceSync on non-constructed sheet should throw"); + assert_throws_dom("NotAllowedError", () => childSheet.replaceSync(":root { background-color: red }"), "replaceSync on non-constructed sheet should throw"); + assert_equals(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 255, 0)", "old sheet should still apply after replace"); +}, "CSSStyleSheet.replaceSync throws NotAllowedError for non-constructed sheets") + +promise_test(async function(t) { + assert_equals(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 255, 0)", "precondition") + let sheet = document.styleSheets[0]; + let childSheet = sheet.cssRules[0].styleSheet; + await promise_rejects_dom(t, "NotAllowedError", sheet.replace(":root { background-color: red }"), "replace on non-constructed sheet should return a rejected promise"); + await promise_rejects_dom(t, "NotAllowedError", childSheet.replace(":root { background-color: red }"), "replace on non-constructed sheet should return a rejected promise"); + assert_equals(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 255, 0)", "old sheet should still apply after replace"); +}, "CSSStyleSheet.replace returns a rejected promise for non-constructed sheets") + +promise_test(async function(t) { + assert_equals(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 255, 0)", "precondition") + let sheet = document.styleSheets[0]; + let childSheet = sheet.cssRules[0].styleSheet; + style.remove() // sheet's associated document becomes null. + await promise_rejects_dom(t, "NotAllowedError", sheet.replace(":root { background-color: red }"), "replace on non-constructed sheet should return a rejected promise"); + await promise_rejects_dom(t, "NotAllowedError", childSheet.replace(":root { background-color: red }"), "replace on non-constructed sheet should return a rejected promise"); + assert_equals(getComputedStyle(document.documentElement).backgroundColor, "rgba(0, 0, 0, 0)", "old sheet was removed, so the default color should apply"); +}, "CSSStyleSheet.replace returns a rejected promise for non-constructed sheets that have no associated document") + +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html new file mode 100644 index 0000000000..f84e4ea9af --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html @@ -0,0 +1,801 @@ +<!DOCTYPE html> +<title>CSSStyleSheet constructor and adoptedStyleSheets</title> +<link rel="author" title="Rakina Zata Amni" href="mailto:rakina@chromium.org"> +<link rel="help" href="https://wicg.github.io/construct-stylesheets/"> +<script src = '/resources/testharness.js'></script> +<script src = '/resources/testharnessreport.js'></script> + +<section id="firstSection"> + <div> + <span class="green"></span> + <span class="red"></span> + <span class="blue"></span> + <span class="white"></span> + <span class="yellow"></span> + </div> +</section> +<section id="shadowHost"></section> +<section id="thirdSection"></section> + +<script> +'use strict'; +const greenStyleText = ".green { color: green; }"; +const redStyleTexts = [".red { color: red; }", ".red + span + span { color: red; }"]; +const blueStyleTexts = [".blue { color: blue; }", ".blue + span + span { color: blue; }"]; +const whiteStyleText = "* { color: white; }"; +const yellowStyleText = ".yellow { color: yellow; }"; + +const firstDiv = document.querySelector('#firstSection > div'); +const secondDiv = firstDiv.cloneNode(true); +const shadowHost = document.querySelector('#shadowHost'); +const shadowRoot = shadowHost.attachShadow({mode: 'open'}); +shadowRoot.appendChild(secondDiv); + +const greenSpan = firstDiv.children[0]; +const redSpan = firstDiv.children[1]; +const blueSpan = firstDiv.children[2]; +const whiteSpan = firstDiv.children[3]; +const yellowSpan = firstDiv.children[4]; +const greenShadowSpan = secondDiv.children[0]; +const redShadowSpan = secondDiv.children[1]; +const blueShadowSpan = secondDiv.children[2]; +const whiteShadowSpan = secondDiv.children[3]; +const yellowShadowSpan = secondDiv.children[4]; + +test(() => { + assert_equals(document.adoptedStyleSheets.length, 0); +}, "document.adoptedStyleSheets should initially have length 0."); + +test(() => { + const sheet = new CSSStyleSheet({disabled: true, media: "screen, print"}); + assert_equals(sheet.title, null, "The title attribute must return the title or null if title is the empty string"); + assert_equals(sheet.ownerNode, null); + assert_equals(sheet.ownerRule, null); + assert_equals(sheet.media.length, 2); + assert_equals(sheet.media.item(0), "screen"); + assert_equals(sheet.media.item(1), "print"); + assert_true(sheet.disabled); + assert_equals(sheet.cssRules.length, 0); + + sheet.insertRule(redStyleTexts[0]); + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]); + + sheet.insertRule(redStyleTexts[1]); + assert_equals(sheet.cssRules.length, 2); + assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]); + + const sheet2 = new CSSStyleSheet({}); + assert_equals(sheet2.title, null, "The title attribute must return the title or null if title is the empty string"); + assert_equals(sheet2.ownerNode, null); + assert_equals(sheet2.ownerRule, null); + assert_equals(sheet2.media.length, 0); + assert_false(sheet2.disabled); + assert_equals(sheet2.cssRules.length, 0); + + sheet2.insertRule(redStyleTexts[1]); + assert_equals(sheet2.cssRules.length, 1); + assert_equals(sheet2.cssRules[0].cssText, redStyleTexts[1]); + + sheet2.deleteRule(0); + assert_equals(sheet2.cssRules.length, 0); + + const sheet3 = new CSSStyleSheet(); + assert_equals(sheet3.title, null, "The title attribute must return the title or null if title is the empty string"); + assert_equals(sheet3.ownerNode, null); + assert_equals(sheet3.ownerRule, null); + assert_equals(sheet3.media.length, 0); + assert_false(sheet3.disabled); + assert_equals(sheet3.cssRules.length, 0); + + sheet3.insertRule(redStyleTexts[1]); + assert_equals(sheet3.cssRules.length, 1); + assert_equals(sheet3.cssRules[0].cssText, redStyleTexts[1]); + + sheet3.deleteRule(0); + assert_equals(sheet3.cssRules.length, 0); +}, 'new CSSStyleSheet produces empty CSSStyleSheet'); + +test(() => { + const sheet = new CSSStyleSheet({title: "something"}); + assert_equals(sheet.title, null, "title and alternate are not supported by the constructor. https://github.com/WICG/construct-stylesheets/issues/105"); +}, "title can be set in the CSSStyleSheet constructor"); + +promise_test(() => { + const sheet = new CSSStyleSheet({disabled: true, media: "screen, print"}); + const promise_sheet = sheet.replace(redStyleTexts[0]); + return promise_sheet.then(function(sheet) { + assert_equals(sheet.title, null, "The title attribute must return the title or null if title is the empty string"); + assert_equals(sheet.ownerNode, null); + assert_equals(sheet.ownerRule, null); + assert_equals(sheet.media.length, 2); + assert_equals(sheet.media.item(0), "screen"); + assert_equals(sheet.media.item(1), "print"); + assert_true(sheet.disabled); + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]); + + sheet.insertRule(redStyleTexts[1]); + assert_equals(sheet.cssRules.length, 2); + assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]); + }); +}, 'CSSStyleSheet.replace produces Promise<CSSStyleSheet>'); + +function createAllSheetsPromise() { + const greenSheet = new CSSStyleSheet(); + const redSheet = new CSSStyleSheet({media: "screen, print"}); + const blueSheet = new CSSStyleSheet({disabled: true}); + const whiteSheet = new CSSStyleSheet({disabled: true}); + const yellowSheet = new CSSStyleSheet({disabled: false}); + + const greenPromise = greenSheet.replace(greenStyleText); + const redPromise = redSheet.replace(redStyleTexts[0] + redStyleTexts[1]); + const bluePromise = blueSheet.replace(blueStyleTexts[0] + blueStyleTexts[1]); + const whitePromise = whiteSheet.replace(whiteStyleText); + const yellowPromise = yellowSheet.replace(yellowStyleText); + return [greenPromise, redPromise, bluePromise, whitePromise, yellowPromise]; +} + +promise_test(() => { + return Promise.all(createAllSheetsPromise()).then(values => { + const greenStyleSheet = values[0]; + const redStyleSheet = values[1]; + const blueStyleSheet = values[2]; + const whiteStyleSheet = values[3]; + const yellowStyleSheet = values[4]; + + // Lists of style sheets can be created, assigned and read. + + // disabled stylesheets aren't applied + document.adoptedStyleSheets = [whiteStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + // disable dsheets don't block other styles from applying + document.adoptedStyleSheets = [greenStyleSheet, blueStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 128, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + document.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet]; + + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(255, 255, 0)"); + + document.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet, greenStyleSheet, blueStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 128, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(255, 255, 0)"); + document.adoptedStyleSheets = []; + }); +}, 'Constructed style sheets can be applied on document'); + +promise_test(() => { + return Promise.all(createAllSheetsPromise()).then(values => { + const greenStyleSheet = values[0]; + const redStyleSheet = values[1]; + const blueStyleSheet = values[2]; + const whiteStyleSheet = values[3]; + const yellowStyleSheet = values[4]; + shadowRoot.adoptedStyleSheets = [whiteStyleSheet]; + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)"); + + shadowRoot.adoptedStyleSheets = [greenStyleSheet, blueStyleSheet]; + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)"); + + shadowRoot.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet]; + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(255, 255, 0)"); + + shadowRoot.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet, greenStyleSheet, blueStyleSheet]; + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(255, 255, 0)"); + }); +}, 'Constructed style sheets can be applied on shadow root'); + +promise_test(() => { + return Promise.all(createAllSheetsPromise()).then(values => { + const greenStyleSheet = values[0]; + const redStyleSheet = values[1]; + shadowRoot.adoptedStyleSheets = [greenStyleSheet]; + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)", "Style applies connected"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(0, 0, 0)", "Style applies when connected"); + let hostParent = shadowHost.parentNode; + hostParent.removeChild(shadowHost); + assert_equals(getComputedStyle(greenShadowSpan).color, "", "Style doesn't apply when detached"); + assert_equals(getComputedStyle(redShadowSpan).color, "", "Style doesn't apply when detached"); + shadowRoot.adoptedStyleSheets = [redStyleSheet, greenStyleSheet]; + hostParent.appendChild(shadowHost); + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)", "Style applies after reattach"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)", "Style applies after reattach"); + }); +}, 'Re-attaching shadow host with adopted stylesheets work'); + +test(() => { + const sheet = new CSSStyleSheet(); + sheet.replaceSync(":host { color: red; }"); + const host = document.createElement("div"); + let sr = host.attachShadow({mode: "open"}); + sr.adoptedStyleSheets = [sheet]; + document.body.appendChild(host); + assert_equals(getComputedStyle(host).color, "rgb(255, 0, 0)", "Style applies when connected"); + sheet.replaceSync(":host { color: blue; }"); + assert_equals(getComputedStyle(host).color, "rgb(0, 0, 255)", "Style update applies when connected"); +}, 'Attaching a shadow root that already has adopted stylesheets work'); + +test(() => { + const sheet = new CSSStyleSheet(); + sheet.replaceSync(":host([red]) { color: red; } :host(.blue) { color: blue; }"); + const host = document.createElement("div"); + host.toggleAttribute("red"); + document.body.appendChild(host); + assert_equals(getComputedStyle(host).color, "rgb(0, 0, 0)", "No style applies yet"); + + let sr = host.attachShadow({mode: "open"}); + sr.adoptedStyleSheets = [sheet]; + + assert_equals(getComputedStyle(host).color, "rgb(255, 0, 0)", "Style applies after adding style"); + document.body.removeChild(host); + document.body.appendChild(host); + assert_equals(getComputedStyle(host).color, "rgb(255, 0, 0)", "Style applies after reattachment"); + host.toggleAttribute("red"); + assert_equals(getComputedStyle(host).color, "rgb(0, 0, 0)", "Attribute updates to the element after reattachment apply"); + host.classList.toggle("blue"); + assert_equals(getComputedStyle(host).color, "rgb(0, 0, 255)", "Class updates to the element after reattachment apply"); + +}, "Re-attaching shadow host and updating attributes work"); + +promise_test(() => { + const plainSheet = new CSSStyleSheet(); + const redStyleSheetPromise = plainSheet.replace(redStyleTexts[0]); + return redStyleSheetPromise.then(function(redStyleSheet) { + document.adoptedStyleSheets = [redStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + redStyleSheet.insertRule(redStyleTexts[1]); + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + redStyleSheet.deleteRule(1); + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + redStyleSheet.cssRules[0].style.color = "white"; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 255, 255)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); +}); +}, 'Changes to constructed stylesheets through CSSOM is reflected'); + +promise_test(() => { + const plainSheet = new CSSStyleSheet(); + const redStyleSheetPromise = plainSheet.replace(redStyleTexts[0]); + return redStyleSheetPromise.then(function(redStyleSheet) { + document.adoptedStyleSheets = [redStyleSheet]; + shadowRoot.adoptedStyleSheets = [redStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)"); + + shadowRoot.adoptedStyleSheets[0].insertRule(redStyleTexts[1]); + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)"); + document.adoptedStyleSheets = []; + }); +}, 'Constructed stylesheet can be used and modified in multiple TreeScopes'); + +promise_test(() => { + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + const thirdDiv = firstDiv.cloneNode(true); + iframe.contentDocument.body.appendChild(thirdDiv); + const greenIframeSpan = thirdDiv.children[0]; + const redIframeSpan = thirdDiv.children[1]; + const blueIframeSpan = thirdDiv.children[2]; + const whiteIframeSpan = thirdDiv.children[3]; + const yellowIframeSpan = thirdDiv.children[4]; + + const plainSheet = new CSSStyleSheet(); + const redStyleSheetPromise = plainSheet.replace(redStyleTexts[0]); + return redStyleSheetPromise.then(function(redStyleSheet) { + assert_throws_dom( + 'NotAllowedError', + iframe.contentWindow.DOMException, + () => { iframe.contentDocument.adoptedStyleSheets = [redStyleSheet]; } + ); + assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)"); + + document.adoptedStyleSheets = [redStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + document.adoptedStyleSheets[0].insertRule(redStyleTexts[1]); + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + }); +}, 'Stylesheets constructed on the main Document cannot be used in iframes'); + +promise_test(async () => { + const iframe = document.createElement("iframe"); + const iframeLoaded = new Promise(resolve => iframe.addEventListener("load", resolve)); + document.body.appendChild(iframe); + await iframeLoaded; + const thirdDiv = firstDiv.cloneNode(true); + iframe.contentDocument.body.appendChild(thirdDiv); + const greenIframeSpan = thirdDiv.children[0]; + const redIframeSpan = thirdDiv.children[1]; + const blueIframeSpan = thirdDiv.children[2]; + const whiteIframeSpan = thirdDiv.children[3]; + const yellowIframeSpan = thirdDiv.children[4]; + + // Make sure both the main Document and the iframe are not styled + const emptyStyleSheet = new CSSStyleSheet(); + document.adoptedStyleSheets = [emptyStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)"); + + const iframePlainSheet = new iframe.contentWindow.CSSStyleSheet(); + const iframeRedStyleSheetPromise = iframePlainSheet.replace(redStyleTexts[0]); + return iframeRedStyleSheetPromise.then(function(iframeRedStyleSheet) { + assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets = [iframeRedStyleSheet]; }); + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + iframe.contentDocument.adoptedStyleSheets = [iframeRedStyleSheet]; + assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redIframeSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)"); + + iframe.contentDocument.adoptedStyleSheets[0].insertRule(redStyleTexts[1]); + assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redIframeSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)"); + }); +}, 'Stylesheet constructed on iframe cannot be used in the main Document'); +</script> + +<div id="divNonConstructed" class="nonConstructed"> +</div> + +<script> +`use strict`; +const shadowRootNonConstructed = divNonConstructed.attachShadow({mode:'open'}) +nonConstructedStyle = document.createElement("style"); +shadowRootNonConstructed.appendChild(nonConstructedStyle); +nonConstructedStyle.sheet.insertRule(".nonConstructed { color: red; }", 0); +const nonConstructedStyleSheet = nonConstructedStyle.sheet; + +test(() => { + assert_equals(getComputedStyle(divNonConstructed).color, "rgb(0, 0, 0)"); + assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets = [nonConstructedStyleSheet]; }); +}, 'Adding non-constructed stylesheet to AdoptedStyleSheets is not allowed when the owner document of the stylesheet is in the same document tree as the AdoptedStyleSheets'); + +test(() => { + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + iframeDiv = iframe.contentDocument.createElement("div"); + iframeDiv.classList.add("nonConstructed"); + iframe.contentDocument.body.appendChild(iframeDiv); + + assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)"); + assert_throws_dom('NotAllowedError', iframe.contentWindow.DOMException, () => { + iframe.contentDocument.adoptedStyleSheets = [nonConstructedStyleSheet]; + }); + assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)"); + + iframeStyle = iframe.contentDocument.createElement("style"); + iframe.contentDocument.body.appendChild(iframeStyle); + iframeStyle.sheet.insertRule(".nonConstructedSpan { color: red; }"); + const iframeStyleSheet = iframeStyle.sheet; + nonConstructedSpan = document.createElement("span"); + nonConstructedSpan.classList.add(".nonConstructedSpan"); + divNonConstructed.appendChild(nonConstructedSpan); + + assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)"); + assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets = [iframeStyleSheet]; }); + assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)"); +}, 'Adding non-constructed stylesheet to AdoptedStyleSheets is not allowed when the owner document of the stylesheet and the AdoptedStyleSheets are in different document trees'); + +function attachShadowDiv(host) { + const shadowRoot = host.attachShadow({mode: 'open'}); + const shadowDiv = document.createElement("div"); + shadowRoot.appendChild(shadowDiv); + return shadowDiv; +} + +test(() => { + const sheet = new CSSStyleSheet(); + assert_equals(sheet.cssRules.length, 0); + + sheet.replaceSync(redStyleTexts[0]) + assert_equals(sheet.cssRules.length, 1); + assert_equals(redStyleTexts[0], sheet.cssRules[0].cssText); + + sheet.replaceSync(redStyleTexts[1]); + assert_equals(sheet.cssRules.length, 1); + assert_equals(redStyleTexts[1], sheet.cssRules[0].cssText); +}, 'CSSStyleSheet.replaceSync replaces stylesheet text synchronously'); + +test(() => { + // Attach a div inside a shadow root with the class ".red". + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + shadowDiv.classList.add("red"); + // Create empty stylesheet. + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + // Replace the stylesheet text that will color it red. + sheet.replaceSync(redStyleTexts[0]); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)"); + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]); + sheet.insertRule(redStyleTexts[1]); + assert_equals(sheet.cssRules.length, 2); + assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]); +}, 'CSSStyleSheet.replaceSync correctly updates the style of its adopters synchronously'); + +test(() => { + // Attach a div inside a shadow root with the class ".red". + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + shadowDiv.classList.add("target"); + + // Create empty stylesheet. + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + + // Replace the stylesheet text that will color it red. + sheet.replaceSync(".target { color: red; }"); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)"); + + // Create a style element that will set colors to white. + const style = document.createElement("style"); + style.textContent = ".target { color: white; }"; + span.shadowRoot.appendChild(style) + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)", "non-adopted styles should be ordered before adopted styles"); + + span.shadowRoot.adoptedStyleSheets = []; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 255, 255)", "with no adopted styles in conflict, the non-adopted style should take effect"); + + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)", "the adopted style should be ordered after the non-adopted style"); + + sheet.disabled = true; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 255, 255)", "with the adopted sheet disabled, the non-adopted style should take effect"); + + sheet.disabled = false; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)", "the adopted sheet re-enabled, it should take effect again"); +}, 'Adopted sheets are ordered after non-adopted sheets in the shadow root') + +test(() => { + // Attach a div inside a shadow root with the class ".red". + const span = document.createElement("span"); + thirdSection.appendChild(span); + span.classList.add("target"); + + // Create empty stylesheet. + const sheet = new CSSStyleSheet(); + document.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(span).color, "rgb(0, 0, 0)"); + + // Replace the stylesheet text that will color it red. + sheet.replaceSync(".target { color: red; }"); + assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)"); + + // Create a style element that will set colors to white. + const style = document.createElement("style"); + style.textContent = ".target { color: white; }"; + span.appendChild(style) + assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)", "non-adopted styles should be ordered before adopted styles"); + + document.adoptedStyleSheets = []; + assert_equals(getComputedStyle(span).color, "rgb(255, 255, 255)", "with no adopted styles in conflict, the non-adopted style should take effect"); + + document.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)", "the adopted style should be ordered after the non-adopted style"); + + sheet.disabled = true; + assert_equals(getComputedStyle(span).color, "rgb(255, 255, 255)", "with the adopted sheet disabled, the non-adopted style should take effect"); + + sheet.disabled = false; + assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)", "the adopted sheet re-enabled, it should take effect again") +}, 'Adopted sheets are ordered after non-adopted sheets in the document') + +const import_text = '@import url("support/constructable-import.css");'; + +test(() => { + assert_throws_dom("SyntaxError", () => { (new CSSStyleSheet).insertRule(import_text) }); +}, 'Inserting an @import rule through insertRule on a constructed stylesheet throws an exception'); + +promise_test(t => { + const importUrl = "support/constructable-import.css"; + const sheet = new CSSStyleSheet(); + + sheet.replaceSync(`@import url("${importUrl}");`); + + const timeAfterReplaceSync = performance.now(); + let link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = importUrl; + + return new Promise(resolve => { + link.addEventListener("error", t.unreached_func("Load shouldn't fail")); + link.addEventListener("load", t.step_func(() => { + let entries = window.performance.getEntriesByType('resource').filter(entry => entry.name.includes(importUrl)); + assert_equals(entries.length, 1, "There should be only one entry for the import URL"); + assert_greater_than_equal(entries[0].startTime, timeAfterReplaceSync, "The entry's start time should be after replaceSync threw"); + link.remove(); + resolve(); + })); + document.body.appendChild(link); + }); +}, "CSSStyleSheet.replaceSync should not trigger any loads from @import rules") + +promise_test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + // Replace and assert that the imported rule is NOT applied. + const sheet_promise = sheet.replace(import_text); + return sheet_promise.then((sheet) => { + // replace() ignores @import rules: + assert_equals(sheet.cssRules.length, 0); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + }).catch((reason) => { + assert_unreached(`Promise was rejected (${reason}) when it should have been resolved`); + }); +}, 'CSSStyleSheet.replace allows, but ignores, import rule inside'); + +promise_test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + const targetSpan = document.createElement("span"); + targetSpan.classList.add("target"); + shadowDiv.appendChild(targetSpan); + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + // Replace and assert that the imported rule is NOT applied, but regular rule does apply. + const sheet_promise = sheet.replace(import_text + ".target { color: blue; }"); + return sheet_promise.then((sheet) => { + assert_equals(sheet.cssRules.length, 1); + // @import not applied: + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + // regular rule applied: + assert_equals(getComputedStyle(targetSpan).color, "rgb(0, 0, 255)"); + }).catch((reason) => { + assert_unreached(`Promise was rejected (${reason}) when it should have been resolved`); + }); +}, 'CSSStyleSheet.replace ignores @import rule but still loads other rules'); + +test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + // Replace and assert that the imported rule is NOT applied. + try { + sheet.replaceSync(import_text); + // replaceSync() ignores @import rules: + assert_equals(sheet.cssRules.length, 0); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + } catch(reason) { + assert_unreached(`replaceSync threw an exception (${reason}) when it shouldn't have`); + } +}, 'CSSStyleSheet.replaceSync allows, but ignores, import rule inside'); + +promise_test(() => { + const sheet = new CSSStyleSheet(); + const sheet_promise = sheet.replace("@import url('not-there.css');"); + + return sheet_promise.then((sheet) => { + // No exception here + }).catch((reason) => { + assert_unreached("Promise was rejected"); + }); +}, 'CSSStyleSheet.replace does not reject on failed imports'); + +test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + + const newSpan = span.cloneNode(true); + assert_equals(newSpan.shadowRoot, null); +}, 'Cloning a shadow host will not clone shadow root, and also adoptedStyleSheets'); + +test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + const newSpan = iframe.contentDocument.importNode(span, true); + iframe.contentDocument.body.appendChild(newSpan); + assert_equals(newSpan.shadowRoot, null); +}, 'Importing a shadow host will not copy shadow root, and also adoptedStyleSheets'); + +test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + const sheet = new CSSStyleSheet(); + sheet.replaceSync("* { color: red; }"); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)"); + + document.adoptNode(span); + assert_equals(span.shadowRoot.adoptedStyleSheets.length, 1); + assert_equals(span.shadowRoot.adoptedStyleSheets[0], sheet); + + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + iframe.contentDocument.adoptNode(span); + iframe.contentDocument.body.appendChild(span); + assert_not_equals(span.shadowRoot, null); + assert_equals(span.shadowRoot.adoptedStyleSheets.length, 0); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); +}, 'Adopting a shadow host will empty adoptedStyleSheets if adopting to a different document'); + +test(() => { + const span = document.createElement("span"); + const div = document.createElement("div"); + thirdSection.appendChild(span); + span.appendChild(div); + const shadowDiv = attachShadowDiv(div); + const sheet = new CSSStyleSheet(); + sheet.replaceSync("* { color: red; }"); + div.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)"); + + document.adoptNode(span); + assert_equals(div.shadowRoot.adoptedStyleSheets.length, 1); + assert_equals(div.shadowRoot.adoptedStyleSheets[0], sheet); + + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + iframe.contentDocument.adoptNode(span); + iframe.contentDocument.body.appendChild(span); + assert_not_equals(div.shadowRoot, null); + assert_equals(div.shadowRoot.adoptedStyleSheets.length, 0); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); +}, `Adopting a shadow host's ancestor will empty adoptedStyleSheets if adopting to a different document`); + +test(() => { + const host = document.createElement("div"); + const root = host.attachShadow({mode: "open"}); + root.adoptedStyleSheets = [new CSSStyleSheet()]; + document.body.offsetTop; +}, 'Forcing a style update after adding an adopted stylesheet on a disconnected shadow root should not crash.'); + +test(() => { + const host = document.createElement("div"); + thirdSection.appendChild(host); + const root = host.attachShadow({mode: "open"}); + const sheet = new CSSStyleSheet(); + root.adoptedStyleSheets = [sheet]; + host.remove(); + sheet.replaceSync(''); +}, 'Modifying an adopted stylesheet on a disconnected shadow root should not crash.'); + +function currentLocation() { + const sections = location.href.split("/") + sections.pop(); + return sections.join("/"); +} + +test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + + const fileName = "example.png" + const fullPath = `${currentLocation()}/${fileName}` + + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + + sheet.replaceSync(`* { background-image: url("${fileName}"); }`); + const styleFromRelative = getComputedStyle(shadowDiv).backgroundImage; + + sheet.replaceSync(`* { background-image: url("${fullPath}"); }`); + const styleFromFull = getComputedStyle(shadowDiv).backgroundImage; + + assert_equals(styleFromRelative, styleFromFull); +}, "Constructing a sheet with the default base URL uses the constructor document's base URL for CSS rules"); + +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-modify-after-removal.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-modify-after-removal.html new file mode 100644 index 0000000000..010c0b9328 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-modify-after-removal.html @@ -0,0 +1,29 @@ +<!doctype html> +<title>CSS Test: CSSStyleSheet modifications after removal</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstylesheet-interface"> +<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssrule-interface"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="frm"></iframe> +<iframe id="frm2"></iframe> +<script> + test(() => { + frm.contentDocument.body.innerHTML = "<style>div {color:red}</style>"; + let sheet = frm.contentDocument.querySelector("style").sheet; + assert_equals(sheet.cssRules.length, 1); + frm.remove(); + document.body.offsetTop; + sheet.insertRule("span {color: green}", 0); + assert_equals(sheet.cssRules.length, 2); + }, "Modify sheet from removed iframe"); + + test(() => { + frm2.contentWindow.eval("let sheet = new CSSStyleSheet(); document.adoptedStyleSheets = [ sheet ];"); + let sheet = frm2.contentDocument.adoptedStyleSheets[0]; + assert_equals(sheet.cssRules.length, 0); + frm2.remove(); + document.body.offsetTop; + sheet.insertRule("span {color: green}", 0); + assert_equals(sheet.cssRules.length, 1); + }, "Modify constructed sheet from removed iframe"); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-template-adoption.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-template-adoption.html new file mode 100644 index 0000000000..e014627ed8 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-template-adoption.html @@ -0,0 +1,58 @@ +<!doctype html> +<title>adoptedStyleSheets should stay when adopting to/from template document</title> +<link rel="author" title="Rakina Zata Amni" href="mailto:rakina@chromium.org"> +<link rel="help" href="https://wicg.github.io/construct-stylesheets/"> +<script src = '/resources/testharness.js'></script> +<script src = '/resources/testharnessreport.js'></script> +<body> +<div id="host"></div> +<template id='template'> + <template id='nested_template'></template> +</template> +</body> +<script> +test(function() { + let sheet = new CSSStyleSheet(); + sheet.replaceSync("div { color: blue }"); + + let host = document.getElementById("host"); + let root = host.attachShadow({ mode: "open" }); + root.innerHTML = `<div></div>`; + root.adoptedStyleSheets = [sheet]; + + function assertAdoptedStyleSheet() { + assert_equals(host.ownerDocument, root.firstChild.ownerDocument, "Shadow root was not adopted?"); + assert_equals(root.adoptedStyleSheets.length, 1); + assert_equals(root.adoptedStyleSheets[0], sheet); + if (root.ownerDocument == document) { + assert_equals(getComputedStyle(root.firstChild).color, "rgb(0, 0, 255)", "Sheet should apply"); + } + } + + assertAdoptedStyleSheet(); + + // adoptedStyleSheets is not cleared when adopted into a <template>. + const template = document.getElementById("template"); + template.content.appendChild(host); + + assert_not_equals(host.ownerDocument, document, "Should've been adopted"); + assertAdoptedStyleSheet(); + + // adoptedStyleSheets is not cleared when adopted back in the main document. + document.body.appendChild(host); + assert_equals(host.ownerDocument, document, "Should've been re-adopted"); + assertAdoptedStyleSheet(); + + // adoptedStyleSheets is not cleared when adopted into a nested <template>. + const nested_template = template.content.firstElementChild; + nested_template.content.appendChild(host); + assert_not_equals(host.ownerDocument, document, "Should've been adopted"); + assertAdoptedStyleSheet(); + + // adoptedStyleSheets is cleared when adopted into an <iframe>. + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + iframe.contentDocument.body.appendChild(host); + assert_equals(root.adoptedStyleSheets.length, 0); +}, "adoptedStyleSheets won'te be cleared when adopting into/from <template>s"); +</script> diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet.html new file mode 100644 index 0000000000..c744382b55 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet.html @@ -0,0 +1,132 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>CSSOM - CSSStyleSheet interface</title> + <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstylesheet-interface"> + <link rel="help" href="https://drafts.csswg.org/cssom/#legacy-css-style-sheet-members"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style id="my-stylesheet"> + body { width: 50%; } + #foo { height: 100px; } + </style> + <style id="empty-stylesheet"></style> + + <script> + var styleSheet, emptyStyleSheet; + + setup(function() { + styleSheet = document.styleSheets[0]; + styleSheet.cssRules[0].randomProperty = 1; + styleSheet.cssRules[1].randomProperty = 2; + emptyStyleSheet = document.styleSheets[1]; + }); + + test(function() { + assert_equals(styleSheet, document.getElementById("my-stylesheet").sheet, "CSSStyleSheet and LinkStyle's sheet attribute"); + assert_equals(styleSheet.cssRules.length, 2, "CSSStyleSheet cssRules attribute"); + assert_equals(styleSheet.cssRules[0].cssText, "body { width: 50%; }", "CSSStyleSheet cssRules attribute"); + assert_equals(styleSheet.cssRules[1].cssText, "#foo { height: 100px; }", "CSSStyleSheet cssRules attribute"); + assert_equals(styleSheet.cssRules[2], undefined, "CSSStyleSheet cssRules attribute"); + + assert_equals(emptyStyleSheet, document.getElementById("empty-stylesheet").sheet, "CSSStyleSheet and LinkStyle's sheet attribute"); + assert_equals(emptyStyleSheet.cssRules.length, 0, "CSSStyleSheet cssRules attribute"); + }, "preconditions"); + + test(function() { + styleSheet.insertRule("#bar { margin: 10px; }", 1); + assert_equals(styleSheet.cssRules.length, 3, "CSSStyleSheet cssRules attribute after insertRule function"); + assert_equals(styleSheet.cssRules[0].cssText, "body { width: 50%; }", "CSSStyleSheet cssRules attribute"); + assert_equals(styleSheet.cssRules[1].cssText, "#bar { margin: 10px; }", "CSSStyleSheet cssRules attribute after insertRule function"); + assert_equals(styleSheet.cssRules[2].cssText, "#foo { height: 100px; }", "CSSStyleSheet cssRules attribute after insertRule function"); + assert_equals(styleSheet.cssRules[0].randomProperty, 1, "[SameObject] cssRules attribute after insertRule function"); + assert_equals(styleSheet.cssRules[2].randomProperty, 2, "[SameObject] cssRules attribute after insertRule function"); + }, 'insertRule with #bar selector'); + + test(function() { + assert_throws_js(TypeError, function() { styleSheet.insertRule() }); + }, 'insertRule with no argument throws'); + + test(function() { + assert_throws_dom("IndexSizeError", function() { + styleSheet.insertRule("#bar { margin: 10px; }", styleSheet.cssRules.length + 1) + }); + }, 'insertRule with index greater than length throws'); + + test(function() { + styleSheet.deleteRule(1); + assert_equals(styleSheet.cssRules.length, 2, "CSSStyleSheet cssRules attribute after deleteRule function"); + assert_equals(styleSheet.cssRules[0].cssText, "body { width: 50%; }", "CSSStyleSheet cssRules attribute after deleteRule function"); + assert_equals(styleSheet.cssRules[1].cssText, "#foo { height: 100px; }", "CSSStyleSheet cssRules attribute after deleteRule function"); + assert_equals(styleSheet.cssRules[2], undefined, "CSSStyleSheet cssRules attribute after deleteRule function"); + assert_equals(styleSheet.cssRules[0].randomProperty, 1, "[SameObject] cssRules attribute after deleteRule function"); + assert_equals(styleSheet.cssRules[1].randomProperty, 2, "[SameObject] cssRules attribute after deleteRule function"); + }, 'deleteRule(1)'); + + test(function() { + assert_throws_js(TypeError, function() { styleSheet.deleteRule() }); + }, 'deleteRule with no argument throws'); + + test(function() { + assert_throws_dom("IndexSizeError", function() { emptyStyleSheet.deleteRule(0) }); + }, 'deleteRule on empty style sheet throws'); + + test(function() { + styleSheet.removeRule(); + assert_equals(styleSheet.cssRules.length, 1, "CSSStyleSheet cssRules attribute after removeRule function"); + assert_equals(styleSheet.cssRules[0].cssText, "#foo { height: 100px; }", "CSSStyleSheet cssRules attribute after removeRule function"); + }, 'removeRule with no argument removes first rule'); + + test(function() { + assert_throws_dom("IndexSizeError", function() { emptyStyleSheet.removeRule(0) }); + }, 'removeRule on empty style sheet throws'); + + test(function() { + assert_equals(styleSheet.addRule("@media all", "#foo { color: red }"), -1); + assert_equals(styleSheet.cssRules.length, 2, "CSSStyleSheet cssRules attribute after addRule function"); + assert_true(styleSheet.cssRules[1] instanceof CSSMediaRule, "CSSStyleSheet addRule does some silly string concatenation"); + }, 'addRule with @media rule'); + + test(function() { + styleSheet.removeRule(1); + assert_equals(styleSheet.cssRules.length, 1, "CSSStyleSheet cssRules attribute after removeRule function with index"); + assert_equals(styleSheet.cssRules[0].cssText, "#foo { height: 100px; }", "CSSStyleSheet cssRules attribute after deleteRule function with index"); + }, 'removeRule(1)'); + + test(function() { + assert_equals(styleSheet.addRule("#foo", "color: red"), -1); + assert_equals(styleSheet.cssRules.length, 2, "CSSStyleSheet cssRules attribute after addRule function with simple selector"); + assert_equals(styleSheet.cssRules[1].cssText, "#foo { color: red; }", "CSSStyleSheet cssRules attribute after addRule function without index appends to the end"); + + assert_equals(styleSheet.addRule("#foo", "color: blue", 0), -1); + assert_equals(styleSheet.cssRules.length, 3, "CSSStyleSheet cssRules attribute after addRule function with simple selector with index"); + assert_equals(styleSheet.cssRules[0].cssText, "#foo { color: blue; }", "addRule function with index performs an insertion"); + }, 'addRule with #foo selectors'); + + test(function() { + assert_equals(styleSheet.addRule(), -1); + assert_equals(styleSheet.cssRules.length, 4, "CSSStyleSheet cssRules attribute after addRule function without arguments"); + assert_equals(styleSheet.cssRules[3].cssText, "undefined { }", "addRule arguments default to undefined"); + }, 'addRule with no argument adds "undefined" selector'); + + test(function() { + assert_throws_dom("IndexSizeError", function() { + styleSheet.addRule("#foo", "color: red", styleSheet.cssRules.length + 1); + }); + }, 'addRule with index greater than length throws'); + + test(function() { + assert_equals(styleSheet.cssRules, styleSheet.rules); + }, "cssRules and rules are the same object"); + + test(function() { + assert_equals(styleSheet.cssRules, styleSheet.cssRules); + }, "cssRules returns the same object twice"); + + test(function() { + assert_equals(styleSheet.rules, styleSheet.rules); + }, "rules returns the same object twice"); + </script> +</head> +</html> diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-001.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-001.html new file mode 100644 index 0000000000..f2c5d0a545 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-001.html @@ -0,0 +1,44 @@ +<!doctype html> +<title><link disabled>, HTMLLinkElement.disabled and CSSStyleSheet.disabled interactions</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link title="alt" rel="stylesheet" disabled href="data:text/css,html { background: green }"> +<script> +function assert_applies(applies) { + (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)"); +} + +const link = document.querySelector("link[disabled]"); + +test(function() { + assert_equals(document.styleSheets.length, 0); + assert_applies(false); +}, "<link disabled> prevents the stylesheet from being in document.styleSheets (from parser)"); + +async_test(function(t) { + assert_true(link.disabled); + + link.onload = t.step_func_done(function() { + assert_equals(document.styleSheets.length, 1); + let sheet = document.styleSheets[0]; + assert_equals(sheet.ownerNode, link); + assert_applies(true); + + link.disabled = true; + assert_equals(sheet.ownerNode, null); + assert_false(sheet.disabled); + assert_applies(false); + assert_true(link.hasAttribute("disabled")); + + assert_equals(document.styleSheets.length, 0); + assert_applies(false); + }); + + link.disabled = false; + assert_true(!link.hasAttribute("disabled")); + assert_false(link.disabled); +}, "HTMLLinkElement.disabled reflects the <link disabled> attribute, and behaves consistently"); +</script> diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-002.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-002.html new file mode 100644 index 0000000000..34bd8182fe --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-002.html @@ -0,0 +1,37 @@ +<!doctype html> +<title><link disabled>, HTMLLinkElement.disabled and CSSStyleSheet.disabled interactions (alternate)</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link title="alt" rel="alternate stylesheet" disabled href="data:text/css,html { background: green }"> +<script> +function assert_applies(applies) { + (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)"); +} + +const link = document.querySelector("link[disabled]"); + +async_test(function(t) { + assert_true(link.disabled); + + link.onload = t.step_func_done(function() { + assert_equals(document.styleSheets.length, 1); + let sheet = document.styleSheets[0]; + assert_equals(sheet.ownerNode, link); + assert_applies(true); + + link.disabled = true; + assert_equals(sheet.ownerNode, null); + assert_false(sheet.disabled); + assert_applies(false); + assert_true(link.hasAttribute("disabled")); + assert_equals(document.styleSheets.length, 0); + }); + + link.disabled = false; + assert_true(!link.hasAttribute("disabled")); + assert_false(link.disabled); +}, "HTMLLinkElement.disabled reflects the <link disabled> attribute, and behaves consistently, when the sheet is an alternate"); +</script> diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-003.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-003.html new file mode 100644 index 0000000000..3d391bbaff --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-003.html @@ -0,0 +1,31 @@ +<!doctype html> +<title><link disabled>'s "explicitly enabled" state persists after getting disconnected from the tree</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link title="alt" rel="alternate stylesheet" disabled href="data:text/css,html { background: green }"> +<script> +function assert_applies(applies) { + (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)"); +} + +const link = document.querySelector("link[disabled]"); +async_test(function(t) { + assert_true(link.disabled); + link.disabled = false; + assert_false(link.disabled); + assert_true(!link.hasAttribute("disabled")); + link.remove(); + + link.onload = t.step_func_done(function() { + assert_equals(document.styleSheets.length, 1); + let sheet = document.styleSheets[0]; + assert_equals(sheet.ownerNode, link); + assert_applies(true); + }); + + document.head.appendChild(link); +}, "HTMLLinkElement.disabled's explicitly enabled state persists when disconnected and connected again"); +</script> diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-004.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-004.html new file mode 100644 index 0000000000..f163f1d68e --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-004.html @@ -0,0 +1,39 @@ +<!doctype html> +<title><link disabled>'s "explicitly enabled" state doesn't persist for clones</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link title="alt" rel="alternate stylesheet" disabled href="data:text/css,html { background: green }"> +<script> +function assert_applies(applies) { + (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)"); +} + +const link = document.querySelector("link[disabled]"); + +async_test(function(t) { + link.remove(); + link.disabled = false; // `link` is explicitly enabled. + + let clonesLoaded = 0; + + for (let shallow of [true, false]) { + const clone = link.cloneNode(shallow); + clone.onload = t.step_func(function() { + assert_false(link.disabled); + // Even though it's not disabled, it still doesn't apply, since it's an alternate. + assert_applies(false); + if (++clonesLoaded == 2) { + link.onload = t.step_func_done(function() { + assert_false(link.disabled); + assert_applies(true); // `link` is still explicitly enabled. + }); + document.head.appendChild(link); + } + }); + document.head.appendChild(clone); + } +}, "HTMLLinkElement.disabled's explicitly enabled state doesn't persist on clones"); +</script> diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-005.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-005.html new file mode 100644 index 0000000000..76de206327 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-005.html @@ -0,0 +1,26 @@ +<!doctype html> +<title><link disabled>'s "explicitly enabled" persists across rel changes</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link title="alt" rel="yadayada" disabled href="data:text/css,html { background: green }"> +<script> +function assert_applies(applies) { + (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)"); +} + +const link = document.querySelector("link[disabled]"); + +async_test(function(t) { + link.onload = t.step_func_done(function() { + assert_applies(true); + link.setAttribute("rel", "alternate stylesheet"); + assert_applies(true); + assert_false(link.disabled); + }); + link.disabled = false; + link.setAttribute("rel", "stylesheet"); +}, "HTMLLinkElement.disabled's explicitly enabled state persists regardless of rel"); +</script> diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-006.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-006.html new file mode 100644 index 0000000000..02749a2e11 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-006.html @@ -0,0 +1,25 @@ +<!doctype html> +<title><link disabled>'s "explicitly enabled" state isn't magically set from the setter</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +function assert_applies(applies) { + (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)"); +} + +async_test(function(t) { + const link = document.createElement("link"); + link.setAttribute("rel", "alternate stylesheet"); + link.setAttribute("title", "alt"); + link.href = "data:text/css,html { background: green }"; + link.disabled = false; // This should do nothing, and is the point of this test. + link.onload = t.step_func_done(function() { + assert_applies(false); // Should not apply, since it's an alternate that hasn't been enabled. + assert_false(link.disabled); + }); + document.head.appendChild(link); +}, "HTMLLinkElement.disabled setter does nothing if the attribute isn't present already."); +</script> diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-007.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-007.html new file mode 100644 index 0000000000..9a695a964c --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-007.html @@ -0,0 +1,26 @@ +<!doctype html> +<title><link disabled>'s "explicitly enabled" state works when set explicitly back and forth</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +function assert_applies(applies) { + (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)"); +} + +async_test(function(t) { + const link = document.createElement("link"); + link.setAttribute("rel", "alternate stylesheet"); + link.setAttribute("title", "alt"); + link.href = "data:text/css,html { background: green }"; + link.disabled = true; + link.disabled = false; // This should make it "explicitly enabled". + link.onload = t.step_func_done(function() { + assert_applies(true); // Should apply, since it's explicitly enabled. + assert_false(link.disabled); + }); + document.head.appendChild(link); +}, "HTMLLinkElement.disabled setter sets the explicitly enabled state if toggled back and forth."); +</script> diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate-ref.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate-ref.html new file mode 100644 index 0000000000..5d87bfdaf5 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate-ref.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<title>CSS Test Reference</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<style> + html { background: green } +</style> diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate.html new file mode 100644 index 0000000000..f1457c0dcf --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<title>CSS Test: alternate stylesheets can be disabled by HTMLLinkElement.disabled if they have the disabled attribute already</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled"> +<link rel="match" href="HTMLLinkElement-disabled-alternate-ref.html"> +<link title="alt" rel="alternate stylesheet" href="data:text/css,html { background: green }" disabled onload="document.documentElement.className = ''"> +<script> + onload = function() { + const link = document.querySelector("link[rel='alternate stylesheet']"); + link.disabled = false; + } +</script> diff --git a/testing/web-platform/tests/css/cssom/META.yml b/testing/web-platform/tests/css/cssom/META.yml new file mode 100644 index 0000000000..3250801f64 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/META.yml @@ -0,0 +1,5 @@ +spec: https://drafts.csswg.org/cssom/ +suggested_reviewers: + - dbaron + - plinss + - lilles diff --git a/testing/web-platform/tests/css/cssom/MediaList.html b/testing/web-platform/tests/css/cssom/MediaList.html new file mode 100644 index 0000000000..8308ee89e4 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/MediaList.html @@ -0,0 +1,55 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>CSSOM - MediaList interface</title> + <link rel="help" href="https://drafts.csswg.org/cssom/#the-medialist-interface"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style> + @media screen and (min-width: 480px), print, projection {} + </style> + + <script> + test(function () { + var media = document.styleSheets[0].cssRules[0].media; + assert_equals(media.length, 3, "MediaList length attribute"); + assert_equals(media.mediaText, "screen and (min-width: 480px), print, projection", "MediaList mediaText attribute"); + assert_equals(media.toString(), "screen and (min-width: 480px), print, projection", "MediaList toString method"); + assert_equals(media[0], "screen and (min-width: 480px)", "MediaList indexed getter"); + assert_equals(media[1], "print", "MediaList indexed getter"); + assert_equals(media[2], "projection", "MediaList indexed getter"); + assert_equals(media[3], undefined, "MediaList indexed getter with out of range"); + assert_equals(media.item(0), "screen and (min-width: 480px)", "MediaList item method"); + assert_equals(media.item(3), null, "MediaList item method"); + + media.deleteMedium("print"); + assert_equals(media.length, 2, "MediaList length attribute after delete method"); + assert_equals(media.mediaText, "screen and (min-width: 480px), projection", "MediaList mediaText attribute after delete method"); + assert_equals(media.toString(), "screen and (min-width: 480px), projection", "MediaList toString method after delete method"); + assert_equals(media[1], "projection", "MediaList indexed getter after delete method"); + assert_equals(media[2], undefined, "MediaList indexed getter with out of range after delete method"); + assert_equals(media.item(1), "projection", "MediaList indexed getter after delete method"); + assert_equals(media.item(2), null, "MediaList item method after delete method"); + + media.appendMedium("speech"); + assert_equals(media.length, 3, "MediaList length attribute after append method"); + assert_equals(media.mediaText, "screen and (min-width: 480px), projection, speech", "MediaList mediaText attribute after append method"); + assert_equals(media.toString(), "screen and (min-width: 480px), projection, speech", "MediaList toString method after append method"); + assert_equals(media[1], "projection", "MediaList indexed getter after append method"); + assert_equals(media[2], "speech", "MediaList indexed getter after append method"); + assert_equals(media[3], undefined, "MediaList indexed getter with out of range after append method"); + assert_equals(media.item(2), "speech", "MediaList item method after append method"); + assert_equals(media.item(3), null, "MediaList item method after append method"); + + media.mediaText = null; + assert_equals(media.mediaText, "", "MediaList mediaText attribute should be empty string in case of null"); + assert_equals(media.toString(), "", "MediaList toString method should be empty string in case of null"); + + var rule = document.styleSheets[0].cssRules[0]; + rule.media = "speech"; + assert_equals(rule.media.mediaText, "speech", "MediaList mediaText attribute should be updated"); + }); + </script> +</head> +</html> diff --git a/testing/web-platform/tests/css/cssom/MediaList2.xhtml b/testing/web-platform/tests/css/cssom/MediaList2.xhtml new file mode 100644 index 0000000000..73acbdec25 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/MediaList2.xhtml @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title>CSS Test: the MediaList interface</title> +<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com"/> +<link rel="help" href="http://www.w3.org/TR/cssom-1/#the-medialist-interface"/> +<link rel="help" href="http://dev.w3.org/2006/webapi/WebIDL/#getownproperty"/> +<style media="screen, print" id="test-style"></style> +<script src="/resources/testharness.js"/> +<script src="/resources/testharnessreport.js"/> +</head> +<body> +<div id="log"/> +<script> +test(function() { + var ss = document.styleSheets[0]; + assert_equals(ss.ownerNode.id, "test-style", "Got the wrong style element"); + + var media = ss.media; + test(function() { + // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17526 + assert_equals(media.mediaText, "screen, print", "Serialization should be \"screen, print\""); + }, "MediaList.mediaText"); + + test(function() { + assert_equals(media.length, 2, "Got wrong number of media"); + }, "MediaList.length"); + + test(function() { + assert_equals(media[-1], undefined, "media[-1] should return undefined"); + assert_equals(media[0], "screen", "media[0] should return \"screen\""); + assert_equals(media[1], "print", "media[1] should return \"print\""); + assert_equals(media[2], undefined, "media[2] should return undefined"); + }, "MediaList getter"); + + test(function() { + assert_equals(media.item(-1), null, "media.item(-1) should return null"); + assert_equals(media.item(0), "screen", "media.item(0) should return \"screen\""); + assert_equals(media.item(1), "print", "media.item(1) should return \"print\""); + assert_equals(media.item(2), null, "media.item(2) should return null"); + }, "MediaList.item"); +}, "MediaList"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/MutationObserver-style.html b/testing/web-platform/tests/css/cssom/MutationObserver-style.html new file mode 100644 index 0000000000..673822d11e --- /dev/null +++ b/testing/web-platform/tests/css/cssom/MutationObserver-style.html @@ -0,0 +1,43 @@ +<!doctype html> +<meta charset=utf-8> +<title>Observer notifications when updating styles</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<body> +<script> + "use strict"; + + promise_test(async () => { + let called = 0; + const el = document.createElement("div"); + document.body.appendChild(el); + const m = new MutationObserver(() => { + called++; + }); + m.observe(el, { attributes: true }); + el.style.height = "100px"; + await Promise.resolve(); + assert_equals(called, 1, "times callback called"); + el.style.height = "100px"; + await Promise.resolve(); + assert_equals(called, 1, "times callback called"); + }, "Updating style property with the same value does not trigger an observation callback"); + + promise_test(async () => { + let called = 0; + const el = document.createElement("div"); + document.body.appendChild(el); + const m = new MutationObserver(() => { + called++; + }); + m.observe(el, { attributes: true }); + el.style.cssText = "height:100px"; + await Promise.resolve(); + assert_equals(called, 1, "times callback called"); + el.style.cssText = "height:100px"; + await Promise.resolve(); + assert_equals(called, 2, "times callback called"); + }, "Updating cssText triggers an observation callback even if the value is the same"); +</script> + diff --git a/testing/web-platform/tests/css/cssom/StyleSheetList.html b/testing/web-platform/tests/css/cssom/StyleSheetList.html new file mode 100644 index 0000000000..bb7d2ff4e3 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/StyleSheetList.html @@ -0,0 +1,34 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>CSSOM - StyleSheetList interface</title> + <link rel="help" href="https://drafts.csswg.org/cssom/#the-stylesheetlist-interface"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style> + body { width: 50%; } + </style> + <style> + #foo { width: 10%; } + #bar { height: 100px; } + </style> + <script> + test(function () { + var styleSheets = document.styleSheets; + assert_equals(styleSheets.length, 2, "StyleSheetList length attribute"); + assert_equals(styleSheets[0].cssRules.length, 1, "StyleSheetList indexed getter length attribute"); + assert_equals(styleSheets[1].cssRules.length, 2, "StyleSheetList indexed getter length attribute"); + assert_equals(styleSheets[2], undefined, "StyleSheetList indexed getter length attribute"); + assert_equals(styleSheets.item(0).cssRules.length, 1, "StyleSheetList item function length attribute"); + assert_equals(styleSheets.item(1).cssRules.length, 2, "StyleSheetList item function length attribute"); + assert_equals(styleSheets.item(2), null, "StyleSheetList item function length attribute"); + + styleSheets[0].randomProperty = 1; + var style = document.createElement("style"); + document.head.appendChild(style); + assert_equals(styleSheets[0].randomProperty, 1, "[SameObject] StyleSheetList"); + }); + </script> +</head> +</html> diff --git a/testing/web-platform/tests/css/cssom/adoptedstylesheets-observablearray.html b/testing/web-platform/tests/css/cssom/adoptedstylesheets-observablearray.html new file mode 100644 index 0000000000..783a054163 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/adoptedstylesheets-observablearray.html @@ -0,0 +1,92 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Adoptedstylesheets as ObservableArray</title> +<link rel="author" href="mailto:masonf@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/cssom/#extensions-to-the-document-or-shadow-root-interface"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<span id=target>Test Span</span> +<div id="divNonConstructed" class="nonConstructed"></div> +<style> + #target {background-color: red;} +</style> + +<script> + +const shadowRootNonConstructed = divNonConstructed.attachShadow({mode:'open'}) +nonConstructedStyle = document.createElement("style"); +shadowRootNonConstructed.appendChild(nonConstructedStyle); +nonConstructedStyle.sheet.insertRule(".nonConstructed { color: red; }", 0); +const nonConstructedStyleSheet = nonConstructedStyle.sheet; + +function assert_is(targetStyle, color) { + assert_equals(targetStyle.getPropertyValue('background-color'), color); +} + +function testRoot(d, targetStyle) { + const red = 'rgb(255, 0, 0)'; + const green = 'rgb(0, 255, 0)'; + const blue = 'rgb(0, 0, 255)'; + + const sheet1 = new CSSStyleSheet(); + sheet1.replaceSync('#target {background-color:lime !important;}'); + const sheet2 = new CSSStyleSheet(); + sheet2.replaceSync('#target {background-color:blue !important;}'); + assert_equals(d.adoptedStyleSheets.length, 0); + assert_is(targetStyle, red); + + d.adoptedStyleSheets = [sheet1]; + assert_equals(d.adoptedStyleSheets.length, 1); + assert_is(targetStyle, green); + + d.adoptedStyleSheets.push(sheet2); + assert_equals(d.adoptedStyleSheets.length, 2); + assert_is(targetStyle, blue); + + d.adoptedStyleSheets.pop(); + assert_equals(d.adoptedStyleSheets.length, 1); + assert_is(targetStyle, green); + + d.adoptedStyleSheets.push(sheet2); + d.adoptedStyleSheets.reverse(); + assert_equals(d.adoptedStyleSheets.length, 2); + assert_is(targetStyle, green); + + d.adoptedStyleSheets.splice(1, 1); + assert_equals(d.adoptedStyleSheets.length, 1); + assert_is(targetStyle, blue); + d.adoptedStyleSheets.splice(0, 1, sheet1); + assert_equals(d.adoptedStyleSheets.length, 1); + assert_is(targetStyle, green); + + // Adding non-constructed stylesheet to AdoptedStyleSheets is not allowed. + assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets.push(nonConstructedStyleSheet); }); + + assert_throws_js(TypeError, () => { document.adoptedStyleSheets.push("foo"); }); +} + +test(function() { + const target = document.querySelector('#target'); + const targetStyle = window.getComputedStyle(target); + testRoot(document, targetStyle); +}, "document.adoptedStyleSheets should allow mutation in-place"); + +test(function() { + const host = document.createElement('div'); + document.body.appendChild(host); + const shadow = host.attachShadow({mode: 'open'}); + shadow.innerHTML = '<span id=target>Test Shadow Span</span><style>#target{background-color: red;}</style>'; + const target = shadow.querySelector('#target'); + const targetStyle = window.getComputedStyle(target); + testRoot(shadow, targetStyle); +}, "shadowRoot.adoptedStyleSheets should allow mutation in-place"); + +test(function() { + assert_true(Array.isArray(document.adoptedStyleSheets)); + const host = document.createElement('div'); + document.body.appendChild(host); + const shadow = host.attachShadow({mode: 'open'}); + assert_true(Array.isArray(shadow.adoptedStyleSheets)); +}, "adoptedStyleSheets should return true for isArray()"); +</script> diff --git a/testing/web-platform/tests/css/cssom/at-namespace.html b/testing/web-platform/tests/css/cssom/at-namespace.html new file mode 100644 index 0000000000..b1c76f3c58 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/at-namespace.html @@ -0,0 +1,29 @@ +<!doctype html> +<title>CSS Test: @namespace in CSSOM is not severely broken</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1464865"> +<link rel="help" href="https://drafts.csswg.org/cssom/#insert-a-css-rule"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style id="s"> + div { color: green } +</style> +<div>Should be green</div> +<script> +test(function() { + assert_throws_dom("InvalidStateError", function() { + s.sheet.insertRule('@namespace myhtml url("http://www.w3.org/1999/xhtml")', 0); + }); + assert_equals(s.sheet.cssRules.length, 1, "Shouldn't have been inserted"); + assert_throws_dom("SyntaxError", function() { + s.sheet.insertRule("myhtml|div { color: red !important }", 0); + }); + assert_equals(s.sheet.cssRules.length, 1); + assert_equals( + getComputedStyle(document.querySelector("div")).color, + "rgb(0, 128, 0)", + "Namespace shouldn't be registered" + ); +}); +</script> + diff --git a/testing/web-platform/tests/css/cssom/base-uri.html b/testing/web-platform/tests/css/cssom/base-uri.html new file mode 100644 index 0000000000..b948d5a7f4 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/base-uri.html @@ -0,0 +1,55 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM base URI is the document's base URI</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://html.spec.whatwg.org/#document-base-url"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1343919"> +<base href="/non-existent-base/"> +<script src='/resources/testharness.js'></script> +<script src='/resources/testharnessreport.js'></script> +<div id="target"></div> +<script> +const target = document.getElementById("target"); +const kRelativeURI = "url(something.png)"; +const kKeyframes = [ + { backgroundImage: kRelativeURI }, + { backgroundImage: kRelativeURI }, +]; +function assertBackground() { + const image = getComputedStyle(target).backgroundImage; + assert_true(image.includes("non-existent-base"), image); +} +function assertNoBackground() { + const image = getComputedStyle(target).backgroundImage; + assert_equals(image, "none"); +} + +promise_test(async function() { + target.style.backgroundImage = kRelativeURI; + assertBackground(); + target.style.backgroundImage = ""; + assertNoBackground(); +}, "setProperty"); + +promise_test(async function() { + const keyframe = new KeyframeEffect(target, kKeyframes, 10000); + const animation = new Animation(keyframe, document.timeline); + animation.play(); + await animation.ready; + assertBackground(); + animation.cancel(); + assertNoBackground(); +}, "KeyframeEffect constructor"); + +promise_test(async function() { + const keyframe = new KeyframeEffect(target, [], 10000); + keyframe.setKeyframes(kKeyframes); + const animation = new Animation(keyframe, document.timeline); + animation.play(); + await animation.ready; + assertBackground(); + animation.cancel(); + assertNoBackground(); +}, "KeyframeEffect.setKeyframes"); +</script> diff --git a/testing/web-platform/tests/css/cssom/border-shorthand-serialization.html b/testing/web-platform/tests/css/cssom/border-shorthand-serialization.html new file mode 100644 index 0000000000..9b5821563c --- /dev/null +++ b/testing/web-platform/tests/css/cssom/border-shorthand-serialization.html @@ -0,0 +1,42 @@ +<!doctype html> +<html> +<link rel="author" title="Erik Nordin" href="mailto:enordin@mozilla.com"> +<link rel="help" href="https://drafts.csswg.org/css-backgrounds-3/#propdef-border"> +<link rel="help" href="https://drafts.csswg.org/cssom-1/#serialize-a-css-declaration-block"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1594241"> +<meta charset="utf-8"> +<title>serialization of border shorthand</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style id="target"> + .a { + border-width: 1px; + border-style: solid; + border-color: black; + } + .b { + border-width: 1px; + border-style: solid; + border-color: black; + border-image: linear-gradient(white,black); + } + .c { + border: 1px solid black; + } +</style> +<script> + test(function() { + let rule = document.getElementById('target').sheet.cssRules[0]; + assert_equals(rule.style.border, "", "border shorthand isn't serialized if border-image longhands are not initial"); + }, "Declaration with border longhands is not serialized to a border shorthand declaration."); + test(function() { + let rule = document.getElementById('target').sheet.cssRules[1]; + assert_equals(rule.style.border, "", "border shorthand isn't serialized if border-image longhands are not initial"); + }, "Declaration with border longhands and border-image is not serialized to a border shorthand declaration."); + test(function() { + let rule = document.getElementById('target').sheet.cssRules[2]; + assert_not_equals(rule.style.border, "", "border shorthand "); + assert_equals(rule.cssText, ".c { border: 1px solid black; }"); + }, "Border shorthand is serialized correctly if all border-image-* are set to their initial specified values."); +</script> +</html> diff --git a/testing/web-platform/tests/css/cssom/caretPositionFromPoint-with-transformation.html b/testing/web-platform/tests/css/cssom/caretPositionFromPoint-with-transformation.html new file mode 100644 index 0000000000..fcd02877ca --- /dev/null +++ b/testing/web-platform/tests/css/cssom/caretPositionFromPoint-with-transformation.html @@ -0,0 +1,46 @@ +<!doctype html> +<meta charset="utf-8"> +<title>getCaretPositionFromPoint should return the correct offset even in iframes with transformation</title> +<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-document-caretpositionfrompoint"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1546612"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #actual { + transform: translateX(100px); + } +</style> +<iframe id="expected"></iframe> +<br> +<iframe id="actual"></iframe> +<script> + const expectedFrame = document.getElementById("expected"); + const actualFrame = document.getElementById("actual"); + + const getCaretPositionOffset = frame => { + const source = `<!doctype html><meta charset="utf-8"><h1>title</h1><p>paragraph</p>` + + const elementCenter = elem => { + const rect = elem.getBoundingClientRect(); + return [rect.x + rect.width / 2, rect.y + rect.height / 2]; + }; + + return new Promise(resolve => { + frame.srcdoc = source; + frame.onload = () => { + const frameDoc = frame.contentDocument; + const {offset} = frameDoc.caretPositionFromPoint( + ...elementCenter(frameDoc.querySelector("h1")) + ); + resolve(offset); + }; + }); + }; + + promise_test(async () => { + assert_equals(...await Promise.all([ + getCaretPositionOffset(expectedFrame), + getCaretPositionOffset(actualFrame) + ]), "caret offset"); + }, "iframe's with equal content should report the same caret offset"); +</script> diff --git a/testing/web-platform/tests/css/cssom/computed-style-001.html b/testing/web-platform/tests/css/cssom/computed-style-001.html new file mode 100644 index 0000000000..e487981e30 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/computed-style-001.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test: getComputedStyle</title> + <link rel="author" title="Bear Travis" href="mailto:betravis@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#extensions-to-the-window-interface"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#resolved-values"> + <meta name="flags" content="dom"> + <meta name="assert" content="getComputedStyle returns a readonly CSSStyleDeclaration with resolved values"> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> + <style> + #outside { + width: 200px; + height: 200px; + } + #outside div { + font-size: 100px; + } + #inside { + width: 50%; + height: 100px; + } + </style> + </head> + <body> + <noscript>Test not run - javascript required.</noscript> + <div id="log"></div> + <div id="outside"><div id="inside"></div></div> + <script type="text/javascript"> + var outer = document.getElementById("outside"); + var inner = document.getElementById("inside"); + var innerStyle; + + // do not allow modifications to a computed CSSStyleDeclaration + test(function() { + innerStyle = window.getComputedStyle(inner); + assert_throws_dom( "NO_MODIFICATION_ALLOWED_ERR", + function() { innerStyle.cssText = "color: blue;"; }, + "do not allow setting cssText on a readonly CSSStyleDeclaration"); + assert_throws_dom( "NO_MODIFICATION_ALLOWED_ERR", + function() { innerStyle.setProperty("color", "blue"); }, + "do not allow calling setProperty on a readonly CSSStyleDeclaration"); + assert_throws_dom( "NO_MODIFICATION_ALLOWED_ERR", + function() { innerStyle.color = "blue"; }, + "do not allow setting a property on a readonly CSSStyleDeclaration"); + }, "read_only"); + + // Directly set properties are resolved + test(function() { + assert_equals(innerStyle.getPropertyValue("height"), "100px"); + }, "property_values"); + + // Inherited properties are resolved + test(function() { + assert_equals(innerStyle.getPropertyValue("font-size"), "100px"); + }, "inherited_property_values"); + + // Relative properties are resolved + test(function() { + assert_equals(innerStyle.getPropertyValue("width"), "100px"); + }, "relative_property_values"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/computed-style-002.html b/testing/web-platform/tests/css/cssom/computed-style-002.html new file mode 100644 index 0000000000..7b31c96c09 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/computed-style-002.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<title>CSS Test: getComputedStyle - resolved width in iframe</title> +<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org" /> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="frm" width="100"></iframe> +<script> + test(() => { + const frmDoc = frm.contentWindow.document; + frmDoc.open(); + frmDoc.write('<body style="margin:0"><div style="width:100%"></div>'); + frmDoc.close(); + + assert_equals(frm.contentWindow.getComputedStyle(frmDoc.querySelector("div")).width, "100px"); + }, "Check that a percent width in an iframe is resolved against iframe width for getComputedStyle."); +</script> diff --git a/testing/web-platform/tests/css/cssom/computed-style-003.html b/testing/web-platform/tests/css/cssom/computed-style-003.html new file mode 100644 index 0000000000..e73b793b8f --- /dev/null +++ b/testing/web-platform/tests/css/cssom/computed-style-003.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<title>CSS Test: getComputedStyle - resolved width in iframe dynamic display</title> +<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org" /> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="frm" width="100" style="display:none"></iframe> +<script> + const frmDoc = frm.contentWindow.document; + frmDoc.open(); + frmDoc.write('<body style="margin:0"><div style="width:100%"></div>'); + frmDoc.close(); + + document.body.offsetWidth; // Make sure we layout the top document. + + test(() => { + frm.style.display = "inline"; + assert_equals(frm.contentWindow.getComputedStyle(frmDoc.querySelector("div")).width, "100px"); + }, "Check that a percent width in an iframe is the resolved width when the iframe is displayed."); +</script> diff --git a/testing/web-platform/tests/css/cssom/computed-style-004.html b/testing/web-platform/tests/css/cssom/computed-style-004.html new file mode 100644 index 0000000000..c5b08712f9 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/computed-style-004.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<title>CSS Test: getComputedStyle - resolved width in nested iframes dynamic width</title> +<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org" /> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="outer" width="100" scrolling="no" frameborder="0"></iframe> +<script> + const outerDoc = outer.contentWindow.document; + outerDoc.open(); + outerDoc.write('<body style="margin:0"><iframe id="inner" scrolling="no" frameborder="0" style="width:100%"></iframe>'); + outerDoc.close(); + + const innerWindow = outerDoc.querySelector("#inner").contentWindow; + const innerDoc = innerWindow.document; + innerDoc.open(); + innerDoc.write('<body style="margin:0"><div style="width:100%"></div>'); + innerDoc.close(); + innerDoc.body.offsetWidth; // Make sure we layout the top document. + + test(() => { + assert_equals(innerWindow.getComputedStyle(innerDoc.querySelector("div")).width, "100px"); + }, "Check that the initial width is 100px."); + + test(() => { + outer.setAttribute("width", "200"); + assert_equals(innerWindow.getComputedStyle(innerDoc.querySelector("div")).width, "200px"); + }, "Check that the resolved width of the inner div is affected by changing the width of outer iframe."); +</script> diff --git a/testing/web-platform/tests/css/cssom/computed-style-005.html b/testing/web-platform/tests/css/cssom/computed-style-005.html new file mode 100644 index 0000000000..49a5977acd --- /dev/null +++ b/testing/web-platform/tests/css/cssom/computed-style-005.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test: getComputedStyle on blocks with auto margins</title> + <link rel="author" title="Brad Werth" href="mailto:bwerth@mozilla.com"> + <link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle" /> + <meta name="assert" content="getComputedStyle produces pixel values for margin: auto blocks"> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> + <style> + x { + display: block; + position: relative; + background: grey; + width: 60px; + height: 10px; + } + y { + display: block; + background: blue; + width: 40px; + height: 10px; + margin: auto; + } + + #absolute { + position: absolute; + left: 0; + right: 0; + } + #relative { + position: relative; + } + </style> + </head> + <body> + <div id="log"></div> + <x><y id="absolute"></y></x> + <x><y id="relative"></y></x> + <script type="text/javascript"> + let idsToTest = [ + "absolute", + "relative", + ]; + + for (let id of idsToTest) { + let elem = document.getElementById(id); + let elemStyle = window.getComputedStyle(elem); + + // positioned element's auto margins should be resolved to 10px. + test(function() { + assert_equals(elemStyle.getPropertyValue("margin-left"), "10px"); + assert_equals(elemStyle.getPropertyValue("margin-right"), "10px"); + }, id + "_computed_margins"); + + // positioned element should have a left and right of 0px (as authored). + test(function() { + assert_equals(elemStyle.getPropertyValue("left"), "0px"); + assert_equals(elemStyle.getPropertyValue("right"), "0px"); + }, id + "_computed_left_and_right"); + } + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/computed-style-set-property.html b/testing/web-platform/tests/css/cssom/computed-style-set-property.html new file mode 100644 index 0000000000..a1f1380dcc --- /dev/null +++ b/testing/web-platform/tests/css/cssom/computed-style-set-property.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<title>NoModificationAllowedError when mutating read only properties</title> +<link rel="help" href="https://www.w3.org/TR/cssom-1/#dom-cssstyledeclaration-setpropertyvalue"> +<meta name="assert" content="This test verifies that NoModificationAllowedError is thrown when mutating read only properties" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body></body> +<script> +test(function(t) { + assert_equals(document.defaultView.getComputedStyle(document.body, null).parentRule, null); +}, "Computed style parent (should be null)"); + +test(function(t) { + assert_throws_dom("NoModificationAllowedError", function() { + document.defaultView.getComputedStyle(document.body, null).color = "blue"; + }); +}, "Exception thrown when trying to change a computed style declaration via property"); + +test(function(t) { + assert_throws_dom("NoModificationAllowedError", function() { + document.defaultView.getComputedStyle(document.body, null).setProperty("color", "blue"); + }); +}, "Exception thrown when trying to change a computed style declaration via setProperty"); + +test(function(t) { + assert_throws_dom("NoModificationAllowedError", function() { + document.defaultView.getComputedStyle(document.body, null).webkitTransition = ""; + }); +}, "Exception thrown when trying to change a computed style alias via property"); + +test(function(t) { + assert_throws_dom("NoModificationAllowedError", function() { + document.defaultView.getComputedStyle(document.body, null).setProperty("webkitTransition", ""); + }); +}, "Exception thrown when trying to change a computed style alias via setProperty"); +</script> diff --git a/testing/web-platform/tests/css/cssom/css-style-attr-decl-block.html b/testing/web-platform/tests/css/cssom/css-style-attr-decl-block.html new file mode 100644 index 0000000000..744b3a744c --- /dev/null +++ b/testing/web-platform/tests/css/cssom/css-style-attr-decl-block.html @@ -0,0 +1,148 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<link rel="author" title="Xidorn Quan" href="mailto:me@upsuper.org"> +<link rel="help" href="https://drafts.csswg.org/cssom-1/#css-declaration-blocks"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> +function createTestElement(style) { + let wrapper = document.createElement("div"); + wrapper.innerHTML = `<div id="test" style="${style}"></div>`; + return wrapper.querySelector("#test"); +} + +test(function() { + let elem = createTestElement("z-index: 10;"); + assert_equals(elem.style.cssText, "z-index: 10;"); +}, "Style attribute should create CSS declaration block based on its content"); + +test(function() { + let elem = createTestElement("z-index: 20;"); + let style = elem.style; + assert_equals(style.cssText, "z-index: 20;"); + function assert_css_text(value, action) { + assert_equals(style.cssText, value, "CSS declaration block after " + action); + } + elem.setAttribute("style", "z-index: 21;"); + assert_css_text("z-index: 21;", "changing the style attribute"); + elem.removeAttribute("style"); + assert_css_text("", "removing the style attribute"); + elem.setAttribute("style", "position: absolute;"); + assert_css_text("position: absolute;", "adding style attribute again"); +}, "Changes to style attribute should reflect on CSS declaration block"); + +test(function() { + let elem = createTestElement("z-index: 30;"); + let style = elem.style; + assert_equals(style.cssText, "z-index: 30;"); + function assert_attr(value, action) { + assert_equals(elem.getAttribute("style"), value, "style attribute after " + action); + } + style.setProperty("z-index", "31"); + assert_attr("z-index: 31;", "changing property in CSS declaration block"); + style.removeProperty("z-index"); + assert_attr("", "removing property from CSS declaration block"); + style.setProperty("position", "absolute"); + assert_attr("position: absolute;", "adding property to CSS declaration block"); + style.cssText = "z-index: 32;"; + assert_attr("z-index: 32;", "changing cssText"); + style.cssText = "z-index: 33; invalid"; + assert_attr("z-index: 33;", "changing cssText to a partial invalid value"); +}, "Changes to CSS declaration block should reflect on style attribute"); + +test(function() { + let elem = createTestElement("z-index: 40;"); + let style = elem.style; + assert_equals(style.cssText, "z-index: 40;"); + // Create an observer for the element. + let observer = new MutationObserver(function() {}); + observer.observe(elem, {attributes: true, attributeOldValue: true}); + function assert_record_with_old_value(oldValue, action) { + let records = observer.takeRecords(); + assert_equals(records.length, 1, "number of mutation records after " + action); + let record = records[0]; + assert_equals(record.type, "attributes", "mutation type after " + action); + assert_equals(record.attributeName, "style", "mutated attribute after " + action); + assert_equals(record.oldValue, oldValue, "old value after " + action); + } + style.setProperty("z-index", "41"); + assert_record_with_old_value("z-index: 40;", "changing property in CSS declaration block"); + style.cssText = "z-index: 42;"; + assert_record_with_old_value("z-index: 41;", "changing cssText"); + style.cssText = "z-index: 42;"; + assert_record_with_old_value("z-index: 42;", "changing cssText with the same content"); + style.removeProperty("z-index"); + assert_record_with_old_value("z-index: 42;", "removing property from CSS declaration block"); + // Mutation to shorthand properties should also trigger only one mutation record. + style.setProperty("margin", "1px"); + assert_record_with_old_value("", "adding shorthand property to CSS declaration block"); + style.removeProperty("margin"); + assert_record_with_old_value("margin: 1px;", "removing shorthand property from CSS declaration block"); + // Final sanity check. + assert_equals(elem.getAttribute("style"), ""); +}, "Changes to CSS declaration block should queue mutation record for style attribute"); + +test(function() { + let elem = createTestElement("z-index: 50; invalid"); + let style = elem.style; + assert_equals(style.cssText, "z-index: 50;"); + // Create an observer for the element. + let observer = new MutationObserver(function() {}); + observer.observe(elem, {attributes: true}); + function assert_no_record(action) { + let records = observer.takeRecords(); + assert_equals(records.length, 0, "expect no record after " + action); + } + style.setProperty("z-index", "invalid"); + assert_no_record("setting invalid value to property"); + // Longhand property. + style.removeProperty("position"); + assert_no_record("removing non-existing longhand property"); + style.setProperty("position", ""); + assert_no_record("setting empty string to non-existing longhand property"); + // Shorthand property. + style.removeProperty("margin"); + assert_no_record("removing non-existing shorthand property"); + style.setProperty("margin", ""); + assert_no_record("setting empty string to non-existing shorthand property"); + // Check that the value really isn't changed. + assert_equals(elem.getAttribute("style"), "z-index: 50; invalid", + "style attribute after removing non-existing properties"); +}, "Removing non-existing property or setting invalid value on CSS declaration block shouldn't queue mutation record"); + +test(function() { + let elem = createTestElement("background-image: url(./);"); + let style = elem.style; + let base = document.createElement("base"); + base.href = "/"; + document.body.appendChild(elem); + let originalComputedValue = getComputedStyle(elem).backgroundImage; + document.head.appendChild(base); + this.add_cleanup(() => { + document.head.removeChild(base); + document.body.removeChild(elem); + }); + style.setProperty("background-color", "green"); + assert_equals(getComputedStyle(elem).backgroundImage, originalComputedValue, + "getComputedStyle(elem).backgroundImage after setting background-color"); + style.setProperty("background-image", "url(./)"); + assert_not_equals(getComputedStyle(elem).backgroundImage, originalComputedValue, + "getComputedStyle(elem).backgroundImage after setting background-image"); +}, "Changes to CSS declaration block after a base URL change"); + +test(function() { + let e1 = document.createElement('div'); + let e2 = document.createElement('div'); + document.body.append(e1, e2); + this.add_cleanup(() => { + e1.remove(); + e2.remove(); + }); + e1.style.cssText = "all:revert;border-bottom-left-radius:1px;"; + e2.style.cssText = "all:unset;border-bottom-left-radius:1px;"; + let processed = e1.style.cssText.split(';') + .map(x => x.replace(/revert$/, 'unset')).join(';'); + assert_equals(processed, e2.style.cssText); +}, "Expansion of all:unset and all:revert treated identically"); +</script> diff --git a/testing/web-platform/tests/css/cssom/css-style-attribute-modifications.html b/testing/web-platform/tests/css/cssom/css-style-attribute-modifications.html new file mode 100644 index 0000000000..524a5adca3 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/css-style-attribute-modifications.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:ecobos@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/cssom/#the-elementcssinlinestyle-interface"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="test" style="color: red"></div> +<script> +test(function() { + var el = document.getElementById("test"); + el.style.color = ""; + assert_true(el.hasAttribute("style")); + + el.removeAttribute("style"); + assert_false(el.hasAttribute("style")); +}, "Mutating the style declaration doesn't remove the style attribute"); +</script> diff --git a/testing/web-platform/tests/css/cssom/css-style-declaration-modifications.html b/testing/web-platform/tests/css/cssom/css-style-declaration-modifications.html new file mode 100644 index 0000000000..a0e2275491 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/css-style-declaration-modifications.html @@ -0,0 +1,71 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test: CSSStyleDeclaration Interface</title> + <link rel="author" title="Bear Travis" href="mailto:betravis@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface"> + <meta name="flags" content="dom"> + <meta name="assert" content="CSSStyleDeclaration is properly initialized and can be modified through its interface"> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> + <style id="styleElement"> + #test { color: green; } + </style> + </head> + <body> + <div id="log"></div> + <div id="test"></div> + <script type="text/javascript"> + var declaration; + setup(function() { + var styleElement = document.getElementById("styleElement"); + declaration = styleElement.sheet.cssRules.item(0).style; + }); + + test(function() { + assert_equals(declaration.cssText, "color: green;"); + assert_equals(declaration.getPropertyValue("color"), "green"); + }, "Reading CSSStyleDeclaration initialized from a style element"); + + test(function() { + declaration.cssText = "margin-left:10px; padding-left:10px"; + assert_equals(declaration.cssText, "margin-left: 10px; padding-left: 10px;"); + assert_equals(declaration.length, 2); + assert_equals(declaration.item(0), "margin-left"); + assert_equals(declaration.item(1), "padding-left"); + assert_equals(declaration.getPropertyValue("margin-left"), "10px"); + assert_equals(declaration.getPropertyValue("padding-left"), "10px"); + + var computedStyle = window.getComputedStyle(document.getElementById("test")); + assert_equals(computedStyle.getPropertyValue("margin-left"), "10px"); + assert_equals(computedStyle.getPropertyValue("padding-left"), "10px"); + }, "Setting CSSStyleDeclaration#cssText"); + + test(function() { + while (declaration.length > 0) { + declaration.removeProperty(declaration.item(0)); + } + declaration.setProperty("margin-left", "15px"); + declaration.setProperty("padding-left", "15px"); + + + assert_equals(declaration.length, 2); + assert_equals(declaration.item(0), "margin-left"); + assert_equals(declaration.item(1), "padding-left"); + assert_equals(declaration.getPropertyValue("margin-left"), "15px"); + assert_equals(declaration.getPropertyValue("padding-left"), "15px"); + + var computedStyle = window.getComputedStyle(document.getElementById("test")); + assert_equals(computedStyle.getPropertyValue("margin-left"), "15px"); + assert_equals(computedStyle.getPropertyValue("padding-left"), "15px"); + + }, "Calling CSSStyleDeclaration#setProperty"); + + test(function() { + declaration.setProperty("background-color", "red", "ImPoRtAnt"); + assert_equals(declaration.getPropertyPriority("background-color"), "important"); + }, "setProperty priority should be case-insensitive"); + + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/css-style-reparse.html b/testing/web-platform/tests/css/cssom/css-style-reparse.html new file mode 100644 index 0000000000..d5153cf267 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/css-style-reparse.html @@ -0,0 +1,59 @@ +<!doctype html> +<html> +<head> + <meta charset=utf-8> + <title>CSS Test: DOM modification re-parsing test</title> + <link rel="help" href="https://drafts.csswg.org/cssom/"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface"> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <style>div { min-width: 0px; }</style> + <style id="style-element"></style> +</head> +<body> +<div id="test-div"></div> +<script type="text/javascript"> + var style = document.getElementById("style-element"); + var div = document.getElementById("test-div"); + + function testProperty(prop) { + // Assigning an empty string to textContent or innerHTML should trigger a + // reparse only if the element is not empty. + style.sheet.insertRule("#test-div { min-width: 42px; }"); + assert_equals(getComputedStyle(div).minWidth, "42px"); + + style[prop] = ""; + assert_equals(getComputedStyle(div).minWidth, "42px"); + + style[prop] = " "; + assert_equals(getComputedStyle(div).minWidth, "0px"); + + style.sheet.insertRule("#test-div { min-width: 42px; }"); + assert_equals(getComputedStyle(div).minWidth, "42px"); + + style[prop] = ""; + assert_equals(getComputedStyle(div).minWidth, "0px"); + + style.sheet.insertRule("#test-div { min-width: 42px; }"); + assert_equals(getComputedStyle(div).minWidth, "42px"); + + style.appendChild(document.createTextNode("")); + assert_equals(getComputedStyle(div).minWidth, "0px"); + + style.sheet.insertRule("#test-div { min-width: 42px; }"); + assert_equals(getComputedStyle(div).minWidth, "42px"); + + style[prop] = ""; + assert_equals(getComputedStyle(div).minWidth, "0px"); + } + + test(function() { + testProperty("textContent"); + }, "style.textContent modification"); + + test(function() { + testProperty("innerHTML"); + }, "style.innerHTML modification"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/cssimportrule-parent.html b/testing/web-platform/tests/css/cssom/cssimportrule-parent.html new file mode 100644 index 0000000000..2792af69c0 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssimportrule-parent.html @@ -0,0 +1,20 @@ +<!doctype html> +<title>CSSImportRule correctly unlinks its child stylesheet from its parent</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssimportrule-interface"> +<style> + @import "data:text/css,:root{background:red}"; +</style> +<script> +let t = async_test("@import stylesheet is properly unlinked from parent after removal"); +window.onload = t.step_func_done(function() { + let sheet = document.styleSheets[0]; + let childSheet = sheet.cssRules[0].styleSheet; + assert_not_equals(childSheet, null, "@import rule should have a stylesheet"); + assert_equals(childSheet.parentStyleSheet, sheet, "@import rule should the correct parent"); + sheet.deleteRule(0); + assert_equals(childSheet.parentStyleSheet, null, "@import rule should be correctly unlinked"); +}); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssimportrule-sheet-identity.html b/testing/web-platform/tests/css/cssom/cssimportrule-sheet-identity.html new file mode 100644 index 0000000000..3db5634ebe --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssimportrule-sheet-identity.html @@ -0,0 +1,24 @@ +<!doctype html> +<title>CSSImportRule has different sheets even if referencing the same URL</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssimportrule-interface"> +<link rel="stylesheet" href="support/import-rule.css"> +<link rel="stylesheet" href="support/import-rule.css"> +<script> +let t = async_test("CSSImportRule has different sheets even if referencing the same URL"); +window.onload = t.step_func_done(function() { + let sheet1 = document.styleSheets[0]; + let sheet2 = document.styleSheets[1]; + + assert_not_equals(sheet1, sheet2); + + let childSheet1 = sheet1.cssRules[0].styleSheet; + let childSheet2 = sheet2.cssRules[0].styleSheet; + + assert_not_equals(childSheet1, null); + assert_not_equals(childSheet2, null); + assert_not_equals(childSheet1, childSheet2, "@import pointing to the same URL shouldn't point to the same StyleSheet object"); +}); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssimportrule.html b/testing/web-platform/tests/css/cssom/cssimportrule.html new file mode 100644 index 0000000000..89269f1c24 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssimportrule.html @@ -0,0 +1,108 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSSOM CSSRule CSSImportRule interface</title> + <link rel="author" title="Letitia Lew" href="mailto:lew.letitia@gmail.com"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#css-rules"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssimportrule-interface"> + <meta name="flags" content="dom"> + <meta name="assert" content="All properties for this CSSImportRule instance of CSSRule are initialized correctly"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <style id="styleElement" type="text/css"> + @import url("support/a-green.css"); + @import url("support/a-green.css") screen; + @import url("support/a-green.css") all; + @page { background-color: red; } + </style> +</head> +<body> + <div id="log"></div> + + <script type="text/javascript"> + var styleSheet, ruleList, rule, ruleWithMedia, ruleWithMediaAll; + setup(function() { + styleSheet = document.getElementById("styleElement").sheet; + ruleList = styleSheet.cssRules; + rule = ruleList[0]; + ruleWithMedia = ruleList[1]; + ruleWithMediaAll = ruleList[2]; + }); + + test(function() { + assert_true(rule instanceof CSSRule); + assert_true(rule instanceof CSSImportRule); + assert_true(ruleWithMedia instanceof CSSRule); + assert_true(ruleWithMedia instanceof CSSImportRule); + }, "CSSRule and CSSImportRule types"); + + test(function() { + assert_equals(rule.STYLE_RULE, 1); + assert_equals(rule.IMPORT_RULE, 3); + assert_equals(rule.MEDIA_RULE, 4); + assert_equals(rule.FONT_FACE_RULE, 5); + assert_equals(rule.PAGE_RULE, 6); + assert_equals(rule.NAMESPACE_RULE, 10); + assert_idl_attribute(rule, "type"); + assert_equals(typeof rule.type, "number"); + }, "Type of CSSRule#type and constant values"); + + test(function() { + assert_true(rule instanceof CSSRule); + assert_idl_attribute(rule, "cssText"); + assert_idl_attribute(rule, "parentRule"); + assert_idl_attribute(rule, "parentStyleSheet"); + + assert_readonly(rule, "type"); + assert_readonly(rule, "parentRule"); + assert_readonly(rule, "parentStyleSheet"); + }, "Existence and writability of CSSRule attributes"); + + test(function() { + assert_equals(rule.type, rule.IMPORT_RULE); + assert_equals(typeof rule.cssText, "string"); + assert_equals(rule.cssText, '@import url("support/a-green.css");'); + assert_equals(ruleWithMedia.cssText, '@import url("support/a-green.css") screen;'); + assert_equals(ruleWithMediaAll.cssText, '@import url("support/a-green.css") all;'); + assert_equals(rule.parentRule, null); + assert_true(rule.parentStyleSheet instanceof CSSStyleSheet); + }, "Values of CSSRule attributes"); + + test(function() { + assert_idl_attribute(rule, "href"); + assert_idl_attribute(rule, "media"); + assert_idl_attribute(rule, "styleSheet"); + + assert_readonly(rule, "href"); + assert_readonly(rule, "media"); + assert_readonly(rule, "styleSheet"); + }, "Existence and writability of CSSImportRule attributes"); + + test(function() { + assert_equals(typeof rule.href, "string"); + assert_true(rule.media instanceof MediaList); + assert_true(rule.styleSheet instanceof CSSStyleSheet); + assert_true(ruleWithMedia.media.length > 0); + assert_equals(ruleWithMedia.media.mediaText, "screen"); + }, "Values of CSSImportRule attributes"); + + test(function() { + ruleWithMedia.media = "print"; + assert_equals(ruleWithMedia.media.mediaText, "print"); + }, "CSSImportRule : MediaList mediaText attribute should be updated due to [PutForwards]"); + + test(function() { + var ruleWithPage = ruleList[3]; + ruleWithPage.style = "margin-top: 10px;" + assert_equals(ruleWithPage.style.cssText, "margin-top: 10px;"); + }, "CSSStyleDeclaration cssText attribute should be updated due to [PutForwards]"); + + test(function() { + styleSheet.media = "screen"; + assert_equals(styleSheet.media.mediaText, "screen"); + }, "StyleSheet : MediaList mediaText attribute should be updated due to [PutForwards]"); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/cssom-cssText-serialize.html b/testing/web-platform/tests/css/cssom/cssom-cssText-serialize.html new file mode 100644 index 0000000000..27479f026d --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssom-cssText-serialize.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSSOM Parsing Test: getting cssText must return the result of serializing the CSS declaration blocks.</title> + <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface"> + + <link rel="source" href="http://trac.webkit.org/export/120528/trunk/LayoutTests/fast/css/cssText-cache.html"> + <meta name="flags" content="dom"> + + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + + <body> + <div id="log"></div> + <div id="box"></div> + <script> + test(function() { + var style = document.getElementById('box').style; + style.left = "10px"; + assert_equals(style.cssText, "left: 10px;"); + style.right = "20px"; + assert_equals(style.cssText, "left: 10px; right: 20px;"); + }, 'CSSStyleDeclaration cssText serializes declaration blocks.'); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/cssom-cssstyledeclaration-set.html b/testing/web-platform/tests/css/cssom/cssom-cssstyledeclaration-set.html new file mode 100644 index 0000000000..1fb7ccd324 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssom-cssstyledeclaration-set.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSSOM: CSSStyleDeclaration on HTMLElement represents inline style changes</title> + <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface"> + + <link rel="source" href="http://trac.webkit.org/export/120528/trunk/LayoutTests/fast/css/cssText-cache.html"> + <meta name="flags" content="dom"> + + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + + <body> + <div id="log"></div> + + <div id="box"></div> + + <script> + + var style = document.getElementById('box').style; + + test(function(){ + + style.left = "10px"; + assert_equals(style.left, "10px", 'left property set on element\'s CSSStyleDeclaration Object'); + style.left = "20px"; + assert_equals(style.left, "20px", 'left property set on element\'s CSSStyleDeclaration Object'); + + }, 'CSSStyleDeclaration on HTMLElement represents inline style changes'); + + </script> + + + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/cssom-fontfacerule-constructors.html b/testing/web-platform/tests/css/cssom/cssom-fontfacerule-constructors.html new file mode 100644 index 0000000000..ed1cab257d --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssom-fontfacerule-constructors.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSSOM Parsing Test: @font-face rules toString() as valid interfaces</title> + <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com"> + <link rel="reviewer" title="Ms2ger" href="mailto:ms2ger@gmail.com"> <!-- 2012-06-17 --> + <link rel="help" href="https://drafts.csswg.org/css-fonts-4/#om-fontface"> + + <meta name="flags" content="dom"> + + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + + <body> + <div id="log"></div> + + + <style id="teststyles"> + @font-face { + src: url(http://foo/bar/font.ttf); + } + @font-face { + font-family: STIXGeneral; + src: local(STIXGeneral), url(/stixfonts/STIXGeneral.otf); + unicode-range: U+000-49F, U+2000-27FF, U+2900-2BFF, U+1D400-1D7FF; + } + @font-face { + font-family: MainText; + src: url(http://example.com/font.ttf); + font-variant: oldstyle-nums proportional-nums styleset(1,3); + } + + @font-face { + font-family: BodyText; + src: local("HiraMaruPro-W4"); + font-variant: proportional-width; + font-feature-settings: "ital"; /* Latin italics within CJK text feature */ + } + </style> + + + <script> + var validRules = document.getElementById('teststyles').sheet.cssRules; + + test(function(){ + for (var i = 0; i < validRules.length; ++i) { + assert_equals(validRules.item(i).toString(), '[object CSSFontFaceRule]'); + } + }, '@font-face declarations are instances of CSSFontFaceRule') + + + test(function(){ + for (var i = 0; i < validRules.length; ++i) { + assert_equals(validRules.item(i).style.toString(), '[object CSSStyleDeclaration]'); + } + }, 'The style attribute must return a CSSStyleDeclaration block') + + </script> + + + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/cssom-fontfacerule.html b/testing/web-platform/tests/css/cssom/cssom-fontfacerule.html new file mode 100644 index 0000000000..9a06808e3a --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssom-fontfacerule.html @@ -0,0 +1,58 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSSOM Parsing Test: @font-face rules parsed into CSSOM CSSFontFaceRules</title> + <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com"> + <link rel="help" href="https://drafts.csswg.org/css-fonts-4/#om-fontface"> + + <meta name="flags" content="dom"> + + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + + <body> + <div id="log"></div> + + + <style id="teststyles"> + @font-face { + src: url(http://foo/bar/font.ttf); + } + @font-face { + font-family: STIXGeneral; + src: local(STIXGeneral), url(/stixfonts/STIXGeneral.otf); + unicode-range: U+000-49F, U+2000-27FF, U+2900-2BFF, U+1D400-1D7FF; + } + @font-face { + font-family: MainText; + src: url(http://example.com/font.ttf); + font-variant: oldstyle-nums proportional-nums styleset(1,3); + } + + @font-face { + font-family: BodyText; + src: local("HiraMaruPro-W4"); + font-variant: proportional-width; + font-feature-settings: "ital"; /* Latin italics within CJK text feature */ + } + </style> + + + <script> + var validRules = document.getElementById('teststyles').sheet.cssRules; + + test(function(){ + + assert_equals(validRules[0].style.src, 'url("http://foo/bar/font.ttf")'); + assert_equals(validRules[1].style.fontFamily, 'STIXGeneral'); + + /* unimplemented @font-face properties are not represented in CSSOM */ + + }, 'CSSStyleDeclaration values are represented within CSSFontFaceRule') + + </script> + + + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/cssom-getPropertyValue-common-checks.html b/testing/web-platform/tests/css/cssom/cssom-getPropertyValue-common-checks.html new file mode 100644 index 0000000000..27d802cd3d --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssom-getPropertyValue-common-checks.html @@ -0,0 +1,223 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Common serialization checks for all properties</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" /> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue"> + +<div id="element"></div> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +const element = document.getElementById("element"); +const { style } = element; +const computedStyle = getComputedStyle(element); +const cssProperties = new Set(); +const cssShorthands = new Map(); +const cssShorthandsForLonghand = new Map(); +const cssLonghands = new Set(); +const cssAliases = new Map(); +const initialValues = new Map(); + +setup(function() { + for (let obj = style; obj; obj = Reflect.getPrototypeOf(obj)) { + for (let name of Object.getOwnPropertyNames(obj)) { + const property = name.replace(/[A-Z]/g, c => "-" + c.toLowerCase()); + if (CSS.supports(property, "initial")) { + cssProperties.add(property); + } + } + } + for (let property of cssProperties) { + style.cssText = ""; + style.setProperty(property, "initial"); + if (style.length > 1) { + cssShorthands.set(property, [...style]); + for (let longhand of style) { + if (cssShorthandsForLonghand.has(longhand)) { + cssShorthandsForLonghand.get(longhand).add(property); + } else { + cssShorthandsForLonghand.set(longhand, new Set([property])); + } + } + } else if (style.length === 1) { + if (property === style[0]) { + cssLonghands.add(property); + } else { + cssAliases.set(property, style[0]); + } + } + } +}); + +test(function() { + const bad = []; + for (let property of cssProperties) { + style.cssText = ""; + style.setProperty(property, "initial"); + const result = style.getPropertyValue(property); + if (result !== "initial") { + bad.push([property, result]); + } + } + assert_array_equals(bad, []); +}, "All properties can serialize 'initial'"); + +test(function() { + for (let longhand of cssLonghands) { + element.style.setProperty(longhand, "initial"); + } + const bad = []; + for (let property of cssProperties) { + const result = computedStyle.getPropertyValue(property); + if (CSS.supports(property, result)) { + initialValues.set(property, result); + } else if (property !== "all") { + bad.push([property, result]); + } + } + assert_array_equals(bad, []); +}, "All properties (except 'all') can serialize their initial value (computed)"); + +test(function() { + const bad = []; + for (let [property, value] of initialValues) { + style.cssText = ""; + style.setProperty(property, value); + const result = style.getPropertyValue(property); + if (!CSS.supports(property, result) && property !== "all") { + bad.push([property, value, result]); + } + } + assert_array_equals(bad, []); +}, "All properties (except 'all') can serialize their initial value (specified)"); + +test(function() { + const bad = []; + for (let [shorthand, longhands] of cssShorthands) { + style.cssText = ""; + for (let longhand of longhands) { + style.setProperty(longhand, "initial"); + } + const result = style.getPropertyValue(shorthand); + if (result !== "initial") { + bad.push([shorthand, result]); + } + } + assert_array_equals(bad, []); +}, "All shorthands can serialize their longhands set to 'initial'"); + +test(function() { + const bad = []; + outerloop: + for (let [shorthand, longhands] of cssShorthands) { + style.cssText = ""; + for (let longhand of longhands) { + if (!initialValues.has(longhand)) { + continue outerloop; + } + style.setProperty(longhand, initialValues.get(longhand)); + } + const result = style.getPropertyValue(shorthand); + if (!CSS.supports(shorthand, result) && shorthand !== "all") { + bad.push([shorthand, result]); + } + } + assert_array_equals(bad, []); +}, "All shorthands (except 'all') can serialize their longhands set to their initial value"); + +test(function() { + const bad = []; + for (let [alias, target] of cssAliases) { + style.cssText = ""; + style.setProperty(target, "initial"); + const result = style.getPropertyValue(alias); + if (result !== "initial") { + bad.push([alias, result]); + } + } + assert_array_equals(bad, []); +}, "All aliases can serialize target property set to 'initial'"); + +test(function() { + const bad = []; + for (let [alias, target] of cssAliases) { + if (!initialValues.has(target)) { + continue; + } + style.cssText = ""; + style.setProperty(target, initialValues.get(target)); + const result = style.getPropertyValue(alias); + if (!CSS.supports(alias, result)) { + bad.push([alias, result]); + } + } + assert_array_equals(bad, []); +}, "All aliases can serialize target property set to its initial value"); + +test(function() { + const bad = []; + for (let [shorthand, longhands] of cssShorthands) { + for (let longhand of longhands) { + style.cssText = ""; + style.setProperty(shorthand, "initial"); + style.setProperty(longhand, "inherit"); + const result = style.getPropertyValue(shorthand); + if (result !== "") { + bad.push([shorthand, longhand, result]); + } + } + } + assert_array_equals(bad, []); +}, "Can't serialize shorthand when longhands are set to different css-wide keywords"); + +test(function() { + const bad = []; + for (let [shorthand, longhands] of cssShorthands) { + for (let longhand of longhands) { + style.cssText = ""; + style.setProperty(shorthand, "initial"); + style.setProperty(longhand, "initial", "important"); + const result = style.getPropertyValue(shorthand); + if (result !== "") { + bad.push([shorthand, longhand, result]); + } + } + } + assert_array_equals(bad, []); +}, "Can't serialize shorthand when longhands have different priority"); + +test(function() { + const bad = []; + for (let [shorthand, longhands] of cssShorthands) { + for (let longhand of longhands) { + style.cssText = ""; + style.setProperty(shorthand, "initial"); + style.removeProperty(longhand); + const result = style.getPropertyValue(shorthand); + if (result !== "") { + bad.push([shorthand, longhand, result]); + } + } + } + assert_array_equals(bad, []); +}, "Can't serialize shorthand set to 'initial' when some longhand is missing"); + +test(function() { + const bad = []; + for (let [shorthand, longhands] of cssShorthands) { + if (initialValues.has(shorthand)) { + for (let longhand of longhands) { + style.cssText = ""; + style.setProperty(shorthand, initialValues.get(shorthand)); + style.removeProperty(longhand); + const result = style.getPropertyValue(shorthand); + if (result !== "") { + bad.push([shorthand, longhand, result]); + } + } + } + } + assert_array_equals(bad, []); +}, "Can't serialize shorthand set to initial value when some longhand is missing"); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssom-pagerule.html b/testing/web-platform/tests/css/cssom/cssom-pagerule.html new file mode 100644 index 0000000000..c7604eba33 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssom-pagerule.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<title>CSSOM: CSSPageRule tests</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#the-csspagerule-interface" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @page {} +</style> +<script> + const sheet = document.styleSheets[0]; + const rule = sheet.cssRules[0]; + + test(() => { + assert_true(!!rule); + assert_equals(rule.type, CSSRule.PAGE_RULE); + }, "Sanity checks"); + + test(() => { + assert_equals(rule.selectorText, ""); + }, "Page selector is initially the empty string"); + + test(() => { + rule.selectorText = ":left"; + assert_equals(rule.selectorText, ":left"); + }, "Set selectorText to :left pseudo page"); + + test(() => { + rule.selectorText = "named"; + assert_equals(rule.selectorText, "named"); + }, "Set selectorText to named page"); + + test(() => { + rule.selectorText = "named:first"; + assert_equals(rule.selectorText, "named:first"); + }, "Set selectorText to named page with :first pseudo page"); + + test(() => { + assert_equals(rule.parentStyleSheet, sheet); + sheet.deleteRule(0); + assert_equals(rule.parentStyleSheet, null); + rule.selectorText = "pagename"; + }, "Set selectorText to named page after rule was removed"); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssom-ruleTypeAndOrder.html b/testing/web-platform/tests/css/cssom/cssom-ruleTypeAndOrder.html new file mode 100644 index 0000000000..19aab35357 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssom-ruleTypeAndOrder.html @@ -0,0 +1,75 @@ +<!doctype html> +<head> + <title>CSS OM: CSS Rule Types and Order</title> + <link rel="help" href="https://drafts.csswg.org/cssom-1/#the-cssrule-interface"> + <meta name="flags" content="dom"> + <meta name="assert" content="Testing Serialization of Style Rules"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style id="s-0"> + @namespace svg "http://www.w3.org/2000/svg"; + </style> + <style id="s-1"> + @import url('main.css'); + </style> + <style id="s-2"> + h1 { background: indianred; } + </style> + <style id="s-3"> + @namespace svg "http://www.w3.org/2000/svg"; + svg|a { color: white; } + </style> + <style id="s-4"> + @font-face { + font-family: 'Megalopolis'; + src: url('fonts/megalopolisextra-webfont.eot'); + src: url('fonts/megalopolisextra-webfont.eot?#iefix') format('embedded-opentype'), + url('fonts/megalopolisextra-webfont.woff') format('woff'), + url('fonts/megalopolisextra-webfont.ttf') format('truetype'), + url('fonts/megalopolisextra-webfont.svg#MEgalopolisExtraRegular') format('svg'); + font-weight: normal; + font-style: normal; + } + </style> + <style id="s-5"> + @media (min-width: 200px) { + h1 { background: aliceblue; } + } + </style> + <style id="s-6"> + @page :first { + h1 { color: #444; } + } + </style> +</head> +<body> + <div id="log"></div> + <script type="text/javascript"> + "use strict"; + + var stylesheets = document.styleSheets; + + var expectedOrderOfTypes = [10, 3, 1, 1, 5, 4, 6]; + + var typesText = { + 1: 'style rule', + 3: '@import rule', + 4: 'media rule', + 5: '@font-face rule', + 6: 'page rule', + 10: 'namespace rule' + }; + + for (var i = 0; i < stylesheets.length; i++) { + test( function () { + if (i === 3) { + var cssType = stylesheets[i].cssRules[1].type; + } else { + var cssType = stylesheets[i].cssRules[0].type; + } + assert_equals(typesText[cssType], typesText[expectedOrderOfTypes[i]]); + }, 'Type of #s-' + i + ' is expected to be ' + typesText[expectedOrderOfTypes[i]]); + } + </script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/cssom-setProperty-shorthand.html b/testing/web-platform/tests/css/cssom/cssom-setProperty-shorthand.html new file mode 100644 index 0000000000..4c0f9a2f93 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssom-setProperty-shorthand.html @@ -0,0 +1,77 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSSOM: CSSStyleDeclaration (set|remove)PropertyValue sets/removes shorthand properties</title> + <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface"> + + <link rel="source" href="http://trac.webkit.org/export/120528/trunk/LayoutTests/fast/css/cssom-remove-shorthand-property.html"> + <meta name="flags" content="dom"> + + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + + <body> + <div id="log"></div> + + <div id="box"></div> + + <script> + shorthandProperties = [ + "font", + "border-top", + "border-right", + "border-bottom", + "border-left", + "border", + "border-color", + "border-style", + "border-width", + "background-position", + "background-repeat", + "border-spacing", + "list-style", + "margin", + "outline", + "padding", + "background", + "overflow", + "border-radius" + ]; + + element = document.createElement('span'); + + function canSetProperty(propertyName, priority) { + element.style.setProperty(propertyName, 'initial', priority); + return element.style.getPropertyValue(propertyName) == 'initial'; + } + + function canRemoveProperty(propertyName) { + element.style.removeProperty(propertyName); + return element.style.getPropertyValue(propertyName) != 'initial'; + } + + for (i = 0; i < shorthandProperties.length; ++i) { + var propertyName = shorthandProperties[i]; + + test(function(){ + assert_true(canSetProperty(propertyName, ''), 'can setPropertyValue with shorthand'); + }, 'shorthand ' + propertyName + ' can be set with setProperty'); + + test(function(){ + assert_true(canRemoveProperty(propertyName), 'can setPropertyValue with shorthand'); + }, 'shorthand ' + propertyName + ' can be removed with removeProperty'); + + test(function(){ + assert_true(canSetProperty(propertyName, 'important'), 'can setPropertyValue with shorthand'); + }, 'shorthand ' + propertyName + ' can be set with setProperty and priority !important'); + + test(function(){ + assert_true(canRemoveProperty(propertyName), 'can setPropertyValue with shorthand'); + }, 'shorthand ' + propertyName + ' can be removed with removeProperty even when set with !important'); + + } + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-cssfontrule.tentative.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-cssfontrule.tentative.html new file mode 100644 index 0000000000..2efab276c5 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-cssfontrule.tentative.html @@ -0,0 +1,27 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSStyleDeclaration for a CSSFontRule</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface"> +<link rel="help" href="https://drafts.csswg.org/css-fonts-4/#cssfontfacerule"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/5649#issuecomment-755796005"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +@font-face {} +</style> +<script> + +const fontFaceRule = document.styleSheets[0].cssRules[0]; + +test(() => { + assert_true("unicode-range" in fontFaceRule.style); + assert_idl_attribute(fontFaceRule.style, "unicode-range"); +}, "a CSSStyleDeclaration for a CSSFontRule contains a unicode-range attribute"); + + +test(() => { + assert_true("flex-direction" in fontFaceRule.style); + assert_idl_attribute(fontFaceRule.style, "flex-direction"); +}, "a CSSStyleDeclaration for a CSSFontRule contains a flex-direction attribute"); + +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-all-shorthand.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-all-shorthand.html new file mode 100644 index 0000000000..6619538cf1 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-all-shorthand.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<title>CSSOM test: serialization of the 'all' shorthand in cssText</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/cssom-1/#dom-cssstyledeclaration-csstext"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +const style = document.createElement("div").style; + +test(function() { + style.cssText = "all: inherit"; + assert_equals(style.cssText, "all: inherit;"); +}, "'all' shorthand alone"); + +test(function() { + style.cssText = "width: 100px; all: inherit; height: inherit"; + assert_equals(style.cssText, "all: inherit;"); +}, "'all' shorthand with 'width' and 'height'"); + +test(function() { + style.cssText = "direction: ltr; all: inherit; unicode-bidi: plaintext"; + assert_equals(style.cssText, "direction: ltr; all: inherit; unicode-bidi: plaintext;"); +}, "'all' shorthand with 'direction' and 'unicode-bidi'"); + +test(function() { + style.cssText = "width: 100px; --a: a; all: inherit; --b: b; height: inherit"; + assert_equals(style.cssText, "--a: a; all: inherit; --b: b;"); +}, "'all' shorthand with 'width', 'height' and custom properties"); + +test(function() { + let cssText = "all: inherit; "; + for (let longhand of getComputedStyle(document.documentElement)) { + cssText += longhand + ": inherit; "; + } + style.cssText = cssText; + assert_equals(style.cssText, "all: inherit; direction: inherit; unicode-bidi: inherit;"); +}, "'all' shorthand with all longhands"); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-final-delimiter.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-final-delimiter.html new file mode 100644 index 0000000000..01b0a32c3f --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-final-delimiter.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSSOM - CSSStyleDeclaration - Text - Serialization - Delimiters</title> + <link rel="author" title="Glenn Adams" href="mailto:glenn@skynav.com"/> + <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface"/> + <meta name="flags" content="dom"/> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <div id="box"></div> + <script> + var style = document.getElementById('box').style; + + test(function(){ + style.cssText = ""; + assert_equals(style.cssText, ""); + }, 'inline style - text - delimiters - zero declarations'); + + test(function(){ + style.cssText = "left: 10px"; + assert_equals(style.cssText, "left: 10px;"); + }, 'inline style - text - delimiters - one declaration'); + + test(function(){ + style.cssText = "left: 10px; right: 20px"; + assert_equals(style.cssText, "left: 10px; right: 20px;"); + }, 'inline style - text - delimiters - two declarations'); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-important.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-important.html new file mode 100644 index 0000000000..c12faf63a4 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-important.html @@ -0,0 +1,12 @@ +<title>CSSOM test: setting a property with cssText and !important</title> +<link rel="help" href="https://drafts.csswg.org/cssom-1/#dom-cssstyledeclaration-csstext"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="box"></div> +<script> + var style = document.getElementById('box').style; + test(function(){ + style.cssText = "padding: 10px !important; padding-left: 20px;"; + assert_equals(style.getPropertyValue("padding-left"), "10px"); + }, "padding-left should be taken from the !important property"); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext.html new file mode 100644 index 0000000000..686684e614 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext.html @@ -0,0 +1,126 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSSOM Test: CSSStyleDeclaration.cssText Test</title> + <link rel="author" title="kkoichi" href="mailto:coarse.ground@gmail.com"> + <link rel="reviewer" title="Simon Pieters" href="mailto:simonp@opera.com"><!-- 06-27-2013 --> + <link rel="help" href="https://drafts.csswg.org/cssom-1/#dom-cssstyledeclaration-csstext"> + <meta name="assert" content="CSS declarations is serialized as expected"> + <meta name="flags" content="dom"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <div id="log"></div> + <script> + function newElm() { + return document.body.appendChild(document.createElement('div')); + } + + test(function(){ + var style = newElm().style; + style.COLOR = 'red'; + + assert_equals(style.cssText, ''); + + }, 'uppercase property'); + + test(function(){ + var style = newElm().style; + style.color = 'RED'; + + // https://www.w3.org/Bugs/Public/show_bug.cgi?id=29317 + assert_any(assert_equals, style.cssText, ['color: red;', 'color: RED;']); + + }, 'uppercase value'); + + test(function(){ + var style = newElm().style; + + style.color = 'red'; + + style.color = 'unknown color'; + + assert_equals(style.cssText, 'color: red;'); + + }, 'overwriting with invalid value'); + + test(function(){ + var style = newElm().style; + style.color = 'rgb(255, 0, 0)'; + + assert_equals(style.cssText, 'color: rgb(255, 0, 0);'); + + }, 'use rgb'); + + test(function(){ + var e = newElm(); + var style = e.style; + + style.color = 'red'; + style.fontSize = '10pt'; + style.fontWeight = 'bold'; + + assert_equals(style.cssText, 'color: red; font-size: 10pt; font-weight: bold;'); + + }, 'cssText order'); + + test(function(){ + var e = newElm(); + var style = e.style; + + style.fontWeight = 'bold'; + style.color = 'red'; + style.fontSize = '10pt'; + + assert_equals(style.cssText, 'font-weight: bold; color: red; font-size: 10pt;'); + + }, 'another cssText order (non-alphabetical order)'); + + test(function(){ + var style = newElm().style; + + style.color = ' red'; + style.fontSize = '10pt '; + + assert_equals(style.cssText, 'color: red; font-size: 10pt;'); + + }, 'whitespaces in value'); + + test(function(){ + var style = newElm().style; + + style.color = 'red'; + style.unknown = 'unknown'; + style.fontSize = '10pt'; + assert_equals(style.cssText, 'color: red; font-size: 10pt;'); + + }, 'invalid property does not appear'); + + test(function(){ + var style = newElm().style; + style.cssText = "margin: 10px; margin-inline: 10px; margin-block: 10px; margin-inline-end: 10px; margin-bottom: 10px;"; + assert_equals(style.cssText, "margin-top: 10px; margin-right: 10px; margin-left: 10px; margin-inline-start: 10px; margin-block: 10px; margin-inline-end: 10px; margin-bottom: 10px;"); + }, 'Shorthands aren\'t serialized if there are other properties with different logical groups in between'); + + test(function(){ + var style = newElm().style; + style.cssText = "margin-top: 10px; margin-left: 10px; margin-right: 10px; margin-bottom: 10px; margin-inline-start: 10px; margin-inline-end: 10px; margin-block-start: 10px; margin-block-end: 10px;"; + assert_equals(style.cssText, "margin: 10px; margin-inline: 10px; margin-block: 10px;"); + }, 'Shorthands _are_ serialized if there are no other properties with different logical groups in between'); + + // https://github.com/w3c/csswg-drafts/issues/1033 + test(function() { + var elm = newElm(); + var style = elm.style; + + style.color = 'red'; + style.unknown = 'unknown'; + style.fontSize = '10pt'; + + assert_not_equals(getComputedStyle(elm).length, 0, "Should have a style"); + assert_equals(getComputedStyle(elm).cssText, "", "cssText is empty"); + }, 'cssText on computed style declaration returns the empty string'); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-custom-properties.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-custom-properties.html new file mode 100644 index 0000000000..92cf2c847d --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-custom-properties.html @@ -0,0 +1,19 @@ +<!doctype html> +<title>CSS Test: computed style declaration includes custom properties.</title> +<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> +<link rel="author" href="https://mozilla.org" title="Mozilla"> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/1316"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div style="--foo:bar"></div> +<div></div> +<script> + test(function() { + let withCustom = getComputedStyle(document.querySelector("div")); + let withoutCustom = getComputedStyle(document.querySelector("div + div")); + assert_equals(withCustom.getPropertyValue("--foo"), "bar", "Should be returned from getPropertyValue"); + assert_equals(withCustom.length, withoutCustom.length + 1, "Should show up in .length"); + assert_equals(withCustom[withCustom.length - 1], "--foo", "Should be after all the non-custom properties"); + }, "Custom properties are included in computed style"); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutability.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutability.html new file mode 100644 index 0000000000..ce5b598ca6 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutability.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSSOM: CSSStyleDeclaration is mutable and immutable in various settings</title> + <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com"> + <link rel="reviewer" title="Ms2ger" href="mailto:ms2ger@gmail.com"> <!-- 2012-06-17 --> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface"> + + <meta name="flags" content="dom"> + + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + + <body> + <div id="log"></div> + <div id="box"></box> + <div id="box2"></box> + <style id="teststyles"> + #box2 { width: 15px; } + </style> + <script> + + test(function(){ + var elem = document.getElementById('box'); + + elem.style.width = '10px'; + assert_equals(elem.style.width, '10px', 'setting via style property'); + elem.style.width = ''; + + elem.style.cssText = 'width: 10px'; + assert_equals(elem.style.width, '10px', 'setting via cssText'); + elem.style.width = ''; + + }, 'HTMLElement\'s CSSStyleDeclaration is mutable') + + + test(function(){ + var elem = document.getElementById('box'); + var style = getComputedStyle(elem); + + assert_throws_dom('NO_MODIFICATION_ALLOWED_ERR', function(){ + style.width = '10px'; + }); + + }, 'getComputedStyle\'s CSSStyleDeclaration is not mutable') + + + test(function(){ + + var style = document.getElementById('teststyles').sheet.cssRules[0].style; + + assert_equals(style.width, '15px', 'width value is correct'); + + style.width = '25px'; + + assert_equals(style.width, '25px', 'width value is mutable'); + + var gCSstyle = getComputedStyle(document.getElementById('box2')); + + assert_equals(gCSstyle.width, '25px', 'styleSheet change is live and accesible via getComputedStyle'); + + }, 'StyleSheet\'s CSSStyleDeclaration is mutable'); + + + </script> + + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-001.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-001.html new file mode 100644 index 0000000000..ba4d926b15 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-001.html @@ -0,0 +1,20 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM: CSSStyleDeclaration.setPropertyValue queues a mutation record when changed</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setpropertyvalue"> +<link rel="help" href="https://drafts.csswg.org/cssom/#update-style-attribute-for"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script> + document.documentElement.style.top = "0px"; + + let test = async_test("CSSStyleDeclaration.setPropertyValue queues a mutation record when serialization is changed"); + let m = new MutationObserver(function(r) { + assert_equals(r.length, 1); + test.done(); + }); + + m.observe(document.documentElement, { attributes: true }); + document.documentElement.style.top = "1px"; +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-002.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-002.html new file mode 100644 index 0000000000..38ce249a31 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-002.html @@ -0,0 +1,15 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM: CSSStyleDeclaration.setPropertyValue doesn't queue a mutation record for invalid values</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script> + let test = async_test("CSSStyleDeclaration.setPropertyValue doesn't queue a mutation record when setting invalid values"); + let m = new MutationObserver(test.unreached_func("shouldn't queue a mutation record")); + m.observe(document.documentElement, { attributes: true }); + + document.documentElement.style.width = "-100px"; + requestAnimationFrame(() => test.done()); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-003.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-003.html new file mode 100644 index 0000000000..7a99dfc645 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-003.html @@ -0,0 +1,16 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM: CSSStyleDeclaration.removeProperty doesn't queue a mutation record when not actually removed, invoked from setPropertyValue</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setpropertyvalue"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script> + let test = async_test("CSSStyleDeclaration.removeProperty doesn't queue a mutation record when not actually removed, invoked from setPropertyValue"); + document.documentElement.style.top = "0"; + let m = new MutationObserver(test.unreached_func("shouldn't queue a mutation record")); + m.observe(document.documentElement, { attributes: true }); + + document.documentElement.style.width = ""; + requestAnimationFrame(() => test.done()); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-004.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-004.html new file mode 100644 index 0000000000..55956dfdaa --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-004.html @@ -0,0 +1,16 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM: CSSStyleDeclaration.removeProperty doesn't queue a mutation record when not actually removed</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setpropertyvalue"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script> + let test = async_test("CSSStyleDeclaration.removeProperty doesn't queue a mutation record when not actually removed"); + document.documentElement.style.top = "0"; + let m = new MutationObserver(test.unreached_func("shouldn't queue a mutation record")); + m.observe(document.documentElement, { attributes: true }); + + document.documentElement.style.removeProperty("width"); + requestAnimationFrame(() => test.done()); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-005.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-005.html new file mode 100644 index 0000000000..26574033ce --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-005.html @@ -0,0 +1,16 @@ +<!doctype html> +<html style="color: inherit"> +<meta charset="utf-8"> +<title>CSSOM: CSSStyleDeclaration.setPropertyValue doesn't queue a mutation record for invalid values</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script> + let test = async_test("CSSStyleDeclaration.setPropertyValue doesn't queue a mutation record when setting invalid values"); + let m = new MutationObserver(test.unreached_func("shouldn't queue a mutation record")); + m.observe(document.documentElement, { attributes: true }); + + document.documentElement.style.width = "-100px"; + requestAnimationFrame(() => test.done()); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-properties.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-properties.html new file mode 100644 index 0000000000..12382cbcdc --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-properties.html @@ -0,0 +1,15 @@ +<!doctype html> +<title>CSS Test: CSSStyleDeclaration properties are defined as WebIDL attributes, not using getOwnPropertyNames()</title> +<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> +<link rel="author" href="https://mozilla.org" title="Mozilla"> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-_camel_cased_attribute"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + test(function() { + let declaration = document.documentElement.style; + assert_true(declaration instanceof CSSStyleDeclaration, "Should be a CSStyleDeclaration"); + assert_true("color" in declaration, "Should support the color property"); + assert_false(declaration.hasOwnProperty("color"), "shouldn't have an own property for WebIDL attributes"); + }); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-registered-custom-properties.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-registered-custom-properties.html new file mode 100644 index 0000000000..5aa4ad2532 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-registered-custom-properties.html @@ -0,0 +1,58 @@ +<!DOCTYPE html> +<title>Computed CSSStyleDeclaration includes registered custom properties</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/1316"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @property --non-inherited-length { + syntax: "<length>"; + inherits: false; + initial-value: 0px; + } + @property --inherited-length { + syntax: "<length>"; + inherits: true; + initial-value: 0px; + } + @property --universal-with-initial { + syntax: "*"; + inherits: false; + initial-value: foo; + } + @property --universal-without-initial { + syntax: "*"; + inherits: false; + } + #outer { --non-registered-outer: 1px; } + #inner { --non-registered-inner: 2px; } + #sibling { --universal-without-initial: bar; } +</style> +<div id=outer> + <div id=inner></div> + <div id=sibling></div> +</div> +<script> + const assert_present = (props, name) => assert_not_equals(props.indexOf(name), -1); + const assert_absent = (props, name) => assert_equals(props.indexOf(name), -1); + + test(function() { + let props = Array.from(getComputedStyle(inner)); + assert_present(props, '--non-inherited-length'); + assert_present(props, '--inherited-length'); + assert_present(props, '--non-registered-outer'); + assert_present(props, '--non-registered-inner'); + assert_present(props, '--universal-with-initial'); + assert_absent(props, '--universal-without-initial'); + }, 'Registered custom properties are included in CSSComputedStyleDeclaration'); + + test(function() { + let props = Array.from(getComputedStyle(sibling)); + assert_present(props, '--non-inherited-length'); + assert_present(props, '--inherited-length'); + assert_present(props, '--non-registered-outer'); + assert_present(props, '--universal-with-initial'); + assert_present(props, '--universal-without-initial'); + assert_absent(props, '--non-registered-inner'); + }, 'Only relevant custom properties are included'); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-attr.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-attr.html new file mode 100644 index 0000000000..20837052e3 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-attr.html @@ -0,0 +1,18 @@ +<!doctype html> +<title>CSSOM test: declaration block after setting via CSSOM</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script> +test(function() { + let element = document.createElement("div"); + element.style.setProperty("doesntexist", "0"); + assert_false(element.hasAttribute("style")); +}, "Setting an invalid property via the declaration setter doesn't create a declaration"); +test(function() { + let element = document.createElement("div"); + element.style.setProperty("width", "-100"); + assert_false(element.hasAttribute("style")); +}, "Setting an invalid value via the declaration setter doesn't create a declaration"); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-declarations.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-declarations.html new file mode 100644 index 0000000000..e66466e7a1 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-declarations.html @@ -0,0 +1,160 @@ +<!DOCTYPE html> +<title>CSSOM test: declaration block after setting via CSSOM</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#set-a-css-declaration-value"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<script> + function generateCSSDeclBlock(props) { + let elem = document.createElement("div"); + let cssText = props.map(({name, value, priority}) => { + let longhand = `${name}: ${value}`; + if (priority) { + longhand += "!" + priority; + } + return longhand + ";"; + }).join(" "); + elem.setAttribute("style", cssText); + return elem.style; + } + function compareByName(a, b) { + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + return 0; + } + function checkDeclarationsAnyOrder(block, props, msg) { + let actual = []; + for (let name of block) { + let value = block.getPropertyValue(name); + let priority = block.getPropertyPriority(name); + actual.push({name, value, priority}); + } + actual.sort(compareByName); + let expected = Array.from(props); + expected.sort(compareByName); + assert_object_equals(actual, expected, "Declaration block content should match " + msg); + } + function longhand(name, value, priority="") { + return {name, value, priority}; + } + function* shorthand(name, value, priority="") { + for (let subprop of SUBPROPS[name]) { + yield longhand(subprop, value, priority); + } + } + + const SUBPROPS = { + "margin": ["margin-top", "margin-right", "margin-bottom", "margin-left"], + "padding": ["padding-top", "padding-right", "padding-bottom", "padding-left"], + }; + + test(function() { + let expectedDecls = [ + longhand("top", "1px"), + longhand("bottom", "2px"), + longhand("left", "3px", "important"), + longhand("right", "4px"), + ]; + let block = generateCSSDeclBlock(expectedDecls); + checkDeclarationsAnyOrder(block, expectedDecls, "in initial block"); + + block.setProperty("top", "5px", "important"); + expectedDecls[0] = longhand("top", "5px", "important"); + checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property"); + + block.setProperty("bottom", "2px"); + checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property with identical value"); + + block.setProperty("left", "3px"); + expectedDecls[2].priority = ""; + checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property with different priority"); + + block.setProperty("float", "none"); + expectedDecls.push(longhand("float", "none")); + checkDeclarationsAnyOrder(block, expectedDecls, "after setting non-existing property"); + }, "setProperty with longhand should update only the declaration being set"); + + test(function() { + let expectedDecls = [ + longhand("top", "1px"), + longhand("bottom", "2px"), + longhand("left", "3px", "important"), + longhand("right", "4px"), + ]; + let block = generateCSSDeclBlock(expectedDecls); + checkDeclarationsAnyOrder(block, expectedDecls, "in initial block"); + + block.top = "5px"; + expectedDecls[0] = longhand("top", "5px"); + checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property"); + + block.bottom = "2px"; + checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property with identical value"); + + block.left = "3px"; + expectedDecls[2].priority = ""; + checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property with different priority"); + + block.float = "none"; + expectedDecls.push(longhand("float", "none")); + checkDeclarationsAnyOrder(block, expectedDecls, "after setting non-existing property"); + }, "property setter should update only the declaration being set"); + + test(function() { + let expectedDecls = [ + ...shorthand("margin", "1px"), + longhand("top", "2px"), + ...shorthand("padding", "3px", "important"), + ]; + let block = generateCSSDeclBlock(expectedDecls); + checkDeclarationsAnyOrder(block, expectedDecls, "in initial block"); + + block.setProperty("margin", "4px"); + for (let i = 0; i < 4; i++) { + expectedDecls[i].value = "4px"; + } + checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand"); + + block.setProperty("margin", "4px"); + checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand with identical value"); + + block.setProperty("padding", "3px"); + for (let i = 5; i < 9; i++) { + expectedDecls[i].priority = ""; + } + checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand with different priority"); + + block.setProperty("margin-bottom", "5px", "important"); + expectedDecls[2] = longhand("margin-bottom", "5px", "important"); + checkDeclarationsAnyOrder(block, expectedDecls, "after setting a longhand in an existing shorthand"); + }, "setProperty with shorthand should update only the declarations being set"); + + test(function() { + let expectedDecls = [ + ...shorthand("margin", "1px"), + longhand("top", "2px"), + ...shorthand("padding", "3px", "important"), + ]; + let block = generateCSSDeclBlock(expectedDecls); + checkDeclarationsAnyOrder(block, expectedDecls, "in initial block"); + + block.margin = "4px"; + for (let i = 0; i < 4; i++) { + expectedDecls[i].value = "4px"; + } + checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand"); + + block.margin = "4px"; + checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand with identical value"); + + block.padding = "3px"; + for (let i = 5; i < 9; i++) { + expectedDecls[i].priority = ""; + } + checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand with different priority"); + + block.marginBottom = "5px"; + expectedDecls[2] = longhand("margin-bottom", "5px"); + checkDeclarationsAnyOrder(block, expectedDecls, "after setting a longhand in an existing shorthand"); + }, "longhand property setter should update only the decoarations being set"); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-form-controls.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-form-controls.html new file mode 100644 index 0000000000..ae556ed1cb --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-form-controls.html @@ -0,0 +1,103 @@ +<!doctype html> +<title>CSSOM test: no side effects from setting "height"</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty"> +<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=107380"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<p> +Historically, the Apple Safari web browser has added an "intrinsic margin" to +form controls which do not specify a `height`. It removed this margin following +modification of the `height`. <a +href="https://bugs.webkit.org/show_bug.cgi?id=107380">This non-standard +behavior was identified as a source of confusion for web developers.</a> +</p> + +<script> +function makeElement(tagName) { + var element = document.createElement(tagName); + document.body.appendChild(element); + return element; +} +function makeInputElement(type) { + var element = makeElement('input'); + element.setAttribute('type', type); + return element; +} +function measure(element) { + var computed = getComputedStyle(element); + return [computed.marginTop, computed.marginBottom]; +} + +test(function() { + var element = makeInputElement('text'); + var initial = measure(element); + + element.style.setProperty('height', '12px'); + + assert_array_equals(measure(element), initial); +}, 'text input element'); + +test(function() { + var element = makeInputElement('button'); + var initial = measure(element); + + element.style.setProperty('height', '12px'); + + assert_array_equals(measure(element), initial); +}, 'button input element'); + +test(function() { + var element = makeInputElement('submit'); + var initial = measure(element); + + element.style.setProperty('height', '12px'); + + assert_array_equals(measure(element), initial); +}, 'submit input element'); + +test(function() { + var element = makeInputElement('radio'); + var initial = measure(element); + + element.style.setProperty('height', '12px'); + + assert_array_equals(measure(element), initial); +}, 'radio input element'); + +test(function() { + var element = makeInputElement('checkbox'); + var initial = measure(element); + + element.style.setProperty('height', '12px'); + + assert_array_equals(measure(element), initial); +}, 'checkbox input element'); + +test(function() { + var element = makeElement('textarea'); + var initial = measure(element); + + element.style.setProperty('height', '12px'); + + assert_array_equals(measure(element), initial); +}, 'textarea element'); + +test(function() { + var element = makeElement('select'); + var initial = measure(element); + + element.style.setProperty('height', '12px'); + + assert_array_equals(measure(element), initial); +}, 'select element'); + +test(function() { + var element = makeElement('button') + var initial = measure(element); + + element.style.setProperty('height', '12px'); + + assert_array_equals(measure(element), initial); +}, 'button element'); +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-logical.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-logical.html new file mode 100644 index 0000000000..13d68e9a70 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-logical.html @@ -0,0 +1,73 @@ +<!DOCTYPE html> +<title>CSSOM test: declaration block after setting via CSSOM</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#set-a-css-declaration-value"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<div id="test"></div> +<script> + test(function() { + const PHYSICAL_PROPS = ["padding-top", "padding-right", + "padding-bottom", "padding-left"]; + const LOGICAL_PROPS = ["padding-block-start", "padding-block-end", + "padding-inline-start", "padding-inline-end"]; + let elem = document.getElementById("test"); + let decls = new Map; + + { + let css = [] + let i = 0; + for (let name of [...PHYSICAL_PROPS, ...LOGICAL_PROPS]) { + let value = `${i++}px`; + css.push(`${name}: ${value};`); + decls.set(name, value); + } + elem.setAttribute("style", css.join(" ")); + } + + let style = elem.style; + function indexOfProperty(prop) { + return Array.prototype.indexOf.apply(style, [prop]); + } + function assertPropAfterProps(prop, props, msg) { + let index = indexOfProperty(prop); + for (let p of props) { + assert_less_than(indexOfProperty(p), index, `${prop} should be after ${p} ${msg}`); + } + } + // We are not changing any value in this test, just order. + function assertValueUnchanged() { + for (let [name, value] of decls.entries()) { + assert_equals(style.getPropertyValue(name), value, + `value of ${name} shouldn't be changed`); + } + } + + assertValueUnchanged(); + // Check that logical properties are all after physical properties + // at the beginning. + for (let p of LOGICAL_PROPS) { + assertPropAfterProps(p, PHYSICAL_PROPS, "initially"); + } + + // Try setting a longhand. + style.setProperty("padding-top", "0px"); + assertValueUnchanged(); + // Check that padding-top is after logical properties, but other + // physical properties are still before logical properties. + assertPropAfterProps("padding-top", LOGICAL_PROPS, "after setting padding-top"); + for (let p of LOGICAL_PROPS) { + assertPropAfterProps(p, PHYSICAL_PROPS.filter(prop => prop != "padding-top"), + "after setting padding-top"); + } + + // Try setting a shorthand. + style.setProperty("padding", "0px 1px 2px 3px"); + assertValueUnchanged(); + // Check that all physical properties are now after logical properties. + for (let p of PHYSICAL_PROPS) { + assertPropAfterProps(p, LOGICAL_PROPS, "after setting padding shorthand"); + } + }, "newly set declaration should be after all declarations " + + "in the same logical property group but have different logical kind"); +</script> diff --git a/testing/web-platform/tests/css/cssom/declaration-block-all-crash.html b/testing/web-platform/tests/css/cssom/declaration-block-all-crash.html new file mode 100644 index 0000000000..2d781e5dcf --- /dev/null +++ b/testing/web-platform/tests/css/cssom/declaration-block-all-crash.html @@ -0,0 +1,10 @@ +<!doctype html> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1711189"> +<div></div> +<script> + let div = document.querySelector("div"); + let cs = getComputedStyle(div); + for (let i = 0; i < cs.length; ++i) + div.style.setProperty(cs[i], cs.getPropertyValue(cs[i])); + div.style.cssText +</script> diff --git a/testing/web-platform/tests/css/cssom/escape.html b/testing/web-platform/tests/css/cssom/escape.html new file mode 100644 index 0000000000..895da59c49 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/escape.html @@ -0,0 +1,102 @@ +<!doctype html> +<meta charset=utf-8> +<title>CSS#escape</title> +<link rel=help href=https://drafts.csswg.org/cssom-1/#the-css.escape()-method> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id=log></div> +<script> +test(function() { + assert_equals(CSS.escape.length, 1); + assert_throws_js(TypeError, function() { CSS.escape(); }); +}, "Number of arguments"); + +test(function() { + assert_equals(CSS.escape(true), 'true'); + assert_equals(CSS.escape(false), 'false'); + assert_equals(CSS.escape(null), 'null'); + assert_equals(CSS.escape(''), ''); +}, "String conversion"); + +test(function() { + assert_equals(CSS.escape('\0'), '\uFFFD'); + assert_equals(CSS.escape('a\0'), 'a\uFFFD'); + assert_equals(CSS.escape('\0b'), '\uFFFDb'); + assert_equals(CSS.escape('a\0b'), 'a\uFFFDb'); +}, "Null bytes"); + +test(function() { + assert_equals(CSS.escape('\uFFFD'), '\uFFFD'); + assert_equals(CSS.escape('a\uFFFD'), 'a\uFFFD'); + assert_equals(CSS.escape('\uFFFDb'), '\uFFFDb'); + assert_equals(CSS.escape('a\uFFFDb'), 'a\uFFFDb'); +}, "Replacement character"); + +test(function() { + assert_equals(CSS.escape('0a'), '\\30 a'); + assert_equals(CSS.escape('1a'), '\\31 a'); + assert_equals(CSS.escape('2a'), '\\32 a'); + assert_equals(CSS.escape('3a'), '\\33 a'); + assert_equals(CSS.escape('4a'), '\\34 a'); + assert_equals(CSS.escape('5a'), '\\35 a'); + assert_equals(CSS.escape('6a'), '\\36 a'); + assert_equals(CSS.escape('7a'), '\\37 a'); + assert_equals(CSS.escape('8a'), '\\38 a'); + assert_equals(CSS.escape('9a'), '\\39 a'); +}, "Number prefix"); + +test(function() { + assert_equals(CSS.escape('a0b'), 'a0b'); + assert_equals(CSS.escape('a1b'), 'a1b'); + assert_equals(CSS.escape('a2b'), 'a2b'); + assert_equals(CSS.escape('a3b'), 'a3b'); + assert_equals(CSS.escape('a4b'), 'a4b'); + assert_equals(CSS.escape('a5b'), 'a5b'); + assert_equals(CSS.escape('a6b'), 'a6b'); + assert_equals(CSS.escape('a7b'), 'a7b'); + assert_equals(CSS.escape('a8b'), 'a8b'); + assert_equals(CSS.escape('a9b'), 'a9b'); +}, "Letter number prefix"); + +test(function() { + assert_equals(CSS.escape('-0a'), '-\\30 a'); + assert_equals(CSS.escape('-1a'), '-\\31 a'); + assert_equals(CSS.escape('-2a'), '-\\32 a'); + assert_equals(CSS.escape('-3a'), '-\\33 a'); + assert_equals(CSS.escape('-4a'), '-\\34 a'); + assert_equals(CSS.escape('-5a'), '-\\35 a'); + assert_equals(CSS.escape('-6a'), '-\\36 a'); + assert_equals(CSS.escape('-7a'), '-\\37 a'); + assert_equals(CSS.escape('-8a'), '-\\38 a'); + assert_equals(CSS.escape('-9a'), '-\\39 a'); +}, "Dash number prefix"); + +test(function() { + assert_equals(CSS.escape('--a'), '--a'); +}, "Double dash prefix"); + +test(function() { + assert_equals(CSS.escape('\x01\x02\x1E\x1F'), '\\1 \\2 \\1e \\1f '); + + assert_equals(CSS.escape('\x80\x2D\x5F\xA9'), '\x80\x2D\x5F\xA9'); + assert_equals(CSS.escape('\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F'), '\\7f \x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F'); + assert_equals(CSS.escape('\xA0\xA1\xA2'), '\xA0\xA1\xA2'); + assert_equals(CSS.escape('a0123456789b'), 'a0123456789b'); + assert_equals(CSS.escape('abcdefghijklmnopqrstuvwxyz'), 'abcdefghijklmnopqrstuvwxyz'); + assert_equals(CSS.escape('ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'); + + assert_equals(CSS.escape("hello\\world"), "hello\\\\world", "Backslashes get backslash-escaped"); + assert_equals(CSS.escape("hello\u{1234}world"), "hello\u{1234}world", "Code points greater than U+0080 are preserved"); + assert_equals(CSS.escape("-"), "\\-", "CSS.escape: Single dash escaped"); + + assert_equals(CSS.escape('\x20\x21\x78\x79'), '\\ \\!xy'); +}, "Various tests"); + +test(function() { + // astral symbol (U+1D306 TETRAGRAM FOR CENTRE) + assert_equals(CSS.escape('\uD834\uDF06'), '\uD834\uDF06'); + // lone surrogates + assert_equals(CSS.escape('\uDF06'), '\uDF06'); + assert_equals(CSS.escape('\uD834'), '\uD834'); +}, "Surrogates"); +</script> diff --git a/testing/web-platform/tests/css/cssom/flex-serialization.html b/testing/web-platform/tests/css/cssom/flex-serialization.html new file mode 100644 index 0000000000..350877c3ce --- /dev/null +++ b/testing/web-platform/tests/css/cssom/flex-serialization.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>CSSOM - Flex serialization</title> + <link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-value"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style> + div { flex: initial; } + div { flex: 0; } + div { flex: initial; flex-basis: initial; flex-shrink: initial; } + div { flex: initial; flex-shrink: 0; } + div { flex: initial; flex-basis: 0; flex-shrink: 2;} + </style> + + <script> + var styleSheet = document.styleSheets[0] + test(function () { + assert_equals(styleSheet.cssRules[0].style.cssText, "flex: initial;") + }, "Single value flex with CSS-wide keyword should serialize correctly.") + test(function () { + assert_in_array(styleSheet.cssRules[1].style.cssText, ["flex: 0px;", "flex: 0 1 0px;"]) + }, "Single value flex with non-CSS-wide value should serialize correctly.") + test(function () { + assert_equals(styleSheet.cssRules[2].style.cssText, "flex: initial;") + }, "Multiple values flex with CSS-wide keyword should serialize correctly.") + test(function () { + assert_equals(styleSheet.cssRules[3].style.cssText, "flex-grow: initial; flex-basis: initial; flex-shrink: 0;") + }, "Multiple values flex with CSS-wide keywords and non-CSS-wide value should serialize correctly.") + test(function () { + assert_equals(styleSheet.cssRules[4].style.cssText, "flex-grow: initial; flex-basis: 0px; flex-shrink: 2;") + }, "Multiple values flex with CSS-wide and two non-CSS-wide-keyword values should serialize correctly.") + </script> +</head> +</html> diff --git a/testing/web-platform/tests/css/cssom/font-family-serialization-001.html b/testing/web-platform/tests/css/cssom/font-family-serialization-001.html new file mode 100644 index 0000000000..436ce7f5c1 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/font-family-serialization-001.html @@ -0,0 +1,44 @@ +<!DOCTYPE HTML> +<html> +<meta charset="utf-8"> +<title>Serialization of font-family</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block"> +<link rel="help" href="https://drafts.csswg.org/css-fonts-4/#font-family-prop"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/css-fonts/support/font-family-keywords.js"></script> +<div id="target"></div> +<script> + function SetFontFamilyAndSerialize(fontFamilyValue) { + var target = document.getElementById('target'); + target.setAttribute("style", `font-family: ${fontFamilyValue}`); + return window.getComputedStyle(target).getPropertyValue('font-family'); + } + test(function() { + kGenericFontFamilyKeywords.forEach(keyword => { + assert_equals(SetFontFamilyAndSerialize(keyword), keyword); + }); + }, "Serialization of <generic-family>"); + + test(function() { + kGenericFontFamilyKeywords.forEach(keyword => { + var quoted_keyword = `"${keyword}"`; + assert_equals(SetFontFamilyAndSerialize(quoted_keyword), quoted_keyword); + }); + }, "Serialization of quoted \"<generic-family>\""); + + test(function() { + kGenericFontFamilyKeywords.forEach(keyword => { + var prefixed_keyword = `-webkit-${keyword}`; + assert_equals(SetFontFamilyAndSerialize(prefixed_keyword), prefixed_keyword); + }); + }, "Serialization of prefixed -webkit-<generic-family>"); + + test(function() { + kNonGenericFontFamilyKeywords.forEach(keyword => { + assert_equals(SetFontFamilyAndSerialize(keyword), keyword); + }); + }, `Serialization of ${kNonGenericFontFamilyKeywords}`); + +</script> +</html> diff --git a/testing/web-platform/tests/css/cssom/font-shorthand-serialization.html b/testing/web-platform/tests/css/cssom/font-shorthand-serialization.html new file mode 100644 index 0000000000..29082f83ea --- /dev/null +++ b/testing/web-platform/tests/css/cssom/font-shorthand-serialization.html @@ -0,0 +1,17 @@ +<!doctype html> +<html> +<meta charset="utf-8"> +<title>Serialization of font shorthand</title> +<link rel="help" href="https://drafts.csswg.org/cssom-1/#serialize-a-css-declaration-block"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<div id="target" style="font: 10px/1 Ahem;"></div> +<script> + test(function() { + var target = document.getElementById('target'); + assert_equals(target.style.cssText, 'font: 10px / 1 Ahem;'); + assert_equals(target.style.font, '10px / 1 Ahem'); + }, "The font shorthand should be serialized just like any other shorthand."); +</script> +</html> diff --git a/testing/web-platform/tests/css/cssom/font-variant-shorthand-serialization.html b/testing/web-platform/tests/css/cssom/font-variant-shorthand-serialization.html new file mode 100644 index 0000000000..0e4651cdc0 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/font-variant-shorthand-serialization.html @@ -0,0 +1,131 @@ +<!doctype html> +<html> +<meta charset="utf-8"> +<title>Serialization of font-variant shorthand</title> +<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m"> +<link rel="help" href="https://drafts.csswg.org/cssom-1/#serialize-a-css-declaration-block"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<div id="target"></div> +<script> + const cssWideKeywords = ["initial", "inherit", "unset", "revert", "revert-layer"]; + function test_serialization_set(expected) { + for (const [property, value] of Object.entries(expected)) { + if (!CSS.supports(`${property}: initial`)) + continue; + assert_equals(target.style[property], value, `${property} was set`); + } + } + function setWithValue(value) { + return { + "font-variant-ligatures": value, + "font-variant-caps": value, + "font-variant-alternates": value, + "font-variant-numeric": value, + "font-variant-east-asian": value, + "font-variant-position": value, + "font-variant-emoji": value, + "font-variant": value + }; + } + const emptySet = setWithValue(""); + const normalSet = setWithValue("normal"); + const nonDefaultValues = { + "font-variant-ligatures": "common-ligatures discretionary-ligatures", + "font-variant-caps": "small-caps", + "font-variant-alternates": "historical-forms", + "font-variant-numeric": "oldstyle-nums stacked-fractions", + "font-variant-east-asian": "ruby", + "font-variant-position": "sub", + "font-variant-emoji": "emoji", + }; + test(function(t) { + target.style.fontVariant = "normal"; + t.add_cleanup(() => target.removeAttribute("style")); + + test_serialization_set(normalSet); + }, "font-variant: normal serialization"); + + test(function(t) { + target.style.fontVariant = "normal"; + target.style.fontVariantLigatures = "none"; + t.add_cleanup(() => target.removeAttribute("style")); + + const expected = Object.assign({}, normalSet); + expected["font-variant-ligatures"] = "none"; + expected["font-variant"] = "none"; + + test_serialization_set(expected); + }, "font-variant: none serialization"); + + test(function(t) { + t.add_cleanup(() => target.removeAttribute("style")); + for (const [property, value] of Object.entries(nonDefaultValues)) { + if (property == "font-variant-ligatures" || !CSS.supports(`${property}: initial`)) + continue; + target.style.fontVariant = "normal"; + target.style.fontVariantLigatures = "none"; + target.style[property] = value; + + const expected = Object.assign({}, normalSet); + expected["font-variant-ligatures"] = "none"; + expected["font-variant"] = ""; + expected[property] = value; + + test_serialization_set(expected); + target.removeAttribute("style"); + } + }, "font-variant-ligatures: none serialization with non-default value for another longhand"); + + test(function(t) { + t.add_cleanup(() => target.removeAttribute("style")); + + for (const [property, value] of Object.entries(nonDefaultValues)) { + if (!CSS.supports(`${property}: initial`)) + continue; + target.style.fontVariant = "normal"; + target.style[property] = value; + + const expected = Object.assign({}, normalSet); + expected[property] = value; + expected["font-variant"] = value; + test_serialization_set(expected); + + target.removeAttribute("style"); + } + }, "font-variant: normal with non-default longhands"); + + test(function(t) { + t.add_cleanup(() => target.removeAttribute("style")); + for (const keyword of cssWideKeywords) { + target.style.fontVariant = "normal"; + target.style.fontVariantLigatures = keyword; + + const expected = Object.assign({}, normalSet); + expected["font-variant-ligatures"] = keyword; + expected["font-variant"] = ""; + test_serialization_set(expected); + target.removeAttribute("style"); + } + }, "CSS-wide keyword in one longhand"); + + test(function(t) { + t.add_cleanup(() => target.removeAttribute("style")); + + for (const keyword of cssWideKeywords) { + target.style.fontVariant = keyword; + test_serialization_set(setWithValue(keyword)); + target.removeAttribute("style"); + } + }, "CSS-wide keyword in shorthand"); + + test(function(t) { + target.style.fontVariant = "normal"; + target.style.font = "menu"; + t.add_cleanup(() => target.removeAttribute("style")); + + test_serialization_set(emptySet); + }, "font: menu serialization"); +</script> +</html> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-animations-replaced-into-ib-split.html b/testing/web-platform/tests/css/cssom/getComputedStyle-animations-replaced-into-ib-split.html new file mode 100644 index 0000000000..47198803a0 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-animations-replaced-into-ib-split.html @@ -0,0 +1,34 @@ +<!doctype html> +<title>getComputedStyle() returns the right style for animating nodes that have been just inserted into the document, and that have an ancestor whose layout tree was recreated (like an IB-split)</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1585882"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<style> + @keyframes my-animation { + from { color: green; } + to { color: green; } + } + div { + color: red; + animation: my-animation 1s infinite linear paused; + } +</style> +<span> + <div></div> +</span> +<script> +test(() => { + let oldDiv = document.querySelector("div"); + window.unused = oldDiv.getBoundingClientRect(); // update layout + + assert_equals(getComputedStyle(oldDiv).color, "rgb(0, 128, 0)", "Should take color from the animation"); + + let newDiv = document.createElement("div"); + oldDiv.replaceWith(newDiv); + + assert_equals(getComputedStyle(newDiv).color, "rgb(0, 128, 0)", "Should take color from the animation (just inserted into the document)"); +}, "getComputedStyle() should return animation styles for nodes just inserted into the document, even if they're in an IB-split"); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-detached-subtree.html b/testing/web-platform/tests/css/cssom/getComputedStyle-detached-subtree.html new file mode 100644 index 0000000000..211d512b28 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-detached-subtree.html @@ -0,0 +1,49 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM: getComputedStyle returns no style for elements not in the tree</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id="host"> + <div id="non-slotted"> + <div id="non-slotted-descendant"></div> + </div> +</div> +<iframe srcdoc="<html></html>" style="display: none"></iframe> +<script> +function testNoComputedStyle(element, description, global) { + test(function() { + assert_true(!!element); + let style = (global ? global : window).getComputedStyle(element); + assert_true(!!style); + // Note that we avoid assert_equals below to get a stable failure message. + assert_true(style.length == 0); + assert_equals(style.color, ""); + }, `getComputedStyle returns no style for ${description}`); +} + +let detached = document.createElement('div'); +testNoComputedStyle(detached, "detached element"); + +testNoComputedStyle(document.querySelector('iframe').contentDocument.documentElement, + "element in non-rendered iframe (display: none)"); + +testNoComputedStyle(document.querySelector('iframe').contentDocument.documentElement, + "element in non-rendered iframe (display: none) from iframe's window", + document.querySelector('iframe').contentWindow); + +host.attachShadow({ mode: "open" }); +testNoComputedStyle(document.getElementById('non-slotted'), + "element outside the flat tree"); + +testNoComputedStyle(document.getElementById('non-slotted-descendant'), + "descendant outside the flat tree"); + +let shadowRoot = detached.attachShadow({ mode: "open" }); +shadowRoot.innerHTML = ` + <div id="detached-shadow-tree-descendant"></div> +`; +testNoComputedStyle(shadowRoot.getElementById('detached-shadow-tree-descendant'), + "shadow tree outside of flattened tree"); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-001.html b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-001.html new file mode 100644 index 0000000000..5a356f15a4 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-001.html @@ -0,0 +1,39 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM: getComputedStyle gets invalidated for display: none elements (inheritance)</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=186882"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<style> +#undisplayed, #host { + display: none; + color: red; +} +</style> +<div id="undisplayed"><div id="child"></div></div> +<div id="host"></div> +<script> + test(function() { + let undisplayed_style = getComputedStyle(undisplayed); + let undisplayed_child_style = getComputedStyle(child); + assert_equals(undisplayed_style.color, "rgb(255, 0, 0)"); + assert_equals(undisplayed_child_style.color, "rgb(255, 0, 0)"); + undisplayed.style.color = "green"; + assert_equals(undisplayed_style.color, "rgb(0, 128, 0)"); + assert_equals(undisplayed_child_style.color, "rgb(0, 128, 0)"); + }, "getComputedStyle gets invalidated in display: none subtrees due to inherited changes to an ancestor"); + test(function() { + host.attachShadow({ mode: 'open' }).innerHTML = ` + <div></div> + `; + let host_style = getComputedStyle(host); + let shadow_style = getComputedStyle(host.shadowRoot.firstElementChild); + assert_equals(host_style.color, "rgb(255, 0, 0)"); + assert_equals(shadow_style.color, "rgb(255, 0, 0)"); + host.style.color = "green"; + assert_equals(host_style.color, "rgb(0, 128, 0)"); + assert_equals(shadow_style.color, "rgb(0, 128, 0)"); + }, "getComputedStyle gets invalidated in display: none subtrees due to inherited changes to an ancestor shadow host"); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-002.html b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-002.html new file mode 100644 index 0000000000..12fb7fc3f8 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-002.html @@ -0,0 +1,34 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM: getComputedStyle gets invalidated for display: none elements (rules)</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=186882"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<style> +#undisplayed { + display: none; + color: red; +} +.sibling + #undisplayed { + color: green; +} + +.sibling + #undisplayed > div { + color: yellow; +} +</style> +<div id="helper"></div> +<div id="undisplayed"><div id="child"></div></div> +<script> + test(function() { + let undisplayed_style = getComputedStyle(undisplayed); + let undisplayed_child_style = getComputedStyle(child); + assert_equals(undisplayed_style.color, "rgb(255, 0, 0)"); + assert_equals(undisplayed_child_style.color, "rgb(255, 0, 0)"); + helper.classList.add("sibling"); + assert_equals(undisplayed_style.color, "rgb(0, 128, 0)"); + assert_equals(undisplayed_child_style.color, "rgb(255, 255, 0)"); + }, "getComputedStyle gets invalidated in display: none subtrees due to rule matching changes"); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-003.html b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-003.html new file mode 100644 index 0000000000..7ea4505588 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-003.html @@ -0,0 +1,26 @@ +<!doctype html> +<title>style is invalidated properly as a result of attribute changes in display: none subtrees</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<style> +div { + color: red +} +.foo { + color: lime; +} +</style> +<div style="display: none"> + <div id="undisplayed"></div> +</div> +<script> + test(function() { + let undisplayed_style = getComputedStyle(undisplayed); + assert_equals(undisplayed_style.color, "rgb(255, 0, 0)"); + undisplayed.classList.add("foo"); + assert_equals(undisplayed_style.color, "rgb(0, 255, 0)"); + }, "getComputedStyle gets invalidated in display: none subtrees due to attribute mutations"); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-dynamic-subdoc.html b/testing/web-platform/tests/css/cssom/getComputedStyle-dynamic-subdoc.html new file mode 100644 index 0000000000..aa49dc33ef --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-dynamic-subdoc.html @@ -0,0 +1,33 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM: getComputedStyle cross-doc properly reflects media query changes</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<iframe id="frm" style="width: 100px; height: 100px"></iframe> +<script> +test(function() { + let frm = document.getElementById('frm'); + let frmDoc = frm.contentWindow.document; + frmDoc.open(); + frmDoc.write('<style>body { color: red } @media all and (min-width: 101px) { body { color: green } }</style><body>Should be green</body>'); + frmDoc.close(); + + document.body.offsetTop; + + assert_equals( + getComputedStyle(frmDoc.body).color, + "rgb(255, 0, 0)", + "Initial color should be red" + ); + + frm.style.width = "200px"; + + assert_equals( + getComputedStyle(frmDoc.body).color, + "rgb(0, 128, 0)", + "style should have been updated to account for media query changes" + ); +}, "getComputedStyle cross-doc properly reflects media query changes"); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-getter-v-properties.tentative.html b/testing/web-platform/tests/css/cssom/getComputedStyle-getter-v-properties.tentative.html new file mode 100644 index 0000000000..e61241e570 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-getter-v-properties.tentative.html @@ -0,0 +1,34 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSStyleDeclaration index getter v. attributes</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2529"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="testElement"></div> +<script> + +/* per spec, the indexed getter gives all supported longhand properties, whereas + attributes are created for all supported properties; this implies the indexed + getter gives a subset of the attributes */ + +const decl = window.getComputedStyle(document.getElementById("testElement")); +const declItems = Array.from(decl).sort(); + +const shorthands = ['border-top', 'border-right', 'border-bottom', 'border-left', 'border', 'font']; +const non_shorthands = ['margin-top', 'font-size', 'max-width', 'width']; + +for (const prop of shorthands) { + test(() => { + assert_true(prop in decl, "getComputedStyle attribute"); + assert_false(declItems.includes(prop), "getComputedStyle indexed getter"); + }, prop); +} + +for (const prop of non_shorthands) { + test(() => { + assert_true(prop in decl, "getComputedStyle attribute"); + assert_true(declItems.includes(prop), "getComputedStyle indexed getter"); + }, prop); +} +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute.html new file mode 100644 index 0000000000..fae0a84a7b --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSSOM: resolved values of the inset properties for absolute positioning</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value"> +<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch"> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script type="module"> +import {runTests, containerForAbspos} from "./support/getComputedStyle-insets.js"; +runTests({ + style: "position: absolute", + containingBlockElement: containerForAbspos, + containingBlockArea: "padding", + preservesPercentages: false, + preservesAuto: false, + canStretchAutoSize: true, + staticPositionY: 1 + 2 + 4 + 8, + staticPositionX: 2 + 4 + 8 + 16, +}); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-fixed.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-fixed.html new file mode 100644 index 0000000000..ff80d2fc50 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-fixed.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSSOM: resolved values of the inset properties for fixed positioning</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value"> +<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch"> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script type="module"> +import {runTests, containerForFixed} from "./support/getComputedStyle-insets.js"; +runTests({ + style: "position: fixed", + containingBlockElement: containerForFixed, + containingBlockArea: "padding", + preservesPercentages: false, + preservesAuto: false, + canStretchAutoSize: true, + staticPositionY: 1 + 2 + 4 + 8 + 16 + 32 + 64, + staticPositionX: 2 + 4 + 8 + 16 + 32 + 64 + 128, +}); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-nobox.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-nobox.html new file mode 100644 index 0000000000..236abf17f9 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-nobox.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSSOM: resolved values of the inset properties when the element generates no box</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value"> +<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch"> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script type="module"> +import {runTests} from "./support/getComputedStyle-insets.js"; +runTests({ + style: "display: none", + containingBlockElement: null, + preservesPercentages: true, + preservesAuto: true, + canStretchAutoSize: false, +}); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-relative.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-relative.html new file mode 100644 index 0000000000..1abfc62c01 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-relative.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSSOM: resolved values of the inset properties for relative positioning</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value"> +<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch"> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script type="module"> +import {runTests, containerForInflow} from "./support/getComputedStyle-insets.js"; +runTests({ + style: "position: relative", + containingBlockElement: containerForInflow, + containingBlockArea: "content", + preservesPercentages: false, + preservesAuto: false, + canStretchAutoSize: false, +}); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-static.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-static.html new file mode 100644 index 0000000000..aa7dbee956 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-static.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSSOM: resolved values of the inset properties for static positioning</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value"> +<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch"> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script type="module"> +import {runTests, containerForInflow} from "./support/getComputedStyle-insets.js"; +runTests({ + style: "position: static", + containingBlockElement: containerForInflow, + containingBlockArea: "content", + preservesPercentages: true, + preservesAuto: true, + canStretchAutoSize: false, +}); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky-container-for-abspos.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky-container-for-abspos.html new file mode 100644 index 0000000000..7f5ec30648 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky-container-for-abspos.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSSOM: resolved values of the inset properties for sticky positioning</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value"> +<link rel="help" href="https://drafts.csswg.org/css-position/#sticky-pos"> +<link rel="author" title="Joonghun Park" href="mailto:pjh0718@gmail.com"> +<style> + #container-for-abspos { + height: 200px; + width: 400px; + overflow: hidden; + } +</style> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script type="module"> +import {runTests, containerForAbspos} from "./support/getComputedStyle-insets.js"; +runTests({ + style: "position: sticky;", + containingBlockElement: containerForAbspos, + containingBlockArea: "content", + preservesPercentages: false, + preservesAuto: true, + canStretchAutoSize: false, +}); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky.html new file mode 100644 index 0000000000..6b23fabcb1 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSSOM: resolved values of the inset properties for sticky positioning</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value"> +<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch"> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<style> + #container-for-inflow { overflow: hidden; } +</style> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script type="module"> +import {runTests, containerForInflow} from "./support/getComputedStyle-insets.js"; +runTests({ + style: "position: sticky;", + containingBlockElement: containerForInflow, + containingBlockArea: "content", + preservesPercentages: false, + preservesAuto: true, + canStretchAutoSize: false, +}); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-removed-ib-sibling.html b/testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-removed-ib-sibling.html new file mode 100644 index 0000000000..83482c5f5c --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-removed-ib-sibling.html @@ -0,0 +1,30 @@ +<!doctype html> +<title>getComputedStyle() returns the right style for layout-dependent properties for nodes that have had an IB sibling removed</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1585882"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<style> + div { + width: 100%; + } +</style> +<span> + <div></div> + <div></div> +</span> +<script> +test(() => { + let first = document.querySelector("div"); + let second = document.querySelector("div + div"); + + let oldWidth = getComputedStyle(second).width; + assert_true(oldWidth.indexOf("px") !== -1, "Should return the used value for width"); + + first.remove(); + + assert_equals(getComputedStyle(second).width, oldWidth, "Should return the used value for width (after sibling removal)"); +}, "getComputedStyle() should return the correct used value for nodes that have had an IB-split sibling removed"); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-replaced-into-ib-split.html b/testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-replaced-into-ib-split.html new file mode 100644 index 0000000000..98771cebc7 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-replaced-into-ib-split.html @@ -0,0 +1,30 @@ +<!doctype html> +<title>getComputedStyle() returns the right style for layout-dependent properties for nodes that have been just inserted into the document, and that have an ancestor whose layout tree was recreated (like an IB-split)</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1585882"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<style> + div { + width: 100%; + } +</style> +<span> + <div></div> +</span> +<script> +test(() => { + let oldDiv = document.querySelector("div"); + window.unused = oldDiv.getBoundingClientRect(); // update layout + + let oldWidth = getComputedStyle(oldDiv).width; + assert_true(oldWidth.indexOf("px") !== -1, "Should return the used value for width"); + + let newDiv = document.createElement("div"); + oldDiv.replaceWith(newDiv); + + assert_equals(getComputedStyle(newDiv).width, oldWidth, "Should return the used value for width (just inserted into the document)"); +}, "getComputedStyle() should return used value correctly for nodes just inserted into the document, even if they're in an IB-split"); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-line-height.html b/testing/web-platform/tests/css/cssom/getComputedStyle-line-height.html new file mode 100644 index 0000000000..089a6d8485 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-line-height.html @@ -0,0 +1,23 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Test: line-height resolved value</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3749"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + div { font-size: 16px; } +</style> +<div style="line-height: normal" data-expected="normal"></div> +<div style="line-height: 1" data-expected="16px"></div> +<div style="line-height: 10px" data-expected="10px"></div> +<div style="line-height: 10%" data-expected="1.6px"></div> +<script> +for (const e of document.querySelectorAll("div")) { + const specified = e.style.lineHeight; + test(function() { + const expected = e.getAttribute("data-expected"); + assert_equals(getComputedStyle(e).lineHeight, expected, specified + " should compute to " + expected); + }, "line-height: " + specified); +} +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-logical-enumeration.html b/testing/web-platform/tests/css/cssom/getComputedStyle-logical-enumeration.html new file mode 100644 index 0000000000..02093120f2 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-logical-enumeration.html @@ -0,0 +1,27 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM: getComputedStyle enumeration</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=210695"> +<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1072180"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script> + test(function() { + let longhand = false; + let shorthand = false; + let env = false; + for (let property of getComputedStyle(document.documentElement)) { + if (property == "block-size") + longhand = true; + if (property == "padding-block") + shorthand = true; + if (property == "safe-area-inset-top") + env = true; + } + assert_true(longhand, "Logical longhands should be enumerated in computed style"); + assert_false(shorthand, "Logical shorthands should not be enumerated in computed style"); + assert_false(env, "Environment variables should not be enumerated in computed style"); + }, "Logical properties in enumeration of computed style") +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-property-order.html b/testing/web-platform/tests/css/cssom/getComputedStyle-property-order.html new file mode 100644 index 0000000000..f52700eb94 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-property-order.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSSOM: getComputedStyle property order</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<meta name="assert" content="This test checks that in a computed style, properties are sorted in lexicographical order." /> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script> +test(function() { + const properties = [...getComputedStyle(document.documentElement)]; + const sorted = properties.slice().sort(function(a,b) { + if (a === b) { + assert_unreached("There shouldn't be repeated properties"); + return 0; + } + // Sort vendor-prefixed properties after standard ones + if (a.startsWith("-") != b.startsWith("-")) { + return b.startsWith("-") ? -1 : 1; + } + // Sort with lexicographical order + return a < b ? -1 : 1; + }); + assert_array_equals(properties, sorted); +}, "Computed style properties should be sorted lexicographically"); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-pseudo.html b/testing/web-platform/tests/css/cssom/getComputedStyle-pseudo.html new file mode 100644 index 0000000000..40af5b0818 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-pseudo.html @@ -0,0 +1,153 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM: Correct resolution of resolved value for display-affected pseudo-elements</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle"> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<style> +#test { width: 100px; } + +#contents { + display: contents; + border: 10px solid red; +} + +#test::before, +#test::after, +#contents::before, +#contents::after, +#flex::before, +#flex::after { + content: " "; + width: 50%; + height: 10px; + display: block; +} +#none { + display: none; +} +#none::before, +#none::after { + content: "Foo"; +} +#flex { + display: flex; +} +#flex-no-pseudo { + display: flex; +} +#contents-pseudos::before, +#contents-pseudos::after { + display: contents; + content: "foo"; + position: absolute; +} +#contents-pseudos-dynamic::before, +#contents-pseudos-dynamic::after { + display: block; + content: "foo"; + position: absolute; +} +#contents-pseudos-dynamic.contents::before, +#contents-pseudos-dynamic.contents::after { + display: contents; +} +</style> +<div id="test"> + <div id="contents"></div> + <div id="none"></div> + <div id="flex"></div> + <div id="flex-no-pseudo"></div> + <div id="contents-pseudos"></div> + <div id="contents-pseudos-dynamic"></div> +</div> +<script> +test(function() { + var div = document.getElementById('test'); + [":before", ":after"].forEach(function(pseudo) { + assert_equals(getComputedStyle(div, pseudo).width, "50px"); + }); +}, "Resolution of width is correct for ::before and ::after pseudo-elements"); +test(function() { + var contents = document.getElementById('contents'); + [":before", ":after"].forEach(function(pseudo) { + assert_equals(getComputedStyle(contents, pseudo).width, "50px"); + }); +}, "Resolution of width is correct for ::before and ::after pseudo-elements of display: contents elements"); +test(function() { + var has_no_pseudos = document.body; + has_no_pseudos.style.position = "relative"; + [":before", ":after"].forEach(function(pseudo) { + assert_equals(getComputedStyle(has_no_pseudos, pseudo).position, "static", + "Nonexistent " + pseudo + " pseudo-element shouldn't claim to have " + + "the same style as the originating element"); + assert_equals(getComputedStyle(has_no_pseudos, pseudo).width, "auto", + "Nonexistent " + pseudo + " pseudo-element shouldn't claim to have " + + "definite size"); + }); +}, "Resolution of nonexistent pseudo-element styles"); +test(function() { + var none = document.getElementById('none'); + [":before", ":after"].forEach(function(pseudo) { + assert_equals(getComputedStyle(none, pseudo).content, "\"Foo\"", + "Pseudo-styles of display: none elements should be correct"); + }); +}, "Resolution of pseudo-element styles in display: none elements"); +test(function() { + var flex = document.getElementById('flex'); + [":before", ":after"].forEach(function(pseudo) { + assert_equals(getComputedStyle(flex, pseudo).display, "block", + "Pseudo-styles of display: flex elements should get blockified"); + }); +}, "Item-based blockification of pseudo-elements"); +test(function() { + var flexNoPseudo = document.getElementById('flex-no-pseudo'); + [":before", ":after"].forEach(function(pseudo) { + assert_equals(getComputedStyle(flexNoPseudo, pseudo).display, "block", + "Pseudo-styles of display: flex elements should get blockified"); + }); +}, "Item-based blockification of nonexistent pseudo-elements"); +test(function() { + var contentsPseudos = document.getElementById('contents-pseudos'); + [":before", ":after"].forEach(function(pseudo) { + assert_equals(getComputedStyle(contentsPseudos, pseudo).display, "contents", + "display: contents in " + pseudo + " should get reflected on CSSOM"); + assert_equals(getComputedStyle(contentsPseudos, pseudo).width, "auto", + pseudo + " with display: contents should have no box"); + assert_equals(getComputedStyle(contentsPseudos, pseudo).position, "absolute", + "display: contents in " + pseudo + " should reflect other non-inherited properties in CSSOM"); + }); +}, "display: contents on pseudo-elements"); +test(function() { + var contentsPseudosDynamic = document.getElementById('contents-pseudos-dynamic'); + [":before", ":after"].forEach(function(pseudo) { + assert_equals(getComputedStyle(contentsPseudosDynamic, pseudo).display, "block", + "Check that display for " + pseudo + " is block before change"); + }); + contentsPseudosDynamic.className = "contents"; + [":before", ":after"].forEach(function(pseudo) { + assert_equals(getComputedStyle(contentsPseudosDynamic, pseudo).display, "contents", + "display: contents in " + pseudo + " should get reflected on CSSOM"); + assert_equals(getComputedStyle(contentsPseudosDynamic, pseudo).width, "auto", + pseudo + " with display: contents should have no box"); + assert_equals(getComputedStyle(contentsPseudosDynamic, pseudo).position, "absolute", + "display: contents in " + pseudo + " should reflect other non-inherited properties in CSSOM"); + }); +}, "Dynamically change to display: contents on pseudo-elements"); +test(function() { + var div = document.getElementById('test'); + // Note that these assertions deliberately avoid assert_[not_]equals to + // avoid gCS().length in the failure output. + assert_true( + getComputedStyle(div, "totallynotapseudo").length != 0, + "Should return the element's style for unknown pseudo-elements that don't start with a colon"); + assert_true( + getComputedStyle(div, "::totallynotapseudo").length == 0, + "Should return an empty style for unknown pseudo-elements starting with double-colon"); + assert_true( + getComputedStyle(div, ":totallynotapseudo").length == 0, + "Should return an empty style for unknown pseudo-elements starting with colon"); +}, "Unknown pseudo-elements"); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-resolved-colors.html b/testing/web-platform/tests/css/cssom/getComputedStyle-resolved-colors.html new file mode 100644 index 0000000000..da4fddbc24 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-resolved-colors.html @@ -0,0 +1,42 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM: resolved values of certain color properties are used values</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #target { + background-color: Menu; + border: 1px solid Menu; + box-shadow: 1px 1px Menu; + caret-color: Menu; + color: Menu; + outline-color: Menu; + } +</style> +<div id="target"></div> +<script> + const properties_to_test = [ + "background-color", + "border-block-end-color", + "border-block-start-color", + "border-bottom-color", + "border-inline-end-color", + "border-inline-start-color", + "border-left-color", + "border-right-color", + "border-top-color", + "box-shadow", + "caret-color", + "color", + "outline-color", + ]; + + for (const property of properties_to_test) { + test(function() { + let resolved_value = + window.getComputedStyle(document.getElementById("target")).getPropertyValue(property); + assert_regexp_match(resolved_value, /^rgb[a]?\(/); + }, "The resolved value for '" + property + "' is the used value"); + } +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-resolved-min-max-clamping.html b/testing/web-platform/tests/css/cssom/getComputedStyle-resolved-min-max-clamping.html new file mode 100644 index 0000000000..e630377c33 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-resolved-min-max-clamping.html @@ -0,0 +1,38 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM: resolved values of the width and height when the element generates no box or are a non-replaced inline aren't clamped by min-width / max-width</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<span id="non-replaced-inline"></span> +<div id="display-none" style="display: none"></div> +<div id="display-contents" style="display: contents"></div> +<script> +test(function() { + for (const e of document.querySelectorAll("[id]")) { + const cs = getComputedStyle(e); + for (const prop of ["width", "height"]) { + e.style.setProperty("min-" + prop, "10px"); + e.style.setProperty("max-" + prop, "50px"); + + e.style.setProperty(prop, "10%"); + assert_equals(cs[prop], "10%", `${e.id}: ${prop} with percentages returns percentages`); + + e.style.setProperty(prop, "15px"); + assert_equals(cs[prop], "15px", `${e.id}: ${prop} with value in range returns computed value`); + + e.style.setProperty(prop, "1px"); + assert_equals(cs[prop], "1px", `${e.id}: ${prop} with value out of range isn't clamped by min-${prop}`); + + e.style.setProperty(prop, "60px"); + assert_equals(cs[prop], "60px", `${e.id}: ${prop} with value out of range isn't clamped by max-${prop}`); + + e.style.removeProperty(prop); + e.style.removeProperty("min-" + prop); + e.style.removeProperty("max-" + prop); + } + } +}, "Resolved value of width / height when there's no used value isn't clamped by min/max properties"); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-sticky-pos-percent.html b/testing/web-platform/tests/css/cssom/getComputedStyle-sticky-pos-percent.html new file mode 100644 index 0000000000..12ad5e8965 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-sticky-pos-percent.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<title>CSS Test: resolve top percentage value against proper box</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3115" /> +<link rel="help" href="https://drafts.csswg.org/css-position/#sticky-pos" /> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values" /> +<meta name="assert" content="Test that the sticky percentage insets are +resolved against the right ancestor, i.e. the nearest scrollport." /> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div style="height: 500px; background: purple; overflow: hidden;"> + <div style="height: 400px; background: yellow;"> + <div id="target" style="height: 100px; position: sticky; left: 0; top: 50%; background: blue;"> + </div> + </div> +</div> +<script> +test(() => { + assert_equals(getComputedStyle(target).top, '250px'); +}, "Sticky element's top property percentage value should be resolved against the div with overflow: hidden"); +</script> diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-width-scroll.tentative.html b/testing/web-platform/tests/css/cssom/getComputedStyle-width-scroll.tentative.html new file mode 100644 index 0000000000..8f629c7016 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/getComputedStyle-width-scroll.tentative.html @@ -0,0 +1,26 @@ +<!doctype html> +<title>getComputedStyle() round-trips in presence of scrollbars.</title> +<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> +<link rel="author" href="https://mozilla.org" title="Mozilla"> +<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div style="width: 100px; height: 100px; overflow: scroll"></div> +<script> +// NOTE(emilio): This is a .tentative.html because the spec is not clear on +// what the used value of `width` or `height` is, but I think this behavior +// should be uncontroversial. +test(function() { + let e = document.querySelector("div"); + let cs = getComputedStyle(e); + + let originalWidth = cs.width; + let originalHeight = cs.height; + + e.style.width = originalWidth; + e.style.height = originalHeight; + + assert_equals(cs.width, originalWidth, "width round-trips"); + assert_equals(cs.height, originalHeight, "height round-trips"); +}, "getComputedStyle() round-trips in presence of scrollbars") +</script> diff --git a/testing/web-platform/tests/css/cssom/historical.html b/testing/web-platform/tests/css/cssom/historical.html new file mode 100644 index 0000000000..44f3f6c8f1 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/historical.html @@ -0,0 +1,58 @@ +<!DOCTYPE html> +<title>Historical features</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#changes-from-5-december-2013"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id=log></div> +<script> +[ + "selectedStyleSheetSet", + "lastStyleSheetSet", + "preferredStyleSheetSet", + "styleSheetSets", + "enableStyleSheetsForSet", + "selectedStylesheetSet", + "preferredStylesheetSet", +].forEach(function(name) { + test(function() { + assert_false(name in document); + + var doc = document.implementation.createDocument(null, null, null); + assert_false(name in doc); + }, "Historical Document member: " + name); +}); + +[ + "Rect", + "RGBColor", + "CSSValue", + "CSSPrimitiveValue", + "CSSValueList", +].forEach(function(name) { + test(function() { + assert_false(name in window); + }, "Historical interface: " + name); +}); + +[ + "getPropertyCSSValue", + "setPropertyValue", + "setPropertyPriority", +].forEach(function(name) { + test(function() { + assert_false(name in document.body.style); + }, "Historical CSSStyleDeclaration member: " + name); +}); + +[ + "cascadedStyle", + "defaultStyle", + "rawComputedStyle", + "usedStyle", +].forEach(function(name) { + test(function() { + assert_false(name in document.body); + assert_false(name in document.createElement("test")); + }, "Historical Element member: " + name); +}); +</script> diff --git a/testing/web-platform/tests/css/cssom/idlharness.html b/testing/web-platform/tests/css/cssom/idlharness.html new file mode 100644 index 0000000000..e27629c03b --- /dev/null +++ b/testing/web-platform/tests/css/cssom/idlharness.html @@ -0,0 +1,77 @@ +<!doctype html> +<meta charset=utf-8> +<!-- WARNING: These tests are preliminary and probably partly incorrect. --> +<title>CSSOM automated IDL tests</title> +<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/cssom-1/#idl-index"> +<meta name="timeout" content="long"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/resources/WebIDLParser.js></script> +<script src=/resources/idlharness.js></script> +<!-- +Provide some objects to test. +Use a non-empty style attribute to get a non-empty CSSStyleDeclaration. +--> +<style id="styleElement" style="z-index: 0;"> +@import url("data:text/css,"); +@namespace x "y"; +@page { @top-left {} } +@media all {} +#test { color: green; } +</style> + +<svg id="svgElement" style="height: 0;"></svg> + +<iframe id="xmlssPiIframe" src="support/xmlss-pi.xhtml" style="display: none;"></iframe> + +<h1>CSSOM IDL tests</h1> +<div id=log></div> + +<script> +'use strict'; + +const waitForLoad = new Promise(r => { addEventListener('load', r); }); + +idl_test( + ['cssom'], + ['SVG', 'uievents', 'html', 'dom', 'mathml-core'], + async idlArray => { + idlArray.add_objects({ + Document: ['document', 'new Document()'], + StyleSheetList: ['document.styleSheets'], + CSSStyleSheet: ['sheet'], + MediaList: ['sheet.media'], + CSSRuleList: ['sheet.cssRules'], + CSSImportRule: ['sheet.cssRules[0]'], + CSSNamespaceRule: ['sheet.cssRules[1]'], + CSSPageRule: ['sheet.cssRules[2]'], + CSSMarginRule: ['sheet.cssRules[2].cssRules[0]'], + CSSMediaRule: ['sheet.cssRules[3]'], + CSSStyleRule: ['sheet.cssRules[4]'], + CSSStyleDeclaration: [ + 'sheet.cssRules[4].style', // CSSStyleRule + 'sheet.cssRules[2].style', // CSSPageRule + 'sheet.cssRules[2].cssRules[0].style', // CSSMarginRule + 'style_element.style', // ElementCSSInlineStyle for HTMLElement + 'svg_element.style', // ElementCSSInlineStyle for SVGElement + 'getComputedStyle(svg_element)' + ], + ProcessingInstruction: ['xmlss_pi'], + Window: ['window'], + HTMLElement: [ + 'style_element', + 'document.createElement("unknownelement")' + ], + SVGElement: ['svg_element'], + }); + + await waitForLoad; + self.style_element = document.getElementById('styleElement'); + self.sheet = style_element.sheet; + self.svg_element = document.getElementById('svgElement'); + self.xmlss_pi = document.getElementById('xmlssPiIframe').contentDocument.firstChild; + } +); + +</script> diff --git a/testing/web-platform/tests/css/cssom/inline-style-001.html b/testing/web-platform/tests/css/cssom/inline-style-001.html new file mode 100644 index 0000000000..d4bf3b92ab --- /dev/null +++ b/testing/web-platform/tests/css/cssom/inline-style-001.html @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test: Inline CSSStyleDeclaration</title> + <link rel="author" title="Bear Travis" href="mailto:betravis@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#elementcssinlinestyle"> + <meta name="flags" content="dom"> + <meta name="assert" content="Inline CSSStyleDeclaration is properly initialized and can be modified through its interface"> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> + </head> + <body> + <noscript>Test not run - javascript required.</noscript> + <div id="log"></div> + <div id="test" style="margin-left: 5px;"></div> + <script type="text/javascript"> + // Can access CSSStyleDeclaration through style property + test(function() { + var test = document.getElementById("test"); + assert_idl_attribute(test, "style"); + declaration = test.style; + }, "CSSStyleDeclaration_accessible"); + + // initial property values are correct + test(function() { + assert_equals(declaration.cssText, "margin-left: 5px;"); + assert_equals(declaration.getPropertyValue("margin-left"), "5px"); + }, "read"); + + // setting cssText adds new properties + // setting cssText removes existing properties + // properties set through cssText are reflected in the computed style + test(function() { + declaration.cssText = "margin-left: 10px; padding-left: 10px;"; + assert_equals(declaration.cssText, "margin-left: 10px; padding-left: 10px;"); + assert_equals(declaration.length, 2); + assert_equals(declaration.item(0), "margin-left"); + assert_equals(declaration.item(1), "padding-left"); + assert_equals(declaration.getPropertyValue("margin-left"), "10px"); + assert_equals(declaration.getPropertyValue("padding-left"), "10px"); + + var computedStyle = window.getComputedStyle(document.getElementById("test")); + assert_equals(computedStyle.getPropertyValue("margin-left"), "10px"); + assert_equals(computedStyle.getPropertyValue("padding-left"), "10px"); + }, "csstext_write"); + + // setProperty adds new properties + // properties set through setProperty are reflected in the computed style + test(function() { + while(declaration.length > 0) + declaration.removeProperty(declaration.item(0)); + declaration.setProperty("margin-left", "15px"); + declaration.setProperty("padding-left", "15px"); + + assert_equals(declaration.length, 2); + assert_equals(declaration.item(0), "margin-left"); + assert_equals(declaration.item(1), "padding-left"); + assert_equals(declaration.getPropertyValue("margin-left"), "15px"); + assert_equals(declaration.getPropertyValue("padding-left"), "15px"); + + var computedStyle = window.getComputedStyle(document.getElementById("test")); + assert_equals(computedStyle.getPropertyValue("margin-left"), "15px"); + assert_equals(computedStyle.getPropertyValue("padding-left"), "15px"); + }, "property_write"); + + // shorthand property is expanded + test(function() { + while(declaration.length > 0) + declaration.removeProperty(declaration.item(0)); + declaration.cssText = "margin: 20px"; + assert_equals(declaration.getPropertyValue("margin-top"), "20px"); + assert_equals(declaration.getPropertyValue("margin-right"), "20px"); + assert_equals(declaration.getPropertyValue("margin-bottom"), "20px"); + assert_equals(declaration.getPropertyValue("margin-left"), "20px"); + }, "shorthand_properties"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/insertRule-across-context.html b/testing/web-platform/tests/css/cssom/insertRule-across-context.html new file mode 100644 index 0000000000..a8327e31ac --- /dev/null +++ b/testing/web-platform/tests/css/cssom/insertRule-across-context.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Test: CSSOM StyleSheet insertRule across context</title> +<link rel="author" title="Kagami Sascha Rosylight" href="mailto:saschanaz@outlook.com"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/cssom/"> +<script src="/resources/testharness.js" type="text/javascript"></script> +<script src="/resources/testharnessreport.js" type="text/javascript"></script> + +<iframe id="iframe"></iframe> +<script> + function createSheet() { + const tempStyleElement = iframe.contentDocument.createElement('style'); + iframe.contentDocument.head.append(tempStyleElement); + + const tempStyle = tempStyleElement.sheet; + tempStyleElement.remove(); + return tempStyle; + } + + test(() => { + const sheet = createSheet(); + sheet.insertRule(".kaoru {}"); + assert_equals(sheet.rules[0].constructor, iframe.contentWindow.CSSStyleRule); + }, "The constructor of inserted rule object must be from iframe"); + + test(() => { + const sheet = new iframe.contentWindow.CSSStyleSheet(); + sheet.insertRule(".kaoru {}"); + assert_equals(sheet.rules[0].constructor, iframe.contentWindow.CSSStyleRule); + }, "The constructor of inserted rule object must be from iframe for new CSSStyleSheet()"); +</script> diff --git a/testing/web-platform/tests/css/cssom/insertRule-charset-no-index.html b/testing/web-platform/tests/css/cssom/insertRule-charset-no-index.html new file mode 100644 index 0000000000..16f2358dc2 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/insertRule-charset-no-index.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSS Test: CSSOM StyleSheet insertRule with charset and omitted second argument</title> + <link rel="author" title="Sendil Kumar N" href="mailto:sendilkumarn.opensource@gmail.com"> + <link rel="help" href="https://drafts.csswg.org/cssom/"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface"> + <meta name="flags" content="dom"> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> + <link rel="stylesheet" type="text/css" href="support/import-charset.css" id="linkElement" > +</head> +<body> +<div id="log"></div> +<script type="text/javascript"> + var sheet = document.getElementById("linkElement").sheet; + test(function() { + assert_equals(sheet.cssRules.length, 0); + sheet.insertRule("p { color: green; }"); + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules.item(0).cssText, "p { color: green; }"); + }, "insertRule with charset and omitted index argument"); + + test(function() { + assert_equals(sheet.cssRules.length, 1); + sheet.insertRule("p { color: yellow; }", undefined); + assert_equals(sheet.cssRules.length, 2); + assert_equals(sheet.cssRules.item(0).cssText, "p { color: yellow; }"); + }, "insertRule with charset and undefined index argument"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/insertRule-from-script-ref.html b/testing/web-platform/tests/css/cssom/insertRule-from-script-ref.html new file mode 100644 index 0000000000..2fa45526a6 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/insertRule-from-script-ref.html @@ -0,0 +1,6 @@ +<!doctype html> +<head> + <meta charset="utf-8"> + <title>insertrule @import test reference</title> + <link rel="stylesheet" href="support/black.css"> +</head>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/cssom/insertRule-from-script.html b/testing/web-platform/tests/css/cssom/insertRule-from-script.html new file mode 100644 index 0000000000..c364afc282 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/insertRule-from-script.html @@ -0,0 +1,12 @@ +<!doctype html> +<head> + <meta charset="utf-8"> + <title>insertrule @import test</title> + <link rel="help" href="https://drafts.csswg.org/cssom/"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface"> + <link rel="match" href="insertRule-from-script-ref.html"> + <style></style> +</head> +<body> + <script>document.styleSheets[0].insertRule("@import url(\"support/black.css\");");</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/cssom/insertRule-import-no-index.html b/testing/web-platform/tests/css/cssom/insertRule-import-no-index.html new file mode 100644 index 0000000000..45481e71f4 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/insertRule-import-no-index.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSS Test: CSSOM StyleSheet insertRule with import and omitted second argument</title> + <link rel="author" title="Sendil Kumar N" href="mailto:sendilkumarn.opensource@gmail.com"> + <link rel="help" href="https://drafts.csswg.org/cssom/"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface"> + <meta name="flags" content="dom"> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> + <style id="styleElement"> + @import url("support/a-green.css"); + </style> +</head> +<body> +<div id="log"></div> +<script type="text/javascript"> + var sheet = document.getElementById("styleElement").sheet; + test(function() { + assert_equals(sheet.cssRules.length, 1); + assert_throws_dom("HierarchyRequestError", function() { sheet.insertRule("p { color: green; }"); }); + assert_equals(sheet.cssRules.length, 1); + }, "insertRule with import and omitted index argument"); + + test(function() { + assert_equals(sheet.cssRules.length, 1); + assert_throws_dom("HierarchyRequestError", function() { sheet.insertRule("p { color: yellow; }", undefined); }); + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules.item(0).cssText, "@import url(\"support/a-green.css\");"); + }, "insertRule with import and undefined index argument"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/insertRule-namespace-no-index.html b/testing/web-platform/tests/css/cssom/insertRule-namespace-no-index.html new file mode 100644 index 0000000000..3265159736 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/insertRule-namespace-no-index.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSS Test: CSSOM StyleSheet insertRule with namespace and omitted second argument</title> + <link rel="author" title="Sendil Kumar N" href="mailto:sendilkumarn.opensource@gmail.com"> + <link rel="help" href="https://drafts.csswg.org/cssom/"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface"> + <meta name="flags" content="dom"> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> + <style id="styleElement"> + @namespace svg url(http://servo); + @namespace url(http://servo1); + @namespace svg url("http://servo2"); + </style> +</head> +<body> +<div id="log"></div> +<script type="text/javascript"> + var sheet = document.getElementById("styleElement").sheet; + test(function() { + assert_equals(sheet.cssRules.length, 3); + assert_throws_dom("HierarchyRequestError", function() { sheet.insertRule("p { color: green; }"); }); + assert_equals(sheet.cssRules.length, 3); + }, "insertRule with namespace and omitted index argument"); + + test(function() { + assert_equals(sheet.cssRules.length, 3); + assert_throws_dom("HierarchyRequestError", function() { sheet.insertRule("p { color: yellow; }", undefined); }); + assert_equals(sheet.cssRules.length, 3); + }, "insertRule with namespace and undefined index argument"); + + test(function() { + assert_equals(sheet.cssRules.length, 3); + sheet.insertRule("@import url(\"support/a-green.css\");"); + assert_equals(sheet.cssRules.length, 4); + }, "insertRule with namespace and omitted index argument should insert import"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/insertRule-no-index.html b/testing/web-platform/tests/css/cssom/insertRule-no-index.html new file mode 100644 index 0000000000..b4370bd080 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/insertRule-no-index.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSS Test: CSSOM StyleSheet insertRule omitted second argument</title> + <link rel="author" title="Sendil Kumar N" href="mailto:sendilkumarn.opensource@gmail.com"> + <link rel="help" href="https://drafts.csswg.org/cssom/"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface"> + <meta name="flags" content="dom"> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> + <style id="styleElement"> + /* An initial style rule to test where the new rule is inserted relative to this one */ + nosuchelement { color: red; } + </style> +</head> +<body> +<div id="log"></div> +<script type="text/javascript"> + var sheet = document.getElementById("styleElement").sheet; + test(function() { + assert_equals(sheet.cssRules.length, 1); + sheet.insertRule("p { color: green; }"); + assert_equals(sheet.cssRules.length, 2); + assert_equals(sheet.cssRules.item(0).cssText, "p { color: green; }"); + }, "insertRule with omitted index argument"); + + test(function() { + assert_equals(sheet.cssRules.length, 2); + sheet.insertRule("p { color: yellow; }", undefined); + assert_equals(sheet.cssRules.length, 3); + assert_equals(sheet.cssRules.item(0).cssText, "p { color: yellow; }"); + }, "insertRule with undefined index argument"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/insertRule-syntax-error-01.html b/testing/web-platform/tests/css/cssom/insertRule-syntax-error-01.html new file mode 100644 index 0000000000..4461a9a943 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/insertRule-syntax-error-01.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/cssom-1/#insert-a-css-rule"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +p { color: green; } +</style> +<p id="test">This text should be green.</p> +<script> + test(function() { + assert_throws_dom("SyntaxError", () => document.styleSheets[0].insertRule("p { color: red; } garbage", 1)); + assert_equals(getComputedStyle(document.getElementById("test")).color, "rgb(0, 128, 0)"); + }, "A syntax error in insertRule should throw and not affect the style of the page"); +</script> diff --git a/testing/web-platform/tests/css/cssom/medialist-dynamic-001-ref.html b/testing/web-platform/tests/css/cssom/medialist-dynamic-001-ref.html new file mode 100644 index 0000000000..9715b5acb8 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/medialist-dynamic-001-ref.html @@ -0,0 +1,5 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Test Reference</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +Should not be red. diff --git a/testing/web-platform/tests/css/cssom/medialist-dynamic-001.html b/testing/web-platform/tests/css/cssom/medialist-dynamic-001.html new file mode 100644 index 0000000000..ab9c134568 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/medialist-dynamic-001.html @@ -0,0 +1,12 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Test: Dynamic changes to the stylesheet media attributes via CSSOM get reflected</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="match" href="medialist-dynamic-001-ref.html"> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-medialist-mediatext"> +<style media="all">* { color: red }</style> +Should not be red. +<script> + document.body.offsetTop; + document.styleSheets[0].media.mediaText = "not all"; +</script> diff --git a/testing/web-platform/tests/css/cssom/medialist-interfaces-001.html b/testing/web-platform/tests/css/cssom/medialist-interfaces-001.html new file mode 100644 index 0000000000..f436177fb8 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/medialist-interfaces-001.html @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test: CSSOM Media Query List Serialization</title> + <link rel="author" title="Ben Sheldon" href="mailto:ben@codeforamerica.org"> + <link rel="author" title="Chapman Shoop" href="mailto:chapman.shoop@gmail.com"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-medialist-interface"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#serializing-media-queries"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#serialize-a-media-query-list"> + <meta name="flags" content="dom"> + <meta name="assert" content="MediaLists are serialized according to the specification"> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> + </head> + <body> + <noscript>Test not run - javascript required.</noscript> + <div id="log"></div> + <script type="text/javascript"> + + var styleElement; + var styleSheet; + var mediaList; + + // Setup + function setup() { + styleElement = document.getElementById("styleElement"); + + if (styleElement) { + // teardown + document.getElementsByTagName("head")[0].removeChild(styleElement); + styleElement = undefined; + styleSheet = undefined; + mediaList = undefined; + } + + styleElement = document.createElement("style"); + styleElement.id = "styleElement"; + styleElement.type = "text/css"; + styleElement.media = "all"; + document.getElementsByTagName("head")[0].appendChild(styleElement); + styleSheet = styleElement.sheet; + mediaList = styleSheet.media; + } + + // MediaList.mediaText equals the 'media' value of the initial 'style' element. + test(function() { + setup(); + + assert_equals(mediaList.mediaText, "all"); + + }, "mediatest_medialist_serialize_element"); + + // To serialize a comma-separated list concatenate all items of the list in list order while separating them by \",\" (U+002C), followed by a space (U+0020). + test(function() { + setup(); + + mediaList.appendMedium('screen'); + assert_equals(mediaList.mediaText, "all, screen"); + + }, "mediatest_medialist_serialize_comma"); + + // If the media query list is empty return the empty string. + test(function() { + setup(); + + mediaList.deleteMedium('all'); + assert_equals(mediaList.mediaText, ""); + + }, "mediatest_medialist_serialize_empty"); + + // Each media query should be sorted in the same order as they appear in the list of media queries. + test(function() { + setup(); + + mediaList.appendMedium('screen'); + mediaList.appendMedium('print'); + assert_equals(mediaList.mediaText, "all, screen, print"); + + }, "mediatest_medialist_serialize_order"); + + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/medialist-interfaces-002.html b/testing/web-platform/tests/css/cssom/medialist-interfaces-002.html new file mode 100644 index 0000000000..0d2ca2e9c7 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/medialist-interfaces-002.html @@ -0,0 +1,71 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSS Test: CSSOM MediaList Interfaces</title> + <link rel="author" title="Chapman Shoop" href="mailto:chapman.shoop@gmail.com"> + <link rel="reviewer" title="Ms2ger" href="mailto:ms2ger@gmail.com"> <!-- 2012-06-17 --> + <link rel="help" href="https://drafts.csswg.org/cssom/#the-medialist-interface"> + <meta name="flags" content="dom"> + <meta name="assert" content="MediaList object has deleteMedium method and it functions properly."> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> +</head> + +<body> + <noscript>Test not run - javascript required.</noscript> + <div id="log"></div> + + <script type="text/javascript"> + function setup() { + // Clean out any old style element + var old_style_el = document.getElementById('test_style'); + if (old_style_el) { + document.head.removeChild(old_style_el); + } + + // Create a fresh style element and return its media attribute + var style_el = document.createElement('style'); + style_el.setAttribute('id', 'test_style'); + document.head.appendChild(style_el); + return style_el.sheet.media; + } + </script> + + <script type="text/javascript"> + + // MediaList.deleteMedium called without argument throws error. + test(function() { + media_list = setup(); + assert_throws_js(TypeError, function() { media_list.deleteMedium(); }); + }, "deleteMedium_called_without_argument"); + + // MediaList.deleteMedium removes correct medium and updates corresponding properties. + test(function() { + media_list = setup(); + + media_list.appendMedium("screen"); + media_list.appendMedium("all"); + + media_list.deleteMedium("screen"); + + assert_equals(media_list.length, 1); + assert_equals(media_list.item(0), "all"); + assert_equals(media_list.mediaText, "all"); + }, "deleteMedium_removes_correct_medium"); + + // MediaList.deleteMedium doesn't modify MediaList when medium is not found. + test(function() { + media_list = setup(); + + media_list.appendMedium("all"); + + assert_throws_dom("NotFoundError", () => media_list.deleteMedium("screen")); + + assert_equals(media_list.length, 1); + assert_equals(media_list.item(0), "all"); + assert_equals(media_list.mediaText, "all"); + }, "deleteMedium_no_matching_medium_to_remove"); + + </script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/medialist-interfaces-004.html b/testing/web-platform/tests/css/cssom/medialist-interfaces-004.html new file mode 100644 index 0000000000..7cbea37cba --- /dev/null +++ b/testing/web-platform/tests/css/cssom/medialist-interfaces-004.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSS Test: CSSOM MediaList Interfaces</title> + <link rel="author" title="Chapman Shoop" href="mailto:chapman.shoop@gmail.com"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-medialist-interface"> + <meta name="flags" content="dom"> + <meta name="assert" content="MediaList object has appendMedium method and it functions properly."> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> +</head> + +<body> + <noscript>Test not run - javascript required.</noscript> + <div id="log"></div> + + <script type="text/javascript"> + function setup() { + // Clean out any old style element + var old_style_el = document.getElementById('test_style'); + if (old_style_el) { + document.head.removeChild(old_style_el); + } + + // Create a fresh style element and return its media attribute + var style_el = document.createElement('style'); + style_el.setAttribute('id', 'test_style'); + document.head.appendChild(style_el); + return style_el.sheet.media; + } + </script> + + <script type="text/javascript"> + + // MediaList.appendMedium correctly adds medium to empty MediaList. + test(function() { + media_list = setup(); + + media_list.appendMedium("all"); + + assert_equals(media_list.length, 1); + assert_equals(media_list.item(0), "all"); + assert_equals(media_list.mediaText, "all"); + }, "appendMedium_correctly_appends_medium_to_empty_MediaList"); + + // MediaList.appendMedium correctly adds medium to a MediaList that already has a medium. + test(function() { + media_list = setup(); + + media_list.appendMedium("screen"); + media_list.appendMedium("all"); + + // The ordering of array items should be different from that of the mediaText element. + // "all" should be appended after "screen" in the array, but "mediaText" should be + // "all, screen" due to lexicographical sorting. + assert_equals(media_list.length, 2); + assert_equals(media_list.item(0), "screen"); + assert_equals(media_list.item(1), "all"); + assert_equals(media_list.mediaText, "screen, all"); + }, "appendMedium_correctly_appends_medium_to_nonempty_MediaList"); + + </script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/mediaquery-sort-dedup.html b/testing/web-platform/tests/css/cssom/mediaquery-sort-dedup.html new file mode 100644 index 0000000000..0ae78d0300 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/mediaquery-sort-dedup.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSSOM test: Media query serialization quirks</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#serializing-media-queries"> +<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> +<link rel="author" href="https://mozilla.org" title="Mozilla"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +test(function() { + assert_equals(matchMedia("(min-width: 10px) and (min-height: 10px)").media, "(min-width: 10px) and (min-height: 10px)"); +} , "Media queries are not sorted lexicographically"); +test(function() { + assert_equals(matchMedia("(color) and (color)").media, "(color) and (color)"); +}, "Media queries are not deduplicated"); +</script> diff --git a/testing/web-platform/tests/css/cssom/overflow-serialization.html b/testing/web-platform/tests/css/cssom/overflow-serialization.html new file mode 100644 index 0000000000..2e4bd97d5c --- /dev/null +++ b/testing/web-platform/tests/css/cssom/overflow-serialization.html @@ -0,0 +1,61 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>CSSOM - Overflow shorthand serialization</title> + <link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-value"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style> + div { overflow: inherit; } + div { overflow: hidden; } + div { overflow-x: initial; overflow-y: initial; } + div { overflow-x: scroll; overflow-y: scroll; } + div { overflow-x: scroll; overflow-y: hidden; } + </style> + + <script> + var styleSheet = document.styleSheets[0] + var div = document.createElement('div') + test(function () { + assert_equals(styleSheet.cssRules[0].style.cssText, "overflow: inherit;") + }, "Single value overflow with CSS-wide keyword should serialize correctly.") + test(function () { + assert_equals(styleSheet.cssRules[1].style.cssText, "overflow: hidden;") + }, "Single value overflow with non-CSS-wide keyword should serialize correctly.") + test(function () { + assert_equals(styleSheet.cssRules[2].style.cssText, "overflow: initial;") + }, "Overflow-x/y longhands with same CSS-wide keyword should serialize correctly.") + test(function () { + assert_equals(styleSheet.cssRules[3].style.cssText, "overflow: scroll;") + }, "Overflow-x/y longhands with same non-CSS-wide keyword should serialize correctly.") + test(function () { + assert_equals(styleSheet.cssRules[4].style.cssText, "overflow: scroll hidden;") + }, "Overflow-x/y longhands with different keywords should serialize correctly.") + test(function () { + div.style.overflow = "inherit" + assert_equals(div.style.overflow, "inherit") + }, "Single value overflow on element with CSS-wide keyword should serialize correctly.") + test(function () { + div.style.overflow = "hidden" + assert_equals(div.style.overflow, "hidden") + }, "Single value overflow on element with non-CSS-wide keyword should serialize correctly.") + test(function () { + div.style.overflow = "" + div.style.overflowX = "initial" + div.style.overflowY = "initial" + assert_equals(div.style.overflow, "initial") + }, "Overflow-x/y longhands on element with same CSS-wide keyword should serialize correctly.") + test(function () { + div.style.overflowX = "scroll" + div.style.overflowY = "scroll" + assert_equals(div.style.overflow, "scroll") + }, "Overflow-x/y longhands on element with same non-CSS-wide keyword should serialize correctly.") + test(function () { + div.style.overflowX = "scroll" + div.style.overflowY = "hidden" + assert_equals(div.style.overflow, "scroll hidden") + }, "Overflow-x/y longhands on element with different keywords should serialize correctly.") + </script> +</head> +</html> diff --git a/testing/web-platform/tests/css/cssom/page-descriptors.html b/testing/web-platform/tests/css/cssom/page-descriptors.html new file mode 100644 index 0000000000..5cf0a6a703 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/page-descriptors.html @@ -0,0 +1,40 @@ +<!DOCTYPE HTML> +<html> +<head> + <link rel="author" title="Mozilla" href="https://mozilla.org"> + <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface"> + <title>Page descriptors shouldn't be exposed to CSS Style declarations</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<div id="target"></div> +<script> +'use strict'; + +let element = document.getElementById("target"); +let computedStyle = window.getComputedStyle(element); +let style = element.style; + +test(t => { + assert_equals(computedStyle.size, undefined, + "computed style should not have size property"); + assert_equals(computedStyle.getPropertyValue("size"), "", + "computed style getPropertyValue(\"size\") should be empty"); + + assert_equals(style.size, undefined, + "style should not have size property"); + assert_equals(style.getPropertyValue("size"), "", + "style getPropertyValue(\"size\") should be empty"); + for(const val of ["initial", "auto", "100px"]){ + style.setProperty("size", val); + assert_false(CSS.supports("size", val)); + assert_equals(style.size, undefined, + "style should not have size property after assigning size=" + val); + assert_equals(style.getPropertyValue("size"), "", + "style getPropertyValue(\"size\") should be empty after assigning size=" + val); + } +}); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/preferred-stylesheet-order.html b/testing/web-platform/tests/css/cssom/preferred-stylesheet-order.html new file mode 100644 index 0000000000..dc990131ab --- /dev/null +++ b/testing/web-platform/tests/css/cssom/preferred-stylesheet-order.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/cssom/#add-a-css-style-sheet"> +<link rel="help" href="https://drafts.csswg.org/cssom/#create-a-css-style-sheet"> +<link rel="help" href="https://html.spec.whatwg.org/#update-a-style-block"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="t1">This text should be green</div> +<script> +function createStyleElement(text, title) { + var elm = document.createElement("style"); + elm.setAttribute("title", title); + elm.appendChild(document.createTextNode(text)); + return elm; +} + +test(function() { + document.head.appendChild(createStyleElement("#t1 {color:green}", "preferred")); + document.head.appendChild(createStyleElement("#t1 {color:red}", "notpreferred")); + + assert_equals(getComputedStyle(t1).color, "rgb(0, 128, 0)"); +}, "Preferred stylesheet where insertion order is reversed tree order"); +</script> diff --git a/testing/web-platform/tests/css/cssom/preferred-stylesheet-reversed-order.html b/testing/web-platform/tests/css/cssom/preferred-stylesheet-reversed-order.html new file mode 100644 index 0000000000..ff3a8b0901 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/preferred-stylesheet-reversed-order.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/cssom/#add-a-css-style-sheet"> +<link rel="help" href="https://drafts.csswg.org/cssom/#create-a-css-style-sheet"> +<link rel="help" href="https://html.spec.whatwg.org/#update-a-style-block"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="t1">This text should be green</div> +<script> +function createStyleElement(text, title) { + var elm = document.createElement("style"); + elm.setAttribute("title", title); + elm.appendChild(document.createTextNode(text)); + return elm; +} + +test(function() { + document.head.insertBefore(createStyleElement("#t1 {color:green}", "preferred"), document.head.firstChild); + document.head.insertBefore(createStyleElement("#t1 {color:red}", "notpreferred"), document.head.firstChild); + + assert_equals(getComputedStyle(t1).color, "rgb(0, 128, 0)"); +}, "Preferred stylesheet where insertion order is tree order"); +</script> diff --git a/testing/web-platform/tests/css/cssom/property-accessors.html b/testing/web-platform/tests/css/cssom/property-accessors.html new file mode 100644 index 0000000000..87c9df0b32 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/property-accessors.html @@ -0,0 +1,64 @@ +<!doctype html> +<html> +<meta charset="utf-8"> +<title>Accessing properties via CSSStyleDeclaration</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface"> +<!-- this is really a crash test, but let's claim it's a testharness test --> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +@font-face {} +</style> +<div id="testElement"></div> +<script> +// Goal here is to test a cross-section of prefixed, properties, and descriptors. +const properties = [ + "-apple-color-filter", "-apple-pay-button-style", "-epub-writing-mode", + "-webkit-flex", "gap", "grid-gap", "overscroll-behavior", + "src", "unicode-range", +]; + +const el = document.getElementById("testElement"); +const decls = [window.getComputedStyle(el), el.style, document.styleSheets[0].cssRules[0].style]; + +for (const prop of properties) { + test(() => { + for (const decl of decls) { + let _; + + _ = decl[prop]; + try { + decl[prop] = "nonsense"; + } catch { + assert_equals(decl, decls[0]); + } + + _ = decl.cssText; + try { + decl.cssText = `${prop}: nonsense`; + } catch { + assert_equals(decl, decls[0]); + } + + _ = decl.getPropertyValue(prop); + _ = decl.getPropertyPriority(prop); + + if ("getPropertyCSSValue" in decl) { + _ = decl.getPropertyCSSValue(prop); + } + + try { + decl.setProperty(prop, "nonsense", ""); + } catch { + assert_equals(decl, decls[0]); + } + + try { + decl.removeProperty(prop); + } catch { + assert_equals(decl, decls[0]); + } + } + }, prop); +} +</script> diff --git a/testing/web-platform/tests/css/cssom/removerule-invalidation-crash.html b/testing/web-platform/tests/css/cssom/removerule-invalidation-crash.html new file mode 100644 index 0000000000..a83f43d5fa --- /dev/null +++ b/testing/web-platform/tests/css/cssom/removerule-invalidation-crash.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>CSSOM - removeRule doesn't assert when removing a particular set of rules</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#cssstylesheet"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1659718"> +<style> +@media all { + * { color: red; } + + #foobar { color: blue } +} +</style> +<script> + document.documentElement.getBoundingClientRect(); + document.styleSheets[0].deleteRule(0); +</script> diff --git a/testing/web-platform/tests/css/cssom/rule-restrictions.html b/testing/web-platform/tests/css/cssom/rule-restrictions.html new file mode 100644 index 0000000000..ce2dee37a0 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/rule-restrictions.html @@ -0,0 +1,51 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM Should correctly honor property restrictions</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1665816"> +<link rel="help" href="https://drafts.csswg.org/css-page-3/#conform-partial"> +<link rel="help" href="https://drafts.csswg.org/css-animations/#keyframes"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @page { + margin-top: 10px; + transform: scale(1); + } + + @keyframes foo { + from { + margin-top: 10px; + animation-name: none; + } + } +</style> +<script> +test(function() { + let rule = document.styleSheets[0].cssRules[0]; + assert_equals(rule.type, CSSRule.PAGE_RULE, "Should be a page rule"); + assert_equals(rule.style.length, 1, "Transform doesn't quite apply to pages"); + assert_equals(rule.style.marginTop, "10px", "Should have a margin-top declaration"); + rule.style.setProperty("transform", "scale(1)"); + assert_equals(rule.style.getPropertyValue("transform"), "", "Shouldn't have been able to set the transform property via setProperty"); + assert_equals(rule.style.length, 1, "Shouldn't have been able to set transform via setProperty"); + rule.style.cssText = "margin-bottom: 10px; transform: scale(1);"; + assert_equals(rule.style.length, 1, "Should not have been able to set transform via cssText"); + assert_equals(rule.style.marginBottom, "10px", "Should have a margin-bottom declaration"); +}, "@page"); + +test(function() { + let rule = document.styleSheets[0].cssRules[1].cssRules[0]; + assert_equals(rule.type, CSSRule.KEYFRAME_RULE, "Should be a keyframe rule"); + assert_equals(rule.style.length, 1, "animation-name doesn't apply to keyframes"); + assert_equals(rule.style.marginTop, "10px", "Should have a margin-top declaration"); + rule.style.setProperty("animation-name", "none"); + assert_equals(rule.style.getPropertyValue("animation-name"), "", "Shouldn't have been able to set the animation-nameproperty via setProperty"); + assert_equals(rule.style.length, 1, "Shouldn't have been able to set animation-name via setProperty"); + rule.style.cssText = "margin-bottom: 10px; animation-name: none;"; + assert_equals(rule.style.length, 1, "Should not have been able to set animation-name via cssText"); + assert_equals(rule.style.marginBottom, "10px", "Should have a margin-bottom declaration"); + +}, "@keyframe"); +</script> diff --git a/testing/web-platform/tests/css/cssom/selectorSerialize.html b/testing/web-platform/tests/css/cssom/selectorSerialize.html new file mode 100644 index 0000000000..f3a402f5f0 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/selectorSerialize.html @@ -0,0 +1,121 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSSOM Test: test serialized selector which is only one simple selector in the sequence of simple selectors</title> + <link rel="author" title="T.Nishitani" href="mailto:lequinharay@gmail.com"> + <link rel="reviewer" title="L. David Baron" href="https://dbaron.org/"> + <link rel="help" href="https://drafts.csswg.org/cssom-1/#serializing-selectors"> + <meta name="flags" content="dom"> + <meta charset="utf-8"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style id="teststyles"> + </style> + </head> + <body> + <div id="log"></div> + <script> + function assert_selector_serializes_to(source, expected_result) { + var style_element = document.getElementById("teststyles"); + style_element.firstChild.data = source + "{ font-size: 1em; }"; + var sheet = style_element.sheet; + assert_equals(sheet.cssRules[sheet.cssRules.length - 1].selectorText, expected_result); + } + + function run_tests_on_anplusb_selector(source) { + assert_selector_serializes_to(source + '( even )', source + '(2n)'); + assert_selector_serializes_to(source + '( odd )', source + '(2n+1)'); + assert_selector_serializes_to(source + '( +10 )', source + '(10)'); + assert_selector_serializes_to(source + '( -10 )', source + '(-10)'); + assert_selector_serializes_to(source + '( +4n )', source + '(4n)'); + assert_selector_serializes_to(source + '( -3n )', source + '(-3n)'); + assert_selector_serializes_to(source + '( 1n + 5 )', source + '(n+5)'); + assert_selector_serializes_to(source + '( -1n + 5 )', source + '(-n+5)'); + assert_selector_serializes_to(source + '( -1n - 5 )', source + '(-n-5)'); + } + + test(function() { + assert_selector_serializes_to(":nth-child( 3n - 0)", ":nth-child(3n)"); + assert_selector_serializes_to(":nth-child( 1n - 0)", ":nth-child(n)"); + }, ":nth-child serialization produces canonical form"); + + + /* for universal selecter with default namespace */ + test(function(){ + /* this is single universal selector */ + assert_selector_serializes_to('*', '*'); + assert_selector_serializes_to(' * ', '*'); + }, 'single universal selector shows \'*\' when serialized.') + + test(function(){ + assert_selector_serializes_to('div', 'div'); + assert_selector_serializes_to(' div ', 'div'); + }, 'single type (simple) selector in the sequence of simple selectors that is not a universal selector') + + test(function(){ + assert_selector_serializes_to('.class', '.class'); + assert_selector_serializes_to(' .class ', '.class'); + }, 'single class (simple) selector in the sequence of simple selectors that is not a universal selector') + + test(function(){ + assert_selector_serializes_to('#id', '#id'); + assert_selector_serializes_to(' #id ', '#id'); + }, 'single id (simple) selector in the sequence of simple selectors that is not a universal selector') + + test(function(){ + assert_selector_serializes_to(':hover', ':hover'); + assert_selector_serializes_to(' :hover ', ':hover'); + }, 'single pseudo (simple) selector which does not accept arguments in the sequence of simple selectors that is not a universal selector') + + test(function(){ + assert_selector_serializes_to(':lang(ja)', ':lang(ja)'); + assert_selector_serializes_to(':lang( ja )', ':lang(ja)'); + assert_selector_serializes_to(':lang( j\\ a )', ':lang(j\\ a)'); + }, 'single pseudo (simple) selector "lang" which accepts arguments in the sequence of simple selectors that is not a universal selector') + + + test(function(){ + run_tests_on_anplusb_selector(':nth-child'); + }, 'single pseudo (simple) selector "nth-child" which accepts arguments in the sequence of simple selectors that is not a universal selector') + + test(function(){ + run_tests_on_anplusb_selector(':nth-last-child'); + }, 'single pseudo (simple) selector "nth-last-child" which accepts arguments in the sequence of simple selectors that is not a universal selector') + + test(function(){ + run_tests_on_anplusb_selector(':nth-of-type'); + }, 'single pseudo (simple) selector "nth-of-child" which accepts arguments in the sequence of simple selectors that is not a universal selector') + + test(function(){ + run_tests_on_anplusb_selector(':nth-last-of-type'); + }, 'single pseudo (simple) selector ":nth-last-of-type" which accepts arguments in the sequence of simple selectors that is not a universal selector') + + test(function(){ + assert_selector_serializes_to(' :not( abc ) ', ':not(abc)'); + assert_selector_serializes_to(' :not( .head ) ', ':not(.head)'); + assert_selector_serializes_to(' :not( #head ) ', ':not(#head)'); + assert_selector_serializes_to(' :not( :hover ) ', ':not(:hover)'); + }, 'single pseudo (simple) selector ":not" which accepts arguments in the sequence of simple selectors that is not a universal selector') + + var escaped_ns_rule = "@namespace ns\\:odd url(ns);"; + test(function() { + assert_selector_serializes_to("[ns\\:foo]", "[ns\\:foo]"); + }, "escaped character in attribute name"); + test(function() { + assert_selector_serializes_to("[\\30zonk]", "[\\30 zonk]"); + }, "escaped character as code point in attribute name"); + test(function() { + assert_selector_serializes_to("[\\@]", "[\\@]"); + }, "escaped character (@) in attribute name"); + test(function() { + assert_selector_serializes_to("[*|ns\\:foo]", "[*|ns\\:foo]"); + }, "escaped character in attribute name with any namespace"); + test(function() { + assert_selector_serializes_to(escaped_ns_rule + "[ns\\:odd|foo]", "[ns\\:odd|foo]"); + }, "escaped character in attribute prefix"); + test(function() { + assert_selector_serializes_to(escaped_ns_rule + "[ns\\:odd|odd\\:name]", "[ns\\:odd|odd\\:name]"); + }, "escaped character in both attribute prefix and name"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001-ref.html b/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001-ref.html new file mode 100644 index 0000000000..74e5807470 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001-ref.html @@ -0,0 +1,13 @@ +<!doctype html> +<meta charset="utf-8"> +<title>(Ref #1) CSSOM - CSSStyleRule.selectorText Modification Restyle - Reference #1</title> + +<style> +div { + color: green; +} +</style> + +<body> +<div>I should be green.</div> +</body> diff --git a/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001.html b/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001.html new file mode 100644 index 0000000000..43a76d1ef2 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001.html @@ -0,0 +1,22 @@ +<!doctype html> +<meta charset="utf-8"> +<title>(Test #1) CSSOM - CSSStyleRule.selectorText Modification Restyle - Test #1</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext"> +<link rel="match" href="selectorText-modification-restyle-001-ref.html"> + +<style> +@namespace bogus url(http://example.com/bogus); + +bogus|div { + color: green; +} +</style> + +<body> +<div>I should be green.</div> +<script> +// Remove the "bogus" namespace--now it should apply to the div above. +// We also expect to see a restyle. +document.querySelector("style").sheet.cssRules[1].selectorText = "div"; +</script> +</body> diff --git a/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-002.html b/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-002.html new file mode 100644 index 0000000000..a6b37c2cf0 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-002.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<title>CSSOM: Modify selectorText in a shadow tree stylesheet</title> +<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext"> +<link rel="help" href="https://drafts.csswg.org/css-scoping/#selectors"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #container { color: red } + .subtree * { color: pink } +</style> +<div id="container"> + <div id="host"></div> +</div> +<script> + const root = host.attachShadow({mode:"open"}); + root.innerHTML = "<style>nomatch { color: green }</style><div>Green</div>"; + const div = root.querySelector("div"); + + test(() => { + assert_equals(getComputedStyle(div).color, "rgb(255, 0, 0)", "Color should initial be red."); + }, "Check initial color."); + + test(() => { + // The combination of the .subtree and CSSOM selector style invalidations + // caused the Blink implementation to fail an assertion. + container.className = "subtree"; + root.styleSheets[0].cssRules[0].selectorText = "div"; + assert_equals(getComputedStyle(div).color, "rgb(0, 128, 0)", "Color should be green after stylesheet change."); + }, "Check that color changes correctly after shadow stylesheet selector and #container class is changed."); +</script> diff --git a/testing/web-platform/tests/css/cssom/serialization-CSSDeclaration-with-important.html b/testing/web-platform/tests/css/cssom/serialization-CSSDeclaration-with-important.html new file mode 100644 index 0000000000..804885a74a --- /dev/null +++ b/testing/web-platform/tests/css/cssom/serialization-CSSDeclaration-with-important.html @@ -0,0 +1,32 @@ +<!doctype html> +<meta charset=utf-8> +<title>cssom - Serialization of CSS declaration with "important" flag</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-declaration"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id="noWhitespace" style="display: inline !important;"></div> +<div id="whitespace" style="background-color: blue !important; color: red ! important;"></div> +<div id="dinamicallyStyle"></div> +<script> + test(function () { + var css_style = document.querySelector('#noWhitespace').style.cssText; + assert_equals(css_style, "display: inline !important;"); + }, "Inline style declaration without whitespace between '!' and 'important'."); + + test(function () { + var css_style = document.querySelector('#whitespace').style.cssText; + assert_equals(css_style, "background-color: blue !important; color: red !important;"); + }, "Inline style declaration with whitespace between '!' and 'important'."); + + test(function () { + document.querySelector('#dinamicallyStyle').style.cssText = "color: black !important;"; + var css_style = document.querySelector('#dinamicallyStyle').style.cssText; + assert_equals(css_style, "color: black !important;"); + }, "Style set dynamically via cssText without whitespace between '!' and 'important'."); + + test(function () { + document.querySelector('#dinamicallyStyle').style.cssText = "color: black ! important;"; + var css_style = document.querySelector('#dinamicallyStyle').style.cssText; + assert_equals(css_style, "color: black !important;"); + }, "Style set dynamically via cssText with whitespace between '!' and 'important'."); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/cssom/serialize-all-longhands.html b/testing/web-platform/tests/css/cssom/serialize-all-longhands.html new file mode 100644 index 0000000000..95204b6361 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/serialize-all-longhands.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Serialize all longhands</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" /> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue"> +<meta name="assert" content="Checks that all longhands indexed in a CSSStyleDeclaration can be serialized to a non-empty string when set to their initial value."> + +<div id="target"></div> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +function nonSerializableProperties(style) { + const result = []; + assert_greater_than(style.length, 0, "Should have longhands"); + for (let property of style) { + if (!style.getPropertyValue(property)) { + result.push(property); + } + } + return result; +} + +const target = document.getElementById("target"); +target.style.cssText = "all: initial; direction: initial; unicode-bidi: initial;"; + +test(function() { + const props = nonSerializableProperties(target.style); + assert_array_equals(props, []); +}, "Specified style"); + +test(function() { + const props = nonSerializableProperties(getComputedStyle(target)); + assert_array_equals(props, []); +}, "Computed style"); +</script> diff --git a/testing/web-platform/tests/css/cssom/serialize-custom-props.html b/testing/web-platform/tests/css/cssom/serialize-custom-props.html new file mode 100644 index 0000000000..cfe96ff0aa --- /dev/null +++ b/testing/web-platform/tests/css/cssom/serialize-custom-props.html @@ -0,0 +1,69 @@ +<link rel=author title="Tab Atkins-Bittner" href="https://www.xanthir.com/contact/"> +<link rel="help" href="https://drafts.csswg.org/css-values/#calc-range"> +<body><!doctype html> +<title>Serializing Integers Never Uses Scinot</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<link rel=author title="Tab Atkins-Bittner" href="https://www.xanthir.com/contact/"> +<link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-component-value"> +<!-- + Per CSSOM, integers always serialize all their digits out. + They never serialize to scinot, regardless of size, + because that makes them stop being an integer. + This applies to custom properties as well. +--> +<body> + +<script> + +try { +CSS.registerProperty({ + name: "--two", + value: "<integer>", + inherits: true, + initial: -1 +}); +}catch(e){} + +testIntLength(4); +testIntLength(6); +testIntLength(8); +testIntLength(12); +// JS starts serializing with scinot at 22 digits... +testIntLength(25); + +function testIntLength(len) { + let el = document.body; + const val = "1".repeat(len); + test(()=>{ + el.removeAttribute("style"); + const nullVal = getComputedStyle(el).zIndex; + el.style.zIndex=val; + assert_not_equals(getComputedStyle(el).zIndex, nullVal) + }, `z-index can take a ${len}-digit integer`); + + test(()=>{ + el.removeAttribute("style"); + el.style.setProperty("--one", val); + assert_equals(getComputedStyle(el).getPropertyValue("--one"), val); + }, `An unregistered custom prop can take a ${len}-digit integer`); + + test(()=>{ + el.removeAttribute("style"); + el.style.setProperty("--two", val); + assert_equals(getComputedStyle(el).getPropertyValue("--two"), val); + }, `An <integer> custom prop can take a ${len}-digit integer`); + + test(()=>{ + el.removeAttribute("style"); + el.style.zIndex = val; + const standardVal = getComputedStyle(el).zIndex; + el.removeAttribute("style"); + el.style.setProperty("--three", val); + el.style.zIndex = "var(--three)"; + assert_equals(getComputedStyle(el).zIndex, standardVal); + }, `z-index can take a custom property set to a ${len}-digit integer`); +} + +</script> diff --git a/testing/web-platform/tests/css/cssom/serialize-media-rule.html b/testing/web-platform/tests/css/cssom/serialize-media-rule.html new file mode 100644 index 0000000000..90561fdf70 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/serialize-media-rule.html @@ -0,0 +1,185 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>CSSOM - Serialization of CSSMediaRule</title> + <link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-rule"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +function makeSheet(t) { + var style = document.createElement('style'); + document.body.appendChild(style); + t.add_cleanup(function() { + document.body.removeChild(style); + }); + return style.sheet; +} + +test(function(t) { + var sheet = makeSheet(t); + sheet.insertRule('@media {}', 0); + + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules[0].cssText, '@media {\n}'); +}, 'empty media query list'); + +test(function(t) { + var sheet = makeSheet(t); + sheet.insertRule('@media all {}'); + sheet.insertRule('@media print {}'); + sheet.insertRule('@media screen {}'); + sheet.insertRule('@media speech {}'); + + assert_equals(sheet.cssRules.length, 4); + assert_equals(sheet.cssRules[0].cssText, '@media speech {\n}'); + assert_equals(sheet.cssRules[1].cssText, '@media screen {\n}'); + assert_equals(sheet.cssRules[2].cssText, '@media print {\n}'); + assert_equals(sheet.cssRules[3].cssText, '@media all {\n}'); +}, 'type - no features'); + +test(function(t) { + var sheet = makeSheet(t); + sheet.insertRule('@media not all {}'); + sheet.insertRule('@media not print {}'); + sheet.insertRule('@media not screen {}'); + sheet.insertRule('@media not speech {}'); + + assert_equals(sheet.cssRules.length, 4); + assert_equals(sheet.cssRules[0].cssText, '@media not speech {\n}'); + assert_equals(sheet.cssRules[1].cssText, '@media not screen {\n}'); + assert_equals(sheet.cssRules[2].cssText, '@media not print {\n}'); + assert_equals(sheet.cssRules[3].cssText, '@media not all {\n}'); +}, 'type - no features - negation'); + +test(function(t) { + var sheet = makeSheet(t); + sheet.insertRule('@media aLL {}'); + sheet.insertRule('@media pRiNt {}'); + sheet.insertRule('@media screEN {}'); + sheet.insertRule('@media spEech {}'); + + assert_equals(sheet.cssRules.length, 4); + assert_equals(sheet.cssRules[0].cssText, '@media speech {\n}'); + assert_equals(sheet.cssRules[1].cssText, '@media screen {\n}'); + assert_equals(sheet.cssRules[2].cssText, '@media print {\n}'); + assert_equals(sheet.cssRules[3].cssText, '@media all {\n}'); +}, 'type - no features - character case normalization'); + +test(function(t) { + var sheet = makeSheet(t); + sheet.insertRule('@media all and (color) {}'); + + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules[0].cssText, '@media (color) {\n}'); +}, 'type - omission of all'); + +test(function(t) { + var sheet = makeSheet(t); + sheet.insertRule('@media not all and (color) {}'); + + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules[0].cssText, '@media not all and (color) {\n}'); +}, 'type - inclusion of negated all'); + +test(function(t) { + var sheet = makeSheet(t); + sheet.insertRule('@media screen and (Color) {}'); + sheet.insertRule('@media screen and (cOLor) {}'); + + assert_equals(sheet.cssRules.length, 2); + assert_equals(sheet.cssRules[0].cssText, '@media screen and (color) {\n}'); + assert_equals(sheet.cssRules[1].cssText, '@media screen and (color) {\n}'); +}, 'features - character case normalization'); + +/** + * The following test is disabled pending clarification of the intended + * behavior: https://github.com/w3c/csswg-drafts/issues/533 + */ +//test(function(t) { +// var sheet = makeSheet(t); +// sheet.insertRule('@media screen and (color) and (color) {}'); +// +// assert_equals(sheet.cssRules.length, 1); +// assert_equals( +// sheet.cssRules[0].cssText, +// '@media screen and (color) {\n}' +// ); +//}, 'features - de-duplication'); + +test(function(t) { + var sheet = makeSheet(t); + sheet.insertRule('@media print and (max-width: 23px) and (max-width: 45px) {}'); + + assert_equals(sheet.cssRules.length, 1); + assert_equals( + sheet.cssRules[0].cssText, + '@media print and (max-width: 23px) and (max-width: 45px) {\n}' + ); +}, 'features - preservation of overspecified features'); + +test(function(t) { + var sheet = makeSheet(t); + sheet.insertRule('@media screen and (max-width: 0px) and (color) {}'); + sheet.insertRule('@media screen and (color) and (max-width: 0px) {}'); + + assert_equals(sheet.cssRules.length, 2); + assert_equals( + sheet.cssRules[0].cssText, + '@media screen and (color) and (max-width: 0px) {\n}' + ); + assert_equals( + sheet.cssRules[1].cssText, + '@media screen and (max-width: 0px) and (color) {\n}' + ); +}, 'features - no lexicographical sorting'); + +test(function(t) { + var sheet = makeSheet(t); + sheet.insertRule('@media screen and (max-width: 0px), screen and (color) {}'); + + assert_equals(sheet.cssRules.length, 1); + assert_equals( + sheet.cssRules[0].cssText, + '@media screen and (max-width: 0px), screen and (color) {\n}' + ); +}, 'media query list'); + +test(function(t) { + var sheet = makeSheet(t); + sheet.insertRule('@media print {}'); + + assert_equals(sheet.cssRules.length, 1); + sheet.cssRules[0].insertRule('#foo { z-index: 23; float: left; }', 0); + assert_equals( + sheet.cssRules[0].cssText, + [ + '@media print {', + ' #foo { z-index: 23; float: left; }', + '}' + ].join('\n') + ); +}, 'one rule'); + +test(function(t) { + var sheet = makeSheet(t); + sheet.insertRule('@media print {}'); + + assert_equals(sheet.cssRules.length, 1); + sheet.cssRules[0].insertRule('#foo { z-index: 23; float: left; }', 0); + sheet.cssRules[0].insertRule('#bar { float: none; z-index: 45; }', 0); + assert_equals( + sheet.cssRules[0].cssText, + [ + '@media print {', + ' #bar { float: none; z-index: 45; }', + ' #foo { z-index: 23; float: left; }', + '}' + ].join('\n') + ); +}, 'many rules'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/serialize-namespaced-type-selectors.html b/testing/web-platform/tests/css/cssom/serialize-namespaced-type-selectors.html new file mode 100644 index 0000000000..600008c7a9 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/serialize-namespaced-type-selectors.html @@ -0,0 +1,257 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSSOM Test: test serialization of type selectors and namespace prefixes</title> + <link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com"> + <link rel="help" href="https://drafts.csswg.org/cssom-1/#serializing-selectors"> + <meta name="flags" content="dom"> + <meta charset="utf-8"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style id="teststyles"> + </style> + </head> + <body> + <div id="log"></div> + <script> + var ns_rule = "@namespace ns url(ns);"; + var default_ns_rules = "@namespace url(default_ns); @namespace nsdefault url(default_ns);" + ns_rule; + + function assert_selector_serializes_to(source, expected_result) { + var style_element = document.getElementById("teststyles"); + style_element.firstChild.data = source + "{ font-size: 1em; }"; + var sheet = style_element.sheet; + assert_equals(sheet.cssRules[sheet.cssRules.length - 1].selectorText, expected_result); + } + test(function() { + assert_selector_serializes_to(ns_rule + "e", "e"); + assert_selector_serializes_to(default_ns_rules + "e", "e"); + }, "Simple type selector"); + test(function() { + assert_selector_serializes_to(ns_rule + "|e", "|e"); + assert_selector_serializes_to(default_ns_rules + "|e", "|e"); + }, "Type selector without a namespace"); + test(function() { + assert_selector_serializes_to(ns_rule + "*|e", "e"); + assert_selector_serializes_to(default_ns_rules + "*|e", "*|e"); + }, "Type selector with any namespace"); + test(function() { + assert_selector_serializes_to(ns_rule + "*", "*"); + assert_selector_serializes_to(default_ns_rules + "*", "*"); + }, "Universal selector"); + test(function() { + assert_selector_serializes_to(ns_rule + "|*", "|*"); + assert_selector_serializes_to(default_ns_rules + "|*", "|*"); + }, "Universal selector without a namespace"); + test(function() { + assert_selector_serializes_to(ns_rule + "*|*", "*"); + assert_selector_serializes_to(default_ns_rules + "*|*", "*|*"); + }, "Universal selector in any namespace"); + test(function() { + assert_selector_serializes_to(ns_rule + "ns|e", "ns|e"); + assert_selector_serializes_to(default_ns_rules + "ns|e", "ns|e"); + }, "Type selector with namespace"); + test(function() { + assert_selector_serializes_to(ns_rule + "ns|*", "ns|*"); + assert_selector_serializes_to(default_ns_rules + "ns|*", "ns|*"); + }, "Universal selector with namespace"); + test(function() { + assert_selector_serializes_to(ns_rule + "e.c", "e.c"); + assert_selector_serializes_to(default_ns_rules + "e.c", "e.c"); + }, "Simple type selector followed by class"); + test(function() { + assert_selector_serializes_to(ns_rule + "e#i", "e#i"); + assert_selector_serializes_to(default_ns_rules + "e#i", "e#i"); + }, "Simple type selector followed by id"); + test(function() { + assert_selector_serializes_to(ns_rule + "e:hover", "e:hover"); + assert_selector_serializes_to(default_ns_rules + "e:hover", "e:hover"); + }, "Simple type selector followed by pseudo class"); + test(function() { + assert_selector_serializes_to(ns_rule + "e::before", "e::before"); + assert_selector_serializes_to(default_ns_rules + "e::before", "e::before"); + }, "Simple type selector followed by pseudo element"); + test(function() { + assert_selector_serializes_to(ns_rule + "e[attr]", "e[attr]"); + assert_selector_serializes_to(default_ns_rules + "e[attr]", "e[attr]"); + }, "Simple type selector followed by atttribute selector"); + test(function() { + assert_selector_serializes_to(ns_rule + "|e.c", "|e.c"); + assert_selector_serializes_to(default_ns_rules + "|e.c", "|e.c"); + }, "Type selector without a namespace followed by class"); + test(function() { + assert_selector_serializes_to(ns_rule + "|e#i", "|e#i"); + assert_selector_serializes_to(default_ns_rules + "|e#i", "|e#i"); + }, "Type selector without a namespace followed by id"); + test(function() { + assert_selector_serializes_to(ns_rule + "|e:hover", "|e:hover"); + assert_selector_serializes_to(default_ns_rules + "|e:hover", "|e:hover"); + }, "Type selector without a namespace followed by pseudo class"); + test(function() { + assert_selector_serializes_to(ns_rule + "|e::before", "|e::before"); + assert_selector_serializes_to(default_ns_rules + "|e::before", "|e::before"); + }, "Type selector without a namespace followed by pseudo element"); + test(function() { + assert_selector_serializes_to(ns_rule + "|e[attr]", "|e[attr]"); + assert_selector_serializes_to(default_ns_rules + "|e[attr]", "|e[attr]"); + }, "Type selector without a namespace followed by attribute selector"); + test(function() { + assert_selector_serializes_to(ns_rule + "*|e.c", "e.c"); + assert_selector_serializes_to(default_ns_rules + "*|e.c", "*|e.c"); + }, "Type selector with any namespace followed by class"); + test(function() { + assert_selector_serializes_to(ns_rule + "*|e#id", "e#id"); + assert_selector_serializes_to(default_ns_rules + "*|e#id", "*|e#id"); + }, "Type selector with any namespace followed by id"); + test(function() { + assert_selector_serializes_to(ns_rule + "*|e:hover", "e:hover"); + assert_selector_serializes_to(default_ns_rules + "*|e:hover", "*|e:hover"); + }, "Type selector with any namespace followed by pseudo class"); + test(function() { + assert_selector_serializes_to(ns_rule + "*|e::before", "e::before"); + assert_selector_serializes_to(default_ns_rules + "*|e::before", "*|e::before"); + }, "Type selector with any namespace followed by pseudo element"); + test(function() { + assert_selector_serializes_to(ns_rule + "*|e[attr]", "e[attr]"); + assert_selector_serializes_to(default_ns_rules + "*|e[attr]", "*|e[attr]"); + }, "Type selector with any namespace followed by attribute selector"); + test(function() { + assert_selector_serializes_to(ns_rule + "*.c", ".c"); + assert_selector_serializes_to(default_ns_rules + "*.c", ".c"); + }, "Universal selector followed by class"); + test(function() { + assert_selector_serializes_to(ns_rule + "*#i", "#i"); + assert_selector_serializes_to(default_ns_rules + "*#i", "#i"); + }, "Universal selector followed by id"); + test(function() { + assert_selector_serializes_to(ns_rule + "*:hover", ":hover"); + assert_selector_serializes_to(default_ns_rules + "*:hover", ":hover"); + }, "Universal selector followed by pseudo class"); + test(function() { + assert_selector_serializes_to(ns_rule + "*::before", "::before"); + assert_selector_serializes_to(default_ns_rules + "*::before", "::before"); + }, "Universal selector followed by pseudo element"); + test(function() { + assert_selector_serializes_to(ns_rule + "*[attr]", "[attr]"); + assert_selector_serializes_to(default_ns_rules + "*[attr]", "[attr]"); + }, "Universal selector followed by attribute selector"); + test(function() { + assert_selector_serializes_to(ns_rule + "|*.c", "|*.c"); + assert_selector_serializes_to(default_ns_rules + "|*.c", "|*.c"); + }, "Universal selector without a namespace followed by class"); + test(function() { + assert_selector_serializes_to(ns_rule + "|*#i", "|*#i"); + assert_selector_serializes_to(default_ns_rules + "|*#i", "|*#i"); + }, "Universal selector without a namespace followed by id"); + test(function() { + assert_selector_serializes_to(ns_rule + "|*:hover", "|*:hover"); + assert_selector_serializes_to(default_ns_rules + "|*:hover", "|*:hover"); + }, "Universal selector without a namespace followed by pseudo class"); + test(function() { + assert_selector_serializes_to(ns_rule + "|*::before", "|*::before"); + assert_selector_serializes_to(default_ns_rules + "|*::before", "|*::before"); + }, "Universal selector without a namespace followed by pseudo element"); + test(function() { + assert_selector_serializes_to(ns_rule + "|*[attr]", "|*[attr]"); + assert_selector_serializes_to(default_ns_rules + "|*[attr]", "|*[attr]"); + }, "Universal selector without a namespace followed by attribute selector"); + test(function() { + assert_selector_serializes_to(ns_rule + "*|*.c", ".c"); + assert_selector_serializes_to(default_ns_rules + "*|*.c", "*|*.c"); + }, "Universal selector in any namespace followed by class"); + test(function() { + assert_selector_serializes_to(ns_rule + "*|*#id", "#id"); + assert_selector_serializes_to(default_ns_rules + "*|*#id", "*|*#id"); + }, "Universal selector in any namespace followed by id"); + test(function() { + assert_selector_serializes_to(ns_rule + "*|*:hover", ":hover"); + assert_selector_serializes_to(default_ns_rules + "*|*:hover", "*|*:hover"); + }, "Universal selector in any namespace followed by pseudo class"); + test(function() { + assert_selector_serializes_to(ns_rule + "*|*::before", "::before"); + assert_selector_serializes_to(default_ns_rules + "*|*::before", "*|*::before"); + }, "Universal selector in any namespace followed by pseudo element"); + test(function() { + assert_selector_serializes_to(ns_rule + "*|*[attr]", "[attr]"); + assert_selector_serializes_to(default_ns_rules + "*|*[attr]", "*|*[attr]"); + }, "Universal selector in any namespace followed by attribute selector"); + test(function() { + assert_selector_serializes_to(ns_rule + "ns|e.c", "ns|e.c"); + assert_selector_serializes_to(default_ns_rules + "ns|e.c", "ns|e.c"); + }, "Type selector with namespace followed by class"); + test(function() { + assert_selector_serializes_to(ns_rule + "ns|e#i", "ns|e#i"); + assert_selector_serializes_to(default_ns_rules + "ns|e#i", "ns|e#i"); + }, "Type selector with namespace followed by id"); + test(function() { + assert_selector_serializes_to(ns_rule + "ns|e:hover", "ns|e:hover"); + assert_selector_serializes_to(default_ns_rules + "ns|e:hover", "ns|e:hover"); + }, "Type selector with namespace followed by pseudo class"); + test(function() { + assert_selector_serializes_to(ns_rule + "ns|e::before", "ns|e::before"); + assert_selector_serializes_to(default_ns_rules + "ns|e::before", "ns|e::before"); + }, "Type selector with namespace followed by pseudo element"); + test(function() { + assert_selector_serializes_to(ns_rule + "ns|e[attr]", "ns|e[attr]"); + assert_selector_serializes_to(default_ns_rules + "ns|e[attr]", "ns|e[attr]"); + }, "Type selector with namespace followed by attribute selector"); + test(function() { + assert_selector_serializes_to(ns_rule + "ns|*.c", "ns|*.c"); + assert_selector_serializes_to(default_ns_rules + "ns|*.c", "ns|*.c"); + }, "Universal selector with namespace followed by class"); + test(function() { + assert_selector_serializes_to(ns_rule + "ns|*#i", "ns|*#i"); + assert_selector_serializes_to(default_ns_rules + "ns|*#i", "ns|*#i"); + }, "Universal selector with namespace followed by id"); + test(function() { + assert_selector_serializes_to(ns_rule + "ns|*:hover", "ns|*:hover"); + assert_selector_serializes_to(default_ns_rules + "ns|*:hover", "ns|*:hover"); + }, "Universal selector with namespace followed by pseudo class"); + test(function() { + assert_selector_serializes_to(ns_rule + "ns|*::before", "ns|*::before"); + assert_selector_serializes_to(default_ns_rules + "ns|*::before", "ns|*::before"); + }, "Universal selector with namespace followed by pseudo element"); + test(function() { + assert_selector_serializes_to(ns_rule + "ns|*[attr]", "ns|*[attr]"); + assert_selector_serializes_to(default_ns_rules + "ns|*[attr]", "ns|*[attr]"); + }, "Universal selector with namespace followed by attribute selector"); + test(function() { + assert_selector_serializes_to(default_ns_rules + "nsdefault|e", "e"); + }, "Type selector with namespace equal to default namespace"); + test(function() { + assert_selector_serializes_to(default_ns_rules + "nsdefault|*", "*"); + }, "Universal selector with namespace equal to default namespace"); + test(function() { + assert_selector_serializes_to(default_ns_rules + "nsdefault|e.c", "e.c"); + }, "Type selector with namespace equal to default namespace followed by class"); + test(function() { + assert_selector_serializes_to(default_ns_rules + "nsdefault|e#i", "e#i"); + }, "Type selector with namespace equal to default namespace followed by id"); + test(function() { + assert_selector_serializes_to(default_ns_rules + "nsdefault|e:hover", "e:hover"); + }, "Type selector with namespace equal to default namespace followed by pseudo class"); + test(function() { + assert_selector_serializes_to(default_ns_rules + "nsdefault|e::before", "e::before"); + }, "Type selector with namespace equal to default namespace followed by pseudo element"); + test(function() { + assert_selector_serializes_to(default_ns_rules + "nsdefault|e[attr]", "e[attr]"); + }, "Type selector with namespace equal to default namespace followed by attribute selector"); + test(function() { + assert_selector_serializes_to(default_ns_rules + "nsdefault|*.c", ".c"); + }, "Universal selector with namespace equal to default namespace followed by class"); + test(function() { + assert_selector_serializes_to(default_ns_rules + "nsdefault|*#i", "#i"); + }, "Universal selector with namespace equal to default namespace followed by id"); + test(function() { + assert_selector_serializes_to(default_ns_rules + "nsdefault|*:hover", ":hover"); + }, "Universal selector with namespace equal to default namespace followed by pseudo class"); + test(function() { + assert_selector_serializes_to(default_ns_rules + "nsdefault|*::before", "::before"); + }, "Universal selector with namespace equal to default namespace followed by pseudo element"); + test(function() { + assert_selector_serializes_to(default_ns_rules + "nsdefault|*[attr]", "[attr]"); + }, "Universal selector with namespace equal to default namespace followed by attribute selector"); + </script> + </body> +</html> + diff --git a/testing/web-platform/tests/css/cssom/serialize-values.html b/testing/web-platform/tests/css/cssom/serialize-values.html new file mode 100644 index 0000000000..e92808af70 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/serialize-values.html @@ -0,0 +1,620 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSSOM serialize values</title> +<link rel="help" href="https://drafts.csswg.org/cssom/#serializing-css-values"> +<meta name="author" title="Josh Matthews" href="mailto:josh@joshmatthews.net"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> + <div id="log"></div> + <div id="parent"></div> + <script> + function iterable(values) { + var i = 0; + return function() { + if (i < values.length) { + return values[i++]; + } + return null; + } + } + + function color() { + var colors = ['black', 'red', 'rgb(50, 75, 100)', 'rgba(5, 7, 10, 0.5)']; + return iterable(colors); + } + + function percentage() { + var values = ["5%", {actual: ".5%", serialized: "0.5%"}]; + return iterable(values); + } + + function negative_percentage() { + var values = ["-5%", {actual: "-.5%", serialized: "-0.5%"}]; + return iterable(values); + } + + function length() { + var values = ["0px", "1px", {actual: ".1em", serialized: "0.1em"}]; + return iterable(values); + } + + function negative_length() { + var values = [{actual: "-0px", serialized: "0px"}, + "-1px", {actual: "-.1em", serialized: "-0.1em"}]; + return iterable(values); + } + + function degree() { + var values = ["87deg"]; + return iterable(values); + } + + function uri() { + var values = ["url(\"http://localhost/\")", + {actual: "url(http://localhost/)", + serialized: "url(\"http://localhost/\")"}]; + return iterable(values); + } + + function border_style() { + var values = ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', + 'inset', 'outset']; + return iterable(values); + } + + function border_style_without_hidden() { + var values = ['none', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', + 'inset', 'outset']; + return iterable(values); + } + + function integer() { + var values = ['0', '101', '-51']; + return iterable(values); + } + + function nonzero_positive_integer() { + var values = ['101']; + return iterable(values); + } + + function shape() { + var values = ['rect(1em, auto, 0.5px, 2000em)']; + return iterable(values); + } + + function string() { + var values = ['"string"', {actual: "'string'", serialized: '"string"'}]; + return iterable(values); + } + + function counter() { + var values = ['counter(par-num)', + { actual: 'counter(par-num, decimal)', serialized: 'counter(par-num)' }, + 'counter(par-num, upper-roman)']; + return iterable(values); + } + + function attr() { + var values = ['attr(foo-bar)', 'attr(foo_bar)']; + return iterable(values); + } + + function family_name() { + var values = ['Arial', {actual: "'Lucida Grande'", serialized: '"Lucida Grande"'}]; + return iterable(values); + } + + function generic_family() { + var values = ['serif', 'sans-serif']; + return iterable(values); + } + + function absolute_size() { + var values = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large']; + return iterable(values); + } + + function relative_size() { + var values = ['larger', 'smaller']; + return iterable(values); + } + + function number() { + var values = ['0', {'actual': '-0', serialized: '0'}, '1000', '-5123', '0.9', '-0.09']; + return iterable(values); + } + + function positive_number() { + var values = ['0', {'actual': '-0', serialized: '0'}, '1000', '0.9']; + return iterable(values); + } + + function generate_inline_style(name, value) { + if (value) { + return {'declaration': name + ": " + value.actual, + 'value': value.actual, + 'result': value.expected}; + } + return null; + } + + // A more elegant way of doing this would be better, but for now this exception list handles cases where "center" must be omitted. + var minimal_results = { + "background-position: 5% center": "5%", + "background-position: 0.5% center": "0.5%", + "background-position: -5% center": "-5%", + "background-position: -0.5% center": "-0.5%", + "background-position: 0px center": "0px", + "background-position: 1px center": "1px", + "background-position: 0.1em center": "0.1em", + "background-position: 0px center": "0px", + "background-position: -1px center": "-1px", + "background-position: -0.1em center": "-0.1em", + "background-position: left center": "left", + "background-position: center top": "top", + "background-position: center center": "center", + "background-position: center bottom": "bottom", + "background-position: right center": "right", + }; + + function create_result(propertyName, actual, expected) { + var key = propertyName + ": " + expected + if (key in minimal_results) + expected = minimal_results[key] + return {actual: actual, expected: expected} + } + + function all_values(propertyName, values) { + var results = []; + for (var i = 0; i < values.length; i++) { + var value = values[i]; + if (typeof value == "function") { + var f = value(); + var result; + while ((result = f()) != null) { + if (typeof result == "object" && 'serialized' in result) { + results.push(create_result(propertyName, result.actual, result.serialized)); + } else { + results.push(create_result(propertyName, result, result)); + } + } + } else if (typeof value == "string") { + results.push(create_result(propertyName, value, value)); + } else if (value instanceof Array) { + var subresults = []; + for (var j = 0; j < value.length; j++) { + var subresult = all_values(propertyName, value[j]); + if (!(subresult instanceof Array)) { + subresult = [subresult]; + } + subresults.push(subresult); + } + if (subresults.length > 1) { + function choose_slices(vecs) { + if (vecs.length == 1) { + return vecs[0].map(function(v) { return [v]; }); + } + var slice_results = []; + var rest = choose_slices(vecs.slice(1, vecs.length)); + for (var a = 0; a < vecs[0].length; a++) { + for (var b = 0; b < rest.length; b++) { + var result = vecs[0][a]; + slice_results.push([result].concat(rest[b])); + } + } + return slice_results; + } + + subresults = choose_slices(subresults).map(function (a) { + var actual = a.map(function(a) { return a.actual }); + var expected = a.map(function(a) { return a.expected }); + return create_result(propertyName, actual.join(' '), expected.join(' ')) + }); + } + for (var j = 0; j < subresults.length; j++) { + results = results.concat(subresults[j]); + } + } else if (value instanceof Object && 'serialized' in value) { + results.push(create_result(propertyName, value.actual, value.serialized)); + } else if (typeof value == "number") { + results.push(create_result(propertyName, value.toString(), value.toString())); + } else { + throw "unexpected value type: " + typeof(value); + } + } + return results; + } + + function create_value_generator(propertyName, property) { + var results = all_values(propertyName, property.values); + return iterable(results); + } + + function to_idl(property) { + return property.replace(/-\w/g, function(x){return x[1].toUpperCase()}); + } + + function run_individual_test(property, generator, initial) { + var elem = document.createElement('div'); + document.getElementById('parent').appendChild(elem); + var test_data = generator(); + var style = generate_inline_style(property, test_data); + if (!style) { + return false; + } + var t = async_test(style.declaration); + + t.add_cleanup(function() { + document.getElementById('parent').removeChild(elem); + }); + + t.step(function() { + elem.setAttribute('style', style.declaration); + var expected = style.result; + var serialized = elem.style[to_idl(property)]; + assert_equals(serialized, expected, property + ' raw inline style declaration'); + elem.setAttribute('style', ''); + elem.style[to_idl(property)] = style.value; + assert_equals(elem.style[to_idl(property)], expected, property + ' style property'); + }); + t.done(); + return true; + } + + function test_property(property) { + var generator = create_value_generator(property[0], property[1]); + while (run_individual_test(property[0], generator, property[1].initial)) { + } + } + + var properties = [ + ['background-attachment', { + 'values': ['scroll', 'fixed', 'inherit'], + 'initial': 'scroll', + }], + ['background-color', { + 'values': [color, 'transparent', 'inherit'], + 'initial': 'transparent', + }], + ['background-image', { + 'values': [uri, 'none', 'inherit'], + 'initial': 'none', + }], + ['background-position', { + 'values': [[[percentage, negative_percentage, length, negative_length, + 'left', 'center', 'right'], + [percentage, negative_percentage, length, negative_length, + 'top', 'center', 'bottom']], + 'inherit'], + 'initial': '0% 0%', + }], + ['background-repeat', { + 'values': ['repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'inherit'], + 'initial': 'repeat', + }], + //background + ['border-collapse', { + 'values': ['collapse', 'separate', 'inherit'], + 'initial': 'separate', + }], + //border-color + ['border-spacing', { + 'values': [length, 'inherit'], + 'initial': '0', + }], + //border-style + //border-top, border-right, border-bottom, border-left + ['border-top-color', { + 'values': [color, 'transparent', 'inherit'], + 'initial': 'black', //FIXME + }], + ['border-right-color', { + 'values': [color, 'transparent', 'inherit'], + 'initial': 'black', //FIXME + }], + ['border-bottom-color', { + 'values': [color, 'transparent', 'inherit'], + 'initial': 'black', //FIXME + }], + ['border-left-color', { + 'values': [color, 'transparent', 'inherit'], + 'initial': 'black', //FIXME + }], + ['border-top-style', { + 'values': [border_style, 'inherit'], + 'initial': null, + }], + ['border-right-style', { + 'values': [border_style, 'inherit'], + 'initial': null, + }], + ['border-bottom-style', { + 'values': [border_style, 'inherit'], + 'initial': null, + }], + ['border-left-style', { + 'values': [border_style, 'inherit'], + 'initial': null, + }], + ['border-top-width', { + 'values': ['thin', 'medium', 'thick', length, 'inherit'], + 'initial': 'medium', + }], + ['border-right-width', { + 'values': ['thin', 'medium', 'thick', length, 'inherit'], + 'initial': 'medium', + }], + ['border-bottom-width', { + 'values': ['thin', 'medium', 'thick', length, 'inherit'], + 'initial': 'medium', + }], + ['border-left-width', { + 'values': ['thin', 'medium', 'thick', length, 'inherit'], + 'initial': 'medium', + }], + //border-width + //border + ['bottom', { + 'values': [length, percentage, 'auto', 'inherit'], + 'initial': 'auto', + }], + ['caption-side', { + 'values': ['top', 'bottom', 'inherit'], + 'initial': 'top', + }], + ['clear', { + 'values': ['none', 'left', 'right', 'both', 'inherit'], + 'initial': 'none', + }], + ['clip', { + 'values': [shape, 'auto', 'inherit'], + 'initial': 'auto', + }], + ['color', { + 'values': [color, 'inherit'], + 'initial': 'black', //FIXME depends on user agent + }], + ['content', { + 'values': ['normal', 'none', string, uri, counter, attr, 'inherit'], //FIXME + 'initial': 'normal', + }], + //counter-increment + //counter-reset + ['cursor', { + 'values': [ 'auto', 'crosshair', 'default', 'pointer', 'move', 'e-resize', 'ne-resize', + 'nw-resize', 'n-resize', 'se-resize', 'sw-resize', 's-resize', 'w-resize', + 'text', 'wait', 'help', 'progress', 'inherit'], + 'initial': 'auto', + }], + ['direction', { + 'values': ['ltr', 'rtl', 'inherit'], + 'initial': 'ltr', + }], + ['display', { + 'values': ['inline', 'block', 'list-item', 'inline-block', 'table', 'inline-table', + 'table-row-group', 'table-header-group', 'table-footer-group', 'table-row', + 'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none', + 'inherit'], + 'initial': 'inline', + }], + ['empty-cells', { + 'values': ['show', 'hide', 'inherit'], + 'initial': 'show', + }], + ['float', { + 'values': ['left', 'right', 'none', 'inherit'], + 'initial': 'none', + 'property': 'cssFloat', + }], + ['font-family', { + 'values': [family_name, generic_family, 'inherit'], + 'initial': 'sans-serif', //FIXME depends on user agent + }], + ['font-size', { + 'values': [absolute_size, relative_size, length, percentage, 'inherit'], + 'initial': 'medium', + }], + ['font-style', { + 'values': ['normal', 'italic', 'oblique', 'inherit'], + 'initial': 'normal', + }], + ['font-variant', { + 'values': ['normal', 'small-caps', 'inherit'], + 'initial': 'normal', + }], + ['font-weight', { + 'values': ['normal', 'bold', 'bolder', 'lighter', 100, 200, 300, 400, 500, 600, + 700, 800, 900, 'inherit'], + 'initial': 'normal', + }], + //font + ['height', { + 'values': [length, percentage, 'auto', 'inherit'], + 'initial': 'auto', + }], + ['left', { + 'values': [length, percentage, 'auto', 'inherit'], + 'initial': 'auto', + }], + ['letter-spacing', { + 'values': ['normal', length, 'inherit'], + 'initial': 'normal', + }], + ['line-height', { + 'values': ['normal', positive_number, length, percentage, 'inherit'], + 'initial': 'normal', + }], + ['list-style-image', { + 'values': [uri, 'none', 'inherit'], + 'initial': 'none', + }], + ['list-style-position', { + 'values': ['inside', 'outside', 'inherit'], + 'initial': 'outside', + }], + ['list-style-type', { + 'values': ['disc', 'circle', 'square', 'disclosure-open', 'disclosure-closed', + 'decimal', 'decimal-leading-zero', 'lower-roman', + 'upper-roman', 'lower-greek', 'lower-latin', 'upper-latin', 'armenian', 'georgian', + 'lower-alpha', 'upper-alpha', 'none', 'inherit'], + 'initial': 'disc', + }], + //list-style + ['margin-right', { + 'values': [length, percentage, 'auto', 'inherit'], + 'initial': 0, + }], + ['margin-left', { + 'values': [length, percentage, 'auto', 'inherit'], + 'initial': 0, + }], + ['margin-top', { + 'values': [length, percentage, 'auto', 'inherit'], + 'initial': 0, + }], + ['margin-bottom', { + 'values': [length, percentage, 'auto', 'inherit'], + 'initial': 0, + }], + //margin + ['max-height', { + 'values': [length, percentage, 'none', 'inherit'], + 'initial': 'none', + }], + ['max-width', { + 'values': [length, percentage, 'none', 'inherit'], + 'initial': 'none', + }], + ['min-height', { + 'values': [length, percentage, 'inherit'], + 'initial': 0, + }], + ['min-width', { + 'values': [length, percentage, 'inherit'], + 'initial': 0, + }], + ['orphans', { + 'values': [nonzero_positive_integer, 'inherit'], + 'initial': 2, + }], + ['outline-color', { + 'values': [color, 'invert', 'inherit'], + 'initial': 'invert', + }], + ['outline-style', { + 'values': [border_style_without_hidden, 'inherit'], + 'initial': 'none', + }], + ['outline-width', { + 'values': ['thin', 'medium', 'thick', length, 'inherit'], + 'initial': 'medium', + }], + //outline + ['overflow', { + 'values': ['visible', 'hidden', 'scroll', 'auto', 'inherit'], + 'initial': 'visible', + }], + ['padding-top', { + 'values': [length, percentage, 'inherit'], + 'initial': 0, + }], + ['padding-right', { + 'values': [length, percentage, 'inherit'], + 'initial': 0, + }], + ['padding-bottom', { + 'values': [length, percentage, 'inherit'], + 'initial': 0, + }], + ['padding-left', { + 'values': [length, percentage, 'inherit'], + 'initial': 0, + }], + //padding + ['page-break-after', { + 'values': ['auto', 'always', 'avoid', 'left', 'right', 'inherit'], + 'initial': 'auto', + }], + ['page-break-before', { + 'values': ['auto', 'always', 'avoid', 'left', 'right', 'inherit'], + 'initial': 'auto', + }], + ['page-break-inside', { + 'values': ['avoid', 'auto', 'inherit'], + 'initial': 'auto', + }], + ['position', { + 'values': ['static', 'relative', 'absolute', 'fixed', 'inherit'], + 'initial': 'static', + }], + //FIXME quotes + ['right', { + 'values': [length, percentage, 'auto', 'inherit'], + 'initial': 'auto', + }], + ['table-layout', { + 'values': ['auto', 'fixed', 'inherit'], + 'initial': 'auto', + }], + ['text-align', { + 'values': ['left', 'right', 'center', 'justify', 'inherit'], + 'initial': null, + }], + ['text-decoration', { + 'values': ['none', 'underline', 'overline', 'line-through', 'blink', 'inherit'], + 'initial': 'none', + }], + ['text-indent', { + 'values': [length, percentage, 'inherit'], + 'initial': 0, + }], + ['text-transform', { + 'values': ['capitalize', 'uppercase', 'lowercase', 'none', 'inherit'], + 'initial': 'none', + }], + ['top', { + 'values': [length, percentage, 'auto', 'inherit'], + 'initial': 'auto', + }], + ['unicode-bidi', { + 'values': ['normal', 'embed', 'bidi-override', 'inherit'], + 'initial': 'normal', + }], + ['vertical-align', { + 'values': ['baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom', + percentage, length, 'inherit'], + 'initial': 'baseline', + }], + ['visibility', { + 'values': ['visible', 'hidden', 'collapse', 'inherit'], + 'initial': 'visible', + }], + ['white-space', { + 'values': ['normal', 'pre', 'nowrap', 'pre-wrap', 'pre-line', 'inherit'], + 'initial': 'normal', + }], + ['widows', { + 'values': [nonzero_positive_integer, 'inherit'], + 'initial': 2, + }], + ['width', { + 'values': [length, percentage, 'auto', 'inherit'], + 'initial': 'auto', + }], + ['word-spacing', { + 'values': ['normal', length, 'inherit'], + 'initial': 'normal', + }], + ['z-index', { + 'values': ['auto', integer, 'inherit'], + 'initial': 'auto', + }], + ] + + for (var index = 0; index < properties.length; index++) { + test_property(properties[index]); + } + </script> +</body> diff --git a/testing/web-platform/tests/css/cssom/serialize-variable-reference.html b/testing/web-platform/tests/css/cssom/serialize-variable-reference.html new file mode 100644 index 0000000000..1d3cbad9e6 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/serialize-variable-reference.html @@ -0,0 +1,36 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSSOM - Serialization with variable preserves original serialization.</title> +<link rel="help" href="https://drafts.csswg.org/css-variables/#serializing-custom-props"> +<link rel="help" href="https://drafts.csswg.org/css-variables/#variables-in-shorthands"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="longhand-whitespace" style="font-size: var(--a);"></div> +<div id="shorthand-whitespace" style="font: var(--a);"></div> +<div id="longhand" style="font-size:var(--a);"></div> +<div id="shorthand" style="font:var(--a);"></div> +<script> + test(function() { + var elem = document.getElementById('longhand-whitespace'); + + assert_equals(elem.style.cssText, 'font-size: var(--a);'); + }, 'Longhand with variable preserves original serialization: with whitespace') + + test(function() { + var elem = document.getElementById('shorthand-whitespace'); + + assert_equals(elem.style.cssText, 'font: var(--a);'); + }, 'Shorthand with variable preserves original serialization: with whitespace') + + test(function() { + var elem = document.getElementById('longhand'); + + assert_equals(elem.style.cssText, 'font-size: var(--a);'); + }, 'Longhand with variable preserves original serialization but trims whitespace: without whitespace') + + test(function() { + var elem = document.getElementById('shorthand'); + + assert_equals(elem.style.cssText, 'font: var(--a);'); + }, 'Shorthand with variable preserves original serialization but trims whitespace: without whitespace') +</script> diff --git a/testing/web-platform/tests/css/cssom/setproperty-null-undefined.html b/testing/web-platform/tests/css/cssom/setproperty-null-undefined.html new file mode 100644 index 0000000000..3de142c0b4 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/setproperty-null-undefined.html @@ -0,0 +1,47 @@ +<!doctype html> +<html> +<head> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty"/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +var style = document.body.style; + +test(function() { + style.color = 'white'; + + assert_equals(style.color, 'white'); + style.setProperty('color', undefined); + assert_equals(style.color, 'white'); +}, "Verify that setting a CSS property to undefined has no effect."); + +test(function() { + style.color = 'white'; + + assert_equals(style.color, 'white'); + assert_equals(style.getPropertyPriority('color'), ''); + style.setProperty('color', 'red', undefined); + assert_equals(style.color, 'red'); + assert_equals(style.getPropertyPriority('color'), ''); +}, "Verify that setting a CSS property priority to undefined is accepted."); + +test(function() { + style.color = 'white'; + + assert_equals(style.color, 'white'); + style.setProperty('color', null); + assert_equals(style.color, ''); +}, "Verify that setting a CSS property to null is treated like empty string."); + +test(function() { + style.color = 'white'; + + assert_equals(style.color, 'white'); + style.setProperty('color', 'red', null); + assert_equals(style.color, 'red'); +}, "Verify that setting a CSS property priority to null is treated like empty string."); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/shorthand-serialization.html b/testing/web-platform/tests/css/cssom/shorthand-serialization.html new file mode 100644 index 0000000000..97e11da8b8 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/shorthand-serialization.html @@ -0,0 +1,86 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>Shorthand serialization should be done correctly.</title> + <link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block"> + <link rel="help" href="https://drafts.csswg.org/css-variables/#variables-in-shorthands"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> + <div id="foo1" style="background: red;">foo</div> + <div id="foo2" style="background-color: blue; background: red !important; background-color: green;">foo</div> + <div id="foo3" style="background-color: blue; background: red; background-color: green !important;">foo</div> + + <div id="foo4" style="margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px;">foo</div> + <div id="foo5" style="margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px!important;">foo</div> + <div id="foo6" style="margin-right: 10px !important; margin-left: 10px !important; margin-top: 10px !important; margin-bottom: 10px!important;">foo</div> + + <div id="foo7" style="background:var(--a);">foo</div> + <div id="test"></div> + + <script> + test(function() { + var elem1 = document.getElementById('foo1'); + var elem2 = document.getElementById('foo2'); + var elem3 = document.getElementById('foo3'); + + assert_false(elem1.style.cssText.endsWith('!important;')); + assert_true(elem2.style.cssText.endsWith('!important;')); + assert_false(elem3.style.background.endsWith('!important')); + + }, "Shorthand serialization with shorthand and longhands mixed."); + + test(function() { + var elem4 = document.getElementById('foo4'); + var elem5 = document.getElementById('foo5'); + var elem6 = document.getElementById('foo6'); + + assert_equals(elem4.style.cssText, 'margin: 10px;'); + assert_equals(elem4.style.margin, '10px'); + assert_equals(elem5.style.cssText, 'margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px !important;'); + assert_equals(elem5.style.margin, ''); + assert_equals(elem6.style.cssText, 'margin: 10px !important;'); + assert_equals(elem6.style.margin, '10px'); + }, "Shorthand serialization with just longhands."); + + test(function() { + var elem7 = document.getElementById('foo7'); + + assert_equals(elem7.style.background, 'var(--a)'); + assert_equals(elem7.style.backgroundPosition, ''); + }, "Shorthand serialization with variable and variable from other shorthand."); + + test(function() { + var testElem = document.getElementById("test"); + testElem.style.margin = "20px 20px 20px 20px"; + assert_equals(testElem.style.margin, "20px"); + assert_equals(testElem.style.cssText, "margin: 20px;") + }, "Shorthand serialization after setting"); + + test(function() { + const testElem = document.getElementById("test"); + testElem.style.cssText = "margin: initial;"; + assert_equals(testElem.style.margin, "initial"); + assert_equals(testElem.style.cssText, "margin: initial;"); + }, "Shorthand serialization with 'initial' value."); + + test(function() { + const testElem = document.getElementById("test"); + testElem.style.setProperty("margin-top", "initial", "important"); + assert_equals(testElem.style.margin, ""); + }, "Shorthand serialization with 'initial' value, one longhand with important flag."); + + test(function() { + const testElem = document.getElementById("test"); + testElem.style.cssText = ""; + testElem.style.setProperty("margin-top", "initial"); + testElem.style.setProperty("margin-right", "initial"); + testElem.style.setProperty("margin-bottom", "initial"); + testElem.style.setProperty("margin-left", "initial", "important"); + assert_equals(testElem.style.margin, ""); + }, "Shorthand serialization with 'initial' value, longhands set individually, one with important flag."); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/shorthand-values.html b/testing/web-platform/tests/css/cssom/shorthand-values.html new file mode 100644 index 0000000000..b64f7e9a12 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/shorthand-values.html @@ -0,0 +1,51 @@ +<!doctype html> +<head> + <title>CSS OM: CSS Values</title> + <link rel="author" title="Divya Manian" href="mailto:manian@adobe.com"> + <link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block"> + <meta name="flags" content="dom"> + <meta name="assert" content="Testing Serialization of Shorthand Values"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> + <div id="test"></div> + <script> + function test_shorthand_serialization(value, expected) { + test(function() { + const div = document.getElementById("test"); + div.style.cssText = value; + assert_equals(div.style.cssText, expected); + }, "The serialization of " + value + " should be canonical."); + } + + var tests = { + // specified -> expected + 'border: 1px; border-top: 1px;': 'border: 1px;', + 'border: 1px solid red;': 'border: 1px solid red;', + 'border: 1px red;': 'border: 1px red;', + 'border: red;': 'border: red;', + 'border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none;': 'border: 1px;', + 'border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px;': 'border-width: 1px; border-style: none; border-color: currentcolor;', + 'border-top: 1px; border-right: 2px; border-bottom: 3px; border-left: 4px;': 'border-width: 1px 2px 3px 4px; border-style: none; border-color: currentcolor;', + 'border: 1px; border-top: 2px;': 'border-width: 2px 1px 1px; border-style: none; border-color: currentcolor; border-image: none;', + 'border: 1px; border-top: 1px !important;': 'border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-right-style: none; border-bottom-style: none; border-left-style: none; border-right-color: currentcolor; border-bottom-color: currentcolor; border-left-color: currentcolor; border-image: none; border-top-width: 1px !important; border-top-style: none !important; border-top-color: currentcolor !important;', + 'border: 1px; border-top-color: red;': 'border-width: 1px; border-style: none; border-color: red currentcolor currentcolor; border-image: none;', + 'border: solid; border-style: dotted': 'border: dotted;', + 'border-width: 1px;': 'border-width: 1px;', + 'overflow-x: scroll; overflow-y: hidden;': 'overflow: scroll hidden;', + 'overflow-x: scroll; overflow-y: scroll;': 'overflow: scroll;', + 'outline-width: 2px; outline-style: dotted; outline-color: blue;': 'outline: blue dotted 2px;', + 'margin-top: 1px; margin-right: 2px; margin-bottom: 3px; margin-left: 4px;': 'margin: 1px 2px 3px 4px;', + 'list-style-type: circle; list-style-position: inside; list-style-image: none;': 'list-style: inside circle;', + 'list-style-type: lower-alpha;': 'list-style-type: lower-alpha;', + 'font-family: sans-serif; line-height: 2em; font-size: 3em; font-style: italic; font-weight: bold;': 'font-family: sans-serif; line-height: 2em; font-size: 3em; font-style: italic; font-weight: bold;', + 'padding-top: 1px; padding-right: 2px; padding-bottom: 3px; padding-left: 4px;': 'padding: 1px 2px 3px 4px;' + } + + for (let test in tests) { + test_shorthand_serialization(test, tests[test]); + } + </script> + </body> + </html> diff --git a/testing/web-platform/tests/css/cssom/style-attr-update-across-documents.html b/testing/web-platform/tests/css/cssom/style-attr-update-across-documents.html new file mode 100644 index 0000000000..2b8f443520 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/style-attr-update-across-documents.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<title>CSSStyleDeclaration setter works when a node changes document</title> +<link rel="author" title="Raphael Kubo da Costa" href="raphael.kubo.da.costa@intel.com"> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-camel-cased-attribute"> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-webkit-cased-attribute"> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-dashed-attribute"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="original-div" style="background-color:green; height: 100px; width: 100px"></div> +<script> + // Create and return a 100x100 red <div>. + function createDiv(containerDocument) { + const div = containerDocument.createElement("div"); + div.style.backgroundColor = "red"; + div.style.height = "100px"; + div.style.width = "100px"; + containerDocument.body.appendChild(div); + return div; + } + + async_test(t => { + const iframeElement = document.createElement("iframe"); + iframeElement.addEventListener("load", t.step_func_done(() => { + const originalDiv = document.getElementById("original-div"); + const newDiv = createDiv(iframeElement.contentDocument); + + assert_not_equals(newDiv.ownerDocument, document, + "The new <div> belongs to the iframe, not the main document"); + + document.body.insertBefore(newDiv, originalDiv); + assert_equals(newDiv.ownerDocument, document, + "The new <div> now belongs to the main document"); + + newDiv.style.backgroundColor = "blue"; + assert_equals(window.getComputedStyle(newDiv).getPropertyValue("background-color"), + "rgb(0, 0, 255)", + "The new <div>'s background-color is blue"); + + document.body.removeChild(iframeElement); + + newDiv.style.backgroundColor = "green"; + assert_equals(window.getComputedStyle(newDiv).getPropertyValue("background-color"), + "rgb(0, 128, 0)", + "The new <div>'s background-color is green"); + })); + document.body.appendChild(iframeElement); + }, "Changing the style of a node that switched documents works"); +</script> diff --git a/testing/web-platform/tests/css/cssom/style-sheet-interfaces-001.html b/testing/web-platform/tests/css/cssom/style-sheet-interfaces-001.html new file mode 100644 index 0000000000..07c4fbb41a --- /dev/null +++ b/testing/web-platform/tests/css/cssom/style-sheet-interfaces-001.html @@ -0,0 +1,140 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test: CSSOM StyleSheet Initial Values</title> + <link rel="author" title="Bear Travis" href="mailto:betravis@adobe.com"> + <link rel="help" href="https://www.w3.org/TR/cssom-1/#css-style-sheets"> + <link rel="help" href="https://www.w3.org/TR/cssom-1/#the-cssimportrule-interface"> + <link rel="help" href="https://www.w3.org/TR/cssom-1/#the-linkstyle-interface"> + <meta name="flags" content="dom"> + <meta name="assert" content="StyleSheet and CSSStyleSheet objects have the properties specified in their interfaces"> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> + <style id="styleElement" type="text/css" media="all" title="internal style sheet" disabled="disabled"> + @import url('support/a-green.css'); + * { margin: 0; padding: 0; } + </style> + <link id="linkElement" rel="stylesheet" href="support/b-green.css"> + </head> + <body> + <noscript>Test not run - javascript required.</noscript> + <div id="log"></div> + <script type="text/javascript"> + var styleElement = document.getElementById("styleElement"); + var linkElement = document.getElementById("linkElement"); + + var styleSheet; + var linkSheet; + + // styleElement.sheet exists and is a CSSStyleSheet + // linkElement.sheet exists and is a CSSStyleSheet + test(function() { + assert_idl_attribute(styleElement, "sheet"); + assert_readonly(styleElement, "sheet"); + styleSheet = styleElement.sheet; + assert_true(styleSheet instanceof CSSStyleSheet); + assert_idl_attribute(linkElement, "sheet"); + linkSheet = linkElement.sheet; + assert_true(linkSheet instanceof CSSStyleSheet); + }, "sheet_property"); + + // The `disabled` IDL attribute setter has no effect before the underlying + // CSSStyleSheet is created. + test(function () { + const style = document.createElement("style"); + style.disabled = true; + assert_false(style.disabled); + document.head.append(style); + const sheet1 = style.sheet; + assert_false(style.disabled); + assert_false(sheet1.disabled); + + // The `disabled` attribute can be set after the sheet exists. + style.disabled = true; + assert_true(style.disabled); + assert_true(sheet1.disabled); + + // Reset `disabled`. + style.disabled = false; + assert_false(style.disabled); + assert_false(style.disabled); + + // Setting `disabled = true` on the CSSStyleSheet also carries over to the + // HTMLStyleElement. + sheet1.disabled = true; + assert_true(style.disabled); + assert_true(sheet1.disabled); + }, "disabled attribute getter/setter"); + + // The sheet property on LinkStyle should always return the current associated style sheet. + test(function () { + var style = document.createElement("style"); + document.querySelector("head").appendChild(style); + var sheet1 = style.sheet; + assert_equals(sheet1.cssRules.length, 0); + style.appendChild(document.createTextNode("a { color: green; }")); + assert_equals(style.sheet.cssRules.length, 1); + }, "sheet_property_updates"); + + // ownerRule, cssRules, insertRule and deleteRule properties exist on CSSStyleSheet + // ownerRule, cssRules are read only + test(function() { + assert_idl_attribute(styleSheet, "ownerRule"); + assert_idl_attribute(styleSheet, "cssRules"); + assert_inherits(styleSheet, "insertRule"); + assert_inherits(styleSheet, "deleteRule"); + + assert_readonly(styleSheet, "ownerRule"); + assert_readonly(styleSheet, "cssRules"); + }, "CSSStyleSheet_properties"); + + var importSheet; + // CSSStyleSheet initial property values are correct + test(function() { + assert_equals(styleSheet.ownerRule, null); + assert_true(styleSheet.cssRules.length > 0); + assert_true(styleSheet.cssRules.item(0) instanceof CSSImportRule); + importSheet = styleSheet.cssRules.item(0).styleSheet; + }, "CSSStyleSheet_property_values"); + + // type, disabled, ownerNode, parentStyleSheet, href, title, and media properties exist on StyleSheet + // type, ownerNode, parentStyleSheet, href, and title properties are read only + test(function() { + assert_idl_attribute(styleSheet, "type"); + assert_idl_attribute(styleSheet, "disabled"); + assert_idl_attribute(styleSheet, "ownerNode"); + assert_idl_attribute(styleSheet, "parentStyleSheet"); + assert_idl_attribute(styleSheet, "href"); + assert_idl_attribute(styleSheet, "title"); + assert_idl_attribute(styleSheet, "media"); + + assert_readonly(styleSheet, "type"); + assert_readonly(styleSheet, "ownerNode"); + assert_readonly(styleSheet, "parentStyleSheet"); + assert_readonly(styleSheet, "href"); + assert_readonly(styleSheet, "title"); + }, "StyleSheet_properties"); + + // StyleSheet initial property values are correct + test(function() { + assert_equals(styleSheet.type, "text/css"); + assert_equals(styleSheet.disabled, false); + + assert_equals(styleSheet.ownerNode, styleElement); + assert_equals(linkSheet.ownerNode, linkElement); + assert_equals(importSheet.ownerNode, null); + + assert_equals(styleSheet.href, null); + assert_regexp_match(linkSheet.href, /support\/b-green.css$/); + assert_regexp_match(importSheet.href, /support\/a-green.css$/); + + assert_equals(styleSheet.parentStyleSheet, null); + assert_equals(linkSheet.parentStyleSheet, null); + assert_equals(importSheet.parentStyleSheet, styleSheet); + + assert_equals(styleSheet.title, "internal style sheet"); + assert_equals(styleSheet.media.item(0), "all"); + }, "StyleSheet_property_values"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/style-sheet-interfaces-002.html b/testing/web-platform/tests/css/cssom/style-sheet-interfaces-002.html new file mode 100644 index 0000000000..e86a9a16e3 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/style-sheet-interfaces-002.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test: CSSOM StyleSheet Modify Rule List</title> + <link rel="author" title="Bear Travis" href="mailto:betravis@adobe.com"> + <link rel="reviewer" title="Ms2ger" href="mailto:ms2ger@gmail.com"> <!-- 2012-06-17 --> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstylesheet-interface"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface"> + <meta name="flags" content="dom"> + <meta name="assert" content="StyleSheet and CSSStyleSheet objects have the properties specified in their interfaces"> + <script src="/resources/testharness.js" type="text/javascript"></script> + <script src="/resources/testharnessreport.js" type="text/javascript"></script> + <style id="styleElement" type="text/css" media="all" title="internal style sheet" disabled="disabled"> + * { margin: 0; padding: 0; } + </style> + </head> + <body> + <noscript>Test not run - javascript required.</noscript> + <div id="log"></div> + <script type="text/javascript"> + var sheet = document.getElementById("styleElement").sheet; + // Initial rule list is of size 1 + // Can add a rule at first index + test(function() { + assert_equals(sheet.cssRules.length, 1); + sheet.insertRule("p { color: green; }", 0); + assert_equals(sheet.cssRules.length, 2); + assert_equals(sheet.cssRules.item(0).cssText, "p { color: green; }"); + }, "add_rule"); + + // Can delete rules until rule list is empty + test(function() { + sheet.deleteRule(0); + assert_equals(sheet.cssRules.length, 1); + sheet.deleteRule(0); + assert_equals(sheet.cssRules.length, 0); + }, "delete_rule"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic-ref.html b/testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic-ref.html new file mode 100644 index 0000000000..bc9cadebf1 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic-ref.html @@ -0,0 +1,7 @@ +<!doctype html> +<title>(Ref #1) CSS Test Reference</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@mozilla.com"> +<link rel="author" title="Cheng You Bai" href="mailto:cyb.ai.815@gmail.com"> +<link rel="help" href="https://dom.spec.whatwg.org/#dom-characterdata-replacedata"> +<style>.pass { color: green }</style> +<div class="pass">Should be green</div>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic.html b/testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic.html new file mode 100644 index 0000000000..3ee5937c95 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic.html @@ -0,0 +1,12 @@ +<!doctype html> +<title>(Test #1) CSS Test: Dynamic changes to the stylesheet contents using replaceData are reflected</title> +<link rel="match" href="stylesheet-replacedata-dynamic-ref.html"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@mozilla.com"> +<link rel="author" title="Cheng You Bai" href="mailto:cyb.ai.815@gmail.com"> +<link rel="help" href="https://dom.spec.whatwg.org/#dom-characterdata-replacedata"> +<style>.fail { color: green }</style> +<div class="pass">Should be green</div> +<script> + document.body.offsetTop; + document.querySelector('style').firstChild.replaceData(1, 4, "pass"); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/cssom/stylesheet-same-origin.css b/testing/web-platform/tests/css/cssom/stylesheet-same-origin.css new file mode 100644 index 0000000000..d61a8dd67c --- /dev/null +++ b/testing/web-platform/tests/css/cssom/stylesheet-same-origin.css @@ -0,0 +1,3 @@ +body { + padding: 10px; +} diff --git a/testing/web-platform/tests/css/cssom/stylesheet-same-origin.sub.html b/testing/web-platform/tests/css/cssom/stylesheet-same-origin.sub.html new file mode 100644 index 0000000000..6ad55190da --- /dev/null +++ b/testing/web-platform/tests/css/cssom/stylesheet-same-origin.sub.html @@ -0,0 +1,83 @@ +<!doctype html> +<html> +<head> + <meta charset="utf-8"> + <title>CSSOM - CSSStylesheet should support origins</title> + <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstylesheet-interface"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <link id="crossorigin" href="http://www1.{{host}}:{{ports[http][1]}}/css/cssom/stylesheet-same-origin.css" rel="stylesheet"> + <link id="sameorigin" href="stylesheet-same-origin.css" rel="stylesheet"> + <link id="sameorigindata" href="data:text/css,.green-text{color:rgb(0, 255, 0)}" rel="stylesheet"> + <link id="redirect-sameorigin-to-crossorigin" + href="/common/redirect.py?location=http://www1.{{host}}:{{ports[http][1]}}/css/cssom/stylesheet-same-origin.css" + rel="stylesheet"> + <link id="redirect-crossorigin-to-sameorigin" + href="http://www1.{{host}}:{{ports[http][1]}}/common/redirect.py?location=http://{{host}}:{{ports[http][0]}}/css/cssom/stylesheet-same-origin.css" + rel="stylesheet"> + <link id="loaderror" href="support/malformed-http-response.asis" rel="stylesheet"> + + <script> + var crossorigin = document.getElementById("crossorigin").sheet; + var redirectSameOriginToCrossOrigin = document.getElementById("redirect-sameorigin-to-crossorigin").sheet; + var redirectCrossOriginToSameOrigin = document.getElementById("redirect-crossorigin-to-sameorigin").sheet; + var loadError = document.getElementById("loaderror").sheet; + var sameorigin = document.getElementById("sameorigin").sheet; + var sameorigindata = document.getElementById("sameorigindata").sheet; + + function doOriginCleanCheck(sheet, name) { + assert_equals(sheet.cssRules.length, 1, name + " stylesheet.cssRules should be accessible."); + sheet.insertRule("#test { margin: 10px; }", 1); + assert_equals(sheet.cssRules.length, 2, name + " stylesheet.insertRule should be accessible."); + sheet.deleteRule(0); + assert_equals(sheet.cssRules.length, 1, name + " stylesheet.deleteRule should be accessible."); + } + + function doOriginDirtyCheck(sheet) { + assert_throws_dom("SecurityError", + function () { + sheet.cssRules; + }, + 'stylesheet.cssRules should throw SecurityError.'); + assert_throws_dom("SecurityError", + function () { + sheet.insertRule("#test { margin: 10px; }", 1); + }, + 'stylesheet.insertRule should throw SecurityError.'); + + assert_throws_dom("SecurityError", + function () { + sheet.deleteRule(0); + }, + 'stylesheet.deleteRule should throw SecurityError.'); + } + + test(function() { + doOriginDirtyCheck(crossorigin); + }, "Origin-clean check in cross-origin CSSOM Stylesheets"); + + test(function() { + doOriginDirtyCheck(redirectSameOriginToCrossOrigin); + }, "Origin-clean check in cross-origin CSSOM Stylesheets (redirect from same-origin to cross-origin)"); + + test(function() { + doOriginDirtyCheck(redirectCrossOriginToSameOrigin); + }, "Origin-clean check in cross-origin CSSOM Stylesheets (redirect from cross-origin to same-origin)"); + + test(function() { + doOriginDirtyCheck(loadError); + }, "Origin-clean check in loading error CSSOM Stylesheets"); + + test(function() { + doOriginCleanCheck(sameorigin, "Same-origin"); + }, "Origin-clean check in same-origin CSSOM Stylesheets"); + + test(function() { + doOriginCleanCheck(sameorigindata, "data:css"); + }, "Origin-clean check in data:css CSSOM Stylesheets"); + + </script> +</head> +<body> +</html> diff --git a/testing/web-platform/tests/css/cssom/stylesheet-title.html b/testing/web-platform/tests/css/cssom/stylesheet-title.html new file mode 100644 index 0000000000..77b1df9677 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/stylesheet-title.html @@ -0,0 +1,39 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Test: StyleSheet's title attribute</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="help" href="https://drafts.csswg.org/cssom/#preferred-css-style-sheet-set-name"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-style-title"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style></style> +<style title=""></style> +<style title="Preferred"> + p { color: green; } +</style> +<style title="Not preferred"> + p { color: red; } +</style> +<p id="test-element">Should be green</p> +<script> +test(function() { + assert_equals( + getComputedStyle(document.getElementById("test-element")).color, + "rgb(0, 128, 0)", + "Preferred style should apply" + ); +}, "Preferred style sheet name"); + +test(function() { + let sheets = document.styleSheets; + let styleElements = Array.from(document.querySelectorAll("style")); + assert_equals(sheets.length, styleElements.length); + for (let i = 0; i < sheets.length; ++i) { + let titleAttr = styleElements[i].getAttribute("title"); + if (titleAttr === null || titleAttr === "") + assert_equals(sheets[i].title, null, "Empty title returns null"); + else + assert_equals(sheets[i].title, titleAttr, "Selected title is properly reflected"); + } +}, "StyleSheet.title"); +</script> diff --git a/testing/web-platform/tests/css/cssom/support/1x1-green.png b/testing/web-platform/tests/css/cssom/support/1x1-green.png Binary files differnew file mode 100644 index 0000000000..b98ca0ba0a --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/1x1-green.png diff --git a/testing/web-platform/tests/css/cssom/support/1x1-lime.png b/testing/web-platform/tests/css/cssom/support/1x1-lime.png Binary files differnew file mode 100644 index 0000000000..cb397fb090 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/1x1-lime.png diff --git a/testing/web-platform/tests/css/cssom/support/1x1-maroon.png b/testing/web-platform/tests/css/cssom/support/1x1-maroon.png Binary files differnew file mode 100644 index 0000000000..3f86b07219 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/1x1-maroon.png diff --git a/testing/web-platform/tests/css/cssom/support/1x1-navy.png b/testing/web-platform/tests/css/cssom/support/1x1-navy.png Binary files differnew file mode 100644 index 0000000000..9b9a03955b --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/1x1-navy.png diff --git a/testing/web-platform/tests/css/cssom/support/1x1-red.png b/testing/web-platform/tests/css/cssom/support/1x1-red.png Binary files differnew file mode 100644 index 0000000000..6bd73ac101 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/1x1-red.png diff --git a/testing/web-platform/tests/css/cssom/support/1x1-white.png b/testing/web-platform/tests/css/cssom/support/1x1-white.png Binary files differnew file mode 100644 index 0000000000..dd43faec54 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/1x1-white.png diff --git a/testing/web-platform/tests/css/cssom/support/60x60-gg-rr.png b/testing/web-platform/tests/css/cssom/support/60x60-gg-rr.png Binary files differnew file mode 100644 index 0000000000..84f5b2a4f1 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/60x60-gg-rr.png diff --git a/testing/web-platform/tests/css/cssom/support/60x60-green.png b/testing/web-platform/tests/css/cssom/support/60x60-green.png Binary files differnew file mode 100644 index 0000000000..b3c8cf3eb4 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/60x60-green.png diff --git a/testing/web-platform/tests/css/cssom/support/README b/testing/web-platform/tests/css/cssom/support/README new file mode 100644 index 0000000000..2e5f2ad073 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/README @@ -0,0 +1,28 @@ +CSS Global Support Directory +============================ + +This directory contains common support files (such as images and external +style sheets). These are sync'ed into the support directories of all our +test suites. If you have test-suite-specific support files, please add +them to the appropriate test-suite-specific support/ directory. + +If you add to a support/ directory, please run the tools/supportprop.py +script from the top of the repository to cascade support files into the +lower-level support directories. + +Description of the Common Support File Collection +------------------------------------------------- + +The 1x1-* images are all exactly one pixel. + +The swatch-* images all use 15x15 cells. + +The square-* images all use 15x15 cells with one pixel borders. + +The pattern-* images use cells of various sizes: + + pattern-grg-rgr-grg.png 20x20 + pattern-rgr-grg-rgr.png 20x20 + pattern-tr.png 15x15 + pattern-grg-rrg-rgg.png 15x15 + diff --git a/testing/web-platform/tests/css/cssom/support/a-green.css b/testing/web-platform/tests/css/cssom/support/a-green.css new file mode 100644 index 0000000000..b0dbb071d5 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/a-green.css @@ -0,0 +1 @@ +.a { color: green; } diff --git a/testing/web-platform/tests/css/cssom/support/b-green.css b/testing/web-platform/tests/css/cssom/support/b-green.css new file mode 100644 index 0000000000..a0473f5ca2 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/b-green.css @@ -0,0 +1 @@ +.b { color: green; }
\ No newline at end of file diff --git a/testing/web-platform/tests/css/cssom/support/black.css b/testing/web-platform/tests/css/cssom/support/black.css new file mode 100644 index 0000000000..833a0e0092 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/black.css @@ -0,0 +1,4 @@ +html { +background-color: black; +color: white; +}
\ No newline at end of file diff --git a/testing/web-platform/tests/css/cssom/support/c-red.css b/testing/web-platform/tests/css/cssom/support/c-red.css new file mode 100644 index 0000000000..d4ba5c64e9 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/c-red.css @@ -0,0 +1 @@ +.c { color: red; }
\ No newline at end of file diff --git a/testing/web-platform/tests/css/cssom/support/cat.png b/testing/web-platform/tests/css/cssom/support/cat.png Binary files differnew file mode 100644 index 0000000000..85dd732481 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/cat.png diff --git a/testing/web-platform/tests/css/cssom/support/constructable-import.css b/testing/web-platform/tests/css/cssom/support/constructable-import.css new file mode 100644 index 0000000000..d8ccc9d491 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/constructable-import.css @@ -0,0 +1,3 @@ +* { + color: red; +} diff --git a/testing/web-platform/tests/css/cssom/support/getComputedStyle-insets.js b/testing/web-platform/tests/css/cssom/support/getComputedStyle-insets.js new file mode 100644 index 0000000000..723990cafb --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/getComputedStyle-insets.js @@ -0,0 +1,375 @@ +export const testEl = document.createElement("div"); +export const containerForInflow = document.createElement("div"); +export const containerForAbspos = document.createElement("div"); +export const containerForFixed = document.createElement("div"); + +testEl.id = "test"; +containerForInflow.id = "container-for-inflow"; +containerForAbspos.id = "container-for-abspos"; +containerForFixed.id = "container-for-fixed"; + +containerForInflow.appendChild(testEl); +containerForAbspos.appendChild(containerForInflow); +containerForFixed.appendChild(containerForAbspos); +document.body.appendChild(containerForFixed); + +const stylesheet = document.createElement("style"); +stylesheet.textContent = ` + #container-for-inflow { + /* Content area: 100px tall, 200px wide */ + height: 100px; + width: 200px; + padding: 1px 2px; + border-width: 2px 4px; + margin: 4px 8px; + } + #container-for-abspos { + /* Padding area: 200px tall, 400px wide */ + height: 184px; + width: 368px; + padding: 8px 16px; + border-width: 16px 32px; + margin: 32px 64px; + position: relative; + } + #container-for-fixed { + /* Padding area: 300px tall, 600px wide */ + height: 172px; + width: 344px; + padding: 64px 128px; + border-width: 128px 256px; + margin: 256px 512px; + position: absolute; + transform: scale(1); + visibility: hidden; + } + [id ^= container] { + border-style: solid; + } +`; +document.head.prepend(stylesheet); + +function runTestsWithWM(data, testWM, cbWM) { + const { + style, + containingBlockElement, + containingBlockArea, + preservesPercentages, + preservesAuto, + canStretchAutoSize, + staticPositionX, + staticPositionY, + } = data; + + let cbHeight = containingBlockElement ? containingBlockElement.clientHeight : NaN; + let cbWidth = containingBlockElement ? containingBlockElement.clientWidth : NaN; + if (containingBlockElement && containingBlockArea == "content") { + const cs = getComputedStyle(containingBlockElement); + cbHeight -= parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom); + cbWidth -= parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight); + } + + const staticPositionTop = cbWM.blockStart == "top" || cbWM.inlineStart == "top" + ? staticPositionY : cbHeight - staticPositionY; + const staticPositionLeft = cbWM.blockStart == "left" || cbWM.inlineStart == "left" + ? staticPositionX : cbWidth - staticPositionX; + const staticPositionBottom = cbWM.blockStart == "bottom" || cbWM.inlineStart == "bottom" + ? staticPositionY : cbHeight - staticPositionY; + const staticPositionRight = cbWM.blockStart == "right" || cbWM.inlineStart == "right" + ? staticPositionX : cbWidth - staticPositionX; + + function serialize(declarations) { + return Object.entries(declarations).map(([p, v]) => `${p}: ${v}; `).join(""); + } + + function wmName(wm) { + return Object.values(wm.style).join(" "); + } + + function checkStyle(declarations, expected, msg) { + test(function() { + testEl.style.cssText = style + "; " + serialize(Object.assign({}, declarations, testWM.style)); + if (containingBlockElement) { + containingBlockElement.style.cssText = serialize(Object.assign({}, cbWM.style)); + } + const cs = getComputedStyle(testEl); + for (let [prop, value] of Object.entries(expected)) { + assert_equals(cs[prop], value, `'${prop}'`); + } + }, `${wmName(testWM)} inside ${wmName(cbWM)} - ${msg}`); + + testEl.style.cssText = ""; + if (containingBlockElement) { + containingBlockElement.style.cssText = ""; + } + } + + checkStyle({ + top: "1px", + left: "2px", + bottom: "3px", + right: "4px", + }, { + top: "1px", + left: "2px", + bottom: "3px", + right: "4px", + }, "Pixels resolve as-is"); + + checkStyle({ + top: "1em", + left: "2em", + bottom: "3em", + right: "4em", + "font-size": "10px", + }, { + top: "10px", + left: "20px", + bottom: "30px", + right: "40px", + }, "Relative lengths are absolutized into pixels"); + + if (preservesPercentages) { + checkStyle({ + top: "10%", + left: "25%", + bottom: "50%", + right: "75%", + }, { + top: "10%", + left: "25%", + bottom: "50%", + right: "75%", + }, "Percentages resolve as-is"); + } else { + checkStyle({ + top: "10%", + left: "25%", + bottom: "50%", + right: "75%", + }, { + top: cbHeight * 10 / 100 + "px", + left: cbWidth * 25 / 100 + "px", + bottom: cbHeight * 50 / 100 + "px", + right: cbWidth * 75 / 100 + "px", + }, "Percentages are absolutized into pixels"); + + checkStyle({ + top: "calc(10% - 1px)", + left: "calc(25% - 2px)", + bottom: "calc(50% - 3px)", + right: "calc(75% - 4px)", + }, { + top: cbHeight * 10 / 100 - 1 + "px", + left: cbWidth * 25 / 100 - 2 + "px", + bottom: cbHeight * 50 / 100 - 3 + "px", + right: cbWidth * 75 / 100 - 4 + "px", + }, "calc() is absolutized into pixels"); + } + + if (canStretchAutoSize) { + // Force overconstraintment by setting size or with insets that would result in + // negative size. Then the resolved value should be the computed one according to + // https://drafts.csswg.org/cssom/#resolved-value-special-case-property-like-top + + checkStyle({ + top: "1px", + left: "2px", + bottom: "3px", + right: "4px", + height: "0px", + width: "0px", + }, { + top: "1px", + left: "2px", + bottom: "3px", + right: "4px", + }, "Pixels resolve as-is when overconstrained"); + + checkStyle({ + top: "100%", + left: "100%", + bottom: "100%", + right: "100%", + }, { + top: cbHeight + "px", + left: cbWidth + "px", + bottom: cbHeight + "px", + right: cbWidth + "px", + }, "Percentages absolutize the computed value when overconstrained"); + } + + if (preservesAuto) { + checkStyle({ + top: "auto", + left: "auto", + bottom: "3px", + right: "4px", + }, { + top: "auto", + left: "auto", + bottom: "3px", + right: "4px", + }, "If start side is 'auto' and end side is not, 'auto' resolves as-is"); + + checkStyle({ + top: "1px", + left: "2px", + bottom: "auto", + right: "auto", + }, { + top: "1px", + left: "2px", + bottom: "auto", + right: "auto", + }, "If end side is 'auto' and start side is not, 'auto' resolves as-is"); + + checkStyle({ + top: "auto", + left: "auto", + bottom: "auto", + right: "auto", + }, { + top: "auto", + left: "auto", + bottom: "auto", + right: "auto", + }, "If opposite sides are 'auto', they resolve as-is"); + } else if (canStretchAutoSize) { + checkStyle({ + top: "auto", + left: "auto", + bottom: "3px", + right: "4px", + }, { + top: cbHeight - 3 + "px", + left: cbWidth - 4 + "px", + bottom: "3px", + right: "4px", + }, "If start side is 'auto' and end side is not, 'auto' resolves to used value"); + + checkStyle({ + top: "1px", + left: "2px", + bottom: "auto", + right: "auto", + }, { + top: "1px", + left: "2px", + bottom: cbHeight - 1 + "px", + right: cbWidth - 2 + "px", + }, "If end side is 'auto' and start side is not, 'auto' resolves to used value"); + + checkStyle({ + top: "auto", + left: "auto", + bottom: "auto", + right: "auto", + }, { + top: staticPositionTop + "px", + left: staticPositionLeft + "px", + bottom: staticPositionBottom + "px", + right: staticPositionRight + "px", + }, "If opposite sides are 'auto', they resolve to used value"); + } else { + checkStyle({ + top: "auto", + left: "auto", + bottom: "3px", + right: "4px", + }, { + top: "-3px", + left: "-4px", + bottom: "3px", + right: "4px", + }, "If start side is 'auto' and end side is not, 'auto' resolves to used value"); + + checkStyle({ + top: "1px", + left: "2px", + bottom: "auto", + right: "auto", + }, { + top: "1px", + left: "2px", + bottom: "-1px", + right: "-2px", + }, "If end side is 'auto' and start side is not, 'auto' resolves to used value"); + + checkStyle({ + top: "auto", + left: "auto", + bottom: "auto", + right: "auto", + }, { + top: "0px", + left: "0px", + bottom: "0px", + right: "0px", + }, "If opposite sides are 'auto', they resolve to used value"); + } +} + +const writingModes = [{ + style: { + "writing-mode": "horizontal-tb", + "direction": "ltr", + }, + blockStart: "top", + blockEnd: "bottom", + inlineStart: "left", + inlineEnd: "right", +}, { + style: { + "writing-mode": "horizontal-tb", + "direction": "rtl", + }, + blockStart: "top", + blockEnd: "bottom", + inlineStart: "right", + inlineEnd: "left", +}, { + style: { + "writing-mode": "vertical-lr", + "direction": "ltr", + }, + blockStart: "left", + blockEnd: "right", + inlineStart: "top", + inlineEnd: "bottom", +}, { + style: { + "writing-mode": "vertical-lr", + "direction": "rtl", + }, + blockStart: "left", + blockEnd: "right", + inlineStart: "bottom", + inlineEnd: "top", +}, { + style: { + "writing-mode": "vertical-rl", + "direction": "ltr", + }, + blockStart: "right", + blockEnd: "left", + inlineStart: "top", + inlineEnd: "bottom", +}, { + style: { + "writing-mode": "vertical-rl", + "direction": "rtl", + }, + blockStart: "right", + blockEnd: "left", + inlineStart: "bottom", + inlineEnd: "top", +}]; + +export function runTests(data) { + for (let testWM of writingModes) { + for (let cbWM of writingModes) { + runTestsWithWM(data, testWM, cbWM); + } + } +} diff --git a/testing/web-platform/tests/css/cssom/support/import-charset.css b/testing/web-platform/tests/css/cssom/support/import-charset.css new file mode 100644 index 0000000000..9f44090c94 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/import-charset.css @@ -0,0 +1 @@ +@charset "UTF-8"; diff --git a/testing/web-platform/tests/css/cssom/support/import-green.css b/testing/web-platform/tests/css/cssom/support/import-green.css new file mode 100644 index 0000000000..537104e663 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/import-green.css @@ -0,0 +1 @@ +.import { color: green; } diff --git a/testing/web-platform/tests/css/cssom/support/import-red.css b/testing/web-platform/tests/css/cssom/support/import-red.css new file mode 100644 index 0000000000..9945ef4711 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/import-red.css @@ -0,0 +1 @@ +.import { color: red; } diff --git a/testing/web-platform/tests/css/cssom/support/import-rule.css b/testing/web-platform/tests/css/cssom/support/import-rule.css new file mode 100644 index 0000000000..5edcef7e69 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/import-rule.css @@ -0,0 +1 @@ +@import "import-red.css"; diff --git a/testing/web-platform/tests/css/cssom/support/malformed-http-response.asis b/testing/web-platform/tests/css/cssom/support/malformed-http-response.asis new file mode 100644 index 0000000000..bc3c68d46d --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/malformed-http-response.asis @@ -0,0 +1 @@ +HAHAHA THIS IS NOT HTTP AND THE BROWSER SHOULD CONSIDER IT A NETWORK ERROR diff --git a/testing/web-platform/tests/css/cssom/support/pattern-grg-rgr-grg.png b/testing/web-platform/tests/css/cssom/support/pattern-grg-rgr-grg.png Binary files differnew file mode 100644 index 0000000000..9b88fbd811 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/pattern-grg-rgr-grg.png diff --git a/testing/web-platform/tests/css/cssom/support/pattern-grg-rrg-rgg.png b/testing/web-platform/tests/css/cssom/support/pattern-grg-rrg-rgg.png Binary files differnew file mode 100644 index 0000000000..fcf4f3fd7d --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/pattern-grg-rrg-rgg.png diff --git a/testing/web-platform/tests/css/cssom/support/pattern-rgr-grg-rgr.png b/testing/web-platform/tests/css/cssom/support/pattern-rgr-grg-rgr.png Binary files differnew file mode 100644 index 0000000000..d454e3a630 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/pattern-rgr-grg-rgr.png diff --git a/testing/web-platform/tests/css/cssom/support/pattern-tr.png b/testing/web-platform/tests/css/cssom/support/pattern-tr.png Binary files differnew file mode 100644 index 0000000000..8b4b25364e --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/pattern-tr.png diff --git a/testing/web-platform/tests/css/cssom/support/ruler-h-50%.png b/testing/web-platform/tests/css/cssom/support/ruler-h-50%.png Binary files differnew file mode 100644 index 0000000000..cf2eea6b43 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/ruler-h-50%.png diff --git a/testing/web-platform/tests/css/cssom/support/ruler-h-50px.png b/testing/web-platform/tests/css/cssom/support/ruler-h-50px.png Binary files differnew file mode 100644 index 0000000000..9f46583665 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/ruler-h-50px.png diff --git a/testing/web-platform/tests/css/cssom/support/ruler-v-100px.png b/testing/web-platform/tests/css/cssom/support/ruler-v-100px.png Binary files differnew file mode 100644 index 0000000000..a837eca222 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/ruler-v-100px.png diff --git a/testing/web-platform/tests/css/cssom/support/ruler-v-50px.png b/testing/web-platform/tests/css/cssom/support/ruler-v-50px.png Binary files differnew file mode 100644 index 0000000000..8414102802 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/ruler-v-50px.png diff --git a/testing/web-platform/tests/css/cssom/support/square-purple.png b/testing/web-platform/tests/css/cssom/support/square-purple.png Binary files differnew file mode 100644 index 0000000000..0f522d7872 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/square-purple.png diff --git a/testing/web-platform/tests/css/cssom/support/square-teal.png b/testing/web-platform/tests/css/cssom/support/square-teal.png Binary files differnew file mode 100644 index 0000000000..e567f51b91 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/square-teal.png diff --git a/testing/web-platform/tests/css/cssom/support/square-white.png b/testing/web-platform/tests/css/cssom/support/square-white.png Binary files differnew file mode 100644 index 0000000000..5853cbb238 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/square-white.png diff --git a/testing/web-platform/tests/css/cssom/support/support/README b/testing/web-platform/tests/css/cssom/support/support/README new file mode 100644 index 0000000000..ea8cb9ef35 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/support/README @@ -0,0 +1,4 @@ +The swatch-green.png file in this directory is really a RED swatch, +and the swatch-red.png file is really a green swatch. + +This directory is used to test relative URIs.
\ No newline at end of file diff --git a/testing/web-platform/tests/css/cssom/support/support/swatch-green.png b/testing/web-platform/tests/css/cssom/support/support/swatch-green.png Binary files differnew file mode 100644 index 0000000000..1caf25c992 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/support/swatch-green.png diff --git a/testing/web-platform/tests/css/cssom/support/support/swatch-red.png b/testing/web-platform/tests/css/cssom/support/support/swatch-red.png Binary files differnew file mode 100644 index 0000000000..0aa79b0c86 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/support/swatch-red.png diff --git a/testing/web-platform/tests/css/cssom/support/swatch-blue.png b/testing/web-platform/tests/css/cssom/support/swatch-blue.png Binary files differnew file mode 100644 index 0000000000..bf2759634d --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/swatch-blue.png diff --git a/testing/web-platform/tests/css/cssom/support/swatch-green.png b/testing/web-platform/tests/css/cssom/support/swatch-green.png Binary files differnew file mode 100644 index 0000000000..0aa79b0c86 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/swatch-green.png diff --git a/testing/web-platform/tests/css/cssom/support/swatch-lime.png b/testing/web-platform/tests/css/cssom/support/swatch-lime.png Binary files differnew file mode 100644 index 0000000000..55fd7fdaed --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/swatch-lime.png diff --git a/testing/web-platform/tests/css/cssom/support/swatch-orange.png b/testing/web-platform/tests/css/cssom/support/swatch-orange.png Binary files differnew file mode 100644 index 0000000000..d3cd498b52 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/swatch-orange.png diff --git a/testing/web-platform/tests/css/cssom/support/swatch-red.png b/testing/web-platform/tests/css/cssom/support/swatch-red.png Binary files differnew file mode 100644 index 0000000000..1caf25c992 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/swatch-red.png diff --git a/testing/web-platform/tests/css/cssom/support/swatch-teal.png b/testing/web-platform/tests/css/cssom/support/swatch-teal.png Binary files differnew file mode 100644 index 0000000000..0293ce89de --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/swatch-teal.png diff --git a/testing/web-platform/tests/css/cssom/support/swatch-white.png b/testing/web-platform/tests/css/cssom/support/swatch-white.png Binary files differnew file mode 100644 index 0000000000..1a7d4323d7 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/swatch-white.png diff --git a/testing/web-platform/tests/css/cssom/support/swatch-yellow.png b/testing/web-platform/tests/css/cssom/support/swatch-yellow.png Binary files differnew file mode 100644 index 0000000000..1591aa0e2e --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/swatch-yellow.png diff --git a/testing/web-platform/tests/css/cssom/support/test-bl.png b/testing/web-platform/tests/css/cssom/support/test-bl.png Binary files differnew file mode 100644 index 0000000000..904e24e996 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/test-bl.png diff --git a/testing/web-platform/tests/css/cssom/support/test-br.png b/testing/web-platform/tests/css/cssom/support/test-br.png Binary files differnew file mode 100644 index 0000000000..f413ff5c1a --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/test-br.png diff --git a/testing/web-platform/tests/css/cssom/support/test-inner-half-size.png b/testing/web-platform/tests/css/cssom/support/test-inner-half-size.png Binary files differnew file mode 100644 index 0000000000..e473bf80ef --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/test-inner-half-size.png diff --git a/testing/web-platform/tests/css/cssom/support/test-outer.png b/testing/web-platform/tests/css/cssom/support/test-outer.png Binary files differnew file mode 100644 index 0000000000..82eeace7fc --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/test-outer.png diff --git a/testing/web-platform/tests/css/cssom/support/test-tl.png b/testing/web-platform/tests/css/cssom/support/test-tl.png Binary files differnew file mode 100644 index 0000000000..f6ac0ef7e8 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/test-tl.png diff --git a/testing/web-platform/tests/css/cssom/support/test-tr.png b/testing/web-platform/tests/css/cssom/support/test-tr.png Binary files differnew file mode 100644 index 0000000000..59843ae54b --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/test-tr.png diff --git a/testing/web-platform/tests/css/cssom/support/xmlss-pi.xhtml b/testing/web-platform/tests/css/cssom/support/xmlss-pi.xhtml new file mode 100644 index 0000000000..8e22b50b25 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/support/xmlss-pi.xhtml @@ -0,0 +1 @@ +<?xml-stylesheet href='data:text/css,'?><html xmlns='http://www.w3.org/1999/xhtml'/> diff --git a/testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-count.html b/testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-count.html new file mode 100644 index 0000000000..5c2b9e6f46 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-count.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSSOM - Extensions to the Document Interface: StyleSheetList length reflects dynamically loaded and unloaded sheets</title> + <link rel="author" title="Jesse Bounds" href="mailto:jesse@codeforamerica.org"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#extensions-to-the-document-interface"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-stylesheetlist-interface"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#css-style-sheet-collections"> + <link rel="stylesheet" href="stylesheet.css" type="text/css"> + <meta name="flags" content="dom"> + <meta name="assert" content="The styleSheets length attribute must reflect the number of sheets at page load and after dynamically"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> + <div id="log"></div> + <script> + + // Get the Document's styleSheets attribute + var styleSheets = document.styleSheets; + + // Verify that the styleSheets list length is 1 due to "stylesheet.css" loaded in head section + test(function() { + assert_equals(styleSheets.length, 1, "styleSheets.length is incorrect:"); + }, "stylesheet.css should be loaded and styleSheets.length === 1"); + + // Verify that the styleSheets list length is 0 after removing the loaded sheet from the DOM + test(function() { + + // get the one and only sheet loaded + var sheet = styleSheets.item(0); + + // remove the sheet from the DOM + sheet.ownerNode.parentNode.removeChild(sheet.ownerNode) + + // assert that there are 0 styleSheets in the styleSheets property + assert_equals(styleSheets.length, 0, "styleSheets.length is incorrect:"); + + }, "stylesheet.css should be unloaded and styleSheets.length === 0"); + + // Verify that the styleSheets list length is back to 1 after loading a new sheet + async_test(function(t) { + + // create a css file reference + var fileReference = document.createElement("link"); + fileReference.setAttribute("rel", "stylesheet"); + fileReference.setAttribute("type", "text/css"); + fileReference.setAttribute("href", "stylesheet-1.css"); + fileReference.onerror = t.step_func_done(function() { + // assert that there is 1 styleSheet in the styleSheets property + assert_equals(styleSheets.length, 1, "styleSheets.length is incorrect:"); + }); + + // load the css file reference into the head section + var head = document.getElementsByTagName("HEAD")[0]; + head.appendChild(fileReference); + }, "stylesheet-1.css should be loaded and styleSheets.length === 1"); + + </script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-tree-order.html b/testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-tree-order.html new file mode 100644 index 0000000000..2824401cb2 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-tree-order.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSSOM - Extensions to the Document Interface: Stylesheet header load order</title> + <link rel="author" title="Jesse Bounds" href="mailto:jesse@codeforamerica.org"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#extensions-to-the-document-interface"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-stylesheetlist-interface"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#css-style-sheet-collections"> + <style title="aaa" type="text/css"> + H1 {border-width: 1; border: solid; text-align: center} + </style> + <link title="zebra" rel="stylesheet" href="zebra.css" type="text/css"> + <link title="kilo" rel="stylesheet" href="kilo.css" type="text/css"> + <link title="alpha" rel="stylesheet" href="alpha.css" type="text/css"> + <link title="zebra" rel="stylesheet" href="/directory01/zebra.css" type="text/css"> + <meta name="flags" content="dom"> + <meta name="assert" content="Document's style sheets created from HTTP Link headers are first in list and loaded in header order"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> + <div id="log"></div> + <script> + onload = function() { + // Get the Document's styleSheets attribute + var styleSheets = document.styleSheets; + + // Verify that the styleSheets list length is 5 + test(function() { + assert_equals(styleSheets.length, 5, "styleSheets.length is incorrect:"); + }, "styleSheets.length must be 5"); + + // Verify that titles of loaded sheets are as expected (in the correct order) + test(function() { + assert_equals(styleSheets.item(0).title, "aaa", "title for item 1 is incorrect:"); + }, "styleSheets item 0 title must be aaa"); + // Verify that titles of loaded sheets are as expected (in the correct order) + test(function() { + assert_equals(styleSheets.item(1).title, "zebra", "title for item 1 is incorrect:"); + }, "styleSheets item 1 title must be zebra"); + // Verify that titles of loaded sheets are as expected (in the correct order) + test(function() { + assert_equals(styleSheets.item(2).title, "kilo", "title for item 1 is incorrect:"); + }, "styleSheets item 0 title must be kilo"); + // Verify that titles of loaded sheets are as expected (in the correct order) + test(function() { + assert_equals(styleSheets.item(3).title, "alpha", "title for item 1 is incorrect:"); + }, "styleSheets item 0 title must be alpha"); + // Verify that titles of loaded sheets are as expected (in the correct order) + test(function() { + assert_equals(styleSheets.item(4).title, "zebra", "title for item 1 is incorrect:"); + }, "styleSheets item 0 title must be zebra"); + }; + </script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/ttwf-cssom-document-extension.html b/testing/web-platform/tests/css/cssom/ttwf-cssom-document-extension.html new file mode 100644 index 0000000000..0c9116dc4b --- /dev/null +++ b/testing/web-platform/tests/css/cssom/ttwf-cssom-document-extension.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSSOM - Extensions to the Document Interface: StyleSheetList length is 0 when no sheets loaded</title> + <link rel="author" title="Jesse Bounds" href="mailto:jesse@codeforamerica.org"> + <link rel="reviewer" title="Ms2ger" href="mailto:ms2ger@gmail.com"> <!-- 2012-06-17 --> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#extensions-to-the-document-interface"> + <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-stylesheetlist-interface"> + <meta name="flags" content="dom"> + <meta name="assert" content="The styleSheets attribute must return a StyleSheetList sequence representing the document style sheets."> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> + <div id="log"></div> + <script> + // Get the Document's styleSheets attribute + var styleSheets = document.styleSheets; + // Verify that the styleSheets list length is 0 because no stylesheets have been loaded + test(function() { + assert_equals(styleSheets.length, 0, "styleSheets.length is incorrect:"); + }) + </script> +</body> +</html> diff --git a/testing/web-platform/tests/css/cssom/variable-names.html b/testing/web-platform/tests/css/cssom/variable-names.html new file mode 100644 index 0000000000..5591411e6e --- /dev/null +++ b/testing/web-platform/tests/css/cssom/variable-names.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Tests for handling of CSS Custom Property names</title> +<link rel="help" href="https://drafts.csswg.org/css-variables/#serializing-custom-props"> +<meta name="author" title="Cameron McCormack" href="mailto:cam@mcc.id.au"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<script> + +// Valid custom property names, before and after CSS escaping. +var valid_names = [ + ["--a", "--a"], + ["--a;b", "--a\\;b"], + ["---", "---"], + ["--\\", "--\\\\"], + ["--ab", "--\\61 b"], + ["--0", "--\\30 "], +]; + +valid_names.forEach(function(t) { + var name = t[0]; + var escaped_name = t[1]; + + test(function() { + var e = document.createElement("span"); + e.style = escaped_name + ":value"; + + for (var after_refeeding = 0; after_refeeding <= 1; ++after_refeeding) { + var desc_suffix = (after_refeeding ? " (after " : " (before ") + + "serialization/re-parsing)"; + + assert_equals(e.style.length, 1, + "appears on specified style" + desc_suffix); + + assert_equals(e.style[0], name, + "name returned correctly from specified " + + "style indexed getter" + desc_suffix); + + assert_equals(e.style.getPropertyValue(name), "value", + "property value returned correctly from " + + "specified style getPropertyValue" + desc_suffix); + + e.style = e.style.cssText; + } + }, "custom property '" + name + "'"); +}); +</script> |