tor-browser

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

test_selection_after_right_click.html (18793B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <meta charset="utf-8" />
      5 <title>Tests for checking the behavior of right click clicking outside selection</title>
      6 <script src="/tests/SimpleTest/SimpleTest.js"></script>
      7 <script src="/tests/SimpleTest/EventUtils.js"></script>
      8 <link rel="stylesheet" href="/tests/SimpleTest/test.css" />
      9 </head>
     10 <body>
     11 <div>Non editable text</div>
     12 <div contenteditable>Editable text</div>
     13 <input value="input value" style="width: 100%" />
     14 <iframe srcdoc="<span>abc</span>"></iframe>
     15 <script>
     16 SimpleTest.waitForExplicitFinish();
     17 SimpleTest.waitForFocus(async () => {
     18  function getRangeDescription(range) {
     19    function getNodeDescription(node) {
     20      if (!node) {
     21        return "null";
     22      }
     23      switch (node.nodeType) {
     24        case Node.TEXT_NODE:
     25        case Node.COMMENT_NODE:
     26        case Node.CDATA_SECTION_NODE:
     27          return `${node.nodeName} "${node.data}"`;
     28        case Node.ELEMENT_NODE:
     29          return `<${node.nodeName.toLowerCase()}>`;
     30        default:
     31          return `${node.nodeName}`;
     32      }
     33    }
     34    if (range === null) {
     35      return "null";
     36    }
     37    if (range === undefined) {
     38      return "undefined";
     39    }
     40    return range.startContainer == range.endContainer &&
     41      range.startOffset == range.endOffset
     42      ? `(${getNodeDescription(range.startContainer)}, ${range.startOffset})`
     43      : `(${getNodeDescription(range.startContainer)}, ${
     44          range.startOffset
     45        }) - (${getNodeDescription(range.endContainer)}, ${range.endOffset})`;
     46  }
     47 
     48  document.addEventListener(
     49    "contextmenu",
     50    event => event.preventDefault(),
     51    { capture: true }
     52  );
     53  const nonEditableDiv = document.querySelector("div");
     54  const editableDiv = document.querySelector("div[contenteditable]");
     55  const input = document.querySelector("input");
     56 
     57  // See also modifying-selection-with-non-primary-mouse-button.tentative.html?secondary for the basic behavior check.
     58  await SpecialPowers.pushPrefEnv({
     59    set: [
     60      ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", false],
     61      ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
     62      ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
     63    ],
     64  });
     65 
     66  getSelection().setBaseAndExtent(
     67    nonEditableDiv.firstChild, "Non editable ".length,
     68    nonEditableDiv.firstChild, "Non editable text".length
     69  );
     70  synthesizeMouse(nonEditableDiv, 10, 10, {button: 2});
     71  ok(
     72    getSelection().isCollapsed,
     73    `Selection should be collapsed by a right click when stop_if_non_collapsed_selection pref is false in non-editable text node (${
     74      getRangeDescription(getSelection().getRangeAt(0))
     75    })`
     76  );
     77 
     78  getSelection().setBaseAndExtent(
     79    editableDiv.firstChild, "Editable ".length,
     80    editableDiv.firstChild, "Editable text".length
     81  );
     82  synthesizeMouse(editableDiv, 10, 10, {button: 2});
     83  ok(
     84    getSelection().isCollapsed,
     85    `Selection should be collapsed by a right click when stop_if_non_collapsed_selection pref is false in editable text node (${
     86      getRangeDescription(getSelection().getRangeAt(0))
     87    })`
     88  );
     89 
     90  input.focus();
     91  input.setSelectionRange("input ".length, "input value".length);
     92  synthesizeMouse(input, 10, 10, {button: 2});
     93  ok(
     94    input.selectionStart == input.selectionEnd,
     95    `Selection in <input> should be collapsed by a right click when stop_if_non_collapsed_selection pref is false (got: ${input.selectionStart} - ${input.selectionEnd})`
     96  );
     97  input.blur();
     98 
     99  await SpecialPowers.pushPrefEnv({
    100    set: [
    101      ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", false],
    102      ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", true],
    103      ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
    104    ],
    105  });
    106 
    107  getSelection().setBaseAndExtent(
    108    nonEditableDiv.firstChild, "Non editable ".length,
    109    nonEditableDiv.firstChild, "Non editable text".length
    110  );
    111  synthesizeMouse(nonEditableDiv, 10, 10, {button: 2});
    112  ok(
    113    !getSelection().isCollapsed,
    114    `Selection should not be collapsed by a right click when stop_if_non_editable_node pref is true in non-editable text node (${
    115      getRangeDescription(getSelection().getRangeAt(0))
    116    })`
    117  );
    118 
    119  getSelection().setBaseAndExtent(
    120    editableDiv.firstChild, "Editable ".length,
    121    editableDiv.firstChild, "Editable text".length
    122  );
    123  synthesizeMouse(editableDiv, 10, 10, {button: 2});
    124  ok(
    125    getSelection().isCollapsed,
    126    `Selection should be collapsed by a right click even when stop_if_non_editable_node pref is true in editable text node (${
    127      getRangeDescription(getSelection().getRangeAt(0))
    128    })`
    129  );
    130 
    131  input.focus();
    132  input.setSelectionRange("input ".length, "input value".length);
    133  synthesizeMouse(input, 10, 10, {button: 2});
    134  ok(
    135    input.selectionStart == input.selectionEnd,
    136    `Selection in <input> should be collapsed by a right click even when stop_if_non_editable_node pref is true (got: ${input.selectionStart} - ${input.selectionEnd})`
    137  );
    138  input.blur();
    139 
    140  await SpecialPowers.pushPrefEnv({
    141    set: [
    142      ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
    143      ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
    144      ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
    145    ],
    146  });
    147 
    148  getSelection().setBaseAndExtent(
    149    nonEditableDiv.firstChild, "Non editable ".length,
    150    nonEditableDiv.firstChild, "Non editable text".length
    151  );
    152  synthesizeMouse(nonEditableDiv, 10, 10, {button: 2});
    153  ok(
    154    !getSelection().isCollapsed,
    155    `Selection should not be collapsed by a right click when stop_if_non_collapsed_selection pref is true in non-editable text node (${
    156      getRangeDescription(getSelection().getRangeAt(0))
    157    })`
    158  );
    159 
    160  getSelection().setBaseAndExtent(
    161    editableDiv.firstChild, "Editable ".length,
    162    editableDiv.firstChild, "Editable text".length
    163  );
    164  synthesizeMouse(editableDiv, 10, 10, {button: 2});
    165  ok(
    166    !getSelection().isCollapsed,
    167    `Selection should be collapsed by a right click when stop_if_non_collapsed_selection pref is true in editable text node (${
    168      getRangeDescription(getSelection().getRangeAt(0))
    169    })`
    170  );
    171 
    172  input.focus();
    173  input.setSelectionRange("input ".length, "input value".length);
    174  synthesizeMouse(input, 10, 10, {button: 2});
    175  ok(
    176    input.selectionStart != input.selectionEnd,
    177    `Selection in <input> should not be collapsed by a right click when stop_if_non_collapsed_selection pref is true (got: ${input.selectionStart} - ${input.selectionEnd})`
    178  );
    179  input.blur();
    180 
    181  // When the right click changes the selection ancestor limit, it should be
    182  // handled correctly after nsIFrame does nothing.
    183  getSelection().setBaseAndExtent(
    184    nonEditableDiv.firstChild, "Non editable ".length,
    185    nonEditableDiv.firstChild, "Non editable text".length
    186  );
    187  synthesizeMouse(editableDiv, 10, 10, {button: 2});
    188  is(
    189    document.activeElement,
    190    editableDiv,
    191    "Right clicking in editable <div> when selection selects some text outside the <div> should move focus into the editor"
    192  );
    193  ok(
    194    getSelection().focusNode == editableDiv.firstChild &&
    195      getSelection().focusOffset > 0,
    196    `Right clicking in editable <div> when selection selects some text outside the <div> should not cause collapsing selection to start of the editor (${
    197      getRangeDescription(getSelection().getRangeAt(0))
    198    })`
    199  );
    200 
    201  getSelection().setBaseAndExtent(
    202    editableDiv.firstChild, "Editable ".length,
    203    editableDiv.firstChild, "Editable text".length
    204  );
    205  synthesizeMouse(nonEditableDiv, 10, 10, {button: 2});
    206  isnot(
    207    document.activeElement,
    208    editableDiv,
    209    "Right clicking outside the editable <div> should blur from it"
    210  );
    211  ok(
    212    getSelection().focusNode == nonEditableDiv.firstChild &&
    213      getSelection().focusOffset > 0,
    214    `Right clicking outside the editable <div> should collapse selection at the clicked content (${
    215      getRangeDescription(getSelection().getRangeAt(0))
    216    })`
    217  );
    218 
    219  // If clicking in a selection range, the range should never be collapsed.
    220  const iframe = document.querySelector("iframe");
    221  const doc = iframe.contentDocument;
    222  doc.addEventListener("contextmenu", event => event.preventDefault(), {capture: true});
    223 
    224  await SpecialPowers.pushPrefEnv({
    225    set: [
    226      ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", false],
    227      ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
    228      ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
    229    ],
    230  });
    231  iframe.focus();
    232  doc.getSelection().selectAllChildren(doc.body);
    233  synthesizeMouseAtCenter(doc.body.firstChild, {button: 2}, iframe.contentWindow);
    234  ok(
    235    !doc.getSelection().isCollapsed,
    236    "Right click in a selection range should not cause collapsing selection"
    237  );
    238  iframe.blur();
    239  doc.activeElement?.blur();
    240 
    241  doc.documentElement.setAttribute("contenteditable", "");
    242  iframe.focus();
    243  doc.getSelection().selectAllChildren(doc.body);
    244  synthesizeMouseAtCenter(doc.body.firstChild, {button: 2}, iframe.contentWindow);
    245  ok(
    246    !doc.getSelection().isCollapsed,
    247    "Right click in a selection range in a editable-root-element should not cause collapsing selection"
    248  );
    249  iframe.blur();
    250  doc.activeElement?.blur();
    251 
    252  await SpecialPowers.pushPrefEnv({
    253    set: [
    254      ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
    255      ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
    256      ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
    257    ],
    258  });
    259  doc.designMode = "on";
    260  iframe.focus();
    261  doc.getSelection().selectAllChildren(doc.body);
    262  synthesizeMouseAtCenter(doc.body.firstChild, {button: 2}, iframe.contentWindow);
    263  ok(
    264    !doc.getSelection().isCollapsed,
    265    "Right click in a selection range in a sub-editable-document should not cause collapsing selection"
    266  );
    267  doc.designMode = "off";
    268 
    269  // If selection is collapsed and `ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node` is
    270  // false, the caret should be moved at clicked point.
    271  await SpecialPowers.pushPrefEnv({
    272    set: [
    273      ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
    274      ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
    275      ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
    276    ],
    277  });
    278  editableDiv.innerHTML = "<b>Bold text</b><i>Italic text</i>";
    279  editableDiv.focus();
    280  getSelection().collapse(
    281    editableDiv.querySelector("b").firstChild,
    282    "Bold".length
    283  );
    284  synthesizeMouseAtCenter(editableDiv.querySelector("i"), {button: 2});
    285  ok(
    286    getSelection().isCollapsed,
    287    "Right clicking in editable <div> should keep Selection collapsed if the pref is unset"
    288  );
    289  is(
    290    getSelection().focusNode.parentNode,
    291    editableDiv.querySelector("i"),
    292    `Right clicking in editable <div> should collapse Selection at the clicked point (${
    293      getRangeDescription(getSelection().getRangeAt(0))
    294    }) if the pref is unset`
    295  );
    296 
    297  input.focus();
    298  input.setSelectionRange(0, 0);
    299  synthesizeMouseAtCenter(input, {button: 2});
    300  ok(
    301    input.selectionStart == input.selectionEnd,
    302    "Right click in <input> should keep Selection collapsed if the pref is unset"
    303  );
    304  ok(
    305    input.selectionStart > 0,
    306    "Right click in <input> should move caret if the pref is unset"
    307  );
    308  input.blur();
    309 
    310  // If selection is collapsed and `ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node` is
    311  // true, the caret should not be moved at clicked point unless focus is changed by the click.
    312  await SpecialPowers.pushPrefEnv({
    313    set: [
    314      ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
    315      ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
    316      ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", true],
    317    ],
    318  });
    319  editableDiv.innerHTML = "<b>Bold text</b><i>Italic text</i>";
    320  editableDiv.focus();
    321  getSelection().collapse(
    322    editableDiv.querySelector("b").firstChild,
    323    "Bold".length
    324  );
    325  synthesizeMouseAtCenter(editableDiv.querySelector("i"), {button: 2});
    326  ok(
    327    getSelection().isCollapsed,
    328    "Right clicking in editable <div> should keep Selection collapsed even if the pref is set"
    329  );
    330  is(
    331    getSelection().focusNode.parentNode,
    332    editableDiv.querySelector("b"),
    333    `Right clicking in editable <div> should not move caret to the clicked point (${
    334      getRangeDescription(getSelection().getRangeAt(0))
    335    }) if the pref is set`
    336  );
    337 
    338  input.focus();
    339  input.setSelectionRange(0, 0);
    340  synthesizeMouseAtCenter(input, {button: 2});
    341  ok(
    342    input.selectionStart == input.selectionEnd,
    343    "Right click in <input> should keep Selection collapsed even if the pref is set"
    344  );
    345  is(
    346    input.selectionStart,
    347    0,
    348    "Right click in <input> should not move caret if the pref is set"
    349  );
    350  input.blur();
    351 
    352  input.setSelectionRange(0, 0);
    353  editableDiv.focus();
    354  synthesizeMouseAtCenter(input, {button: 2});
    355  ok(
    356    input.selectionStart == input.selectionEnd,
    357    "Right click in <input> which is not focused should keep Selection collapsed"
    358  );
    359  ok(
    360    input.selectionStart > 0,
    361    "Right click in <input> which is not focused should move caret"
    362  );
    363 
    364  editableDiv.focus();
    365  getSelection().collapse(
    366    editableDiv.querySelector("b").firstChild,
    367    "Bold".length
    368  );
    369  input.focus();
    370  synthesizeMouseAtCenter(editableDiv.querySelector("i"), {button: 2});
    371  ok(
    372    getSelection().isCollapsed,
    373    "Right clicking in editable <div> which is not focused should collapse selection"
    374  );
    375  is(
    376    getSelection().focusNode.parentNode,
    377    editableDiv.querySelector("i"),
    378    `Right clicking in editable <div> which is not focused should collapse Selection at the clicked point (${
    379      getRangeDescription(getSelection().getRangeAt(0))
    380    })`
    381  );
    382 
    383  // If Shift + right click should forcibly open context menu, users may want the click to work as
    384  // same as without Shift.
    385  await SpecialPowers.pushPrefEnv({
    386    set: [
    387      ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
    388      ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
    389      ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
    390      ["dom.event.contextmenu.shift_suppresses_event", true],
    391    ],
    392  });
    393  nonEditableDiv.innerHTML = "<b>bold</b> <i>italic</i>";
    394  getSelection().collapse(nonEditableDiv.querySelector("b").firstChild, 0);
    395  synthesizeMouseAtCenter(nonEditableDiv.querySelector("i"), {shiftKey: true, button: 2});
    396  ok(
    397    getSelection().isCollapsed,
    398    `Selection should be collapsed by a Shift + right click on non-editable node when it does not open context menu (${
    399      getRangeDescription(getSelection().getRangeAt(0))
    400    })`
    401  );
    402  is(
    403    getSelection().focusNode,
    404    nonEditableDiv.querySelector("i").firstChild,
    405    `Selection should be collapsed at the click point by a Shift + right click on non-editable node when it does not open context menu (${
    406      getRangeDescription(getSelection().getRangeAt(0))
    407    })`
    408  );
    409 
    410  editableDiv.innerHTML = "<b>bold</b> <i>italic</i>";
    411  getSelection().collapse(editableDiv.querySelector("b").firstChild, 0);
    412  synthesizeMouseAtCenter(editableDiv.querySelector("i"), {shiftKey: true, button: 2});
    413  ok(
    414    getSelection().isCollapsed,
    415    `Selection should be collapsed by a Shift + right click on editable node when it does not open context menu (${
    416      getRangeDescription(getSelection().getRangeAt(0))
    417    })`
    418  );
    419  is(
    420    getSelection().focusNode,
    421    editableDiv.querySelector("i").firstChild,
    422    `Selection should be collapsed at the click point by a Shift + right click on editable node when it does not open context menu (${
    423      getRangeDescription(getSelection().getRangeAt(0))
    424    })`
    425  );
    426 
    427  input.focus();
    428  input.setSelectionRange(0, 0);
    429  synthesizeMouseAtCenter(input, {shiftKey: true, button: 2});
    430  ok(
    431    input.selectionStart == input.selectionEnd,
    432    `Selection in <input> should be collapsed by a Shift + right click when it does not open context menu (got: ${
    433      input.selectionStart
    434    } - ${input.selectionEnd})`
    435  );
    436  isnot(
    437    input.selectionStart,
    438    0,
    439    `Selection in <input> should be collapsed at the click point by a Shift + right click when it does not open context menu (got: ${
    440      input.selectionStart
    441    } - ${input.selectionEnd})`
    442  );
    443  input.blur();
    444 
    445  // If Shift + right click should open context menu, users may want the click to work as
    446  // same as a left click.
    447  await SpecialPowers.pushPrefEnv({
    448    set: [
    449      ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
    450      ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
    451      ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
    452      ["dom.event.contextmenu.shift_suppresses_event", false],
    453    ],
    454  });
    455  nonEditableDiv.innerHTML = "<b>bold</b> <i>italic</i>";
    456  getSelection().collapse(nonEditableDiv.querySelector("b").firstChild, 0);
    457  synthesizeMouseAtCenter(nonEditableDiv.querySelector("i"), {shiftKey: true, button: 2});
    458  is(
    459    getRangeDescription(getSelection().getRangeAt(0)),
    460    getRangeDescription({
    461      startContainer: nonEditableDiv.querySelector("b").firstChild,
    462      startOffset: 0,
    463      endContainer: nonEditableDiv.querySelector("i").firstChild,
    464      endOffset: getSelection().focusOffset,
    465    }),
    466    `Selection should be extended by a Shift + right click on non-editable node when it should open context menu`
    467  );
    468 
    469  editableDiv.innerHTML = "<b>bold</b> <i>italic</i>";
    470  getSelection().collapse(editableDiv.querySelector("b").firstChild, 0);
    471  synthesizeMouseAtCenter(editableDiv.querySelector("i"), {shiftKey: true, button: 2});
    472  is(
    473    getRangeDescription(getSelection().getRangeAt(0)),
    474    getRangeDescription({
    475      startContainer: editableDiv.querySelector("b").firstChild,
    476      startOffset: 0,
    477      endContainer: editableDiv.querySelector("i").firstChild,
    478      endOffset: getSelection().focusOffset,
    479    }),
    480    `Selection should be extended by a Shift + right click on editable node when it should open context menu`
    481  );
    482 
    483  input.focus();
    484  input.setSelectionRange(0, 0);
    485  synthesizeMouseAtCenter(input, {shiftKey: true, button: 2});
    486  isnot(
    487    input.selectionStart,
    488    input.selectionEnd,
    489    `Selection in <input> should be extended by a Shift + right click when it should open context menu (got: ${
    490      input.selectionStart
    491    } - ${input.selectionEnd})`
    492  );
    493  input.blur();
    494 
    495  SimpleTest.finish();
    496 });
    497 </script>
    498 </body>
    499 </html>