tor-browser

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

test_dragstart.html (32263B)


      1 <html>
      2 <head>
      3  <title>Tests for the dragstart event</title>
      4  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
      5  <script src="/tests/SimpleTest/SimpleTest.js"></script>
      6  <script src="/tests/SimpleTest/EventUtils.js"></script>
      7 
      8 <!--
      9 This test checks the dragstart event and the DataTransfer object
     10  -->
     11 
     12 <script>
     13 
     14 SimpleTest.waitForExplicitFinish();
     15 
     16 var gDragInfo;
     17 var gDataTransfer = null;
     18 var gExtraDragTests = 0;
     19 var gIsMac = navigator.platform.includes("Mac");
     20 
     21 function runTests()
     22 {
     23  // first, create a selection and try dragging it
     24  var draggable = $("draggable");
     25  window.getSelection().selectAllChildren(draggable);
     26  synthesizeMouse(draggable, 6, 6, { type: "mousedown" });
     27  synthesizeMouse(draggable, 14, 14, { type: "mousemove" });
     28  // drags are asynchronous on Linux, so this extra event is needed to make
     29  // sure the drag gets processed
     30  synthesizeMouse(draggable, 15, 15, { type: "mousemove" });
     31 }
     32 
     33 function afterDragTests()
     34 {
     35  // the dragstart should have occurred due to moving the mouse. gDataTransfer
     36  // caches the dataTransfer that was used, however it should now be empty and
     37  // be read only.
     38  ok(gDataTransfer instanceof DataTransfer, "DataTransfer after dragstart event");
     39  checkTypes(gDataTransfer, [], 0, "after dragstart event");
     40 
     41  expectError(() => gDataTransfer.setData("text/plain", "Some Text"),
     42              "NoModificationAllowedError", "setData when read only");
     43  expectError(() => gDataTransfer.clearData("text/plain"),
     44              "NoModificationAllowedError", "clearData when read only");
     45  expectError(() => gDataTransfer.addElement(draggable),
     46              "NoModificationAllowedError", "addElement when read only");
     47 
     48  var evt = document.createEvent("dragevent");
     49  ok(evt instanceof DragEvent, "synthetic dragevent class")
     50  ok(evt instanceof MouseEvent, "synthetic event inherits from MouseEvent")
     51  evt.initDragEvent("dragstart", true, true, window, 1, 40, 35, 20, 15,
     52                    false, true, false, false, 0, null, null);
     53  $("synthetic").dispatchEvent(evt);
     54 
     55  var evt = document.createEvent("dragevent");
     56  ok(evt instanceof DragEvent, "synthetic dragevent class")
     57  evt.initDragEvent("dragover", true, true, window, 0, 40, 35, 20, 15,
     58                    true, false, true, true, 2, document.documentElement, null);
     59  $("synthetic2").dispatchEvent(evt);
     60 
     61  // next, dragging links and images
     62  sendMouseEventsForDrag("link");
     63  sendMouseEventsForDrag("image");
     64 
     65 //  disable testing input dragging for now, as it doesn't seem to be testable
     66 //  draggable = $("input");
     67 //  draggable.setSelectionRange(0, 4);
     68 //  synthesizeMouse(draggable, 8, 8, { type: "mousedown" });
     69 //  synthesizeMouse(draggable, 15, 15, { type: "mousemove" });
     70 //  sendMouseEventsForDrag("input");
     71 
     72  // draggable elements inside a shadow root
     73  sendMouseEventsForShadowRootDrag("shadow_host_containing_draggable");
     74  sendMouseEventsForShadowRootDrag("shadow_host_containing_image");
     75 
     76  // next, check if the draggable attribute can be used to adjust the drag target
     77  gDragInfo = { target: $("dragtrue"), testid: "draggable true node" };
     78  sendMouseEventsForDrag("dragtrue");
     79  gDragInfo = { target: $("dragtrue"), testid: "draggable true child" };
     80  sendMouseEventsForDrag("spantrue");
     81 
     82  gDragInfo = { target: $("dragfalse").firstChild, testid: "draggable false node" };
     83  sendMouseEventsForDrag("dragfalse");
     84  synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
     85 
     86  gDragInfo = { target: $("spanfalse").firstChild, testid: "draggable false child" };
     87  sendMouseEventsForDrag("spanfalse");
     88  synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
     89 
     90  gDragInfo = { target: $("userselectnone").firstChild, testid: "user select none inside draggable false node" };
     91  sendMouseEventsForDrag("userselectnone");
     92  synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
     93 
     94  gDragInfo = { target: $("draggable_with_undraggable_descendant"), testid: "undraggable inside draggable" };
     95  sendMouseEventsForDrag("undraggable_inside_draggable");
     96  synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
     97 
     98  sendMouseEventsForDrag("link_to_file");
     99  synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
    100 
    101  sendMouseEventsForDrag("broken_file_image");
    102  synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
    103 
    104  if (navigator.platform.startsWith("Win")) {
    105    sendMouseEventsForDrag("link_to_windows_file");
    106    synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
    107 
    108    sendMouseEventsForDrag("broken_windows_file_image");
    109    synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
    110 
    111    sendMouseEventsForDrag("link_to_backslash_windows_file");
    112    synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
    113 
    114    sendMouseEventsForDrag("broken_backslash_windows_file_image");
    115    synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
    116  }
    117 
    118  if (gExtraDragTests == 7)
    119    SimpleTest.finish();
    120 }
    121 
    122 function sendMouseEventsForDrag(nodeid)
    123 {
    124  info("Sending events for " + nodeid);
    125  var draggable = $(nodeid);
    126  synthesizeMouse(draggable, 3, 3, { type: "mousedown" });
    127  synthesizeMouse(draggable, 10, 10, { type: "mousemove" });
    128  synthesizeMouse(draggable, 12, 12, { type: "mousemove" });
    129 }
    130 function sendMouseEventsForShadowRootDrag(host)
    131 {
    132  var draggable = $(host).shadowRoot.firstElementChild;
    133  synthesizeMouse(draggable, 3, 3, { type: "mousedown" });
    134  synthesizeMouse(draggable, 10, 10, { type: "mousemove" });
    135  synthesizeMouse(draggable, 12, 12, { type: "mousemove" });
    136 }
    137 
    138 function doDragStartSelection(event)
    139 {
    140  is(event.type, "dragstart", "dragstart event type");
    141  is(event.target, $("draggable").firstChild, "dragstart event target");
    142  is(event.bubbles, true, "dragstart event bubbles");
    143  is(event.cancelable, true, "dragstart event cancelable");
    144 
    145  is(event.clientX, 14, "dragstart clientX");
    146  is(event.clientY, 14, "dragstart clientY");
    147  ok(event.screenX > 0, "dragstart screenX");
    148  ok(event.screenY > 0, "dragstart screenY");
    149  is(event.layerX, 14, "dragstart layerX");
    150  is(event.layerY, 14, "dragstart layerY");
    151  is(event.pageX, 14, "dragstart pageX");
    152  is(event.pageY, 14, "dragstart pageY");
    153 
    154  var dt = event.dataTransfer;
    155  ok(dt instanceof DataTransfer, "dataTransfer is DataTransfer");
    156  gDataTransfer = dt;
    157 
    158  var types = dt.types;
    159  ok(Array.isArray(types), "initial types is an Array");
    160  checkTypes(dt, ["text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial selection");
    161 
    162  const includeCommonAncestor = SpecialPowers.getBoolPref(
    163    "dom.serializer.includeCommonAncestor.enabled"
    164  );
    165  is(dt.getData("text/plain"), "This is a draggable bit of text.", "initial selection text/plain");
    166  is(dt.getData("text/html"),
    167     `${includeCommonAncestor ? "<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">" : ""}` +
    168     `This is a <em>draggable</em> bit of text.` +
    169     `${includeCommonAncestor ? "</div>" : ""}`,
    170     "initial selection text/html");
    171 
    172  // text/plain and Text are available for compatibility. They retrieve the
    173  // text/plain data. text/unicode is also for compatibility and retreives the plain text.
    174  is(dt.getData("text/plain"), "This is a draggable bit of text.", "initial selection text/plain");
    175  is(dt.getData("Text"), "This is a draggable bit of text.", "initial selection Text");
    176  is(dt.getData("TEXT"), "This is a draggable bit of text.", "initial selection TEXT");
    177  is(dt.getData("text/PLAIN"), "This is a draggable bit of text.", "initial selection text/PLAIN");
    178  is(dt.getData("text/unicode"), "This is a draggable bit of text.", "initial selection text/unicode");
    179  
    180  is(SpecialPowers.wrap(dt).mozItemCount, 1, "initial selection item count");
    181 
    182  dt.clearData("text/plain");
    183  dt.clearData("text/html");
    184  dt.clearData("text/_moz_htmlinfo");
    185  dt.clearData("text/_moz_htmlcontext");
    186 
    187  test_DataTransfer(dt);
    188  setTimeout(afterDragTests, 0);
    189 }
    190 
    191 function test_DataTransfer(dt)
    192 {
    193  is(SpecialPowers.wrap(dt).mozItemCount, 0, "empty itemCount");
    194 
    195  var types = dt.types;
    196  ok(Array.isArray(types), "empty types is an Array");
    197  // The above test fails if we have SpecialPowers.wrap(dt).types instead of dt.types
    198  // because dt.types will be wrapped and become a proxy.
    199  // So wrap with special powers after the test
    200  dt = SpecialPowers.wrap(dt);
    201  checkTypes(dt, [], 0, "empty");
    202  is(dt.getData("text/plain"), "", "empty data is empty");
    203 
    204  // calling setDataAt requires an index that is 0 <= index <= dt.itemCount
    205  expectError(() => dt.mozSetDataAt("text/plain", "Some Text", 1),
    206              "IndexSizeError", "setDataAt index too high");
    207 
    208  is(dt.mozUserCancelled, false, "userCancelled");
    209 
    210  // because an exception occurred, the data should not have been added
    211  is(dt.mozItemCount, 0, "empty setDataAt index too high itemCount");
    212  dt.getData("text/plain", "", "empty setDataAt index too high getData");
    213 
    214  // if the type is '', do nothing, or return ''
    215  dt.setData("", "Invalid Type");
    216  is(dt.types.length, 0, "invalid type setData");
    217  is(dt.getData(""), "", "invalid type getData");
    218  dt.mozSetDataAt("", "Invalid Type", 0);
    219  is(dt.types.length, 0, "invalid type setDataAt");
    220  is(dt.mozGetDataAt("", 0), null, "invalid type getDataAt");
    221 
    222  // similar with clearDataAt and getDataAt
    223  expectError(() => dt.mozGetDataAt("text/plain", 1),
    224              "IndexSizeError", "getDataAt index too high");
    225  expectError(() => dt.mozClearDataAt("text/plain", 1),
    226              "IndexSizeError", "clearDataAt index too high");
    227 
    228  dt.setData("text/plain", "Sample Text");
    229  is(dt.mozItemCount, 1, "added plaintext itemCount");
    230  checkOneDataItem(dt, ["text/plain"], ["Sample Text"], 0, "added plaintext");
    231 
    232   // after all those exceptions, the data should still be the same
    233  checkOneDataItem(dt, ["text/plain"], ["Sample Text"], 0, "added plaintext after exception");
    234 
    235  // modifying the data associated with the format should give it the new value
    236  dt.setData("text/plain", "Modified Text");
    237  is(dt.mozItemCount, 1, "modified plaintext itemCount");
    238  checkOneDataItem(dt, ["text/plain"], ["Modified Text"], 0, "modified plaintext");
    239 
    240  dt.setData("text/html", "<strong>Modified Text</strong>");
    241  is(dt.mozItemCount, 1, "modified html itemCount");
    242  checkOneDataItem(dt, ["text/plain", "text/html"],
    243                       ["Modified Text", "<strong>Modified Text</strong>"],
    244                       0, "modified html");
    245 
    246  // modifying data for a type that already exists should adjust it in place,
    247  // not reinsert it at the beginning
    248  dt.setData("text/plain", "New Text");
    249  is(dt.mozItemCount, 1, "modified text again itemCount");
    250  checkOneDataItem(dt, ["text/plain", "text/html"],
    251                       ["New Text", "<strong>Modified Text</strong>"],
    252                       0, "modified text again");
    253 
    254  var draggable = $("draggable");
    255  dt.setData("application/-moz-node", draggable);
    256  checkOneDataItem(dt, ["text/plain", "text/html", "application/-moz-node"],
    257                       ["New Text", "<strong>Modified Text</strong>", draggable.toString()],
    258                       0, "added node");
    259 
    260  dt.clearData(""); // null means clear all
    261  is(dt.mozItemCount, 0, "itemCount after clearData empty string");
    262  checkTypes(dt, [], 0, "empty after clearData empty string");
    263  
    264  dt.setData("text/plain", 22);
    265  dt.setData("text/html", 5.6);
    266  dt.setData("text/xml", 5.6);
    267  checkTypes(dt, ["text/plain", "text/html", "text/xml"], ["22", "5.6", ""], 0, "add numeric and empty data");
    268 
    269  dt.clearData(); // no argument means clear all
    270  is(dt.mozItemCount, 0, "itemCount after clearData no argument");
    271  checkTypes(dt, [], 0, "empty after clearData no argument");
    272 
    273  // check 'Text' type which should convert into text/plain
    274  dt.setData("Text", "Sample Text");
    275  checkOneDataItem(dt, ["text/plain"], ["Sample Text"], 0, "set Text");
    276  is(dt.getData("Text"), "Sample Text", "getData Text");
    277  is(dt.mozGetDataAt("Text", 0), "Sample Text", "getDataAt Text");
    278  dt.setData("text/plain", "More Text");
    279  checkOneDataItem(dt, ["text/plain"], ["More Text"], 0, "set text/plain after set Text");
    280 
    281  dt.mozClearDataAt("", 0); // null means clear all
    282  is(dt.mozItemCount, 0, "itemCount after clearDataAt empty string");
    283  checkTypes(dt, [], 0, "empty after clearDataAt empty string");
    284 
    285  // check text/uri-list type
    286  dt.setData("text/uri-list", "http://www.mozilla.org");
    287  checkURL(dt, "http://www.mozilla.org", "http://www.mozilla.org", 0, "set text/uri-list");
    288 
    289  // check URL type which should add text/uri-list data
    290  dt.setData("URL", "ftp://ftp.example.com");
    291  checkURL(dt, "ftp://ftp.example.com", "ftp://ftp.example.com", 0, "set URL");
    292  checkTypes(dt, ["text/uri-list"], ["ftp://ftp.example.com"], "url types");
    293 
    294  // clearing text/uri-list data
    295  dt.clearData("text/uri-list");
    296  is(dt.mozItemCount, 0, "itemCount after clear url-list");
    297  is(dt.getData("text/uri-list"), "", "text/uri-list after clear url-list");
    298  is(dt.getData("URL"), "", "URL after clear url-list");
    299 
    300  // check text/uri-list parsing
    301  dt.setData("text/uri-list", "#http://www.mozilla.org\nhttp://www.xulplanet.com\nhttp://www.example.com");
    302  checkURL(dt, "http://www.xulplanet.com",
    303           "#http://www.mozilla.org\nhttp://www.xulplanet.com\nhttp://www.example.com",
    304           0, "uri-list 3 lines");
    305 
    306  dt.setData("text/uri-list", "#http://www.mozilla.org");
    307  is(dt.getData("URL"), "", "uri-list commented");
    308  dt.setData("text/uri-list", "#http://www.mozilla.org\n");
    309  is(dt.getData("URL"), "", "uri-list commented with newline");
    310 
    311  // check that clearing the URL type also clears the text/uri-list type
    312  dt.clearData("URL");
    313  is(dt.getData("text/uri-list"), "", "clear URL");
    314 
    315  dt.setData("text/uri-list", "#http://www.mozilla.org\n\n\n\n\n");
    316  is(dt.getData("URL"), "", "uri-list with blank lines");
    317  dt.setData("text/uri-list", "");
    318  is(dt.getData("URL"), "", "empty uri-list");
    319  dt.setData("text/uri-list", "#http://www.mozilla.org\n#Sample\nhttp://www.xulplanet.com  \r\n");
    320  is(dt.getData("URL"), "http://www.xulplanet.com", "uri-list mix");
    321  dt.setData("text/uri-list", "\nhttp://www.mozilla.org");
    322  is(dt.getData("URL"), "", "empty line to start uri-list");
    323  dt.setData("text/uri-list", "  http://www.mozilla.org#anchor  ");
    324  is(dt.getData("URL"), "http://www.mozilla.org#anchor", "uri-list with spaces and hash");
    325 
    326  // ensure that setDataAt works the same way
    327  dt.mozSetDataAt("text/uri-list", "#http://www.mozilla.org\n#Sample\nhttp://www.xulplanet.com  \r\n", 0);
    328  checkURL(dt, "http://www.xulplanet.com",
    329           "#http://www.mozilla.org\n#Sample\nhttp://www.xulplanet.com  \r\n",
    330           0, "uri-list mix setDataAt");
    331 
    332  // now test adding multiple items to be dragged using the setDataAt method
    333  dt.clearData();
    334  dt.mozSetDataAt("text/plain", "First Item", 0);
    335  dt.mozSetDataAt("text/plain", "Second Item", 1);
    336  expectError(() => dt.mozSetDataAt("text/plain", "Some Text", 3),
    337              "IndexSizeError", "setDataAt index too high with two items");
    338  is(dt.mozItemCount, 2, "setDataAt item itemCount");
    339  checkOneDataItem(dt, ["text/plain"], ["First Item"], 0, "setDataAt item at index 0");
    340  checkOneDataItem(dt, ["text/plain"], ["Second Item"], 1, "setDataAt item at index 1");
    341 
    342  dt.mozSetDataAt("text/html", "<em>First Item</em>", 0);
    343  dt.mozSetDataAt("text/html", "<em>Second Item</em>", 1);
    344  is(dt.mozItemCount, 2, "setDataAt two types item itemCount");
    345  checkOneDataItem(dt, ["text/plain", "text/html"],
    346                   ["First Item", "<em>First Item</em>"], 0, "setDataAt two types item at index 0");
    347  checkOneDataItem(dt, ["text/plain", "text/html"],
    348                   ["Second Item", "<em>Second Item</em>"], 1, "setDataAt two types item at index 1");
    349 
    350  dt.mozSetDataAt("text/html", "<em>Changed First Item</em>", 0);
    351  dt.mozSetDataAt("text/plain", "Changed Second Item", 1);
    352  is(dt.mozItemCount, 2, "changed with setDataAt item itemCount");
    353  checkOneDataItem(dt, ["text/plain", "text/html"],
    354                   ["First Item", "<em>Changed First Item</em>"], 0, "changed with setDataAt item at index 0");
    355  checkOneDataItem(dt, ["text/plain", "text/html"],
    356                   ["Changed Second Item", "<em>Second Item</em>"], 1, "changed with setDataAt item at index 1");
    357 
    358  dt.setData("text/html", "Changed with setData");
    359  is(dt.mozItemCount, 2, "changed with setData");
    360  checkOneDataItem(dt, ["text/plain", "text/html"],
    361                   ["First Item", "Changed with setData"], 0, "changed with setData item at index 0");
    362  checkOneDataItem(dt, ["text/plain", "text/html"],
    363                   ["Changed Second Item", "<em>Second Item</em>"], 1, "changed with setData item at index 1");
    364 
    365  dt.mozSetDataAt("application/-moz-node", "draggable", 2);
    366  is(dt.mozItemCount, 3, "setDataAt node itemCount");
    367  checkOneDataItem(dt, ["application/-moz-node"], ["draggable"], 2, "setDataAt node item at index 2");
    368 
    369  // Try to add and then remove a non-string type to the DataTransfer and ensure
    370  // that the type appears in DataTransfer.types.
    371  {
    372    dt.mozSetDataAt("application/-x-body", document.body, 0);
    373    let found = false;
    374    for (let i = 0; i < dt.types.length; ++i) {
    375      if (dt.types[i] == "application/-x-body") {
    376        found = true;
    377        break;
    378      }
    379    }
    380    ok(found, "Data should appear in datatransfer.types despite having a non-string type");
    381    dt.mozClearDataAt("application/-x-body", 0);
    382  }
    383 
    384  dt.mozClearDataAt("text/html", 1);
    385  is(dt.mozItemCount, 3, "clearDataAt itemCount");
    386  checkOneDataItem(dt, ["text/plain", "text/html"],
    387                   ["First Item", "Changed with setData"], 0, "clearDataAt item at index 0");
    388  checkOneDataItem(dt, ["text/plain"], ["Changed Second Item"], 1, "clearDataAt item at index 1");
    389 
    390  dt.mozClearDataAt("text/plain", 1);
    391  is(dt.mozItemCount, 2, "clearDataAt last type itemCount");
    392  checkOneDataItem(dt, ["text/plain", "text/html"],
    393                   ["First Item", "Changed with setData"], 0, "clearDataAt last type at index 0");
    394  checkOneDataItem(dt, ["application/-moz-node"], ["draggable"], 1, "clearDataAt last type item at index 2");
    395  expectError(() => dt.mozGetDataAt("text/plain", 2),
    396              "IndexSizeError", "getDataAt after item removed index too high");
    397 
    398  dt.mozSetDataAt("text/unknown", "Unknown type", 2);
    399  dt.mozSetDataAt("text/unknown", "Unknown type", 1);
    400  is(dt.mozItemCount, 3, "add unknown type");
    401  checkOneDataItem(dt, ["application/-moz-node", "text/unknown"],
    402                   ["draggable", "Unknown type"], 1, "add unknown type item at index 1");
    403  checkOneDataItem(dt, ["text/unknown"], ["Unknown type"], 2, "add unknown type item at index 2");
    404 
    405  dt.mozClearDataAt("", 1);
    406  is(dt.mozItemCount, 2, "clearDataAt empty string");
    407  checkOneDataItem(dt, ["text/plain", "text/html"],
    408                   ["First Item", "Changed with setData"], 0, "clearDataAt empty string item at index 0");
    409  checkOneDataItem(dt, ["text/unknown"],
    410                   ["Unknown type"], 1, "clearDataAt empty string item at index 1");
    411 
    412  // passing a format that doesn't exist to clearData or clearDataAt should just
    413  // do nothing
    414  dt.clearData("text/something");
    415  dt.mozClearDataAt("text/something", 1);
    416  is(dt.mozItemCount, 2, "clearData type that does not exist");
    417  checkOneDataItem(dt, ["text/plain", "text/html"],
    418                   ["First Item", "Changed with setData"], 0, "clearData type that does not exist item at index 0");
    419  checkOneDataItem(dt, ["text/unknown"],
    420                   ["Unknown type"], 1, "clearData type that does not exist item at index 1");
    421 
    422  expectError(() => dt.mozClearDataAt("text/plain", 3),
    423              "IndexSizeError", "clearData index too high with two items");
    424 
    425  // ensure that clearData() removes all data associated with the first item, but doesn't
    426  // shift the second item down into the first item's slot.
    427  dt.clearData();
    428  is(dt.mozItemCount, 2, "clearData no argument with multiple items itemCount");
    429  checkOneDataItem(dt, [], [], 0,
    430                   "clearData no argument with multiple items item at index 0");
    431  checkOneDataItem(dt, ["text/unknown"],
    432                   ["Unknown type"], 1, "clearData no argument with multiple items item at index 1");
    433 
    434  // remove tha remaining data in index 1. As index 0 is empty at this point, this will actually
    435  // drop mozItemCount to 0. (XXX: This is because of spec-compliance reasons related
    436  // to the more-recent dt.item API. It's an unfortunate, but hopefully rare edge case)
    437  dt.mozClearDataAt("", 1);
    438  is(dt.mozItemCount, 0, "all data cleared");
    439 
    440  // now check the effectAllowed and dropEffect properties
    441  is(dt.dropEffect, "none", "initial dropEffect");
    442  is(dt.effectAllowed, "uninitialized", "initial effectAllowed");
    443 
    444  ["copy", "none", "link", "", "other", "copyMove", "all", "uninitialized", "move"].forEach(
    445    function (i) {
    446      dt.dropEffect = i;
    447      is(dt.dropEffect, i == "" || i == "other" || i == "copyMove" ||
    448                        i == "all" || i == "uninitialized" ? "link" : i,
    449         "dropEffect set to " + i);
    450      is(dt.effectAllowed, "uninitialized", "effectAllowed not modified by dropEffect set to " + i);
    451    }
    452  );
    453 
    454  ["move", "copy", "link", "", "other", "moveCopy", "copyMove",
    455   "linkMove", "copyLink", "all", "uninitialized", "none"].forEach(
    456    function (i) {
    457      dt.effectAllowed = i;
    458      is(dt.dropEffect, "move", "dropEffect not modified by effectAllowed set to " + i);
    459      is(dt.effectAllowed, i == "" || i == "other" || i == "moveCopy" ? "link" : i,
    460         "effectAllowed set to " + i);
    461    }
    462  );
    463 }
    464 
    465 function doDragStartLink(event)
    466 {
    467  var dt = event.dataTransfer;
    468  checkTypes(dt, ["text/x-moz-url", "text/x-moz-url-data", "text/x-moz-url-desc", "text/uri-list",
    469                  "text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial link");
    470 
    471  is(SpecialPowers.wrap(dt).mozItemCount, 1, "initial link item count");
    472  is(dt.getData("text/uri-list"), "http://www.mozilla.org/", "link text/uri-list");
    473  is(dt.getData("text/plain"), "http://www.mozilla.org/", "link text/plain");
    474 
    475  event.preventDefault();
    476 
    477  gExtraDragTests++;
    478 }
    479 
    480 function doDragStartImage(event)
    481 {
    482  var dataurl = $("image").src;
    483 
    484  var dt = event.dataTransfer;
    485  checkTypes(dt, ["text/x-moz-url", "text/x-moz-url-data", "text/x-moz-url-desc", "text/uri-list",
    486                  "text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain",
    487                  "application/x-moz-nativeimage", "application/x-moz-file-promise",
    488                  "application/x-moz-file-promise-url", "application/x-moz-file-promise-dest-filename"],
    489             0, "initial image");
    490 
    491  is(SpecialPowers.wrap(dt).mozItemCount, 1, "initial image item count");
    492  is(dt.getData("text/uri-list"), dataurl, "image text/uri-list");
    493  is(dt.getData("text/plain"), dataurl, "image text/plain");
    494 
    495  event.preventDefault();
    496 
    497  gExtraDragTests++;
    498 }
    499 
    500 function doDragStartInput(event)
    501 {
    502  var dt = event.dataTransfer;
    503  checkTypes(dt, ["text/plain"], 0, "initial input");
    504 
    505  is(SpecialPowers.wrap(dt).mozItemCount, 1, "initial input item count");
    506 //  is(dt.getData("text/plain"), "Text", "input text/plain");
    507 
    508 //  event.preventDefault();
    509 }
    510 
    511 
    512 function doDragStartInShadowRoot(event)
    513 {
    514  is(event.type, "dragstart", "shadow root dragstart event type");
    515  is(event.target, $("shadow_host_containing_draggable"), "shadow root dragstart event target");
    516  is(event.bubbles, true, "shadow root dragstart event bubbles");
    517  is(event.cancelable, true, "shadow root dragstart event cancelable");
    518 
    519  event.preventDefault();
    520 
    521  gExtraDragTests++;
    522 }
    523 
    524 function doDragStartShadowRootImage(event)
    525 {
    526  var dt = event.dataTransfer;
    527 
    528  checkTypes(dt, ["text/x-moz-url", "text/x-moz-url-data", "text/x-moz-url-desc", "text/uri-list",
    529                  "text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain",
    530                  "application/x-moz-nativeimage", "application/x-moz-file-promise",
    531                  "application/x-moz-file-promise-url", "application/x-moz-file-promise-dest-filename"],
    532             0, "shadow root link with image");
    533 
    534  is(dt.getData("text/uri-list"), "http://example.org/", "shadow root link text/uri-list");
    535  is(dt.getData("text/plain"), "http://example.org/", "shadow root link text/plain");
    536 
    537  event.preventDefault();
    538 
    539  gExtraDragTests++;
    540 }
    541 
    542 function doDragStartSynthetic(event)
    543 {
    544  is(event.type, "dragstart", "synthetic dragstart event type");
    545 
    546  var dt = event.dataTransfer;
    547  todo(dt instanceof DataTransfer, "synthetic dragstart dataTransfer is DataTransfer");
    548 //  Uncomment next line once the todo instanceof above is fixed.
    549 //  checkTypes(dt, [], 0, "synthetic dragstart");
    550 
    551  is(event.detail, 1, "synthetic dragstart detail");
    552  is(event.screenX, 40, "synthetic dragstart screenX");
    553  is(event.screenY, 35, "synthetic dragstart screenY");
    554  is(event.clientX, 20, "synthetic dragstart clientX");
    555  is(event.clientY, 15, "synthetic dragstart clientY");
    556  is(event.ctrlKey, false, "synthetic dragstart ctrlKey");
    557  is(event.altKey, true, "synthetic dragstart altKey");
    558  is(event.shiftKey, false, "synthetic dragstart shiftKey");
    559  is(event.metaKey, false, "synthetic dragstart metaKey");
    560  is(event.button, 0, "synthetic dragstart button ");
    561  is(event.relatedTarget, null, "synthetic dragstart relatedTarget");
    562 
    563 //  Uncomment next two lines once the todo instanceof above is fixed.
    564 //  dt.setData("text/plain", "Text");
    565 //  is(dt.getData("text/plain"), "Text", "synthetic dragstart data is set after adding");
    566 }
    567 
    568 function doDragOverSynthetic(event)
    569 {
    570  is(event.type, "dragover", "synthetic dragover event type");
    571 
    572  var dt = event.dataTransfer;
    573  todo(dt instanceof DataTransfer, "synthetic dragover dataTransfer is DataTransfer");
    574 //  Uncomment next line once the todo instanceof above is fixed.
    575 // checkTypes(dt, [], 0, "synthetic dragover");
    576 
    577  is(event.detail, 0, "synthetic dragover detail");
    578  is(event.screenX, 40, "synthetic dragover screenX");
    579  is(event.screenY, 35, "synthetic dragover screenY");
    580  is(event.clientX, 20, "synthetic dragover clientX");
    581  is(event.clientY, 15, "synthetic dragover clientY");
    582  is(event.ctrlKey, true, "synthetic dragover ctrlKey");
    583  is(event.altKey, false, "synthetic dragover altKey");
    584  is(event.shiftKey, true, "synthetic dragover shiftKey");
    585  is(event.metaKey, true, "synthetic dragover metaKey");
    586  is(event.button, 2, "synthetic dragover button");
    587  is(event.relatedTarget, document.documentElement, "synthetic dragover relatedTarget");
    588 
    589 //  Uncomment next two lines once the todo instanceof above is fixed.
    590 //  dt.setData("text/plain", "Text");
    591 //  is(dt.getData("text/plain"), "Text", "synthetic dragover data is set after adding");
    592 }
    593 
    594 function onDragStartDraggable(event)
    595 {
    596  var dt = event.dataTransfer;
    597  ok(SpecialPowers.wrap(dt).mozItemCount == 0 && !dt.types.length && event.originalTarget == gDragInfo.target, gDragInfo.testid);
    598 
    599  event.preventDefault();
    600  gExtraDragTests++;
    601 }
    602 
    603 // Expects dt wrapped in SpecialPowers
    604 function checkOneDataItem(dt, expectedtypes, expecteddata, index, testid)
    605 {
    606  checkTypes(dt, expectedtypes, index, testid);
    607  for (var f = 0; f < expectedtypes.length; f++) {
    608    if (index == 0)
    609      is(dt.getData(expectedtypes[f]), expecteddata[f], testid + " getData " + expectedtypes[f]);
    610    is(dt.mozGetDataAt(expectedtypes[f], index), expecteddata[f] ? expecteddata[f] : null,
    611       testid + " getDataAt " + expectedtypes[f]);
    612  }
    613 }
    614 
    615 function checkTypes(dt, expectedtypes, index, testid)
    616 {
    617  if (index == 0) {
    618    var types = dt.types;
    619    is(types.length, expectedtypes.length, testid + " types length");
    620    for (var f = 0; f < expectedtypes.length; f++) {
    621      is(types[f], expectedtypes[f], testid + " " + types[f] + " check");
    622    }
    623  }
    624 
    625  types = SpecialPowers.wrap(dt).mozTypesAt(index);
    626  if (gIsMac && expectedtypes.find((t) => t === "application/x-moz-nativeimage")) {
    627    expectedtypes.push("text/x-moz-requestmime");
    628  }
    629  is(types.length, expectedtypes.length, testid + " typesAt length");
    630  for (var f = 0; f < expectedtypes.length; f++) {
    631    is(types[f], expectedtypes[f], testid + " " + types[f] + " at " + index + " check");
    632  }
    633 }
    634 
    635 // Expects dt wrapped in SpecialPowers
    636 function checkURL(dt, url, fullurllist, index, testid)
    637 {
    638  is(dt.getData("text/uri-list"), fullurllist, testid + " text/uri-list");
    639  is(dt.getData("URL"), url, testid + " URL");
    640  is(dt.mozGetDataAt("text/uri-list", 0), fullurllist, testid + " text/uri-list");
    641  is(dt.mozGetDataAt("URL", 0), fullurllist, testid + " URL");
    642 }
    643 
    644 function onDragOverDraggableFalse(event) {
    645  ok(false, "Triggered dragstart on draggable=false node " + event.target.id);
    646 }
    647 
    648 function onDragStartUnlinkable(event) {
    649  ok(false, "Triggered dragstart on undraggable node " + event.target.id);
    650 }
    651 
    652 function expectError(fn, eid, testid)
    653 {
    654  var error = "";
    655  try {
    656    fn();
    657  } catch (ex) {
    658    error = ex.name;
    659  }
    660  is(error, eid, testid + " causes exception " + eid);
    661 }
    662 
    663 </script>
    664 
    665 </head>
    666 
    667 <body style="height: 300px; overflow: auto;" onload="setTimeout(runTests, 0)">
    668 
    669 <div id="draggable" ondragstart="doDragStartSelection(event)">This is a <em>draggable</em> bit of text.</div>
    670 
    671 <fieldset>
    672 <a id="link" href="http://www.mozilla.org/" ondragstart="doDragStartLink(event)">mozilla.org</a>
    673 </fieldset>
    674 
    675 <label>
    676 <img id="image" src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%18%00%00%00%18%02%03%00%00%00%9D%19%D5k%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%0CPLTE%FF%FF%FF%FF%FF%FF%F7%DC%13%00%00%00%03%80%01X%00%00%00%01tRNS%08N%3DPT%00%00%00%01bKGD%00%88%05%1DH%00%00%00%09pHYs%00%00%0B%11%00%00%0B%11%01%7Fd_%91%00%00%00%07tIME%07%D2%05%0C%14%0C%0D%D8%3F%1FQ%00%00%00%5CIDATx%9C%7D%8E%CB%09%C0%20%10D%07r%B7%20%2F%E9wV0%15h%EA%D9%12D4%BB%C1x%CC%5C%1E%0C%CC%07%C0%9C0%9Dd7()%C0A%D3%8D%E0%B8%10%1DiCHM%D0%AC%D2d%C3M%F1%B4%E7%FF%10%0BY%AC%25%93%CD%CBF%B5%B2%C0%3Alh%CD%AE%13%DF%A5%F7%E0%03byW%09A%B4%F3%E2%00%00%00%00IEND%AEB%60%82"
    677     ondragstart="doDragStartImage(event)">
    678 </label>
    679 
    680 <input id="input" value="Text in a box" ondragstart="doDragStartInput(event)">
    681 
    682 <div ondragstart="onDragStartDraggable(event)">
    683  <div id="dragtrue" draggable="true">
    684    This is a <span id="spantrue">draggable</span> area.
    685  </div>
    686  <div id="dragfalse" draggable="false">
    687    This is a <span id="spanfalse">non-draggable</span> area.
    688  </div>
    689 </div>
    690 
    691 <!--iframe src="http://www.mozilla.org" width="400" height="400"></iframe-->
    692 
    693 <div id="synthetic" ondragstart="doDragStartSynthetic(event)">Synthetic Event Dispatch</div>
    694 <div id="synthetic2" ondragover="doDragOverSynthetic(event)">Synthetic Event Dispatch</div>
    695 
    696 <div draggable="true" id="shadow_host_containing_draggable"></div>
    697 
    698 <script>
    699 shadow_host_containing_draggable.attachShadow({ mode: 'open' }).innerHTML =
    700 `<span>Inside shadow root</span>`;
    701 shadow_host_containing_draggable.addEventListener("dragstart", doDragStartInShadowRoot);
    702 </script>
    703 
    704 <a href="http://example.org" ondragstart="doDragStartShadowRootImage(event)">
    705  <div id="shadow_host_containing_image"></div>
    706 </a>
    707 
    708 <script>
    709 shadow_host_containing_image.attachShadow({ mode: 'open' }).innerHTML =
    710 `<img src="${$("image").src}" alt="Alt Text"></img>`;
    711 </script>
    712 
    713 <a href="http://example.org" id="link_user_select_none_child" draggable="false" ondragstart="onDragOverDraggableFalse(event)">
    714  <span id="userselectnone" style="user-select: none">
    715    This is an unselectable, undraggable area.
    716  </span>
    717 </a>
    718 
    719 <div id="draggable_with_undraggable_descendant" draggable="true" ondragstart="onDragStartDraggable(event)">
    720  <a id="undraggable_inside_draggable" href="http://example.org" draggable="false" ondragstart="onDragOverDraggableFalse(event)">
    721    This is an undraggable link inside a draggable ancestor.
    722  </a>
    723 </div>
    724 
    725 <a id="link_to_file" href="file:///" ondragstart="onDragStartUnlinkable(event)">This is an undraggable 'file' link</a>
    726 <a id="link_to_windows_file" href="c:/Users/" ondragstart="onDragStartUnlinkable(event)">This is an undraggable 'file' link</a>
    727 <a id="link_to_backslash_windows_file" href="c:\\Users\\" ondragstart="onDragStartUnlinkable(event)">This is an undraggable 'file' link</a>
    728 <img id="broken_file_image" src="file:///" ondragstart="onDragStartUnlinkable(event)">
    729 <img id="broken_windows_file_image" src="c:/Users/" ondragstart="onDragStartUnlinkable(event)">
    730 <img id="broken_backslash_windows_file_image" src="c:\\Users\\" ondragstart="onDragStartUnlinkable(event)">
    731 </body>
    732 </html>