tor-browser

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

copypaste_flat_tree.js (9658B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 function getLoadContext() {
      5  var Ci = SpecialPowers.Ci;
      6  return SpecialPowers.wrap(window).docShell.QueryInterface(Ci.nsILoadContext);
      7 }
      8 
      9 var clipboard = SpecialPowers.Services.clipboard;
     10 var documentViewer = SpecialPowers.wrap(
     11  window
     12 ).docShell.docViewer.QueryInterface(SpecialPowers.Ci.nsIDocumentViewerEdit);
     13 
     14 function getClipboardData(mime) {
     15  var transferable = SpecialPowers.Cc[
     16    "@mozilla.org/widget/transferable;1"
     17  ].createInstance(SpecialPowers.Ci.nsITransferable);
     18  transferable.init(getLoadContext());
     19  transferable.addDataFlavor(mime);
     20  clipboard.getData(
     21    transferable,
     22    1,
     23    SpecialPowers.wrap(window).browsingContext.currentWindowContext
     24  );
     25  var data = SpecialPowers.createBlankObject();
     26  transferable.getTransferData(mime, data);
     27  return data;
     28 }
     29 
     30 function testClipboardValue(suppressHTMLCheck, mime, expected) {
     31  if (suppressHTMLCheck && mime == "text/html") {
     32    return null;
     33  }
     34  var data = SpecialPowers.wrap(getClipboardData(mime));
     35  is(
     36    data.value == null
     37      ? data.value
     38      : data.value.QueryInterface(SpecialPowers.Ci.nsISupportsString).data,
     39    expected,
     40    mime + " value in the clipboard"
     41  );
     42  return data.value;
     43 }
     44 
     45 function testSelectionToString(expected) {
     46  const flags =
     47    SpecialPowers.Ci.nsIDocumentEncoder.SkipInvisibleContent |
     48    SpecialPowers.Ci.nsIDocumentEncoder.AllowCrossShadowBoundary;
     49  is(
     50    SpecialPowers.wrap(window)
     51      .getSelection()
     52      .toStringWithFormat("text/plain", flags, 0)
     53      .replace(/\r\n/g, "\n"),
     54    expected,
     55    "Selection.toString"
     56  );
     57 }
     58 
     59 function testHtmlClipboardValue(suppressHTMLCheck, mime, expected) {
     60  // For Windows, navigator.platform returns "Win32".
     61  var expectedValue = expected;
     62  if (navigator.platform.includes("Win")) {
     63    // Windows has extra content.
     64    expectedValue =
     65      kTextHtmlPrefixClipboardDataWindows +
     66      expected.replace(/\n/g, "\n") +
     67      kTextHtmlSuffixClipboardDataWindows;
     68  }
     69  testClipboardValue(suppressHTMLCheck, mime, expectedValue);
     70 }
     71 
     72 function testPasteText(textarea, expected) {
     73  textarea.value = "";
     74  textarea.focus();
     75  textarea.editor.paste(1);
     76  is(textarea.value, expected, "value of the textarea after the paste");
     77 }
     78 
     79 async function copySelectionToClipboard() {
     80  await SimpleTest.promiseClipboardChange(
     81    () => true,
     82    () => {
     83      documentViewer.copySelection();
     84    }
     85  );
     86  ok(clipboard.hasDataMatchingFlavors(["text/plain"], 1), "check text/plain");
     87  ok(clipboard.hasDataMatchingFlavors(["text/html"], 1), "check text/html");
     88 }
     89 
     90 async function testCopyPasteShadowDOM() {
     91  var textarea = SpecialPowers.wrap(document.getElementById("input"));
     92 
     93  function clear() {
     94    textarea.blur();
     95    var sel = window.getSelection();
     96    sel.removeAllRanges();
     97  }
     98 
     99  async function copySelectionToClipboardShadow(
    100    anchorNode,
    101    anchorOffset,
    102    focusNode,
    103    focusOffset
    104  ) {
    105    clear();
    106    var sel = window.getSelection();
    107    sel.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
    108    await copySelectionToClipboard();
    109  }
    110 
    111  let start1 = document.getElementById("start1");
    112  let end1 = document.getElementById("end1");
    113  let host1 = document.getElementById("host1");
    114 
    115  info("Test 1: Start is in Light DOM and end is a slotted node.");
    116  await copySelectionToClipboardShadow(
    117    start1.firstChild,
    118    2,
    119    end1.firstChild,
    120    2
    121  );
    122  testSelectionToString("art\nEn");
    123  testClipboardValue(false, "text/plain", "art\nEn");
    124  testHtmlClipboardValue(
    125    false,
    126    "text/html",
    127    '<span id="start1">art</span>\n  <div id="host1">\n      <slot>\n    \n    <span id="end1">En</span></slot></div>'
    128  );
    129  testPasteText(textarea, "art\nEn");
    130 
    131  info(
    132    "Test 2: Start is in Light DOM and end is a slotted node, while there's a Shadow DOM node before the slotted node."
    133  );
    134  await copySelectionToClipboardShadow(
    135    start1.firstChild,
    136    2,
    137    host1.shadowRoot.getElementById("inner1").firstChild,
    138    3
    139  );
    140  testSelectionToString("art\nEnd Inn");
    141  testClipboardValue(false, "text/plain", "art\nEnd Inn");
    142  testHtmlClipboardValue(
    143    false,
    144    "text/html",
    145    '<span id="start1">art</span>\n  <div id="host1">\n      <slot>\n    \n    <span id="end1">End</span>\n  </slot>\n      <span id="inner1">Inn</span></div>'
    146  );
    147  testPasteText(textarea, "art\nEnd Inn");
    148 
    149  info("Test 3: Start is a slotted node and end is in shadow DOM.");
    150  await copySelectionToClipboardShadow(
    151    end1.firstChild,
    152    2,
    153    host1.shadowRoot.getElementById("inner1").firstChild,
    154    3
    155  );
    156  testSelectionToString("d Inn");
    157  testClipboardValue(false, "text/plain", "d Inn");
    158  testHtmlClipboardValue(
    159    false,
    160    "text/html",
    161    '<slot><span id="end1">d</span>\n  </slot>\n      <span id="inner1">Inn</span>'
    162  );
    163  testPasteText(textarea, "d Inn");
    164 
    165  let start2 = document.getElementById("start2");
    166  let host2 = document.getElementById("host2");
    167  let host2_slot2 = document.getElementById("host2_slot2");
    168  let host2_slot4 = document.getElementById("host2_slot4");
    169 
    170  info(
    171    "Test 4: start is in light DOM and end is a slotted node with multiple assigned nodes in the same slot.\n"
    172  );
    173  await copySelectionToClipboardShadow(
    174    start2.firstChild,
    175    2,
    176    host2_slot2.firstChild,
    177    5
    178  );
    179  testSelectionToString("art\nSlotted1Slott");
    180  testClipboardValue(false, "text/plain", "art\nSlotted1Slott");
    181  testHtmlClipboardValue(
    182    false,
    183    "text/html",
    184    '<span id="start2">art</span>\n  <div id="host2">\n      <slot name="slot1"><span id="host2_slot1" slot="slot1">Slotted1</span><span id="host2_slot2" slot="slot1">Slott</span></slot></div>'
    185  );
    186  testPasteText(textarea, "art\nSlotted1Slott");
    187 
    188  info(
    189    "Test 5: start is in light DOM and end is a slotted node with endOffset includes the entire slotted node\n"
    190  );
    191  await copySelectionToClipboardShadow(
    192    start2.firstChild,
    193    2,
    194    host2_slot2.firstChild,
    195    8
    196  );
    197  testSelectionToString("art\nSlotted1Slotted2");
    198  testClipboardValue(false, "text/plain", "art\nSlotted1Slotted2");
    199  testHtmlClipboardValue(
    200    false,
    201    "text/html",
    202    '<span id="start2">art</span>\n  <div id="host2">\n      <slot name="slot1"><span id="host2_slot1" slot="slot1">Slotted1</span><span id="host2_slot2" slot="slot1">Slotted2</span></slot></div>'
    203  );
    204  testPasteText(textarea, "art\nSlotted1Slotted2");
    205 
    206  info("Test 6: start is in light DOM and end is a shadow node.\n");
    207  await copySelectionToClipboardShadow(
    208    start2.firstChild,
    209    2,
    210    host2.shadowRoot.getElementById("inner2").firstChild,
    211    3
    212  );
    213  testSelectionToString("art\nSlotted1Slotted2 Inn");
    214  testClipboardValue(false, "text/plain", "art\nSlotted1Slotted2 Inn");
    215  testHtmlClipboardValue(
    216    false,
    217    "text/html",
    218    '<span id="start2">art</span>\n  <div id="host2">\n      <slot name="slot1"><span id="host2_slot1" slot="slot1">Slotted1</span><span id="host2_slot2" slot="slot1">Slotted2</span></slot>\n      <span id="inner2">Inn</span></div>'
    219  );
    220  testPasteText(textarea, "art\nSlotted1Slotted2 Inn");
    221 
    222  info("Test 7: start is in light DOM and end is a slotted node.\n");
    223  await copySelectionToClipboardShadow(
    224    start2.firstChild,
    225    2,
    226    host2_slot4.firstChild,
    227    8
    228  );
    229  testSelectionToString("art\nSlotted1Slotted2 Inner Slotted3Slotted4");
    230  testClipboardValue(
    231    false,
    232    "text/plain",
    233    "art\nSlotted1Slotted2 Inner Slotted3Slotted4"
    234  );
    235  testHtmlClipboardValue(
    236    false,
    237    "text/html",
    238    '<span id="start2">art</span>\n  <div id="host2">\n      <slot name="slot1"><span id="host2_slot1" slot="slot1">Slotted1</span><span id="host2_slot2" slot="slot1">Slotted2</span></slot>\n      <span id="inner2">Inner</span>\n      <slot name="slot2"><span slot="slot2">Slotted3</span><span id="host2_slot4" slot="slot2">Slotted4</span></slot></div>'
    239  );
    240  testPasteText(textarea, "art\nSlotted1Slotted2 Inner Slotted3Slotted4");
    241 
    242  let host3 = document.getElementById("host3");
    243  let host3_slot1 = document.getElementById("host3_slot1");
    244  let host3_slot4 = document.getElementById("host3_slot4");
    245 
    246  info(
    247    "Test 8: Both start and end are slotted nodes, and their DOM tree order is reversed compare to flat tree order.\n"
    248  );
    249  await copySelectionToClipboardShadow(
    250    host3_slot1.firstChild,
    251    2,
    252    host3_slot4.firstChild,
    253    8
    254  );
    255  testSelectionToString("otted1 Slotted2 Inner Slotted3 Slotted4");
    256  testClipboardValue(
    257    false,
    258    "text/plain",
    259    "otted1 Slotted2 Inner Slotted3 Slotted4"
    260  );
    261  testHtmlClipboardValue(
    262    false,
    263    "text/html",
    264    '<slot name="slot1"><span id="host3_slot1" slot="slot1">otted1</span></slot>\n      <slot name="slot2"><span id="host3_slot2" slot="slot2">Slotted2</span></slot>\n      <span id="inner2">Inner</span>\n      <slot name="slot3"><span id="host3_slot3" slot="slot3">Slotted3</span></slot>\n      <slot name="slot4"><span id="host3_slot4" slot="slot4">Slotted4</span></slot>'
    265  );
    266  testPasteText(textarea, "otted1 Slotted2 Inner Slotted3 Slotted4");
    267 
    268  info("Test 9: start is in Shadow DOM and end is in Light DOM.\n");
    269  await copySelectionToClipboardShadow(
    270    host3.shadowRoot.getElementById("inner2").firstChild,
    271    3,
    272    host3_slot1.firstChild,
    273    4
    274  );
    275  testSelectionToString("ted1 Slotted2 Inn");
    276  testClipboardValue(false, "text/plain", "ted1 Slotted2 Inn");
    277  testHtmlClipboardValue(
    278    false,
    279    "text/html",
    280    '<slot name="slot1"><span id="host3_slot1" slot="slot1">ted1</span></slot>\n      <slot name="slot2"><span id="host3_slot2" slot="slot2">Slotted2</span></slot>\n      <span id="inner2">Inn</span>'
    281  );
    282  testPasteText(textarea, "ted1 Slotted2 Inn");
    283 }