tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

anchor-invalid-fallback.html (8655B)


      1 <!DOCTYPE html>
      2 <title>CSS Anchor Position Test: invalid at computed-value time</title>
      3 <link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#anchor-valid">
      4 <link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#anchor-size-valid">
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <style>
      8  :root {
      9    --top: top;
     10  }
     11  #cb {
     12    position: relative;
     13    width: 200px;
     14    height: 200px;
     15    border: 1px solid black;
     16  }
     17 
     18  #anchor {
     19    anchor-name: --a;
     20    position: absolute;
     21    width: 50px;
     22    height: 40px;
     23    left: 75px;
     24    top: 75px;
     25    background: coral;
     26  }
     27 
     28  #main > div, #ref {
     29    position: absolute;
     30    background: seagreen;
     31  }
     32 
     33  #ref {
     34    inset: unset;
     35    width: unset;
     36    height: unset;
     37    min-width: unset;
     38    min-height: unset;
     39    max-width: unset;
     40    max-height: unset;
     41  }
     42 
     43 </style>
     44 <div id=cb>
     45  <div id=anchor></div>
     46  <div id=main></div>
     47  <div id=ref>X</div>
     48 </div>
     49 <script>
     50 
     51 // Append <div>X</div> to `container`, and remove it again once the test (`t`)
     52 // is finished.
     53 function createTarget(t, container) {
     54  t.add_cleanup(() => { container.replaceChildren(); });
     55  let target = document.createElement('div');
     56  target.textContent = 'X';
     57  container.append(target);
     58  return target;
     59 }
     60 
     61 // First, some sanity checks to verify that the anchor etc is set up correctly,
     62 // and that anchor() queries can produce results if done correctly.
     63 
     64 test((t) => {
     65  let target = createTarget(t, main);
     66  target.style = `
     67    position-anchor: --a;
     68    left:anchor(right);
     69    top:anchor(top);
     70    width:anchor-size(width);
     71    height:anchor-size(height);
     72  `;
     73  let cs = getComputedStyle(target);
     74  assert_equals(cs.left, '125px');
     75  assert_equals(cs.top, '75px');
     76  assert_equals(cs.width, '50px');
     77  assert_equals(cs.height, '40px');
     78 }, 'Element can be anchor positioned');
     79 
     80 test((t) => {
     81  let target = createTarget(t, main);
     82  target.style = `
     83    /* No position-anchor here */
     84    left:anchor(right, 17px);
     85    top:anchor(top, 18px);
     86    width:anchor-size(width, 42px);
     87    height:anchor-size(height, 43px);
     88  `;
     89  let cs = getComputedStyle(target);
     90  assert_equals(cs.left, '17px');
     91  assert_equals(cs.top, '18px');
     92  assert_equals(cs.width, '42px');
     93  assert_equals(cs.height, '43px');
     94 }, 'Element can use <length> fallback if present');
     95 
     96 test((t) => {
     97  let target = createTarget(t, main);
     98  target.style = `
     99    /* No position-anchor here */
    100    left:anchor(right, 8.5%);
    101    top:calc(8.5% + anchor(top, 1px));
    102    width:anchor-size(width, 21%);
    103    height:calc(21% + anchor-size(height, 1px));
    104  `;
    105  let cs = getComputedStyle(target);
    106  assert_equals(cs.left, '17px');
    107  assert_equals(cs.top, '18px');
    108  assert_equals(cs.width, '42px');
    109  assert_equals(cs.height, '43px');
    110 }, 'Element can use <length> fallback with percentage');
    111 
    112 test((t) => {
    113  let target = createTarget(t, main);
    114  target.style = `
    115    /* No position-anchor here */
    116    max-width:anchor-size(width, 28%);
    117    max-height:calc(28% + anchor-size(height, 1px));
    118  `;
    119  let cs = getComputedStyle(target);
    120  assert_equals(cs.maxWidth, '28%');
    121  assert_equals(cs.maxHeight, 'calc(28% + 1px)');
    122 }, 'Element can use <length> fallback for max size with percentage');
    123 
    124 test((t) => {
    125  let target = createTarget(t, main);
    126  target.style = `
    127    /* No position-anchor here */
    128    margin-left:anchor-size(width, 6%);
    129    margin-top:calc(6% + anchor-size(height, 1px));
    130  `;
    131  let cs = getComputedStyle(target);
    132  assert_equals(cs.marginLeft, '12px');
    133  assert_equals(cs.marginTop, '13px');
    134 }, 'Element can use <length> fallback for margin with percentage');
    135 
    136 // Now test that any invalid anchor*() behaves as invalid at computed-value
    137 // time if there's no fallback specified.
    138 
    139 // Check that an anchored element with the specified style has the same
    140 // computed insets and sizing as the reference element (#ref), i.e. all
    141 // insets and sizing properties behave as 'unset'.
    142 function test_ref(style, description) {
    143  test((t) => {
    144    let target = createTarget(t, main);
    145    target.style = style;
    146    let cs = getComputedStyle(target);
    147    let ref_cs = getComputedStyle(ref);
    148    assert_equals(cs.top, ref_cs.top, 'top');
    149    assert_equals(cs.left, ref_cs.left, 'left');
    150    assert_equals(cs.right, ref_cs.right, 'right');
    151    assert_equals(cs.bottom, ref_cs.bottom, 'bottom');
    152    assert_equals(cs.width, ref_cs.width, 'width');
    153    assert_equals(cs.height, ref_cs.height, 'height');
    154    assert_equals(cs.minWidth, ref_cs.minWidth, 'minWidth');
    155    assert_equals(cs.minHeight, ref_cs.minHeight, 'minHeight');
    156    assert_equals(cs.maxWidth, ref_cs.maxWidth, 'maxWidth');
    157    assert_equals(cs.maxHeight, ref_cs.maxHeight, 'maxHeight');
    158  }, `Invalid anchor function, ${description}`);
    159 }
    160 
    161 // No default anchor (position-anchor):
    162 test_ref('left:anchor(left)', 'left');
    163 test_ref('right:anchor(right)', 'right');
    164 test_ref('bottom:anchor(bottom)', 'bottom');
    165 test_ref('top:anchor(top)', 'top');
    166 test_ref('width:anchor-size(width)', 'width');
    167 test_ref('height:anchor-size(height)', 'height');
    168 test_ref('min-width:anchor-size(width)', 'min-width');
    169 test_ref('min-height:anchor-size(height)', 'min-height');
    170 test_ref('max-width:anchor-size(width)', 'max-width');
    171 test_ref('max-height:anchor-size(height)', 'max-height');
    172 
    173 // Unknown anchor reference:
    174 test_ref('left:anchor(--unknown left)', '--unknown left');
    175 test_ref('width:anchor-size(--unknown width)', '--unknown width');
    176 
    177 // Wrong axis;
    178 test_ref('left:anchor(--a top)', 'cross-axis query (vertical)');
    179 test_ref('top:anchor(--a left)', ' cross-axis query (horizontal)');
    180 
    181 // Wrong query for the given property:
    182 test_ref('width:anchor(--a left)', 'anchor() in sizing property');
    183 
    184 // Invalid anchor*() deeper within calc():
    185 test_ref('left:calc(anchor(left) + 10px)', 'nested left');
    186 test_ref('right:calc(anchor(right) + 10px)', 'nested right');
    187 test_ref('bottom:calc(anchor(bottom) + 10px)', 'nested bottom');
    188 test_ref('top:calc(anchor(top) + 10px)', 'nested top');
    189 test_ref('min-width:calc(anchor-size(width) + 10px)', 'nested min-width');
    190 test_ref('min-height:calc(anchor-size(height) + 10px)', 'nested min-height');
    191 test_ref('max-width:calc(anchor-size(width) + 10px)', 'nested max-width');
    192 test_ref('max-height:calc(anchor-size(height) + 10px)', 'nested max-height');
    193 
    194 // Invalid anchor*() within fallback:
    195 test_ref('top:anchor(top, anchor(--unknown top))', 'invalid anchor() in fallback');
    196 test_ref('width:anchor-size(width, anchor-size(--unknown width))', 'invalid anchor-size() in fallback');
    197 
    198 // Non-calc() functions:
    199 test_ref('top:min(10px, anchor(top))', 'min()');
    200 test_ref('top:max(10px, anchor(top))', 'max()');
    201 test_ref('top:abs(anchor(top) - 100px)', 'abs()');
    202 test_ref('top:calc(sign(anchor(top) - 100px) * 20px)', 'sign()');
    203 
    204 // var():
    205 test_ref('top:anchor(var(--top))', 'anchor(var())');
    206 test_ref('top:anchor(var(--unknown, top))', 'anchor(unknown var()) (fallback)');
    207 test_ref('top:anchor(var(--unknown))', 'anchor(unknown var()) (no fallback)');
    208 
    209 // Reverting to an invalid anchor():
    210 test((t) => {
    211  let target = createTarget(t, main);
    212  target.setAttribute('id', 'target');
    213 
    214  let css = document.createElement('style');
    215  css.textContent = `
    216    @layer base {
    217      #target {
    218        top: anchor(top); /* Invalid */
    219        color: green;
    220      }
    221    }
    222    #target {
    223      top: revert-layer; /* Reverts to 'base'. */
    224    }
    225  `;
    226 
    227  t.add_cleanup(() => { css.remove(); })
    228  cb.append(css);
    229 
    230  let cs = getComputedStyle(target);
    231  let ref_cs = getComputedStyle(ref);
    232  // The color check verifies that the rule is applied at all.
    233  assert_equals(cs.color, 'rgb(0, 128, 0)');
    234  assert_equals(cs.top, ref_cs.top);
    235 }, 'Revert to invalid anchor()');
    236 
    237 // Using <try-tactic> to flip to an invalid anchor():
    238 test((t) => {
    239  let target = createTarget(t, main);
    240  target.setAttribute('id', 'target');
    241 
    242  let css = document.createElement('style');
    243  css.textContent = `
    244    @position-try --pt {
    245      /* Undo force overflow, and also use this value to check that
    246         the rule is applied at all. */
    247      left: 10px;
    248 
    249      /* Invalid. Becomes bottom:anchor(bottom) (also invalid)
    250         after flip-block. */
    251      top: anchor(top);
    252    }
    253 
    254    #target {
    255      left: 9999px; /* Force overflow. */
    256      position-try-fallbacks: --pt flip-block;
    257    }
    258  `;
    259 
    260  t.add_cleanup(() => { css.remove(); })
    261  cb.append(css);
    262 
    263  let cs = getComputedStyle(target);
    264  let ref_cs = getComputedStyle(ref);
    265  assert_equals(cs.left, '10px', 'left');
    266  // 'right' is not important in this test.
    267 
    268  assert_equals(cs.top, ref_cs.top, 'top');
    269  assert_equals(cs.bottom, ref_cs.bottom, 'bottom');
    270 }, 'Flip to invalid anchor()');
    271 
    272 </script>