Bug 849236 - Add a screenshot button to the responsive mode. r=mratcliffe
authorPaul Rouget <paul@mozilla.com>
Tue, 03 Sep 2013 09:15:51 +0200
changeset 145227 0d0feeec23a81e87b1534c9f0492fdaa81c4a551
parent 145226 0169b1c99f312bb1b01e4ad501eaa4b6d42f89ae
child 145228 9cc1839ef1f25741216a529bfa0aed71163a7e7c
push id2476
push userprouget@mozilla.com
push dateTue, 03 Sep 2013 07:16:13 +0000
treeherderfx-team@61a110ae7914 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmratcliffe
bugs849236
milestone26.0a1
Bug 849236 - Add a screenshot button to the responsive mode. r=mratcliffe
browser/devtools/responsivedesign/responsivedesign.jsm
browser/devtools/responsivedesign/test/browser_responsiveui.js
browser/devtools/responsivedesign/test/browser_responsiveuiaddcustompreset.js
browser/locales/en-US/chrome/browser/devtools/responsiveUI.properties
browser/themes/linux/jar.mn
browser/themes/osx/jar.mn
browser/themes/shared/devtools/responsivedesign.inc.css
browser/themes/shared/devtools/responsiveui-rotate.png
browser/themes/shared/devtools/responsiveui-screenshot.png
browser/themes/windows/jar.mn
--- a/browser/devtools/responsivedesign/responsivedesign.jsm
+++ b/browser/devtools/responsivedesign/responsivedesign.jsm
@@ -152,16 +152,17 @@ function ResponsiveUI(aWindow, aTab)
   this.container.setAttribute("responsivemode", "true");
   this.stack.setAttribute("responsivemode", "true");
 
   // Let's bind some callbacks.
   this.bound_presetSelected = this.presetSelected.bind(this);
   this.bound_addPreset = this.addPreset.bind(this);
   this.bound_removePreset = this.removePreset.bind(this);
   this.bound_rotate = this.rotate.bind(this);
+  this.bound_screenshot = () => this.screenshot();
   this.bound_close = this.close.bind(this);
   this.bound_startResizing = this.startResizing.bind(this);
   this.bound_stopResizing = this.stopResizing.bind(this);
   this.bound_onDrag = this.onDrag.bind(this);
   this.bound_onKeypress = this.onKeypress.bind(this);
 
   // Events
   this.tab.addEventListener("TabClose", this);
@@ -223,16 +224,17 @@ ResponsiveUI.prototype = {
       this.stopResizing();
 
     // Remove listeners.
     this.mainWindow.document.removeEventListener("keypress", this.bound_onKeypress, false);
     this.menulist.removeEventListener("select", this.bound_presetSelected, true);
     this.tab.removeEventListener("TabClose", this);
     this.tabContainer.removeEventListener("TabSelect", this);
     this.rotatebutton.removeEventListener("command", this.bound_rotate, true);
+    this.screenshotbutton.removeEventListener("command", this.bound_screenshot, true);
     this.closebutton.removeEventListener("command", this.bound_close, true);
     this.addbutton.removeEventListener("command", this.bound_addPreset, true);
     this.removebutton.removeEventListener("command", this.bound_removePreset, true);
 
     // Removed elements.
     this.container.removeChild(this.toolbar);
     this.stack.removeChild(this.resizer);
     this.stack.removeChild(this.resizeBarV);
@@ -295,18 +297,19 @@ ResponsiveUI.prototype = {
    },
 
   /**
    * Build the toolbar and the resizers.
    *
    * <vbox class="browserContainer"> From tabbrowser.xml
    *  <toolbar class="devtools-toolbar devtools-responsiveui-toolbar">
    *    <menulist class="devtools-menulist"/> // presets
-   *    <toolbarbutton tabindex="0" class="devtools-toolbarbutton" label="rotate"/> // rotate
-   *    <toolbarbutton tabindex="0" class="devtools-toolbarbutton devtools-closebutton" tooltiptext="Leave Responsive Design View"/> // close
+   *    <toolbarbutton tabindex="0" class="devtools-toolbarbutton" tooltiptext="rotate"/> // rotate
+   *    <toolbarbutton tabindex="0" class="devtools-toolbarbutton" tooltiptext="screenshot"/> // screenshot
+   *    <toolbarbutton tabindex="0" class="devtools-toolbarbutton" tooltiptext="Leave Responsive Design View"/> // close
    *  </toolbar>
    *  <stack class="browserStack"> From tabbrowser.xml
    *    <browser/>
    *    <box class="devtools-responsiveui-resizehandle" bottom="0" right="0"/>
    *    <box class="devtools-responsiveui-resizebarV" top="0" right="0"/>
    *    <box class="devtools-responsiveui-resizebarH" bottom="0" left="0"/>
    *  </stack>
    * </vbox>
@@ -336,29 +339,36 @@ ResponsiveUI.prototype = {
     this.removebutton.addEventListener("command", this.bound_removePreset, true);
 
     menupopup.appendChild(this.chromeDoc.createElement("menuseparator"));
     menupopup.appendChild(this.addbutton);
     menupopup.appendChild(this.removebutton);
 
     this.rotatebutton = this.chromeDoc.createElement("toolbarbutton");
     this.rotatebutton.setAttribute("tabindex", "0");
-    this.rotatebutton.setAttribute("label", this.strings.GetStringFromName("responsiveUI.rotate"));
-    this.rotatebutton.className = "devtools-toolbarbutton";
+    this.rotatebutton.setAttribute("tooltiptext", this.strings.GetStringFromName("responsiveUI.rotate2"));
+    this.rotatebutton.className = "devtools-toolbarbutton devtools-responsiveui-rotate";
     this.rotatebutton.addEventListener("command", this.bound_rotate, true);
 
+    this.screenshotbutton = this.chromeDoc.createElement("toolbarbutton");
+    this.screenshotbutton.setAttribute("tabindex", "0");
+    this.screenshotbutton.setAttribute("tooltiptext", this.strings.GetStringFromName("responsiveUI.screenshot"));
+    this.screenshotbutton.className = "devtools-toolbarbutton devtools-responsiveui-screenshot";
+    this.screenshotbutton.addEventListener("command", this.bound_screenshot, true);
+
     this.closebutton = this.chromeDoc.createElement("toolbarbutton");
     this.closebutton.setAttribute("tabindex", "0");
-    this.closebutton.className = "devtools-toolbarbutton devtools-closebutton";
+    this.closebutton.className = "devtools-toolbarbutton devtools-responsiveui-close";
     this.closebutton.setAttribute("tooltiptext", this.strings.GetStringFromName("responsiveUI.close"));
     this.closebutton.addEventListener("command", this.bound_close, true);
 
     this.toolbar.appendChild(this.closebutton);
     this.toolbar.appendChild(this.menulist);
     this.toolbar.appendChild(this.rotatebutton);
+    this.toolbar.appendChild(this.screenshotbutton);
 
     // Resizers
     this.resizer = this.chromeDoc.createElement("box");
     this.resizer.className = "devtools-responsiveui-resizehandle";
     this.resizer.setAttribute("right", "0");
     this.resizer.setAttribute("bottom", "0");
     this.resizer.onmousedown = this.bound_startResizing;
 
@@ -564,16 +574,53 @@ ResponsiveUI.prototype = {
       this.saveCustomSize();
     } else {
       this.rotateValue = !this.rotateValue;
       this.saveCurrentPreset();
     }
   },
 
   /**
+   * Take a screenshot of the page.
+   *
+   * @param aFileName name of the screenshot file (used for tests).
+   */
+  screenshot: function RUI_screenshot(aFileName) {
+    let window = this.browser.contentWindow;
+    let document = window.document;
+    let canvas = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+
+    let width = window.innerWidth;
+    let height = window.innerHeight;
+
+    canvas.width = width;
+    canvas.height = height;
+
+    let ctx = canvas.getContext("2d");
+    ctx.drawWindow(window, window.scrollX, window.scrollY, width, height, "#fff");
+
+    let filename = aFileName;
+
+    if (!filename) {
+      let date = new Date();
+      let month = ("0" + (date.getMonth() + 1)).substr(-2, 2);
+      let day = ("0" + (date.getDay() + 1)).substr(-2, 2);
+      let dateString = [date.getFullYear(), month, day].join("-");
+      let timeString = date.toTimeString().replace(/:/g, ".").split(" ")[0];
+      filename = this.strings.formatStringFromName("responsiveUI.screenshotGeneratedFilename", [dateString, timeString], 2);
+    }
+
+    canvas.toBlob(blob => {
+      let chromeWindow = this.chromeDoc.defaultView;
+      let url = chromeWindow.URL.createObjectURL(blob);
+      chromeWindow.saveURL(url, filename + ".png", null, true, true, document.documentURIObject, document);
+    });
+  },
+
+  /**
    * Change the size of the browser.
    *
    * @param aWidth width of the browser.
    * @param aHeight height of the browser.
    */
   setSize: function RUI_setSize(aWidth, aHeight) {
     aWidth = Math.min(Math.max(aWidth, MIN_WIDTH), MAX_WIDTH);
     aHeight = Math.min(Math.max(aHeight, MIN_HEIGHT), MAX_HEIGHT);
--- a/browser/devtools/responsivedesign/test/browser_responsiveui.js
+++ b/browser/devtools/responsivedesign/test/browser_responsiveui.js
@@ -148,20 +148,48 @@ function test() {
     is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
 
     // Menus are correctly updated?
     is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "true", "menus checked");
 
     is(content.innerWidth, widthBeforeClose, "width restored.");
     is(content.innerHeight, heightBeforeClose, "height restored.");
 
-    mgr.once("off", function() {executeSoon(finishUp)});
+    mgr.once("off", function() {executeSoon(testScreenshot)});
     EventUtils.synthesizeKey("VK_ESCAPE", {});
   }
 
+  function testScreenshot() {
+    let isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
+    if (isWinXP) {
+      // We have issues testing this on Windows XP.
+      // See https://bugzilla.mozilla.org/show_bug.cgi?id=848760#c17
+      return finishUp();
+    }
+
+    info("screenshot");
+    instance.screenshot("responsiveui");
+    let FileUtils = (Cu.import("resource://gre/modules/FileUtils.jsm", {})).FileUtils;
+
+    // while(1) until we find the file.
+    // no need for a timeout, the test will get killed anyway.
+    info("checking if file exists in 200ms");
+    function checkIfFileExist() {
+      let file = FileUtils.getFile("DfltDwnld", [ "responsiveui.png" ]);
+      if (file.exists()) {
+        ok(true, "Screenshot file exists");
+        file.remove(false);
+        finishUp();
+      } else {
+        setTimeout(checkIfFileExist, 200);
+      }
+    }
+    checkIfFileExist();
+  }
+
   function finishUp() {
 
     // Menus are correctly updated?
     is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "false", "menu unchecked");
 
     delete instance;
     gBrowser.removeCurrentTab();
     finish();
--- a/browser/devtools/responsivedesign/test/browser_responsiveuiaddcustompreset.js
+++ b/browser/devtools/responsivedesign/test/browser_responsiveuiaddcustompreset.js
@@ -80,17 +80,17 @@ function test() {
     mgr.once("off", restart);
 
     // Force document reflow to avoid intermittent failures.
     info("document height " + document.height);
 
     // We're still in the loop of initializing the responsive mode.
     // Let's wait next loop to stop it.
     executeSoon(function() {
-      EventUtils.synthesizeKey("VK_ESCAPE", {});
+      instance.close();
     });
   }
 
   function restart() {
     info("Restarting Responsive Mode");
     mgr.once("on", function() {
       let container = gBrowser.getBrowserContainer();
       is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
@@ -130,17 +130,17 @@ function test() {
     deletedPresetB = instance.menulist.selectedItem.getAttribute("label");
     instance.removebutton.doCommand();
 
     info("waiting for responsive mode to turn off");
     mgr.once("off", restartAgain);
 
     // We're still in the loop of initializing the responsive mode.
     // Let's wait next loop to stop it.
-    executeSoon(() => EventUtils.synthesizeKey("VK_ESCAPE", {}));
+    executeSoon(() => instance.close());
   }
 
   function restartAgain() {
     info("waiting for responsive mode to turn on");
     mgr.once("on", () => {
       instance = gBrowser.selectedTab.__responsiveUI;
       testCustomPresetsNotInListAnymore();
     });
--- a/browser/locales/en-US/chrome/browser/devtools/responsiveUI.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/responsiveUI.properties
@@ -7,18 +7,26 @@
 #
 # The correct localization of this file might be to keep it in
 # English, or another language commonly spoken among web developers.
 # You want to make that choice consistent across the developer tools.
 # A good criteria is the language in which you'd find the best
 # documentation on web development on the web.
 
 
-# LOCALIZATION NOTE  (responsiveUI.rotate): label of the rotate button.
-responsiveUI.rotate=rotate
+# LOCALIZATION NOTE  (responsiveUI.rotate2): tooltip of the rotate button.
+responsiveUI.rotate2=Rotate
+
+# LOCALIZATION NOTE  (responsiveUI.screenshot): tooltip of the screenshot button.
+responsiveUI.screenshot=Screenshot
+
+# LOCALIZATION NOTE (responsiveUI.screenshotGeneratedFilename): The auto generated filename.
+# The first argument (%1$S) is the date string in yyyy-mm-dd format and the second
+# argument (%2$S) is the time string in HH.MM.SS format.
+responsiveUI.screenshotGeneratedFilename=Screen Shot %1$S at %2$S
 
 # LOCALIZATION NOTE  (responsiveUI.addPreset): label of the add preset button.
 responsiveUI.addPreset=Add Preset
 
 # LOCALIZATION NOTE  (responsiveUI.removePreset): label of the remove preset button.
 responsiveUI.removePreset=Remove Preset
 
 # LOCALIZATION NOTE  (responsiveUI.customResolution): label of the first item
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -217,16 +217,18 @@ browser.jar:
   skin/classic/browser/devtools/tool-network.png          (devtools/tool-network.png)
   skin/classic/browser/devtools/close.png                 (devtools/close.png)
   skin/classic/browser/devtools/vview-delete.png          (devtools/vview-delete.png)
   skin/classic/browser/devtools/vview-edit.png            (devtools/vview-edit.png)
   skin/classic/browser/devtools/undock.png                (devtools/undock.png)
   skin/classic/browser/devtools/font-inspector.css        (devtools/font-inspector.css)
   skin/classic/browser/devtools/computedview.css          (devtools/computedview.css)
   skin/classic/browser/devtools/arrow-e.png               (devtools/arrow-e.png)
+  skin/classic/browser/devtools/responsiveui-rotate.png   (../shared/devtools/responsiveui-rotate.png)
+  skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/responsiveui-screenshot.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-16-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-24-throbber.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -307,16 +307,18 @@ browser.jar:
   skin/classic/browser/devtools/tool-network.png            (devtools/tool-network.png)
   skin/classic/browser/devtools/close.png                   (devtools/close.png)
   skin/classic/browser/devtools/vview-delete.png            (devtools/vview-delete.png)
   skin/classic/browser/devtools/vview-edit.png              (devtools/vview-edit.png)
   skin/classic/browser/devtools/undock.png                  (devtools/undock.png)
   skin/classic/browser/devtools/font-inspector.css          (devtools/font-inspector.css)
   skin/classic/browser/devtools/computedview.css            (devtools/computedview.css)
   skin/classic/browser/devtools/arrow-e.png                 (devtools/arrow-e.png)
+  skin/classic/browser/devtools/responsiveui-rotate.png     (../shared/devtools/responsiveui-rotate.png)
+  skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/responsiveui-screenshot.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-mobileIcon.png
--- a/browser/themes/shared/devtools/responsivedesign.inc.css
+++ b/browser/themes/shared/devtools/responsivedesign.inc.css
@@ -29,16 +29,28 @@
   border-radius: 0;
 }
 
 .devtools-responsiveui-toolbar:-moz-locale-dir(ltr) > *:first-child,
 .devtools-responsiveui-toolbar:-moz-locale-dir(rtl) > *:last-child {
   margin-left: 0;
 }
 
+.devtools-responsiveui-close {
+  list-style-image: url("chrome://browser/skin/devtools/close.png");
+}
+
+.devtools-responsiveui-rotate {
+  list-style-image: url("chrome://browser/skin/devtools/responsiveui-rotate.png");
+}
+
+.devtools-responsiveui-screenshot {
+  list-style-image: url("chrome://browser/skin/devtools/responsiveui-screenshot.png");
+}
+
 .devtools-responsiveui-resizebarV {
   width: 7px;
   height: 24px;
   cursor: ew-resize;
   transform: translate(12px, -12px);
   background-image: url("chrome://browser/skin/devtools/responsive-vertical-resizer.png");
 }
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1e30f708697532e260694d36d6d0ae50cd491ef7
GIT binary patch
literal 498
zc$@+90S*3%P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0005GNkl<Zc-p0t
z-z!657{?FWq{(JTIew??ViZ%BD4YvRlI3n!EJ-9xp@_KJo!oFCGMW;N+3c6iR%4o9
ze*n1@`5A6^G2ge|qqR0QE<WdZ-{*P0-}9d5oWn#Sk$=I6r0icv2U#S>7K9)h$bkwp
z(kQ2SAr&jV{=foad9+vz6aym@)BZrv`TF)A3WvkjB$7SJqh1RdR*j}z9RC{YmLG9;
zK}Cbb=CGh}1unoj2+?>!^7~f%!P>@_8S6_@gRYZ5c9qTHKA?i&FNC6dpwke899{qL
z3=*dxK)8Q+^q-uCyf?RZKC({)Z&2y-_{iOTPtPx2ZBM^RC}l^14(PeIuAPt1uVYe=
zg*V_ZC^rV?Zc=NOgc7z{AYls;^I_1sxU@nB<^e~s@CNPJIL(&BJqS7=&ZKWdxxBKz
zNt+$<;0@F-uWy#|skv1cC}5C6=3=$G47Aaj2=sFdE-K8xI5s(Jz`6`#l9PZs&`swt
z76&aTCCw$hgZd$(X?tFT1YmFq0muNkATI$jkj7wGVwhCn>*_-+NcO*A$TP!4S}d<A
os;=i`NgL23r-C#X36eGV23z8q2k?OIX#fBK07*qoM6N<$g5THRF8}}l
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..82e6c3f3c5446fb17b430f688a33fbd28042120f
GIT binary patch
literal 528
zc$@(c0`L8aP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0005kNkl<Zc-p0r
z-Ahw(9LA6A3?i6DZ5JMhE~L7VSPhElg`!@_rYR^aBt&r1kTQty4Kpx9(cOBptdQ+O
zjMR2v^JNvGQb_d&6y3F#NcV#CLNC_y!1=}Z!XW741J8NB&-1`JM=KVK|Al5PGb9#Z
z2To7}YKf^`pd46bM~M=%e8$(=<KHh7cCh|Ma(H<3X=rr(0Wt>A*--;FP<f@QzWJil
zee1<y^c4!*$MJFJ^C~iZ%(BBS2QGd6_I(|J4<=jS{a@HrId<6PKqHJdAg=h4NUjI&
z_Agv;x~B*=J(*#LT@JM3A*M(q)2}NkuLkupqvMou{>DkiIUhB>WGcNPv+SyYsG{%w
za0G?+(MOZhg@c0_K4^@Z9&a)$2VN>JyX!_yoIcx<%jcJsm(Asu;9ciUX63-VqN>g_
z0IwyL{`tqFrgyETaX@C}Kv*&S@No!U)6MSQU?Q1{5Ndc{-egt|JXieM-HYHq5AmHr
zfKa=t8wPokSvAmvhgrc>1jAz!!I^qb+dV=}k2jfRSHrq{E&eXFyO)tX$z*fSz#O5b
zCo`)XZ$s>AdZ|$k;ROM32iyj|zz@0zH9g*#VU`_sHA|_X0~@dd$3J2>JNj>ErW>=K
S?OJXC0000<MNUMnLSTZ!nD#FK
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -244,16 +244,18 @@ browser.jar:
         skin/classic/browser/devtools/tool-network.png              (devtools/tool-network.png)
         skin/classic/browser/devtools/close.png                     (devtools/close.png)
         skin/classic/browser/devtools/vview-delete.png              (devtools/vview-delete.png)
         skin/classic/browser/devtools/vview-edit.png                (devtools/vview-edit.png)
         skin/classic/browser/devtools/undock.png                    (devtools/undock.png)
         skin/classic/browser/devtools/font-inspector.css            (devtools/font-inspector.css)
         skin/classic/browser/devtools/computedview.css              (devtools/computedview.css)
         skin/classic/browser/devtools/arrow-e.png                   (devtools/arrow-e.png)
+        skin/classic/browser/devtools/responsiveui-rotate.png       (../shared/devtools/responsiveui-rotate.png)
+        skin/classic/browser/devtools/responsiveui-screenshot.png   (../shared/devtools/responsiveui-screenshot.png)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/sync-throbber.png
         skin/classic/browser/sync-16.png
         skin/classic/browser/sync-32.png
         skin/classic/browser/sync-128.png
         skin/classic/browser/sync-bg.png
         skin/classic/browser/sync-desktopIcon.png
         skin/classic/browser/sync-mobileIcon.png
@@ -503,16 +505,18 @@ browser.jar:
         skin/classic/aero/browser/devtools/tool-network.png          (devtools/tool-network.png)
         skin/classic/aero/browser/devtools/close.png                 (devtools/close.png)
         skin/classic/aero/browser/devtools/vview-delete.png          (devtools/vview-delete.png)
         skin/classic/aero/browser/devtools/vview-edit.png            (devtools/vview-edit.png)
         skin/classic/aero/browser/devtools/undock.png                (devtools/undock.png)
         skin/classic/aero/browser/devtools/font-inspector.css        (devtools/font-inspector.css)
         skin/classic/aero/browser/devtools/computedview.css          (devtools/computedview.css)
         skin/classic/aero/browser/devtools/arrow-e.png               (devtools/arrow-e.png)
+        skin/classic/aero/browser/devtools/responsiveui-rotate.png   (../shared/devtools/responsiveui-rotate.png)
+        skin/classic/aero/browser/devtools/responsiveui-screenshot.png (../shared/devtools/responsiveui-screenshot.png)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/sync-throbber.png
         skin/classic/aero/browser/sync-16.png
         skin/classic/aero/browser/sync-32.png
         skin/classic/aero/browser/sync-128.png
         skin/classic/aero/browser/sync-bg.png
         skin/classic/aero/browser/sync-desktopIcon.png
         skin/classic/aero/browser/sync-mobileIcon.png