addon-sdk/source/lib/sdk/clipboard.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 "use strict";
     7 module.metadata = {
     8   "stability": "stable",
     9   "engines": {
    10     // TODO Fennec Support 789757
    11     "Firefox": "*"
    12   }
    13 };
    15 const { Cc, Ci } = require("chrome");
    16 const { DataURL } = require("./url");
    17 const errors = require("./deprecated/errors");
    18 const apiUtils = require("./deprecated/api-utils");
    19 /*
    20 While these data flavors resemble Internet media types, they do
    21 no directly map to them.
    22 */
    23 const kAllowableFlavors = [
    24   "text/unicode",
    25   "text/html",
    26   "image/png"
    27   /* CURRENTLY UNSUPPORTED FLAVORS
    28   "text/plain",
    29   "image/jpg",
    30   "image/jpeg",
    31   "image/gif",
    32   "text/x-moz-text-internal",
    33   "AOLMAIL",
    34   "application/x-moz-file",
    35   "text/x-moz-url",
    36   "text/x-moz-url-data",
    37   "text/x-moz-url-desc",
    38   "text/x-moz-url-priv",
    39   "application/x-moz-nativeimage",
    40   "application/x-moz-nativehtml",
    41   "application/x-moz-file-promise-url",
    42   "application/x-moz-file-promise-dest-filename",
    43   "application/x-moz-file-promise",
    44   "application/x-moz-file-promise-dir"
    45   */
    46 ];
    48 /*
    49 Aliases for common flavors. Not all flavors will
    50 get an alias. New aliases must be approved by a
    51 Jetpack API druid.
    52 */
    53 const kFlavorMap = [
    54   { short: "text", long: "text/unicode" },
    55   { short: "html", long: "text/html" },
    56   { short: "image", long: "image/png" }
    57 ];
    59 let clipboardService = Cc["@mozilla.org/widget/clipboard;1"].
    60                        getService(Ci.nsIClipboard);
    62 let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].
    63                       getService(Ci.nsIClipboardHelper);
    65 let imageTools = Cc["@mozilla.org/image/tools;1"].
    66                getService(Ci.imgITools);
    68 exports.set = function(aData, aDataType) {
    70   let options = {
    71     data: aData,
    72     datatype: aDataType || "text"
    73   };
    75   // If `aDataType` is not given or if it's "image", the data is parsed as
    76   // data URL to detect a better datatype
    77   if (aData && (!aDataType || aDataType === "image")) {
    78     try {
    79       let dataURL = new DataURL(aData);
    81       options.datatype = dataURL.mimeType;
    82       options.data = dataURL.data;
    83     }
    84     catch (e) {
    85       // Ignore invalid URIs
    86       if (e.name !== "URIError") {
    87         throw e;
    88       }
    89     }
    90   }
    92   options = apiUtils.validateOptions(options, {
    93     data: {
    94       is: ["string"]
    95     },
    96     datatype: {
    97       is: ["string"]
    98     }
    99   });
   101   let flavor = fromJetpackFlavor(options.datatype);
   103   if (!flavor)
   104     throw new Error("Invalid flavor for " + options.datatype);
   106   // Additional checks for using the simple case
   107   if (flavor == "text/unicode") {
   108     clipboardHelper.copyString(options.data);
   109     return true;
   110   }
   112   // Below are the more complex cases where we actually have to work with a
   113   // nsITransferable object
   114   var xferable = Cc["@mozilla.org/widget/transferable;1"].
   115                  createInstance(Ci.nsITransferable);
   116   if (!xferable)
   117     throw new Error("Couldn't set the clipboard due to an internal error " +
   118                     "(couldn't create a Transferable object).");
   119   // Bug 769440: Starting with FF16, transferable have to be inited
   120   if ("init" in xferable)
   121     xferable.init(null);
   123   switch (flavor) {
   124     case "text/html":
   125       // add text/html flavor
   126       let (str = Cc["@mozilla.org/supports-string;1"].
   127                  createInstance(Ci.nsISupportsString))
   128       {
   129         str.data = options.data;
   130         xferable.addDataFlavor(flavor);
   131         xferable.setTransferData(flavor, str, str.data.length * 2);
   132       }
   134       // add a text/unicode flavor (html converted to plain text)
   135       let (str = Cc["@mozilla.org/supports-string;1"].
   136                  createInstance(Ci.nsISupportsString),
   137            converter = Cc["@mozilla.org/feed-textconstruct;1"].
   138                        createInstance(Ci.nsIFeedTextConstruct))
   139       {
   140         converter.type = "html";
   141         converter.text = options.data;
   142         str.data = converter.plainText();
   143         xferable.addDataFlavor("text/unicode");
   144         xferable.setTransferData("text/unicode", str, str.data.length * 2);
   145       }
   146       break;
   148     // Set images to the clipboard is not straightforward, to have an idea how
   149     // it works on platform side, see:
   150     // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsCopySupport.cpp?rev=7857c5bff017#530
   151     case "image/png":
   152       let image = options.data;
   154       let container = {};
   156       try {
   157         let input = Cc["@mozilla.org/io/string-input-stream;1"].
   158                       createInstance(Ci.nsIStringInputStream);
   160         input.setData(image, image.length);
   162         imageTools.decodeImageData(input, flavor, container);
   163       }
   164       catch (e) {
   165         throw new Error("Unable to decode data given in a valid image.");
   166       }
   168       // Store directly the input stream makes the cliboard's data available
   169       // for Firefox but not to the others application or to the OS. Therefore,
   170       // a `nsISupportsInterfacePointer` object that reference an `imgIContainer`
   171       // with the image is needed.
   172       var imgPtr = Cc["@mozilla.org/supports-interface-pointer;1"].
   173                      createInstance(Ci.nsISupportsInterfacePointer);
   175       imgPtr.data = container.value;
   177       xferable.addDataFlavor(flavor);
   178       xferable.setTransferData(flavor, imgPtr, -1);
   180       break;
   181     default:
   182       throw new Error("Unable to handle the flavor " + flavor + ".");
   183   }
   185   // TODO: Not sure if this will ever actually throw. -zpao
   186   try {
   187     clipboardService.setData(
   188       xferable,
   189       null,
   190       clipboardService.kGlobalClipboard
   191     );
   192   } catch (e) {
   193     throw new Error("Couldn't set clipboard data due to an internal error: " + e);
   194   }
   195   return true;
   196 };
   199 exports.get = function(aDataType) {
   200   let options = {
   201     datatype: aDataType
   202   };
   204   // Figure out the best data type for the clipboard's data, if omitted
   205   if (!aDataType) {
   206     if (~currentFlavors().indexOf("image"))
   207       options.datatype = "image";
   208     else
   209       options.datatype = "text";
   210   }
   212   options = apiUtils.validateOptions(options, {
   213     datatype: {
   214       is: ["string"]
   215     }
   216   });
   218   var xferable = Cc["@mozilla.org/widget/transferable;1"].
   219                  createInstance(Ci.nsITransferable);
   220   if (!xferable)
   221     throw new Error("Couldn't set the clipboard due to an internal error " +
   222                     "(couldn't create a Transferable object).");
   223   // Bug 769440: Starting with FF16, transferable have to be inited
   224   if ("init" in xferable)
   225     xferable.init(null);
   227   var flavor = fromJetpackFlavor(options.datatype);
   229   // Ensure that the user hasn't requested a flavor that we don't support.
   230   if (!flavor)
   231     throw new Error("Getting the clipboard with the flavor '" + flavor +
   232                     "' is not supported.");
   234   // TODO: Check for matching flavor first? Probably not worth it.
   236   xferable.addDataFlavor(flavor);
   237   // Get the data into our transferable.
   238   clipboardService.getData(
   239     xferable,
   240     clipboardService.kGlobalClipboard
   241   );
   243   var data = {};
   244   var dataLen = {};
   245   try {
   246     xferable.getTransferData(flavor, data, dataLen);
   247   } catch (e) {
   248     // Clipboard doesn't contain data in flavor, return null.
   249     return null;
   250   }
   252   // There's no data available, return.
   253   if (data.value === null)
   254     return null;
   256   // TODO: Add flavors here as we support more in kAllowableFlavors.
   257   switch (flavor) {
   258     case "text/unicode":
   259     case "text/html":
   260       data = data.value.QueryInterface(Ci.nsISupportsString).data;
   261       break;
   262     case "image/png":
   263       let dataURL = new DataURL();
   265       dataURL.mimeType = flavor;
   266       dataURL.base64 = true;
   268       let image = data.value;
   270       // Due to the differences in how images could be stored in the clipboard
   271       // the checks below are needed. The clipboard could already provide the
   272       // image as byte streams, but also as pointer, or as image container.
   273       // If it's not possible obtain a byte stream, the function returns `null`.
   274       if (image instanceof Ci.nsISupportsInterfacePointer)
   275         image = image.data;
   277       if (image instanceof Ci.imgIContainer)
   278         image = imageTools.encodeImage(image, flavor);
   280       if (image instanceof Ci.nsIInputStream) {
   281         let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
   282                               createInstance(Ci.nsIBinaryInputStream);
   284         binaryStream.setInputStream(image);
   286         dataURL.data = binaryStream.readBytes(binaryStream.available());
   288         data = dataURL.toString();
   289       }
   290       else
   291         data = null;
   293       break;
   294     default:
   295       data = null;
   296   }
   298   return data;
   299 };
   301 function currentFlavors() {
   302   // Loop over kAllowableFlavors, calling hasDataMatchingFlavors for each.
   303   // This doesn't seem like the most efficient way, but we can't get
   304   // confirmation for specific flavors any other way. This is supposed to be
   305   // an inexpensive call, so performance shouldn't be impacted (much).
   306   var currentFlavors = [];
   307   for each (var flavor in kAllowableFlavors) {
   308     var matches = clipboardService.hasDataMatchingFlavors(
   309       [flavor],
   310       1,
   311       clipboardService.kGlobalClipboard
   312     );
   313     if (matches)
   314       currentFlavors.push(toJetpackFlavor(flavor));
   315   }
   316   return currentFlavors;
   317 };
   319 Object.defineProperty(exports, "currentFlavors", { get : currentFlavors });
   321 // SUPPORT FUNCTIONS ////////////////////////////////////////////////////////
   323 function toJetpackFlavor(aFlavor) {
   324   for each (let flavorMap in kFlavorMap)
   325     if (flavorMap.long == aFlavor)
   326       return flavorMap.short;
   327   // Return null in the case where we don't match
   328   return null;
   329 }
   331 function fromJetpackFlavor(aJetpackFlavor) {
   332   // TODO: Handle proper flavors better
   333   for each (let flavorMap in kFlavorMap)
   334     if (flavorMap.short == aJetpackFlavor || flavorMap.long == aJetpackFlavor)
   335       return flavorMap.long;
   336   // Return null in the case where we don't match.
   337   return null;
   338 }

mercurial