summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/css-anchor-position
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/css/css-anchor-position
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/css/css-anchor-position')
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/WEB_FEATURES.yml3
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-center-001.html24
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-center-002-ref.html49
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-center-002.html52
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-htb.html84
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-vrl.html85
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-htb.html86
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-vrl.html85
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-default-001.html56
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-default-002.html70
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-default-003.html55
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-default-basics.html42
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-default-ref.html44
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-001.html124
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-002.html112
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-003.html92
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-001.html39
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-002.html58
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-003.html151
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-004.html44
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-basics.html41
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-cross-shadow.html54
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-in-shadow-002.html54
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-in-shadow.html53
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-inline-001.html58
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-001.html55
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-002.html62
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-003.html67
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-004.html61
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained-dynamic.html71
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained.html60
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-non-oof-inherit.html117
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-parse-invalid.html41
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-parse-valid.html67
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-001.html55
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-002.html73
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-003.html59
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-004.html117
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-borders-001.html136
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-borders-002.html85
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-001.html68
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-002.html55
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-003.html106
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-004.html46
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-grid-001.html83
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-001.html37
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-002.html38
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-003.html38
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-004.html176
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-001.html44
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-002.html110
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-003.html67
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-004.html84
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-005.html65
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-006.html72
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-colspan-001.html101
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-colspan-002.html77
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-fixed-001.html81
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html80
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-principal-box.html32
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-001.html51
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-002.html51
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-003.html52
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-004.html52
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-005.html52
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-006.html52
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-ref.html34
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-writing-modes-001.html101
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-position-writing-modes-002.html103
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-query-custom-property-registration.html50
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-query-fallback.html70
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-001.html71
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-002.html99
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-003.html76
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-004.html89
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-005.html55
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-006.html137
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-007.html86
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-001-crash.html41
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-002-crash.html49
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-003-crash.html49
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-004-crash.html46
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-005-crash.html51
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-006.html61
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-001.html101
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-002.html62
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-003.html73
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-004.html75
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-005.html63
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-006.html91
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-007.html95
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-008.html96
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-009.html95
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-010.html96
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-011.html115
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos.html38
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-js-expose.html89
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-nested.html90
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-001.html83
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-002.html84
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-003.html81
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-004.html93
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-005.html56
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-006.html56
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-007.html82
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vlr.html81
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vrl.html81
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-size-001.html43
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-size-minmax-001.html45
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-size-parse-invalid.html42
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-size-parse-valid.html65
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-size-replaced-001.html75
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-size-writing-modes-001.html68
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-transition-001.html78
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-transition-002.html70
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-transition-003.html89
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/at-fallback-position-allowed-declarations.html104
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/at-fallback-position-parse.html39
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/at-position-fallback-cssom.html81
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/at-position-fallback-invalidation-shadow-dom.html45
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/at-position-fallback-invalidation.html57
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/chrome-1512373-2-crash.html18
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/chrome-1512373-crash.html7
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/idlharness.html37
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/inset-area-abs-inline-container.html40
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/inset-area-basic.html121
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/inset-area-computed-insets.tentative.html24
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/inset-area-computed.html23
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/inset-area-inline-container-ref.html16
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/inset-area-inline-container.html38
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/inset-area-interpolation.html14
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/inset-area-parsing.html49
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/inset-area-wm-dir.html172
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-computed.html22
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-parsing.html29
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/parsing/position-try-order-computed.html20
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/parsing/position-try-order-parsing.html23
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-001.html109
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-002.html62
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-003.html142
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-004.html74
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-basics.html40
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-001.html82
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-002.html85
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-003.html96
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-004.html98
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-005.html75
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-006.html64
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-basics.html40
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-cascade-layer-reorder.html72
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-container-query.html95
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-custom-property.html69
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-dynamic.html36
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-grid-001.html93
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-pseudo-element.html57
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/position-fallback-tree-scoped.html153
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/property-interpolations.html90
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-composited-scrolling-006-ref.html39
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-fixedpos-ref.html32
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-nested-ref.html74
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-ref.html61
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-005-ref.html34
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-006-ref.html34
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-vlr-ref.html70
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-vrl-ref.html70
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/reference/sticky-anchor-position-invalid-ref.html28
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/sticky-anchor-position-invalid.html32
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/support/green-16x16.pngbin0 -> 92 bytes
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/support/test-common.js44
169 files changed, 11359 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/css-anchor-position/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-anchor-position/WEB_FEATURES.yml
new file mode 100644
index 0000000000..2e4c34982a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: anchor-positioning
+ files: "**"
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-center-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-center-001.html
new file mode 100644
index 0000000000..f8583e68c0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-center-001.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>Tests the anchor-center keyword is parsed and computed as specified</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#anchor-center">
+<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>
+
+<div id="container">
+ <div id="target"></div>
+</div>
+
+<script>
+test_valid_value('align-self', 'anchor-center');
+test_valid_value('align-items', 'anchor-center');
+test_valid_value('justify-self', 'anchor-center');
+test_valid_value('justify-items', 'anchor-center');
+
+test_computed_value('align-self', 'anchor-center');
+test_computed_value('align-items', 'anchor-center');
+test_computed_value('justify-self', 'anchor-center');
+test_computed_value('justify-items', 'anchor-center');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-center-002-ref.html b/testing/web-platform/tests/css/css-anchor-position/anchor-center-002-ref.html
new file mode 100644
index 0000000000..40ccc2b044
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-center-002-ref.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>Tests that 'anchor-center' behaves as 'center' in non-OOF layout modes</title>
+
+<style>
+.container {
+ width: 100px;
+ height: 100px;
+ background: orange;
+ margin-block: 5px;
+}
+
+.item {
+ width: 40px;
+ height: 40px;
+ background: lime;
+}
+
+.flex {
+ display: flex;
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: repeat(8, 1fr);
+ grid-auto-rows: 50px;
+ grid-template-areas:
+ "a a a a b b b b"
+ "a a a a b b b b";
+}
+</style>
+
+<div class="flex container" style="align-items: center">
+ <div class="item"></div>
+</div>
+
+<div class="flex container">
+ <div class="item" style="align-self: center"></div>
+</div>
+
+<div class="grid container"
+ style="align-items: center; justify-items: center">
+ <div class="item" style="grid-area: a"></div>
+ <div class="item" style="grid-area: b"></div>
+</div>
+
+<div class="grid container">
+ <div class="item" style="grid-area: a; align-self: center"></div>
+ <div class="item" style="grid-area: b; justify-self: center"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-center-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-center-002.html
new file mode 100644
index 0000000000..b7f61003a6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-center-002.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<title>Tests that 'anchor-center' behaves as 'center' in non-OOF layout modes</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#anchor-center">
+<link rel="match" href="anchor-center-002-ref.html">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+
+<style>
+.container {
+ width: 100px;
+ height: 100px;
+ background: orange;
+ margin-block: 5px;
+}
+
+.item {
+ width: 40px;
+ height: 40px;
+ background: lime;
+}
+
+.flex {
+ display: flex;
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: repeat(8, 1fr);
+ grid-auto-rows: 50px;
+ grid-template-areas:
+ "a a a a b b b b"
+ "a a a a b b b b";
+}
+</style>
+
+<div class="flex container" style="align-items: anchor-center">
+ <div class="item"></div>
+</div>
+
+<div class="flex container">
+ <div class="item" style="align-self: anchor-center"></div>
+</div>
+
+<div class="grid container"
+ style="align-items: anchor-center; justify-items: anchor-center">
+ <div class="item" style="grid-area: a"></div>
+ <div class="item" style="grid-area: b"></div>
+</div>
+
+<div class="grid container">
+ <div class="item" style="grid-area: a; align-self: center"></div>
+ <div class="item" style="grid-area: b; justify-self: center"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-htb.html b/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-htb.html
new file mode 100644
index 0000000000..7012208044
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-htb.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/">
+<title>Tests the position and available-size of 'anchor-center' alignment with different insets.</title>
+<style>
+.container {
+ width: 100px;
+ height: 100px;
+ border: solid 3px;
+ position: relative;
+ margin: 50px;
+}
+
+.anchor {
+ anchor-name: --anchor;
+ position: relative;
+ width: 50px;
+ height: 50px;
+ left: 40px;
+ top: 40px;
+ background: lime;
+}
+
+.target {
+ anchor-default: --anchor;
+ position: absolute;
+ background: cyan;
+ justify-self: anchor-center;
+ top: anchor(bottom);
+ height: 20px;
+}
+/* used to test the available-size given to the element */
+.target::after {
+ color: transparent;
+ content: 'a a a a a a a a a a a a a a a a a a';
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+
+<body onload="checkLayout('.target')">
+
+<!-- no insets -->
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" data-expected-width="70" data-offset-x="30"></div>
+</div>
+
+<!-- single insets -->
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="left: 20px;" data-expected-width="70" data-offset-x="30"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="right: 20px;" data-expected-width="30" data-offset-x="50"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="right: -20px;" data-expected-width="110" data-offset-x="10"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="right: -100px;" data-expected-width="130" data-offset-x="0"></div>
+</div>
+
+<!-- both insets -->
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="left: 10px; right: 20px;" data-expected-width="30" data-offset-x="50"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="left: 10px; right: -20px;" data-expected-width="110" data-offset-x="10"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="left: -10px; right: -50px;" data-expected-width="150" data-offset-x="-10"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-vrl.html b/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-vrl.html
new file mode 100644
index 0000000000..584424d306
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-vrl.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/">
+<title>Tests the position and available-size of 'anchor-center' alignment with different insets.</title>
+<style>
+.container {
+ width: 100px;
+ height: 100px;
+ border: solid 3px;
+ position: relative;
+ margin: 50px;
+}
+
+.anchor {
+ anchor-name: --anchor;
+ position: relative;
+ width: 50px;
+ height: 50px;
+ left: 40px;
+ top: 40px;
+ background: lime;
+}
+
+.target {
+ writing-mode: vertical-rl;
+ anchor-default: --anchor;
+ position: absolute;
+ background: cyan;
+ align-self: anchor-center;
+ right: anchor(left);
+ width: 20px;
+}
+/* used to test the available-size given to the element */
+.target::after {
+ color: transparent;
+ content: 'a a a a a a a a a a a a a a a a a a';
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+
+<body onload="checkLayout('.target')">
+
+<!-- no insets -->
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" data-expected-height="70" data-offset-y="30"></div>
+</div>
+
+<!-- single insets -->
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="top: 20px;" data-expected-height="70" data-offset-y="30"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="bottom: 20px;" data-expected-height="30" data-offset-y="50"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="bottom: -20px;" data-expected-height="110" data-offset-y="10"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="bottom: -100px;" data-expected-height="130" data-offset-y="0"></div>
+</div>
+
+<!-- both insets -->
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="top: 10px; bottom: 20px;" data-expected-height="30" data-offset-y="50"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="top: 10px; bottom: -20px;" data-expected-height="110" data-offset-y="10"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="top: -10px; bottom: -50px;" data-expected-height="150" data-offset-y="-10"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-htb.html b/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-htb.html
new file mode 100644
index 0000000000..c7ee230262
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-htb.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/">
+<title>Tests the position and available-size of 'anchor-center' alignment with different insets.</title>
+<style>
+.container {
+ writing-mode: vertical-rl;
+ width: 100px;
+ height: 100px;
+ border: solid 3px;
+ position: relative;
+ margin: 50px;
+}
+
+.anchor {
+ anchor-name: --anchor;
+ position: relative;
+ width: 50px;
+ height: 50px;
+ right: 10px;
+ top: 40px;
+ background: lime;
+}
+
+.target {
+ writing-mode: horizontal-tb;
+ anchor-default: --anchor;
+ position: absolute;
+ background: cyan;
+ align-self: anchor-center;
+ top: anchor(bottom);
+ height: 20px;
+}
+/* used to test the available-size given to the element */
+.target::after {
+ color: transparent;
+ content: 'a a a a a a a a a a a a a a a a a a';
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+
+<body onload="checkLayout('.target')">
+
+<!-- no insets -->
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" data-expected-width="70" data-offset-x="30"></div>
+</div>
+
+<!-- single insets -->
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="left: 20px;" data-expected-width="70" data-offset-x="30"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="right: 20px;" data-expected-width="30" data-offset-x="50"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="right: -20px;" data-expected-width="110" data-offset-x="10"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="right: -100px;" data-expected-width="130" data-offset-x="0"></div>
+</div>
+
+<!-- both insets -->
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="left: 10px; right: 20px;" data-expected-width="30" data-offset-x="50"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="left: 10px; right: -20px;" data-expected-width="110" data-offset-x="10"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="left: -10px; right: -50px;" data-expected-width="150" data-offset-x="-10"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-vrl.html b/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-vrl.html
new file mode 100644
index 0000000000..d314dc7f2f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-vrl.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/">
+<title>Tests the position and available-size of 'anchor-center' alignment with different insets.</title>
+<style>
+.container {
+ writing-mode: vertical-rl;
+ width: 100px;
+ height: 100px;
+ border: solid 3px;
+ position: relative;
+ margin: 50px;
+}
+
+.anchor {
+ anchor-name: --anchor;
+ position: relative;
+ width: 50px;
+ height: 50px;
+ right: 40px;
+ top: 40px;
+ background: lime;
+}
+
+.target {
+ anchor-default: --anchor;
+ position: absolute;
+ background: cyan;
+ justify-self: anchor-center;
+ right: anchor(left);
+ width: 20px;
+}
+/* used to test the available-size given to the element */
+.target::after {
+ color: transparent;
+ content: 'a a a a a a a a a a a a a a a a a a';
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+
+<body onload="checkLayout('.target')">
+
+<!-- no insets -->
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" data-expected-height="70" data-offset-y="30"></div>
+</div>
+
+<!-- single insets -->
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="top: 20px;" data-expected-height="70" data-offset-y="30"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="bottom: 20px;" data-expected-height="30" data-offset-y="50"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="bottom: -20px;" data-expected-height="110" data-offset-y="10"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="bottom: -100px;" data-expected-height="130" data-offset-y="0"></div>
+</div>
+
+<!-- both insets -->
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="top: 10px; bottom: 20px;" data-expected-height="30" data-offset-y="50"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="top: 10px; bottom: -20px;" data-expected-height="110" data-offset-y="10"></div>
+</div>
+
+<div class="container">
+ <div class="anchor"></div>
+ <div class="target" style="top: -10px; bottom: -50px;" data-expected-height="150" data-offset-y="-10"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-default-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-default-001.html
new file mode 100644
index 0000000000..8bb59851ee
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-default-001.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<title>Tests the 'anchor-default' property</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-default">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="anchor-default-ref.html">
+<style>
+.anchor {
+ width: 100px;
+ height: 100px;
+ background: orange;
+}
+
+.target {
+ position: fixed;
+ background: lime;
+ position-fallback: --pf;
+}
+
+@position-fallback --pf {
+ @try {
+ top: anchor(bottom, 0px);
+ left: anchor(left, 0px);
+ width: anchor-size(width, 0px);
+ height: anchor-size(height, 0px);
+ }
+}
+
+body {
+ margin: 0;
+}
+
+#anchor1 {
+ anchor-name: --a1;
+ margin-left: 100px;
+}
+
+#target1 {
+ anchor-default: --a1;
+}
+
+#anchor2 {
+ anchor-name: --a2;
+ margin-left: 300px;
+ margin-top: 100px;
+}
+
+#target2 {
+ anchor-default: --a2;
+}
+</style>
+
+<div id="anchor1" class="anchor">anchor1</div>
+<div id="anchor2" class="anchor">anchor2</div>
+
+<div id="target1" class="target">target1</div>
+<div id="target2" class="target">target2</div>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-default-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-default-002.html
new file mode 100644
index 0000000000..261119e017
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-default-002.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<title>Tests that 'anchor-default' property value is tree-scoped</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-default">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="anchor-default-ref.html">
+<style>
+.anchor {
+ width: 100px;
+ height: 100px;
+ background: orange;
+}
+
+.target {
+ position: fixed;
+ background: lime;
+ position-fallback: --pf;
+}
+
+@position-fallback --pf {
+ @try {
+ top: anchor(bottom, 0px);
+ left: anchor(left, 0px);
+ width: anchor-size(width, 0px);
+ height: anchor-size(height, 0px);
+ }
+}
+
+body {
+ margin: 0;
+}
+
+#fake-anchor {
+ anchor-name: --a;
+}
+
+#anchor1 {
+ margin-left: 100px;
+}
+
+#anchor2 {
+ margin-left: 300px;
+ margin-top: 100px;
+}
+
+</style>
+
+<div id="fake-anchor"></div>
+
+<div id="anchor1" class="anchor">
+ anchor1
+ <div id="target1" class="target">target1</div>
+</div>
+
+<div id="anchor2" class="anchor">
+ anchor2
+ <div id="target2" class="target">target2</div>
+</div>
+
+<script>
+for (let host of document.querySelectorAll('.anchor')) {
+ let shadow = host.attachShadow({mode: 'open'});
+ shadow.innerHTML = `
+ <style>
+ :host { anchor-name: --a; }
+ ::slotted(.target) { anchor-default: --a; }
+ </style>
+ <slot></slot>
+ `;
+}
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-default-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-default-003.html
new file mode 100644
index 0000000000..00c2032434
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-default-003.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<title>Tests that layout is updated on anchor-default value changes</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/#anchor-default">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+#target {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background: lime;
+ top: anchor(top);
+ left: anchor(right);
+ anchor-default: --a;
+}
+
+#target.after {
+ anchor-default: --b;
+}
+
+#anchor1, #anchor2 {
+ width: 100px;
+ height: 100px;
+ background: orange;
+}
+
+#anchor1 {
+ anchor-name: --a;
+}
+
+#anchor2 {
+ margin-left: 100px;
+ anchor-name: --b;
+}
+
+body {
+ margin: 0;
+}
+</style>
+
+<div id="anchor1"></div>
+<div id="anchor2"></div>
+<div id="target"></div>
+
+<script>
+test(() => {
+ document.body.offsetLeft; // Force layout
+ target.classList.add('after');
+ // #target should be anchored to #anchor2 now
+ assert_equals(target.offsetLeft, 200);
+ assert_equals(target.offsetTop, 100);
+}, 'Layout is updated on `anchor-default` changes');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-default-basics.html b/testing/web-platform/tests/css/css-anchor-position/anchor-default-basics.html
new file mode 100644
index 0000000000..783cb539cc
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-default-basics.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<title>Tests basics of the 'anchor-default' property</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-default">
+<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-default: <anchor-element>
+// <anchor-element> = implicit | <dashed-ident>
+test_valid_value('anchor-default', 'implicit');
+test_valid_value('anchor-default', '--foo');
+test_invalid_value('anchor-default', 'none');
+test_invalid_value('anchor-default', 'foo-bar');
+test_invalid_value('anchor-default', '--foo --bar')
+test_invalid_value('anchor-default', '--foo, --bar')
+test_invalid_value('anchor-default', '100px');
+test_invalid_value('anchor-default', '100%');
+
+// Computed value: as specified
+test_computed_value('anchor-default', 'implicit');
+test_computed_value('anchor-default', '--foo');
+
+// Initial: implicit
+// Inherited: no
+assert_not_inherited('anchor-default', 'implicit', '--foo');
+
+// Animation type: discrete
+test_no_interpolation({
+ property: 'anchor-default',
+ from: '--foo',
+ to: 'implicit',
+});
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-default-ref.html b/testing/web-platform/tests/css/css-anchor-position/anchor-default-ref.html
new file mode 100644
index 0000000000..4d7de12447
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-default-ref.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<style>
+.anchor {
+ width: 100px;
+ height: 100px;
+ background: orange;
+}
+
+.target {
+ position: fixed;
+ background: lime;
+ width: 100px;
+ height: 100px;
+}
+
+body {
+ margin: 0;
+}
+
+#anchor1 {
+ margin-left: 100px;
+}
+
+#target1 {
+ left: 100px;
+ top: 100px;
+}
+
+#anchor2 {
+ margin-left: 300px;
+ margin-top: 100px;
+}
+
+#target2 {
+ left: 300px;
+ top: 300px;
+}
+</style>
+
+<div id="anchor1" class="anchor">anchor1</div>
+<div id="anchor2" class="anchor">anchor2</div>
+
+<div id="target1" class="target">target1</div>
+<div id="target2" class="target">target2</div>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-001.html
new file mode 100644
index 0000000000..6d77cf9a9d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-001.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests that getComputedStyle() resolves anchor functions</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1">
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+.vrl { writing-mode: vertical-rl; }
+.htb { writing-mode: horizontal-tb; }
+.ltr { direction: ltr; }
+.rtl { direction: rtl; }
+
+.cb {
+ transform: scale(1);
+ width: 200px;
+ height: 150px;
+ outline: 1px dashed black;
+}
+
+.padding-10 {
+ box-sizing: border-box;
+ padding: 10px;
+}
+
+.anchor {
+ width: 40px;
+ height: 30px;
+ background: orange;
+ anchor-name: --a;
+ position: relative;
+ top: 50px;
+ left: 60px;
+}
+
+.anchored-topleft {
+ position: absolute;
+ width: anchor-size(--a width);
+ height: anchor-size(--a height);
+ bottom: anchor(--a top);
+ right: anchor(--a left);
+ background: lime;
+}
+
+.anchored-bottomright {
+ position: absolute;
+ width: anchor-size(--a width);
+ height: anchor-size(--a height);
+ top: anchor(--a bottom);
+ left: anchor(--a right);
+ background: lime;
+}
+</style>
+
+<div class=cb id=test1>
+ <div class=anchor></div>
+ <div class=anchored-topleft></div>
+ <div class=anchored-bottomright></div>
+</div>
+<script>
+test(() => {
+ const container = document.getElementById('test1');
+
+ const topleft = container.querySelector('.anchored-topleft');
+ assert_equals(getComputedStyle(topleft).bottom, '100px');
+ assert_equals(getComputedStyle(topleft).right, '140px');
+ assert_equals(getComputedStyle(topleft).width, '40px');
+ assert_equals(getComputedStyle(topleft).height, '30px');
+
+ const bottomright = container.querySelector('.anchored-bottomright');
+ assert_equals(getComputedStyle(bottomright).top, '80px');
+ assert_equals(getComputedStyle(bottomright).left, '100px');
+ assert_equals(getComputedStyle(bottomright).width, '40px');
+ assert_equals(getComputedStyle(bottomright).height, '30px');
+}, 'Basic case');
+</script>
+
+<div class="cb vrl" id=test2>
+ <div class=anchor></div>
+ <div class="anchored-topleft htb ltr"></div>
+ <div class="anchored-bottomright htb rtl"></div>
+</div>
+<script>
+test(() => {
+ const container = document.getElementById('test2');
+
+ const topleft = container.querySelector('.anchored-topleft');
+ assert_equals(getComputedStyle(topleft).bottom, '100px');
+ assert_equals(getComputedStyle(topleft).right, '-20px');
+ assert_equals(getComputedStyle(topleft).width, '40px');
+ assert_equals(getComputedStyle(topleft).height, '30px');
+
+ const bottomright = container.querySelector('.anchored-bottomright');
+ assert_equals(getComputedStyle(bottomright).top, '80px');
+ assert_equals(getComputedStyle(bottomright).left, '260px');
+ assert_equals(getComputedStyle(bottomright).width, '40px');
+ assert_equals(getComputedStyle(bottomright).height, '30px');
+}, 'Mixed writing modes and directions');
+</script>
+
+<div class="cb padding-10" id=test3>
+ <div class=anchor></div>
+ <div class=anchored-topleft></div>
+ <div class=anchored-bottomright></div>
+</div>
+<script>
+test(() => {
+ const container = document.getElementById('test3');
+
+ const topleft = container.querySelector('.anchored-topleft');
+ assert_equals(getComputedStyle(topleft).bottom, '90px');
+ assert_equals(getComputedStyle(topleft).right, '130px');
+ assert_equals(getComputedStyle(topleft).width, '40px');
+ assert_equals(getComputedStyle(topleft).height, '30px');
+
+ const bottomright = container.querySelector('.anchored-bottomright');
+ assert_equals(getComputedStyle(bottomright).top, '90px');
+ assert_equals(getComputedStyle(bottomright).left, '110px');
+ assert_equals(getComputedStyle(bottomright).width, '40px');
+ assert_equals(getComputedStyle(bottomright).height, '30px');
+}, 'With containing block padding');
+</script>
+
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-002.html
new file mode 100644
index 0000000000..ae697fcc74
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-002.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests getComputedStyle() resolving anchor() in fragmentation context</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1">
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="stylesheet" href="/fonts/ahem.css">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+.cb {
+ position: relative;
+ background: lightgray;
+}
+
+.anchor {
+ anchor-name: --a;
+ background: orange;
+}
+
+.target {
+ position: absolute;
+ left: anchor(--a left);
+ right: anchor(--a right);
+ top: anchor(--a top);
+ bottom: anchor(--a bottom);
+ background: lime;
+ opacity: 0.5;
+}
+</style>
+
+<!-- anchor is fragmented in second and third columns -->
+<div class="multicol" id="test1">
+ <div class="cb">
+ <div class="spacer"></div>
+ <div class="anchor"></div>
+ <div class="target"></div>
+ </div>
+</div>
+<style>
+#test1.multicol {
+ column-count: 3;
+ column-width: 100px;
+ column-gap: 10px;
+ width: 320px;
+ height: 100px;
+}
+
+#test1 .cb {
+ width: 100px;
+ height: 300px;
+}
+
+#test1 .spacer {
+ height: 175px;
+}
+
+#test1 .anchor {
+ margin-left: 25px;
+ width: 50px;
+ height: 50px;
+}
+</style>
+<script>
+test(() => {
+ const target = test1.querySelector('.target');
+ const style = getComputedStyle(target);
+ assert_equals(style.left, '25px');
+ assert_equals(style.right, '-85px');
+ assert_equals(style.top, '100px');
+ assert_equals(style.bottom, '100px');
+}, 'getComputedStyle() with fragmented containing block in multicolumn layout');
+</script>
+
+
+<div id="test2" style="font: 20px/1 Ahem; width: 11em">
+ Lorem
+ <span class="cb">
+ ipsum <span class="anchor">dolor</span> sit
+ <span class="target"></span>
+ </span>
+ amet.<br>
+
+ Lorem
+ <span class="cb">
+ ipsum dolor <span class="anchor">sit</span>
+ <span class="target"></span>
+ </span>
+ amet.<br>
+</div>
+<script>
+test(() => {
+ const targets = test2.querySelectorAll('.target');
+
+ const style1 = getComputedStyle(targets[0]);
+ assert_equals(style1.top, '20px');
+ assert_equals(style1.bottom, '0px');
+ assert_equals(style1.left, '-120px');
+ assert_equals(style1.right, '80px');
+
+ const style2 = getComputedStyle(targets[1]);
+ assert_equals(style2.top, '20px');
+ assert_equals(style2.bottom, '0px');
+ assert_equals(style2.left, '0px');
+ assert_equals(style2.right, '0px');
+}, 'getComputedStyle() with fragmented containing block in inline layout');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-003.html
new file mode 100644
index 0000000000..f9fca97654
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-003.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests that getComputedStyle() returns used position fallback style</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1">
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+.cb {
+ position: relative;
+ width: 400px;
+ height: 400px;
+ background: lightgray;
+}
+
+.anchor {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ background: orange;
+}
+
+.target {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ background: lime;
+ position-fallback: --pf;
+}
+
+@position-fallback --pf {
+ @try {
+ top: anchor(top);
+ left: anchor(right);
+ }
+
+ @try {
+ top: anchor(top);
+ right: anchor(left);
+ }
+}
+
+#anchor1 {
+ top: 0;
+ left: 0;
+ anchor-name: --a1;
+}
+
+#target1 {
+ anchor-default: --a1;
+}
+
+#anchor2 {
+ top: 200px;
+ right: 0;
+ anchor-name: --a2;
+}
+
+#target2 {
+ anchor-default: --a2;
+}
+</style>
+
+<div class="cb">
+ <div id="anchor1" class="anchor">anchor1</div>
+ <div id="anchor2" class="anchor">anchor2</div>
+
+ <div id="target1" class="target">target1</div>
+ <div id="target2" class="target">target2</div>
+</div>
+
+<script>
+test(() => {
+ const style1 = getComputedStyle(target1);
+ assert_equals(style1.top, '0px');
+ assert_equals(style1.left, '100px');
+ assert_equals(style1.right, '200px');
+}, 'getComputedStyle() should return and absolutize the first @try rule style for target1');
+
+test(() => {
+ const style2 = getComputedStyle(target2);
+ assert_equals(style2.top, '200px');
+ assert_equals(style2.left, '200px');
+ assert_equals(style2.right, '100px');
+}, 'getComputedStyle() should return and absolutize the second @try rule style for target2');
+</script>
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..4caf3ee210
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-001.html
@@ -0,0 +1,39 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.target')">
+ <!--
+ All targets should find the 30px anchor, because it's the last
+ one in the pre-order DFS from the `relpos`.
+ -->
+ <div class="relpos">
+ <div class="target" data-expected-width=30></div>
+ <div class="anchor1" style="width: 10px">
+ <div class="anchor1" style="width: 20px"></div>
+ <div class="target" data-expected-width=30></div>
+ </div>
+ <div class="anchor1" style="width: 30px"></div>
+ <div class="target" data-expected-width=30></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..63b5d66a4e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-002.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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.target')">
+ <div class="relpos">
+ <div>
+ <div class="relpos">
+ <!-- This target should not find the anchor, because the last containing
+ block has `position: absolute` and is after in tree order. -->
+ <div class="target" data-expected-width=0></div>
+ <div class="abspos">
+ <div class="relpos">
+ <!-- This target should not find the anchor, because the anchor is
+ absolutely positioned after it. -->
+ <div class="target" data-expected-width=0></div>
+ <div class="anchor1" style="position: absolute"></div>
+ <!-- This target should find the anchor, because the anchor is
+ absolutely positioned before it. -->
+ <div class="target" data-expected-width=10></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 find the anchor, because the last containing
+ block has `position: absolute` and is before in tree order. -->
+ <div class="target" data-expected-width=10></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..9ab6b66d94
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-003.html
@@ -0,0 +1,151 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.target')">
+ <!-- In-flow and out-of-flow boxes in a containing block. -->
+ <div class="relpos">
+ <div class="target" data-expected-width=30></div>
+ <div>
+ <div class="target" data-expected-width=30></div>
+ <div class="relpos">
+ <div class="target" data-expected-width=0></div>
+ <div class="abspos">
+ <div class="target" data-expected-width=30></div>
+ <div class="relpos">
+ <div class="target" data-expected-width=40></div>
+ <div class="anchor1" style="width: 20px"></div>
+ <div class="anchor1" style="position: absolute; width: 10px"></div>
+ <div class="anchor1" style="width: 40px"></div>
+ <div class="anchor1" style="position: absolute; width: 30px"></div>
+ <div class="target" data-expected-width=30></div>
+ </div>
+ </div>
+ <div class="target" data-expected-width=30></div>
+ </div>
+ <div class="target" data-expected-width=30></div>
+ </div>
+ <div class="target" data-expected-width=30></div>
+ </div>
+
+ <!-- In-flow boxes in ancestors, after the propagated ones. -->
+ <div class="relpos">
+ <div>
+ <div class="relpos">
+ <div class="target" data-expected-width=0></div>
+ <div class="abspos">
+ <div class="relpos">
+ <div class="target" data-expected-width=20></div>
+ <div class="anchor1" style="width: 20px"></div>
+ <div class="anchor1" style="position: absolute; width: 10px"></div>
+ <div class="target" data-expected-width=10></div>
+ </div>
+ <div class="anchor1" style="width: 50px"></div>
+ <div class="target" data-expected-width=50></div>
+ </div>
+ <div class="target" data-expected-width=50></div>
+ </div>
+ <div class="anchor1" style="width: 60px"></div>
+ <div class="target" data-expected-width=70></div>
+ </div>
+ <div class="anchor1" style="width: 70px"></div>
+ <div class="target" data-expected-width=70></div>
+ </div>
+
+ <!-- Out-of-flow boxes in ancestors, after the propagated ones. -->
+ <div class="relpos">
+ <div>
+ <div class="relpos">
+ <div class="target" data-expected-width=0></div>
+ <div class="abspos">
+ <div class="relpos">
+ <div class="target" data-expected-width=20></div>
+ <div class="anchor1" style="width: 20px"></div>
+ <div class="anchor1" style="position: absolute; width: 10px"></div>
+ <div class="target" data-expected-width=10></div>
+ </div>
+ <div class="anchor1" style="position: absolute; width: 110px"></div>
+ <div class="target" data-expected-width=110></div>
+ </div>
+ <div class="target" data-expected-width=110></div>
+ </div>
+ <div class="target" data-expected-width=110></div>
+ </div>
+ <div class="anchor1" style="position: absolute; width: 100px"></div>
+ <div class="target" data-expected-width=100></div>
+ </div>
+
+ <!-- In-flow boxes in ancestors, before the propagated ones. -->
+ <div class="relpos">
+ <div>
+ <div class="relpos">
+ <div class="abspos">
+ <div class="relpos">
+ <div class="target" data-expected-width=20></div>
+ <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: 120px"></div>
+ <div class="target" data-expected-width=120></div>
+ </div>
+ <div class="anchor1" style="width: 110px"></div>
+ <div class="target" data-expected-width=110></div>
+ </div>
+ <div class="target" data-expected-width=100></div>
+ </div>
+ <div class="anchor1" style="width: 100px"></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="target" data-expected-width=10></div>
+ <div class="anchor1" style="position: absolute; width: 100px"></div>
+ <div>
+ <div class="target" data-expected-width=10></div>
+ <div class="relpos">
+ <div class="target" data-expected-width=0></div>
+ <div class="anchor1" style="position: absolute; width: 110px"></div>
+ <div class="abspos">
+ <div class="target" data-expected-width=10></div>
+ <div class="anchor1" style="position: absolute; width: 120px"></div>
+ <div class="relpos">
+ <div class="target" data-expected-width=20></div>
+ <div class="anchor1" style="width: 20px"></div>
+ <div class="anchor1" style="position: absolute; width: 10px"></div>
+ <div class="target" data-expected-width=10></div>
+ </div>
+ <div class="target" data-expected-width=10></div>
+ </div>
+ <div class="target" data-expected-width=10></div>
+ </div>
+ <div class="target" data-expected-width=10></div>
+ </div>
+ <div class="target" data-expected-width=10></div>
+ </div>
+</body>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-004.html
new file mode 100644
index 0000000000..ebe96df740
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-004.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#determining">
+<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>
+<script src="support/test-common.js"></script>
+<style>
+.relpos {
+ position: relative;
+}
+.anchor1 {
+ anchor-name: --a1, --a2;
+ width: 30px;
+ height: 10px;
+ background: orange;
+}
+.target {
+ position: absolute;
+ height: 10px;
+ background: lime;
+}
+#target1 {
+ width: anchor-size(--a1 width);
+}
+#target2 {
+ width: anchor-size(--a2 width);
+}
+#target3 {
+ width: anchor-size(--a3 width, 11px);
+}
+</style>
+<body onload="checkLayoutForAnchorPos('.target')">
+ <!--
+ First two targets should find the same anchor via different names.
+ Third target shouldn't find the anchor, as the name is invalid.
+ -->
+ <div class="relpos">
+ <div class="anchor1" style="width: 30px"></div>
+ <div class="target" id="target1" data-expected-width=30></div>
+ <div class="target" id="target2" data-expected-width=30></div>
+ <div class="target" id="target3" data-expected-width=11></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..9523ee8772
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-basics.html
@@ -0,0 +1,41 @@
+<!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_valid_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');
+test_computed_value('anchor-name', '--foo, --bar');
+
+// 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-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-in-shadow-002.html
new file mode 100644
index 0000000000..321ceebcc4
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-in-shadow-002.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<title>Tests that anchor names are correctly tree-scoped even with style sheet sharing</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>
+body {
+ margin: 0;
+}
+
+.host {
+ width: 100px;
+ height: 100px;
+}
+
+#host2 {
+ margin-left: 200px;
+}
+</style>
+
+<div class="host" id="host1"></div>
+<div class="host" id="host2"></div>
+
+<script>
+document.querySelectorAll('.host').forEach(host => {
+ let shadow = host.attachShadow({mode: 'open'});
+ shadow.innerHTML = `
+ <style>
+ div { width: 100px; height: 100px; }
+ #anchor { anchor-name: --a; background: orange; }
+ #target {
+ position: fixed;
+ background: lime;
+ left: anchor(--a left);
+ top: anchor(--a bottom);
+ }
+ </style>
+ <div id=anchor>anchor</div>
+ <div id=target>target</div>
+ `;
+});
+
+test(() => {
+ const target1 = host1.shadowRoot.getElementById('target');
+ assert_equals(target1.offsetLeft, 0);
+ assert_equals(target1.offsetTop, 100);
+
+ const target2 = host2.shadowRoot.getElementById('target');
+ assert_equals(target2.offsetLeft, 200);
+ assert_equals(target2.offsetTop, 200);
+}, 'Anchor names in different tree scopes should not be confused');
+</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..dba3472f5b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-inline-001.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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.target')">
+ <div class="container relpos xcolumns">
+ <div style="height: 150px"></div>
+ <div class="relpos">
+ <span class="target" data-expected-width=20></span>
+ <span class="relpos">
+ <span class="target" data-expected-width=20></span>
+ <span class="relpos">
+ <span class="anchor">12</span>
+ <span class="anchor abspos">123</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..641ecf999b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-001.html
@@ -0,0 +1,55 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..73b7b56620
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-002.html
@@ -0,0 +1,62 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..02fd1c164d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-003.html
@@ -0,0 +1,67 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.target')">
+ <!-- All targets should find the abspos anchor -->
+ <div class="spacer"></div>
+ <div class="relpos">
+ <div class="columns relpos">
+ <div class="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=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>
+ <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..fdd1772359
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-multicol-004.html
@@ -0,0 +1,61 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.target')">
+ <!-- All targets should find the static positioned anchor -->
+ <div class="spacer"></div>
+ <div class="relpos">
+ <div class="columns 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=40 data-expected-height=50></div>
+ </div>
+ </div>
+</body>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained-dynamic.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained-dynamic.html
new file mode 100644
index 0000000000..50cec96f36
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained-dynamic.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: anchor-name is style contained - dynamic containment</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#name">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<script src="support/test-common.js"></script>
+<style>
+ .container {
+ position: relative;
+ }
+ .anchor {
+ width: 100px;
+ height: 100px;
+ anchor-name: var(--anchor-name);
+ }
+ .contain-style {
+ contain: style;
+ }
+ .target {
+ position: absolute;
+ anchor-default: var(--anchor-name);
+ top: anchor(bottom, 50px);
+ }
+ #a1 { --anchor-name: --a1; }
+ #a2 { --anchor-name: --a2; }
+ #a3 { --anchor-name: --a3; }
+</style>
+<div id="a1" class="container">
+ <div class="anchor"></div>
+ <div class="contain-style">
+ <div class="anchor"></div>
+ </div>
+ <div class="target" data-offset-y="100"></div>
+</div>
+<div id="a2" class="container">
+ <div class="anchor"></div>
+ <div class="anchor"></div>
+ <div class="contain-style">
+ <div class="target" data-offset-y="50"></div>
+ </div>
+</div>
+<div id="a3" class="container">
+ <div class="anchor contain-style">
+ <div class="target" data-offset-y="50"></div>
+ </div>
+</div>
+<script type="module">
+ await checkLayoutForAnchorPos('.target');
+
+ const t1 = document.querySelector("#a1 .target");
+ const t2 = document.querySelector("#a2 .target");
+ const t3 = document.querySelector("#a3 .target");
+ const t4 = document.querySelector("#a4 .target");
+ const t5 = document.querySelector("#a5 .target");
+ t1.setAttribute("data-offset-y", "200");
+ t2.setAttribute("data-offset-y", "200");
+ t3.setAttribute("data-offset-y", "100");
+ for (let element of document.querySelectorAll(".contain-style")) {
+ element.style.contain = "none";
+ }
+ await checkLayoutForAnchorPos('.target');
+
+ t1.setAttribute("data-offset-y", "100");
+ t2.setAttribute("data-offset-y", "50");
+ t3.setAttribute("data-offset-y", "50");
+ for (let element of document.querySelectorAll(".contain-style")) {
+ element.style.contain = "";
+ }
+ await checkLayoutForAnchorPos('.target');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained.html
new file mode 100644
index 0000000000..a529575889
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: anchor-name is style contained</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#name">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<script src="support/test-common.js"></script>
+<style>
+ .container {
+ position: relative;
+ }
+ .anchor {
+ width: 100px;
+ height: 100px;
+ anchor-name: var(--anchor-name);
+ }
+ .contain-style {
+ contain: style;
+ }
+ .target {
+ position: absolute;
+ anchor-default: var(--anchor-name);
+ top: anchor(bottom, 50px);
+ }
+ #a1 { --anchor-name: --a1; }
+ #a2 { --anchor-name: --a2; }
+ #a3 { --anchor-name: --a3; }
+ #a4 { --anchor-name: --a4; }
+ #a5 { --anchor-name: --a5; }
+</style>
+<body onload="checkLayoutForAnchorPos('.target')">
+<div id="a1" class="container">
+ <div class="anchor"></div>
+ <div class="contain-style">
+ <div class="anchor"></div>
+ </div>
+ <div class="target" data-offset-y="100"></div>
+</div>
+<div id="a2" class="container">
+ <div class="anchor"></div>
+ <div class="anchor"></div>
+ <div class="contain-style">
+ <div class="target" data-offset-y="50"></div>
+ </div>
+</div>
+<div id="a3" class="container">
+ <div class="contain-style">
+ <div class="anchor"></div>
+ <div class="target" data-offset-y="100"></div>
+ </div>
+</div>
+<div id="a4" class="container">
+ <div class="anchor contain-style">
+ <div class="target" data-offset-y="50"></div>
+ </div>
+</div>
+<div id="a5" class="container">
+ <div class="anchor contain-style"></div>
+ <div class="target" data-offset-y="100"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-non-oof-inherit.html b/testing/web-platform/tests/css/css-anchor-position/anchor-non-oof-inherit.html
new file mode 100644
index 0000000000..75f98a1ebf
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-non-oof-inherit.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<title>Tests that anchor functions can be inherited from in-flow elements</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1382151">
+<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>
+<script src="support/test-common.js"></script>
+
+<style>
+.cb {
+ width: 200px;
+ height: 200px;
+ transform: scale(1);
+}
+
+.anchor {
+ width: 100px;
+ height: 100px;
+ top: 50px;
+ left: 50px;
+ position: relative;
+ background: red;
+ anchor-name: --a;
+}
+
+.target {
+ position: absolute;
+ background: green;
+ top: inherit;
+ bottom: inherit;
+ left: inherit;
+ right: inherit;
+ width: inherit;
+ min-width: inherit;
+ max-width: inherit;
+ height: inherit;
+ min-height: inherit;
+ max-height: inherit;
+}
+
+.inset-parent {
+ top: anchor(--a top);
+ bottom: anchor(--a bottom);
+ left: anchor(--a left);
+ right: anchor(--a right);
+}
+
+.size-parent {
+ width: anchor-size(--a width);
+ height: anchor-size(--a height);
+ top: 50px;
+ left: 50px;
+}
+
+.min-size-parent {
+ min-width: anchor-size(--a width);
+ min-height: anchor-size(--a height);
+ top: 50px;
+ left: 50px;
+ bottom: 200px;
+ right: 200px;
+}
+
+.max-size-parent {
+ max-width: anchor-size(--a width);
+ max-height: anchor-size(--a height);
+ top: 50px;
+ left: 50px;
+ bottom: 0px;
+ right: 0px;
+}
+
+</style>
+
+<body onload="checkLayoutForAnchorPos('.target')">
+
+<p>In each test case, we should see a filled green square with no red.</p>
+
+<div class=cb>
+ <div class="anchor"></div>
+ <div class="inset-parent">
+ <div class="target"
+ data-offset-x=50 data-offset-y=50
+ data-expected-width=100 data-expected-height=100></div>
+ </div>
+</div>
+
+<div class=cb>
+ <div class="anchor"></div>
+ <div class="size-parent">
+ <div class="target"
+ data-offset-x=50 data-offset-y=50
+ data-expected-width=100 data-expected-height=100></div>
+ </div>
+</div>
+
+<div class=cb>
+ <div class="anchor"></div>
+ <div class="min-size-parent">
+ <div class="target"
+ data-offset-x=50 data-offset-y=50
+ data-expected-width=100 data-expected-height=100></div>
+ </div>
+</div>
+
+<div class=cb>
+ <div class="anchor"></div>
+ <div class="max-size-parent">
+ <div class="target"
+ data-offset-x=50 data-offset-y=50
+ data-expected-width=100 data-expected-height=100></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..4690775388
--- /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(50% + 1px)',
+ '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..6fc188ab69
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-001.html
@@ -0,0 +1,55 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('#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..de8fc4792a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-002.html
@@ -0,0 +1,73 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..c149dd0e11
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-003.html
@@ -0,0 +1,59 @@
+<!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>
+<script src="support/test-common.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 last acceptable anchor
+ element el in tree order.
+ https://drafts.csswg.org/css-anchor-1/#determining
+-->
+<body onload="checkLayoutForAnchorPos('.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=9></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=9></div>
+ </div>
+ <div class="target" style="left: anchor(--a1 right)" data-offset-x=9></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=9></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..387c9ab692
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-004.html
@@ -0,0 +1,117 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..1e2ecbc909
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-borders-001.html
@@ -0,0 +1,136 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..96d5f996ef
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-001.html
@@ -0,0 +1,68 @@
+<!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>
+<script src="support/test-common.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>
+ <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>
+<script type="module">
+document.body.offsetTop; // Force layout.
+container.classList.add('after');
+await checkLayoutForAnchorPos('#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..e97ae5e12b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-002.html
@@ -0,0 +1,55 @@
+<!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>
+<script src="support/test-common.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 type="module">
+await checkLayoutForAnchorPos('.target', false);
+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';
+}
+await checkLayoutForAnchorPos('.after .target', true);
+</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..0400be1bde
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-003.html
@@ -0,0 +1,106 @@
+<!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>
+<script src="support/test-common.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 type="module">
+for (const element of document.getElementsByClassName('target')) {
+ element.dataset.offsetX = '50';
+ element.dataset.offsetY = '70';
+ element.dataset.expectedWidth = '50';
+ element.dataset.expectedHeight = '70';
+}
+await checkLayoutForAnchorPos('.target', false);
+
+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';
+}
+await checkLayoutForAnchorPos('.after .target', true);
+</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..146703e628
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-004.html
@@ -0,0 +1,46 @@
+<!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>
+<script src="support/test-common.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>
+ <div class="cb">
+ <div style="contain: layout size paint; 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 type="module">
+document.body.classList.add('after');
+await checkLayoutForAnchorPos('.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..92fb4d275b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-grid-001.html
@@ -0,0 +1,83 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..12c1766a6c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-001.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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..d723e0c004
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-002.html
@@ -0,0 +1,38 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..2915d6328c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-003.html
@@ -0,0 +1,38 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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&#x202E;2&#x202D;</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..e47147ec67
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-inline-004.html
@@ -0,0 +1,176 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..ddbbc8d2f4
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..7b2691a2b9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-002.html
@@ -0,0 +1,110 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..d23779ad9b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-003.html
@@ -0,0 +1,67 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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=50></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=50></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..399494120e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-004.html
@@ -0,0 +1,84 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.target')">
+ <p>The green box should be a union of two blue boxes in the right,
+ 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=34 data-offset-y=225
+ data-expected-width=130 data-expected-height=100></div>
+ <div class="target target1-rb"
+ data-offset-x=154 data-offset-y=315></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..2239331ae3
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-005.html
@@ -0,0 +1,65 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..93d4ac6598
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-006.html
@@ -0,0 +1,72 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..f90ee5c3a2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-colspan-001.html
@@ -0,0 +1,101 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..0c341f987a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-colspan-002.html
@@ -0,0 +1,77 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..b94680816e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-fixed-001.html
@@ -0,0 +1,81 @@
+<!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>
+<script src="support/test-common.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: 0;
+ left: 20px;
+ top: 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="checkLayoutForAnchorPos('.target')">
+ <!-- All targets should find the fixed positioned anchor -->
+ <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"></div>
+ <div class="anchor fixedpos"></div>
+ <div class="target"
+ data-offset-x="10" data-offset-y="20"
+ data-expected-width=130 data-expected-height=50></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..35ab2cfc15
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html
@@ -0,0 +1,80 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..055459551b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-001.html
@@ -0,0 +1,51 @@
+<!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-default: --a;
+ outline: none;
+}
+
+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..a87a9d7eed
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-002.html
@@ -0,0 +1,51 @@
+<!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-default: --a;
+ outline: none;
+}
+
+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..96d5219c5c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-003.html
@@ -0,0 +1,52 @@
+<!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-default: --a;
+ outline: none;
+}
+
+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..c986e3f98d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-004.html
@@ -0,0 +1,52 @@
+<!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-default: --a;
+ outline: none;
+}
+
+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..cf39c77736
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-005.html
@@ -0,0 +1,52 @@
+<!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-default: --a;
+}
+
+body {
+ margin: 0;
+ height: 300vh;
+}
+
+dialog {
+ margin: 0;
+ border: 0;
+ padding: 0;
+ inset: auto;
+ outline: none;
+}
+
+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..c13284b854
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-006.html
@@ -0,0 +1,52 @@
+<!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-default: --a;
+}
+
+body {
+ margin: 0;
+ height: 300vh;
+}
+
+dialog {
+ margin: 0;
+ border: 0;
+ padding: 0;
+ outline: none;
+}
+
+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..6058d23885
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-query-fallback.html
@@ -0,0 +1,70 @@
+<!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>
+<script src="support/test-common.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 onload="checkLayoutForAnchorPos('.target')">
+ <div id="container">
+ <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>
+ </div>
+</body>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-001.html
new file mode 100644
index 0000000000..8609795c8a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-001.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<title>Basic of anchor positioned scrolling: 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-default: --anchor;
+}
+
+#outer-anchored {
+ color: yellow;
+ position: absolute;
+ left: anchor(--anchor left);
+ top: anchor(--anchor bottom);
+ anchor-default: --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-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-002.html
new file mode 100644
index 0000000000..8ef6f500a1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-002.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<title>Tests anchor positioned scrolling resolving name conflicts</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#scroll">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+.scroller {
+ width: 100px;
+ height: 100px;
+ overflow-y: scroll;
+}
+
+.nonpos-cb {
+ transform: scale(1);
+}
+
+.abspos-cb {
+ position: absolute;
+}
+
+.anchor {
+ background: orange;
+ anchor-name: --a1;
+ position: absolute;
+ width: 50px;
+ height: 50px;
+ top: 50px;
+}
+
+.spacer {
+ height: 200px;
+}
+
+.target {
+ background: lime;
+ position: absolute;
+ width: 50px;
+ height: 50px;
+ top: anchor(--a1 top);
+ left: anchor(--a1 right);
+ anchor-default: --a1;
+}
+</style>
+
+<div class="abspos-cb" style="width: 300px; height: 400px">
+ <div class="scroller nonpos-cb" id="scroller1">
+ <div class="anchor" id="anchor1"></div>
+ <div class="spacer"></div>
+ </div>
+ <div class="target" id="target1"></div>
+
+ <div class="scroller abspos-cb" style="top: 125px" id="scroller2">
+ <div class="anchor" id="anchor2"></div>
+ <div class="spacer"></div>
+ </div>
+ <div class="target" id="target2"></div>
+
+ <div class="scroller abspos-cb" style="top: 250px" id="scroller3">
+ <div class="anchor" id="anchor3"></div>
+ <div class="spacer"></div>
+ </div>
+ <div class="target" id="target3"></div>
+</div>
+
+<script>
+promise_test(async () => {
+ scroller1.scrollTop = 10;
+ await waitUntilNextAnimationFrame();
+ await waitUntilNextAnimationFrame();
+
+ assert_equals(target1.getBoundingClientRect().top, anchor1.getBoundingClientRect().top);
+ assert_equals(target1.getBoundingClientRect().top, 40);
+}, 'target1 should scroll with anchor1');
+
+promise_test(async () => {
+ scroller2.scrollTop = 20;
+ await waitUntilNextAnimationFrame();
+ await waitUntilNextAnimationFrame();
+
+ assert_equals(target2.getBoundingClientRect().top, anchor2.getBoundingClientRect().top);
+ assert_equals(target2.getBoundingClientRect().top, 155);
+}, 'target2 should scroll with anchor2');
+
+promise_test(async () => {
+ scroller3.scrollTop = 30;
+ await waitUntilNextAnimationFrame();
+ await waitUntilNextAnimationFrame();
+
+ assert_equals(target3.getBoundingClientRect().top, anchor3.getBoundingClientRect().top);
+ assert_equals(target3.getBoundingClientRect().top, 270);
+}, 'target3 should scroll with anchor3');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-003.html
new file mode 100644
index 0000000000..c1b31c0bec
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-003.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<title>Tests anchor positioned scrolling with fragmented containing block</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#scroll">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+#multicol {
+ column-count: 3;
+ column-width: 90px;
+ column-gap: 10px;
+ width: 300px;
+ height: 100px;
+}
+
+#cb {
+ height: 300px;
+ background: yellow;
+ position: relative;
+}
+
+#spacer {
+ height: 110px;
+}
+
+#scroller {
+ overflow-y: scroll;
+ height: 80px;
+ background: blue;
+}
+
+#anchor {
+ anchor-name: --a;
+ width: 50px;
+ height: 50px;
+ background: orange;
+ position: relative;
+ top: 80px;
+}
+
+#target {
+ position: absolute;
+ left: anchor(--a left);
+ bottom: anchor(--a top);
+ anchor-default: --a;
+ width: 50px;
+ height: 50px;
+ background: lime;
+}
+</style>
+
+<div id=multicol>
+ <div id=cb>
+ <div id=spacer></div>
+ <div id=scroller>
+ <div id=anchor></div>
+ </div>
+ <div id=target></div>
+ </div>
+</div>
+
+<script>
+promise_test(async () => {
+ scroller.scrollTop = 10;
+ await waitUntilNextAnimationFrame();
+ await waitUntilNextAnimationFrame();
+
+ let targetRect = target.getBoundingClientRect();
+ assert_equals(targetRect.top, 30);
+}, 'Scrolling should work in fragmented containing block');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-004.html
new file mode 100644
index 0000000000..d08279118d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-004.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests anchor positioned scrolling with relatively positioned inline containers</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/#scroll">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="stylesheet" href="/fonts/ahem.css">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+.cb {
+ position: relative;
+ font: 20px/1 Ahem, monospace;
+}
+
+.scroller {
+ display: inline-block;
+ overflow-x: scroll;
+ width: 160px;
+ white-space: nowrap;
+}
+
+.anchor {
+ anchor-name: --a;
+ color: orange;
+}
+
+.target {
+ position: absolute;
+ anchor-default: --a;
+ top: anchor(--a bottom);
+ left: anchor(--a left);
+ color: lime;
+}
+</style>
+
+<div>
+ <span class="cb">
+ <span class="scroller" id="scroller1">
+ before
+ <span class="anchor" id="anchor1">anchor</span>
+ after
+ </span>
+ <span class="target" id="target1">target</span>
+ </span>
+
+ <br>
+ <br>
+
+ <span class="cb">
+ <span class="scroller" id="scroller2">
+ before
+ <span class="anchor" id="anchor2">anchor</span>
+ after
+ </span>
+ <span class="target" id="target2">target</span>
+ </span>
+</div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ await waitUntilNextAnimationFrame();
+
+ assert_equals(target1.getBoundingClientRect().left, 140);
+ assert_equals(target2.getBoundingClientRect().left, 140);
+}, 'Initial position of the targets');
+
+promise_test(async () => {
+ scroller1.scrollLeft = 20;
+ await waitUntilNextAnimationFrame();
+ await waitUntilNextAnimationFrame();
+
+ assert_equals(target1.getBoundingClientRect().left, 120);
+}, '#target1 should scroll with #anchor1');
+
+promise_test(async () => {
+ scroller2.scrollLeft = 40;
+ await waitUntilNextAnimationFrame();
+ await waitUntilNextAnimationFrame();
+
+ assert_equals(target2.getBoundingClientRect().left, 100);
+}, '#target2 should scroll with #anchor2');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-005.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-005.html
new file mode 100644
index 0000000000..a9a7d24d2b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-005.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<title>Tests scrolling with anchor in fixed-positioned scroller</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#scroll">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+ margin: 0;
+ height: 200vh;
+}
+
+#scroller {
+ position: fixed;
+ width: 200px;
+ height: 200px;
+ overflow-y: scroll;
+}
+
+#anchor {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ top: 200px;
+ background: orange;
+ anchor-name: --a;
+}
+
+#target {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ bottom: anchor(--a top);
+ anchor-default: --a;
+ background: lime;
+}
+</style>
+
+<div id=scroller>
+ <div id=anchor></div>
+</div>
+<div id=target></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_equals(target.getBoundingClientRect().top, 100);
+
+ document.documentElement.scrollTop = 100;
+ await waitUntilNextAnimationFrame();
+ assert_equals(target.getBoundingClientRect().top, 100);
+}, 'Target should not scroll with viewport when anchor is in fixed-positioned scroller');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-006.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-006.html
new file mode 100644
index 0000000000..2ffd026b55
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-006.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<title>Tests that scroll adjustment is applied per-axis</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#needs-scroll-adjustment">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+.scroller {
+ width: 150px;
+ height: 150px;
+ margin-bottom: 50px;
+ overflow: scroll;
+ position: relative;
+}
+
+.spacer {
+ width: 400px;
+ height: 400px;
+}
+
+.anchor {
+ position: absolute;
+ width: 50px;
+ height: 50px;
+ top: 50px;
+ left: 50px;
+ background: orange;
+}
+
+.target {
+ position: fixed;
+ width: 50px;
+ height: 50px;
+ background: lime;
+}
+
+#scroller1 { anchor-name: --scroller1; }
+#scroller2 { anchor-name: --scroller2; }
+#scroller3 { anchor-name: --scroller3; }
+
+#anchor1 { anchor-name: --a1; }
+#anchor2 { anchor-name: --a2; }
+#anchor3 { anchor-name: --a3; }
+
+/* Needs scroll adjustment in x axis only */
+#target1 {
+ anchor-default: --a1;
+ left: anchor(left);
+ top: anchor(--scroller1 bottom);
+}
+
+/* Needs scroll adjustment in y axis only */
+#target2 {
+ anchor-default: --a2;
+ top: anchor(top);
+ left: anchor(--scroller2 right);
+}
+
+/* No scroll adjustment needed */
+#target3 {
+ anchor-default: --a3;
+ top: anchor(--scroller3 bottom);
+ left: anchor(--scroller3 right);
+}
+</style>
+
+<div class="scroller" id="scroller1">
+ <div class="spacer"></div>
+ <div class="anchor" id="anchor1"></div>
+</div>
+<div class="target" id="target1"></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_equals(target1.getBoundingClientRect().left, 50);
+ assert_equals(target1.getBoundingClientRect().top, 150);
+
+ scroller1.scrollLeft = 50;
+ await waitUntilNextAnimationFrame();
+ assert_equals(target1.getBoundingClientRect().left, 0);
+
+ scroller1.scrollTop = 50;
+ await waitUntilNextAnimationFrame();
+ assert_equals(target1.getBoundingClientRect().top, 150);
+}, '#target1 is scroll-adjusted in x axis only');
+</script>
+
+<div class="scroller" id="scroller2">
+ <div class="spacer"></div>
+ <div class="anchor" id="anchor2"></div>
+</div>
+<div class="target" id="target2"></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_equals(target2.getBoundingClientRect().left, 150);
+ assert_equals(target2.getBoundingClientRect().top, 250);
+
+ scroller2.scrollLeft = 50;
+ await waitUntilNextAnimationFrame();
+ assert_equals(target2.getBoundingClientRect().left, 150);
+
+ scroller2.scrollTop = 50;
+ await waitUntilNextAnimationFrame();
+ assert_equals(target2.getBoundingClientRect().top, 200);
+}, '#target2 is scroll-adjusted in y axis only');
+</script>
+
+<div class="scroller" id="scroller3">
+ <div class="spacer"></div>
+ <div class="anchor" id="anchor3"></div>
+</div>
+<div class="target" id="target3"></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_equals(target3.getBoundingClientRect().left, 150);
+ assert_equals(target3.getBoundingClientRect().top, 550);
+
+ scroller3.scrollLeft = 50;
+ await waitUntilNextAnimationFrame();
+ assert_equals(target3.getBoundingClientRect().left, 150);
+
+ scroller3.scrollTop = 50;
+ await waitUntilNextAnimationFrame();
+ assert_equals(target3.getBoundingClientRect().top, 550);
+}, '#target3 is scroll-adjusted in neither axis');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-007.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-007.html
new file mode 100644
index 0000000000..ec51910619
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-007.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<title>Tests that scroll adjustment still applies when using another anchor in default anchor's scroll container</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#needs-scroll-adjustment">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+#scroller {
+ width: 400px;
+ height: 400px;
+ overflow: scroll;
+ position: relative;
+}
+
+#spacer {
+ width: 1000px;
+ height: 1000px;
+}
+
+.anchor {
+ width: 50px;
+ height: 50px;
+ position: absolute;
+ background: orange;
+}
+
+#anchor1 {
+ anchor-name: --a1;
+ left: 300px;
+ top: 100px;
+}
+
+#anchor2 {
+ anchor-name: --a2;
+ left: 200px;
+ top: 200px;
+}
+
+#anchor3 {
+ anchor-name: --a3;
+ left: 100px;
+ top: 300px;
+}
+
+/* Uses different anchors in insets instead of the default anchor.
+ * Still scroll-adjusted because they are in the same scroll container. */
+#target {
+ position: fixed;
+ width: 50px;
+ height: 50px;
+ left: anchor(--a3 left);
+ top: anchor(--a1 top);
+ anchor-default: --a2;
+ background: lime;
+}
+</style>
+
+<div id="scroller">
+ <div id="spacer"></div>
+ <div class="anchor" id="anchor1"></div>
+ <div class="anchor" id="anchor2"></div>
+ <div class="anchor" id="anchor3"></div>
+</div>
+<div id="target"></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_equals(target.getBoundingClientRect().left, 100);
+ assert_equals(target.getBoundingClientRect().top, 100);
+
+ scroller.scrollLeft = 50;
+ await waitUntilNextAnimationFrame();
+ assert_equals(target.getBoundingClientRect().left, 50);
+
+ scroller.scrollTop = 50;
+ await waitUntilNextAnimationFrame();
+ assert_equals(target.getBoundingClientRect().top, 50);
+}, '#target3 is scroll-adjusted in both axises');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-001-crash.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-001-crash.html
new file mode 100644
index 0000000000..005a27393a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-001-crash.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>Tests that scrolling 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-default: --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-composited-scrolling-002-crash.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-002-crash.html
new file mode 100644
index 0000000000..83ce146825
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-002-crash.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html class="test-wait">
+<title>Tests that scrolling doesn't crash renderer when scroller's content no longer overflows</title>
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/">
+
+<style>
+#scroller {
+ margin-top: 200px;
+ height: 200px;
+ overflow-y: scroll;
+}
+
+#anchor {
+ anchor-name: --a;
+}
+
+#spacer {
+ height: 400px;
+}
+
+#target {
+ position: fixed;
+ top: anchor(--a bottom);
+ left: anchor(--a left);
+ anchor-default: --a;
+}
+</style>
+
+<div id="scroller">
+ <div id="spacer"></div>
+ <div id="anchor">anchor</div>
+</div>
+<div id="target">target</div>
+
+<script type="module">
+const raf = () => new Promise(resolve => requestAnimationFrame(resolve));
+
+await raf();
+await raf();
+
+spacer.style.height = '0';
+
+// Force paint property update on target in the same frame
+target.style.transform = 'scale(1)';
+
+document.documentElement.classList.remove('test-wait');
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-003-crash.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-003-crash.html
new file mode 100644
index 0000000000..594c844bfb
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-003-crash.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html class="test-wait">
+<title>Tests that scrolling doesn't crash renderer when scroller becomes non-scroller</title>
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/">
+
+<style>
+#scroller {
+ margin-top: 200px;
+ height: 200px;
+ overflow-y: scroll;
+}
+
+#anchor {
+ anchor-name: --a;
+}
+
+#spacer {
+ height: 400px;
+}
+
+#target {
+ position: fixed;
+ top: anchor(--a bottom);
+ left: anchor(--a left);
+ anchor-default: --a;
+}
+</style>
+
+<div id="scroller">
+ <div id="spacer"></div>
+ <div id="anchor">anchor</div>
+</div>
+<div id="target">target</div>
+
+<script type="module">
+const raf = () => new Promise(resolve => requestAnimationFrame(resolve));
+
+await raf();
+await raf();
+
+scroller.style.overflowY = 'visible';
+
+// Force paint property update on target in the same frame
+target.style.transform = 'scale(1)';
+
+document.documentElement.classList.remove('test-wait');
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-004-crash.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-004-crash.html
new file mode 100644
index 0000000000..226a1b099c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-004-crash.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html class="test-wait">
+<title>Tests that scrolling doesn't crash renderer when scroller's content no longer overflows, and target precedes scroller in DOM</title>
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/">
+
+<style>
+#scroller {
+ margin-top: 200px;
+ height: 200px;
+ overflow-y: scroll;
+}
+
+#anchor {
+ anchor-name: --a;
+}
+
+#spacer {
+ height: 400px;
+}
+
+#target {
+ position: fixed;
+ top: anchor(--a bottom);
+ left: anchor(--a left);
+ anchor-default: --a;
+}
+</style>
+
+<div id="target">target</div>
+<div id="scroller">
+ <div id="spacer"></div>
+ <div id="anchor">anchor</div>
+</div>
+
+<script type="module">
+const raf = () => new Promise(resolve => requestAnimationFrame(resolve));
+
+await raf();
+await raf();
+
+spacer.style.height = '0';
+
+document.documentElement.classList.remove('test-wait');
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-005-crash.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-005-crash.html
new file mode 100644
index 0000000000..639e2e064a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-005-crash.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html class="test-wait">
+<title>Tests that scrolling doesn't crash renderer with `overflow: hidden` scroller</title>
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/">
+
+<style>
+#scroller {
+ margin-top: 200px;
+ height: 200px;
+ overflow-y: hidden;
+}
+
+#anchor {
+ anchor-name: --a;
+}
+
+#spacer {
+ height: 400px;
+}
+
+#target {
+ position: fixed;
+ top: anchor(--a bottom);
+ left: anchor(--a left);
+ anchor-default: --a;
+}
+</style>
+
+<div id="scroller">
+ <div id="spacer"></div>
+ <div id="anchor">anchor</div>
+</div>
+<div id="target">target</div>
+
+<script type="module">
+const raf = () => new Promise(resolve => requestAnimationFrame(resolve));
+
+scroller.scrollTop = 100;
+
+await raf();
+await raf();
+
+scroller.scrollTop = 0;
+
+// Force paint property update on target in the same frame
+target.style.transform = 'scale(1)';
+
+document.documentElement.classList.remove('test-wait');
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-006.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-006.html
new file mode 100644
index 0000000000..6e57accc45
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-006.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Tests anchor-positioned element paint order in composited scrolling</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/#scroll">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="reference/anchor-scroll-composited-scrolling-006-ref.html">
+<style>
+body {
+ margin: 0;
+}
+#scroller {
+ width: 200px;
+ height: 100px;
+ overflow: scroll;
+ will-change: scroll-position;
+}
+#spacer {
+ height: 400px;
+}
+#anchor {
+ width: 100px;
+ height: 100px;
+ anchor-name: --a;
+}
+#target {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ background: red;
+ left: 0;
+ bottom: anchor(--a top);
+ anchor-default: --a;
+}
+#overlap {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ top: 150px;
+ left: 0;
+ z-index: 100;
+ background: green;
+}
+</style>
+
+<div id="overlap"></div>
+<div id="scroller">
+ <div id="spacer"></div>
+ <div id="anchor"></div>
+</div>
+<div id="target"></div>
+
+<script type="module">
+function raf() { return new Promise(resolve => requestAnimationFrame(resolve)); }
+
+await raf();
+await raf();
+scroller.scrollTop = 150;
+
+document.documentElement.classList.remove('reftest-wait');
+</script>
+</html>
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..04518e5019
--- /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-default: --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..f30c35d390
--- /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-default: --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..60bc9b919a
--- /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-default: --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..99e4f9c30d
--- /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-default: --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..0adfe0834d
--- /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-default: --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-006.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-006.html
new file mode 100644
index 0000000000..b4a1a24de6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-006.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<title>Tests position fallback with initially out-of-viewport anchor</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;
+ width: 200vw;
+ height: 200vh;
+}
+
+#anchor {
+ anchor-name: --a;
+ width: 100px;
+ height: 100px;
+ margin-block-start: 100vb;
+ margin-inline-start: 100vi;
+ background: orange;
+}
+
+#anchored {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background: green;
+ anchor-default: --a;
+ position-fallback: --pf;
+}
+
+@position-fallback --pf {
+ @try {
+ inset-block-start: anchor(--a end);
+ inset-inline-start: anchor(--a end);
+ }
+ @try {
+ inset-block-end: anchor(--a start);
+ inset-inline-start: anchor(--a end);
+ }
+ @try {
+ inset-block-start: anchor(--a end);
+ inset-inline-end: anchor(--a start);
+ }
+ @try {
+ inset-block-end: anchor(--a start);
+ inset-inline-end: anchor(--a start);
+ }
+}
+</style>
+
+<div id="anchor"></div>
+<div id="anchored"></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'top');
+ assert_fallback_position(anchored, anchor, 'left');
+}, 'Should use the last fallback position initially');
+
+promise_test(async () => {
+ // Scroll down to have enough space below the anchor, but not enough right.
+ document.documentElement.scrollTop = 250;
+ document.documentElement.scrollLeft = 150;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'bottom');
+ assert_fallback_position(anchored, anchor, 'left');
+}, 'Should use the third fallback position with enough space below');
+
+promise_test(async () => {
+ // Scroll right to have enough space right to the anchor, but not enough below.
+ document.documentElement.scrollTop = 150;
+ document.documentElement.scrollLeft = 250;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'top');
+ assert_fallback_position(anchored, anchor, 'right');
+}, 'Should use the second fallback position with enough space right');
+
+promise_test(async () => {
+ // Scroll down and right to have enough space on both axes.
+ document.documentElement.scrollTop = 250;
+ document.documentElement.scrollLeft = 250;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'bottom');
+ assert_fallback_position(anchored, anchor, 'right');
+}, 'Should use the first fallback position with enough space below and right');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-007.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-007.html
new file mode 100644
index 0000000000..baa283ba94
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-007.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<title>Tests position fallback with initially out-of-viewport anchor in vertial-rl</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;
+ width: 200vw;
+ height: 200vh;
+}
+
+html {
+ writing-mode: vertical-rl;
+}
+
+#anchor {
+ anchor-name: --a;
+ width: 100px;
+ height: 100px;
+ margin-block-start: 100vb;
+ margin-inline-start: 100vi;
+ background: orange;
+}
+
+#anchored {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background: green;
+ anchor-default: --a;
+ position-fallback: --pf;
+}
+
+@position-fallback --pf {
+ @try {
+ inset-block-start: anchor(--a end);
+ inset-inline-start: anchor(--a end);
+ }
+ @try {
+ inset-block-end: anchor(--a start);
+ inset-inline-start: anchor(--a end);
+ }
+ @try {
+ inset-block-start: anchor(--a end);
+ inset-inline-end: anchor(--a start);
+ }
+ @try {
+ inset-block-end: anchor(--a start);
+ inset-inline-end: anchor(--a start);
+ }
+}
+</style>
+
+<div id="anchor"></div>
+<div id="anchored"></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'top');
+ assert_fallback_position(anchored, anchor, 'right');
+}, 'Should use the last fallback position initially');
+
+promise_test(async () => {
+ // Scroll left to have enough space left to the anchor, but not enough below.
+ document.documentElement.scrollLeft = -250;
+ document.documentElement.scrollTop = 150;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'left');
+ assert_fallback_position(anchored, anchor, 'top');
+}, 'Should use the third fallback position with enough space left');
+
+promise_test(async () => {
+ // Scroll down to have enough space below the anchor, but not enough left.
+ document.documentElement.scrollLeft = -150;
+ document.documentElement.scrollTop = 250;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'right');
+ assert_fallback_position(anchored, anchor, 'bottom');
+}, 'Should use the second fallback position with enough space below');
+
+promise_test(async () => {
+ // Scroll down and left to have enough space on both axes.
+ document.documentElement.scrollLeft = -250;
+ document.documentElement.scrollTop = 250;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'left');
+ assert_fallback_position(anchored, anchor, 'bottom');
+}, 'Should use the first fallback position with enough space left and below');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-008.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-008.html
new file mode 100644
index 0000000000..ae625d5823
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-008.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<title>Tests position fallback with initially out-of-viewport anchor in vertial-rl rtl</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;
+ width: 200vw;
+ height: 200vh;
+}
+
+html {
+ writing-mode: vertical-rl;
+ direction: rtl;
+}
+
+#anchor {
+ anchor-name: --a;
+ width: 100px;
+ height: 100px;
+ margin-block-start: 100vb;
+ margin-inline-start: 100vi;
+ background: orange;
+}
+
+#anchored {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background: green;
+ anchor-default: --a;
+ position-fallback: --pf;
+}
+
+@position-fallback --pf {
+ @try {
+ inset-block-start: anchor(--a end);
+ inset-inline-start: anchor(--a end);
+ }
+ @try {
+ inset-block-end: anchor(--a start);
+ inset-inline-start: anchor(--a end);
+ }
+ @try {
+ inset-block-start: anchor(--a end);
+ inset-inline-end: anchor(--a start);
+ }
+ @try {
+ inset-block-end: anchor(--a start);
+ inset-inline-end: anchor(--a start);
+ }
+}
+</style>
+
+<div id="anchor"></div>
+<div id="anchored"></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'bottom');
+ assert_fallback_position(anchored, anchor, 'right');
+}, 'Should use the last fallback position initially');
+
+promise_test(async () => {
+ // Scroll left to have enough space left to the anchor, but not enough above.
+ document.documentElement.scrollLeft = -250;
+ document.documentElement.scrollTop = -150;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'left');
+ assert_fallback_position(anchored, anchor, 'bottom');
+}, 'Should use the third fallback position with enough space left');
+
+promise_test(async () => {
+ // Scroll up to have enough space above the anchor, but not enough left.
+ document.documentElement.scrollLeft = -150;
+ document.documentElement.scrollTop = -250;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'right');
+ assert_fallback_position(anchored, anchor, 'top');
+}, 'Should use the second fallback position with enough space above');
+
+promise_test(async () => {
+ // Scroll up and left to have enough space on both axes.
+ document.documentElement.scrollLeft = -250;
+ document.documentElement.scrollTop = -250;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'left');
+ assert_fallback_position(anchored, anchor, 'top');
+}, 'Should use the first fallback position with enough space left and above');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-009.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-009.html
new file mode 100644
index 0000000000..b355d476e6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-009.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<title>Tests position fallback with initially out-of-viewport anchor in vertial-lr</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;
+ width: 200vw;
+ height: 200vh;
+}
+
+html {
+ writing-mode: vertical-lr;
+}
+
+#anchor {
+ anchor-name: --a;
+ width: 100px;
+ height: 100px;
+ margin-block-start: 100vb;
+ margin-inline-start: 100vi;
+ background: orange;
+}
+
+#anchored {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background: green;
+ anchor-default: --a;
+ position-fallback: --pf;
+}
+
+@position-fallback --pf {
+ @try {
+ inset-block-start: anchor(--a end);
+ inset-inline-start: anchor(--a end);
+ }
+ @try {
+ inset-block-end: anchor(--a start);
+ inset-inline-start: anchor(--a end);
+ }
+ @try {
+ inset-block-start: anchor(--a end);
+ inset-inline-end: anchor(--a start);
+ }
+ @try {
+ inset-block-end: anchor(--a start);
+ inset-inline-end: anchor(--a start);
+ }
+}
+</style>
+
+<div id="anchor"></div>
+<div id="anchored"></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'top');
+ assert_fallback_position(anchored, anchor, 'left');
+}, 'Should use the last fallback position initially');
+
+promise_test(async () => {
+ // Scroll left to have enough space right to the anchor, but not enough below.
+ document.documentElement.scrollLeft = 250;
+ document.documentElement.scrollTop = 150;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'right');
+ assert_fallback_position(anchored, anchor, 'top');
+}, 'Should use the third fallback position with enough space right');
+
+promise_test(async () => {
+ // Scroll down to have enough space below the anchor, but not enough right.
+ document.documentElement.scrollLeft = 150;
+ document.documentElement.scrollTop = 250;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'left');
+ assert_fallback_position(anchored, anchor, 'bottom');
+}, 'Should use the second fallback position with enough space below');
+
+promise_test(async () => {
+ // Scroll down and right to have enough space on both axes.
+ document.documentElement.scrollLeft = 250;
+ document.documentElement.scrollTop = 250;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'right');
+ assert_fallback_position(anchored, anchor, 'bottom');
+}, 'Should use the first fallback position with enough space right and below');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-010.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-010.html
new file mode 100644
index 0000000000..a0dd599b3b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-010.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<title>Tests position fallback with initially out-of-viewport anchor in vertial-lr rtl</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;
+ width: 200vw;
+ height: 200vh;
+}
+
+html {
+ writing-mode: vertical-lr;
+ direction: rtl;
+}
+
+#anchor {
+ anchor-name: --a;
+ width: 100px;
+ height: 100px;
+ margin-block-start: 100vb;
+ margin-inline-start: 100vi;
+ background: orange;
+}
+
+#anchored {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background: green;
+ anchor-default: --a;
+ position-fallback: --pf;
+}
+
+@position-fallback --pf {
+ @try {
+ inset-block-start: anchor(--a end);
+ inset-inline-start: anchor(--a end);
+ }
+ @try {
+ inset-block-end: anchor(--a start);
+ inset-inline-start: anchor(--a end);
+ }
+ @try {
+ inset-block-start: anchor(--a end);
+ inset-inline-end: anchor(--a start);
+ }
+ @try {
+ inset-block-end: anchor(--a start);
+ inset-inline-end: anchor(--a start);
+ }
+}
+</style>
+
+<div id="anchor"></div>
+<div id="anchored"></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'bottom');
+ assert_fallback_position(anchored, anchor, 'left');
+}, 'Should use the last fallback position initially');
+
+promise_test(async () => {
+ // Scroll left to have enough space right to the anchor, but not enough above.
+ document.documentElement.scrollLeft = 250;
+ document.documentElement.scrollTop = -150;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'right');
+ assert_fallback_position(anchored, anchor, 'bottom');
+}, 'Should use the third fallback position with enough space right');
+
+promise_test(async () => {
+ // Scroll up to have enough space above the anchor, but not enough right.
+ document.documentElement.scrollLeft = 150;
+ document.documentElement.scrollTop = -250;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'left');
+ assert_fallback_position(anchored, anchor, 'top');
+}, 'Should use the second fallback position with enough space above');
+
+promise_test(async () => {
+ // Scroll up and right to have enough space on both axes.
+ document.documentElement.scrollLeft = 250;
+ document.documentElement.scrollTop = -250;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'right');
+ assert_fallback_position(anchored, anchor, 'top');
+}, 'Should use the first fallback position with enough space right and above');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-011.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-011.html
new file mode 100644
index 0000000000..98fa4b5f86
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fallback-position-011.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<title>Tests position fallback with initially out-of-viewport anchor in columb-reverse flexbox</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>
+.flex {
+ display: flex;
+ flex-direction: column-reverse;
+}
+
+#container {
+ transform: scale(1);
+ width: fit-content;
+ height: fit-content;
+ margin: 100px;
+}
+
+#scroller {
+ width: 400px;
+ height: 400px;
+ overflow: scroll;
+}
+
+#scroll-content {
+ min-width: 800px;
+ min-height: 800px;
+}
+
+#anchor {
+ anchor-name: --a;
+ width: 100px;
+ height: 100px;
+ margin-bottom: 400px;
+ margin-inline-start: 400px;
+ background: orange;
+}
+
+#anchored {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ background: green;
+ anchor-default: --a;
+ position-fallback: --pf;
+}
+
+@position-fallback --pf {
+ @try {
+ bottom: anchor(--a top);
+ left: anchor(--a right);
+ }
+ @try {
+ top: anchor(--a bottom);
+ left: anchor(--a right);
+ }
+ @try {
+ bottom: anchor(--a top);
+ right: anchor(--a left);
+ }
+ @try {
+ top: anchor(--a bottom);
+ right: anchor(--a left);
+ }
+}
+</style>
+
+<!-- Use flex column-reverse to make everything bottom-up -->
+<div id="container" class="flex">
+ <div id="scroller" class="flex">
+ <div id="scroll-content" class="flex">
+ <div id="anchor" class="flex"></div>
+ </div>
+ </div>
+ <div id="anchored" class="flex"></div>
+</div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'bottom');
+ assert_fallback_position(anchored, anchor, 'left');
+}, 'Should use the last fallback position initially');
+
+promise_test(async () => {
+ // Scroll up to have enough space above the anchor, but not enough right.
+ scroller.scrollTop = -250;
+ scroller.scrollLeft = 150;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'top');
+ assert_fallback_position(anchored, anchor, 'left');
+}, 'Should use the third fallback position with enough space above');
+
+promise_test(async () => {
+ // Scroll right to have enough space right to the anchor, but not enough above.
+ scroller.scrollTop = -150;
+ scroller.scrollLeft = 250;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'bottom');
+ assert_fallback_position(anchored, anchor, 'right');
+}, 'Should use the second fallback position with enough space right');
+
+promise_test(async () => {
+ // Scroll up and right to have enough space on both axes.
+ scroller.scrollTop = -250;
+ scroller.scrollLeft = 250;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(anchored, anchor, 'top');
+ assert_fallback_position(anchored, anchor, 'right');
+}, 'Should use the first fallback position with enough space above and right');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos.html
new file mode 100644
index 0000000000..a32ef3f7c4
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>Tests that scroll adjustments of fixed-positioned elements are applied 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-default: --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.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-js-expose.html
new file mode 100644
index 0000000000..1ef44d03c2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-js-expose.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<title>Tests that anchored element's actual rendered location is property exposed via JS APIs under scrolling</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#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-default: --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 scroll snapshot.
+ scroller.scrollTop = 300;
+ scroller.scrollLeft = 1300;
+
+ // Ensure up-to-date 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 anchored element is moved into visible area');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-nested.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-nested.html
new file mode 100644
index 0000000000..557f748c02
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-nested.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Tests anchor positioning 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-default: --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-update-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-001.html
new file mode 100644
index 0000000000..f11797edad
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-001.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Anchored elements should keep "pinned" to the anchor during 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-default: --anchor;
+}
+
+#outer-anchored {
+ color: yellow;
+ position: absolute;
+ left: anchor(--anchor left);
+ top: anchor(--anchor bottom);
+ anchor-default: --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.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-002.html
new file mode 100644
index 0000000000..19447952b0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-002.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Anchored elements should update location on `anchor-default` 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.anchorDefault = '--anchor';
+ document.getElementById('outer-anchored').style.anchorDefault = '--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.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-003.html
new file mode 100644
index 0000000000..57a524c483
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-003.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>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-default: --anchor;
+}
+
+#outer-anchored {
+ color: yellow;
+ position: absolute;
+ left: anchor(--anchor left);
+ top: anchor(--anchor bottom);
+ anchor-default: --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.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-004.html
new file mode 100644
index 0000000000..d20a7b660a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-004.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>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-default: --anchor;
+}
+
+#outer-anchored {
+ color: yellow;
+ position: absolute;
+ left: anchor(--anchor left);
+ top: anchor(--anchor bottom);
+ anchor-default: --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.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-005.html
new file mode 100644
index 0000000000..c2e7248c80
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-005.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-default: --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 scroll adjustment to 200.
+}
+runTest();
+</script>
+
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-006.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-006.html
new file mode 100644
index 0000000000..2535c68f78
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-006.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-default: --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 scroll adjustment to 200.
+}
+runTest();
+</script>
+
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-007.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-007.html
new file mode 100644
index 0000000000..4859f01d66
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-007.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Fixed positioned 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-default: --anchor;
+}
+
+#outer-anchored {
+ color: yellow;
+ position: fixed;
+ left: anchor(--anchor left);
+ top: anchor(--anchor bottom);
+ anchor-default: --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>
+
+<div id="outer-anchored">outer-anchored</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-vlr.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vlr.html
new file mode 100644
index 0000000000..00406c825e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vlr.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<title>Tests that anchor positioned scrolling 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/#scrolling">
+<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-default: --anchor;
+}
+
+#outer-anchored {
+ color: yellow;
+ position: absolute;
+ top: anchor(--anchor top);
+ right: anchor(--anchor left);
+ anchor-default: --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.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vrl.html
new file mode 100644
index 0000000000..2432d72899
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vrl.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<title>Tests that anchor positioned scrolling 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/#scrolling">
+<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-default: --anchor;
+}
+
+#outer-anchored {
+ color: yellow;
+ position: absolute;
+ top: anchor(--anchor top);
+ right: anchor(--anchor left);
+ anchor-default: --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..50620da257
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-size-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>
+<script src="support/test-common.js"></script>
+<style>
+.container {
+ position: relative;
+ height: 10px;
+}
+.anchor1 {
+ anchor-name: --a1;
+ width: 5px;
+ height: 7px;
+ background: orange;
+}
+.target {
+ position: absolute;
+}
+</style>
+<body onload="checkLayoutForAnchorPos('.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..364b3eb24f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-size-minmax-001.html
@@ -0,0 +1,45 @@
+<!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>
+<script src="support/test-common.js"></script>
+<style>
+.container {
+ position: relative;
+ height: 10px;
+}
+.anchor1 {
+ anchor-name: --a1;
+ width: 5px;
+ height: 7px;
+ background: orange;
+}
+.target {
+ position: absolute;
+}
+</style>
+<body onload="checkLayoutForAnchorPos('.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..32b308f056
--- /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(50% + 1px)',
+ '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..27554b3135
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-size-replaced-001.html
@@ -0,0 +1,75 @@
+<!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>
+<script src="support/test-common.js"></script>
+<style>
+.container {
+ position: relative;
+ height: 10px;
+}
+.anchor1 {
+ anchor-name: --a1;
+ width: 5px;
+ height: 24px;
+ background: orange;
+}
+.target {
+ position: absolute;
+}
+</style>
+<body onload="checkLayoutForAnchorPos('.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..25d4eea192
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-size-writing-modes-001.html
@@ -0,0 +1,68 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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/anchor-transition-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-001.html
new file mode 100644
index 0000000000..e69a388205
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-001.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<title>Tests CSS transition of anchor() and anchor-size() functions</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/8180"></script>
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+#anchor1 {
+ margin-left: 0px;
+ margin-top: 0px;
+ width: 200px;
+ height: 100px;
+ background: orange;
+ anchor-name: --a1;
+}
+
+#anchor2 {
+ margin-left: 400px;
+ margin-top: 200px;
+ width: 100px;
+ height: 200px;
+ background: orange;
+ anchor-name: --a2;
+}
+
+#target {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background-color: lime;
+ transition: all 10s -5s linear;
+}
+
+#target.before {
+ top: anchor(--a1 top);
+ left: anchor(--a1 right);
+ width: anchor-size(--a1 width);
+ height: anchor-size(--a1 height);
+}
+
+#target.after {
+ top: anchor(--a2 bottom);
+ left: anchor(--a2 left);
+ width: anchor-size(--a2 width);
+ height: anchor-size(--a2 height);
+}
+</style>
+
+<div id="anchor1"></div>
+<div id="anchor2"></div>
+<div id="target" class="before"></div>
+
+<script>
+setup(() => {
+ // Forces layout
+ target.offsetLeft;
+ // Triggers transition starting at 50% immediately
+ target.classList.remove('before');
+ target.classList.add('after');
+});
+
+test(() => {
+ assert_equals(target.offsetLeft, 300);
+ assert_equals(target.offsetTop, 250);
+}, 'Transition of anchor() when changing target anchor element name');
+
+test(() => {
+ assert_equals(target.offsetWidth, 150);
+ assert_equals(target.offsetHeight, 150);
+}, 'Transition of anchor-size() when changing target anchor element name');
+</script>
+
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-transition-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-002.html
new file mode 100644
index 0000000000..b0fddc4943
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-002.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<title>Tests CSS transition of anchor() across tree scopes</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/8180"></script>
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+#anchor1 {
+ width: 100px;
+ height: 100px;
+ background: orange;
+ anchor-name: --a;
+}
+
+#anchor2 {
+ width: 100px;
+ height: 100px;
+ margin-top: 200px;
+ margin-left: 300px;
+ background: orange;
+}
+
+#anchor2.after::part(target) {
+ top: anchor(--a top);
+ left: anchor(--a right);
+}
+</style>
+
+<div id="anchor1"></div>
+<div id="anchor2"></div>
+
+<script>
+setup(() => {
+ let shadow = anchor2.attachShadow({mode: 'open'});
+ shadow.innerHTML = `
+ <style>
+ :host { anchor-name: --a; }
+ #target {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background: lime;
+ top: anchor(--a top);
+ left: anchor(--a right);
+ transition: all 10s -5s linear;
+ }
+ </style>
+ <div id="target" part="target"></div>
+ `;
+});
+
+test(() => {
+ let target = anchor2.shadowRoot.getElementById('target');
+
+ // Forces layout
+ target.offsetLeft;
+
+ // Triggers a transition from using #anchor2 to using #anchor1,
+ // starting immediately at 50% progress.
+ anchor2.classList.add('after');
+ assert_equals(target.offsetLeft, 250);
+ assert_equals(target.offsetTop, 150);
+}, 'Transition with anchor names defined in different tree scopes');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-transition-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-003.html
new file mode 100644
index 0000000000..e7624575da
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-003.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<title>Tests CSS transition of anchor() across three tree scopes</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/8180"></script>
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+#anchor1 {
+ width: 100px;
+ height: 100px;
+ background: orange;
+ anchor-name: --a;
+}
+
+#host.after::part(target) {
+ left: anchor(--a left);
+}
+</style>
+
+<div id="anchor1"></div>
+<div id="host"></div>
+
+<script>
+setup(() => {
+let shadow = host.attachShadow({mode: 'open'});
+shadow.innerHTML = `
+ <style>
+ div {
+ width: 100px;
+ height: 100px;
+ background: orange;
+ }
+ #anchor2 {
+ margin-left: 200px;
+ anchor-name: --a;
+ }
+ #anchor3 {
+ margin-left: 400px;
+ }
+ #target {
+ position: fixed;
+ background: lime;
+ top: 300px;
+ transition: left 10s -5s linear;
+ }
+ #target.after {
+ left: anchor(--a left);
+ }
+ </style>
+ <div id="anchor2"></div>
+ <div id="anchor3">
+ <div id="target" part="target"></div>
+ </div>
+`;
+
+let anchor3 = shadow.getElementById('anchor3');
+let innerShadow = anchor3.attachShadow({mode: 'open'});
+innerShadow.innerHTML = `
+ <style>
+ :host { anchor-name: --a; }
+ ::slotted(*) { left: anchor(--a left); }
+ </style>
+ <slot></slot>
+`;
+});
+
+test(() => {
+ let target = host.shadowRoot.getElementById('target');
+
+ // Forces layout
+ target.offsetLeft;
+
+ // Triggers a transition from using #anchor3 to using #anchor2,
+ // starting immediately at 50% progress
+ target.classList.add('after');
+ assert_equals(target.offsetLeft, 300);
+
+ // Triggers another transition to using #anchor1 while the previous
+ // transition is still in progress.
+ host.classList.add('after');
+ assert_equals(target.offsetLeft, 150);
+}, 'Transition with anchor names defined in three different tree scopes');
+</script>
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..cca222ac6d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/at-fallback-position-allowed-declarations.html
@@ -0,0 +1,104 @@
+<!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');
+test_allowed_declaration('inset-area', 'all');
+
+// Margin properties are allowed
+test_allowed_declaration('margin-top');
+test_allowed_declaration('margin-bottom');
+test_allowed_declaration('margin-left');
+test_allowed_declaration('margin-right');
+test_allowed_declaration('margin-block-start');
+test_allowed_declaration('margin-block-end');
+test_allowed_declaration('margin-inline-start');
+test_allowed_declaration('margin-inline-end');
+test_allowed_declaration('margin-block');
+test_allowed_declaration('margin-inline');
+test_allowed_declaration('margin');
+
+// 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');
+
+// 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-cssom.html b/testing/web-platform/tests/css/css-anchor-position/at-position-fallback-cssom.html
new file mode 100644
index 0000000000..df295bf2d0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/at-position-fallback-cssom.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<title>Tests the CSSOM interfaces of @position-fallback and @try rules</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#interfaces">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="anchor"></div>
+<div id="not-anchor"></div>
+<div id="target"></div>
+
+<script>
+function createStyle(t, text) {
+ const style = document.createElement('style');
+ style.textContent = text;
+ t.add_cleanup(() => style.remove());
+ document.head.appendChild(style);
+ return style;
+}
+
+test(t => {
+ const style = createStyle(
+ t, '@position-fallback --pf { @try { left: anchor(right); } }');
+ const positionFallbackRule = style.sheet.cssRules[0];
+ assert_true(positionFallbackRule instanceof CSSPositionFallbackRule);
+ assert_equals(positionFallbackRule.name, '--pf');
+ assert_equals(positionFallbackRule.cssRules.length, 1);
+
+ const tryRule = positionFallbackRule.cssRules[0];
+ assert_true(tryRule instanceof CSSTryRule);
+ assert_true(tryRule.style instanceof CSSStyleDeclaration);
+ assert_equals(tryRule.style.length, 1);
+ assert_equals(tryRule.style.left, 'anchor(right)');
+}, 'CSSPositionFallbackRule and CSSTryRule attribute values');
+
+test(t => {
+ const style = createStyle(t, '@position-fallback --pf {}');
+ const positionFallbackRule = style.sheet.cssRules[0];
+
+ assert_equals(positionFallbackRule.insertRule('@try {}', 0), 0,
+ '@try rules can be inserted');
+ assert_throws_dom('HierarchyRequestError',
+ () => positionFallbackRule.insertRule('#target { color: red; }', 1),
+ 'style rules cannot be inserted');
+ assert_throws_dom('HierarchyRequestError',
+ () => positionFallbackRule.insertRule('@keyframes foo {}', 1),
+ 'other at-rules cannot be inserted');
+}, 'CSSPositionFallbackRule.insertRule can insert @try rules only');
+
+
+test(t => {
+ const style = createStyle(t, `
+ @position-fallback --pf { @try { top: anchor(top); } }
+ #anchor, #not-anchor, #target {
+ position: absolute; width: 100px; height: 100px; left: 0;
+ }
+ #anchor { top: 100px; anchor-name: --a; }
+ #not-anchor { top: 200px; anchor-name: --b; }
+ #target { position-fallback: --pf; anchor-default: --a; }
+ `);
+ const positionFallbackRule = style.sheet.cssRules[0];
+ const tryRule = positionFallbackRule.cssRules[0];
+
+ // Check the initial position fallback result
+ assert_equals(target.getBoundingClientRect().left, 0);
+ assert_equals(target.getBoundingClientRect().top, 100);
+
+ // `left` is an allowed property in `@try` and should affect position fallback.
+ tryRule.style.setProperty('left', 'anchor(right)');
+ assert_equals(target.getBoundingClientRect().left, 100);
+ assert_equals(target.getBoundingClientRect().top, 100);
+
+ // These properties are disallowed in `@try` rule, and hence should not affect
+ // position fallback.
+ tryRule.style.setProperty('anchor-default', '--b');
+ tryRule.style.setProperty('position', 'static');
+ assert_equals(target.getBoundingClientRect().left, 100);
+ assert_equals(target.getBoundingClientRect().top, 100);
+}, 'CSSTryRule.style.setProperty setting allowed and disallowed properties');
+
+</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..066cba1dac
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/at-position-fallback-invalidation-shadow-dom.html
@@ -0,0 +1,45 @@
+<!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>
+<style>
+ body { margin: 0; }
+</style>
+
+<div id="host">
+ <template shadowrootmode="open">
+ <style>
+ ::slotted(#slotted), :host {
+ position-fallback: --pf;
+ position: absolute;
+ }
+ </style>
+ <slot></slot>
+ </template>
+ <div id="slotted"></div>
+</div>
+
+<script>
+ 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/chrome-1512373-2-crash.html b/testing/web-platform/tests/css/css-anchor-position/chrome-1512373-2-crash.html
new file mode 100644
index 0000000000..ac27d8c264
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/chrome-1512373-2-crash.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>Chrome crash bug 1512373 with @try fallback</title>
+<link rel="help" href="https://crbug.com/1512373">
+<style>
+ @position-fallback --foo {
+ @try {
+ left: 0;
+ }
+ }
+ #t {
+ position: absolute;
+ position-fallback: --foo;
+ }
+</style>
+<div id="t"></div>
+<script>
+ t.animate([{"height":"100px"},{"height":"200px"}], {"duration":1});
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/chrome-1512373-crash.html b/testing/web-platform/tests/css/css-anchor-position/chrome-1512373-crash.html
new file mode 100644
index 0000000000..d3ae0b5bb6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/chrome-1512373-crash.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<title>Chrome crash bug 1512373</title>
+<link rel="help" href="https://crbug.com/1512373">
+<div id="t" style="position:absolute"></div>
+<script>
+ t.animate([{"height":"100px"},{"height":"200px"}], {"duration":1});
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/idlharness.html b/testing/web-platform/tests/css/css-anchor-position/idlharness.html
new file mode 100644
index 0000000000..2679bb3740
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/idlharness.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>CSS Anchor Positioning IDL tests</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#interfaces">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="/resources/idlharness.js"></script>
+
+<style>
+@position-fallback --fallback {
+ @try {}
+ @try {}
+}
+</style>
+
+<script>
+ 'use strict';
+ idl_test(
+ ['css-anchor-position'],
+ ['cssom'],
+ idl_array => {
+ try {
+ self.positionFallback = document.styleSheets[0].cssRules.item(0);
+ self.try1 = self.positionFallback.cssRules.item(0);
+ self.try2 = self.positionFallback.cssRules.item(1);
+ } catch (e) {
+ // Will be surfaced when any rule is undefined below.
+ }
+
+ idl_array.add_objects({
+ CSSPositionFallbackRule: ['positionFallback'],
+ CSSTryRule: ['try1', 'try2'],
+ CSSStyleDeclaration: ['try1.style', 'try2.style'],
+ });
+ }
+ );
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-abs-inline-container.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-abs-inline-container.html
new file mode 100644
index 0000000000..52344614f0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-abs-inline-container.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: inset-area positioning with absolute inline container</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area">
+<link rel="match" href="inset-area-inline-container-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+<style>
+ #in-flow {
+ font-family: Ahem;
+ font-size: 100px;
+ color: orange;
+ }
+ #inline-container {
+ position: absolute;
+ }
+ #anchor {
+ position: absolute;
+ top: 25px;
+ left: 100px;
+ width: 200px;
+ height: 50px;
+ anchor-name: --anchor;
+ background-color: cyan;
+ }
+ .anchored {
+ position: absolute;
+ align-self: stretch;
+ justify-self: stretch;
+ anchor-default: --anchor;
+ background-color: blue;
+ }
+ #top-left { inset-area: top / left; }
+ #top-right { inset-area: top / right; }
+ #bottom-left { inset-area: bottom / left; }
+ #bottom-right { inset-area: bottom / right; }
+</style>
+<div id="in-flow">
+ <br>
+ <br>
+ &nbsp;&nbsp;<span id="inline-container">XXXX<span id="anchor"></span><div id="top-left" class="anchored"></div><div id="top-right" class="anchored"></div><div id="bottom-left" class="anchored"></div><div id="bottom-right" class="anchored"></div></span>
+</div>
diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-basic.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-basic.html
new file mode 100644
index 0000000000..4a63635558
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-basic.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: basic inset-area positioning</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!-- The grid:
+
+ 100 150 150
+ +--------+--------+--------+
+ | | | |
+ 150 | | | |
+ | | | |
+ +--------+--------+--------+
+ | | | |
+ 75 | | | |
+ | | | |
+ +--------+--------+--------+
+ | | | |
+ 175 | | | |
+ | | | |
+ +--------+--------+--------+
+
+ -->
+<style>
+ #container {
+ position: absolute;
+ width: 400px;
+ height: 400px;
+ }
+ #anchored {
+ position: absolute;
+ align-self: stretch;
+ justify-self: stretch;
+ anchor-default: --anchor;
+ }
+ #anchor {
+ margin-top: 150px;
+ margin-left: 100px;
+ width: 150px;
+ height: 75px;
+ anchor-name: --anchor;
+ }
+</style>
+<div id="container">
+ <div id="anchored"></div>
+ <div id="anchor"></div>
+</div>
+<script>
+ function test_inset_area(inset_area, expected_offsets) {
+ anchored.style.insetArea = inset_area;
+ test(() => {
+ assert_equals(anchored.offsetLeft, expected_offsets.left);
+ assert_equals(anchored.offsetTop, expected_offsets.top);
+ assert_equals(anchored.offsetWidth, expected_offsets.width);
+ assert_equals(anchored.offsetHeight, expected_offsets.height);
+ }, "Offsets for: " + inset_area);
+ }
+
+ test_inset_area("none", {left:0, top:0, width:0, height:0});
+ test_inset_area("all", {left:0, top:0, width:400, height:400});
+ test_inset_area("all / all", {left:0, top:0, width:400, height:400});
+
+ // Single region spans
+ test_inset_area("top / left", {left:0, top:0, width:100, height:150});
+ test_inset_area("top / center", {left:100, top:0, width:150, height:150});
+ test_inset_area("top / right", {left:250, top:0, width:150, height:150});
+ test_inset_area("center / left", {left:0, top:150, width:100, height:75});
+ test_inset_area("center / center", {left:100, top:150, width:150, height:75});
+ test_inset_area("center / right", {left:250, top:150, width:150, height:75});
+ test_inset_area("bottom / left", {left:0, top:225, width:100, height:175});
+ test_inset_area("bottom / center", {left:100, top:225, width:150, height:175});
+ test_inset_area("bottom / right", {left:250, top:225, width:150, height:175});
+
+ test_inset_area("start / start", {left:0, top:0, width:100, height:150});
+ test_inset_area("start / center", {left:100, top:0, width:150, height:150});
+ test_inset_area("start / end", {left:250, top:0, width:150, height:150});
+ test_inset_area("center / start", {left:0, top:150, width:100, height:75});
+ test_inset_area("center / end", {left:250, top:150, width:150, height:75});
+ test_inset_area("end / start", {left:0, top:225, width:100, height:175});
+ test_inset_area("end / center", {left:100, top:225, width:150, height:175});
+ test_inset_area("end / end", {left:250, top:225, width:150, height:175});
+
+ test_inset_area("self-start / self-start", {left:0, top:0, width:100, height:150});
+ test_inset_area("self-start / center", {left:100, top:0, width:150, height:150});
+ test_inset_area("self-start / self-end", {left:250, top:0, width:150, height:150});
+ test_inset_area("center / self-start", {left:0, top:150, width:100, height:75});
+ test_inset_area("center / self-end", {left:250, top:150, width:150, height:75});
+ test_inset_area("self-end / self-start", {left:0, top:225, width:100, height:175});
+ test_inset_area("self-end / center", {left:100, top:225, width:150, height:175});
+ test_inset_area("self-end / self-end", {left:250, top:225, width:150, height:175});
+
+ test_inset_area("y-start / x-start", {left:0, top:0, width:100, height:150});
+ test_inset_area("y-start / center", {left:100, top:0, width:150, height:150});
+ test_inset_area("y-start / x-end", {left:250, top:0, width:150, height:150});
+ test_inset_area("center / x-start", {left:0, top:150, width:100, height:75});
+ test_inset_area("center / x-end", {left:250, top:150, width:150, height:75});
+ test_inset_area("y-end / x-start", {left:0, top:225, width:100, height:175});
+ test_inset_area("y-end / center", {left:100, top:225, width:150, height:175});
+ test_inset_area("y-end / x-end", {left:250, top:225, width:150, height:175});
+
+ test_inset_area("y-self-start / x-self-start", {left:0, top:0, width:100, height:150});
+ test_inset_area("y-self-start / center", {left:100, top:0, width:150, height:150});
+ test_inset_area("y-self-start / x-self-end", {left:250, top:0, width:150, height:150});
+ test_inset_area("center / x-self-start", {left:0, top:150, width:100, height:75});
+ test_inset_area("center / x-self-end", {left:250, top:150, width:150, height:75});
+ test_inset_area("y-self-end / x-self-start", {left:0, top:225, width:100, height:175});
+ test_inset_area("y-self-end / center", {left:100, top:225, width:150, height:175});
+ test_inset_area("y-self-end / x-self-end", {left:250, top:225, width:150, height:175});
+
+ // Multi-region spans
+ test_inset_area("y-self-start center / self-end center", {left:100, top:0, width:300, height:225});
+ test_inset_area("bottom center / x-start x-end", {left:0, top:150, width:400, height:250});
+
+ // Non-orthogonal axes.
+ test_inset_area("x-start / left", {left:0, top:0, width:0, height:0});
+ test_inset_area("y-end / y-self-start", {left:0, top:0, width:0, height:0});
+
+ // No implicit anchor means the inset-area should not apply.
+ anchored.style.anchorDefault = "implicit";
+ test_inset_area("all / top", {left:0, top:0, width:0, height:0});
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-computed-insets.tentative.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-computed-insets.tentative.html
new file mode 100644
index 0000000000..4e35dd883a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-computed-insets.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: inset-area should not affect computed inset values</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area">
+<lnik rel="help" href="https://github.com/w3c/csswg-drafts/issues/9598">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/css-typed-om/resources/testhelper.js"></script>
+<style>
+ #abs {
+ position: absolute;
+ inset-area: all;
+ }
+</style>
+<div id="abs"></div>
+<script>
+ test(() => {
+ let style = abs.computedStyleMap();
+ assert_equals(style.get("inset-area").toString(), "all", "inset-area is supported");
+ assert_style_value_equals(style.get("left"), new CSSKeywordValue("auto"));
+ assert_style_value_equals(style.get("right"), new CSSKeywordValue("auto"));
+ assert_style_value_equals(style.get("top"), new CSSKeywordValue("auto"));
+ assert_style_value_equals(style.get("bottom"), new CSSKeywordValue("auto"));
+ }, "inset-area does not affect insets at computed value time");
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-computed.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-computed.html
new file mode 100644
index 0000000000..46e29fda39
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-computed.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: inset-area getComputedStyle()</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<script src="/css/support/inheritance-testcommon.js"></script>
+<div id="container">
+ <div id="target"></div>
+</div>
+<script>
+ test_computed_value("inset-area", "none");
+ test_computed_value("inset-area", "all");
+ test_computed_value("inset-area", "x-start");
+ test_computed_value("inset-area", "center");
+ test_computed_value("inset-area", "all / all", "all");
+ test_computed_value("inset-area", "top center");
+ test_computed_value("inset-area", "bottom center / all", "center bottom");
+ test_computed_value("inset-area", "x-start center x-end", "x-start x-end");
+ test_computed_value("inset-area", "x-start / x-end");
+
+ assert_not_inherited("inset-area", "none", "all");
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-inline-container-ref.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-inline-container-ref.html
new file mode 100644
index 0000000000..ecf54f1a9a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-inline-container-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>CSS Test Reference</title>
+<div style="position:relative">
+ <!-- The Ahem 'XXXX' text: -->
+ <div style="position:absolute; background-color:orange; left:200px; top:200px; width:400px; height:100px"></div>
+ <!-- The #anchor -->
+ <div style="position:absolute; background-color:cyan; left:300px; top:225px; width:200px; height:50px"></div>
+ <!-- top / left -->
+ <div style="position:absolute; background-color:blue; left:200px; top:200px; width:100px; height:25px"></div>
+ <!-- top / right -->
+ <div style="position:absolute; background-color:blue; left:500px; top:200px; width:100px; height:25px"></div>
+ <!-- bottom / left -->
+ <div style="position:absolute; background-color:blue; left:200px; top:275px; width:100px; height:25px"></div>
+ <!-- bottom / right -->
+ <div style="position:absolute; background-color:blue; left:500px; top:275px; width:100px; height:25px"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-inline-container.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-inline-container.html
new file mode 100644
index 0000000000..91c9b09735
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-inline-container.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: inset-area positioning with inline container</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area">
+<link rel="match" href="inset-area-inline-container-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+<style>
+ #in-flow {
+ font-family: Ahem;
+ font-size: 100px;
+ color: orange;
+ }
+ #inline-container {
+ position: relative;
+ }
+ #anchor {
+ position: absolute;
+ top: 25px;
+ left: 100px;
+ width: 200px;
+ height: 50px;
+ anchor-name: --anchor;
+ background-color: cyan;
+ }
+ .anchored {
+ position: absolute;
+ anchor-default: --anchor;
+ background-color: blue;
+ }
+ #top-left { inset-area: top / left; }
+ #top-right { inset-area: top / right; }
+ #bottom-left { inset-area: bottom / left; }
+ #bottom-right { inset-area: bottom / right; }
+</style>
+<div id="in-flow">
+ <br>
+ <br>
+ &nbsp;&nbsp;<span id="inline-container">XXXX<span id="anchor"></span><div id="top-left" class="anchored"></div><div id="top-right" class="anchored"></div><div id="bottom-left" class="anchored"></div><div id="bottom-right" class="anchored"></div></span>
+</div>
diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-interpolation.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-interpolation.html
new file mode 100644
index 0000000000..29fe76ca5c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-interpolation.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: inset-area interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+<body>
+<script>
+ test_no_interpolation({
+ property: "inset-area",
+ from: "none",
+ to: "all / left"
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-parsing.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-parsing.html
new file mode 100644
index 0000000000..1ee88b00e6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-parsing.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: inset-area parsing</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<body>
+<script>
+ test_valid_value("inset-area", "none");
+ test_valid_value("inset-area", "all");
+ test_valid_value("inset-area", "start");
+ test_valid_value("inset-area", "end");
+ test_valid_value("inset-area", "top");
+ test_valid_value("inset-area", "left");
+ test_valid_value("inset-area", "bottom");
+ test_valid_value("inset-area", "right");
+ test_valid_value("inset-area", "self-start");
+ test_valid_value("inset-area", "self-end");
+ test_valid_value("inset-area", "x-start");
+ test_valid_value("inset-area", "x-end");
+ test_valid_value("inset-area", "x-self-start");
+ test_valid_value("inset-area", "x-self-end");
+ test_valid_value("inset-area", "y-start");
+ test_valid_value("inset-area", "y-end");
+ test_valid_value("inset-area", "y-self-start");
+ test_valid_value("inset-area", "y-self-end");
+
+ test_valid_value("inset-area", "all / all", "all");
+ test_valid_value("inset-area", "top / all", "top");
+ test_valid_value("inset-area", "all / top", "all / top");
+ test_valid_value("inset-area", "start end", "all");
+ test_valid_value("inset-area", "center end start", "all");
+ test_valid_value("inset-area", "center x-end x-start", "x-start x-end");
+ test_valid_value("inset-area", "center end start / top center bottom", "all / top bottom");
+ test_valid_value("inset-area", "end center / start", "center end / start");
+ test_valid_value("inset-area", "bottom / left");
+ test_valid_value("inset-area", "center start", "start center");
+ // Valid to parse and compute, but resolves to 'none'.
+ test_valid_value("inset-area", "x-start / x-start");
+
+ test_invalid_value("inset-area", "/ all");
+ test_invalid_value("inset-area", "none / none");
+ test_invalid_value("inset-area", "start / none");
+ test_invalid_value("inset-area", "none / start");
+ test_invalid_value("inset-area", "center center");
+ test_invalid_value("inset-area", "top left top");
+ test_invalid_value("inset-area", "x-start end");
+ test_invalid_value("inset-area", "bottom left");
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-wm-dir.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-wm-dir.html
new file mode 100644
index 0000000000..5268cba7e5
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-wm-dir.html
@@ -0,0 +1,172 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: inset-area with writing-mode and direction</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!-- The grid:
+
+ 100 150 150
+ +--------+--------+--------+
+ | | | |
+ 150 | | | |
+ | | | |
+ +--------+--------+--------+
+ | | | |
+ 75 | | | |
+ | | | |
+ +--------+--------+--------+
+ | | | |
+ 175 | | | |
+ | | | |
+ +--------+--------+--------+
+
+ -->
+<style>
+ #container {
+ position: absolute;
+ width: 400px;
+ height: 400px;
+ }
+ #anchored {
+ position: absolute;
+ align-self: stretch;
+ justify-self: stretch;
+ anchor-default: --anchor;
+ }
+ #anchor {
+ margin-top: 150px;
+ margin-bottom: 175px;
+ margin-left: 100px;
+ margin-right: 150px;
+ width: 150px;
+ height: 75px;
+ anchor-name: --anchor;
+ }
+</style>
+<div id="container">
+ <div id="anchored"></div>
+ <div id="anchor"></div>
+</div>
+<script>
+ function test_inset_area(writing_direction, inset_area, expected_offsets) {
+ anchored.style.insetArea = inset_area;
+ test(() => {
+ assert_equals(anchored.offsetLeft, expected_offsets.left, "Checking offsetLeft");
+ assert_equals(anchored.offsetTop, expected_offsets.top, "Checking offsetTop");
+ assert_equals(anchored.offsetWidth, expected_offsets.width, "Checking offsetWidth");
+ assert_equals(anchored.offsetHeight, expected_offsets.height, "Checking offsetHeight");
+ }, "Offsets for: " + inset_area + " with writing-mode / direction: " + writing_direction);
+ }
+
+ const top_left = {left:0, top:0, width:100, height:150};
+ const top_right = {left:250, top:0, width:150, height:150};
+ const bottom_left = {left:0, top:225, width:100, height:175};
+ const bottom_right = {left:250, top:225, width:150, height:175};
+
+ anchored.style.writingMode = "horizontal-tb";
+ anchored.style.direction = "ltr";
+
+ // Writing-mode and direction on container
+ let writing_direction = "containing-block: horizontal-tb / rtl";
+ container.style.writingMode = "horizontal-tb";
+ container.style.direction = "rtl";
+ test_inset_area(writing_direction, "start / start", top_right);
+ test_inset_area(writing_direction, "self-start / self-start", top_left);
+ test_inset_area(writing_direction, "x-start / y-start", top_right);
+ test_inset_area(writing_direction, "x-self-start / y-self-start", top_left);
+
+ // containing-block: vertical-lr / ltr
+ // self: horizontal-tb / ltr
+ writing_direction = "containing-block: vertical-lr / ltr";
+ container.style.writingMode = "vertical-lr";
+ container.style.direction = "ltr";
+ test_inset_area(writing_direction, "start / start", top_left);
+ test_inset_area(writing_direction, "self-start / self-start", top_left);
+ test_inset_area(writing_direction, "x-start / y-start", top_left);
+ test_inset_area(writing_direction, "x-self-start / y-self-start", top_left);
+
+ // containing-block: vertical-lr / rtl
+ // self: horizontal-tb / ltr
+ writing_direction = "containing-block: vertical-lr / rtl";
+ container.style.writingMode = "vertical-lr";
+ container.style.direction = "rtl";
+ test_inset_area(writing_direction, "start / start", bottom_left);
+ test_inset_area(writing_direction, "self-start / self-start", top_left);
+ test_inset_area(writing_direction, "x-start / y-start", bottom_left);
+ test_inset_area(writing_direction, "x-self-start / y-self-start", top_left);
+
+ // containing-block: vertical-rl / ltr
+ // self: horizontal-tb / ltr
+ writing_direction = "containing-block: vertical-rl / ltr";
+ container.style.writingMode = "vertical-rl";
+ container.style.direction = "ltr";
+ test_inset_area(writing_direction, "start / start", top_right);
+ test_inset_area(writing_direction, "self-start / self-start", top_left);
+ test_inset_area(writing_direction, "x-start / y-start", top_right);
+ test_inset_area(writing_direction, "x-self-start / y-self-start", top_left);
+
+ // containing-block: vertical-rl / rtl
+ // self: horizontal-tb / ltr
+ writing_direction = "containing-block: vertical-rl / rtl";
+ container.style.writingMode = "vertical-rl";
+ container.style.direction = "rtl";
+ test_inset_area(writing_direction, "start / start", bottom_right);
+ test_inset_area(writing_direction, "self-start / self-start", top_left);
+ test_inset_area(writing_direction, "x-start / y-start", bottom_right);
+ test_inset_area(writing_direction, "x-self-start / y-self-start", top_left);
+
+ // Writing-mode and direction on self
+ container.style.writingMode = "horizontal-tb";
+ container.style.direction = "ltr";
+
+ // containing-block: horizontal-tb / ltr
+ // self: horizontal-tb / rtl
+ writing_direction = "self: horizontal-tb / rtl";
+ anchored.style.writingMode = "horizontal-tb";
+ anchored.style.direction = "rtl";
+ test_inset_area(writing_direction, "start / start", top_left);
+ test_inset_area(writing_direction, "self-start / self-start", top_right);
+ test_inset_area(writing_direction, "x-start / y-start", top_left);
+ test_inset_area(writing_direction, "x-self-start / y-self-start", top_right);
+
+ // containing-block: horizontal-tb / ltr
+ // self: vertical-lr / ltr
+ writing_direction = "self: vertical-lr / ltr";
+ anchored.style.writingMode = "vertical-lr";
+ anchored.style.direction = "ltr";
+ test_inset_area(writing_direction, "start / start", top_left);
+ test_inset_area(writing_direction, "self-start / self-start", top_left);
+ test_inset_area(writing_direction, "x-start / y-start", top_left);
+ test_inset_area(writing_direction, "x-self-start / y-self-start", top_left);
+
+ // containing-block: horizontal-tb / ltr
+ // self: vertical-lr / rtl
+ writing_direction = "self: vertical-lr / rtl";
+ anchored.style.writingMode = "vertical-lr";
+ anchored.style.direction = "rtl";
+ test_inset_area(writing_direction, "start / start", top_left);
+ test_inset_area(writing_direction, "self-start / self-start", bottom_left);
+ test_inset_area(writing_direction, "x-start / y-start", top_left);
+ test_inset_area(writing_direction, "x-self-start / y-self-start", bottom_left);
+
+ // containing-block: horizontal-tb / ltr
+ // self: vertical-rl / ltr
+ writing_direction = "self: vertical-rl / ltr";
+ anchored.style.writingMode = "vertical-rl";
+ anchored.style.direction = "ltr";
+ test_inset_area(writing_direction, "start / start", top_left);
+ test_inset_area(writing_direction, "self-start / self-start", top_right);
+ test_inset_area(writing_direction, "x-start / y-start", top_left);
+ test_inset_area(writing_direction, "x-self-start / y-self-start", top_right);
+
+ // containing-block: horizontal-tb / ltr
+ // self: vertical-rl / rtl
+ writing_direction = "self: vertical-rl / rtl";
+ anchored.style.writingMode = "vertical-rl";
+ anchored.style.direction = "rtl";
+ test_inset_area(writing_direction, "start / start", top_left);
+ test_inset_area(writing_direction, "self-start / self-start", bottom_right);
+ test_inset_area(writing_direction, "x-start / y-start", top_left);
+ test_inset_area(writing_direction, "x-self-start / y-self-start", bottom_right);
+
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-computed.html b/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-computed.html
new file mode 100644
index 0000000000..d6423c164b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-computed.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Anchor Positioning Test: Computed position-try-options</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#position-try-options">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<script src="/css/support/inheritance-testcommon.js"></script>
+<div id="container">
+ <div id="target"></div>
+</div>
+<script>
+ test_computed_value("position-try-options", "none");
+ test_computed_value("position-try-options", "flip-block");
+ test_computed_value("position-try-options", "flip-inline");
+ test_computed_value("position-try-options", "flip-start");
+ test_computed_value("position-try-options", "flip-block, flip-inline");
+ test_computed_value("position-try-options", "--foo, --bar");
+ test_computed_value("position-try-options", "flip-start flip-inline flip-block", "flip-block flip-inline flip-start");
+
+ assert_not_inherited("position-try-options", "none", "flip-inline");
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-parsing.html b/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-parsing.html
new file mode 100644
index 0000000000..dfc9997e9f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-parsing.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Anchor Positioning Test: Parsing of position-try-options</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#position-try-options">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<div id="target"></div>
+<script>
+ test_valid_value("position-try-options", "initial");
+ test_valid_value("position-try-options", "inherit");
+ test_valid_value("position-try-options", "unset");
+ test_valid_value("position-try-options", "revert");
+ test_valid_value("position-try-options", "none");
+ test_valid_value("position-try-options", "flip-block");
+ test_valid_value("position-try-options", "flip-start, flip-block");
+ test_valid_value("position-try-options", "flip-start flip-inline, flip-block");
+ test_valid_value("position-try-options", "flip-start, flip-start");
+ test_valid_value("position-try-options", "flip-block, --foo");
+ test_valid_value("position-try-options", "--bar, flip-block flip-start");
+ test_valid_value("position-try-options", "--foo, --bar, --baz");
+
+ test_invalid_value("position-try-options", "none, flip-start");
+ test_invalid_value("position-try-options", "flip-block flip-block");
+ test_invalid_value("position-try-options", "flip-block --foo");
+ test_invalid_value("position-try-options", "--foo --bar");
+ test_invalid_value("position-try-options", "-foo");
+ test_invalid_value("position-try-options", "foo");
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-order-computed.html b/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-order-computed.html
new file mode 100644
index 0000000000..aebec00aef
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-order-computed.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Anchor Positioning Test: Computed position-try-order</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#position-try-order-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<script src="/css/support/inheritance-testcommon.js"></script>
+<div id="container">
+ <div id="target"></div>
+</div>
+<script>
+ test_computed_value("position-try-order", "normal");
+ test_computed_value("position-try-order", "most-width");
+ test_computed_value("position-try-order", "most-height");
+ test_computed_value("position-try-order", "most-block-size");
+ test_computed_value("position-try-order", "most-inline-size");
+
+ assert_not_inherited("position-try-order", "normal", "most-inline-size");
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-order-parsing.html b/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-order-parsing.html
new file mode 100644
index 0000000000..4b3b34287f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-order-parsing.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Anchor Positioning Test: Parsing of position-try-order</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#position-try-order-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<div id="target"></div>
+<script>
+ test_valid_value("position-try-order", "initial");
+ test_valid_value("position-try-order", "inherit");
+ test_valid_value("position-try-order", "unset");
+ test_valid_value("position-try-order", "revert");
+ test_valid_value("position-try-order", "normal");
+ test_valid_value("position-try-order", "most-width");
+ test_valid_value("position-try-order", "most-height");
+ test_valid_value("position-try-order", "most-block-size");
+ test_valid_value("position-try-order", "most-inline-size");
+
+ test_invalid_value("position-try-order", "normal most-inline-size");
+ test_invalid_value("position-try-order", "most-block-size most-inline-size");
+ test_invalid_value("position-try-order", "most-block-size, most-inline-size");
+</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..d28c71ec68
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-001.html
@@ -0,0 +1,109 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..ea6ff11479
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-002.html
@@ -0,0 +1,62 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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-003.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-003.html
new file mode 100644
index 0000000000..531dc303d6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-003.html
@@ -0,0 +1,142 @@
+<!DOCTYPE html>
+<title>Tests fallback positions that overflow the inset-modified containing block regardless of scrolling</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>
+<script src="support/test-common.js"></script>
+<style>
+body { margin: 0; }
+.ltr { direction: ltr; }
+.rtl { direction: rtl; }
+.vrl { writing-mode: vertical-rl; }
+.vlr { writing-mode: vertical-lr; }
+
+.cb {
+ width: 200px;
+ height: 200px;
+ transform: scale(1);
+}
+
+.spacer {
+ block-size: 50px;
+}
+
+.anchor {
+ width: 100px;
+ height: 100px;
+ margin-inline-start: 50px;
+ background: orange;
+ anchor-name: --a;
+}
+
+.anchored {
+ position: absolute;
+ width: 50px;
+ height: 50px;
+ background: lime;
+}
+
+.exceeds-end {
+ position-fallback: --exceeds-end;
+}
+
+/* Used on a element whose block and inline axes are the same with its
+ containing block, so that the first two positions will exceed the end edges
+ of the IMCB, and the last position will be used. */
+@position-fallback --exceeds-end {
+ @try {
+ left: 0;
+ right: anchor(--a left);
+ width: 100px;
+ }
+
+ @try {
+ top: 0;
+ bottom: anchor(--a top);
+ height: 100px;
+ }
+
+ @try {
+ top: 11px;
+ left: 22px;
+ }
+}
+
+.exceeds-start {
+ position-fallback: --exceeds-start;
+}
+
+/* Used on a element whose block and inline axes are in the opposite directions
+ of its containing block, so that the first two positions will exceed the
+ start edges of the IMCB, and the last position will be used. */
+@position-fallback --exceeds-start {
+ @try {
+ bottom: 0;
+ top: anchor(--a bottom);
+ height: 100px;
+ }
+
+ @try {
+ right: 0;
+ left: anchor(--a right);
+ width: 100px;
+ }
+
+ @try {
+ top: 11px;
+ left: 22px;
+ }
+}
+
+.exceeds-size {
+ position-fallback: --exceeds-size;
+}
+
+/* Both inset sides are `auto`, but the size is too big to fit in the containing
+ block. */
+@position-fallback --exceeds-size {
+ @try {
+ top: anchor(--a bottom);
+ left: auto;
+ right: auto;
+ width: 300px;
+ }
+
+ @try {
+ left: anchor(--a right);
+ top: auto;
+ bottom: auto;
+ height: 300px;
+ }
+
+ @try {
+ top: 11px;
+ left: 22px;
+ }
+}
+</style>
+
+<body onload="checkLayoutForAnchorPos('.anchored')">
+ <div class=cb>
+ <div class=spacer></div>
+ <div class=anchor></div>
+ <div class="anchored exceeds-end"
+ data-offset-x=22 data-offset-y=11></div>
+ </div>
+
+ <div class="cb rtl vrl">
+ <div class=spacer></div>
+ <div class=anchor></div>
+ <div class="anchored ltr vlr exceeds-start"
+ data-offset-x=22 data-offset-y=11></div>
+ </div>
+
+ <div class="cb">
+ <div class=spacer></div>
+ <div class=anchor></div>
+ <div class="anchored exceeds-size"
+ data-offset-x=22 data-offset-y=11></div>
+ </div>
+</body>
diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-004.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-004.html
new file mode 100644
index 0000000000..e4dbd71866
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-004.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<title>Tests margin properties in position fallback</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#accepted-try-properties">
+<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>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+.cb {
+ width: 300px;
+ height: 150px;
+ position: relative;
+ background: lightgray;
+}
+
+.anchor {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ top: 25px;
+ background: orange;
+ anchor-name: --a;
+}
+
+.target {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ background: lime;
+ position-fallback: --fallbacks;
+}
+
+@position-fallback --fallbacks {
+ @try {
+ top: anchor(--a top);
+ right: anchor(--a left);
+ margin-top: 10px;
+ margin-right: 10px;
+ }
+
+ @try {
+ bottom: anchor(--a bottom);
+ left: anchor(--a right);
+ margin-bottom: 10px;
+ margin-left: 10px;
+ }
+}
+</style>
+
+<body onload="checkLayoutForAnchorPos('.target')">
+
+<div class=cb>
+ <div class=anchor style="left: 110px"></div>
+ <!-- Chooses 1st @try block. -->
+ <div class=target data-offset-x=0
+ data-expected-margin-left=0 data-expected-margin-right=10
+ data-expected-margin-top=10 data-expected-margin-bottom=0></div>
+</div>
+
+<div class=cb>
+ <div class=anchor style="right: 110px"></div>
+ <!-- Chooses 2nd @try block. -->
+ <div class=target data-offset-x=200
+ data-expected-margin-left=10 data-expected-margin-right=0
+ data-expected-margin-top=0 data-expected-margin-bottom=10></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-bounds-001.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-001.html
new file mode 100644
index 0000000000..7735115e59
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-001.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<title>Tests basic functionalities of 'position-fallback-bounds'</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds">
+<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>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+.container {
+ position: relative;
+ width: 400px;
+ height: 400px;
+ top: 100px;
+ anchor-name: --bounds;
+ outline: 1px dashed black;
+}
+
+.anchor {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ left: 150px;
+ background-color: orange;
+}
+
+.target {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background-color: lime;
+ left: anchor(left);
+ position-fallback-bounds: --bounds;
+}
+
+#anchor1 {
+ top: 0;
+ anchor-name: --a1;
+}
+#anchor2 {
+ bottom: 0;
+ anchor-name: --a2;
+}
+
+#target1 {
+ anchor-default: --a1;
+ position-fallback: --top-then-bottom;
+}
+#target2 {
+ anchor-default: --a2;
+ position-fallback: --bottom-then-top;
+}
+
+@position-fallback --top-then-bottom {
+ @try { bottom: anchor(top); }
+ @try { top: anchor(bottom); }
+}
+@position-fallback --bottom-then-top {
+ @try { top: anchor(bottom); }
+ @try { bottom: anchor(top); }
+}
+</style>
+
+<body onload="checkLayoutForAnchorPos('.target')">
+ <div class="container" id="bounds">
+ <div class="anchor" id="anchor1"></div>
+ <div class="anchor" id="anchor2"></div>
+ </div>
+
+ <!-- Enough space above the anchor in the viewport but not in the additional
+ bounds rect, which triggers fallback -->
+ <div class="target" id="target1" data-offset-y=200></div>
+
+ <!-- Enough space below the anchor in the viewport but not in the additional
+ bounds rect, which triggers fallback -->
+ <div class="target" id="target2" data-offset-y=300></div>
+</body>
diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-002.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-002.html
new file mode 100644
index 0000000000..7bd71216e7
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-002.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<title>Tests 'position-fallback-bounds' with mixed writing modes</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds">
+<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>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+.container {
+ position: relative;
+ width: 400px;
+ height: 400px;
+ top: 100px;
+ anchor-name: --bounds;
+ outline: 1px dashed black;
+}
+
+.anchor {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ left: 150px;
+ background-color: orange;
+}
+
+.target {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background-color: lime;
+ left: anchor(left);
+ position-fallback-bounds: --bounds;
+}
+
+#anchor1 {
+ top: 0;
+ anchor-name: --a1;
+}
+#anchor2 {
+ bottom: 0;
+ anchor-name: --a2;
+}
+
+#target1 {
+ anchor-default: --a1;
+ position-fallback: --top-then-bottom;
+ writing-mode: vertical-rl;
+}
+#target2 {
+ anchor-default: --a2;
+ position-fallback: --bottom-then-top;
+ writing-mode: vertical-lr;
+ direction: rtl;
+}
+
+@position-fallback --top-then-bottom {
+ @try { bottom: anchor(top); }
+ @try { top: anchor(bottom); }
+}
+@position-fallback --bottom-then-top {
+ @try { top: anchor(bottom); }
+ @try { bottom: anchor(top); }
+}
+</style>
+
+<body onload="checkLayoutForAnchorPos('.target')">
+ <div class="container" id="bounds">
+ <div class="anchor" id="anchor1"></div>
+ <div class="anchor" id="anchor2"></div>
+ </div>
+
+ <!-- Enough space above the anchor in the viewport but not in the additional
+ bounds rect, which triggers fallback -->
+ <div class="target" id="target1" data-offset-y=200></div>
+
+ <!-- Enough space below the anchor in the viewport but not in the additional
+ bounds rect, which triggers fallback -->
+ <div class="target" id="target2" data-offset-y=300></div>
+</body>
diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-003.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-003.html
new file mode 100644
index 0000000000..0e0e243a16
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-003.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<title>Tests basic interaction between 'position-fallback-bounds' and scrolling</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+ position: relative;
+ width: 200vw;
+ height: 200vh;
+}
+
+#bounds {
+ position: fixed;
+ inset: 50px;
+ background: gray;
+ opacity: 0.1;
+ z-index: -1;
+ anchor-name: --bounds;
+}
+
+#anchor {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ left: 200px;
+ top: 200px;
+ background-color: orange;
+ anchor-name: --a;
+}
+
+#target {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background-color: lime;
+ anchor-default: --a;
+ position-fallback-bounds: --bounds;
+ position-fallback: --corners;
+}
+
+@position-fallback --corners {
+ @try {
+ bottom: anchor(top);
+ right: anchor(left);
+ }
+ @try {
+ top: anchor(bottom);
+ right: anchor(left);
+ }
+ @try {
+ bottom: anchor(top);
+ left: anchor(right);
+ }
+ @try {
+ top: anchor(bottom);
+ left: anchor(right);
+ }
+}
+</style>
+
+<div id="bounds"></div>
+<div id="anchor"></div>
+<div id="target"></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(target, anchor, 'top');
+ assert_fallback_position(target, anchor, 'left');
+}, "Target is at anchor's top-left corner at initial scroll position");
+
+promise_test(async () => {
+ document.documentElement.scrollTop = 100;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(target, anchor, 'bottom');
+ assert_fallback_position(target, anchor, 'left');
+}, "Target falls back to anchor's bottom-left corner after anchor is scrolled upwards");
+
+promise_test(async () => {
+ document.documentElement.scrollLeft = 100;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(target, anchor, 'bottom');
+ assert_fallback_position(target, anchor, 'right');
+}, "Target falls back to anchor's bottom-right corner after anchor is further scrolled leftwards");
+
+promise_test(async () => {
+ document.documentElement.scrollTop = 0;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(target, anchor, 'top');
+ assert_fallback_position(target, anchor, 'right');
+}, "Target falls back to anchor's top-left corner after anchor is scrolled back downwards");
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-004.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-004.html
new file mode 100644
index 0000000000..ff982ea93e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-004.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<title>Tests complex interaction between 'position-fallback-bounds' and scrolling</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+#anchor-scroller {
+ position: absolute;
+ height: 175px;
+ width: 200px;
+ left: 200px;
+ top: 100px;
+ overflow-y: scroll;
+ z-index: 100;
+}
+
+#anchor {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ left: 0;
+ top: 150px;
+ background-color: orange;
+ anchor-name: --a;
+}
+
+#bounds-scroller {
+ position: absolute;
+ width: 400px;
+ height: 400px;
+ left: 100px;
+ top: 0;
+ overflow-y: scroll;
+}
+
+#bounds {
+ position: absolute;
+ width: 400px;
+ height: 400px;
+ top: 100px;
+ background: gray;
+ anchor-name: --bounds;
+}
+
+#target {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background-color: lime;
+ left: anchor(left);
+ anchor-default: --a;
+ position-fallback-bounds: --bounds;
+ position-fallback: --top-then-bottom;
+}
+
+@position-fallback --top-then-bottom {
+ @try { bottom: anchor(top); }
+ @try { top: anchor(bottom); }
+}
+</style>
+
+<div id="anchor-scroller">
+ <div id="anchor"></div>
+</div>
+
+<div id="bounds-scroller">
+ <div id="bounds"></div>
+</div>
+
+<div id="target"></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(target, anchor, 'top');
+}, 'Target is above anchor at initial scroll position');
+
+promise_test(async () => {
+ const anchorScroller = document.getElementById('anchor-scroller');
+ anchorScroller.scrollTop = 100;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(target, anchor, 'bottom');
+}, 'Target falls back to below anchor after anchor is scrolled upwards');
+
+promise_test(async () => {
+ const boundsScroller = document.getElementById('bounds-scroller');
+ boundsScroller.scrollTop = 100;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(target, anchor, 'top');
+}, 'Target returns to above anchor after bounds are scrolled upwards');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-005.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-005.html
new file mode 100644
index 0000000000..deb546a1e2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-005.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<title>Tests relayout after 'position-fallback-bounds' change</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-common.js"></script>
+
+<style>
+.bounds {
+ position: absolute;
+ left: 100px;
+ width: 200px;
+ height: 200px;
+ background: gray;
+ opacity: 0.1;
+}
+
+#bounds1 {
+ top: 0;
+ anchor-name: --bounds1;
+}
+
+#bounds2 {
+ top: 300px;
+ anchor-name: --bounds2;
+}
+
+#anchor {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ background: orange;
+ left: 150px;
+ top: 200px;
+ anchor-name: --a;
+}
+
+#target {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background: lime;
+ left: anchor(left);
+ anchor-default: --a;
+ position-fallback: --top-then-bottom;
+ position-fallback-bounds: --bounds1;
+}
+
+@position-fallback --top-then-bottom {
+ @try { bottom: anchor(top); }
+ @try { top: anchor(bottom); }
+}
+</style>
+
+<div class="bounds" id="bounds1"></div>
+<div class="bounds" id="bounds2"></div>
+<div id="anchor"></div>
+<div id="target"></div>
+
+<script>
+test(() => {
+ assert_fallback_position(target, anchor, 'top');
+}, 'Initial layout');
+
+test(() => {
+ target.style = 'position-fallback-bounds: --bounds2';
+ assert_fallback_position(target, anchor, 'bottom');
+}, 'Layout is updated after position-fallback-bounds property changes');
+
+test(() => {
+ bounds2.style = 'top: 0; height: 500px';
+ assert_fallback_position(target, anchor, 'top');
+}, 'Layout is updated after additional fallback-bounds rect changes');
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-006.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-006.html
new file mode 100644
index 0000000000..5a19c59f31
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-006.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<title>Tests 'position-fallback-bounds' should work without default anchor</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+ margin: 0;
+}
+
+#bounds {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 100px;
+ height: calc(100vh + 200px);
+ background: lightgray;
+ anchor-name: --bounds;
+}
+
+#anchor {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ left: 0;
+ top: 100px;
+ background: orange;
+ anchor-name: --a;
+}
+
+#target {
+ position: fixed;
+ width: 100px;
+ height: 100px;
+ background: lime;
+ position-fallback: --top-then-bottom;
+ position-fallback-bounds: --bounds;
+}
+
+@position-fallback --top-then-bottom {
+ @try { bottom: anchor(--a top); }
+ @try { top: anchor(--a bottom); }
+}
+</style>
+
+<div id=bounds></div>
+<div id=anchor></div>
+<div id=target></div>
+
+<script>
+promise_test(async () => {
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(target, anchor, 'bottom');
+}, "Target is below anchor at initial scroll position");
+
+promise_test(async () => {
+ document.documentElement.scrollTop = 100;
+ await waitUntilNextAnimationFrame();
+ assert_fallback_position(target, anchor, 'top');
+}, "Target moves to above anchor after the additional fallback-bounds rect is scrolled upwards");
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-basics.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-basics.html
new file mode 100644
index 0000000000..3b8b67bd97
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-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-position-1/#propdef-position-fallback-bounds">
+<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-bounds: normal | <dashed-ident>
+test_valid_value('position-fallback-bounds', 'normal');
+test_valid_value('position-fallback-bounds', '--foo');
+test_invalid_value('position-fallback-bounds', 'foo-bar');
+test_invalid_value('position-fallback-bounds', '--foo --bar')
+test_invalid_value('position-fallback-bounds', '--foo, --bar')
+test_invalid_value('position-fallback-bounds', '100px');
+test_invalid_value('position-fallback-bounds', '100%');
+
+// Computed value: as specified
+test_computed_value('position-fallback-bounds', 'normal');
+test_computed_value('position-fallback-bounds', '--foo');
+
+// Initial: normal
+// Inherited: no
+assert_not_inherited('position-fallback-bounds', 'normal', '--foo');
+
+// Animation type: discrete
+test_no_interpolation({
+ property: 'position-fallback-bounds',
+ from: '--foo',
+ to: 'normal',
+});
+</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-container-query.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-container-query.html
new file mode 100644
index 0000000000..b4ef806206
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-container-query.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<title>@position-fallback with container query responding to fallback widths</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #relative {
+ background: maroon;
+ position: relative;
+ width: 195px;
+ height: 200px;
+ }
+ #target1 {
+ container-type: inline-size;
+ container-name: target1;
+ position-fallback: --fallback1;
+ background: green;
+ position: absolute;
+ top: 0px;
+ left: 100px;
+ width: 100px;
+ height: 100px;
+ }
+ @position-fallback --fallback1 {
+ @try {
+ top: 100px;
+ left: 0px;
+ width: 150px;
+ }
+ }
+ @container (width > 100px) {
+ #inner1 {
+ background-color: lime;
+ width: 100px;
+ height: 100px;
+ }
+ }
+
+ #target2 {
+ container-type: inline-size;
+ container-name: target2;
+ position-fallback: --fallback2;
+ background: orange;
+ position: absolute;
+ top: 0px;
+ left: 100px;
+ width: 100px;
+ }
+ @position-fallback --fallback2 {
+ @try {
+ top: 100px;
+ left: 0px;
+ width: 150px;
+ }
+ @try {
+ top: 0px;
+ left: 0px;
+ width: 150px;
+ }
+ }
+ @container target2 (width = 150px) {
+ #inner2 {
+ background-color: yellow;
+ width: 100px;
+ height: 150px;
+ }
+ }
+</style>
+<div id="relative">
+ <div id="target1">
+ <div id="inner1"></div>
+ </div>
+ <div id="target2">
+ <div id="inner2"></div>
+ </div>
+</div>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(inner1).backgroundColor, "rgb(0, 255, 0)");
+ assert_equals(getComputedStyle(inner1).width, "100px");
+ assert_equals(getComputedStyle(inner1).height, "100px");
+ assert_equals(getComputedStyle(target1).top, "100px");
+ assert_equals(getComputedStyle(target1).left, "0px");
+ assert_equals(getComputedStyle(target1).width, "150px");
+ }, "Size container query responds to fallback width");
+
+ test(() => {
+ assert_equals(getComputedStyle(inner2).backgroundColor, "rgb(255, 255, 0)");
+ assert_equals(getComputedStyle(inner2).width, "100px");
+ assert_equals(getComputedStyle(inner2).height, "150px");
+ assert_equals(getComputedStyle(target2).top, "0px");
+ assert_equals(getComputedStyle(target2).left, "0px");
+ assert_equals(getComputedStyle(target2).width, "150px");
+ }, "Size container query responds to fallback width and applies height to not fit the first fallback");
+</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..edb7efc7ed
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-custom-property.html
@@ -0,0 +1,69 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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..abe80bf51e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-grid-001.html
@@ -0,0 +1,93 @@
+<!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>
+<script src="support/test-common.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="checkLayoutForAnchorPos('.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-pseudo-element.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-pseudo-element.html
new file mode 100644
index 0000000000..e2f95b9a24
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-pseudo-element.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<title>@position-fallback for ::before and ::after pseudo elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #container {
+ background: maroon;
+ position: relative;
+ width: 195px;
+ height: 200px;
+ }
+ #target::before {
+ position-fallback: --fallback;
+ background: lime;
+ position: absolute;
+ left: 200px;
+ top: 200px;
+ width: 100px;
+ height: 200px;
+ content: "";
+ }
+ #target::after {
+ position-fallback: --fallback;
+ background: green;
+ position: absolute;
+ left: 100px;
+ width: 100px;
+ height: 100px;
+ content: "";
+ }
+ @position-fallback --fallback {
+ @try {
+ top: 100px;
+ left: 50px;
+ }
+ @try {
+ top: 0px;
+ left: 0px;
+ }
+ }
+</style>
+<div id="container">
+ <div id="target"></div>
+</div>
+<script>
+ test(() => {
+ const before_style = getComputedStyle(target, "::before");
+ assert_equals(before_style.top, "0px");
+ assert_equals(before_style.left, "0px");
+ }, "::before using second fallback");
+ test(() => {
+ const after_style = getComputedStyle(target, "::after");
+ assert_equals(after_style.top, "100px");
+ assert_equals(after_style.left, "50px");
+ }, "::after using first fallback");
+</script>
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..2650a32f60
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/position-fallback-tree-scoped.html
@@ -0,0 +1,153 @@
+<!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>
+
+<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 shadowrootmode="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 shadowrootmode="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 shadowrootmode="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>
+ 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/property-interpolations.html b/testing/web-platform/tests/css/css-anchor-position/property-interpolations.html
new file mode 100644
index 0000000000..878f46375b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/property-interpolations.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Anchor Positioning Test: Interpolation of anchor related properties</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+<body>
+<script>
+ test_no_interpolation({
+ property: 'anchor-name',
+ from: 'none',
+ to: '--foo',
+ });
+ test_no_interpolation({
+ property: 'anchor-name',
+ from: '--foo',
+ to: '--bar',
+ });
+
+ test_no_interpolation({
+ property: 'anchor-default',
+ from: 'implicit',
+ to: '--foo',
+ });
+ test_no_interpolation({
+ property: 'anchor-default',
+ from: '--foo',
+ to: '--bar',
+ });
+
+ test_no_interpolation({
+ property: 'inset-area',
+ from: 'none',
+ to: 'center',
+ });
+ test_no_interpolation({
+ property: 'inset-area',
+ from: 'left',
+ to: 'right',
+ });
+
+ test_no_interpolation({
+ property: 'position-try-options',
+ from: 'none',
+ to: '--foo',
+ });
+ test_no_interpolation({
+ property: 'position-try-options',
+ from: 'none',
+ to: 'flip-block',
+ });
+ test_no_interpolation({
+ property: 'position-try-options',
+ from: 'flip-inline',
+ to: 'flip-block',
+ });
+ test_no_interpolation({
+ property: 'position-try-options',
+ from: '--foo',
+ to: '--bar',
+ });
+ test_no_interpolation({
+ property: 'position-try-options',
+ from: '--foo',
+ to: 'flip-block',
+ });
+
+ test_no_interpolation({
+ property: 'position-try-order',
+ from: 'normal',
+ to: 'most-width',
+ });
+ test_no_interpolation({
+ property: 'position-try-order',
+ from: 'most-width',
+ to: 'most-height',
+ });
+
+ test_no_interpolation({
+ property: 'position-fallback-bounds',
+ from: 'normal',
+ to: '--foo',
+ });
+ test_no_interpolation({
+ property: 'position-fallback-bounds',
+ from: '--foo',
+ to: '--bar',
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-composited-scrolling-006-ref.html b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-composited-scrolling-006-ref.html
new file mode 100644
index 0000000000..92fe187117
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-composited-scrolling-006-ref.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<style>
+body {
+ margin: 0;
+}
+#scroller {
+ width: 200px;
+ height: 100px;
+ overflow: scroll;
+ will-change: scroll-position;
+}
+#spacer {
+ height: 400px;
+}
+#anchor {
+ width: 100px;
+ height: 100px;
+ anchor-name: --a;
+}
+#overlap {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ top: 150px;
+ left: 0;
+ z-index: 100;
+ background: green;
+}
+</style>
+
+<div id="overlap"></div>
+<div id="scroller">
+ <div id="spacer"></div>
+ <div id="anchor"></div>
+</div>
+
+<script>
+scroller.scrollTop = 150;
+</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..a651114868
--- /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 positioned scrolling 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..06d4a60e23
--- /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 positioned scrolling: 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..01be0823e3
--- /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 positioned scrolling 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..2366a7dc09
--- /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 positioned scrolling 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
new file mode 100644
index 0000000000..e19a3ffddd
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/support/green-16x16.png
Binary files differ
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..d5cf1861c1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/support/test-common.js
@@ -0,0 +1,44 @@
+// 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));
+}
+
+// This function is a thin wrapper around `checkLayout` (from
+// resources/check-layout-th.js) and simply reads the `CHECK_LAYOUT_DELAY`
+// variable to optionally add a delay. This global variable is not intended
+// to be set by other tests; instead, polyfills can set it to give themselves
+// time to apply changes before proceeding with assertions about the layout.
+// Tests that call this function and then do additional work after the call
+// should `await` it to avoid race conditions.
+window.checkLayoutForAnchorPos = async function(selectorList, callDone = true) {
+ if (window.CHECK_LAYOUT_DELAY) {
+ assert_equals(window.INJECTED_SCRIPT,undefined,'CHECK_LAYOUT_DELAY is only allowed when serving WPT with --injected-script.');
+ await waitUntilNextAnimationFrame();
+ await waitUntilNextAnimationFrame();
+ await waitUntilNextAnimationFrame();
+ }
+ return window.checkLayout(selectorList, callDone);
+}