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.

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

mercurial