Bug 964015 - Add "Copy as data URI" for images in the Network Monitor. r=vp
authorPeiyong Lin <pylaurent1314@gmail.com>
Sun, 02 Feb 2014 10:39:53 +0200
changeset 182477 4e95343c93e6c4d49457a4391dd866d3117a9620
parent 182476 5744bf41d817d9ef8dc37875b3ea6caf09338990
child 182478 463bae14bef347a349f6026a843a298371f478c0
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvp
bugs964015
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 964015 - Add "Copy as data URI" for images in the Network Monitor. r=vp
browser/devtools/netmonitor/netmonitor-view.js
browser/devtools/netmonitor/netmonitor.xul
browser/devtools/netmonitor/test/browser.ini
browser/devtools/netmonitor/test/browser_net_copy_image_as_data_uri.js
browser/devtools/netmonitor/test/head.js
browser/devtools/netmonitor/test/html_content-type-without-cache-test-page.html
browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -347,29 +347,31 @@ RequestsMenuView.prototype = Heritage.ex
     window.addEventListener("resize", this._onResize, false);
 
     this.requestsMenuSortEvent = getKeyWithEvent(this.sortBy.bind(this));
     this.requestsMenuFilterEvent = getKeyWithEvent(this.filterOn.bind(this));
     this.reqeustsMenuClearEvent = this.clear.bind(this);
     this._onContextShowing = this._onContextShowing.bind(this);
     this._onContextNewTabCommand = this.openRequestInTab.bind(this);
     this._onContextCopyUrlCommand = this.copyUrl.bind(this);
+    this._onContextCopyImageAsDataUriCommand = this.copyImageAsDataUri.bind(this);
     this._onContextResendCommand = this.cloneSelectedRequest.bind(this);
     this._onContextPerfCommand = () => NetMonitorView.toggleFrontendMode();
 
     this.sendCustomRequestEvent = this.sendCustomRequest.bind(this);
     this.closeCustomRequestEvent = this.closeCustomRequest.bind(this);
     this.cloneSelectedRequestEvent = this.cloneSelectedRequest.bind(this);
 
     $("#toolbar-labels").addEventListener("click", this.requestsMenuSortEvent, false);
     $("#requests-menu-footer").addEventListener("click", this.requestsMenuFilterEvent, false);
     $("#requests-menu-clear-button").addEventListener("click", this.reqeustsMenuClearEvent, false);
     $("#network-request-popup").addEventListener("popupshowing", this._onContextShowing, false);
     $("#request-menu-context-newtab").addEventListener("command", this._onContextNewTabCommand, false);
     $("#request-menu-context-copy-url").addEventListener("command", this._onContextCopyUrlCommand, false);
+    $("#request-menu-context-copy-image-as-data-uri").addEventListener("command", this._onContextCopyImageAsDataUriCommand, false);
     $("#request-menu-context-resend").addEventListener("command", this._onContextResendCommand, false);
     $("#request-menu-context-perf").addEventListener("command", this._onContextPerfCommand, false);
 
     $("#requests-menu-perf-notice-button").addEventListener("command", this._onContextPerfCommand, false);
     $("#requests-menu-network-summary-button").addEventListener("command", this._onContextPerfCommand, false);
     $("#requests-menu-network-summary-label").addEventListener("click", this._onContextPerfCommand, false);
 
     $("#custom-request-send-button").addEventListener("click", this.sendCustomRequestEvent, false);
@@ -388,16 +390,17 @@ RequestsMenuView.prototype = Heritage.ex
     window.removeEventListener("resize", this._onResize, false);
 
     $("#toolbar-labels").removeEventListener("click", this.requestsMenuSortEvent, false);
     $("#requests-menu-footer").removeEventListener("click", this.requestsMenuFilterEvent, false);
     $("#requests-menu-clear-button").removeEventListener("click", this.reqeustsMenuClearEvent, false);
     $("#network-request-popup").removeEventListener("popupshowing", this._onContextShowing, false);
     $("#request-menu-context-newtab").removeEventListener("command", this._onContextNewTabCommand, false);
     $("#request-menu-context-copy-url").removeEventListener("command", this._onContextCopyUrlCommand, false);
+    $("#request-menu-context-copy-image-as-data-uri").removeEventListener("command", this._onContextCopyImageAsDataUriCommand, false);
     $("#request-menu-context-resend").removeEventListener("command", this._onContextResendCommand, false);
     $("#request-menu-context-perf").removeEventListener("command", this._onContextPerfCommand, false);
 
     $("#requests-menu-perf-notice-button").removeEventListener("command", this._onContextPerfCommand, false);
     $("#requests-menu-network-summary-button").removeEventListener("command", this._onContextPerfCommand, false);
     $("#requests-menu-network-summary-label").removeEventListener("click", this._onContextPerfCommand, false);
 
     $("#custom-request-send-button").removeEventListener("click", this.sendCustomRequestEvent, false);
@@ -502,16 +505,28 @@ RequestsMenuView.prototype = Heritage.ex
    * Copy the request url from the currently selected item.
    */
   copyUrl: function() {
     let selected = this.selectedItem.attachment;
     clipboardHelper.copyString(selected.url, document);
   },
 
   /**
+   * Copy image as data uri.
+   */
+  copyImageAsDataUri: function() {
+    let selected = this.selectedItem.attachment;
+    let { mimeType, text, encoding } = selected.responseContent.content;
+    gNetwork.getString(text).then(aString => {
+      let data = "data:" + mimeType + ";" + encoding + "," + aString;
+      clipboardHelper.copyString(data, document);
+    });
+  },
+
+  /**
    * Send a new HTTP request using the data in the custom request form.
    */
   sendCustomRequest: function() {
     let selected = this.selectedItem.attachment;
 
     let data = Object.create(selected, {
       headers: { value: selected.requestHeaders.headers }
     });
@@ -1375,24 +1390,31 @@ RequestsMenuView.prototype = Heritage.ex
     setNamedTimeout(
       "resize-events", RESIZE_REFRESH_RATE, () => this._flushWaterfallViews(true));
   },
 
   /**
    * Handle the context menu opening. Hide items if no request is selected.
    */
   _onContextShowing: function() {
+    let selectedItem = this.selectedItem;
+
     let resendElement = $("#request-menu-context-resend");
-    resendElement.hidden = !this.selectedItem || this.selectedItem.attachment.isCustom;
+    resendElement.hidden = !selectedItem || selectedItem.attachment.isCustom;
 
     let copyUrlElement = $("#request-menu-context-copy-url");
-    copyUrlElement.hidden = !this.selectedItem;
+    copyUrlElement.hidden = !selectedItem;
+
+    let copyImageAsDataUriElement = $("#request-menu-context-copy-image-as-data-uri");
+    copyImageAsDataUriElement.hidden = !selectedItem ||
+      !selectedItem.attachment.responseContent ||
+      !selectedItem.attachment.responseContent.content.mimeType.contains("image/");
 
     let newTabElement = $("#request-menu-context-newtab");
-    newTabElement.hidden = !this.selectedItem;
+    newTabElement.hidden = !selectedItem;
   },
 
   /**
    * Checks if the specified unix time is the first one to be known of,
    * and saves it if so.
    *
    * @param number aUnixTime
    *        The milliseconds to check and save.
--- a/browser/devtools/netmonitor/netmonitor.xul
+++ b/browser/devtools/netmonitor/netmonitor.xul
@@ -23,16 +23,19 @@
   <popupset id="networkPopupSet">
     <menupopup id="network-request-popup">
       <menuitem id="request-menu-context-newtab"
                 label="&netmonitorUI.context.newTab;"
                 accesskey="&netmonitorUI.context.newTab.accesskey;"/>
       <menuitem id="request-menu-context-copy-url"
                 label="&netmonitorUI.context.copyUrl;"
                 accesskey="&netmonitorUI.context.copyUrl.accesskey;"/>
+      <menuitem id="request-menu-context-copy-image-as-data-uri"
+                label="&netmonitorUI.context.copyImageAsDataUri;"
+                accesskey="&netmonitorUI.context.copyImageAsDataUri.accesskey;"/>
       <menuitem id="request-menu-context-resend"
                 label="&netmonitorUI.summary.editAndResend;"
                 accesskey="&netmonitorUI.summary.editAndResend.accesskey;"/>
       <menuseparator/>
       <menuitem id="request-menu-context-perf"
                 label="&netmonitorUI.context.perfTools;"
                 accesskey="&netmonitorUI.context.perfTools.accesskey;"/>
     </menupopup>
--- a/browser/devtools/netmonitor/test/browser.ini
+++ b/browser/devtools/netmonitor/test/browser.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
 support-files =
   head.js
   html_content-type-test-page.html
+  html_content-type-without-cache-test-page.html
   html_custom-get-page.html
   html_cyrillic-test-page.html
   html_filter-test-page.html
   html_infinite-get-page.html
   html_json-custom-mime-test-page.html
   html_json-long-test-page.html
   html_json-malformed-test-page.html
   html_jsonp-test-page.html
@@ -30,16 +31,17 @@ support-files =
 [browser_net_charts-01.js]
 [browser_net_charts-02.js]
 [browser_net_charts-03.js]
 [browser_net_charts-04.js]
 [browser_net_charts-05.js]
 [browser_net_clear.js]
 [browser_net_content-type.js]
 [browser_net_copy_url.js]
+[browser_net_copy_image_as_data_uri.js]
 [browser_net_cyrillic-01.js]
 [browser_net_cyrillic-02.js]
 [browser_net_filter-01.js]
 [browser_net_filter-02.js]
 [browser_net_filter-03.js]
 [browser_net_footer-summary.js]
 [browser_net_json-long.js]
 [browser_net_json-malformed.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/netmonitor/test/browser_net_copy_image_as_data_uri.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if copying an image as data uri works.
+ */
+
+function test() {
+  initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL).then(([aTab, aDebuggee, aMonitor]) => {
+    info("Starting test... ");
+
+    let { NetMonitorView } = aMonitor.panelWin;
+    let { RequestsMenu } = NetMonitorView;
+    RequestsMenu.lazyUpdate = false;
+    let imageDataUri = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
+
+    waitForNetworkEvents(aMonitor, 6).then(() => {
+      let requestItem = RequestsMenu.getItemAtIndex(5);
+      RequestsMenu.selectedItem = requestItem;
+
+      waitForClipboard(imageDataUri, function setup() {
+        RequestsMenu.copyImageAsDataUri();
+      }, function onSuccess() {
+        ok(true, "Clipboard contains the currently selected image as data uri.");
+        cleanUp();
+      }, function onFailure() {
+        ok(false, "Copying the currently selected image as data uri was unsuccessful.");
+        cleanUp();
+      });
+    });
+
+    aDebuggee.performRequests();
+
+    function cleanUp(){
+      teardown(aMonitor).then(finish);
+    }
+  });
+}
--- a/browser/devtools/netmonitor/test/head.js
+++ b/browser/devtools/netmonitor/test/head.js
@@ -12,16 +12,17 @@ let { devtools } = Cu.import("resource:/
 let TargetFactory = devtools.TargetFactory;
 let Toolbox = devtools.Toolbox;
 
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/netmonitor/test/";
 
 const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html";
 const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html";
 const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html";
+const CONTENT_TYPE_WITHOUT_CACHE_URL = EXAMPLE_URL + "html_content-type-without-cache-test-page.html";
 const CYRILLIC_URL = EXAMPLE_URL + "html_cyrillic-test-page.html";
 const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html";
 const POST_DATA_URL = EXAMPLE_URL + "html_post-data-test-page.html";
 const POST_RAW_URL = EXAMPLE_URL + "html_post-raw-test-page.html";
 const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html";
 const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html";
 const JSON_MALFORMED_URL = EXAMPLE_URL + "html_json-malformed-test-page.html";
 const JSON_CUSTOM_MIME_URL = EXAMPLE_URL + "html_json-custom-mime-test-page.html";
new file mode 100644
--- /dev/null
+++ b/browser/devtools/netmonitor/test/html_content-type-without-cache-test-page.html
@@ -0,0 +1,45 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Network Monitor test page</title>
+  </head>
+
+  <body>
+    <p>Content type test</p>
+
+    <script type="text/javascript">
+      function get(aAddress, aCallback) {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", aAddress, true);
+
+        xhr.onreadystatechange = function() {
+          if (this.readyState == this.DONE) {
+            aCallback();
+          }
+        };
+        xhr.send(null);
+      }
+
+      function performRequests() {
+        get("sjs_content-type-test-server.sjs?fmt=xml", function() {
+          get("sjs_content-type-test-server.sjs?fmt=css", function() {
+            get("sjs_content-type-test-server.sjs?fmt=js", function() {
+              get("sjs_content-type-test-server.sjs?fmt=json", function() {
+                get("sjs_content-type-test-server.sjs?fmt=bogus", function() {
+                  get("test-image.png?v=" + Math.random(), function() {
+                    // Done.
+                  });
+                });
+              });
+            });
+          });
+        });
+      }
+    </script>
+  </body>
+
+</html>
--- a/browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd
@@ -197,16 +197,24 @@
 <!-- LOCALIZATION NOTE (netmonitorUI.context.copyUrl): This is the label displayed
   -  on the context menu that copies the selected request's url -->
 <!ENTITY netmonitorUI.context.copyUrl     "Copy URL">
 
 <!-- LOCALIZATION NOTE (netmonitorUI.context.copyUrl.accesskey): This is the access key
   -  for the Copy URL menu item displayed in the context menu for a request -->
 <!ENTITY netmonitorUI.context.copyUrl.accesskey "C">
 
+<!-- LOCALIZATION NOTE (netmonitorUI.context.copyImageAsDataUri): This is the label displayed
+  -  on the context menu that copies the selected image as data uri -->
+<!ENTITY netmonitorUI.context.copyImageAsDataUri "Copy Image as Data URI">
+
+<!-- LOCALIZATION NOTE (netmonitorUI.context.copyImageAsDataUri.accesskey): This is the access key
+  -  for the Copy Image As Data URI menu item displayed in the context menu for a request -->
+<!ENTITY netmonitorUI.context.copyImageAsDataUri.accesskey  "I">
+
 <!-- LOCALIZATION NOTE (debuggerUI.summary.editAndResend): This is the label displayed
   -  on the button in the headers tab that opens a form to edit and resend the currently
      displayed request -->
 <!ENTITY netmonitorUI.summary.editAndResend "Edit and Resend">
 
 <!-- LOCALIZATION NOTE (debuggerUI.summary.editAndResend.accesskey): This is the access key
   -  for the "Edit and Resend" menu item displayed in the context menu for a request -->
 <!ENTITY netmonitorUI.summary.editAndResend.accesskey "R">