tor-browser

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

file_clipboard_common.js (11660B)


      1 // This test is called from both test_clipboard_editor.html and test_clipboard_noeditor.html
      2 // This is to test that the code works both in the presence of a contentEditable node, and in the absense of one
      3 var WATCH_TIMEOUT = 300;
      4 
      5 // Some global variables to make the debug messages easier to track down
      6 var gTestN0 = 0,
      7  gTestN1 = 0,
      8  gTestN2 = 0;
      9 function testLoc() {
     10  return " " + gTestN0 + " - " + gTestN1 + " - " + gTestN2;
     11 }
     12 
     13 // Listen for cut & copy events
     14 var gCopyCount = 0,
     15  gCutCount = 0;
     16 document.addEventListener("copy", function () {
     17  gCopyCount++;
     18 });
     19 document.addEventListener("cut", function () {
     20  gCutCount++;
     21 });
     22 
     23 // Helper methods
     24 function selectNode(aSelector, aCb) {
     25  var dn = document.querySelector(aSelector);
     26  var range = document.createRange();
     27  range.selectNodeContents(dn);
     28  window.getSelection().removeAllRanges();
     29  window.getSelection().addRange(range);
     30  if (aCb) {
     31    aCb();
     32  }
     33 }
     34 
     35 function selectInputNode(aSelector, aCb) {
     36  var dn = document.querySelector(aSelector);
     37  synthesizeMouse(dn, 10, 10, {});
     38  SimpleTest.executeSoon(function () {
     39    synthesizeKey("A", { accelKey: true });
     40    // Clear the user activation state which is set from synthesized mouse and
     41    // key event.
     42    SpecialPowers.wrap(document).clearUserGestureActivation();
     43    SimpleTest.executeSoon(aCb);
     44  });
     45 }
     46 
     47 // Callback functions for attaching to the button
     48 function execCommand(aCommand, aShouldSucceed, aAsync = false) {
     49  var cb = function (e) {
     50    e.preventDefault();
     51    document.removeEventListener("keydown", cb);
     52 
     53    if (aAsync) {
     54      setTimeout(() => {
     55        is(
     56          aShouldSucceed,
     57          document.execCommand(aCommand),
     58          "Keydown caused " + aCommand + " invocation" + testLoc()
     59        );
     60      }, 0);
     61    } else {
     62      is(
     63        aShouldSucceed,
     64        document.execCommand(aCommand),
     65        "Keydown caused " + aCommand + " invocation" + testLoc()
     66      );
     67    }
     68  };
     69  return cb;
     70 }
     71 
     72 // The basic test set. Tries to cut/copy everything
     73 function cutCopyAll(
     74  aDoCut,
     75  aDoCopy,
     76  aDone,
     77  aNegate,
     78  aClipOverride,
     79  aJustClipboardNegate
     80 ) {
     81  var execCommandAlwaysSucceed = !!(aClipOverride || aJustClipboardNegate);
     82 
     83  function waitForClipboard(aCond, aSetup, aNext, aNegateOne) {
     84    if (aClipOverride) {
     85      aCond = aClipOverride;
     86      aNegateOne = false;
     87    }
     88    if (aNegate || aNegateOne || aJustClipboardNegate) {
     89      SimpleTest.waitForClipboard(
     90        null,
     91        aSetup,
     92        aNext,
     93        aNext,
     94        "text/plain",
     95        WATCH_TIMEOUT,
     96        true
     97      );
     98    } else {
     99      SimpleTest.waitForClipboard(aCond, aSetup, aNext, aNext);
    100    }
    101  }
    102 
    103  function validateCutCopy(aExpectedCut, aExpectedCopy) {
    104    if (aNegate) {
    105      aExpectedCut = aExpectedCopy = 0;
    106    } // When we are negating - we always expect callbacks not to be run
    107 
    108    is(
    109      aExpectedCut,
    110      gCutCount,
    111      (aExpectedCut > 0
    112        ? "Expect cut callback to run"
    113        : "Expect cut callback not to run") + testLoc()
    114    );
    115    is(
    116      aExpectedCopy,
    117      gCopyCount,
    118      (aExpectedCopy > 0
    119        ? "Expect copy callback to run"
    120        : "Expect copy callback not to run") + testLoc()
    121    );
    122    gCutCount = gCopyCount = 0;
    123  }
    124 
    125  function step(n) {
    126    function nextStep() {
    127      step(n + 1);
    128    }
    129 
    130    // Reset the user activation state before running next test.
    131    SpecialPowers.wrap(document).clearUserGestureActivation();
    132 
    133    document.querySelector("span").textContent = "span text";
    134    document.querySelector("input[type=text]").value = "text text";
    135    document.querySelector("input[type=password]").value = "password text";
    136    document.querySelector("textarea").value = "textarea text";
    137 
    138    var contentEditableNode = document.querySelector(
    139      "div[contentEditable=true]"
    140    );
    141    if (contentEditableNode) {
    142      contentEditableNode.textContent = "contenteditable text";
    143    }
    144 
    145    gTestN2 = n;
    146    switch (n) {
    147      case 0:
    148        // copy on readonly selection
    149        selectNode("span");
    150        waitForClipboard(
    151          "span text",
    152          function () {
    153            aDoCopy(true);
    154          },
    155          nextStep
    156        );
    157        return;
    158 
    159      case 1:
    160        validateCutCopy(0, 1);
    161 
    162        // cut on readonly selection
    163        selectNode("span");
    164 
    165        waitForClipboard(
    166          "span text",
    167          function () {
    168            aDoCut(execCommandAlwaysSucceed);
    169          },
    170          nextStep,
    171          true
    172        );
    173        return;
    174 
    175      case 2:
    176        validateCutCopy(1, 0);
    177 
    178        // copy on textbox selection
    179        selectInputNode("input[type=text]", nextStep);
    180        return;
    181 
    182      case 3:
    183        waitForClipboard(
    184          "text text",
    185          function () {
    186            selectInputNode("input[type=text]", function () {
    187              aDoCopy(true);
    188            });
    189          },
    190          nextStep
    191        );
    192        return;
    193 
    194      case 4:
    195        validateCutCopy(0, 1);
    196 
    197        // cut on textbox selection
    198        selectInputNode("input[type=text]", nextStep);
    199        return;
    200 
    201      case 5:
    202        waitForClipboard(
    203          "text text",
    204          function () {
    205            aDoCut(true);
    206          },
    207          nextStep
    208        );
    209        return;
    210 
    211      case 6:
    212        validateCutCopy(1, 0);
    213 
    214        // copy on password selection
    215        selectInputNode("input[type=password]", nextStep);
    216        return;
    217 
    218      case 7:
    219        waitForClipboard(
    220          null,
    221          function () {
    222            aDoCopy(execCommandAlwaysSucceed);
    223          },
    224          nextStep,
    225          true
    226        );
    227        return;
    228 
    229      case 8:
    230        validateCutCopy(0, 1);
    231 
    232        // cut on password selection
    233        selectInputNode("input[type=password]", nextStep);
    234        return;
    235 
    236      case 9:
    237        waitForClipboard(
    238          null,
    239          function () {
    240            aDoCut(execCommandAlwaysSucceed);
    241          },
    242          nextStep,
    243          true
    244        );
    245        return;
    246 
    247      case 10:
    248        validateCutCopy(1, 0);
    249 
    250        // copy on textarea selection
    251        selectInputNode("textarea", nextStep);
    252        return;
    253 
    254      case 11:
    255        waitForClipboard(
    256          "textarea text",
    257          function () {
    258            aDoCopy(true);
    259          },
    260          nextStep
    261        );
    262        return;
    263 
    264      case 12:
    265        validateCutCopy(0, 1);
    266 
    267        // cut on password selection
    268        selectInputNode("textarea", nextStep);
    269        return;
    270 
    271      case 13:
    272        waitForClipboard(
    273          "textarea text",
    274          function () {
    275            aDoCut(true);
    276          },
    277          nextStep
    278        );
    279        return;
    280 
    281      case 14:
    282        validateCutCopy(1, 0);
    283 
    284        // copy on no selection
    285        document.querySelector("textarea").blur();
    286 
    287        waitForClipboard(
    288          null,
    289          function () {
    290            aDoCopy(true);
    291          },
    292          nextStep,
    293          true
    294        );
    295        return;
    296 
    297      case 15:
    298        validateCutCopy(0, 1);
    299 
    300        // cut on no selection
    301        waitForClipboard(
    302          null,
    303          function () {
    304            aDoCut(execCommandAlwaysSucceed);
    305          },
    306          nextStep,
    307          true
    308        );
    309        return;
    310 
    311      case 16:
    312        validateCutCopy(1, 0);
    313 
    314        if (!document.querySelector("div[contentEditable=true]")) {
    315          // We're done! (no contentEditable node!)
    316          step(-1);
    317          return;
    318        }
    319        break;
    320 
    321      case 17:
    322        // copy on contenteditable selection
    323        waitForClipboard(
    324          "contenteditable text",
    325          function () {
    326            selectNode("div[contentEditable=true]", function () {
    327              aDoCopy(true);
    328            });
    329          },
    330          nextStep
    331        );
    332        return;
    333 
    334      case 18:
    335        validateCutCopy(0, 1);
    336        break;
    337 
    338      case 19:
    339        // cut on contenteditable selection
    340        waitForClipboard(
    341          "contenteditable text",
    342          function () {
    343            selectNode("div[contentEditable=true]", function () {
    344              aDoCut(true);
    345            });
    346          },
    347          nextStep
    348        );
    349        return;
    350 
    351      case 20:
    352        validateCutCopy(1, 0);
    353        break;
    354 
    355      default:
    356        aDone();
    357        return;
    358    }
    359 
    360    SimpleTest.executeSoon(function () {
    361      step(n + 1);
    362    });
    363  }
    364 
    365  step(0);
    366 }
    367 
    368 function allMechanisms(aCb, aClipOverride, aNegateAll) {
    369  function testStep(n) {
    370    gTestN1 = n;
    371    switch (n) {
    372      /** Test for Bug 1012662 */
    373      case 0:
    374        // Keyboard issued
    375        cutCopyAll(
    376          function docut() {
    377            synthesizeKey("x", { accelKey: true });
    378          },
    379          function docopy() {
    380            synthesizeKey("c", { accelKey: true });
    381          },
    382          function done() {
    383            testStep(n + 1);
    384          },
    385          false,
    386          aClipOverride,
    387          aNegateAll
    388        );
    389        return;
    390 
    391      case 1:
    392        // Button issued
    393        cutCopyAll(
    394          function docut(aSucc) {
    395            document.addEventListener("keydown", execCommand("cut", aSucc));
    396            sendString("Q");
    397          },
    398          function docopy(aSucc) {
    399            document.addEventListener("keydown", execCommand("copy", aSucc));
    400            sendString("Q");
    401          },
    402          function done() {
    403            testStep(n + 1);
    404          },
    405          false,
    406          aClipOverride,
    407          aNegateAll
    408        );
    409        return;
    410 
    411      case 2:
    412        // Not triggered by user gesture
    413        cutCopyAll(
    414          function doCut() {
    415            is(
    416              false,
    417              document.execCommand("cut"),
    418              "Can't directly execCommand not in a user callback"
    419            );
    420          },
    421          function doCopy() {
    422            is(
    423              false,
    424              document.execCommand("copy"),
    425              "Can't directly execCommand not in a user callback"
    426            );
    427          },
    428          function done() {
    429            testStep(n + 1);
    430          },
    431          true,
    432          aClipOverride,
    433          aNegateAll
    434        );
    435        return;
    436 
    437      /** Test for Bug 1597857 */
    438      case 3:
    439        // Button issued async
    440        cutCopyAll(
    441          function docut(aSucc) {
    442            document.addEventListener(
    443              "keydown",
    444              execCommand("cut", aSucc, true)
    445            );
    446            sendString("Q");
    447          },
    448          function docopy(aSucc) {
    449            document.addEventListener(
    450              "keydown",
    451              execCommand("copy", aSucc, true)
    452            );
    453            sendString("Q");
    454          },
    455          function done() {
    456            testStep(n + 1);
    457          },
    458          false,
    459          aClipOverride,
    460          aNegateAll
    461        );
    462        return;
    463 
    464      default:
    465        aCb();
    466    }
    467  }
    468  testStep(0);
    469 }
    470 
    471 // Run the tests
    472 SimpleTest.waitForExplicitFinish();
    473 SimpleTest.requestLongerTimeout(5); // On the emulator - this times out occasionally
    474 SimpleTest.waitForFocus(function () {
    475  function justCancel(aEvent) {
    476    aEvent.preventDefault();
    477  }
    478 
    479  function override(aEvent) {
    480    aEvent.clipboardData.setData("text/plain", "overridden");
    481    aEvent.preventDefault();
    482  }
    483 
    484  allMechanisms(function () {
    485    gTestN0 = 1;
    486    document.addEventListener("cut", override);
    487    document.addEventListener("copy", override);
    488 
    489    allMechanisms(function () {
    490      gTestN0 = 2;
    491      document.removeEventListener("cut", override);
    492      document.removeEventListener("copy", override);
    493      document.addEventListener("cut", justCancel);
    494      document.addEventListener("copy", justCancel);
    495 
    496      allMechanisms(
    497        function () {
    498          SimpleTest.finish();
    499        },
    500        null,
    501        true
    502      );
    503    }, "overridden");
    504  });
    505 });