tor-browser

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

headingoffset-and-headingreset.html (12848B)


      1 <!doctype html>
      2 <meta charset="utf-8" />
      3 <meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" />
      4 <link rel="help" href="https://github.com/whatwg/html/pull/11086" />
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src="resources/invoker-utils.js"></script>
      8 
      9 <div headingoffset="1" title="container headingoffset=1">
     10  <!-- h1s are now h2s and so on -->
     11  <h1 data-expected-offset="2"><!-- Level 2, h1 + 1 = 2 --></h1>
     12  <h2 data-expected-offset="3"><!-- Level 3, h2 + 1 = 3 --></h2>
     13  <h3 data-expected-offset="4"><!-- Level 4, h3 + 1 = 4 --></h3>
     14  <div headingoffset="2" title="container headingoffset=2">
     15    <!-- h1s are now h4s -->
     16    <h1 data-expected-offset="4"><!-- Level 4, h1 + 2 + 1 = 4 --></h1>
     17    <h2 data-expected-offset="5"><!-- Level 5, h2 + 2 + 1 = 5 --></h2>
     18    <div headingreset title="container headingreset">
     19      <!-- h1s are now h1s -->
     20      <h1 data-expected-offset="1"><!-- Level 1, h1 (headingreset)--></h1>
     21    </div>
     22    <dialog open title="container dialog">
     23      <!-- non-modal dialogs do not headingreset, h1s are still h4s -->
     24      <h1 data-expected-offset="4"><!-- Level 4, h1 + 2 + 1 = 4 --></h1>
     25      <h1 data-expected-offset="1" headingreset>
     26        <!-- Level 1, h1 (headingreset) -->
     27      </h1>
     28    </dialog>
     29  </div>
     30 </div>
     31 <!-- Clamping -->
     32 <div headingoffset="8" title="container headingoffset=8">
     33  <!-- h1s are now h9s -->
     34  <h1 data-expected-offset="9"><!-- Level 9, h1 + 8 --></h1>
     35  <h2 data-expected-offset="9"><!-- Level 9, h2 + 8 (clamped) --></h2>
     36  <h3 data-expected-offset="9"><!-- Level 9, h3 + 8 (clamped) --></h3>
     37  <h4 data-expected-offset="9"><!-- Level 9, h4 + 8 (clamped) --></h4>
     38  <h5 data-expected-offset="9"><!-- Level 9, h5 + 8 (clamped) --></h5>
     39  <h6 data-expected-offset="9"><!-- Level 9, h6 + 8 (clamped) --></h6>
     40  <div headingreset title="container headingreset">
     41    <!-- h1s are now h1s -->
     42    <h1 data-expected-offset="1"><!-- Level 1, h1 (headingreset)--></h1>
     43  </div>
     44  <dialog open title="container dialog">
     45    <!-- non-modal dialogs do not headingreset, h1s are still h4s -->
     46    <h1 data-expected-offset="9"><!-- Level 9, h1 + 8 --></h1>
     47  </dialog>
     48 </div>
     49 <!-- Negative headingoffsets are clamped to `0` -->
     50 <div headingoffset="-3" title="container headingoffset=-3">
     51  <h1 data-expected-offset="1"><!-- Level 1, h1 + (-3 clamped to 0) --></h1>
     52  <h2 data-expected-offset="2"><!-- Level 2, h2 + (-3 clamped to 0) --></h2>
     53  <h3 data-expected-offset="3"><!-- Level 3, h3 + (-3 clamped to 0) --></h3>
     54  <h4 data-expected-offset="4"><!-- Level 4, h4 + (-3 clamped to 0) --></h4>
     55  <h5 data-expected-offset="5"><!-- Level 5, h5 + (-3 clamped to 0) --></h5>
     56  <h6 data-expected-offset="6"><!-- Level 6, h6 + (-3 clamped to 0) --></h6>
     57  <div headingreset title="container headingreset">
     58    <!-- h1s are now h1s -->
     59    <h1 data-expected-offset="1"><!-- Level 1, h1 (headingreset)--></h1>
     60  </div>
     61  <dialog open title="container dialog">
     62    <!-- non-modal dialogs do not headingreset, h1s are still h1s -->
     63    <h1 data-expected-offset="1"><!-- Level 1, h1 + (-3 clamped to 0) --></h1>
     64  </dialog>
     65 </div>
     66 <!-- Resetting applies after headingOffset -->
     67 <div headingreset title="container headingreset + headingoffset">
     68  <div headingoffset="2" headingreset>
     69    <div headingoffset="2">
     70      <h1 data-expected-offset="5"><!-- Level 5, h1 + 2 + 2 = 5 --></h1>
     71      <h2 data-expected-offset="6"><!-- Level 6, h2 + 2 + 2 = 6 --></h2>
     72      <h3 data-expected-offset="7"><!-- Level 7, h3 + 2 + 2 = 7 --></h3>
     73      <h4 data-expected-offset="8"><!-- Level 8, h4 + 2 + 2 = 8 --></h4>
     74      <h5 data-expected-offset="9"><!-- Level 9, h5 + 2 + 2 = 9 --></h5>
     75      <h6 data-expected-offset="9">
     76        <!-- Level 9, h6 + 2 + 2 = (10 clamped to 9) -->
     77      </h6>
     78      <h1 data-expected-offset="3" headingoffset="2" headingreset>
     79        <!-- Level 3, h1 + 2 + (headingreset) -->
     80      </h1>
     81      <h2 data-expected-offset="4" headingoffset="2" headingreset>
     82        <!-- Level 4, h2 + 2 + (headingreset) -->
     83      </h2>
     84    </div>
     85  </div>
     86 </div>
     87 <!-- Ensure shadow roots work -->
     88 <div headingoffset="1" title="container shadowroot headingoffset=1">
     89  <template shadowrootmode="open">
     90    <h1 data-expected-offset="2"><!-- Level 2, h1 + 1 --></h1>
     91    <h2 data-expected-offset="2" headingreset>
     92      <!-- Level 2, h2 (headingreset) -->
     93    </h2>
     94  </template>
     95 </div>
     96 <!-- Ensure slotted elements are correctly set -->
     97 <div headingoffset="1" title="container shadowroot slotted headingoffset=1">
     98  <template shadowrootmode="open">
     99    <h1 data-expected-offset="2"><!-- Level 2, h1 + 1 --></h1>
    100    <h2 data-expected-offset="3"><!-- Level 3, h2 + 1 --></h2>
    101    <h3 data-expected-offset="3" headingreset>
    102      <!-- Level 3, h3 (headingreset) -->
    103    </h3>
    104    <slot></slot>
    105  </template>
    106  <h1><!-- Level 2, h1 + 1 --></h1>
    107 </div>
    108 <!-- Ensure slotted elements respect their parents -->
    109 <div
    110  headingoffset="1"
    111  title="container shadowroot slotted with container headingoffset=1"
    112 >
    113  <template shadowrootmode="open">
    114    <h1 data-expected-offset="2"><!-- Level 2, h1 + 1 --></h1>
    115    <div headingoffset="1" title="container inside shadowroot headingoffset=1">
    116      <slot></slot>
    117    </div>
    118  </template>
    119  <h2 data-expected-offset="4"><!-- Level 4, h2 + 1 + 1 --></h2>
    120  <h4 data-expected-offset="6"><!-- Level 6, h4 + 1 + 1 --></h4>
    121  <h4 data-expected-offset="4" headingreset>
    122    <!-- Level 4, h4 (headingreset) -->
    123  </h4>
    124 </div>
    125 <!-- Ensure the slot can be decorated with headingoffset -->
    126 <div
    127  headingoffset="1"
    128  title="container shadowroot slot with attr headingoffset=1"
    129 >
    130  <template shadowrootmode="open">
    131    <h1 data-expected-offset="2"><!-- Level 2, h1 + 1 --></h1>
    132    <slot headingoffset="1"></slot>
    133  </template>
    134  <h2 data-expected-offset="4"><!-- Level 4, h2 + 1 + 1 --></h2>
    135 </div>
    136 <h1 data-expected-offset="2" headingoffset="1"><!-- Level 2, h1 + 1 --></h1>
    137 <h2 data-expected-offset="3" headingoffset="1"><!-- Level 3, h2 + 1 --></h2>
    138 <h1 data-expected-offset="3" headingoffset="2"><!-- Level 3, h1 + 2--></h1>
    139 <h2 data-expected-offset="4" headingoffset="2"><!-- Level 4, h2 + 2 --></h2>
    140 <h1 data-expected-offset="2" headingoffset="1" headingreset>
    141  <!-- Level 2, h1 + 1 (headingreset) -->
    142 </h1>
    143 <h2 data-expected-offset="3" headingoffset="1" headingreset>
    144  <!-- Level 3, h2 + 1 (headingreset) -->
    145 </h2>
    146 <h1 data-expected-offset="3" headingoffset="2" headingreset>
    147  <!-- Level 3, h1 + 2 (headingreset) -->
    148 </h1>
    149 <h2 data-expected-offset="4" headingoffset="2" headingreset>
    150  <!-- Level 4, h2 + 2 (headingreset) -->
    151 </h2>
    152 <h1 data-expected-offset="9" headingoffset="20" headingreset>
    153  <!-- Level 9, h1 + 20 (clamped)  -->
    154 </h1>
    155 <h2 data-expected-offset="9" headingoffset="20" headingreset>
    156  <!-- Level 9, h2 + 20 (clamped) -->
    157 </h2>
    158 <h1 data-expected-offset="1" headingoffset="0" headingreset>
    159  <!-- Level 1, h1 + 0 -->
    160 </h1>
    161 <h2 data-expected-offset="2" headingoffset="0" headingreset>
    162  <!-- Level 2, h2 + 0 -->
    163 </h2>
    164 <div title="container with no attr">
    165  <h1 data-expected-offset="2" headingoffset="1"><!-- Level 2, h1 + 1 --></h1>
    166  <h2 data-expected-offset="3" headingoffset="1"><!-- Level 3, h2 + 1 --></h2>
    167  <h1 data-expected-offset="2" headingoffset="1" headingreset>
    168    <!-- Level 2, h1 + 1 -->
    169  </h1>
    170  <h2 data-expected-offset="3" headingoffset="1" headingreset>
    171    <!-- Level 3, h2 + 1 -->
    172  </h2>
    173  <h1 data-expected-offset="1" headingoffset="-1" headingreset>
    174    <!-- Level 1, h1 + (-1 clamped to 0) -->
    175  </h1>
    176  <h2 data-expected-offset="2" headingoffset="-1" headingreset>
    177    <!-- Level 2, h2 + (-1 clamped to 0) -->
    178  </h2>
    179 </div>
    180 <div headingreset title="many nested + and - values">
    181  <div headingoffset="-1">
    182    <div headingoffset="3">
    183      <div headingoffset="-6">
    184        <div headingoffset="1">
    185          <h1 data-expected-offset="5">
    186            <!-- Level 5, h1 + 1 + (-6 clamped to 0) + 3 + (-1 clamped to 0) -->
    187          </h1>
    188        </div>
    189      </div>
    190    </div>
    191  </div>
    192 </div>
    193 <h1 data-expected-offset="9" headingoffset="9" aria-level="3">
    194  <!-- Level 3 -->
    195 </h1>
    196 
    197 <div headingoffset="9" id="modalParent">
    198  <dialog id="modal">
    199    <h1></h1>
    200  </dialog>
    201 </div>
    202 
    203 <script>
    204  const attribute = (el, val) => el.setAttribute("headingoffset", val);
    205  const property = (el, val) => (el.headingOffset = val);
    206  const levels = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    207  const matchAllLevels = (el) =>
    208    levels.map((l) => el.matches(`:heading(${l})`));
    209 
    210  for (const fn of [attribute, property]) {
    211    test(function () {
    212      const el = document.createElement("h1");
    213      assert_true(el.matches(":heading"), `h1 should match :heading`);
    214      assert_true(el.matches(":heading(1)"), `h1 should match :heading(1)`);
    215      assert_equals(
    216        el.headingOffset,
    217        0,
    218        `h1 has an initial headingOffset of 0`,
    219      );
    220      fn(el, 3);
    221      assert_equals(el.headingOffset, 3, `h1 now has a headingOffset of 3`);
    222      assert_false(
    223        el.matches(":heading(1)"),
    224        `h1[headingoffset=3] should no longer match :heading(1)`,
    225      );
    226      assert_true(
    227        el.matches(":heading(4)"),
    228        `h1[headingoffset=3] should match :heading(4)`,
    229      );
    230    }, `headingoffset (set via ${fn.name}) should change the level a heading matches against`);
    231 
    232    test(function () {
    233      const parent = document.createElement("div");
    234      const el = document.createElement("h1");
    235      parent.append(el);
    236      assert_true(el.matches(":heading"), `h1 should match :heading`);
    237      assert_true(el.matches(":heading(1)"), `h1 should match :heading(1)`);
    238      assert_equals(
    239        parent.headingOffset,
    240        0,
    241        `parent has an initial headingOffset of 0`,
    242      );
    243      fn(parent, 3);
    244      assert_equals(parent.headingOffset, 3, `h1 now has a headingOffset of 3`);
    245      assert_false(
    246        el.matches(":heading(1)"),
    247        `h1[headingoffset=3] should no longer match :heading(1)`,
    248      );
    249      assert_true(
    250        el.matches(":heading(4)"),
    251        `div[headingoffset=3] h1 should match :heading(4)`,
    252      );
    253      const bools = matchAllLevels(el);
    254      const expected_bools = levels.map((l) => l == 4);
    255      assert_array_equals(
    256        bools,
    257        expected_bools,
    258        `${el.outerHTML} should match only the expected heading level`,
    259      );
    260      assert_false(el.headingReset, "h1 has an initial headingReset=false");
    261      el.headingReset = true;
    262      assert_true(el.headingReset, "h1 now has a headingReset of true");
    263      assert_false(
    264        el.matches(":heading(4)"),
    265        `div[headingoffset=3] h1[headingreset] should not match :heading(4)`,
    266      );
    267      assert_true(
    268        el.matches(":heading(1)"),
    269        `div[headingoffset=3] h1[headingreset] should match :heading(1)`,
    270      );
    271      const reset_bools = matchAllLevels(el);
    272      const reset_expected_bools = levels.map((l) => l == 1);
    273      assert_array_equals(
    274        reset_bools,
    275        reset_expected_bools,
    276        `${el.outerHTML} should match only the expected heading level`,
    277      );
    278    }, `headingoffset (set via ${fn.name}) should change the level when a parent changes offset`);
    279  }
    280 
    281  test(function (t) {
    282    const el = modal.querySelector("h1");
    283    assert_false(modal.headingReset, `modal dialog has not set heading reset`);
    284    modal.showModal();
    285    t.add_cleanup(() => modal.close());
    286 
    287    assert_true(modal.headingReset, `modal dialogs return headingReset true`);
    288    assert_true(el.matches(":heading"), `h1 inside modal should match :heading`);
    289    assert_true(el.matches(":heading(1)"), `h1 inside modal should match :heading(1)`);
    290    const bools = matchAllLevels(el);
    291    const expected_bools = levels.map((l) => l == 1);
    292    assert_array_equals(
    293      bools,
    294      expected_bools,
    295      `${el.outerHTML} should match only the expected heading level`,
    296    );
    297  }, `headingoffset should not impact modals or explicit headingreset containers`);
    298 
    299  let i = 0;
    300  for (const el of document.querySelectorAll("[data-expected-offset]")) {
    301    i += 1;
    302    test(function () {
    303      const expected = parseInt(el.getAttribute("data-expected-offset"));
    304      assert_true(
    305        expected > 0 && expected < 10,
    306        "expected offset should be a level from 1 to 9",
    307      );
    308      assert_true(
    309        el.matches(":heading"),
    310        `${el.outerHTML} should match :heading`,
    311      );
    312      const bools = matchAllLevels(el);
    313      const expected_bools = levels.map((l) => l == expected);
    314      assert_true(
    315        el.matches(`:heading(${expected})`),
    316        `${el.outerHTML} should match :heading(${expected})`,
    317      );
    318      assert_array_equals(
    319        bools,
    320        expected_bools,
    321        `${el.outerHTML} should match only the expected heading level`,
    322      );
    323    }, `case ${i}: heading level for ${el.outerHTML} should match based on expected document structure`);
    324  }
    325 </script>