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/css-anchor-position | |
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/css-anchor-position')
97 files changed, 6384 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-001.html new file mode 100644 index 0000000000..f85c9248d9 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-001.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#determining"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; +} +.anchor1 { + anchor-name: --a1; + width: 10px; + height: 10px; + background: orange; +} +.target { + position: absolute; + width: anchor-size(--a1 width); + height: 10px; + background: lime; +} +</style> +<body onload="checkLayout('.target')"> + <!-- + All targets should find the 10px anchor, because it's the first + one in the pre-order DFS from the `relpos`. + --> + <div class="relpos"> + <div class="target" data-expected-width=10></div> + <div class="anchor1" style="width: 10px"> + <div class="anchor1" style="width: 20px"></div> + <div class="target" data-expected-width=10></div> + </div> + <div class="anchor1" style="width: 30px"></div> + <div class="target" data-expected-width=10></div> + <div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-002.html new file mode 100644 index 0000000000..88728f6f47 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-002.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#determining"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; +} +.abspos { + position: absolute; +} +.anchor1 { + anchor-name: --a1; + width: 10px; + height: 10px; + background: orange; +} +.target { + position: absolute; + width: anchor-size(--a1 width); + height: 10px; + background: lime; +} +</style> +<body onload="checkLayout('.target')"> + <div class="relpos"> + <div> + <div class="relpos"> + <div class="abspos"> + <div class="relpos"> + <div class="anchor1" style="position: absolute"></div> + <!-- This target should not find the anchor, because the anchor is + positioned. --> + <div class="target" data-expected-width=0></div> + </div> + <!-- This target should find the anchor, because the last containing + block has `position: relative`. --> + <div class="target" data-expected-width=10></div> + </div> + <!-- This target should not find the anchor, because the last containing + block has `position: absolute`. --> + <div class="target" data-expected-width=0></div> + </div> + </div> + <!-- This target should find the anchor, because the last containing block + is statically positioned. --> + <div class="target" data-expected-width=10></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-003.html new file mode 100644 index 0000000000..998e6433d2 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-003.html @@ -0,0 +1,136 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#determining"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; +} +.abspos { + position: absolute; +} +.anchor1 { + anchor-name: --a1; + width: 10px; + height: 10px; + background: orange; +} +.target { + position: absolute; + width: anchor-size(--a1 width); + height: 10px; + background: lime; +} +</style> +<body onload="checkLayout('.target')"> + <!-- In-flow and out-of-flow boxes in a containing block. --> + <div class="relpos"> + <div> + <div class="relpos"> + <div class="abspos"> + <div class="relpos"> + <div class="anchor1" style="position: absolute; width: 10px"></div> + <div class="anchor1" style="width: 20px"></div> + <div class="anchor1" style="position: absolute; width: 30px"></div> + <div class="anchor1" style="width: 40px"></div> + <div class="target" data-expected-width=20></div> + </div> + <div class="target" data-expected-width=10></div> + </div> + <div class="target" data-expected-width=0></div> + </div> + <div class="target" data-expected-width=10></div> + </div> + <div class="target" data-expected-width=10></div> + </div> + + <!-- In-flow boxes in ancestors, after the propagated ones. --> + <div class="relpos"> + <div> + <div class="relpos"> + <div class="abspos"> + <div class="relpos"> + <div class="anchor1" style="position: absolute; width: 10px"></div> + <div class="anchor1" style="width: 20px"></div> + <div class="target" data-expected-width=20></div> + </div> + <div class="anchor1" style="width: 50px"></div> + <div class="target" data-expected-width=10></div> + </div> + <div class="target" data-expected-width=0></div> + </div> + <div class="anchor1" style="width: 60px"></div> + <div class="target" data-expected-width=10></div> + </div> + <div class="anchor1" style="width: 70px"></div> + <div class="target" data-expected-width=10></div> + </div> + + <!-- Out-of-flow boxes in ancestors, after the propagated ones. --> + <div class="relpos"> + <div> + <div class="relpos"> + <div class="abspos"> + <div class="relpos"> + <div class="anchor1" style="position: absolute; width: 10px"></div> + <div class="anchor1" style="width: 20px"></div> + <div class="target" data-expected-width=20></div> + </div> + <div class="anchor1" style="position: absolute; width: 110px"></div> + <div class="target" data-expected-width=10></div> + </div> + <div class="target" data-expected-width=0></div> + </div> + <div class="target" data-expected-width=10></div> + </div> + <div class="anchor1" style="position: absolute; width: 100px"></div> + <div class="target" data-expected-width=10></div> + </div> + + <!-- In-flow boxes in ancestors, before the propagated ones. --> + <div class="relpos"> + <div class="anchor1" style="width: 100px"></div> + <div> + <div class="relpos"> + <div class="anchor1" style="width: 110px"></div> + <div class="abspos"> + <div class="anchor1" style="width: 120px"></div> + <div class="relpos"> + <div class="anchor1" style="position: absolute; width: 10px"></div> + <div class="anchor1" style="width: 20px"></div> + <div class="target" data-expected-width=20></div> + </div> + <div class="target" data-expected-width=120></div> + </div> + <div class="target" data-expected-width=110></div> + </div> + <div class="target" data-expected-width=100></div> + </div> + <div class="target" data-expected-width=100></div> + </div> + + <!-- Out-of-flow boxes in ancestors, before the propagated ones. --> + <div class="relpos"> + <div class="anchor1" style="position: absolute; width: 100px"></div> + <div> + <div class="relpos"> + <div class="anchor1" style="position: absolute; width: 110px"></div> + <div class="abspos"> + <div class="anchor1" style="position: absolute; width: 120px"></div> + <div class="relpos"> + <div class="anchor1" style="position: absolute; width: 10px"></div> + <div class="anchor1" style="width: 20px"></div> + <div class="target" data-expected-width=20></div> + </div> + <div class="target" data-expected-width=10></div> + </div> + <div class="target" data-expected-width=0></div> + </div> + <div class="target" data-expected-width=110></div> + </div> + <div class="target" data-expected-width=110></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-basics.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-basics.html new file mode 100644 index 0000000000..1d88b94137 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-basics.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<title>Tests basics of the 'anchor-name' property</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +<script src="/css/support/inheritance-testcommon.js"></script> +<script src="/css/support/interpolation-testcommon.js"></script> + +<div id="container"> + <div id="target"></div> +</div> + +<script> +// anchor-name: none | <dashed-ident> +test_valid_value('anchor-name', 'none'); +test_valid_value('anchor-name', '--foo'); +test_invalid_value('anchor-name', 'foo-bar'); +test_invalid_value('anchor-name', '--foo --bar') +test_invalid_value('anchor-name', '--foo, --bar') +test_invalid_value('anchor-name', '100px'); +test_invalid_value('anchor-name', '100%'); + +// Computed value: as specified +test_computed_value('anchor-name', 'none'); +test_computed_value('anchor-name', '--foo'); + +// Initial: none +// Inherited: no +assert_not_inherited('anchor-name', 'none', '--foo'); + +// Animation type: discrete +test_no_interpolation({ + property: 'anchor-name', + from: '--foo', + to: 'none', +}); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-cross-shadow.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-cross-shadow.html new file mode 100644 index 0000000000..cc494831a8 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-cross-shadow.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<title>Tests that the anchor element can be in a different tree scope</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#target-anchor-element"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.cb { + position: absolute; +} + +#host1::part(anchor) { + anchor-name: --a1; + margin-left: 15px; +} +#target1 { + position: absolute; + left: anchor(--a1 left); +} +</style> + +<div class="cb"> + <div id="host1"></div> + <div id="target1"></div> +</div> + +<div class="cb"> + <div id="host2"></div> +</div> + +<script> +test(() => { + host1.attachShadow({mode: 'open'}).innerHTML = '<div part="anchor"></div>'; + assert_equals(target1.offsetLeft, 15); +}, 'Should be able to set anchor-name to a shadow DOM part and anchor to it'); + +test(() => { + let shadow = host2.attachShadow({mode: 'open'}); + shadow.innerHTML = ` + <style> + :host { + anchor-name: --a2; + margin-left: 15px; + } + #target2 { + position: absolute; + left: anchor(--a2 left); + } + </style> + <div id="target2"></div> + `; + assert_equals(shadow.getElementById('target2').offsetLeft, 15); +}, 'Should be able to set anchor-name to the shadow host and anchor to it'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-in-shadow.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-in-shadow.html new file mode 100644 index 0000000000..7e505ed592 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-in-shadow.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<title>anchor-name is a tree scoped reference</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#target-anchor-element"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + body { margin-top: 0; } + #anchor { + anchor-name: --anchor; + } + #filler { + height: 100px; + } + #anchored { + position: absolute; + top: anchor(--anchor top); + } +</style> +<div id="host"></div> +<div id="filler"></div> +<div id="anchor"></div> +<div id="anchored"></div> +<script> + const host_root = host.attachShadow({mode:"open"}); + host_root.innerHTML = ` + <style> + div { anchor-name: --anchor; } + </style> + <div></div> + `; + + test(() => { + assert_equals(anchored.offsetTop, 100, "#anchored is positioned against #anchor"); + }, "anchor-name should not leak out of a shadow tree"); +</script> + +<div id="anchor_host" style="anchor-name: --anchor-host"></div> +<script> + const anchor_host_root = anchor_host.attachShadow({mode:"open"}); + anchor_host_root.innerHTML = ` + <style> + div { + position: absolute; + left: anchor(--anchor-host left, 37px); + } + </style> + <div id="anchored"></div> + `; + + test(() => { + assert_equals(anchor_host_root.querySelector("#anchored").offsetLeft, 37, "#anchored is positioned using fallback"); + }, "anchor() in shadow tree should not match host anchor-name"); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-inline-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-inline-001.html new file mode 100644 index 0000000000..024225fecb --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-inline-001.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#determining"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<style> +.container { + font-family: Ahem; + font-size: 10px; + line-height: 1; + width: 10em; +} +.relpos { + position: relative; +} +.abspos { + position: absolute; +} +.columns { + column-count: 2; + column-fill: auto; + column-gap: 10px; + column-width: 100px; + width: 210px; + height: 100px; +} +.anchor { + anchor-name: --a1; + background: orange; +} +.target { + position: absolute; + width: anchor-size(--a1 width); + height: 10px; + background: lime; +} +</style> +<body onload="checkLayout('.target')"> + <div class="container relpos xcolumns"> + <div style="height: 150px"></div> + <div class="relpos"> + <span class="relpos"> + <span class="relpos"> + <span class="anchor abspos">123</span> + <span class="anchor">12</span> + <span class="target" data-expected-width=20></span> + </span> + <span class="target" data-expected-width=30></span> + </span> + <span class="target" data-expected-width=30></span> + </div> + <div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-001.html new file mode 100644 index 0000000000..13ff9524b7 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-001.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<title>Anchors in a different containing block in multicol</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; +} +.columns { + column-count: 2; + column-fill: auto; + column-gap: 10px; + column-width: 100px; + width: 210px; + height: 100px; +} +.spacer { + height: 10px; + background: pink; +} +.anchor1 { + anchor-name: --a1; + margin-left: 10px; + width: 40px; + height: 20px; + background: orange; +} +.target { + position: absolute; + left: anchor(--a1 left); + top: anchor(--a1 top); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); + background: lime; + opacity: .3; +} +</style> +<body onload="checkLayout('.target')"> + <div class="columns"> + <div class="relpos"> + <div class="spacer"></div> + <div class="anchor1"></div> + </div> + <div class="relpos"> + <div class="target" + data-offset-x=0 data-offset-y=0 + data-expected-width=0 data-expected-height=0></div> + </div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-002.html new file mode 100644 index 0000000000..292b6f295a --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-002.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<title>Anchors in OOF in multicol</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; +} +.abspos { + position: absolute; +} +.columns { + column-count: 2; + column-fill: auto; + column-gap: 10px; + column-width: 100px; + width: 210px; + height: 100px; +} +.spacer { + height: 10px; + background: pink; +} +.anchor { + anchor-name: --a1; + margin-left: 10px; + width: 40px; + height: 60px; + background: orange; +} +.target { + position: absolute; + background: lime; + opacity: .5; + left: anchor(--a1 left); + top: anchor(--a1 top); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); +} +</style> +<body onload="checkLayout('.target')"> + <div class="spacer"></div> + <div class="columns"> + <div class="relpos"> + <div class="relpos"> + <div class="abspos"> + <div class="spacer"></div> + <div class="anchor"></div> + </div> + </div> + <div class="target" + data-offset-x=10 data-offset-y=10 + data-expected-width=40 data-expected-height=60></div> + </div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-003.html new file mode 100644 index 0000000000..a343a732ad --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-003.html @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<title>Anchor name resolution of OOF anchors in multicol</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; +} +.abspos { + position: absolute; +} +.columns { + column-count: 6; + column-fill: auto; + column-gap: 10px; + column-width: 20px; + width: 170px; + height: 50px; +} +.spacer { + height: 10px; + background: pink; +} +.anchor { + anchor-name: --a1; + margin-left: 5px; + width: 10px; + background: orange; +} +.target { + position: absolute; + background: lime; + opacity: .3; + left: anchor(--a1 left); + top: anchor(--a1 top); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); +} +</style> +<body onload="checkLayout('.target')"> + <div class="spacer"></div> + <div class="relpos"> + <div class="columns relpos"> + <div class="relpos"> + <div class="relpos"> + <div class="spacer"></div> + <div class="anchor abspos" style="top: 120px; height: 100px"></div> + <div class="anchor" style="height: 60px"></div> + <div class="target" + data-expected-width=40 data-expected-height=50></div> + </div> + <div class="target" + data-expected-width=70 data-expected-height=50></div> + </div> + <div class="target" + data-expected-width=70 data-expected-height=50></div> + </div> + <div class="target" + data-expected-width=70 data-expected-height=50></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-004.html new file mode 100644 index 0000000000..11287c0c60 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-004.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<title>Anchor name resolution of OOF anchors in multicol</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; +} +.abspos { + position: absolute; +} +.columns { + column-count: 6; + column-fill: auto; + column-gap: 10px; + column-width: 20px; + width: 170px; + height: 50px; +} +.spacer { + height: 10px; + background: pink; +} +.anchor { + anchor-name: --a1; + margin-left: 5px; + width: 10px; + background: orange; +} +.target { + position: absolute; + background: lime; + opacity: .3; + left: anchor(--a1 left); + top: anchor(--a1 top); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); +} +</style> +<body onload="checkLayout('.target')"> + <div class="spacer"></div> + <div class="relpos"> + <div class="columns relpos"> + <div class="relpos"> + <div class="spacer"></div> + <div class="anchor" style="height: 60px"></div> + <div class="anchor abspos" style="top: 120px; height: 100px"></div> + <div class="target" + data-expected-width=40 data-expected-height=50></div> + </div> + <div class="target" + data-expected-width=40 data-expected-height=50></div> + </div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-parse-invalid.html b/testing/web-platform/tests/css/css-anchor-position/anchor-parse-invalid.html new file mode 100644 index 0000000000..de4b0ffac4 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-parse-invalid.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<title>Tests values that are invalid at parse time for the anchor() function</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +// anchor() can only be used in inset properties +test_invalid_value('margin-top', 'anchor(--foo top)'); +test_invalid_value('height', 'anchor(--foo top)'); +test_invalid_value('font-size', 'anchor(--foo top)'); + +// Invalid parameter format +test_invalid_value('top', 'anchor(--foo, top)'); +test_invalid_value('top', 'anchor(--foo top,)'); +test_invalid_value('top', 'anchor(--foo top bottom)'); +test_invalid_value('top', 'anchor(--foo top, 10px 20%)'); +test_invalid_value('top', 'anchor(--foo top, 10px, 20%)'); + +// Anchor name must be a dashed ident +test_invalid_value('top', 'anchor(foo top)'); + +// Invalid anchor side values +test_invalid_value('top', 'anchor(--foo height)'); +test_invalid_value('top', 'anchor(--foo 10em)'); +test_invalid_value('top', 'anchor(--foo 100s)'); + +// Invalid fallback values +test_invalid_value('top', 'anchor(--foo top, 1)'); +test_invalid_value('top', 'anchor(--foo top, 100s)'); +test_invalid_value('top', 'anchor(--foo top, bottom)'); +test_invalid_value('top', 'anchor(--foo top, anchor(bar top))'); +test_invalid_value('top', 'anchor(--foo top, anchor-size(--bar height))'); + +// Invalid anchor values in calc tree +test_invalid_value('top', 'calc(anchor(foo top) + 10px + 10%)'); +test_invalid_value('top', 'calc(10px + 100 * anchor(--foo top, anchor(bar bottom)))'); +test_invalid_value('top', 'min(anchor(--foo top), anchor(--bar bottom), anchor-size(--baz height))'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-parse-valid.html b/testing/web-platform/tests/css/css-anchor-position/anchor-parse-valid.html new file mode 100644 index 0000000000..14d233e5f4 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-parse-valid.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<title>Tests parsing of the anchor() function</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +const anchorNames = [ + '', + '--foo', +]; + +const insetProperties = [ + 'left', + 'right', + 'top', + 'bottom', + 'inset-block-start', + 'inset-block-end', + 'inset-inline-start', + 'inset-inline-end', +]; + +const anchorSides = [ + 'left', + 'right', + 'top', + 'bottom', + 'start', + 'end', + 'self-start', + 'self-end', + 'center', + '50%', +]; + +const fallbacks = [ + null, + '1px', + '50%', + 'calc(1px + 50%)', + 'anchor(left)', + 'anchor(--bar left)', + 'anchor(--bar left, anchor(--baz right))', +]; + +// Tests basic combinations +for (let property of insetProperties) { + // Using a wrong anchor-side (e.g., `top: anchor(--foo left)`) doesn't cause a + // parse error, but triggers the fallback when resolved. + for (let name of anchorNames) { + for (let side of anchorSides) { + for (let fallback of fallbacks) { + let value = `anchor(${name ? name + ' ' : ''}${side}${fallback ? ', ' + fallback : ''})`; + test_valid_value(property, value); + } + } + } +} + +// Tests that anchor() can be used in a calc tree +test_valid_value('top', 'calc((anchor(--foo top) + anchor(--bar bottom)) / 2)'); +test_valid_value('top', 'anchor(--foo top, calc(anchor(--bar bottom) * 0.5))'); +test_valid_value('top', 'min(100px, 10%, anchor(--foo top), anchor(--bar bottom))'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-001.html new file mode 100644 index 0000000000..68e62e5479 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-001.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<title>Tests `anchor` function for top/left/bottom/right properties</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +#container { + position: relative; +} +#a1 { + anchor-name: --a1; + background: orange; + margin-left: 100px; + margin-top: 100px; + width: 100px; + height: 100px; +} +#a2 { + anchor-name: --a2; + background: purple; + margin-left: 500px; + margin-top: 100px; + width: 100px; + height: 100px; +} +#target { + background: green; + position: absolute; + left: anchor(--a1 right); + top: anchor(--a1 bottom); + right: anchor(--a2 left); + bottom: anchor(--a2 top); +} +#ref { + background: red; + position: absolute; + left: 200px; + top: 100px; + width: 300px; + height: 100px; +} +</style> +<body onload="checkLayout('#target')"> + <div id="container"> + <div id="a1"></div> + <div id="a2"></div> + <div id="ref"></div> + <div id="target" + data-offset-x=200 data-offset-y=100 + data-expected-width=300 data-expected-height=100></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-002.html new file mode 100644 index 0000000000..0e4b2808d3 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-002.html @@ -0,0 +1,72 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +#container { + position: relative; + transform: translate(0, 0); /* Make it a containing block. */ +} +#anchor1 { + anchor-name: --a1; + width: 5px; + height: 7px; + background: orange; +} +#anchor2 { + anchor-name: --a2; + width: 9px; + height: 11px; + background: blue; +} +#anchor3 { + anchor-name: --a3; + width: 13px; + height: 15px; + background: purple; +} +.target { + position: absolute; +} +</style> +<!-- + The anchors are in different containing blocks, but they still fulfill the + conditions: + * if it has a different containing block from the querying element, the last + containing block in its containing block chain before reaching the querying + element’s containing block is not, itself, positioned. + https://drafts.csswg.org/css-anchor-1/#determining + + From the definition of the "containing block": + https://drafts.csswg.org/css-position/#def-cb + properties such as `transform` can create a containing block without setting + the `position` property. +--> +<body onload="checkLayout('.target')"> + <div id="container"> + <div> + <div id="anchor1"></div> + </div> + <div class="target" style="left: anchor(--a1 right)" data-offset-x=5></div> + + <div> + <div style="transform: translate(0, 0)"> + <div style="position: absolute; left: 10px;"> + <div id="anchor2"></div> + </div> + </div> + </div> + <div class="target" style="left: anchor(--a2 right)" data-offset-x=19></div> + + <div> + <div style="transform: translate(0, 0)"> + <div style="position: fixed; left: 20px"> + <div id="anchor3"></div> + </div> + </div> + </div> + <div class="target" style="left: anchor(--a3 right)" data-offset-x=33></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-003.html new file mode 100644 index 0000000000..fe8ec3a8d6 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-003.html @@ -0,0 +1,58 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#determining"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.cb { + position: relative; +} +.not-positioned-cb { + transform: translate(0, 0); /* Make it a containing block. */ +} +.anchor1 { + anchor-name: --a1; +} +.size5x7 { + width: 5px; + height: 7px; + background: orange; +} +.size9x11 { + width: 9px; + height: 11px; + background: blue; +} +.target { + position: absolute; +} +</style> +<!-- + To determine the target anchor element, find the first element el in tree + order. + https://drafts.csswg.org/css-anchor-1/#determining +--> +<body onload="checkLayout('.target')"> + <div class="cb"> + <div class="anchor1 size5x7"></div> + <div class="anchor1 size9x11"></div> + <div class="target" style="left: anchor(--a1 right)" data-offset-x=5></div> + </div> + + <div class="cb"> + <div class="anchor1 size5x7"> + <div class="anchor1 size9x11"></div> + <div class="target" style="left: anchor(--a1 right)" data-offset-x=5></div> + </div> + <div class="target" style="left: anchor(--a1 right)" data-offset-x=5></div> + </div> + + <div class="cb"> + <div class="anchor1 size5x7 not-positioned-cb"> + <div class="anchor1 size9x11"></div> + <div class="target" style="left: anchor(--a1 right)" data-offset-x=9></div> + </div> + <div class="target" style="left: anchor(--a1 right)" data-offset-x=5></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-004.html new file mode 100644 index 0000000000..d6d4d64276 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-004.html @@ -0,0 +1,116 @@ +<!DOCTYPE html> +<title>The `anchor()` function with percentages</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; + width: 200px; + outline: 1px solid; +} +.vrl-rtl { + writing-mode: vertical-rl; + direction: rtl; +} +.spacer { + background: yellow; + width: 10px; + height: 10px; +} +#anchor { + anchor-name: --a1; + margin: 20px; + width: 100px; + height: 200px; + background: orange; +} +.target { + position: absolute; +} +</style> +<body onload="checkLayout('.target')"> + <div class="relpos"> + <div class="spacer"></div> + <div id="anchor"></div> + + <div class="target" style="left: anchor(--a1 0%)" + data-offset-x="20"></div> + <div class="target" style="left: anchor(--a1 20%)" + data-offset-x="40"></div> + <div class="target" style="left: anchor(--a1 50%)" + data-offset-x="70"></div> + <div class="target" style="left: anchor(--a1 center)" + data-offset-x="70"></div> + <div class="target" style="left: anchor(--a1 80%)" + data-offset-x="100"></div> + <div class="target" style="left: anchor(--a1 100%)" + data-offset-x="120"></div> + + <div class="target" style="right: anchor(--a1 0%)" + data-offset-x="20"></div> + <div class="target" style="right: anchor(--a1 20%)" + data-offset-x="40"></div> + <div class="target" style="right: anchor(--a1 50%)" + data-offset-x="70"></div> + <div class="target" style="right: anchor(--a1 center)" + data-offset-x="70"></div> + <div class="target" style="right: anchor(--a1 80%)" + data-offset-x="100"></div> + <div class="target" style="right: anchor(--a1 100%)" + data-offset-x="120"></div> + + <div class="target" style="top: anchor(--a1 0%)" + data-offset-y="30"></div> + <div class="target" style="top: anchor(--a1 20%)" + data-offset-y="70"></div> + <div class="target" style="top: anchor(--a1 50%)" + data-offset-y="130"></div> + <div class="target" style="top: anchor(--a1 center)" + data-offset-y="130"></div> + <div class="target" style="top: anchor(--a1 80%)" + data-offset-y="190"></div> + <div class="target" style="top: anchor(--a1 100%)" + data-offset-y="230"></div> + + <div class="target" style="bottom: anchor(--a1 0%)" + data-offset-y="30"></div> + <div class="target" style="bottom: anchor(--a1 20%)" + data-offset-y="70"></div> + <div class="target" style="bottom: anchor(--a1 50%)" + data-offset-y="130"></div> + <div class="target" style="bottom: anchor(--a1 center)" + data-offset-y="130"></div> + <div class="target" style="bottom: anchor(--a1 80%)" + data-offset-y="190"></div> + <div class="target" style="bottom: anchor(--a1 100%)" + data-offset-y="230"></div> + </div> + + <div class="vrl-rtl relpos"> + <div class="spacer"></div> + <div id="anchor"></div> + + <div class="target" style="left: anchor(--a1 0%)" + data-offset-x="170"></div> + <div class="target" style="left: anchor(--a1 100%)" + data-offset-x="70"></div> + + <div class="target" style="right: anchor(--a1 0%)" + data-offset-x="170"></div> + <div class="target" style="right: anchor(--a1 100%)" + data-offset-x="70"></div> + + <div class="target" style="top: anchor(--a1 0%)" + data-offset-y="220"></div> + <div class="target" style="top: anchor(--a1 100%)" + data-offset-y="20"></div> + + <div class="target" style="bottom: anchor(--a1 0%)" + data-offset-y="220"></div> + <div class="target" style="bottom: anchor(--a1 100%)" + data-offset-y="20"></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-borders-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-borders-001.html new file mode 100644 index 0000000000..5bbe3d7914 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-borders-001.html @@ -0,0 +1,135 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.cb { + position: relative; + border-bottom: 2px solid gray; +} +.not-positioned-cb { + transform: translate(0, 0); /* Make it a containing block. */ +} +.margins { margin: 5px 6px 7px 8px; } +.borders { border-width: 5px 6px 7px 8px; border-style: solid; } +.paddings { padding: 5px 6px 7px 8px; } +.spacer { + height: 9px; +} +.anchor1 { + anchor-name: --a1; + margin-left: 50px; + width: 31px; + height: 31px; + background: blue; +} +.target { + position: absolute; + left: anchor(--a1 left); + right: anchor(--a1 right); + top: anchor(--a1 top); + bottom: anchor(--a1 bottom); + background: orange; +} +</style> +<body onload="checkLayout('.target')"> + <!-- Margins/borders/paddings on the containing block. --> + <div class="cb margins"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target" + data-offset-x=50 data-offset-y=9 + data-expected-width=31 data-expected-height=31></div> + </div> + <div class="cb borders"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target" + data-offset-x=50 data-offset-y=9 + data-expected-width=31 data-expected-height=31></div> + </div> + <div class="cb paddings"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target" + data-offset-x=58 data-offset-y=14 + data-expected-width=31 data-expected-height=31></div> + </div> + + <!-- Margins/borders/paddings on the nested containing block. --> + <div class="cb"> + <div class="spacer"></div> + <div class="not-positioned-cb margins"> + <div class="anchor1"></div> + </div> + <div class="target" + data-offset-x=58 data-offset-y=14 + data-expected-width=31 data-expected-height=31></div> + </div> + <div class="cb"> + <div class="spacer"></div> + <div class="not-positioned-cb borders"> + <div class="anchor1"></div> + </div> + <div class="target" + data-offset-x=58 data-offset-y=14 + data-expected-width=31 data-expected-height=31></div> + </div> + <div class="cb"> + <div class="spacer"></div> + <div class="not-positioned-cb paddings"> + <div class="anchor1"></div> + </div> + <div class="target" + data-offset-x=58 data-offset-y=14 + data-expected-width=31 data-expected-height=31></div> + </div> + + <!-- Margins/borders/paddings on the anchor. --> + <div class="cb"> + <div class="spacer"></div> + <div class="anchor1 margins"></div> + <div class="target" + data-offset-x=50 data-offset-y=14 + data-expected-width=31 data-expected-height=31></div> + </div> + <div class="cb"> + <div class="spacer"></div> + <div class="anchor1 borders"></div> + <div class="target" + data-offset-x=50 data-offset-y=9 + data-expected-width=45 data-expected-height=43></div> + </div> + <div class="cb"> + <div class="spacer"></div> + <div class="anchor1 paddings"></div> + <div class="target" + data-offset-x=50 data-offset-y=9 + data-expected-width=45 data-expected-height=43></div> + </div> + + <!-- Margins/borders/paddings on the querying element. --> + <div class="cb"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target margins" + data-offset-x=58 data-offset-y=14 + data-expected-width=17 data-expected-height=19></div> + </div> + <div class="cb"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target borders" + data-offset-x=50 data-offset-y=9 + data-expected-width=31 data-expected-height=31></div> + </div> + <div class="cb"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target paddings" + data-offset-x=50 data-offset-y=9 + data-expected-width=31 data-expected-height=31></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-borders-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-borders-002.html new file mode 100644 index 0000000000..a3813750bf --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-borders-002.html @@ -0,0 +1,85 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.cb { + position: relative; + border-bottom: 2px solid gray; +} +.not-positioned-cb { + transform: translate(0, 0); /* Make it a containing block. */ +} +.scroller { overflow: scroll; } +.borders { border-width: 5px 6px 7px 8px; border-style: solid; } +.spacer { + height: 9px; +} +.anchor1 { + anchor-name: --a1; + margin-right: 50px; + width: 31px; + height: 31px; + background: red; +} +.target { + position: absolute; + left: anchor(--a1 left); + right: anchor(--a1 right); + top: anchor(--a1 top); + bottom: anchor(--a1 bottom); + background: lime; +} +</style> +<body> + <div class="spacer"></div> + <div class="cb scroller" dir="rtl"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target"></div> + </div> + <div class="cb scroller borders" dir="rtl"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target"></div> + </div> + <div class="cb"> + <div class="scroller borders" dir="rtl"> + <div class="spacer"></div> + <div class="anchor1"></div> + </div> + <div class="target"></div> + </div> + <div class="cb scroller borders" dir="rtl"> + <div class="not-positioned-cb"> + <div class="spacer"></div> + <div class="anchor1"></div> + </div> + <div class="target"></div> + </div> + <div class="cb scroller borders" dir="rtl"> + <div class="not-positioned-cb scroller borders"> + <div class="spacer"></div> + <div class="anchor1"></div> + </div> + <div class="target"></div> + </div> +<script> +function getBoundingClientRectAsArray(element) { + const rect = element.getBoundingClientRect(); + return [rect.left, rect.top, rect.right, rect.bottom]; +} + +const anchors = document.getElementsByClassName('anchor1'); +const targets = document.getElementsByClassName('target'); +for (let i = 0; i < targets.length; ++i) { + test(() => { + const anchor = anchors[i]; + const target = targets[i]; + assert_array_equals(getBoundingClientRectAsArray(anchor), + getBoundingClientRectAsArray(target)); + }); +} +</script> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-001.html new file mode 100644 index 0000000000..5b5ee82990 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-001.html @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<title>Tests `anchor` function when anchor positions are changed dynamically</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +#container { + position: relative; +} +#a1 { + anchor-name: --a1; + background: orange; + margin-left: 100px; + margin-top: 100px; + width: 50px; + height: 50px; +} +.after #a1 { + width: 100px; + height: 100px; +} +#a2 { + anchor-name: --a2; + background: purple; + margin-left: 250px; + margin-top: 350px; + width: 100px; + height: 100px; +} +.after #a2 { + margin-left: 500px; + margin-top: 100px; +} +#target { + background: green; + position: absolute; + left: anchor(--a1 right); + top: anchor(--a1 bottom); + right: anchor(--a2 left); + bottom: anchor(--a2 top); +} +#ref { + background: red; + position: absolute; + left: 200px; + top: 100px; + width: 300px; + height: 100px; +} +</style> +<body id="container"> + <div id="a1"></div> + <div id="a2"></div> + <div id="ref"></div> + <div id="target" + data-offset-x=200 data-offset-y=100 + data-expected-width=300 data-expected-height=100></div> +<script> +document.body.offsetTop; // Force layout. +container.classList.add('after'); +checkLayout('#target'); +</script> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-002.html new file mode 100644 index 0000000000..f70f93441d --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-002.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +#container { + position: relative; +} +#anchor1 { + anchor-name: --a1; +} +#anchor2 { + anchor-name: --a2; +} +#anchor1, #anchor2 { + width: 5px; + height: 7px; + background: orange; +} +.after #anchor1, .after #anchor2 { + width: 10px; +} +.target { + position: absolute; +} +</style> +<body> + <div id="container"> + <!-- When the anchor is in the same containing block. --> + <div id="anchor1"></div> + <div class="target" style="left: anchor(--a1 right)" data-offset-x=5></div> + <div class="target" style="width: anchor-size(--a1 width)" data-expected-width=5></div> + + <!-- When the anchor is in a different containing block. --> + <div> + <div id="anchor2"></div> + </div> + <div class="target" style="left: anchor(--a2 right)" data-offset-x=5></div> + <div class="target" style="width: anchor-size(--a2 width)" data-expected-width=5></div> + </div> +<script> +checkLayout('.target'); +container.classList.add('after'); +for (const element of document.getElementsByClassName('target')) { + if (element.dataset.offsetX === '5') + element.dataset.offsetX = '10'; + if (element.dataset.expectedWidth === '5') + element.dataset.expectedWidth = '10'; +} +checkLayout('.after .target'); +</script> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-003.html new file mode 100644 index 0000000000..04ccd56c77 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-003.html @@ -0,0 +1,105 @@ +<!DOCTYPE html> +<title>Tests that anchor layout changes in another BFC cause relayout on the anchored element</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.containing-block { + position: absolute; +} +.anchor { + anchor-name: --a1; + width: 50px; + height: 70px; + background: orange; +} +.after .anchor { + width: 70px; + height: 50px; +} +.target { + position: absolute; + left: anchor(--a1 right); + top: anchor(--a1 bottom); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); + background: green; +} + +/* Various types of BFC as the containing block of the anchor */ +.float { + float: left; +} +.table { + display: table; +} +.inline-block { + display: inline-block; + vertical-align: bottom; +} +.contain { + contain: layout; +} +.scroller { + overflow: scroll; + width: 20px; + height: 20px; +} +</style> +<body> + <div class="containing-block"> + <div class="float"> + <div class="anchor"></div> + </div> + <div class="target"></div> + </div> + + <div class="containing-block"> + <div class="table"> + <div class="anchor"></div> + </div> + <div class="target"></div> + </div> + + <div class="containing-block"> + <div class="inline-block"> + <div class="anchor"></div> + </div> + <div class="target"></div> + </div> + + <div class="containing-block"> + <div class="contain"> + <div class="anchor"></div> + </div> + <div class="target"></div> + </div> + + <div class="containing-block"> + <div class="scroller"> + <div class="anchor"></div> + </div> + <div class="target"></div> + </div> + +<script> +for (const element of document.getElementsByClassName('target')) { + element.dataset.offsetX = '50'; + element.dataset.offsetY = '70'; + element.dataset.expectedWidth = '50'; + element.dataset.expectedHeight = '70'; +} +checkLayout('.target'); + +document.body.classList.add('after'); +for (const element of document.getElementsByClassName('target')) { + element.dataset.offsetX = '70'; + element.dataset.offsetY = '50'; + element.dataset.expectedWidth = '70'; + element.dataset.expectedHeight = '50'; +} +checkLayout('.after .target'); +</script> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-004.html new file mode 100644 index 0000000000..b818e23c7f --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-004.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.cb { + position: relative; +} +#anchor1 { + anchor-name: --a1; + margin-left: 15px; + width: 30px; + height: 20px; + background: red; +} +.after #anchor1 { + margin-left: 50px; +} +.target { + position: absolute; + left: anchor(--a1 left); + top: anchor(--a1 top); + right: anchor(--a1 right); + bottom: anchor(--a1 bottom); + background: lime; +} +</style> +<body onload="run_test()"> + <div class="cb"> + <div style="contain: strict; height: 50px"> + <div class="spacer" style="height: 10px"></div> + <div id="anchor1"></div> + </div> + + <div class="target" + data-offset-x=50 data-offset-y=10 + data-expected-width=30 data-expected-height=20></div> + </div> +<script> +function run_test() { + document.body.classList.add('after'); + checkLayout('.target'); +} +</script> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-grid-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-grid-001.html new file mode 100644 index 0000000000..8178f212fe --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-grid-001.html @@ -0,0 +1,82 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.cb { + position: relative; +} +.columns { + column-count: 3; + column-fill: auto; + column-gap: 10px; + width: 620px; + height: 100px; +} +.grid { + display: grid; + grid-template-columns: repeat(2, 100px); + grid-template-rows: 50px 100px; +} +.spacer { + background: yellow; +} +.anchor1 { + anchor-name: --a1; + grid-column: 2; + grid-row: 2; + border: 2px solid orange; + border-width: 5px 6px 7px 8px; +} +.target { + grid-column: 2; + grid-row: 2; + position: absolute; + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); + background: lime; + opacity: .2; +} +.target1-l { + left: anchor(--a1 left); + top: anchor(--a1 top); + width: 8px; +} +.target1-r { + right: anchor(--a1 right); + bottom: anchor(--a1 bottom); + width: 6px; +} +.target1-t { + left: anchor(--a1 left); + top: anchor(--a1 top); + height: 5px; +} +.target1-b { + right: anchor(--a1 right); + bottom: anchor(--a1 bottom); + height: 5px; +} +</style> +<body onload="checkLayout('.target')"> + <div> + <div class="spacer" style="height: 10px"></div> + <div class="columns"> + <div class="spacer" style="height: 10px"></div> + <div class="grid cb"> + <div>1</div> + <div>2</div> + <div>3</div> + <div class="anchor1"></div> + + <div class="target target1-l" data-offset-x=100 data-expected-height=100></div> + <div class="target target1-r" data-offset-x=404 data-expected-height=100></div> + <div class="target target1-t" data-offset-y=0 data-expected-width=310></div> + <div class="target target1-b" data-offset-y=95 data-expected-width=310></div> + </div> + </div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-001.html new file mode 100644 index 0000000000..09acf6be8b --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-001.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<style> +#container { + position: relative; + font-family: Ahem; + font-size: 10px; + line-height: 1; + width: 10em; +} +#anchor1 { + anchor-name: --a1; +} +.target { + position: absolute; +} +</style> +<body onload="checkLayout('.target')"> + <div id="container"> + <div>spacer</div> + <div> + <br> + 0123<span id="anchor1">456</span>78 + </div> + + <div class="target" style="left: anchor(--a1 left)" data-offset-x=40></div> + <div class="target" style="right: anchor(--a1 right)" data-offset-x=70></div> + <div class="target" style="top: anchor(--a1 top)" data-offset-y=20></div> + <div class="target" style="bottom: anchor(--a1 bottom)" data-offset-y=30></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-002.html new file mode 100644 index 0000000000..c366db4b28 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-002.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<style> +#container { + position: relative; + font-family: Ahem; + font-size: 10px; + line-height: 1; + width: 10em; +} +#anchor1 { + anchor-name: --a1; +} +.target { + position: absolute; +} +</style> +<body onload="checkLayout('.target')"> + <div id="container"> + <div>spacer</div> + <div> + <br> + <!-- The following line wraps between "5" and "7". --> + 0123<span id="anchor1">45 789</span>000 + </div> + + <div class="target" style="left: anchor(--a1 left)" data-offset-x=0></div> + <div class="target" style="right: anchor(--a1 right)" data-offset-x=60></div> + <div class="target" style="top: anchor(--a1 top)" data-offset-y=20></div> + <div class="target" style="bottom: anchor(--a1 bottom)" data-offset-y=40></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-003.html new file mode 100644 index 0000000000..17ab508583 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-003.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<style> +#container { + position: relative; + font-family: Ahem; + font-size: 10px; + line-height: 1; + width: 10em; +} +#anchor1 { + anchor-name: --a1; +} +.target { + position: absolute; +} +</style> +<body onload="checkLayout('.target')"> + <div id="container"> + <div>spacer</div> + <div> + <br> + <!-- The bidi reordering creates two fragments of the `span` in a line. --> + a<span id="anchor1">1‮2‭</span>z + </div> + + <div class="target" style="left: anchor(--a1 left)" data-offset-x=10></div> + <div class="target" style="right: anchor(--a1 right)" data-offset-x=40></div> + <div class="target" style="top: anchor(--a1 top)" data-offset-y=20></div> + <div class="target" style="bottom: anchor(--a1 bottom)" data-offset-y=30></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-004.html new file mode 100644 index 0000000000..58da8be7c7 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-004.html @@ -0,0 +1,175 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<style> +body > div { + font-family: Ahem; + font-size: 10px; + line-height: 1; + width: 10em; +} +.cb { + position: relative; +} +.columns { + column-count: 3; + column-fill: auto; + column-gap: 1em; + column-width: 10em; + orphans: 1; + widows: 1; + width: 32em; + height: 50px; + background: yellow; +} +.anchor1 { + anchor-name: --a1; + color: red; +} +.target { + position: absolute; + background: lime; + opacity: .2; +} +.target1-pos { + left: anchor(--a1 left); + top: anchor(--a1 top); + right: anchor(--a1 right); + bottom: anchor(--a1 bottom); +} +.target1-size { + left: anchor(--a1 left); + top: anchor(--a1 top); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); +} +</style> +<body onload="checkLayout('.target')"> + <!-- The inline anchor appear in a single line inline containing block. --> + <div class="cb"> + <div>spacer</div> + <div> + 0 + <span class="cb"> + 12 + <span class="anchor1">a1</span> + 34 + <span class="target target1-pos" + data-offset-x=30 data-offset-y=0 + data-expected-width=20 data-expected-height=10></span> + <span class="target target1-size" + data-offset-x=30 data-offset-y=0 + data-expected-width=20 data-expected-height=10></span> + </span> + <span class="target target1-pos" + data-offset-x=50 data-offset-y=10 + data-expected-width=20 data-expected-height=10></span> + <span class="target target1-size" + data-offset-x=50 data-offset-y=10 + data-expected-width=20 data-expected-height=10></span> + </div> + <span class="target target1-pos" + data-offset-x=50 data-offset-y=10 + data-expected-width=20 data-expected-height=10></span> + <span class="target target1-size" + data-offset-x=50 data-offset-y=10 + data-expected-width=20 data-expected-height=10></span> + </div> + + <!-- The inline anchor and inline containing block wrap to two lines. --> + <div class="cb"> + <div> + 0 + <span class="cb"> + 12 + <span class="anchor1">a1 a1 a1</span> + 345 + <span class="target target1-pos" + data-offset-x=-20 data-offset-y=0 + data-expected-width=100 data-expected-height=20></span> + <span class="target target1-size" + data-offset-x=-20 data-offset-y=0 + data-expected-width=100 data-expected-height=20></span> + </span> + <span class="target target1-pos" + data-offset-x=0 data-offset-y=0 + data-expected-width=100 data-expected-height=20></span> + <span class="target target1-size" + data-offset-x=0 data-offset-y=0 + data-expected-width=100 data-expected-height=20></span> + </div> + <span class="target target1-pos" + data-offset-x=0 data-offset-y=0 + data-expected-width=100 data-expected-height=20></span> + <span class="target target1-size" + data-offset-x=0 data-offset-y=0 + data-expected-width=100 data-expected-height=20></span> + </div> + + <!-- The inline anchor and inline containing block have forced line breaks. --> + <div class="cb"> + <div> + 0 + <span class="cb"> + 12 + <span class="anchor1">a1<br>a1</span> + 345 + <span class="target target1-pos" + data-offset-x=-20 data-offset-y=0 + data-expected-width=70 data-expected-height=20></span> + <span class="target target1-size" + data-offset-x=-20 data-offset-y=0 + data-expected-width=70 data-expected-height=20></span> + </span> + <span class="target target1-pos" + data-offset-x=0 data-offset-y=0 + data-expected-width=70 data-expected-height=20></span> + <span class="target target1-size" + data-offset-x=0 data-offset-y=0 + data-expected-width=70 data-expected-height=20></span> + </div> + <span class="target target1-pos" + data-offset-x=0 data-offset-y=0 + data-expected-width=70 data-expected-height=20></span> + <span class="target target1-size" + data-offset-x=0 data-offset-y=0 + data-expected-width=70 data-expected-height=20></span> + </div> + + <!-- The inline anchor and inline containing block wrap to two columns. --> + <div class="cb columns"> + <div class="spacer" style="height: 90px"></div> + <div> + 0 + <span class="cb"> + 12 + <span class="anchor1">a1 a1 a1</span> + 345 + <span class="target target1-pos" + data-offset-x=30 data-offset-y=-40 + data-expected-width=80 data-expected-height=50></span> + <span class="target target1-size" + data-offset-x=30 data-offset-y=-40 + data-expected-width=80 data-expected-height=50></span> + </span> + <span class="target target1-pos" + data-offset-x=160 data-offset-y=0 + data-expected-width=80 data-expected-height=50></span> + <span class="target target1-size" + data-offset-x=160 data-offset-y=0 + data-expected-width=80 data-expected-height=50></span> + </div> + <span class="target target1-pos" + data-offset-x=160 data-offset-y=0 + data-expected-width=80 data-expected-height=50></span> + <span class="target target1-size" + data-offset-x=160 data-offset-y=0 + data-expected-width=80 data-expected-height=50></span> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-001.html new file mode 100644 index 0000000000..777df1b84c --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-001.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<style> +#container { + position: relative; + font-family: Ahem; + font-size: 10px; + line-height: 1; + width: 10em; +} +.columns { + column-width: 100px; + column-count: 3; + column-gap: 10px; + height: 100px; +} +#anchor1 { + anchor-name: --a1; + background: blue; +} +.target { + position: absolute; +} +</style> +<body onload="checkLayout('.target')"> + <div id="container"> + <div>spacer</div> + <div class="columns"> + <div style="height: 150px"></div> + <div id="anchor1" style="height: 100px"></div> + </div> + + <div class="target" style="left: anchor(--a1 left)" data-offset-x=110></div> + <div class="target" style="right: anchor(--a1 right)" data-offset-x=320></div> + <div class="target" style="top: anchor(--a1 top)" data-offset-y=10></div> + <div class="target" style="bottom: anchor(--a1 bottom)" data-offset-y=110></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-002.html new file mode 100644 index 0000000000..748f9ad2b5 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-002.html @@ -0,0 +1,109 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.cb { + transform: translate(0); /* Make it a containing block. */ + border-width: 5px 6px 7px 8px; + border-style: solid; + padding: 5px 6px 7px 8px; +} +.columns { + column-count: 4; + column-fill: auto; + column-gap: 10px; + column-width: 100px; + width: 320px; + height: 100px; +} +.spacer { + height: 10px; + background: pink; +} +.anchor1 { + anchor-name: --a1; + margin-left: 10px; + width: 50px; + height: 90px; + background: blue; +} +.target { + position: absolute; +} +.fixed { + position: fixed; +} +.target1 { + left: anchor(--a1 left); + top: anchor(--a1 top); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); + background: lime; + opacity: .3; +} +.target1-rb { + right: anchor(--a1 right); + bottom: anchor(--a1 bottom); + width: 10px; + height: 10px; + background: purple; +} +</style> +<body onload="checkLayout('.target')"> + <p>The green box should be a union of blue boxes, + and the purple box should be at the right-bottom of the green box. + </p> + <div class="spacer" style="height: 10px"></div> + <div class="cb"> + <div class="columns"> + <div class="spacer" style="height: 30px"></div> + <div class="cb"> + <!-- This spacer fills up to the middle of the 2nd column. --> + <div class="spacer" style="height: 130px"></div> + <div class="anchor1"></div> + <div class="spacer" style="height: 100px"></div> + + <!-- The containing block of querying elements is block-fragmented. --> + <div class="target target1" + data-offset-x=18 data-offset-y=65 + data-expected-width=160 data-expected-height=100></div> + <div class="target target1-rb" + data-offset-x=168 data-offset-y=155></div> + <div class="target fixed target1" + data-offset-x=26 data-offset-y=70 + data-expected-width=160 data-expected-height=100></div> + <div class="target fixed target1-rb" + data-offset-x=176 data-offset-y=160></div> + </div> + + <!-- The containing block of querying elements is a multi-column. --> + <div class="target target1" + data-offset-x=144 data-offset-y=5 + data-expected-width=160 data-expected-height=100></div> + <div class="target target1-rb" + data-offset-x=294 data-offset-y=95></div> + <div class="target fixed target1" + data-offset-x=152 data-offset-y=10 + data-expected-width=160 data-expected-height=100></div> + <div class="target fixed target1-rb" + data-offset-x=302 data-offset-y=100></div> + </div> + + <!-- The containing block of querying elements is not fragmented. --> + <div class="target target1" + data-offset-x=144 data-offset-y=5 + data-expected-width=160 data-expected-height=100></div> + <div class="target target1-rb" + data-offset-x=294 data-offset-y=95></div> + <div class="target fixed target1" + data-offset-x=152 data-offset-y=10 + data-expected-width=160 data-expected-height=100></div> + <div class="target fixed target1-rb" + data-offset-x=302 data-offset-y=100></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-003.html new file mode 100644 index 0000000000..3685d0782c --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-003.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<title>Tests anchors on out-of-flow boxes</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#determining"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; +} +.columns { + column-count: 2; + column-fill: auto; + column-gap: 10px; + column-width: 100px; + width: 210px; + height: 50px; +} +.anchor1 { + anchor-name: --a1; + position: absolute; + width: 10px; + height: 30px; + background: orange; +} +.target { + position: absolute; + height: anchor-size(--a1 height); + background: lime; +} +</style> +<body onload="checkLayout('.target')"> + <div class="spacer" style="height: 10px"></div> + <div class="relpos"> + <div class="columns"> + <div class="relpos"> + <div class="spacer" style="height: 30px"></div> + <div class="anchor1"></div> + <div class="target" data-expected-height=0></div> + </div> + <div class="target" data-expected-height=50></div> + </div> + <div class="target" data-expected-height=50></div> + </div> + + <div class="spacer" style="height: 10px"></div> + <div class="relpos"> + <div class="columns"> + <div class="spacer" style="height: 10px"></div> + <div class="relpos"> + <div class="spacer" style="height: 10px"></div> + <div class="relpos"> + <div class="spacer" style="height: 10px"></div> + <div class="anchor1"></div> + <div class="target" data-expected-height=0></div> + </div> + <div class="target" data-expected-height=50></div> + </div> + <div class="target" data-expected-height=50></div> + </div> + <div class="target" data-expected-height=50></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-004.html new file mode 100644 index 0000000000..dea289f07e --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-004.html @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<title>Tests resolving anchor-name conflicts in multicol</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.cb { + transform: translate(0); /* Make it a containing block. */ + border-width: 5px 6px 7px 8px; + border-style: solid; + padding: 5px 6px 7px 8px; +} +.columns { + column-count: 5; + column-fill: auto; + column-gap: 10px; + column-width: 100px; + width: 540px; + height: 100px; +} +.spacer { + height: 10px; + background: pink; +} +.anchor1 { + anchor-name: --a1; + margin-left: 10px; + width: 40px; + height: 90px; + background: blue; +} +.target { + position: absolute; +} +.target1 { + left: anchor(--a1 left); + top: anchor(--a1 top); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); + background: lime; + opacity: .3; +} +.target1-rb { + right: anchor(--a1 right); + bottom: anchor(--a1 bottom); + width: 10px; + height: 10px; + background: purple; +} +</style> +<body onload="checkLayout('.target')"> + <p>The green box should be a union of blue boxes, + and the purple box should be at the right-bottom of the green box. + </p> + <div class="spacer" style="height: 10px"></div> + <div class="columns"> + <div class="spacer" style="height: 70px"></div> + <div class="cb"> + <!-- This spacer fills up to the middle of the 2nd column. --> + <div class="spacer" style="height: 60px"></div> + <div class="anchor1"></div> + <div class="cb"> + <div class="spacer" style="height: 120px"></div> + <div class="anchor1" style="width: 20px"></div> + + <div class="target target1" + data-offset-x=18 data-offset-y=65 + data-expected-width=130 data-expected-height=100></div> + <div class="target target1-rb" + data-offset-x=138 data-offset-y=155></div> + </div> + + <div class="target target1" + data-offset-x=18 data-offset-y=25 + data-expected-width=150 data-expected-height=100></div> + <div class="target target1-rb" + data-offset-x=158 data-offset-y=115></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-005.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-005.html new file mode 100644 index 0000000000..92f468ceda --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-005.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<title>Overflow pushing anchors to later fragmentainers than querying element in multicol</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; +} +.abspos { + position: absolute; +} +.columns { + column-count: 2; + column-fill: auto; + column-gap: 10px; + column-width: 100px; + width: 210px; + height: 100px; +} +.spacer { + height: 10px; + background: pink; +} +.anchor1 { + anchor-name: --a1; + margin-left: 10px; + width: 40px; + height: 80px; + background: orange; +} +.target { + position: absolute; + background: lime; + opacity: 1; +} +.target1 { + left: anchor(--a1 left); + top: anchor(--a1 top); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); +} +</style> +<body onload="checkLayout('.target')"> + <div class="spacer" style="height: 10px"></div> + <div class="columns"> + <div class="relpos"> + <div style="height: 50px"> + <div class="spacer" style="height: 110px"></div> + <div class="relpos" style="height: 50px"> + <div class="anchor1 abspos"></div> + </div> + </div> + <div style="height: 50px"> + <div class="target target1" + data-expected-width=40 data-expected-height=80></div> + </div> + </div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-006.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-006.html new file mode 100644 index 0000000000..2b9f55c024 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-006.html @@ -0,0 +1,71 @@ +<!DOCTYPE html> +<title>Tests two OOF anchors in different containing blocks in multicol.</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; +} +.columns { + column-count: 3; + column-fill: auto; + column-gap: 10px; + column-width: 100px; + width: 320px; + height: 50px; +} +.anchor1 { + anchor-name: --a1; + position: absolute; + width: 10px; + height: 30px; + background: orange; +} +.anchor2 { + anchor-name: --a2; + position: absolute; + width: 30px; + height: 30px; + background: purple; +} +.target { + position: absolute; + width: 5px; + background: lime; +} +.target1 { + left: anchor(--a1 left); + top: anchor(--a1 top); + height: anchor-size(--a1 height); +} +.target2 { + left: anchor(--a2 left); + top: anchor(--a2 top); + height: anchor-size(--a2 height); +} +</style> +<body onload="checkLayout('.target')"> + <div class="spacer" style="height: 10px"></div> + <div class="relpos"> + <div class="columns"> + <div class="relpos"> + <div class="spacer" style="height: 30px"></div> + <div class="anchor1"></div> + </div> + <div class="spacer" style="height: 70px"></div> + <div class="relpos"> + <div class="spacer" style="height: 10px"></div> + <div class="anchor2"></div> + </div> + <div class="target target1" data-expected-height=50></div> + <div class="target target2" data-expected-height=30></div> + </div> + <div class="target target1" data-expected-height=50></div> + <div class="target target2" data-expected-height=30></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-colspan-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-colspan-001.html new file mode 100644 index 0000000000..705f872a79 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-colspan-001.html @@ -0,0 +1,100 @@ +<!DOCTYPE html> +<title>Anchors in column spanners in multicol</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; +} +.columns { + column-count: 2; + column-fill: auto; + column-gap: 10px; + column-width: 100px; + width: 210px; + height: 100px; +} +.colspan { + column-span: all; +} +.spacer { + height: 10px; + background: pink; +} +.anchor1 { + anchor-name: --a1; + margin-left: 10px; + width: 40px; + height: 20px; + background: orange; +} +.target { + position: absolute; + left: anchor(--a1 left); + top: anchor(--a1 top); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); + background: lime; + opacity: .3; +} +</style> +<body onload="checkLayout('.target')"> + <div class="relpos"> + <div class="spacer" style="height: 10px"></div> + <div class="columns relpos"> + <div class="spacer" style="height: 40px"></div> + <div class="relpos"> + <div class="colspan"> + <div class="relpos"> + <!-- + The containing block chain of this anchor is `relpos`, `colspan`, + and `columns`, skipping the 2nd `relpos`, because the containing + block of the spanner is the multicol container. + https://drafts.csswg.org/css-multicol/#column-span + --> + <div class="anchor1"></div> + <!-- + The containing block of this target is inside a spanner. + --> + <div class="target" + data-offset-x=10 data-offset-y=0 + data-expected-width=40 data-expected-height=20></div> + </div> + <!-- + The containing block of this target is the multicol container. + --> + <div class="target" + data-offset-x=10 data-offset-y=20 + data-expected-width=40 data-expected-height=20></div> + </div> + <!-- + The containing block of this target is in the multicol, + but outside of the spanner. + This should not find the anchor, because the containing block of the + spanner is the multicol container. + --> + <div class="target" + data-offset-x=0 data-offset-y=0 + data-expected-width=0 data-expected-height=0></div> + </div> + <div class="spacer" style="height: 80px"></div> + <!-- + The containing block of this target is the multicol container. + --> + <div class="target" + data-offset-x=10 data-offset-y=20 + data-expected-width=40 data-expected-height=20></div> + </div> + <!-- + The containing block of this target is outside of the multicol. + --> + <div class="target" + data-offset-x=10 data-offset-y=30 + data-expected-width=40 data-expected-height=20></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-colspan-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-colspan-002.html new file mode 100644 index 0000000000..b6e720da18 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-colspan-002.html @@ -0,0 +1,76 @@ +<!DOCTYPE html> +<title>Anchors in column-spanner in multicol</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.relpos { + position: relative; +} +.columns { + column-count: 2; + column-fill: auto; + column-gap: 10px; + column-width: 100px; + width: 210px; + height: 100px; +} +.colspan { + column-span: all; +} +.spacer { + height: 10px; + background: pink; +} +.anchor1 { + anchor-name: --a1; + margin-left: 10px; + width: 40px; + background: blue; +} +.anchor1 .spacer { + background: inherit; +} +.target { + position: absolute; + left: anchor(--a1 left); + top: anchor(--a1 top); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); + background: lime; + opacity: .3; +} +</style> +<body onload="checkLayout('.target')"> + <div class="relpos"> + <div class="spacer" style="height: 10px"></div> + <div class="columns relpos"> + <div class="spacer" style="height: 50px"></div> + <div class="relpos"> + <div class="anchor1"> + <div class="spacer" style="height: 10px"></div> + <div class="colspan" style="height: 20px"></div> + <div class="spacer" style="height: 30px"></div> + </div> + <!-- + When anchors are split by the column spanners, OOFs laid out in a + multicol (i.e., the containing block is within a multicol) can't + compute the united bounding box, because the coordinate system in the + multicol can't express united rectangles across columns that are not + lined up. + <div class="target"></div> + --> + </div> + <div class="target" + data-offset-x=10 data-offset-y=20 + data-expected-width=150 data-expected-height=60></div> + </div> + <div class="target" + data-offset-x=10 data-offset-y=30 + data-expected-width=150 data-expected-height=60></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-fixed-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-fixed-001.html new file mode 100644 index 0000000000..4321c20def --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-fixed-001.html @@ -0,0 +1,77 @@ +<!DOCTYPE html> +<title>Anchors on fixed positioned objects in multicol</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.transform { + transform: translate(0); +} +.columns { + column-count: 2; + column-fill: auto; + column-gap: 10px; + column-width: 100px; + width: 210px; + height: 50px; +} +.colspan { + column-span: all; +} +.spacer { + height: 10px; + background: pink; +} +.anchor { + anchor-name: --a1; + background: orange; + margin-left: 10px; + width: 40px; + height: 10px; +} +.fixedpos { + position: fixed; + margin-left: 20px; + width: 20px; + height: 30px; +} +.target { + position: absolute; + background: lime; + opacity: .3; + left: anchor(--a1 left); + top: anchor(--a1 top); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); +} +</style> +<body onload="checkLayout('.target')"> + <div class="transform"> + <div class="spacer" style="height: 10px"></div> + <div class="columns"> + <div class="transform"> + <div class="spacer" style="height: 20px"></div> + <div class="transform"> + <div class="spacer" style="height: 20px"></div> + <div class="anchor fixedpos"></div> + <div class="anchor"></div> + <div class="target" + data-offset-x="10" data-offset-y="20" + data-expected-width=40 data-expected-height=10></div> + </div> + <div class="target" + data-offset-x="20" data-offset-y="0" + data-expected-width=130 data-expected-height=50></div> + </div> + <div class="target" + data-offset-x="20" data-offset-y="10" + data-expected-width=130 data-expected-height=50></div> + </div> + <div class="target" + data-offset-x="20" data-offset-y="10" + data-expected-width=130 data-expected-height=50></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html new file mode 100644 index 0000000000..d83b8ddd95 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html @@ -0,0 +1,79 @@ +<!DOCTYPE html> +<title>Anchors in nested multicol</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.cb { + position: relative; + border-style: solid; + border-width: 3px 2px 4px 1px; + padding: 3px 2px 4px 1px; +} +.columns { + column-count: 4; + column-fill: auto; + column-gap: 10px; + column-width: 150px; + width: 630px; + height: 100px; +} +.columns .columns { + column-count: 2; + column-width: 60px; + width: 130px; + height: 200px; +} +.spacer { + background: pink; +} +.anchor1 { + anchor-name: --a1; + margin-left: 10px; + width: 20px; + height: 150px; + background: blue; +} +.target { + position: absolute; + left: anchor(--a1 left); + top: anchor(--a1 top); + width: anchor-size(--a1 width); + height: anchor-size(--a1 height); + background: lime; + opacity: .3; +} +</style> +<body onload="checkLayout('.target')"> + <p>The green box should be a union of blue boxes, + and the purple box should be at the right-bottom of the green box. + </p> + <div class="cb"> + <div class="spacer" style="height: 10px"></div> + <div class="columns"> + <div class="cb"> + <div class="spacer" style="height: 110px"></div> + <div class="columns"> + <div class="cb"> + <div class="spacer" style="height: 60px"></div> + <div class="anchor1"></div> + <div class="spacer" style="height: 80px"></div> + <div class="target" + data-offset-x=11 data-offset-y=-19 + data-expected-width=180 data-expected-height=100></div> + </div> + </div> + <div class="target" + data-offset-x=13 data-offset-y=97 + data-expected-width=180 data-expected-height=100></div> + </div> + </div> + <div class="target" + data-offset-x=175 data-offset-y=13 + data-expected-width=180 data-expected-height=100></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-principal-box.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-principal-box.html new file mode 100644 index 0000000000..9bb1fd4c4d --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-principal-box.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<title>anchor-name only applies to elements which generate a principal box</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#target-anchor-element"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + body { margin-top: 0; } + #outer { + anchor-name: --anchor; + display: contents; + } + #inner { + anchor-name: --anchor; + } + #filler { + height: 100px; + } + #anchored { + position: absolute; + top: anchor(--anchor top); + } +</style> +<div id="outer"> + <div id="filler"></div> + <div id="inner"></div> +</div> +<div id="anchored"></div> +<script> + test(() => { + assert_equals(anchored.offsetTop, 100, "#anchored is positioned against #inner"); + }, "anchor-name should only apply to elements which generate a principal box"); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-001.html new file mode 100644 index 0000000000..c96f6eef55 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-001.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<title>Top-layer element can anchor to non-top-layer absolutely positioned element</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#target-anchor-element"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="match" href="anchor-position-top-layer-ref.html"> + +<style> +#anchor { + position: absolute; + top: 300px; + left: 200px; + width: 100px; + height: 100px; + background: orange; + anchor-name: --a; +} + +#target { + top: anchor(--a top); + left: anchor(--a right); + width: 100px; + height: 100px; + background: lime; + anchor-scroll: --a; +} + +body { + margin: 0; + height: 300vh; +} + +dialog { + margin: 0; + border: 0; + padding: 0; + inset: auto; +} + +dialog::backdrop { + background: transparent; +} +</style> + +<div id="anchor"></div> +<dialog id="target"></dialog> + +<script> +target.showModal(); +document.scrollingElement.scrollTop = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-002.html new file mode 100644 index 0000000000..e626e6b935 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-002.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<title>Top-layer element can anchor to non-top-layer fixed positioned element</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#target-anchor-element"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="match" href="anchor-position-top-layer-ref.html"> + +<style> +#anchor { + position: fixed; + top: 200px; + left: 200px; + width: 100px; + height: 100px; + background: orange; + anchor-name: --a; +} + +#target { + top: anchor(--a top); + left: anchor(--a right); + width: 100px; + height: 100px; + background: lime; + anchor-scroll: --a; +} + +body { + margin: 0; + height: 300vh; +} + +dialog { + margin: 0; + border: 0; + padding: 0; + inset: auto; +} + +dialog::backdrop { + background: transparent; +} +</style> + +<div id="anchor"></div> +<dialog id="target"></dialog> + +<script> +target.showModal(); +document.scrollingElement.scrollTop = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-003.html new file mode 100644 index 0000000000..39f3c362c7 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-003.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<title>Top-layer element can anchor to preceeding top-layer absolutely positioned element</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#target-anchor-element"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="match" href="anchor-position-top-layer-ref.html"> + +<style> +#anchor { + position: absolute; + top: 300px; + left: 200px; + width: 100px; + height: 100px; + background: orange; + anchor-name: --a; +} + +#target { + top: anchor(--a top); + left: anchor(--a right); + width: 100px; + height: 100px; + background: lime; + anchor-scroll: --a; +} + +body { + margin: 0; + height: 300vh; +} + +dialog { + margin: 0; + border: 0; + padding: 0; + inset: auto; +} + +dialog::backdrop { + background: transparent; +} +</style> + +<dialog id="anchor"></dialog> +<dialog id="target"></dialog> + +<script> +anchor.showModal(); +target.showModal(); +document.scrollingElement.scrollTop = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-004.html new file mode 100644 index 0000000000..8e189e0e7b --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-004.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<title>Top-layer element can anchor to preceeding top-layer fixed positioned element</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#target-anchor-element"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="match" href="anchor-position-top-layer-ref.html"> + +<style> +#anchor { + position: fixed; + top: 200px; + left: 200px; + width: 100px; + height: 100px; + background: orange; + anchor-name: --a; +} + +#target { + top: anchor(--a top); + left: anchor(--a right); + width: 100px; + height: 100px; + background: lime; + anchor-scroll: --a; +} + +body { + margin: 0; + height: 300vh; +} + +dialog { + margin: 0; + border: 0; + padding: 0; + inset: auto; +} + +dialog::backdrop { + background: transparent; +} +</style> + +<dialog id="anchor"></dialog> +<dialog id="target"></dialog> + +<script> +anchor.showModal(); +target.showModal(); +document.scrollingElement.scrollTop = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-005.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-005.html new file mode 100644 index 0000000000..d9e4fa86e1 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-005.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<title>Non-top-layer element cannot anchor to top-layer element</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#target-anchor-element"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="match" href="anchor-position-top-layer-ref.html"> + +<style> +#anchor { + position: absolute; + top: 300px; + left: 200px; + width: 100px; + height: 100px; + background: orange; + anchor-name: --a; +} + +#target { + position: fixed; + top: anchor(--a bottom, 200px); + left: anchor(--a left, 300px); + width: 100px; + height: 100px; + background: lime; + anchor-scroll: --a; +} + +body { + margin: 0; + height: 300vh; +} + +dialog { + margin: 0; + border: 0; + padding: 0; + inset: auto; +} + +dialog::backdrop { + background: transparent; +} +</style> + +<dialog id="anchor"></dialog> +<div id="target"></div> + +<script> +anchor.showModal(); +document.scrollingElement.scrollTop = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-006.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-006.html new file mode 100644 index 0000000000..5f5cd67a1d --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-006.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<title>Top-layer element cannot anchor to succeeding top-layer element</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#target-anchor-element"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="match" href="anchor-position-top-layer-ref.html"> + +<style> +#anchor { + position: absolute; + top: 300px; + left: 200px; + width: 100px; + height: 100px; + background: orange; + anchor-name: --a; +} + +#target { + position: fixed; + top: anchor(--a bottom, 200px); + left: anchor(--a left, 300px); + width: 100px; + height: 100px; + background: lime; + anchor-scroll: --a; +} + +body { + margin: 0; + height: 300vh; +} + +dialog { + margin: 0; + border: 0; + padding: 0; +} + +dialog::backdrop { + background: transparent; +} +</style> + +<dialog id="anchor"></dialog> +<dialog id="target"></dialog> + +<script> +target.showModal(); +anchor.showModal(); +document.scrollingElement.scrollTop = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-ref.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-ref.html new file mode 100644 index 0000000000..dc7f77f2b3 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-ref.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<title>Tests anchor positioning with top-layer elements</title> + +<style> +body { + margin: 0; + height: 300vh; +} + +#anchor { + position: fixed; + top: 200px; + left: 200px; + width: 100px; + height: 100px; + background: orange; +} + +#target { + position: fixed; + top: 200px; + left: 300px; + width: 100px; + height: 100px; + background: lime; +} +</style> + +<div id="anchor"></div> +<div id="target"></div> + +<script> +document.scrollingElement.scrollTop = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-writing-modes-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-writing-modes-001.html new file mode 100644 index 0000000000..c75a7c1e39 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-writing-modes-001.html @@ -0,0 +1,101 @@ +<!DOCTYPE html> +<title>Tests `anchor` function for `writing-mode`/`direction`s</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script><style> +:root, body { + margin: 0; +} +#container { + position: relative; + width: 600px; + height: 400px; +} +#a1 { + anchor-name: --a1; + background: orange; + margin-left: 100px; + margin-top: 100px; + width: 100px; + height: 100px; +} +.htb-rtl #a1 { margin-right: 400px; } +.vlr-rtl #a1 { margin-bottom: 200px; } +.vrl-ltr #a1 { margin-right: 400px; } +.vrl-rtl #a1 { margin-right: 400px; margin-bottom: 200px; } +#a2 { + anchor-name: --a2; + background: purple; + margin-left: 500px; + margin-top: 100px; + width: 100px; + height: 100px; +} +.vlr-ltr #a2 { margin-left: 300px; margin-top: 300px; } +.vlr-rtl #a2 { margin-left: 300px; margin-top: 300px; } +.vrl-ltr #a2 { margin-right: -600px; margin-top: 300px; } +.vrl-rtl #a2 { margin-right: -600px; margin-top: 300px; } +#target { + background: green; + position: absolute; + left: anchor(--a1 right); + top: anchor(--a1 bottom); + right: anchor(--a2 left); + bottom: anchor(--a2 top); +} +.htb-ltr { writing-mode: horizontal-tb; direction: ltr; } +.htb-rtl { writing-mode: horizontal-tb; direction: rtl; } +.vlr-ltr { writing-mode: vertical-lr; direction: ltr; } +.vlr-rtl { writing-mode: vertical-lr; direction: rtl; } +.vrl-ltr { writing-mode: vertical-rl; direction: ltr; } +.vrl-rtl { writing-mode: vertical-rl; direction: rtl; } +</style> +<body> + <div id="container"> + <div id="a1"></div> + <div id="a2"></div> + <div id="target"></div> + </div> +<script> +const classes = [ + 'htb-ltr', 'htb-rtl', + 'vlr-ltr', 'vlr-rtl', + 'vrl-ltr', 'vrl-rtl']; +const combinations = []; +for (const container_class of classes) { + for (const a1_class of classes) { + for (const a2_class of classes) { + for (const target_class of classes) { + combinations.push([container_class, a1_class, a2_class, target_class]); + } + } + } +} + +for (let i = 0; i < combinations.length; ++i) + run_test_index(i); + +function run_test_index(i) { + const combination = combinations[i]; + run_test(i, ...combination); +} + +function run_test(i, container_class, a1_class, a2_class, target_class) { + container.classList.add(container_class); + a1.classList.add(a1_class); + a2.classList.add(a2_class); + target.classList.add(target_class); + test(() => { + const bounds = target.getBoundingClientRect(); + assert_array_equals( + [bounds.left, bounds.top, bounds.right, bounds.bottom], + [200, 200, 500, 300]); + }, `${i}: ${container_class}/${a1_class}/${a2_class}/${target_class}`); + container.classList.remove(container_class); + a1.classList.remove(a1_class); + a2.classList.remove(a2_class); + target.classList.remove(target_class); +} +</script> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-writing-modes-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-writing-modes-002.html new file mode 100644 index 0000000000..834835ef21 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-writing-modes-002.html @@ -0,0 +1,103 @@ +<!DOCTYPE html> +<title>Tests logical `anchor` function for `writing-mode`/`direction`s</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.htb-ltr { writing-mode: horizontal-tb; direction: ltr; } +.htb-rtl { writing-mode: horizontal-tb; direction: rtl; } +.vlr-ltr { writing-mode: vertical-lr; direction: ltr; } +.vlr-rtl { writing-mode: vertical-lr; direction: rtl; } +.vrl-ltr { writing-mode: vertical-rl; direction: ltr; } +.vrl-rtl { writing-mode: vertical-rl; direction: rtl; } +.relpos { + position: relative; + outline: blue 1px solid; +} +.spacer { + width: 10px; + height: 10px; + background: yellow; +} +.anchor { + anchor-name: --a1; + margin: 5px; + width: 30px; + height: 30px; + background: orange; +} +.target { + position: absolute; + outline: 5px solid lime; +} +</style> +<body> + <template id="template"> + <div class="relpos"> + <div class="spacer"></div> + <div class="anchor"></div> + </div> + </template> +<script> +// Generate tests for all combinations. +// The first two entries are the side `start` and `end` should match in x-axis. +// The next two entries are the side `start` and `end` should match in y-axis. +const writingDirs = { + 'htb-ltr':['l', 'r', 't', 'b'], + 'htb-rtl':['r', 'l', 't', 'b'], + 'vrl-ltr':['r', 'l', 't', 'b'], + 'vrl-rtl':['r', 'l', 'b', 't'], + 'vlr-ltr':['l', 'r', 't', 'b'], + 'vlr-rtl':['l', 'r', 'b', 't'], +}; +const container = document.body; +const cb_template = template.content.firstElementChild; +for (const [writingDir, matches] of Object.entries(writingDirs)) { + const cb = cb_template.cloneNode(true); + cb.classList.add(writingDir); + createTarget(null, 'left: anchor(--a1 start)', matches[0], cb); + createTarget(null, 'left: anchor(--a1 end)', matches[1], cb); + createTarget(null, 'top: anchor(--a1 start)', matches[2], cb); + createTarget(null, 'top: anchor(--a1 end)', matches[3], cb); + createTarget(writingDir, 'left: anchor(--a1 self-start)', matches[0], cb); + createTarget(writingDir, 'left: anchor(--a1 self-end)', matches[1], cb); + createTarget(writingDir, 'top: anchor(--a1 self-start)', matches[2], cb); + createTarget(writingDir, 'top: anchor(--a1 self-end)', matches[3], cb); + container.appendChild(cb); +} + +function createTarget(className, style, match, cb) { + const target = document.createElement('div'); + target.classList.add('target'); + if (className) + target.classList.add(className); + target.style = style; + target.dataset.name = style; + target.dataset.match = match; + cb.appendChild(target); +} + +// Test all `.target`s. +for (const target of document.querySelectorAll('.target')) { + const cb = target.parentElement; + const anchor = cb.querySelector('.anchor'); + test(() => { + switch (target.dataset.match) { + case 'l': + assert_equals(anchor.offsetLeft, target.offsetLeft); + break; + case 'r': + assert_equals(anchor.offsetLeft + anchor.offsetWidth, target.offsetLeft); + break; + case 't': + assert_equals(anchor.offsetTop, target.offsetTop); + break; + case 'b': + assert_equals(anchor.offsetTop + anchor.offsetHeight, target.offsetTop); + break; + } + }, `${cb.classList}/${target.classList}/${target.dataset.name}`); +} +</script> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-query-custom-property-registration.html b/testing/web-platform/tests/css/css-anchor-position/anchor-query-custom-property-registration.html new file mode 100644 index 0000000000..af211a0ee5 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-query-custom-property-registration.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<title>Tests using anchor queries in custom property initial value</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#register-a-custom-property"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<script> +setup(() => assert_own_property(CSS, 'registerProperty')); + +// Anchor queries are not computationally independent, so they cannot be used +// in the initial value of any typed custom property. + +test(() => assert_throws_dom( + 'SyntaxError', + () => CSS.registerProperty({ + name: '--x', + syntax: '<length>', + inherits: false, + initialValue: 'anchor(--foo top)', + })), 'anchor() cannot be used as <length> initial value'); + +test(() => assert_throws_dom( + 'SyntaxError', + () => CSS.registerProperty({ + name: '--x', + syntax: '<length>', + inherits: false, + initialValue: 'anchor-size(--foo width)', + })), 'anchor-size() cannot be used as <length> initial value'); + +test(() => assert_throws_dom( + 'SyntaxError', + () => CSS.registerProperty({ + name: '--x', + syntax: '<length-percentage>', + inherits: false, + initialValue: 'anchor(--foo top)', + })), 'anchor() cannot be used as <length-percentage> initial value'); + +test(() => assert_throws_dom( + 'SyntaxError', + () => CSS.registerProperty({ + name: '--x', + syntax: '<length-percentage>', + inherits: false, + initialValue: 'anchor-size(--foo width)', + })), 'anchor-size() cannot be used as <length-percentage> initial value'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-query-fallback.html b/testing/web-platform/tests/css/css-anchor-position/anchor-query-fallback.html new file mode 100644 index 0000000000..8d413fd862 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-query-fallback.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<title>Tests the fallback value in anchor queries</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +#container { + position: relative; + display: flex; + flex-wrap: wrap; + width: 300px; +} + +.flex-item { + width: 100px; + height: 50px; + flex: auto; +} + +#a1 { + anchor-name: --a1; + background: orange; +} +#a2 { + anchor-name: --a2; + background: purple; +} + +.target { + position: absolute; +} +</style> + +<body id="container" onload="checkLayout('.target')"> + <div class="flex-item" id="a1"></div> + <div class="flex-item"></div> + <div class="flex-item"></div> + <div class="flex-item"></div> + <div class="flex-item"></div> + <div class="flex-item"></div> + <div class="flex-item"></div> + <div class="flex-item"></div> + <div class="flex-item" id="a2"></div> + + <!-- Fallback due to no valid anchor --> + <div class="target" style="left: anchor(--inexist-anchor left, 50px)" data-offset-x="50"></div> + <div class="target" style="width: anchor-size(--inexist-anchor width, 50px)" data-expected-width="50"></div> + + <!-- Fallback due to wrong axis for anchor() --> + <div class="target" style="left: anchor(--a1 top, 50px)" data-offset-x="50"></div> + <div class="target" style="left: anchor(--a1 bottom, 50px)" data-offset-x="50"></div> + <div class="target" style="top: anchor(--a1 left, 50px)" data-offset-y="50"></div> + <div class="target" style="top: anchor(--a1 right, 50px)" data-offset-y="50"></div> + + <!-- More complicated fallback values --> + <div class="target" style="left: anchor(--inexist-anchor left, 50%)" data-offset-x="150"></div> + <div class="target" style="left: anchor(--inexist-anchor left, calc(20% + 20px))" data-offset-x="80"></div> + <div class="target" style="top: anchor(--a1 left, anchor(--a2 top))" data-offset-y="100"></div> + <div class="target" style="top: anchor(--a1 left, calc((anchor(--a1 bottom) + anchor(--a2 top)) / 2)" data-offset-y="75"></div> + <div class="target" style="width: anchor-size(--inexist-anchor width, 50%)" data-expected-width="150"></div> + <div class="target" style="width: anchor-size(--inexist-anchor width, calc(20% + 20px))" data-expected-width="80"></div> + <div class="target" style="height: anchor-size(--inexist-anchor height, anchor-size(--a1 width))" data-expected-height="100"></div> + <div class="target" style="height: anchor-size(--inexist-anchor height, calc((anchor-size(--a1 width) + anchor-size(--a2 height)) / 2)" data-expected-height="75"></div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-basics.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-basics.tentative.html new file mode 100644 index 0000000000..fa42e33d92 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-basics.tentative.html @@ -0,0 +1,71 @@ +<!DOCTYPE html> +<title>Basic of anchor-scroll: anchored elements should be "pinned" to the anchor when anchor is scrolled</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<link rel="match" href="reference/anchor-scroll-ref.html"> +<link rel="stylesheet" href="/fonts/ahem.css"> +<style> +body { + font: 20px/1 Ahem; + margin: 0; +} + +#scroll-container { + width: 400px; + height: 400px; + overflow: scroll; +} + +#scroll-contents { + width: 1000px; + height: 1000px; + position: relative; +} + +#placefiller-above-anchor { + height: 500px; +} + +#placefiller-before-anchor { + display: inline-block; + width: 500px; +} + +#anchor { + anchor-name: --anchor; +} + +#inner-anchored { + color: green; + position: absolute; + left: anchor(--anchor left); + bottom: anchor(--anchor top); + anchor-scroll: --anchor; +} + +#outer-anchored { + color: yellow; + position: absolute; + left: anchor(--anchor left); + top: anchor(--anchor bottom); + anchor-scroll: --anchor; +} +</style> + +<div style="position: relative"> + <div id="scroll-container"> + <div id="scroll-contents"> + <div id="placefiller-above-anchor"></div> + <div id="placefiller-before-anchor"></div> + <span id="anchor">anchor</span> + <div id="inner-anchored">inner-anchored</div> + </div> + </div> + <div id="outer-anchored">outer-anchored</div> +</div> + +<script> +const scroller = document.getElementById('scroll-container'); +scroller.scrollTop = 300; +scroller.scrollLeft = 450; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-001.html new file mode 100644 index 0000000000..a9a5d8c0ff --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-001.html @@ -0,0 +1,101 @@ +<!DOCTYPE html> +<title>Tests that position fallback responds to scrolling</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#scroll"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback-apply"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/test-common.js"></script> +<style> +#cb { + width: 400px; + height: 400px; + margin: 100px; + transform: scale(1); + outline: 1px solid black; +} + +#scroller { + width: 400px; + height: 400px; + overflow: scroll; +} + +#anchor { + width: 100px; + height: 100px; + margin-left: 150px; + margin-right: 275px; + margin-top: 300px; + margin-bottom: 300px; + background: orange; + anchor-name: --a; +} + +#anchored { + position: absolute; + background: green; + anchor-scroll: --a; + position-fallback: --fallback; +} + +@position-fallback --fallback { + /* Above the anchor */ + @try { + width: 100px; height: 100px; + left: anchor(--a left); + bottom: anchor(--a top); + } + /* Left of the anchor */ + @try { + width: 100px; height: 100px; + right: anchor(--a left); + top: anchor(--a top); + } + /* Right of the anchor */ + @try { + width: 100px; height: 100px; + left: anchor(--a right); + top: anchor(--a top); + } +} +</style> + +<div id="cb"> + <div id="scroller"> + <div id="anchor"></div> + </div> + <div id="anchored"></div> +</div> + +<script> +promise_test(async () => { + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'top'); +}, 'Should be above the anchor when at initial scroll position'); + +promise_test(async () => { + scroller.scrollTop = 200; + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'top'); +}, 'Scroll down until the top edge of #anchor touches container but not overflowing'); + +promise_test(async () => { + scroller.scrollTop = 201; + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'left'); +}, 'Scroll further down, making the first fallback position overflow by 1px'); + +promise_test(async () => { + scroller.scrollTop = 200; + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'top'); +}, 'Scroll back up to reuse the first fallback position'); + +promise_test(async () => { + scroller.scrollTop = 249; + scroller.scrollLeft = 51; + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'right'); +}, 'Scroll bottom-right to make the first three fallback positions overflow'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-002.html new file mode 100644 index 0000000000..e51f675fff --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-002.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<html dir="rtl"> +<title>Tests position fallback with rtl scroller</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#scroll"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback-apply"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/test-common.js"></script> + +<style> +body { + margin: 0; +} + +#anchor { + anchor-name: --a; + width: 100px; + height: 100px; + margin-right: 200px; + background: orange; +} + +#spacer { + width: 1000vw; + height: 1px; +} + +#anchored { + position: fixed; + width: 100px; + height: 100px; + background: green; + anchor-scroll: --a; + top: anchor(--a top); + position-fallback: --pf; +} + +@position-fallback --pf { + @try { left: anchor(--a right); } + @try { right: anchor(--a left); } +} +</style> + +<div id="anchor"></div> +<div id="spacer"></div> +<div id="anchored"></div> + +<script> +promise_test(async () => { + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'right'); +}, 'Should use the first fallback position at the initial scroll offset'); + +promise_test(async () => { + document.documentElement.scrollLeft = -101; + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'left'); +}, 'Should use the second fallback position after scrolling left'); +</script> + +</html> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-003.html new file mode 100644 index 0000000000..ae212f9190 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-003.html @@ -0,0 +1,73 @@ +<!DOCTYPE html> +<title>Tests position fallback with bottom-up scroller</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#scroll"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback-apply"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/test-common.js"></script> + +<style> +#containing-block { + width: 400px; + height: 400px; + transform: scale(1); + border: 1px solid black; +} + +#scroller { + display: flex; + flex-direction: column-reverse; + width: 400px; + height: 400px; + overflow-y: scroll; +} + +.box { + min-height: 100px; + width: 100px; +} + +#anchor { + anchor-name: --a; + background: orange; +} + +#anchored { + position: absolute; + width: 100px; + height: 100px; + background: green; + anchor-scroll: --a; + position-fallback: --pf; +} + +@position-fallback --pf { + @try { top: anchor(--a bottom); } + @try { bottom: anchor(--a top); } +} +</style> + +<div id="containing-block"> + <div id="scroller"> + <div class="box"></div> + <div class="box" id="anchor"></div> + <div class="box"></div> + <div class="box"></div> + <div class="box"></div> + </div> + <div id="anchored"></div> +</div> + +<script> +promise_test(async () => { + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'bottom'); +}, 'Should use the first fallback position at the initial scroll offset'); + +promise_test(async () => { + scroller.scrollTop = -1; + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'top'); +}, 'Should use the second fallback position after scrolling up'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-004.html new file mode 100644 index 0000000000..0227fa753e --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-004.html @@ -0,0 +1,75 @@ +<!DOCTYPE html> +<title>Tests position fallback with scrollers with mixed writing modes</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#scroll"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback-apply"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/test-common.js"></script> + +<style> +body { + margin: 0; +} + +#vrl-scroller { + writing-mode: vertical-rl; + overflow-x: scroll; + width: calc(100vw - 90px); + height: 400px; + outline: 1px solid black; +} + +#anchor { + anchor-name: --a; + width: 100px; + height: 100px; + margin-left: 1000px; + background: orange; +} + +#anchored { + position: fixed; + width: 100px; + height: 100px; + background: green; + anchor-scroll: --a; + position-fallback: --pf; +} + +@position-fallback --pf { + /* Top of the anchor */ + @try { bottom: anchor(--a top); left: anchor(--a left); } + /* Bottom of the anchor */ + @try { top: anchor(--a bottom); left: anchor(--a left); } + /* Left of the anchor */ + @try { top: anchor(--a top); right: anchor(--a left); } +} +</style> + +<div style="height: 200px"></div> +<div id="vrl-scroller"> + <div id="anchor"></div> +</div> +<div style="height: 1000px"></div> +<div id="anchored"></div> + +<script> +promise_test(async () => { + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'top'); +}, 'Should use the first fallback position at the initial scroll offsets'); + +promise_test(async () => { + document.documentElement.scrollTop = 101; + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'bottom'); +}, 'Should use the second fallback position after scrolling viewport down'); + +promise_test(async () => { + let vrlScroller = document.getElementById('vrl-scroller'); + vrlScroller.scrollLeft = -100; + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'left'); +}, 'Should use the third fallback position after scrolling the vrl scroller left'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-005.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-005.html new file mode 100644 index 0000000000..3dedd4d7e6 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-005.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<html dir="rtl"> +<title>Tests position fallback with rtl scroller and vertical-rl OOF</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#scroll"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback-apply"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/test-common.js"></script> + +<style> +body { + margin: 0; +} + +#anchor { + anchor-name: --a; + width: 100px; + height: 100px; + margin-right: 200px; + background: orange; +} + +#spacer { + width: 1000vw; + height: 1px; +} + +#anchored { + writing-mode: vertical-rl; + position: fixed; + width: 100px; + height: 100px; + background: green; + anchor-scroll: --a; + top: anchor(--a top); + position-fallback: --pf; +} + +@position-fallback --pf { + @try { left: anchor(--a right); } + @try { right: anchor(--a left); } +} +</style> + +<div id="anchor"></div> +<div id="spacer"></div> +<div id="anchored"></div> + +<script> +promise_test(async () => { + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'right'); +}, 'Should use the first fallback position at the initial scroll offset'); + +promise_test(async () => { + document.documentElement.scrollLeft = -101; + await waitUntilNextAnimationFrame(); + assert_fallback_position(anchored, anchor, 'left'); +}, 'Should use the second fallback position after scrolling left'); +</script> + +</html> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos.tentative.html new file mode 100644 index 0000000000..ee7d22608b --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos.tentative.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<title>Tests that anchor-scroll adjusts location of fixed-positioned elements correctly</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<link rel="match" href="reference/anchor-scroll-fixedpos-ref.html"> +<style> +body { + margin: 0; + height: 2000px; +} + +div { + width: 100px; + height: 100px; +} + +#anchor { + anchor-name: --a1; + margin: 300px; + background: orange; +} + +#anchored { + position: fixed; + anchor-scroll: --a1; + left: anchor(--a1 right); + top: anchor(--a1 top); + background: green; +} + +</style> + +<div id="anchor"></div> +<div id="anchored"></div> + +<script> +document.documentElement.scrollTop = 200; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-js-expose.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-js-expose.tentative.html new file mode 100644 index 0000000000..f19d010d35 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-js-expose.tentative.html @@ -0,0 +1,89 @@ +<!DOCTYPE html> +<title>Tests that anchor element's actual rendered location is property exposed via JS APIs under anchor-scroll</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-scroll"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +#container { + position: absolute; + top: 0px; + left: 0px; +} +#scroller { + margin-left: 100px; + margin-top: 100px; + width: 400px; + height: 400px; + overflow: scroll; +} +#anchor-container { + width: 2000px; + height: 1000px; +} +#anchor { + margin: 400px; + margin-left: 1400px; + width: 100px; + height: 100px; + background-color: yellow; + anchor-name: --anchor; +} +#anchored { + position: absolute; + left: anchor(--anchor left); + bottom: anchor(--anchor top); + width: 100px; + height: 100px; + anchor-scroll: --anchor; + background-color: green; +} +</style> + +<div id="container"> + <div id="scroller"> + <div id="anchor"></div> + </div> + <div id="anchored">Text</div> +</div> + +<script> +promise_setup(async () => { + // Move both the anchor and the anchored elements into the visible area of the + // scroller. This also runs layout to setup an empty anchor-scroll snapshot. + scroller.scrollTop = 300; + scroller.scrollLeft = 1300; + + // Ensure up-to-date anchor-scroll snapshot. + await new Promise(resolve => requestAnimationFrame(resolve)); +}); + +promise_test(async () => { + let rect = anchored.getBoundingClientRect(); + assert_equals(rect.x, 200); + assert_equals(rect.y, 100); +}, 'Element.getBoundingClientRect() returns the actual rendered location'); + +promise_test(async () => { + let range = document.createRange(); + let text = anchored.firstChild; + range.setStart(text, 0); + range.setEnd(text, text.length); + let rect = range.getBoundingClientRect(); + assert_equals(rect.x, 200); + assert_equals(rect.y, 100); +}, 'Range.getBoundingClientRect() returns the actual rendered location'); + +promise_test(async () => { + assert_equals(anchored.offsetLeft, 200); + assert_equals(anchored.offsetTop, 100); +}, 'Element.offset* return adjusted offsets'); + +promise_test(() => new Promise(resolve => { + let observer = new IntersectionObserver(entryList => { + if (entryList.some(entry => entry.isIntersecting)) + resolve(); + }); + observer.observe(anchored); +}), 'IntersectionObserver should fire when anchor-scroll moves element into visible area'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-nested.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-nested.tentative.html new file mode 100644 index 0000000000..964b02a5b0 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-nested.tentative.html @@ -0,0 +1,90 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<title>Tests anchor-scroll with nested scroll containers</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<link rel="match" href="reference/anchor-scroll-nested-ref.html"> +<style> +body { + margin: 0; + width: 1500px; + height: 1500px; + position: relative; +} + +#outer-scroller { + margin: 500px; + width: 350px; + height: 350px; + outline: 1px solid black; + overflow: scroll; +} + +#inner-scroller { + margin: 100px; + width: 250px; + height: 250px; + outline: 1px solid black; + overflow: scroll; +} + +#anchor { + margin: 200px; + width: 50px; + height: 50px; + background-color: green; + anchor-name: --anchor; +} + +.anchored { + position: absolute; + width: 50px; + height: 50px; + left: anchor(--anchor left); + anchor-scroll: --anchor; +} + +.above { + bottom: anchor(--anchor top); + background-color: red; +} + +.below { + top: anchor(--anchor bottom); + background-color: yellow; +} +</style> + +<div id="outer-scroller"> + <div id="inner-scroller"> + <div id="anchor"></div> + <div class="anchored above"></div> + </div> +</div> + +<div class="anchored below"></div> + +<script> +function raf() { + return new Promise(resolve => requestAnimationFrame(resolve)); +} + +async function runTest() { + await raf(); + await raf(); + + document.documentElement.scrollTop = 400; + document.documentElement.scrollLeft = 400; + + let outerScroller = document.getElementById('outer-scroller'); + outerScroller.scrollTop = 50; + outerScroller.scrollLeft = 50; + + let innerScroller = document.getElementById('inner-scroller'); + innerScroller.scrollTop = 100; + innerScroller.scrollLeft = 100; + + document.documentElement.classList.remove('reftest-wait'); +} +runTest(); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-no-overflow-crash.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-no-overflow-crash.html new file mode 100644 index 0000000000..d8fa3821cf --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-no-overflow-crash.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<title>Tests that anchor-scroll doesn't crash renderer when anchor is in a scroller whose content doesn't overflow</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<style> +#container { + position: relative; +} + +#scroller { + overflow: scroll; + width: 400px; + height: 400px; +} + +#anchor { + anchor-name: --a; + margin: 100px; + width: 100px; + height: 100px; + background: green; +} + +#anchored { + position: absolute; + anchor-scroll: --a; + left: anchor(--a left); + bottom: anchor(--a top); + width: 100px; + height: 100px; + background: orange; +} + +</style> + +<div id="container"> + <div id="scroller"> + <div id="anchor">anchor</div> + </div> + <div id="anchored">anchored</div> +</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-001.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-001.tentative.html new file mode 100644 index 0000000000..9400136e98 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-001.tentative.html @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<title>Basic of anchor-scroll: anchored elements should keep "pinned" to the anchor during anchor scroll updates</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<link rel="match" href="reference/anchor-scroll-ref.html"> +<link rel="stylesheet" href="/fonts/ahem.css"> +<style> +body { + font: 20px/1 Ahem; + margin: 0; +} + +#scroll-container { + width: 400px; + height: 400px; + overflow: scroll; +} + +#scroll-contents { + width: 1000px; + height: 1000px; + position: relative; +} + +#placefiller-above-anchor { + height: 500px; +} + +#placefiller-before-anchor { + display: inline-block; + width: 500px; +} + +#anchor { + anchor-name: --anchor; +} + +#inner-anchored { + color: green; + position: absolute; + left: anchor(--anchor left); + bottom: anchor(--anchor top); + anchor-scroll: --anchor; +} + +#outer-anchored { + color: yellow; + position: absolute; + left: anchor(--anchor left); + top: anchor(--anchor bottom); + anchor-scroll: --anchor; +} +</style> + +<div style="position: relative"> + <div id="scroll-container"> + <div id="scroll-contents"> + <div id="placefiller-above-anchor"></div> + <div id="placefiller-before-anchor"></div> + <span id="anchor">anchor</span> + <div id="inner-anchored">inner-anchored</div> + </div> + </div> + <div id="outer-anchored">outer-anchored</div> +</div> + +<script> +function raf() { + return new Promise(resolve => requestAnimationFrame(resolve)); +} + +async function runTest() { + await raf(); + await raf(); + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 300; + scroller.scrollLeft = 450; + document.documentElement.classList.remove('reftest-wait'); +} +runTest(); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-002.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-002.tentative.html new file mode 100644 index 0000000000..4e8fcbe295 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-002.tentative.html @@ -0,0 +1,84 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<title>Basic of anchor-scroll: anchored elements should update location on `anchor-scroll` property changes</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<link rel="match" href="reference/anchor-scroll-ref.html"> +<link rel="stylesheet" href="/fonts/ahem.css"> +<style> +body { + font: 20px/1 Ahem; + margin: 0; +} + +#scroll-container { + width: 400px; + height: 400px; + overflow: scroll; +} + +#scroll-contents { + width: 1000px; + height: 1000px; + position: relative; +} + +#placefiller-above-anchor { + height: 500px; +} + +#placefiller-before-anchor { + display: inline-block; + width: 500px; +} + +#anchor { + anchor-name: --anchor; +} + +#inner-anchored { + color: green; + position: absolute; + left: anchor(--anchor left); + bottom: anchor(--anchor top); +} + +#outer-anchored { + color: yellow; + position: absolute; + left: anchor(--anchor left); + top: anchor(--anchor bottom); +} +</style> + +<div style="position: relative"> + <div id="scroll-container"> + <div id="scroll-contents"> + <div id="placefiller-above-anchor"></div> + <div id="placefiller-before-anchor"></div> + <span id="anchor">anchor</span> + <div id="inner-anchored">inner-anchored</div> + </div> + </div> + <div id="outer-anchored">outer-anchored</div> +</div> + +<script> +const scroller = document.getElementById('scroll-container'); +scroller.scrollTop = 300; +scroller.scrollLeft = 450; + +function raf() { + return new Promise(resolve => requestAnimationFrame(resolve)); +} + +async function runTest() { + await raf(); + await raf(); + document.getElementById('inner-anchored').style.anchorScroll = '--anchor'; + document.getElementById('outer-anchored').style.anchorScroll = '--anchor'; + document.documentElement.classList.remove('reftest-wait'); +} +runTest(); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-003.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-003.tentative.html new file mode 100644 index 0000000000..0d3ec2830c --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-003.tentative.html @@ -0,0 +1,81 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<title>Basic of anchor-scroll: anchored elements should update location on anchor's `anchor-name` property changes</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<link rel="match" href="reference/anchor-scroll-ref.html"> +<link rel="stylesheet" href="/fonts/ahem.css"> +<style> +body { + font: 20px/1 Ahem; + margin: 0; +} + +#scroll-container { + width: 400px; + height: 400px; + overflow: scroll; +} + +#scroll-contents { + width: 1000px; + height: 1000px; + position: relative; +} + +#placefiller-above-anchor { + height: 500px; +} + +#placefiller-before-anchor { + display: inline-block; + width: 500px; +} + +#inner-anchored { + color: green; + position: absolute; + left: anchor(--anchor left); + bottom: anchor(--anchor top); + anchor-scroll: --anchor; +} + +#outer-anchored { + color: yellow; + position: absolute; + left: anchor(--anchor left); + top: anchor(--anchor bottom); + anchor-scroll: --anchor; +} +</style> + +<div style="position: relative"> + <div id="scroll-container"> + <div id="scroll-contents"> + <div id="placefiller-above-anchor"></div> + <div id="placefiller-before-anchor"></div> + <span id="anchor">anchor</span> + <div id="inner-anchored">inner-anchored</div> + </div> + </div> + <div id="outer-anchored">outer-anchored</div> +</div> + +<script> +const scroller = document.getElementById('scroll-container'); +scroller.scrollTop = 300; +scroller.scrollLeft = 450; + +function raf() { + return new Promise(resolve => requestAnimationFrame(resolve)); +} + +async function runTest() { + await raf(); + await raf(); + document.getElementById('anchor').style.anchorName = '--anchor'; + document.documentElement.classList.remove('reftest-wait'); +} +runTest(); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-004.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-004.tentative.html new file mode 100644 index 0000000000..7d1aadc95d --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-004.tentative.html @@ -0,0 +1,93 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<title>Basic of anchor-scroll: anchored elements should update location on anchor's layout changes</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<link rel="match" href="reference/anchor-scroll-ref.html"> +<link rel="stylesheet" href="/fonts/ahem.css"> +<style> +body { + font: 20px/1 Ahem; + margin: 0; +} + +#scroll-container { + width: 400px; + height: 400px; + overflow: scroll; +} + +#scroll-contents { + width: 1000px; + height: 1000px; + position: relative; +} + +#anchor { + anchor-name: --anchor; +} + +#placefiller-above-anchor { + height: 800px; +} + +.after #placefiller-above-anchor { + height: 500px; +} + +#placefiller-before-anchor { + display: inline-block; + width: 800px; +} + +.after #placefiller-before-anchor { + width: 500px; +} + +#inner-anchored { + color: green; + position: absolute; + left: anchor(--anchor left); + bottom: anchor(--anchor top); + anchor-scroll: --anchor; +} + +#outer-anchored { + color: yellow; + position: absolute; + left: anchor(--anchor left); + top: anchor(--anchor bottom); + anchor-scroll: --anchor; +} +</style> + +<div style="position: relative"> + <div id="scroll-container"> + <div id="scroll-contents"> + <div id="placefiller-above-anchor"></div> + <div id="placefiller-before-anchor"></div> + <span id="anchor">anchor</span> + <div id="inner-anchored">inner-anchored</div> + </div> + </div> + <div id="outer-anchored">outer-anchored</div> +</div> + +<script> +const scroller = document.getElementById('scroll-container'); +scroller.scrollTop = 300; +scroller.scrollLeft = 450; + +function raf() { + return new Promise(resolve => requestAnimationFrame(resolve)); +} + +async function runTest() { + await raf(); + await raf(); + document.getElementById('scroll-contents').classList.add('after'); + document.documentElement.classList.remove('reftest-wait'); +} +runTest(); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-005.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-005.tentative.html new file mode 100644 index 0000000000..5675507a47 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-005.tentative.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<title>Tests that anchored element should update location after scroll offset changes caused by scroller resizing</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<link rel="match" href="reference/anchor-scroll-update-005-ref.html"> +<style> + #cb { + position: absolute; + inset: 0; + } + #scroller { + margin-top: 300px; + overflow-y: scroll; + height: 100px; + } + #scroller.changed { height: 200px; } + #spacer { height: 400px; } + #anchor { anchor-name: --a; } + #anchored { + position: absolute; + width: 100px; + height: 100px; + background-color: green; + top: anchor(--a top); + left: 0; + anchor-scroll: --a; + } +</style> +<div id="cb"> + <div id="scroller"> + <div id="anchor"></div> + <div id="spacer"></div> + </div> + <div id="anchored"></div> +</div> +<script> +function raf() { + return new Promise(resolve => requestAnimationFrame(resolve)); +} + +async function runTest() { + const scroller = document.getElementById('scroller'); + scroller.scrollTop = 300; + + await raf(); + await raf(); + + scroller.classList.add('changed'); + document.documentElement.classList.remove('reftest-wait'); + + // Should change scroll offset and anchor-scroll adjustment to 200. +} +runTest(); +</script> + diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-006.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-006.tentative.html new file mode 100644 index 0000000000..7a8e675baa --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-006.tentative.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<title>Tests that anchored element should update location after scroll offset changes caused by scroll content resizing</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<link rel="match" href="reference/anchor-scroll-update-006-ref.html"> +<style> + #cb { + position: absolute; + inset: 0; + } + #scroller { + margin-top: 300px; + overflow-y: scroll; + height: 100px; + } + #spacer { height: 400px; } + #spacer.changed { height: 300px; } + #anchor { anchor-name: --a; } + #anchored { + position: absolute; + width: 100px; + height: 100px; + background-color: green; + top: anchor(--a top); + left: 0; + anchor-scroll: --a; + } +</style> +<div id="cb"> + <div id="scroller"> + <div id="anchor"></div> + <div id="spacer"></div> + </div> + <div id="anchored"></div> +</div> +<script> +function raf() { + return new Promise(resolve => requestAnimationFrame(resolve)); +} + +async function runTest() { + const scroller = document.getElementById('scroller'); + scroller.scrollTop = 300; + + await raf(); + await raf(); + + document.getElementById('spacer').classList.add('changed'); + document.documentElement.classList.remove('reftest-wait'); + + // Should change scroll offset and anchor-scroll adjustment to 200. +} +runTest(); +</script> + diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vlr.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vlr.tentative.html new file mode 100644 index 0000000000..82d167f48d --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vlr.tentative.html @@ -0,0 +1,81 @@ +<!DOCTYPE html> +<title>Tests that anchor-scroll works in vertical-lr writing mode</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-scroll"> +<link rel="match" href="reference/anchor-scroll-vlr-ref.html"> +<link rel="stylesheet" href="/fonts/ahem.css"> +<style> +:root { + /* Prevents creatings scrollbar on the viewport due to anchored element's + * layout position being out of the viewport. + * TODO(crbug.com/1309178): Revisit this behavior. + */ + overflow: clip; +} + +body { + font: 20px/1 Ahem; + margin: 0; + writing-mode: vertical-lr; + white-space: nowrap; +} + +#scroll-container { + width: 400px; + height: 400px; + overflow: scroll; +} + +#scroll-contents { + width: 1000px; + height: 1000px; + position: relative; +} + +#placefiller-above-anchor { + width: 500px; +} + +#placefiller-before-anchor { + display: inline-block; + height: 500px; +} + +#anchor { + anchor-name: --anchor; +} + +#inner-anchored { + color: green; + position: absolute; + top: anchor(--anchor top); + left: anchor(--anchor right); + anchor-scroll: --anchor; +} + +#outer-anchored { + color: yellow; + position: absolute; + top: anchor(--anchor top); + right: anchor(--anchor left); + anchor-scroll: --anchor; +} +</style> + +<div style="position: relative;"> + <div id="scroll-container"> + <div id="scroll-contents"> + <div id="placefiller-above-anchor"></div> + <div id="placefiller-before-anchor"></div> + <span id="anchor">anchor</span> + <div id="inner-anchored">inner-anchored</div> + </div> + </div> + <div id="outer-anchored">outer-anchored</div> +</div> + +<script> +const scroller = document.getElementById('scroll-container'); +scroller.scrollTop = 450; +scroller.scrollLeft = 300; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vrl.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vrl.tentative.html new file mode 100644 index 0000000000..55b7355897 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vrl.tentative.html @@ -0,0 +1,81 @@ +<!DOCTYPE html> +<title>Tests that anchor-scroll works in vertical-rl writing mode</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-scroll"> +<link rel="match" href="reference/anchor-scroll-vrl-ref.html"> +<link rel="stylesheet" href="/fonts/ahem.css"> +<style> +:root { + /* Prevents creatings scrollbar on the viewport due to anchored element's + * layout position being out of the viewport. + * TODO(crbug.com/1309178): Revisit this behavior. + */ + overflow: clip; +} + +body { + font: 20px/1 Ahem; + margin: 0; + writing-mode: vertical-rl; + white-space: nowrap; +} + +#scroll-container { + width: 400px; + height: 400px; + overflow: scroll; +} + +#scroll-contents { + width: 1000px; + height: 1000px; + position: relative; +} + +#placefiller-above-anchor { + width: 500px; +} + +#placefiller-before-anchor { + display: inline-block; + height: 500px; +} + +#anchor { + anchor-name: --anchor; +} + +#inner-anchored { + color: green; + position: absolute; + top: anchor(--anchor top); + left: anchor(--anchor right); + anchor-scroll: --anchor; +} + +#outer-anchored { + color: yellow; + position: absolute; + top: anchor(--anchor top); + right: anchor(--anchor left); + anchor-scroll: --anchor; +} +</style> + +<div style="position: relative;"> + <div id="scroll-container"> + <div id="scroll-contents"> + <div id="placefiller-above-anchor"></div> + <div id="placefiller-before-anchor"></div> + <span id="anchor">anchor</span> + <div id="inner-anchored">inner-anchored</div> + </div> + </div> + <div id="outer-anchored">outer-anchored</div> +</div> + +<script> +const scroller = document.getElementById('scroll-container'); +scroller.scrollTop = 450; +scroller.scrollLeft = -300; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-size-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-size-001.html new file mode 100644 index 0000000000..e5a0dfde1f --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-size-001.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.container { + position: relative; + height: 10px; +} +.anchor1 { + anchor-name: --a1; + width: 5px; + height: 7px; + background: orange; +} +.target { + position: absolute; +} +</style> +<body onload="checkLayout('.target')"> + <div class="container"> + <div class="anchor1"></div> + + <!-- Basic cases of all `anchor-size`s. --> + <div class="target" style="width: anchor-size(--a1 width)" data-expected-width=5></div> + <div class="target" style="height: anchor-size(--a1 height)" data-expected-height=7></div> + <div class="target" style="width: anchor-size(--a1 inline)" data-expected-width=5></div> + <div class="target" style="height: anchor-size(--a1 block)" data-expected-height=7></div> + <div class="target" style="width: anchor-size(--a1 self-inline)" data-expected-width=5></div> + <div class="target" style="height: anchor-size(--a1 self-block)" data-expected-height=7></div> + + <!-- Different axes should work. --> + <div class="target" style="height: anchor-size(--a1 width)" data-expected-height=5></div> + <div class="target" style="width: anchor-size(--a1 height)" data-expected-width=7></div> + <div class="target" style="height: anchor-size(--a1 inline)" data-expected-height=5></div> + <div class="target" style="width: anchor-size(--a1 block)" data-expected-width=7></div> + <div class="target" style="height: anchor-size(--a1 self-inline)" data-expected-height=5></div> + <div class="target" style="width: anchor-size(--a1 self-block)" data-expected-width=7></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-size-minmax-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-size-minmax-001.html new file mode 100644 index 0000000000..f7fb07b0af --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-size-minmax-001.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.container { + position: relative; + height: 10px; +} +.anchor1 { + anchor-name: --a1; + width: 5px; + height: 7px; + background: orange; +} +.target { + position: absolute; +} +</style> +<body onload="checkLayout('.target')"> + <div class="container"> + <div class="anchor1"></div> + + <div class="target" + style="min-width: anchor-size(--a1 width)" + data-expected-width=5></div> + <div class="target" + style="min-height: anchor-size(--a1 width)" + data-expected-height=5></div> + + <div class="target" + style="max-width: anchor-size(--a1 width)" + data-expected-width=5> + <div style="width: 100px"></div> + </div> + <div class="target" + style="max-height: anchor-size(--a1 width)" + data-expected-height=5> + <div style="height: 100px"></div> + </div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-size-parse-invalid.html b/testing/web-platform/tests/css/css-anchor-position/anchor-size-parse-invalid.html new file mode 100644 index 0000000000..d13f21a585 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-size-parse-invalid.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<title>Tests values that are invalid at parse time for the anchor-size() function</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +// anchor-size() can only be used in sizing properties +test_invalid_value('margin-top', 'anchor-size(--foo width)'); +test_invalid_value('top', 'anchor-size(--foo width)'); +test_invalid_value('font-size', 'anchor-size(--foo width)'); + +// Invalid parameter format +test_invalid_value('width', 'anchor-size(--foo, width)'); +test_invalid_value('width', 'anchor-size(--foo width,)'); +test_invalid_value('width', 'anchor-size(--foo width height)'); +test_invalid_value('width', 'anchor-size(--foo width, 10px 20%)'); +test_invalid_value('width', 'anchor-size(--foo width, 10px, 20%)'); + +// Anchor name must be a dashed ident +test_invalid_value('width', 'anchor-size(foo width)'); + +// Invalid anchor size values +test_invalid_value('width', 'anchor-size(--foo top)'); +test_invalid_value('width', 'anchor-size(--foo 10em)'); +test_invalid_value('width', 'anchor-size(--foo 100s)'); +test_invalid_value('width', 'anchor-size(--foo 50%)'); + +// Invalid fallback values +test_invalid_value('width', 'anchor-size(--foo width, 1)'); +test_invalid_value('width', 'anchor-size(--foo width, 100s)'); +test_invalid_value('width', 'anchor-size(--foo width, height)'); +test_invalid_value('width', 'anchor-size(--foo width, anchor-size(bar width))'); +test_invalid_value('width', 'anchor-size(--foo width, anchor(--bar top))'); + +// Invalid anchor size values in calc tree +test_invalid_value('width', 'calc(anchor-size(foo width) + 10px + 10%)'); +test_invalid_value('width', 'calc(10px + 100 * anchor-size(--foo width, anchor-size(bar bottom)))'); +test_invalid_value('width', 'min(anchor-size(--foo width), anchor-size(--bar height), anchor(--baz top))'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-size-parse-valid.html b/testing/web-platform/tests/css/css-anchor-position/anchor-size-parse-valid.html new file mode 100644 index 0000000000..9ed4d04223 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-size-parse-valid.html @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<title>Tests parsing of the anchor-size() function</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-size"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +const anchorNames = [ + '--foo', + '', +]; + +const sizeProperties = [ + 'width', + 'min-width', + 'max-width', + 'height', + 'min-height', + 'max-height', + 'block-size', + 'min-block-size', + 'max-block-size', + 'inline-size', + 'min-inline-size', + 'max-inline-size', +]; + +const anchorSizes = [ + 'width', + 'height', + 'block', + 'inline', + 'self-block', + 'self-inline', +]; + +const fallbacks = [ + null, + '1px', + '50%', + 'calc(1px + 50%)', + 'anchor-size(block)', + 'anchor-size(--bar block)', + 'anchor-size(--bar block, anchor-size(--baz inline))', +]; + +// Tests basic combinations +for (let name of anchorNames) { + for (let property of sizeProperties) { + for (let size of anchorSizes) { + for (let fallback of fallbacks) { + let value = `anchor-size(${name ? name + ' ' : ''}${size}${fallback ? ', ' + fallback : ''})`; + test_valid_value(property, value); + } + } + } +} + +// Tests that anchor-size() can be used in a calc tree +test_valid_value('width', 'calc((anchor-size(--foo width) + anchor-size(--bar height)) / 2)'); +test_valid_value('width', 'anchor-size(--foo width, calc(anchor-size(--bar height) * 0.5))'); +test_valid_value('width', 'min(100px, 10%, anchor-size(--foo width), anchor-size(--bar height))'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-size-replaced-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-size-replaced-001.html new file mode 100644 index 0000000000..e5d133acf7 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-size-replaced-001.html @@ -0,0 +1,74 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.container { + position: relative; + height: 10px; +} +.anchor1 { + anchor-name: --a1; + width: 5px; + height: 24px; + background: orange; +} +.target { + position: absolute; +} +</style> +<body onload="checkLayout('.target')"> + <div class="container"> + <div class="anchor1"></div> + + <!-- Specifying the width or the height should scale uniformly. --> + <img class="target" src="support/green-16x16.png" + style="width: anchor-size(--a1 width)" + data-expected-width=5 data-expected-height=5> + <img class="target" src="support/green-16x16.png" + style="height: anchor-size(--a1 width)" + data-expected-width=5 data-expected-height=5> + + <!-- Smaller `min-width/height` than the natural size should be ignored. --> + <img class="target" src="support/green-16x16.png" + style="min-width: anchor-size(--a1 width)" + data-expected-width=16 data-expected-height=16> + <img class="target" src="support/green-16x16.png" + style="min-height: anchor-size(--a1 width)" + data-expected-width=16 data-expected-height=16> + + <!-- Larger `min-width/height` than the natural size should scale. --> + <img class="target" src="support/green-16x16.png" + style="min-width: anchor-size(--a1 height)" + data-expected-width=24 data-expected-height=24> + <img class="target" src="support/green-16x16.png" + style="min-height: anchor-size(--a1 height)" + data-expected-width=24 data-expected-height=24> + + <!-- Smaller `max-width/height` than the natural size should scale. --> + <img class="target" src="support/green-16x16.png" + style="max-width: anchor-size(--a1 width)" + data-expected-width=5 data-expected-height=5> + <img class="target" src="support/green-16x16.png" + style="max-height: anchor-size(--a1 width)" + data-expected-width=5 data-expected-height=5> + + <!-- Larger `min-width/height` than the natural size should be ignored. --> + <img class="target" src="support/green-16x16.png" + style="max-width: anchor-size(--a1 height)" + data-expected-width=16 data-expected-height=16> + <img class="target" src="support/green-16x16.png" + style="max-height: anchor-size(--a1 height)" + data-expected-width=16 data-expected-height=16> + + <!-- The `aspect-ratio` property should be honored. --> + <img class="target" src="support/green-16x16.png" + style="width: anchor-size(--a1 width); aspect-ratio: 0.5" + data-expected-width=5 data-expected-height=10> + <img class="target" src="support/green-16x16.png" + style="height: anchor-size(--a1 width); aspect-ratio: 2" + data-expected-width=10 data-expected-height=5> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-size-writing-modes-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-size-writing-modes-001.html new file mode 100644 index 0000000000..c9e0619741 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-size-writing-modes-001.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-name"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.container { + position: relative; + height: 10px; +} +.anchor1 { + anchor-name: --a1; + width: 5px; + height: 7px; + background: orange; +} +.target { + position: absolute; +} +.htb { + writing-mode: horizontal-tb; +} +.vrl { + writing-mode: vertical-rl; +} +</style> +<body onload="checkLayout('.target')"> + <div class="container"> + <div class="anchor1 vrl"></div> + + <!-- Anchor's `writing-mode` should not matter. --> + <div class="target" style="width: anchor-size(--a1 width)" data-expected-width=5></div> + <div class="target" style="width: anchor-size(--a1 height)" data-expected-width=7></div> + <div class="target" style="width: anchor-size(--a1 inline)" data-expected-width=5></div> + <div class="target" style="width: anchor-size(--a1 block)" data-expected-width=7></div> + <div class="target" style="width: anchor-size(--a1 self-inline)" data-expected-width=5></div> + <div class="target" style="width: anchor-size(--a1 self-block)" data-expected-width=7></div> + + <!-- `self-inline` and `self-block` take target's `writing-mode` into account. --> + <div class="target vrl" style="width: anchor-size(--a1 width)" data-expected-width=5></div> + <div class="target vrl" style="width: anchor-size(--a1 height)" data-expected-width=7></div> + <div class="target vrl" style="width: anchor-size(--a1 inline)" data-expected-width=5></div> + <div class="target vrl" style="width: anchor-size(--a1 block)" data-expected-width=7></div> + <div class="target vrl" style="width: anchor-size(--a1 self-inline)" data-expected-width=7></div> + <div class="target vrl" style="width: anchor-size(--a1 self-block)" data-expected-width=5></div> + </div> + <div class="container vrl"> + <div class="anchor1 htb"></div> + + <!-- Anchor's `writing-mode` should not matter. --> + <div class="target" style="width: anchor-size(--a1 width)" data-expected-width=5></div> + <div class="target" style="width: anchor-size(--a1 height)" data-expected-width=7></div> + <div class="target" style="width: anchor-size(--a1 inline)" data-expected-width=7></div> + <div class="target" style="width: anchor-size(--a1 block)" data-expected-width=5></div> + <div class="target" style="width: anchor-size(--a1 self-inline)" data-expected-width=7></div> + <div class="target" style="width: anchor-size(--a1 self-block)" data-expected-width=5></div> + + <!-- `self-inline` and `self-block` take target's `writing-mode` into account. --> + <div class="target htb" style="width: anchor-size(--a1 width)" data-expected-width=5></div> + <div class="target htb" style="width: anchor-size(--a1 height)" data-expected-width=7></div> + <div class="target htb" style="width: anchor-size(--a1 inline)" data-expected-width=7></div> + <div class="target htb" style="width: anchor-size(--a1 block)" data-expected-width=5></div> + <div class="target htb" style="width: anchor-size(--a1 self-inline)" data-expected-width=5></div> + <div class="target htb" style="width: anchor-size(--a1 self-block)" data-expected-width=7></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/at-fallback-position-allowed-declarations.html b/testing/web-platform/tests/css/css-anchor-position/at-fallback-position-allowed-declarations.html new file mode 100644 index 0000000000..873fa13140 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/at-fallback-position-allowed-declarations.html @@ -0,0 +1,97 @@ +<!DOCTYPE html> +<title>Tests which properties are allowed in @fallback-position</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback-rule"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style id="style"></style> +<script> +function cleanup() { + while (style.sheet.cssRules.length) + style.sheet.deleteRule(0); +} + +function test_allowed_declaration(property, value = '1px') { + test(t => { + t.add_cleanup(cleanup); + const serialization = `${property}: ${value}`; + const rule = `@position-fallback --foo { @try { ${property}: ${value}; } }`; + const index = style.sheet.insertRule(rule); + const parsed = style.sheet.cssRules[index].cssRules[0]; + assert_equals(parsed.cssText, `@try { ${serialization}; }`); + }, `${property}: ${value} is allowed in @fallback-position`); +} + +function test_disallowed_declaration(property, value = '1px') { + test(t => { + t.add_cleanup(cleanup); + const rule = `@position-fallback --foo { @try { ${property}: ${value}; } }`; + const index = style.sheet.insertRule(rule); + const parsed = style.sheet.cssRules[index].cssRules[0]; + assert_equals(parsed.cssText, `@try { }`); + }, `${property}: ${value} is disallowed in @fallback-position`); +} + +// Inset properties are allowed +test_allowed_declaration('top'); +test_allowed_declaration('bottom'); +test_allowed_declaration('left'); +test_allowed_declaration('right'); +test_allowed_declaration('inset-block-start'); +test_allowed_declaration('inset-block-end'); +test_allowed_declaration('inset-inline-start'); +test_allowed_declaration('inset-inline-end'); +test_allowed_declaration('inset-block'); +test_allowed_declaration('inset-inline'); +test_allowed_declaration('inset'); + +// Sizing properties are allowed +test_allowed_declaration('width'); +test_allowed_declaration('height'); +test_allowed_declaration('block-size'); +test_allowed_declaration('inline-size'); +test_allowed_declaration('min-width'); +test_allowed_declaration('min-height'); +test_allowed_declaration('min-block-size'); +test_allowed_declaration('min-inline-size'); +test_allowed_declaration('max-width'); +test_allowed_declaration('max-height'); +test_allowed_declaration('max-block-size'); +test_allowed_declaration('max-inline-size'); + +// Box alignment properties are allowed +test_allowed_declaration('justify-content', 'normal'); +test_allowed_declaration('align-content', 'normal'); +test_allowed_declaration('justify-items', 'normal'); +test_allowed_declaration('align-items', 'normal'); +test_allowed_declaration('justify-self', 'normal'); +test_allowed_declaration('align-self', 'normal'); + +// Custom properties are disallowed +test_disallowed_declaration('--custom'); + +// Margin properties are disallowed +test_disallowed_declaration('margin-left'); +test_disallowed_declaration('margin-right'); +test_disallowed_declaration('margin-top'); +test_disallowed_declaration('margin-bottom'); +test_disallowed_declaration('margin'); + +// Test some other disallowed properties +test_disallowed_declaration('font-size'); +test_disallowed_declaration('border-width'); +test_disallowed_declaration('padding'); +test_disallowed_declaration('display'); +test_disallowed_declaration('position'); +test_disallowed_declaration('float'); + +// 'revert' and 'revert-layer' are disallowed +test_disallowed_declaration('top', 'revert'); +test_disallowed_declaration('top', 'revert-layer'); +test_disallowed_declaration('inset', 'revert'); +test_disallowed_declaration('inset', 'revert-layer'); + +// !important is disallowed +test_disallowed_declaration('top', '1px !important'); +test_disallowed_declaration('inset', '1px !important'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/at-fallback-position-parse.html b/testing/web-platform/tests/css/css-anchor-position/at-fallback-position-parse.html new file mode 100644 index 0000000000..942d9a2984 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/at-fallback-position-parse.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<title>Tests parsing of @fallback-position rule</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback-rule"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<script> +test_valid_rule('@position-fallback --foo {\n}'); +test_valid_rule(`@position-fallback --foo { + @try { } +}`); + +// @position-fallback needs exactly one <dashed-ident> as its name +test_invalid_rule('@position-fallback {\n}'); +test_invalid_rule('@position-fallback foo {\n}'); +test_invalid_rule('@position-fallback --foo --bar {\n}'); +test_invalid_rule('@position-fallback --foo, --bar {\n}'); + +// @position-fallback accepts only @try blocks as its child rules. Other +// contents should be ignored. +test_valid_rule('@position-fallback --foo { top: 1px; }', + '@position-fallback --foo {\n}'); +test_valid_rule('@position-fallback --foo { --bar: 1px; }', + '@position-fallback --foo {\n}'); +test_valid_rule('@position-fallback --foo { @keyframes bar {} }', + '@position-fallback --foo {\n}'); +test_valid_rule('@position-fallback --foo { @font-face {} }', + '@position-fallback --foo {\n}'); +test_valid_rule('@position-fallback --foo { arbitrary garbage }', + '@position-fallback --foo {\n}'); + +// @try accepts only regular style declarations. Other contents should be +// ignored. +test_valid_rule('@position-fallback --foo { @try { @keyframes bar { } } }', + '@position-fallback --foo {\n @try { }\n}'); +test_valid_rule('@position-fallback --foo { @try { @font-face { } } }', + '@position-fallback --foo {\n @try { }\n}'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/at-position-fallback-invalidation-shadow-dom.html b/testing/web-platform/tests/css/css-anchor-position/at-position-fallback-invalidation-shadow-dom.html new file mode 100644 index 0000000000..569278ac90 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/at-position-fallback-invalidation-shadow-dom.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning Test: Dynamically change @position-fallback rules in Shadow DOM</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback-rule"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/declarative-shadow-dom-polyfill.js"></script> +<style> + body { margin: 0; } +</style> + +<div id="host"> + <template shadowroot="open"> + <style> + ::slotted(#slotted), :host { + position-fallback: --pf; + position: absolute; + } + </style> + <slot></slot> + </template> + <div id="slotted"></div> +</div> + +<script> + setup(() => { + polyfill_declarative_shadow_dom(host); + }); + + test(() => { + assert_equals(host.offsetLeft, 0); + }, "#host is initially left:auto"); + + test(() => { + assert_equals(slotted.offsetLeft, 0); + }, "#slotted is initially left:auto"); + + host.shadowRoot.styleSheets[0].insertRule(` + @position-fallback --pf { + @try { left: 100px; } + } + `); + + test(() => { + assert_equals(host.offsetLeft, 100); + }, "#host with inserted @position-fallback applied"); + + test(() => { + assert_equals(slotted.offsetLeft, 100); + }, "#slotted with inserted @position-fallback applied"); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/at-position-fallback-invalidation.html b/testing/web-platform/tests/css/css-anchor-position/at-position-fallback-invalidation.html new file mode 100644 index 0000000000..68a0874931 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/at-position-fallback-invalidation.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning Test: Dynamically change @position-fallback rules</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback-rule"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + body { margin: 0; } + + #anchor { + anchor-name: --a; + margin-left: 100px; + width: 100px; + } + + #anchored { + position: absolute; + position-fallback: --pf; + } +</style> + +<style id="to-enable" media="print"> + @position-fallback --pf { + @try { left: anchor(--a left); } + } +</style> + +<div> + <div id="anchor">anchor</div> + <div id="anchored">anchored</div> +</div> + +<script> + test(() => { + assert_equals(anchored.offsetLeft, 0); + }, "Position-fallback initially not matching any rules"); + + test(() => { + document.getElementById("to-enable").media = ""; + assert_equals(anchored.offsetLeft, 100); + }, "Enable @position-fallback rule stylesheet"); + + const sheet = document.getElementById("to-enable").sheet; + + test(() => { + sheet.insertRule( + `@position-fallback --pf { + @try { left: anchor(--a right); } + }`, 1); + assert_equals(anchored.offsetLeft, 200); + }, "Insert overriding @position-fallback rule"); + + test(() => { + sheet.disabled = "true"; + assert_equals(anchored.offsetLeft, 0); + }, "Disable the @position-fallback rules"); + +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-001.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-001.html new file mode 100644 index 0000000000..85f1d1d65a --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-001.html @@ -0,0 +1,108 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.cb { + position: relative; + width: 190px; + height: 70px; + background: yellow; + border-bottom: 1px solid black; +} +.spacer { + width: 1px; + height: 20px; +} +.anchor1 { + anchor-name: --a1; + margin-left: 45px; + width: 100px; + height: 30px; + background: blue; +} +.target { + position: absolute; + position-fallback: --fallback1; + width: 40px; + height: 15px; + margin: 5px; + background: orange; +} +@position-fallback --fallback1 { + @try { /* 1: Position to the right of the anchor. */ + left: anchor(--a1 right); + top: anchor(--a1 top); + } + @try { /* 2: Position to the left of the anchor. */ + right: anchor(--a1 left); + top: anchor(--a1 top); + } + @try { /* 3: Position to the bottom of the anchor. */ + left: anchor(--a1 left); + top: anchor(--a1 bottom); + } + @try { /* 4: Position to the top of the anchor. */ + left: anchor(--a1 left); + bottom: anchor(--a1 top); + } + @try { /* 5: Position to the left with the narrower width. */ + left: anchor(--a1 right); + top: anchor(--a1 top); + width: 35px; + height: 40px; + } +} +</style> +<body onload="checkLayout('.target')"> + <!-- If the `cb` is wider, the 1st `@try` fits. --> + <div class="cb" style="width: 195px"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target" + data-offset-x=150 data-offset-y=25 + data-expected-width=40 data-expected-height=15></div> + </div> + <!-- If the `margin-left` is wider, the 2nd `@try` fits. --> + <div class="cb"> + <div class="spacer"></div> + <div class="anchor1" style="margin-left: 50px"></div> + <div class="target" + data-offset-x=5 data-offset-y=25 + data-expected-width=40 data-expected-height=15></div> + </div> + <!-- Without a spacer, the 3rd `@try` fits. --> + <div class="cb"> + <div class="anchor1"></div> + <div class="target" + data-offset-x=50 data-offset-y=35 + data-expected-width=40 data-expected-height=15></div> + </div> + <!-- With two spacers, the 4th `@try` fits. --> + <div class="cb"> + <div class="spacer"></div> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target" + data-offset-x=50 data-offset-y=20 + data-expected-width=40 data-expected-height=15></div> + </div> + <!-- With a spacer, the last `@try` fits. --> + <div class="cb"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target" + data-offset-x=150 data-offset-y=25 + data-expected-width=35 data-expected-height=40></div> + </div> + <!-- If the `cb` is narrower, no rules fit. The last rule is used. --> + <div class="cb" style="width: 185px"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target" + data-offset-x=150 data-offset-y=25 + data-expected-width=35 data-expected-height=40></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-002.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-002.html new file mode 100644 index 0000000000..52c964609b --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-002.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<title>Tests that overflowing the inset-modified containing block triggers position fallback</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.cb { + width: 400px; + height: 400px; + transform: scale(1); + background: yellow; +} +.anchor1 { + anchor-name: --a; + margin-left: 100px; + width: 100px; + height: 100px; + background: blue; +} +.target { + position: absolute; + position-fallback: --fallback; + width: min-content; + height: 100px; + background: orange; +} +.inline-spacer { + display: inline-block; + width: 200px; + height: 100px; +} +@position-fallback --fallback { + @try { /* 1: Position to the left of the anchor. */ + left: 0; + right: anchor(--a left); + top: anchor(--a top); + } + @try { /* 2: Position to the right of the anchor. */ + left: anchor(--a right); + right: 0; + top: anchor(--a top); + } + @try { /* 3: Placeholder fallback that shouldn't be selected when the previous + ones do not overflow the available space. */ + inset: 0; + } +} +</style> +<body onload="checkLayout('.target')"> + <!-- The first `@try` overflows the inset-modifed containing block --> + <div class="cb"> + <div class="anchor1"></div> + <div class="target" + data-offset-x=200 data-offset-y=0 + data-expected-width=200 data-expected-height=100> + <span class="inline-spacer"></span> + </div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-basics.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-basics.html new file mode 100644 index 0000000000..bfeb921de1 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-basics.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<title>Tests basics of the 'position-fallback' property</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-position-fallback"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +<script src="/css/support/inheritance-testcommon.js"></script> +<script src="/css/support/interpolation-testcommon.js"></script> + +<div id="container"> + <div id="target"></div> +</div> + +<script> +// position-fallback: none | <dashed-ident> +test_valid_value('position-fallback', 'none'); +test_valid_value('position-fallback', '--foo'); +test_invalid_value('position-fallback', 'foo-bar'); +test_invalid_value('position-fallback', '--foo --bar') +test_invalid_value('position-fallback', '--foo, --bar') +test_invalid_value('position-fallback', '100px'); +test_invalid_value('position-fallback', '100%'); + +// Computed value: as specified +test_computed_value('position-fallback', 'none'); +test_computed_value('position-fallback', '--foo'); + +// Initial: none +// Inherited: no +assert_not_inherited('position-fallback', 'none', '--foo'); + +// Animation type: discrete +test_no_interpolation({ + property: 'position-fallback', + from: '--foo', + to: 'none', +}); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-cascade-layer-reorder.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-cascade-layer-reorder.html new file mode 100644 index 0000000000..ec1a64205c --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-cascade-layer-reorder.html @@ -0,0 +1,72 @@ +<!DOCTYPE html> +<title>Tests that @position-fallback rules are reordered by cascade layers</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback-rule"> +<link rel="help" href="https://www.w3.org/TR/css-cascade-5/#layering"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +body { margin: 0; } +#anchor { + width: 100px; + height: 100px; + margin-left: 200px; + margin-top: 200px; + color: orange; + anchor-name: --a; +} + +.target { + position: absolute; + width: 100px; + height: 100px; + color: lime; + position-fallback: --fallback; +} +</style> + +<div id="anchor"></div> + +<script> +function createTargetWithStyle(test, style) { + let styleElement = document.createElement('style'); + styleElement.textContent = style; + let target = document.createElement('div'); + target.classList.add('target'); + + test.add_cleanup(() => { + styleElement.remove(); + target.remove(); + }); + + document.head.appendChild(styleElement); + document.body.appendChild(target); + return target; +} + +test(t => { + const target = createTargetWithStyle(t, ` + @position-fallback --fallback { + @try { right: anchor(--a left); } + } + @position-fallback --fallback { + @try { left: anchor(--a right); } + } + `); + assert_equals(target.offsetLeft, 300); +}, 'When in the same layer, the last rule of each name wins'); + +test(t => { + const target = createTargetWithStyle(t, ` + @position-fallback --fallback { + @try { bottom: anchor(--a top); } + } + @layer { + @position-fallback --fallback { + @try { top: anchor(--a bottom); } + } + } + `); + assert_equals(target.offsetTop, 100); +}, 'When in different layers, the rule of each name in the highest layer wins'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-custom-property.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-custom-property.html new file mode 100644 index 0000000000..ca709af3de --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-custom-property.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<title>Variable substitution in @try rules</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.cb { + position: relative; + width: 195px; + height: 70px; + background: yellow; + border-bottom: 1px solid black; +} +.spacer { + width: 1px; + height: 20px; +} +.anchor1 { + anchor-name: --a1; + margin-left: 45px; + width: 100px; + height: 30px; + background: blue; +} +.target { + position: absolute; + position-fallback: --fallback1; + width: 40px; + height: 15px; + margin: 5px; + background: orange; + --left: anchor(--a1 right); + --top: anchor(--a1 top); +} +.fallback1 { + position-fallback: --fallback1; +} +.fallback2 { + position-fallback: --fallback2; +} +@position-fallback --fallback1 { + @try { /* Position to the right of the anchor. */ + left: var(--left); + top: var(--top); + } +} +/* Same as above, but using a shorthand. */ +@position-fallback --fallback2 { + @try { + inset: var(--top) 0px 0px var(--left); + } +} +</style> +<body onload="checkLayout('.target')"> + <div class="cb"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target fallback1" + data-offset-x=150 data-offset-y=25></div> + </div> + <div class="cb"> + <div class="spacer"></div> + <div class="anchor1"></div> + <div class="target fallback2" + data-offset-x=150 data-offset-y=25></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-dynamic.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-dynamic.html new file mode 100644 index 0000000000..f6d8210427 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-dynamic.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning Test: Dynamically change position via position-fallback property</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + body { margin: 0; } + + @position-fallback --fallback1 { + @try { + left: anchor(--a1 right); + } + } + #anchor { + anchor-name: --a1; + width: 100px; + height: 100px; + } + #anchored { + position: absolute; + width: 100px; + height: 100px; + } +</style> +<div id="anchor"></div> +<div id="anchored"></div> +<script> + test(() => { + assert_equals(anchored.offsetLeft, 0); + }, "Initial static left position is 0"); + + test(() => { + anchored.style.positionFallback = "--fallback1"; + assert_equals(anchored.offsetLeft, 100); + }, "Left position set to right edge of anchor with @position-fallback"); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-grid-001.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-grid-001.html new file mode 100644 index 0000000000..f5ab4254b5 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-grid-001.html @@ -0,0 +1,92 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback"> +<link rel="help" href="https://drafts.csswg.org/css-grid/#abspos-items"> +<link rel="author" href="mailto:kojii@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<style> +.cb { + position: relative; +} +.grid { + display: grid; + grid-template-columns: repeat(4, 100px); + grid-template-rows: 50px 100px 50px 50px; +} +.item { + background: lightgray; +} +.spacer { + background: yellow; +} +.anchor1 { + anchor-name: --a1; + background: orange; + margin-left: 15px; + width: 20px; + height: 30px; +} +.target { + grid-column: 2 / 4; + grid-row: 2 / 4; + position: absolute; + position-fallback: --fallback1; + width: 100px; + height: 100px; + background: lime; + opacity: .2; +} +@position-fallback --fallback1 { + @try { /* Position to the left of the anchor. */ + right: anchor(--a1 left); + top: anchor(--a1 top); + } + @try { /* Position to the right of the anchor with the wider width than CB. */ + left: anchor(--a1 right); + top: anchor(--a1 top); + width: 250px; + } + @try { /* Position to the right of the anchor. This entry should succeed. */ + left: anchor(--a1 right); + top: anchor(--a1 top); + } + @try { /* Zero-sized, the last entry wins if none succeeded. */ + left: 0; + top: 0; + width: 0; + height: 0; + } +} +</style> +<body onload="checkLayout('.target')"> + <div> + <div class="spacer" style="height: 10px"></div> + <div class="grid cb"> + <div class="item">1</div> + <div class="item">2</div> + <div class="item">3</div> + <div class="item">4</div> + <div class="item">5</div> + <div class="item"> + <div class="spacer" style="height: 20px"></div> + <div class="anchor1"></div> + </div> + <div class="item">7</div> + <div class="item">8</div> + <div class="item">9</div> + <div class="item">10</div> + <div class="item">11</div> + <div class="item">12</div> + <div class="item">13</div> + <div class="item">14</div> + <div class="item">15</div> + <div class="item">16</div> + + <div class="target" + data-offset-x=135 data-offset-y=70 + data-expected-height=100></div> + </div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-tree-scoped.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-tree-scoped.html new file mode 100644 index 0000000000..955c3a7c1b --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-tree-scoped.html @@ -0,0 +1,159 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning Test: @position-fallback - tree scoped names</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback-rule"> +<link rel="help" href="https://drafts.csswg.org/css-scoping/#shadow-names"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/declarative-shadow-dom-polyfill.js"></script> + +<style> + body { margin: 0; } + + @position-fallback --doc { + @try { + left: 100px; + } + } + + .abs { position: absolute; } + + #doc_pf_doc { position-fallback: --doc; } + #doc_pf_outer { position-fallback: --outer; } + #doc_pf_inner { position-fallback: --inner; } +</style> + +<div id="doc_pf_doc" class="abs"></div> +<div id="doc_pf_outer" class="abs"></div> +<div id="doc_pf_inner" class="abs"></div> +<div id="outer_host"> + <template shadowroot="open"> + <style> + @position-fallback --outer { + @try { + left: 200px; + } + } + + .abs { position: absolute; } + + #outer_pf_doc { position-fallback: --doc; } + #outer_pf_outer { position-fallback: --outer; } + #outer_pf_inner { position-fallback: --inner; } + </style> + <div id="outer_pf_doc" class="abs"></div> + <div id="outer_pf_outer" class="abs"></div> + <div id="outer_pf_inner" class="abs"></div> + <div id="inner_host"> + <template shadowroot="open"> + <style> + @position-fallback --inner { + @try { + left: 300px; + } + } + + .abs { position: absolute; } + + #inner_pf_doc { position-fallback: --doc; } + #inner_pf_outer { position-fallback: --outer; } + #inner_pf_inner { position-fallback: --inner; } + </style> + <div id="inner_pf_doc" class="abs"></div> + <div id="inner_pf_outer" class="abs"></div> + <div id="inner_pf_inner" class="abs"></div> + </template> + </div> + </template> +</div> + + +<style> + @position-fallback --host-slot-part { + @try { + left: 1px; + } + } + #host_slotted_part::part(part) { + position-fallback: --host-slot-part; + } +</style> +<div id="host_slotted_part"> + <template shadowroot="open"> + <style> + @position-fallback --host-slot-part { + @try { + left: 2px; + } + } + ::slotted(#slotted), :host { + position: absolute; + position-fallback: --host-slot-part; + } + #part { + position: absolute; + } + </style> + <div id="part" part="part"></div> + <slot></slot> + </template> + <div id="slotted"></div> +</div> + + +<script> + setup(() => { + polyfill_declarative_shadow_dom(outer_host); + polyfill_declarative_shadow_dom(host_slotted_part); + }); + + test(() => { + assert_equals(doc_pf_doc.offsetLeft, 100); + }, "Document position-fallback matches @position-fallback in document scope"); + + test(() => { + assert_equals(doc_pf_outer.offsetLeft, 0); + }, "Document position-fallback does not match @position-fallback in #outer_host scope"); + + test(() => { + assert_equals(doc_pf_inner.offsetLeft, 0); + }, "Document position-fallback does not match @position-fallback in #inner_host scope"); + + const outer_root = outer_host.shadowRoot; + const inner_root = outer_root.querySelector("#inner_host").shadowRoot; + + test(() => { + assert_equals(outer_root.querySelector("#outer_pf_doc").offsetLeft, 100); + }, "Outer position-fallback matches @position-fallback in document scope"); + + test(() => { + assert_equals(outer_root.querySelector("#outer_pf_outer").offsetLeft, 200); + }, "Outer position-fallback matches @position-fallback in #outer_host scope"); + + test(() => { + assert_equals(outer_root.querySelector("#outer_pf_inner").offsetLeft, 0); + }, "Outer position-fallback does not match @position-fallback in #inner_host scope"); + + test(() => { + assert_equals(inner_root.querySelector("#inner_pf_doc").offsetLeft, 100) + }, "Inner position-fallback matches @position-fallback in document scope"); + + test(() => { + assert_equals(inner_root.querySelector("#inner_pf_outer").offsetLeft, 200); + }, "Inner position-fallback matches @position-fallback in #outer_host scope"); + + test(() => { + assert_equals(inner_root.querySelector("#inner_pf_inner").offsetLeft, 300); + }, "Inner position-fallback matches @position-fallback in #inner_host scope"); + + test(() => { + assert_equals(host_slotted_part.offsetLeft, 2); + }, "@position-fallback from same scope as :host rule"); + + test(() => { + assert_equals(slotted.offsetLeft, 2); + }, "@position-fallback from same scope as ::slotted() rule"); + + test(() => { + assert_equals(host_slotted_part.shadowRoot.querySelector("#part").offsetLeft, 1); + }, "@position-fallback from same scope as ::part() rule"); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-fixedpos-ref.html b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-fixedpos-ref.html new file mode 100644 index 0000000000..e73354df72 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-fixedpos-ref.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<style> +body { + margin: 0; + height: 2000px; +} + +div { + width: 100px; + height: 100px; +} + +#anchor { + margin: 300px; + background: orange; +} + +#anchored { + position: fixed; + left: 400px; + top: 100px; + background: green; +} + +</style> + +<div id="anchor"></div> +<div id="anchored"></div> + +<script> +document.documentElement.scrollTop = 200; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-nested-ref.html b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-nested-ref.html new file mode 100644 index 0000000000..6190258f3c --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-nested-ref.html @@ -0,0 +1,74 @@ +<!DOCTYPE html> +<title>Tests anchor-scroll with nested scroll containers</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<style> +body { + margin: 0; + width: 1500px; + height: 1500px; + position: relative; +} + +#outer-scroller { + margin: 500px; + width: 350px; + height: 350px; + outline: 1px solid black; + overflow: scroll; +} + +#inner-scroller { + margin: 100px; + width: 250px; + height: 250px; + outline: 1px solid black; + overflow: scroll; +} + +#anchor { + margin: 200px; + width: 50px; + height: 50px; + background-color: green; +} + +.anchored { + position: fixed; + width: 50px; + height: 50px; + left: 250px; +} + +.above { + top: 200px; + background-color: red; +} + +.below { + top: 300px; + background-color: yellow; +} +</style> + +<div id="outer-scroller"> + <div id="inner-scroller"> + <div id="anchor"></div> + <div class="anchored above"></div> + </div> +</div> + +<div class="anchored below"></div> + +<script> +document.documentElement.scrollTop = 400; +document.documentElement.scrollLeft = 400; + +let outerScroller = document.getElementById('outer-scroller'); +outerScroller.scrollTop = 50; +outerScroller.scrollLeft = 50; + +let innerScroller = document.getElementById('inner-scroller'); +innerScroller.scrollTop = 100; +innerScroller.scrollLeft = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-ref.html b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-ref.html new file mode 100644 index 0000000000..7935f3823f --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-ref.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<title>Basic of anchor-scroll: anchored elements should be "pinned" to the anchor when anchor is scrolled</title> +<link rel="stylesheet" href="/fonts/ahem.css"> +<style> +body { + font: 20px/1 Ahem; + margin: 0; +} + +#scroll-container { + width: 400px; + height: 400px; + overflow: scroll; +} + +#scroll-contents { + width: 1000px; + height: 1000px; + position: relative; +} + +#placefiller-above-anchor { + height: 500px; +} + +#placefiller-before-anchor { + display: inline-block; + width: 500px; +} + +#inner-anchored { + color: green; + position: fixed; + left: 70px; + top: 180px; +} + +#outer-anchored { + color: yellow; + position: fixed; + left: 70px; + top: 220px; +} +</style> + +<div id="scroll-container"> + <div id="scroll-contents"> + <div id="placefiller-above-anchor"></div> + <div id="placefiller-before-anchor"></div> + <span id="anchor">anchor</span> + </div> +</div> + +<div id="inner-anchored">inner-anchored</div> +<div id="outer-anchored">outer-anchored</div> + +<script> +const scroller = document.getElementById('scroll-container'); +scroller.scrollTop = 300; +scroller.scrollLeft = 450; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-005-ref.html b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-005-ref.html new file mode 100644 index 0000000000..b0c3a820f8 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-005-ref.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<style> + #cb { + position: absolute; + inset: 0; + } + #scroller { + margin-top: 300px; + overflow-y: scroll; + height: 200px; + } + #spacer { height: 400px; } + #anchor { anchor-name: --a; } + #anchored { + position: absolute; + width: 100px; + height: 100px; + background-color: green; + top: 100px; + left: 0; + } +</style> +<div id="cb"> + <div id="scroller"> + <div id="anchor"></div> + <div id="spacer"></div> + </div> + <div id="anchored"></div> +</div> +<script> +const scroller = document.getElementById('scroller'); +scroller.scrollTop = 200; +</script> + diff --git a/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-006-ref.html b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-006-ref.html new file mode 100644 index 0000000000..c7fbb57e76 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-006-ref.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<style> + #cb { + position: absolute; + inset: 0; + } + #scroller { + margin-top: 300px; + overflow-y: scroll; + height: 100px; + } + #spacer { height: 300px; } + #anchor { anchor-name: --a; } + #anchored { + position: absolute; + width: 100px; + height: 100px; + background-color: green; + top: 100px; + left: 0; + } +</style> +<div id="cb"> + <div id="scroller"> + <div id="anchor"></div> + <div id="spacer"></div> + </div> + <div id="anchored"></div> +</div> +<script> +const scroller = document.getElementById('scroller'); +scroller.scrollTop = 200; +</script> + diff --git a/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-vlr-ref.html b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-vlr-ref.html new file mode 100644 index 0000000000..cbb249e150 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-vlr-ref.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<title>Tests that anchor-scroll works in vertical-lr writing mode</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> +<link rel="stylesheet" href="/fonts/ahem.css"> +<style> +:root { + overflow: clip; +} + +body { + font: 20px/1 Ahem; + margin: 0; + writing-mode: vertical-lr; + white-space: nowrap; +} + +#scroll-container { + width: 400px; + height: 400px; + overflow: scroll; +} + +#scroll-contents { + width: 1000px; + height: 1000px; + position: relative; +} + +#placefiller-above-anchor { + width: 480px; +} + +#placefiller-before-anchor { + display: inline-block; + height: 500px; +} + +#anchor { + anchor-name: --anchor; +} + +#inner-anchored { + color: green; + margin-top: 520px; +} + +#outer-anchored { + color: yellow; + margin-top: 520px; +} +</style> + +<div style="position: relative;"> + <div id="scroll-container"> + <div id="scroll-contents"> + <div id="placefiller-above-anchor"></div> + <div id="outer-anchored">outer-anchored</div> + <div id="placefiller-before-anchor"></div> + <span id="anchor">anchor</span> + <div id="inner-anchored">inner-anchored</div> + </div> + </div> +</div> + +<script> +const scroller = document.getElementById('scroll-container'); +scroller.scrollTop = 450; +scroller.scrollLeft = 300; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-vrl-ref.html b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-vrl-ref.html new file mode 100644 index 0000000000..746dfee074 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-vrl-ref.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<title>Tests that anchor-scroll works in vertical-rl writing mode</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-scroll"> +<link rel="stylesheet" href="/fonts/ahem.css"> +<style> +:root { + overflow: clip; +} + +body { + font: 20px/1 Ahem; + margin: 0; + writing-mode: vertical-rl; + white-space: nowrap; +} + +#scroll-container { + width: 400px; + height: 400px; + overflow: scroll; +} + +#scroll-contents { + width: 1000px; + height: 1000px; + position: relative; +} + +#placefiller-above-anchor { + width: 480px; +} + +#placefiller-before-anchor { + display: inline-block; + height: 500px; +} + +#anchor { + anchor-name: --anchor; +} + +#inner-anchored { + margin-top: 520px; + color: green; +} + +#outer-anchored { + margin-top: 520px; + color: yellow; +} +</style> + +<div style="position: relative;"> + <div id="scroll-container"> + <div id="scroll-contents"> + <div id="placefiller-above-anchor"></div> + <div id="inner-anchored">inner-anchored</div> + <div id="placefiller-before-anchor"></div> + <span id="anchor">anchor</span> + <div id="outer-anchored">outer-anchored</div> + </div> + </div> +</div> + +<script> +const scroller = document.getElementById('scroll-container'); +scroller.scrollTop = 450; +scroller.scrollLeft = -300; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/reference/sticky-anchor-position-invalid-ref.html b/testing/web-platform/tests/css/css-anchor-position/reference/sticky-anchor-position-invalid-ref.html new file mode 100644 index 0000000000..3a48755874 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/reference/sticky-anchor-position-invalid-ref.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<title>Anchor queries in sticky positioning is invalid</title> +<style> +#scroll-container { + width: 200px; + height: 200px; + overflow-y: scroll; +} + +#scroller { + height: 400px; +} + +#sticky { + position: sticky; + height: 150px; + background: green; +} +</style> +<div id="scroll-container"> + <div id="scroller"> + <div id="sticky"> + </div> + </div> +</div> +<script> +document.getElementById("scroll-container").scrollTop = 50; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/sticky-anchor-position-invalid.html b/testing/web-platform/tests/css/css-anchor-position/sticky-anchor-position-invalid.html new file mode 100644 index 0000000000..f7c1e1a374 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/sticky-anchor-position-invalid.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<title>Anchor queries in sticky positioning is invalid</title> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#queries"> +<link rel="match" href="reference/sticky-anchor-position-invalid-ref.html"> +<style> +#scroll-container { + width: 200px; + height: 200px; + overflow-y: scroll; +} + +#scroller { + height: 400px; +} + +#sticky { + position: sticky; + height: 150px; + background: green; + top: anchor(--invalid top); /* should be invalid and treated as auto */ +} +</style> +<div id="scroll-container"> + <div id="scroller"> + <div id="sticky"> + </div> + </div> +</div> +<script> +document.getElementById("scroll-container").scrollTop = 50; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/support/green-16x16.png b/testing/web-platform/tests/css/css-anchor-position/support/green-16x16.png Binary files differnew file mode 100644 index 0000000000..e19a3ffddd --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/support/green-16x16.png diff --git a/testing/web-platform/tests/css/css-anchor-position/support/test-common.js b/testing/web-platform/tests/css/css-anchor-position/support/test-common.js new file mode 100644 index 0000000000..5ef7a951b4 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/support/test-common.js @@ -0,0 +1,27 @@ +// Asserts that the anchored element is at the top/bottom/left/right of the +// anchor. +function assert_fallback_position(anchored, anchor, direction) { + let anchoredRect = anchored.getBoundingClientRect(); + let anchorRect = anchor.getBoundingClientRect(); + let message = `Anchored element should be at the ${direction} of anchor`; + switch (direction) { + case 'top': + assert_equals(anchoredRect.bottom, anchorRect.top, message); + return; + case 'bottom': + assert_equals(anchoredRect.top, anchorRect.bottom, message); + return; + case 'left': + assert_equals(anchoredRect.right, anchorRect.left, message); + return; + case 'right': + assert_equals(anchoredRect.left, anchorRect.right, message); + return; + default: + assert_unreached('unsupported direction'); + } +} + +async function waitUntilNextAnimationFrame() { + return new Promise(resolve => requestAnimationFrame(resolve)); +} |