toolkit/devtools/gcli/commands/screenshot.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 const { Cc, Ci, Cu } = require("chrome");
michael@0 8 const gcli = require("gcli/index");
michael@0 9
michael@0 10 loader.lazyImporter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
michael@0 11 loader.lazyImporter(this, "LayoutHelpers", "resource://gre/modules/devtools/LayoutHelpers.jsm");
michael@0 12 loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm");
michael@0 13 loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
michael@0 14
michael@0 15 const BRAND_SHORT_NAME = Cc["@mozilla.org/intl/stringbundle;1"]
michael@0 16 .getService(Ci.nsIStringBundleService)
michael@0 17 .createBundle("chrome://branding/locale/brand.properties")
michael@0 18 .GetStringFromName("brandShortName");
michael@0 19
michael@0 20 // String used as an indication to generate default file name in the following
michael@0 21 // format: "Screen Shot yyyy-mm-dd at HH.MM.SS.png"
michael@0 22 const FILENAME_DEFAULT_VALUE = " ";
michael@0 23
michael@0 24 exports.items = [
michael@0 25 {
michael@0 26 name: "screenshot",
michael@0 27 description: gcli.lookup("screenshotDesc"),
michael@0 28 manual: gcli.lookup("screenshotManual"),
michael@0 29 returnType: "dom",
michael@0 30 params: [
michael@0 31 {
michael@0 32 name: "filename",
michael@0 33 type: "string",
michael@0 34 defaultValue: FILENAME_DEFAULT_VALUE,
michael@0 35 description: gcli.lookup("screenshotFilenameDesc"),
michael@0 36 manual: gcli.lookup("screenshotFilenameManual")
michael@0 37 },
michael@0 38 {
michael@0 39 group: gcli.lookup("screenshotGroupOptions"),
michael@0 40 params: [
michael@0 41 {
michael@0 42 name: "clipboard",
michael@0 43 type: "boolean",
michael@0 44 description: gcli.lookup("screenshotClipboardDesc"),
michael@0 45 manual: gcli.lookup("screenshotClipboardManual")
michael@0 46 },
michael@0 47 {
michael@0 48 name: "chrome",
michael@0 49 type: "boolean",
michael@0 50 description: gcli.lookupFormat("screenshotChromeDesc2", [BRAND_SHORT_NAME]),
michael@0 51 manual: gcli.lookupFormat("screenshotChromeManual2", [BRAND_SHORT_NAME])
michael@0 52 },
michael@0 53 {
michael@0 54 name: "delay",
michael@0 55 type: { name: "number", min: 0 },
michael@0 56 defaultValue: 0,
michael@0 57 description: gcli.lookup("screenshotDelayDesc"),
michael@0 58 manual: gcli.lookup("screenshotDelayManual")
michael@0 59 },
michael@0 60 {
michael@0 61 name: "fullpage",
michael@0 62 type: "boolean",
michael@0 63 description: gcli.lookup("screenshotFullPageDesc"),
michael@0 64 manual: gcli.lookup("screenshotFullPageManual")
michael@0 65 },
michael@0 66 {
michael@0 67 name: "selector",
michael@0 68 type: "node",
michael@0 69 defaultValue: null,
michael@0 70 description: gcli.lookup("inspectNodeDesc"),
michael@0 71 manual: gcli.lookup("inspectNodeManual")
michael@0 72 }
michael@0 73 ]
michael@0 74 }
michael@0 75 ],
michael@0 76 exec: function(args, context) {
michael@0 77 if (args.chrome && args.selector) {
michael@0 78 // Node screenshot with chrome option does not work as intended
michael@0 79 // Refer https://bugzilla.mozilla.org/show_bug.cgi?id=659268#c7
michael@0 80 // throwing for now.
michael@0 81 throw new Error(gcli.lookup("screenshotSelectorChromeConflict"));
michael@0 82 }
michael@0 83 var document = args.chrome? context.environment.chromeDocument
michael@0 84 : context.environment.document;
michael@0 85 if (args.delay > 0) {
michael@0 86 var deferred = context.defer();
michael@0 87 document.defaultView.setTimeout(() => {
michael@0 88 this.grabScreen(document, args.filename, args.clipboard,
michael@0 89 args.fullpage).then(deferred.resolve, deferred.reject);
michael@0 90 }, args.delay * 1000);
michael@0 91 return deferred.promise;
michael@0 92 }
michael@0 93
michael@0 94 return this.grabScreen(document, args.filename, args.clipboard,
michael@0 95 args.fullpage, args.selector);
michael@0 96 },
michael@0 97 grabScreen: function(document, filename, clipboard, fullpage, node) {
michael@0 98 return Task.spawn(function() {
michael@0 99 let window = document.defaultView;
michael@0 100 let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
michael@0 101 let left = 0;
michael@0 102 let top = 0;
michael@0 103 let width;
michael@0 104 let height;
michael@0 105 let div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
michael@0 106
michael@0 107 if (!fullpage) {
michael@0 108 if (!node) {
michael@0 109 left = window.scrollX;
michael@0 110 top = window.scrollY;
michael@0 111 width = window.innerWidth;
michael@0 112 height = window.innerHeight;
michael@0 113 } else {
michael@0 114 let lh = new LayoutHelpers(window);
michael@0 115 let rect = lh.getRect(node, window);
michael@0 116 top = rect.top;
michael@0 117 left = rect.left;
michael@0 118 width = rect.width;
michael@0 119 height = rect.height;
michael@0 120 }
michael@0 121 } else {
michael@0 122 width = window.innerWidth + window.scrollMaxX;
michael@0 123 height = window.innerHeight + window.scrollMaxY;
michael@0 124 }
michael@0 125 canvas.width = width;
michael@0 126 canvas.height = height;
michael@0 127
michael@0 128 let ctx = canvas.getContext("2d");
michael@0 129 ctx.drawWindow(window, left, top, width, height, "#fff");
michael@0 130 let data = canvas.toDataURL("image/png", "");
michael@0 131
michael@0 132 let loadContext = document.defaultView
michael@0 133 .QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 134 .getInterface(Ci.nsIWebNavigation)
michael@0 135 .QueryInterface(Ci.nsILoadContext);
michael@0 136
michael@0 137 if (clipboard) {
michael@0 138 try {
michael@0 139 let io = Cc["@mozilla.org/network/io-service;1"]
michael@0 140 .getService(Ci.nsIIOService);
michael@0 141 let channel = io.newChannel(data, null, null);
michael@0 142 let input = channel.open();
michael@0 143 let imgTools = Cc["@mozilla.org/image/tools;1"]
michael@0 144 .getService(Ci.imgITools);
michael@0 145
michael@0 146 let container = {};
michael@0 147 imgTools.decodeImageData(input, channel.contentType, container);
michael@0 148
michael@0 149 let wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
michael@0 150 .createInstance(Ci.nsISupportsInterfacePointer);
michael@0 151 wrapped.data = container.value;
michael@0 152
michael@0 153 let trans = Cc["@mozilla.org/widget/transferable;1"]
michael@0 154 .createInstance(Ci.nsITransferable);
michael@0 155 trans.init(loadContext);
michael@0 156 trans.addDataFlavor(channel.contentType);
michael@0 157 trans.setTransferData(channel.contentType, wrapped, -1);
michael@0 158
michael@0 159 let clipid = Ci.nsIClipboard;
michael@0 160 let clip = Cc["@mozilla.org/widget/clipboard;1"].getService(clipid);
michael@0 161 clip.setData(trans, null, clipid.kGlobalClipboard);
michael@0 162 div.textContent = gcli.lookup("screenshotCopied");
michael@0 163 }
michael@0 164 catch (ex) {
michael@0 165 div.textContent = gcli.lookup("screenshotErrorCopying");
michael@0 166 }
michael@0 167 throw new Task.Result(div);
michael@0 168 }
michael@0 169
michael@0 170 let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
michael@0 171
michael@0 172 // Create a name for the file if not present
michael@0 173 if (filename == FILENAME_DEFAULT_VALUE) {
michael@0 174 let date = new Date();
michael@0 175 let dateString = date.getFullYear() + "-" + (date.getMonth() + 1) +
michael@0 176 "-" + date.getDate();
michael@0 177 dateString = dateString.split("-").map(function(part) {
michael@0 178 if (part.length == 1) {
michael@0 179 part = "0" + part;
michael@0 180 }
michael@0 181 return part;
michael@0 182 }).join("-");
michael@0 183 let timeString = date.toTimeString().replace(/:/g, ".").split(" ")[0];
michael@0 184 filename = gcli.lookupFormat("screenshotGeneratedFilename",
michael@0 185 [dateString, timeString]) + ".png";
michael@0 186 }
michael@0 187 // Check there is a .png extension to filename
michael@0 188 else if (!filename.match(/.png$/i)) {
michael@0 189 filename += ".png";
michael@0 190 }
michael@0 191 // If the filename is relative, tack it onto the download directory
michael@0 192 if (!filename.match(/[\\\/]/)) {
michael@0 193 let preferredDir = yield Downloads.getPreferredDownloadsDirectory();
michael@0 194 filename = OS.Path.join(preferredDir, filename);
michael@0 195 }
michael@0 196
michael@0 197 try {
michael@0 198 file.initWithPath(filename);
michael@0 199 } catch (ex) {
michael@0 200 div.textContent = gcli.lookup("screenshotErrorSavingToFile") + " " + filename;
michael@0 201 throw new Task.Result(div);
michael@0 202 }
michael@0 203
michael@0 204 let ioService = Cc["@mozilla.org/network/io-service;1"]
michael@0 205 .getService(Ci.nsIIOService);
michael@0 206
michael@0 207 let Persist = Ci.nsIWebBrowserPersist;
michael@0 208 let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
michael@0 209 .createInstance(Persist);
michael@0 210 persist.persistFlags = Persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
michael@0 211 Persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
michael@0 212
michael@0 213 let source = ioService.newURI(data, "UTF8", null);
michael@0 214 persist.saveURI(source, null, null, null, null, file, loadContext);
michael@0 215
michael@0 216 div.textContent = gcli.lookup("screenshotSavedToFile") + " \"" + filename +
michael@0 217 "\"";
michael@0 218 div.addEventListener("click", function openFile() {
michael@0 219 div.removeEventListener("click", openFile);
michael@0 220 file.reveal();
michael@0 221 });
michael@0 222 div.style.cursor = "pointer";
michael@0 223 let image = document.createElement("div");
michael@0 224 let previewHeight = parseInt(256*height/width);
michael@0 225 image.setAttribute("style",
michael@0 226 "width:256px; height:" + previewHeight + "px;" +
michael@0 227 "max-height: 256px;" +
michael@0 228 "background-image: url('" + data + "');" +
michael@0 229 "background-size: 256px " + previewHeight + "px;" +
michael@0 230 "margin: 4px; display: block");
michael@0 231 div.appendChild(image);
michael@0 232 throw new Task.Result(div);
michael@0 233 });
michael@0 234 }
michael@0 235 }
michael@0 236 ];

mercurial