tor-browser

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

test_selection_changes_with_middle_mouse_button.html (13646B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4  <title>Test for selection changes with middle mouse button</title>
      5  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
      6  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
      7  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
      8  <style>
      9    span, td {
     10      white-space: nowrap;
     11    }
     12  </style>
     13 </head>
     14 <body>
     15 <p id="display"></p>
     16 <div id="content" style="display: none;">
     17 
     18 </div>
     19 
     20 <textarea id="textarea" cols="1" rows="1">copied to clipboard</textarea>
     21 
     22 <div id="container">
     23 <span id="span1">first span.</span>
     24 <span id="span2">second span.</span><br>
     25 <a id="link" href="#top">link.</a>
     26 <table>
     27 <tr><td id="td1">first td.</td></tr>
     28 <tr><td id="td2">second td.</td></tr>
     29 </table>
     30 </div>
     31 
     32 <pre id="test">
     33 
     34 <script class="testbody" type="application/javascript">
     35 SimpleTest.waitForExplicitFinish();
     36 
     37 const kIsMac = navigator.platform.includes("Mac");
     38 const selection = getSelection();
     39 
     40 async function doTests(aEnableMiddlePaste, aEditable, aDescription) {
     41  await SpecialPowers.pushPrefEnv({"set": [
     42    ["middlemouse.paste", aEnableMiddlePaste],
     43    ["middlemouse.contentLoadURL", false],
     44    ["general.autoScroll", false],
     45    ["general.autoscroll.prevent_to_collapse_selection_by_middle_mouse_down", false],
     46  ]});
     47 
     48  if (aEditable) {
     49    document.getElementById("container").setAttribute("contenteditable", "true");
     50  } else {
     51    document.getElementById("container").removeAttribute("contenteditable");
     52  }
     53 
     54  let pasteEvents = [];
     55  function pasteEventHandler(event) {
     56    pasteEvents.push(event);
     57    event.preventDefault();
     58  }
     59  document.getElementById("container").addEventListener("paste", pasteEventHandler, true);
     60 
     61  // We need to behave as same as Chromium as far as possible.
     62  // - middle mouse button down should be collapse selection at the point.
     63  // - middle mouse button down can expand only with mouse button down with Shift key.
     64  // - middle mouse button shouldn't select table cells.
     65 
     66  function doTest(aMouseDown, aMouseUp,
     67                  aExpectedSelectionAnchor, aExpectedSelectionFocus, aExpectedPastEventTarget,
     68                  aAdditionalDescription) {
     69    pasteEvents = [];
     70    synthesizeMouseAtCenter(aMouseDown.target,
     71      {
     72        button: 1,
     73        type: "mousedown",
     74        shiftKey: aMouseDown.shiftKey,
     75        ctrlKey: aMouseDown.ctrlKey && !kIsMac,
     76        metaKey: aMouseDown.ctrlKey && kIsMac,
     77      });
     78    if (aExpectedSelectionAnchor === aExpectedSelectionFocus) {
     79      ok(selection.isCollapsed,
     80         aDescription + aAdditionalDescription + "Selection should be collapsed at mousedown");
     81      is(selection.focusNode, aExpectedSelectionFocus,
     82         aDescription + aAdditionalDescription + "Selection should be collapsed in the node at mousedown");
     83    } else {
     84      is(selection.anchorNode, aExpectedSelectionAnchor,
     85         aDescription + aAdditionalDescription + "Anchor node of Selection should be previous anchor node");
     86      is(selection.focusNode, aExpectedSelectionFocus,
     87         aDescription + aAdditionalDescription + "Focus node of Selection should be the node at mousedown");
     88    }
     89    is(pasteEvents.length, 0,
     90       aDescription + aAdditionalDescription + "paste event shouldn't be fired when middle mouse button down");
     91 
     92    if (aMouseDown.target != aMouseUp.target) {
     93      synthesizeMouseAtCenter(aMouseUp.target, {type: "mousemove"});
     94    }
     95    synthesizeMouseAtCenter(aMouseUp.target,
     96      {
     97        button: 1,
     98        type: "mouseup",
     99        shiftKey: aMouseUp.shiftKey,
    100        ctrlKey: aMouseUp.ctrlKey && !kIsMac,
    101        metaKey: aMouseUp.ctrlKey && kIsMac,
    102      });
    103    is(selection.anchorNode, aExpectedSelectionAnchor,
    104       aDescription + aAdditionalDescription + "Anchor node of Selection shouldn't be modified at mouseup");
    105    is(selection.focusNode, aExpectedSelectionFocus,
    106       aDescription + aAdditionalDescription + "Focus node of Selection shouldn't be modified at mouseup");
    107    if (aEnableMiddlePaste) {
    108      if (aExpectedPastEventTarget === null) {
    109        is(pasteEvents.length, 0,
    110           aDescription + aAdditionalDescription + "paste event shouldn't be fired even when middle mouse button up");
    111      } else {
    112        is(pasteEvents.length, 1,
    113           aDescription + aAdditionalDescription + "paste event should be fired only once at mouse up");
    114        is(pasteEvents[0]?.target, aExpectedPastEventTarget,
    115           aDescription + aAdditionalDescription + "paste event should be fired on start of selection");
    116      }
    117    } else {
    118      is(pasteEvents.length, 0,
    119         aDescription + aAdditionalDescription + "paste event shouldn't be fired when middle mouse button up when middle mouse paste is disabled");
    120    }
    121  }
    122 
    123  let span1 = document.getElementById("span1");
    124  let span2 = document.getElementById("span2");
    125  let link = document.getElementById("link");
    126 
    127  selection.removeAllRanges();
    128  doTest({target: span1}, {target: span1},
    129         span1.firstChild, span1.firstChild, span1,
    130         "Clicking span1 when there is no selection: ");
    131  doTest({target: span2}, {target: span2},
    132         span2.firstChild, span2.firstChild, span2,
    133         "Clicking span2 when selection is collapsed in span1: ");
    134  doTest({target: span1}, {target: span2},
    135         span1.firstChild, span1.firstChild, span1,
    136         "Dragging from span1 to span2: ");
    137  doTest({target: span2}, {target: span1},
    138         span2.firstChild, span2.firstChild, span2,
    139         "Dragging from span2 to span1: ");
    140  doTest({target: span1, shiftKey: true}, {target: span1, shiftKey: true},
    141         span2.firstChild, span1.firstChild, span1,
    142         "Expanding selection with Shift key from span2 to span1: ");
    143  selection.collapse(span1.firstChild, 3);
    144  if (aEditable) {
    145    // Collapse link into editable link.
    146    doTest({target: link, shiftKey: true}, {target: link, shiftKey: true},
    147          link.firstChild, link.firstChild,
    148          link /* TODO: Perhaps, the "paste" event target should be the link */,
    149          "Clicking an editable link with middle-button with Shift key when selection is collapsed in span1: ");
    150  } else {
    151    // Don't extend selection into a link.
    152    link.onauxclick = event => event.preventDefault();
    153    doTest({target: link, shiftKey: true}, {target: link, shiftKey: true},
    154          span1.firstChild, span1.firstChild,
    155          null /* due to the call of preventDefault */,
    156          "Clicking a link with middle-button with Shift key when selection is collapsed in span1: ");
    157    link.onauxclick = null;
    158  }
    159  // "paste" event should be fired in the "start" of selection.
    160  selection.collapse(span1.firstChild, 3);
    161  doTest({target: span2, shiftKey: true}, {target: span2, shiftKey: true},
    162         span1.firstChild, span2.firstChild, span1,
    163         "Expanding selection with Shift key from span1 to span2: ");
    164  // XXX This case is different from Chrome for Linux.
    165  //     In this case, Chrome does not collapse Selection at mousedown,
    166  //     but collapse at click.  So, if mouseup occurs different element,
    167  //     Selection isn't modified.
    168  selection.selectAllChildren(span1);
    169  doTest({target: span1}, {target: span1},
    170         span1.firstChild, span1.firstChild, span1,
    171         "Clicking span1 when span1 is selected: ");
    172 
    173  let td1 = document.getElementById("td1");
    174  let td2 = document.getElementById("td2");
    175 
    176  selection.removeAllRanges();
    177  doTest({target: td1}, {target: td1},
    178         td1.firstChild, td1.firstChild, td1,
    179         "Clicking td1 when there is no selection: ");
    180  if (aEditable) {
    181    // XXX In this case, we don't allow to expand selection with Shift key
    182    //     click across table cell boundary.
    183    doTest({target: td2, shiftKey: true}, {target: td2, shiftKey: true},
    184           td1.firstChild, td1.firstChild, td1,
    185           "Expanding selection with Shift key from td1 to td2: ");
    186  } else {
    187    doTest({target: td2, shiftKey: true}, {target: td2, shiftKey: true},
    188           td1.firstChild, td2.firstChild, td1,
    189           "Expanding selection with Shift key from td1 to td2: ");
    190  }
    191  // Shouldn't select per table cell when the button is middle mouse button.
    192  doTest({target: td1, ctrlKey: true}, {target: td1, ctrlKey: true},
    193         td1.firstChild, td1.firstChild, td1,
    194         "Click td1 with Control key: ");
    195 
    196  document.getElementById("container").removeEventListener("paste", pasteEventHandler, true);
    197 }
    198 
    199 async function runTestPreventingToCollapseSelectionByPrefs() {
    200  await SpecialPowers.pushPrefEnv({"set": [
    201    ["middlemouse.contentLoadURL", false],
    202    ["general.autoscroll.prevent_to_collapse_selection_by_middle_mouse_down", true],
    203  ]});
    204 
    205  // If middle click paste is disabled and autoscroll is enabled, we should
    206  // allow users to prevent to collapse selection by middle mouse down.
    207  await SpecialPowers.pushPrefEnv({"set": [
    208    ["middlemouse.paste", false],
    209    ["general.autoScroll", true],
    210  ]});
    211 
    212  const container = document.getElementById("container");
    213  container.removeAttribute("contenteditable");
    214  container.getBoundingClientRect();
    215 
    216  const span1 = container.querySelector("span#span1");
    217  const span2 = container.querySelector("span#span2");
    218  function checkSelectAllChildrenOfSpan2(description) {
    219    is(
    220      getSelection().focusNode,
    221      span2,
    222      `Selection shouldn't be collapsed by ${description} (focusNode)`
    223    );
    224    is(
    225      getSelection().focusOffset,
    226      span2.childNodes.length,
    227      `Selection shouldn't be collapsed by ${description} (focusOffset)`
    228    );
    229    is(
    230      getSelection().anchorNode,
    231      span2,
    232      `Selection shouldn't be collapsed by ${description} (anchorNode)`
    233    );
    234    is(
    235      getSelection().anchorOffset,
    236      0,
    237      `Selection shouldn't be collapsed by ${description} (anchorOffset)`
    238    );
    239  }
    240 
    241  function checkSelectionCollapsedInSpan1(description) {
    242    ok(
    243      getSelection().isCollapsed,
    244      `Selection should be collapsed into the clicked text node ${description} (isCollapsed)`
    245    );
    246    is(
    247      getSelection().focusNode,
    248      span1.firstChild,
    249      `Selection should be collapsed into the clicked text node ${description} (focusNode)`
    250    );
    251    is(
    252      getSelection().anchorNode,
    253      span1.firstChild,
    254      `Selection should be collapsed into the clicked text node ${description} (anchorNode)`
    255    );
    256  }
    257  getSelection().selectAllChildren(span2);
    258  synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mousedown", button: 1 });
    259  checkSelectAllChildrenOfSpan2("middle mousedown if the pref is enabled and middle click paste is disabled and autoscroll are enabled");
    260 
    261  synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mouseup", button: 1 });
    262  checkSelectAllChildrenOfSpan2("middle mouseup if the pref is enabled and middle click paste is disabled and autoscroll are enabled");
    263 
    264  container.setAttribute("contenteditable", "true");
    265  container.getBoundingClientRect();
    266  getSelection().selectAllChildren(span2);
    267  synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mousedown", button: 1 });
    268  checkSelectionCollapsedInSpan1("by middle mousedown if it's editable content");
    269 
    270  synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mouseup", button: 1 });
    271  checkSelectionCollapsedInSpan1("after middle mouseup if it's editable content");
    272 
    273  container.removeAttribute("contenteditable");
    274  container.getBoundingClientRect();
    275  await SpecialPowers.pushPrefEnv({"set": [
    276    ["middlemouse.paste", false],
    277    ["general.autoScroll", false],
    278  ]});
    279 
    280  getSelection().selectAllChildren(span2);
    281  synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mousedown", button: 1 });
    282  checkSelectionCollapsedInSpan1("by middle mousedown if the pref is enabled but autoscroll are disabled");
    283 
    284  synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mouseup", button: 1 });
    285  checkSelectionCollapsedInSpan1("after middle mousedown if the pref is enabled but autoscroll are disabled");
    286 
    287  await SpecialPowers.pushPrefEnv({"set": [
    288    ["middlemouse.paste", true],
    289    ["general.autoScroll", true],
    290  ]});
    291 
    292  getSelection().selectAllChildren(span2);
    293  synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mousedown", button: 1 });
    294  checkSelectionCollapsedInSpan1("by middle mousedown if the pref is enabled but middle mouse paste is enabled");
    295 
    296  synthesizeMouseAtCenter(document.getElementById("span1"), { type: "mouseup", button: 1 });
    297  checkSelectionCollapsedInSpan1("after middle mousedown if the pref is enabled but middle mouse paste is enabled");
    298 }
    299 
    300 async function runAllTests() {
    301  let textarea = document.getElementById("textarea");
    302  textarea.focus();
    303  await new Promise((resolve, reject) => {
    304    SimpleTest.waitForClipboard(textarea.value,
    305      () => {
    306        synthesizeKey("a", {accelKey: true});
    307        synthesizeKey("c", {accelKey: true});
    308      },
    309      () => {
    310        ok(true, `Succeeded to copy "${textarea.value}" to clipboard`);
    311        textarea.style.display = "none";
    312        resolve();
    313      },
    314      () => {
    315        ok(false, `Failed to copy "${textarea.value}" to clipboard`);
    316        reject();
    317      });
    318  });
    319 
    320  await doTests(true, false, "Testing with the middle paste enabled: ");
    321  await doTests(false, false, "Testing with the middle paste disabled: ");
    322 
    323  await doTests(true, true, "Testing in editable content with the middle paste enabled: ");
    324  await doTests(false, true, "Testing in editable content with the middle paste disabled: ");
    325 
    326  await runTestPreventingToCollapseSelectionByPrefs();
    327 
    328  SimpleTest.finish();
    329 }
    330 
    331 SimpleTest.waitForFocus(runAllTests);
    332 </script>
    333 </pre>
    334 </body>
    335 </html>