summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/css-anchor-position/anchor-invalid-fallback.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/css/css-anchor-position/anchor-invalid-fallback.html')
-rw-r--r--testing/web-platform/tests/css/css-anchor-position/anchor-invalid-fallback.html234
1 files changed, 234 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-invalid-fallback.html b/testing/web-platform/tests/css/css-anchor-position/anchor-invalid-fallback.html
new file mode 100644
index 0000000000..4768beac62
--- /dev/null
+++ b/testing/web-platform/tests/css/css-anchor-position/anchor-invalid-fallback.html
@@ -0,0 +1,234 @@
+<!DOCTYPE html>
+<title>CSS Anchor Position Test: invalid at computed-value time</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#anchor-valid">
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#anchor-size-valid">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ :root {
+ --top: top;
+ }
+ #cb {
+ position: relative;
+ width: 200px;
+ height: 200px;
+ border: 1px solid black;
+ }
+
+ #anchor {
+ anchor-name: --a;
+ position: absolute;
+ width: 50px;
+ height: 40px;
+ left: 75px;
+ top: 75px;
+ background: coral;
+ }
+
+ #main > div, #ref {
+ position: absolute;
+ background: seagreen;
+ }
+
+ #ref {
+ inset: unset;
+ width: unset;
+ height: unset;
+ min-width: unset;
+ min-height: unset;
+ max-width: unset;
+ max-height: unset;
+ }
+
+</style>
+<div id=cb>
+ <div id=anchor></div>
+ <div id=main></div>
+ <div id=ref>X</div>
+</div>
+<script>
+
+// Append <div>X</div> to `container`, and remove it again once the test (`t`)
+// is finished.
+function createTarget(t, container) {
+ t.add_cleanup(() => { container.replaceChildren(); });
+ let target = document.createElement('div');
+ target.textContent = 'X';
+ container.append(target);
+ return target;
+}
+
+// First, some sanity checks to verify that the anchor etc is set up correctly,
+// and that anchor() queries can produce results if done correctly.
+
+test((t) => {
+ let target = createTarget(t, main);
+ target.style = `
+ position-anchor: --a;
+ left:anchor(right);
+ top:anchor(top);
+ width:anchor-size(width);
+ height:anchor-size(height);
+ `;
+ let cs = getComputedStyle(target);
+ assert_equals(cs.left, '125px');
+ assert_equals(cs.top, '75px');
+ assert_equals(cs.width, '50px');
+ assert_equals(cs.height, '40px');
+}, 'Element can be anchor positioned');
+
+test((t) => {
+ let target = createTarget(t, main);
+ target.style = `
+ /* No position-anchor here */
+ left:anchor(right, 17px);
+ top:anchor(top, 18px);
+ width:anchor-size(width, 42px);
+ height:anchor-size(height, 43px);
+ `;
+ let cs = getComputedStyle(target);
+ assert_equals(cs.left, '17px');
+ assert_equals(cs.top, '18px');
+ assert_equals(cs.width, '42px');
+ assert_equals(cs.height, '43px');
+}, 'Element can use <length> fallback if present');
+
+// Now test that any invalid anchor*() behaves as invalid at computed-value
+// time if there's no fallback specified.
+
+// Check that an anchored element with the specified style has the same
+// computed insets and sizing as the reference element (#ref), i.e. all
+// insets and sizing properties behave as 'unset'.
+function test_ref(style, description) {
+ test((t) => {
+ let target = createTarget(t, main);
+ target.style = style;
+ let cs = getComputedStyle(target);
+ let ref_cs = getComputedStyle(ref);
+ assert_equals(cs.top, ref_cs.top, 'top');
+ assert_equals(cs.left, ref_cs.left, 'left');
+ assert_equals(cs.right, ref_cs.right, 'right');
+ assert_equals(cs.bottom, ref_cs.bottom, 'bottom');
+ assert_equals(cs.width, ref_cs.width, 'width');
+ assert_equals(cs.height, ref_cs.height, 'height');
+ assert_equals(cs.minWidth, ref_cs.minWidth, 'minWidth');
+ assert_equals(cs.minHeight, ref_cs.minHeight, 'minHeight');
+ assert_equals(cs.maxWidth, ref_cs.maxWidth, 'maxWidth');
+ assert_equals(cs.maxHeight, ref_cs.maxHeight, 'maxHeight');
+ }, `Invalid anchor function, ${description}`);
+}
+
+// No default anchor (position-anchor):
+test_ref('left:anchor(left)', 'left');
+test_ref('right:anchor(right)', 'right');
+test_ref('bottom:anchor(bottom)', 'bottom');
+test_ref('top:anchor(top)', 'top');
+test_ref('width:anchor-size(width)', 'width');
+test_ref('height:anchor-size(height)', 'height');
+test_ref('min-width:anchor-size(width)', 'min-width');
+test_ref('min-height:anchor-size(height)', 'min-height');
+test_ref('max-width:anchor-size(width)', 'max-width');
+test_ref('max-height:anchor-size(height)', 'max-height');
+
+// Unknown anchor reference:
+test_ref('left:anchor(--unknown left)', '--unknown left');
+test_ref('width:anchor-size(--unknown width)', '--unknown width');
+
+// Wrong axis;
+test_ref('left:anchor(--a top)', 'cross-axis query (vertical)');
+test_ref('top:anchor(--a left)', ' cross-axis query (horizontal)');
+
+// Wrong query for the given property:
+test_ref('top:anchor-size(--a width)', 'anchor-size() in inset');
+test_ref('width:anchor(--a left)', 'anchor() in sizing property');
+
+// Invalid anchor*() deeper within calc():
+test_ref('left:calc(anchor(left) + 10px)', 'nested left');
+test_ref('right:calc(anchor(right) + 10px)', 'nested right');
+test_ref('bottom:calc(anchor(bottom) + 10px)', 'nested bottom');
+test_ref('top:calc(anchor(top) + 10px)', 'nested top');
+test_ref('min-width:calc(anchor-size(width) + 10px)', 'nested min-width');
+test_ref('min-height:calc(anchor-size(height) + 10px)', 'nested min-height');
+test_ref('max-width:calc(anchor-size(width) + 10px)', 'nested max-width');
+test_ref('max-height:calc(anchor-size(height) + 10px)', 'nested max-height');
+
+// Invalid anchor*() within fallback:
+test_ref('top:anchor(top, anchor(--unknown top))', 'invalid anchor() in fallback');
+test_ref('width:anchor-size(width, anchor-size(--unknown width))', 'invalid anchor-size() in fallback');
+
+// Non-calc() functions:
+test_ref('top:min(10px, anchor(top))', 'min()');
+test_ref('top:max(10px, anchor(top))', 'max()');
+test_ref('top:abs(anchor(top) - 100px)', 'abs()');
+test_ref('top:calc(sign(anchor(top) - 100px) * 20px)', 'sign()');
+
+// var():
+test_ref('top:anchor(var(--top))', 'anchor(var())');
+test_ref('top:anchor(var(--unknown, top))', 'anchor(unknown var()) (fallback)');
+test_ref('top:anchor(var(--unknown))', 'anchor(unknown var()) (no fallback)');
+
+// Reverting to an invalid anchor():
+test((t) => {
+ let target = createTarget(t, main);
+ target.setAttribute('id', 'target');
+
+ let css = document.createElement('style');
+ css.textContent = `
+ @layer base {
+ #target {
+ top: anchor(top); /* Invalid */
+ color: green;
+ }
+ }
+ #target {
+ top: revert-layer; /* Reverts to 'base'. */
+ }
+ `;
+
+ t.add_cleanup(() => { css.remove(); })
+ cb.append(css);
+
+ let cs = getComputedStyle(target);
+ let ref_cs = getComputedStyle(ref);
+ // The color check verifies that the rule is applied at all.
+ assert_equals(cs.color, 'rgb(0, 128, 0)');
+ assert_equals(cs.top, ref_cs.top);
+}, 'Revert to invalid anchor()');
+
+// Using <try-tactic> to flip to an invalid anchor():
+test((t) => {
+ let target = createTarget(t, main);
+ target.setAttribute('id', 'target');
+
+ let css = document.createElement('style');
+ css.textContent = `
+ @position-try --pt {
+ /* Undo force overflow, and also use this value to check that
+ the rule is applied at all. */
+ left: 10px;
+
+ /* Invalid. Becomes bottom:anchor(bottom) (also invalid)
+ after flip-block. */
+ top: anchor(top);
+ }
+
+ #target {
+ left: 9999px; /* Force overflow. */
+ position-try-options: --pt flip-block;
+ }
+ `;
+
+ t.add_cleanup(() => { css.remove(); })
+ cb.append(css);
+
+ let cs = getComputedStyle(target);
+ let ref_cs = getComputedStyle(ref);
+ assert_equals(cs.left, '10px', 'left');
+ // 'right' is not important in this test.
+
+ assert_equals(cs.top, ref_cs.top, 'top');
+ assert_equals(cs.bottom, ref_cs.bottom, 'bottom');
+}, 'Flip to invalid anchor()');
+
+</script>
+