DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
<window title="nsTransferable with large string"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        onload="RunTest();">
  <title>nsTransferable with large string</title>
  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>

  <script type="application/javascript">
  <![CDATA[
  // This value is chosen such that the size of the memory for the string exceeds
  // the kLargeDatasetSize threshold in nsTransferable.h (one million).
  // Each character of a JS string is internally represented by two bytes,
  // so the following string of length 500 001 uses 1 000 002 bytes.
  const BIG_STRING = "x" + "BIGGY".repeat(100000);

  // Some value with a length that is exactly kLargeDatasetSize (1 000 000).
  const SMALL_STRING = "small".repeat(100000);

  const nsTransferable = Components.Constructor("@mozilla.org/widget/transferable;1", "nsITransferable");
  const nsSupportsString = Components.Constructor("@mozilla.org/supports-string;1", "nsISupportsString");

  function assignTextToTransferable(transferable, string) {
    var Suppstr = nsSupportsString();
    Suppstr.data = string;
    transferable.setTransferData("text/unicode", Suppstr, string.length * 2);
  }

  function checkTransferableText(transferable, expectedString, description) {
    var data = {};
    transferable.getTransferData("text/unicode", data);
    var actualValue = data.value.QueryInterface(Ci.nsISupportsString).data;
    // Use ok + shortenString instead of is(...) to avoid dumping millions of characters in the output.
    ok(actualValue === expectedString, description + ": text should match. " +
       "Expected " + shortenString(expectedString)  + ", got " + shortenString(actualValue));

    function shortenString(str) {
      return str && str.length > 30 ? str.slice(0, 10) + "..." + str.slice(-10) : String(str);
    }
  }

  function isFDCountingSupported() {
    // On on-Windows we can count the number of file handles for the current process,
    // while on Windows we need to count the number of files in ${TempD}\mozilla-temp-files\,
    // which can be unreliable, especially because nsAnonymousTemporaryFile has documented
    // that the deletion might not be immediate.
    //
    // To avoid intermittents, we only check the file descriptor counts on non-Windows.
    // test_bug1123480.xul will do some basic testing for Windows.
    const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
    return AppConstants.platform !== 'win';
  }

  function getClipboardCacheFDCount() {
    var dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
    dir.initWithPath("/dev/fd");
    var count = 0;
    for (var de = dir.directoryEntries; de.hasMoreElements(); ) {
      var fdFile = de.nextFile;
      var fileSize;
      try {
        fileSize = fdFile.fileSize;
      } catch (e) {
        // This can happen on macOS.
        continue;
      }
      if (fileSize === BIG_STRING.length * 2 ||
          // We are not expecting files of this small size,
          // but include them in the count anyway
          // in case the files are unexpectedly created.
          fileSize === SMALL_STRING.length * 2) {
        // Assume that the file was created by us if the size matches.
        ++count;
      }
    }
    return count;
  }

  function RunTest() {
    const {PrivateBrowsingUtils} = ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
    const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");

    var win = window.open("about:blank", "_blank", "chrome, width=500, height=200");
    ok(win, "should open window");
    is(PrivateBrowsingUtils.isContentWindowPrivate(win), false, "used correct window context");

    // ### Part 1 - Writing to the clipboard.

    var Loadctx = PrivateBrowsingUtils.privacyContextFromWindow(win);
    var Transfer = nsTransferable();
    Transfer.init(Loadctx);
    Transfer.addDataFlavor("text/unicode");
    var initialFdCount = isFDCountingSupported() ? getClipboardCacheFDCount() : -1;

    assignTextToTransferable(Transfer, BIG_STRING);
    checkTransferableText(Transfer, BIG_STRING, "transferable after assigning BIG_STRING");
    if (isFDCountingSupported()) {
      is(getClipboardCacheFDCount(), initialFdCount + 1, "should use a file for BIG_STRING");
    }

    // Share the transferable with the system.
    Services.clipboard.setData(Transfer, null, Services.clipboard.kGlobalClipboard);

    // Sanity check: Copying to the clipboard should not have altered the transferable.
    checkTransferableText(Transfer, BIG_STRING, "transferable after copying to clipboard");
    if (isFDCountingSupported()) {
      // We are only counting file descriptors for the current process,
      // so even if the test were to be multi-process and the parent process creates another
      // nsTransferable, then the count should still be the same.
      is(getClipboardCacheFDCount(), initialFdCount + 1, "should still be using files for previously stored BIG_STRING");

      // Re-establish baseline for the second part of the test below.
      initialFdCount = getClipboardCacheFDCount();
    }

    // ### Part 2 - Reading from the clipboard.

    var Transfer2 = nsTransferable();
    Transfer2.init(Loadctx);
    Transfer2.addDataFlavor("text/unicode");

    // Iniitalize with a small string, so we can see that mData -> mCacheFD works.
    assignTextToTransferable(Transfer2, SMALL_STRING);
    checkTransferableText(Transfer2, SMALL_STRING, "transferable after assigning SMALL_STRING");
    if (isFDCountingSupported()) {
      is(getClipboardCacheFDCount(), initialFdCount, "should not use file to store SMALL_STRING.");
    }

    // Check whether the clipboard data can be read, and simulatenously trigger mData -> mCacheFD.
    Services.clipboard.getData(Transfer2, Services.clipboard.kGlobalClipboard);
    checkTransferableText(Transfer2, BIG_STRING, "transferable after retrieving from clipboard");
    if (isFDCountingSupported()) {
      is(getClipboardCacheFDCount(), initialFdCount + 1, "should use a file for BIG_STRING (read from clipboard).");
    }

    // Store a small string, to exercise the code path from mCacheFD -> mData.
    assignTextToTransferable(Transfer2, SMALL_STRING);
    checkTransferableText(Transfer2, SMALL_STRING, "transferable after assigning SMALL_STRING");
    if (isFDCountingSupported()) {
      is(getClipboardCacheFDCount(), initialFdCount, "should release the file after clearing the transferable.");
    }
  }
  ]]>
  </script>

  <!-- test results are displayed in the html:body -->
  <body xmlns="http://www.w3.org/1999/xhtml">
    This test checks whether a big string can be copied to the clipboard, and then retrieved in the same form.
    On non-Windows, the test also checks whether the data of the transferable is really stored in a file.
  </body>
</window>