toolkit/devtools/gcli/commands/screenshot.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial