tor-browser

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

dir-auto-dynamic-changes.window.js (15588B)


      1 // META: script=dir-shadow-utils.js
      2 
      3 test(t => {
      4  let a = setup_tree(`
      5    <div id="a" dir="auto">
      6      <div id="b"></div>
      7      hello
      8    </div>
      9  `);
     10 
     11  let acs = getComputedStyle(a);
     12  assert_true(a.matches(":dir(ltr)"), ":dir(ltr) matches before insertion");
     13  assert_false(a.matches(":dir(rtl)"), ":dir(rtl) does not match before insertion");
     14  assert_equals(acs.direction, "ltr", "CSSdirection before insertion");
     15  b.innerHTML = "\u05D0";
     16  assert_false(a.matches(":dir(ltr)"), ":dir(ltr) does not match after insertion");
     17  assert_true(a.matches(":dir(rtl)"), ":dir(rtl) matches after insertion");
     18  assert_equals(acs.direction, "rtl", "CSSdirection after insertion");
     19 
     20  a.remove();
     21 }, "dynamic insertion of RTL text in a child element");
     22 
     23 test(() => {
     24  let div_rtlchar = document.createElement("div");
     25  div_rtlchar.innerHTML = "\u05D0";
     26 
     27  let container1 = document.createElement("div");
     28  document.body.appendChild(container1);
     29  let container2 = document.createElement("div");
     30 
     31  for (let container of [container1, container2]) {
     32    container.dir = "auto";
     33    assert_true(container.matches(":dir(ltr)"));
     34    container.appendChild(div_rtlchar);
     35    assert_false(container.matches(":dir(ltr)"));
     36    div_rtlchar.remove();
     37    assert_true(container.matches(":dir(ltr)"));
     38  }
     39 
     40  container1.remove();
     41 }, "dir=auto changes for content insertion and removal, in and out of document");
     42 
     43 test(() => {
     44  let [tree, shadow] = setup_tree(`
     45    <div>
     46      <div id="root">
     47        <span id="l">A</span>
     48        <span id="r">\u05D0</span>
     49      </div>
     50    </div>
     51  `, `
     52    <slot id="one" name="one" dir="auto">\u05D0</slot>
     53    <slot id="two" dir="auto"></slot>
     54  `);
     55 
     56  let one = shadow.getElementById("one");
     57  let two = shadow.getElementById("two");
     58  let l = tree.querySelector("#l");
     59  let r = tree.querySelector("#r");
     60  assert_false(one.matches(":dir(ltr)"), "#one while empty");
     61  assert_true(two.matches(":dir(ltr)"), "#two with both spans");
     62  l.slot = "one";
     63  assert_true(one.matches(":dir(ltr)"), "#one with LTR child span");
     64  assert_false(two.matches(":dir(ltr)"), "#two with RTL child span");
     65  r.slot = "one";
     66  assert_true(one.matches(":dir(ltr)"), "#one with both child spans");
     67  assert_true(two.matches(":dir(ltr)"), "#two while empty");
     68  l.slot = "";
     69  assert_false(one.matches(":dir(ltr)"), "#one with RTL child span");
     70  assert_true(two.matches(":dir(ltr)"), "#two with LTR child span");
     71 
     72  tree.remove();
     73 }, "dir=auto changes for slot reassignment");
     74 
     75 test(() => {
     76  let [tree, shadow] = setup_tree(`
     77    <div dir=auto>
     78      <div id=root>
     79        <div id=text>A</div>
     80      </div>
     81    </div>
     82  `, `
     83    <div dir=ltr>
     84      <slot id=slot dir=auto></slot>
     85    </div>
     86  `);
     87 
     88  let text = tree.querySelector("#text");
     89  let slot = shadow.querySelector("#slot");
     90 
     91  assert_true(tree.matches(":dir(ltr)"), "node tree ancestor before first text change");
     92  assert_true(slot.matches(":dir(ltr)"), "slot before first text change");
     93  text.innerText = "\u05D0";
     94  assert_false(tree.matches(":dir(ltr)"), "node tree ancestor after first text change");
     95  assert_false(slot.matches(":dir(ltr)"), "slot after first text change");
     96  tree.dir = "rtl";
     97  assert_false(tree.matches(":dir(ltr)"), "node tree ancestor before second text change");
     98  assert_false(slot.matches(":dir(ltr)"), "slot before second text change");
     99  text.innerText = "A";
    100  assert_false(tree.matches(":dir(ltr)"), "node tree ancestor after second text change");
    101  assert_true(slot.matches(":dir(ltr)"), "slot after second text change");
    102  slot.dir = "ltr";
    103  assert_false(tree.matches(":dir(ltr)"), "node tree ancestor before third text change");
    104  assert_true(slot.matches(":dir(ltr)"), "slot before third text change");
    105  text.innerText = "\u05D0";
    106  assert_false(tree.matches(":dir(ltr)"), "node tree ancestor after third text change");
    107  assert_true(slot.matches(":dir(ltr)"), "slot after third text change");
    108  slot.dir = "auto";
    109  tree.dir = "auto";
    110  assert_false(tree.matches(":dir(ltr)"), "node tree ancestor after fourth text change");
    111  assert_false(slot.matches(":dir(ltr)"), "slot after fourth text change");
    112  text.innerText = "A";
    113  assert_true(tree.matches(":dir(ltr)"), "node tree ancestor before fourth text change");
    114  assert_true(slot.matches(":dir(ltr)"), "slot before fourth text change");
    115  slot.dir = "rtl";
    116  assert_true(tree.matches(":dir(ltr)"), "node tree ancestor before fifth text change");
    117  assert_false(slot.matches(":dir(ltr)"), "slot before fifth text change");
    118  text.innerText = "\u05D0";
    119  assert_false(tree.matches(":dir(ltr)"), "node tree ancestor before fifth text change");
    120  assert_false(slot.matches(":dir(ltr)"), "slot before fifth text change");
    121 
    122  tree.remove();
    123 }, "text changes affecting both slot and ancestor with dir=auto");
    124 
    125 test(() => {
    126  let tree = setup_tree(`
    127    <div dir="auto">
    128      <span id="a1">A</span>
    129      <span id="aleph1">\u05D0</span>
    130      <span id="a2">A</span>
    131      <span id="aleph2">\u05D0</span>
    132    </div>
    133  `);
    134 
    135  let a1 = tree.querySelector("#a1");
    136  let aleph1 = tree.querySelector("#aleph1");
    137  assert_true(tree.matches(":dir(ltr)"), "initial state");
    138  assert_false(tree.matches(":dir(rtl)"), "initial state");
    139  a1.dir = "ltr";
    140  assert_false(tree.matches(":dir(ltr)"), "after change 1");
    141  a1.dir = "invalid";
    142  assert_true(tree.matches(":dir(ltr)"), "after change 2");
    143  a1.dir = "rtl";
    144  assert_false(tree.matches(":dir(ltr)"), "after change 3");
    145  a1.removeAttribute("dir");
    146  assert_true(tree.matches(":dir(ltr)"), "after change 4");
    147  a1.dir = "invalid";
    148  assert_true(tree.matches(":dir(ltr)"), "after change 5");
    149  a1.dir = "rtl";
    150  assert_false(tree.matches(":dir(ltr)"), "after change 6");
    151  aleph1.dir = "auto";
    152  assert_true(tree.matches(":dir(ltr)"), "after change 7");
    153  aleph1.dir = "invalid";
    154  assert_false(tree.matches(":dir(ltr)"), "after change 8");
    155 
    156  tree.remove();
    157 }, "dynamic changes to subtrees excluded as a result of the dir attribute");
    158 
    159 test(() => {
    160  let tree = setup_tree(`
    161    <div dir="auto">
    162      <!-- element goes here -->
    163    </div>
    164  `);
    165 
    166  let element = document.createElementNS("namespace", "element");
    167  let text = document.createTextNode("\u05D0");
    168  element.appendChild(text);
    169  tree.prepend(element);
    170  assert_not_equals(element.namespaceURI, tree.namespaceURI);
    171 
    172  assert_true(tree.matches(":dir(rtl)"), "initial state");
    173  assert_false(tree.matches(":dir(ltr)"), "initial state");
    174  text.data = "A";
    175  assert_true(tree.matches(":dir(ltr)"), "after dynamic change");
    176  assert_false(tree.matches(":dir(rtl)"), "after dynamic change");
    177 
    178  tree.remove();
    179 }, "dynamic changes inside of non-HTML elements");
    180 
    181 test(() => {
    182  let [tree, shadow] = setup_tree(`
    183    <div dir="auto">
    184      <div id="root">
    185        <element xmlns="namespace">A</element>
    186        \u05D0
    187      </div>
    188    </div>
    189  `, `
    190    <div dir="ltr">
    191      <slot dir="auto">\u05D0</slot>
    192    </div>
    193  `);
    194 
    195  let element = tree.querySelector("element");
    196  let slot = shadow.querySelector("slot");
    197  let text = element.firstChild;
    198 
    199  assert_true(tree.matches(":dir(ltr)"), "initial state (tree)");
    200  assert_true(element.matches(":dir(ltr)"), "initial state (element)");
    201  assert_true(slot.matches(":dir(ltr)"), "initial state (slot)");
    202 
    203  text.data = "\u05D0";
    204 
    205  assert_true(tree.matches(":dir(rtl)"), "state after first change (tree)");
    206  assert_true(element.matches(":dir(rtl)"), "state after first change (element)");
    207  assert_true(slot.matches(":dir(rtl)"), "state after first change (slot)");
    208 
    209  text.data = "";
    210 
    211  assert_true(tree.matches(":dir(rtl)"), "state after second change (tree)");
    212  assert_true(element.matches(":dir(rtl)"), "state after second change (element)");
    213  assert_true(slot.matches(":dir(rtl)"), "state after second change (slot)");
    214 
    215  tree.remove();
    216 }, "slotted non-HTML elements");
    217 
    218 test(() => {
    219  let [tree, shadow] = setup_tree(`
    220    <div>
    221      <div id="root">
    222        <!-- element goes here -->
    223        \u05D0
    224      </div>
    225    </div>
    226  `, `
    227    <div dir="ltr">
    228      <slot></slot>
    229    </div>
    230  `);
    231 
    232  let element = document.createElementNS("namespace", "element");
    233  let text = document.createTextNode("A");
    234  element.appendChild(text);
    235  tree.querySelector("#root").prepend(element);
    236 
    237  assert_not_equals(element.namespaceURI, tree.namespaceURI);
    238 
    239  assert_true(tree.matches(":dir(ltr)"), "initial state (tree)");
    240  assert_true(element.matches(":dir(ltr)"), "initial state (element)");
    241 
    242  tree.dir = "auto";
    243 
    244  assert_true(tree.matches(":dir(ltr)"), "state after making dir=auto (tree)");
    245  assert_true(element.matches(":dir(ltr)"), "state after making dir=auto (element)");
    246 
    247  text.data = "\u05D0";
    248 
    249  assert_true(tree.matches(":dir(rtl)"), "state after first change (tree)");
    250  assert_true(element.matches(":dir(rtl)"), "state after first change (element)");
    251 
    252  text.data = "";
    253 
    254  assert_true(tree.matches(":dir(rtl)"), "state after second change (tree)");
    255  assert_true(element.matches(":dir(rtl)"), "state after second change (element)");
    256 
    257  tree.remove();
    258 }, "slotted non-HTML elements after dynamically assigning dir=auto, and dir attribute ignored on non-HTML elements");
    259 
    260 test(() => {
    261  let e1 = setup_tree(`
    262    <div dir=auto>
    263      <div dir=ltr>
    264        \u05D0
    265      </div>
    266    </div>
    267  `);
    268  let e2 = e1.firstElementChild;
    269  assert_true(e1.matches(":dir(ltr)"), "parent is LTR before changes");
    270  assert_true(e2.matches(":dir(ltr)"), "child is LTR before changes");
    271  e2.removeAttribute("dir");
    272  assert_false(e1.matches(":dir(ltr)"), "parent is RTL after removing dir from child");
    273  assert_false(e2.matches(":dir(ltr)"), "child is RTL after removing dir from child");
    274 }, "dir=auto ancestor considers text in subtree after removing dir=ltr from it");
    275 
    276 test(() => {
    277  let tree1, shadow1;
    278  [tree1, shadow1] = setup_tree(`
    279    <div>
    280      <div id="root" dir="auto">
    281        <div id="root2">
    282          <span>A</span>
    283        </div>
    284      </div>
    285    </div>
    286  `,`
    287    <slot dir="auto"></slot>
    288  `);
    289  let tree2 = tree1.querySelector("#root2");
    290  let shadow2 = tree2.attachShadow({mode: 'open'});
    291  shadow2.innerHTML = '<slot dir="auto"></slot>';
    292 
    293  let slot1 = shadow1.querySelector("slot");
    294  let slot2 = shadow2.querySelector("slot");
    295  let span = tree1.querySelector("span");
    296 
    297  // span slotted in slot2 hosted in root2 slotted in slot1
    298  // span thus impacts auto-dir of two slots
    299  assert_true(slot1.matches(":dir(ltr)", "outer slot initially ltr"));
    300  assert_true(slot2.matches(":dir(ltr)", "inner slot initially ltr"));
    301  span.innerHTML = "\u05D0";
    302  assert_true(slot1.matches(":dir(rtl)", "outer slot changed to rtl"));
    303  assert_true(slot2.matches(":dir(rtl)", "inner slot changed to rtl"));
    304 
    305  tree1.remove();
    306 }, 'Slotted content affects multiple dir=auto slots');
    307 
    308 test(() => {
    309  let [tree, shadow] = setup_tree(`
    310    <div>
    311      <div id="root">
    312        <span>اختبر</span>
    313      </div>
    314    </div>
    315  `, `
    316    <slot dir="auto"></slot>
    317  `);
    318 
    319  let slot = shadow.querySelector("slot");
    320  assert_equals(html_direction(slot), "rtl", "slot initially rtl");
    321  let span = tree.querySelector("span");
    322  span.remove();
    323  assert_equals(html_direction(slot), "ltr", "slot is reset to ltr");
    324  tree.remove();
    325 }, 'Removing slotted content resets direction on dir=auto slot');
    326 
    327 test(() => {
    328  let [tree, shadow] = setup_tree(`
    329    <div>
    330      <div id=root>
    331        <div>
    332          <span>اختبر</span>
    333        </div>
    334      </div>
    335    </div>
    336  `,`
    337    <slot dir=auto></slot>
    338  `);
    339 
    340  let slot = shadow.querySelector("slot");
    341  assert_equals(html_direction(slot), "rtl", "slot initially rtl");
    342  let span = tree.querySelector("span");
    343  span.remove();
    344  assert_equals(html_direction(slot), "ltr", "slot is reset to ltr");
    345  tree.remove();
    346 }, 'Removing child of slotted content changes direction on dir=auto slot');
    347 
    348 test(() => {
    349  let tree;
    350  tree = setup_tree(`
    351    <div>
    352      <span>اختبر</span>
    353      <p>Text</p>
    354    </div>
    355  `);
    356  let p = tree.querySelector("p");
    357  assert_true(p.matches(":dir(ltr)"), "child initially ltr");
    358  tree.dir = "auto";
    359  assert_true(p.matches(":dir(rtl)"), "child updated to rtl");
    360  tree.remove();
    361 }, 'Child directionality gets updated when dir=auto is set on parent');
    362 
    363 test(() => {
    364  let [tree, shadow] = setup_tree(`
    365    <div>
    366      <div id=root>
    367        <input value="اختبر">
    368      </div>
    369    </div>
    370  `,`
    371    <slot dir=auto></slot>
    372  `);
    373  let slot = shadow.querySelector("slot");
    374  assert_equals(html_direction(slot), "ltr");
    375  tree.remove();
    376 }, 'dir=auto slot is not affected by text in value of input element children');
    377 
    378 test(() => {
    379  let tree = setup_tree(`
    380    <div>
    381      <input dir="auto" value="اختبر">
    382    </div>
    383  `);
    384  let inp = tree.querySelector("input");
    385  assert_equals(html_direction(inp), "rtl");
    386  inp.type = "number";
    387  assert_equals(html_direction(inp), "ltr");
    388  tree.remove();
    389 }, 'input direction changes if it stops being auto-directionality form-associated');
    390 
    391 test(() => {
    392  let [tree, shadow] = setup_tree(`
    393    <div>
    394      <div id=root dir=ltr>
    395        <span>اختبر</span>
    396      </div>
    397    </div>
    398  `,`
    399    <div dir=auto id=container>
    400      <slot></slot>
    401    </div>
    402  `);
    403  let div = shadow.querySelector("#container");
    404  let host = tree.querySelector("#root");
    405  assert_equals(html_direction(div), 'ltr', 'ltr inherited from host despite rtl content');
    406  // set rtl on host directly, test it is propagated through slots
    407  host.dir = "rtl";
    408  assert_equals(html_direction(div), 'rtl', 'host dir change propagated via slot');
    409  host.dir = "";
    410  assert_equals(html_direction(host), 'ltr', 'host dir reset to ltr');
    411  assert_equals(html_direction(div), 'ltr', 'host dir change propagated via slot');
    412  // host inherits rtl from parent, test it is still propagated through slots
    413  tree.dir = "rtl";
    414  assert_equals(html_direction(host), 'rtl', 'host inherited rtl from parent');
    415  assert_equals(html_direction(div), 'rtl', 'host dir change propagated via slot');
    416  tree.remove();
    417 }, 'slot provides updated directionality from host to a dir=auto container');
    418 
    419 test(() => {
    420  let input = setup_tree(`<input type="text">`);
    421  assert_equals(html_direction(input), "ltr", "initial direction of input");
    422  input.value = "\u05D0";
    423  assert_equals(html_direction(input), "ltr", "direction of input with RTL contents");
    424  input.dir = "auto";
    425  assert_equals(html_direction(input), "rtl", "direction of input dir=auto with RTL contents");
    426  input.value = "a";
    427  assert_equals(html_direction(input), "ltr", "direction of input dir=auto with LTR contents");
    428  input.dir = "rtl";
    429  assert_equals(html_direction(input), "rtl", "direction of input dir=rtl with LTR contents");
    430  input.value = "ab";
    431  assert_equals(html_direction(input), "rtl", "direction of input dir=rtl with LTR contents (2)");
    432  input.value = "\u05D0";
    433  assert_equals(html_direction(input), "rtl", "direction of input dir=rtl with RTL contents");
    434 
    435  let textarea = setup_tree(`<textarea dir="auto"></textarea>`);
    436  assert_equals(html_direction(textarea), "ltr", "direction of textarea dir=auto with empty contents");
    437  textarea.value = "a";
    438  assert_equals(html_direction(textarea), "ltr", "direction of textarea dir=auto with LTR contents");
    439  textarea.value = "\u05D0";
    440  assert_equals(html_direction(textarea), "rtl", "direction of textarea dir=auto with RTL contents");
    441  textarea.dir = "rtl";
    442  assert_equals(html_direction(textarea), "rtl", "direction of textarea dir=rtl with RTL contents");
    443  textarea.value = "a";
    444  assert_equals(html_direction(textarea), "rtl", "direction of textarea dir=rtl with LTR contents");
    445 }, 'text input and textarea value changes should only be reflected in :dir() when dir=auto (value changes)');