Backed out changeset bc2d12101a25 (bug 861335) for browser_perf-recording-selected-04.js leaks CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Fri, 11 Sep 2015 13:33:39 -0700
changeset 261970 2bfa1927d504401c7c8f8f61d2e38433e81d4b77
parent 261969 6dbfe6719621efde26304de20ebf9c6e2cf1f1b2
child 262010 e522b63d1fb35aeb49721247690f88e5584d60f8
push id15147
push userkwierso@gmail.com
push dateFri, 11 Sep 2015 20:35:31 +0000
treeherderfx-team@2bfa1927d504 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs861335
milestone43.0a1
backs outbc2d12101a250b5f82d991e5c70d36709387354b
Backed out changeset bc2d12101a25 (bug 861335) for browser_perf-recording-selected-04.js leaks CLOSED TREE
browser/devtools/jar.mn
browser/devtools/netmonitor/netmonitor-controller.js
browser/devtools/webconsole/NetworkPanel.xhtml
browser/devtools/webconsole/moz.build
browser/devtools/webconsole/network-panel.js
browser/devtools/webconsole/test/browser.ini
browser/devtools/webconsole/test/browser_netpanel_longstring_expand.js
browser/devtools/webconsole/test/browser_webconsole_bug_594477_clickable_output.js
browser/devtools/webconsole/test/browser_webconsole_bug_602572_log_bodies_checkbox.js
browser/devtools/webconsole/test/browser_webconsole_bug_618311_close_panels.js
browser/devtools/webconsole/test/browser_webconsole_netlogging.js
browser/devtools/webconsole/test/browser_webconsole_network_panel.js
browser/devtools/webconsole/webconsole.js
browser/locales/en-US/chrome/browser/devtools/webConsole.dtd
browser/locales/en-US/chrome/browser/devtools/webconsole.properties
browser/themes/linux/jar.mn
browser/themes/osx/jar.mn
browser/themes/shared/devtools/webconsole_networkpanel.css
browser/themes/windows/jar.mn
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -11,16 +11,17 @@ browser.jar:
     content/browser/devtools/readdir.js                                (projecteditor/lib/helpers/readdir.js)
     content/browser/devtools/projecteditor-loader.xul                        (projecteditor/chrome/content/projecteditor-loader.xul)
     content/browser/devtools/projecteditor-test.xul                          (projecteditor/chrome/content/projecteditor-test.xul)
     content/browser/devtools/projecteditor-loader.js                         (projecteditor/chrome/content/projecteditor-loader.js)
     content/browser/devtools/netmonitor.xul                            (netmonitor/netmonitor.xul)
     content/browser/devtools/netmonitor.css                            (netmonitor/netmonitor.css)
     content/browser/devtools/netmonitor-controller.js                  (netmonitor/netmonitor-controller.js)
     content/browser/devtools/netmonitor-view.js                        (netmonitor/netmonitor-view.js)
+    content/browser/devtools/NetworkPanel.xhtml                        (webconsole/NetworkPanel.xhtml)
     content/browser/devtools/webconsole.xul                            (webconsole/webconsole.xul)
 *   content/browser/devtools/scratchpad.xul                            (scratchpad/scratchpad.xul)
     content/browser/devtools/scratchpad.js                             (scratchpad/scratchpad.js)
     content/browser/devtools/splitview.css                             (shared/splitview.css)
     content/browser/devtools/theme-switching.js                        (shared/theme-switching.js)
     content/browser/devtools/frame-script-utils.js                     (shared/frame-script-utils.js)
     content/browser/devtools/styleeditor.xul                           (styleeditor/styleeditor.xul)
     content/browser/devtools/styleeditor.css                           (styleeditor/styleeditor.css)
--- a/browser/devtools/netmonitor/netmonitor-controller.js
+++ b/browser/devtools/netmonitor/netmonitor-controller.js
@@ -318,47 +318,16 @@ let NetMonitorController = {
       this._currentActivity = aType;
       return reconfigureTab({ cacheDisabled: true, performReload: false }).then(standBy);
     }
     this._currentActivity = ACTIVITY_TYPE.NONE;
     return promise.reject(new Error("Invalid activity type"));
   },
 
   /**
-   * Selects the specified request in the waterfall and opens the details view.
-   *
-   * @param string requestId
-   *        The actor ID of the request to inspect.
-   * @return object
-   *         A promise resolved once the task finishes.
-   */
-  inspectRequest: function(requestId) {
-    // Look for the request in the existing ones or wait for it to appear, if
-    // the network monitor is still loading.
-    let deferred = promise.defer();
-    let request = null;
-    let inspector = function() {
-      let predicate = i => i.value === requestId;
-      request = NetMonitorView.RequestsMenu.getItemForPredicate(predicate);
-      if (request) {
-        window.off(EVENTS.REQUEST_ADDED, inspector);
-        NetMonitorView.RequestsMenu.filterOn("all");
-        NetMonitorView.RequestsMenu.selectedItem = request;
-        deferred.resolve();
-      }
-    }
-
-    inspector();
-    if (!request) {
-      window.on(EVENTS.REQUEST_ADDED, inspector);
-    }
-    return deferred.promise;
-  },
-
-  /**
    * Getter that tells if the server supports sending custom network requests.
    * @type boolean
    */
   get supportsCustomRequest() {
     return this.webConsoleClient &&
            (this.webConsoleClient.traits.customNetworkRequest ||
             !this._target.isApp);
   },
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/NetworkPanel.xhtml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
+<!ENTITY % webConsoleDTD SYSTEM "chrome://browser/locale/devtools/webConsole.dtd" >
+%webConsoleDTD;
+]>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+  <link rel="stylesheet" href="chrome://browser/skin/devtools/webconsole_networkpanel.css" type="text/css"/>
+</head>
+<body role="application">
+<table id="header">
+  <tr>
+    <th class="property-name"
+        scope="row">&networkPanel.requestURLColon;</th>
+    <td class="property-value"
+        id="headUrl"></td>
+  </tr>
+  <tr>
+    <th class="property-name"
+        scope="row">&networkPanel.requestMethodColon;</th>
+    <td class="property-value"
+        id="headMethod"></td>
+  </tr>
+  <tr>
+    <th class="property-name"
+        scope="row">&networkPanel.statusCodeColon;</th>
+    <td class="property-value"
+        id="headStatus"></td>
+  </tr>
+</table>
+
+<div class="group">
+  <h1>
+    &networkPanel.requestHeaders;
+    <span id="requestHeadersInfo" class="info"></span>
+  </h1>
+  <table class="property-table" id="requestHeadersContent"></table>
+
+  <div id="requestCookie" style="display:none">
+    <h1>&networkPanel.requestCookie;</h1>
+    <table class="property-table" id="requestCookieContent"></table>
+  </div>
+
+  <div id="requestBody" style="display:none">
+    <h1>&networkPanel.requestBody;</h1>
+    <table class="property-table" id="requestBodyContent"></table>
+  </div>
+  <div id="requestFormData" style="display:none">
+    <h1>&networkPanel.requestFormData;</h1>
+    <table class="property-table" id="requestFormDataContent"></table>
+  </div>
+  <p id="requestBodyFetchLink" style="display:none"></p>
+</div>
+
+<div class="group" id="responseContainer" style="display:none">
+  <h1>
+    &networkPanel.responseHeaders;
+    <span id="responseHeadersInfo" class="info">&Delta;</span>
+  </h1>
+  <table class="property-table" id="responseHeadersContent"></table>
+
+  <div id="responseCookie" style="display:none">
+    <h1>&networkPanel.responseCookie;</h1>
+    <table class="property-table" id="responseCookieContent"></table>
+  </div>
+
+  <div id="responseBody" style="display:none">
+    <h1>
+      &networkPanel.responseBody;
+      <span class="info" id="responseBodyInfo">&Delta;</span>
+    </h1>
+    <table class="property-table" id="responseBodyContent"></table>
+  </div>
+  <div id="responseBodyCached" style="display:none">
+    <h1>
+      &networkPanel.responseBodyCached;
+      <span class="info" id="responseBodyCachedInfo">&Delta;</span>
+    </h1>
+    <table class="property-table" id="responseBodyCachedContent"></table>
+  </div>
+  <div id="responseNoBody" style="display:none">
+    <h1>
+      &networkPanel.responseNoBody;
+      <span id="responseNoBodyInfo" class="info">&Delta;</span>
+    </h1>
+  </div>
+  <div id="responseBodyUnknownType" style="display:none">
+    <h1>
+      &networkPanel.responseBodyUnknownType;
+      <span id="responseBodyUnknownTypeInfo" class="info">&Delta;</span>
+    </h1>
+    <table class="property-table" id="responseBodyUnknownTypeContent"></table>
+  </div>
+  <div id="responseImage" style="display:none">
+    <h1>
+      &networkPanel.responseImage;
+      <span id="responseImageInfo" class="info"></span>
+    </h1>
+    <div id="responseImageNodeDiv">
+      <img id="responseImageNode" />
+    </div>
+  </div>
+  <div id="responseImageCached" style="display:none">
+    <h1>
+      &networkPanel.responseImageCached;
+      <span id="responseImageCachedInfo" class="info"></span>
+    </h1>
+    <div id="responseImageNodeDiv">
+      <img id="responseImageCachedNode" />
+    </div>
+  </div>
+  <p id="responseBodyFetchLink" style="display:none"></p>
+</div>
+</body>
+</html>
--- a/browser/devtools/webconsole/moz.build
+++ b/browser/devtools/webconsole/moz.build
@@ -5,11 +5,12 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 
 EXTRA_JS_MODULES.devtools.webconsole += [
     'console-commands.js',
     'console-output.js',
     'hudservice.js',
+    'network-panel.js',
     'panel.js',
     'webconsole.js',
 ]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/network-panel.js
@@ -0,0 +1,835 @@
+/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc, Ci, Cu} = require("chrome");
+
+loader.lazyGetter(this, "NetworkHelper", () => require("devtools/toolkit/webconsole/network-helper"));
+loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
+loader.lazyServiceGetter(this, "mimeService", "@mozilla.org/mime;1", "nsIMIMEService");
+
+let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
+
+const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
+let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
+
+
+/**
+ * Creates a new NetworkPanel.
+ *
+ * @constructor
+ * @param nsIDOMNode aParent
+ *        Parent node to append the created panel to.
+ * @param object aHttpActivity
+ *        HttpActivity to display in the panel.
+ * @param object aWebConsoleFrame
+ *        The parent WebConsoleFrame object that owns this network panel
+ *        instance.
+ */
+function NetworkPanel(aParent, aHttpActivity, aWebConsoleFrame)
+{
+  let doc = aParent.ownerDocument;
+  this.httpActivity = aHttpActivity;
+  this.webconsole = aWebConsoleFrame;
+  this._responseBodyFetch = this._responseBodyFetch.bind(this);
+  this._requestBodyFetch = this._requestBodyFetch.bind(this);
+
+  // Create the underlaying panel
+  this.panel = createElement(doc, "panel", {
+    label: l10n.getStr("NetworkPanel.label"),
+    titlebar: "normal",
+    noautofocus: "true",
+    noautohide: "true",
+    close: "true"
+  });
+
+  // Create the iframe that displays the NetworkPanel XHTML.
+  this.iframe = createAndAppendElement(this.panel, "iframe", {
+    src: "chrome://browser/content/devtools/NetworkPanel.xhtml",
+    type: "content",
+    flex: "1"
+  });
+
+  let self = this;
+
+  // Destroy the panel when it's closed.
+  this.panel.addEventListener("popuphidden", function onPopupHide() {
+    self.panel.removeEventListener("popuphidden", onPopupHide, false);
+    self.panel.parentNode.removeChild(self.panel);
+    self.panel = null;
+    self.iframe = null;
+    self.httpActivity = null;
+    self.webconsole = null;
+
+    if (self.linkNode) {
+      self.linkNode._panelOpen = false;
+      self.linkNode = null;
+    }
+  }, false);
+
+  // Set the document object and update the content once the panel is loaded.
+  this.iframe.addEventListener("load", function onLoad() {
+    if (!self.iframe) {
+      return;
+    }
+
+    self.iframe.removeEventListener("load", onLoad, true);
+    self.update();
+  }, true);
+
+  this.panel.addEventListener("popupshown", function onPopupShown() {
+    self.panel.removeEventListener("popupshown", onPopupShown, true);
+    self.update();
+  }, true);
+
+  // Create the footer.
+  let footer = createElement(doc, "hbox", { align: "end" });
+  createAndAppendElement(footer, "spacer", { flex: 1 });
+
+  createAndAppendElement(footer, "resizer", { dir: "bottomend" });
+  this.panel.appendChild(footer);
+
+  aParent.appendChild(this.panel);
+}
+exports.NetworkPanel = NetworkPanel;
+
+NetworkPanel.prototype =
+{
+  /**
+   * The current state of the output.
+   */
+  _state: 0,
+
+  /**
+   * State variables.
+   */
+  _INIT: 0,
+  _DISPLAYED_REQUEST_HEADER: 1,
+  _DISPLAYED_REQUEST_BODY: 2,
+  _DISPLAYED_RESPONSE_HEADER: 3,
+  _TRANSITION_CLOSED: 4,
+
+  _fromDataRegExp: /Content-Type\:\s*application\/x-www-form-urlencoded/,
+
+  _contentType: null,
+
+  /**
+   * Function callback invoked whenever the panel content is updated. This is
+   * used only by tests.
+   *
+   * @private
+   * @type function
+   */
+  _onUpdate: null,
+
+  get document() {
+    return this.iframe && this.iframe.contentWindow ?
+           this.iframe.contentWindow.document : null;
+  },
+
+  /**
+   * Small helper function that is nearly equal to l10n.getFormatStr
+   * except that it prefixes aName with "NetworkPanel.".
+   *
+   * @param string aName
+   *        The name of an i10n string to format. This string is prefixed with
+   *        "NetworkPanel." before calling the HUDService.getFormatStr function.
+   * @param array aArray
+   *        Values used as placeholder for the i10n string.
+   * @returns string
+   *          The i10n formated string.
+   */
+  _format: function NP_format(aName, aArray)
+  {
+    return l10n.getFormatStr("NetworkPanel." + aName, aArray);
+  },
+
+  /**
+   * Returns the content type of the response body. This is based on the
+   * response.content.mimeType property. If this value is not available, then
+   * the content type is guessed by the file extension of the request URL.
+   *
+   * @return string
+   *         Content type or empty string if no content type could be figured
+   *         out.
+   */
+  get contentType()
+  {
+    if (this._contentType) {
+      return this._contentType;
+    }
+
+    let request = this.httpActivity.request;
+    let response = this.httpActivity.response;
+
+    let contentType = "";
+    let types = response.content ?
+                (response.content.mimeType || "").split(/,|;/) : [];
+    for (let i = 0; i < types.length; i++) {
+      if (types[i] in NetworkHelper.mimeCategoryMap) {
+        contentType = types[i];
+        break;
+      }
+    }
+
+    if (contentType) {
+      this._contentType = contentType;
+      return contentType;
+    }
+
+    // Try to get the content type from the request file extension.
+    let uri = NetUtil.newURI(request.url);
+    if ((uri instanceof Ci.nsIURL) && uri.fileExtension) {
+      try {
+         contentType = mimeService.getTypeFromExtension(uri.fileExtension);
+      }
+      catch(ex) {
+        // Added to prevent failures on OS X 64. No Flash?
+        Cu.reportError(ex);
+      }
+    }
+
+    this._contentType = contentType;
+    return contentType;
+  },
+
+  /**
+   *
+   * @returns boolean
+   *          True if the response is an image, false otherwise.
+   */
+  get _responseIsImage()
+  {
+    return this.contentType &&
+           NetworkHelper.mimeCategoryMap[this.contentType] == "image";
+  },
+
+  /**
+   *
+   * @returns boolean
+   *          True if the response body contains text, false otherwise.
+   */
+  get _isResponseBodyTextData()
+  {
+    return this.contentType ?
+           NetworkHelper.isTextMimeType(this.contentType) : false;
+  },
+
+  /**
+   * Tells if the server response is cached.
+   *
+   * @returns boolean
+   *          Returns true if the server responded that the request is already
+   *          in the browser's cache, false otherwise.
+   */
+  get _isResponseCached()
+  {
+    return this.httpActivity.response.status == 304;
+  },
+
+  /**
+   * Tells if the request body includes form data.
+   *
+   * @returns boolean
+   *          Returns true if the posted body contains form data.
+   */
+  get _isRequestBodyFormData()
+  {
+    let requestBody = this.httpActivity.request.postData.text;
+    if (typeof requestBody == "object" && requestBody.type == "longString") {
+      requestBody = requestBody.initial;
+    }
+    return this._fromDataRegExp.test(requestBody);
+  },
+
+  /**
+   * Appends the node with id=aId by the text aValue.
+   *
+   * @private
+   * @param string aId
+   * @param string aValue
+   * @return nsIDOMElement
+   *         The DOM element with id=aId.
+   */
+  _appendTextNode: function NP__appendTextNode(aId, aValue)
+  {
+    let textNode = this.document.createTextNode(aValue);
+    let elem = this.document.getElementById(aId);
+    elem.appendChild(textNode);
+    return elem;
+  },
+
+  /**
+   * Generates some HTML to display the key-value pair of the aList data. The
+   * generated HTML is added to node with id=aParentId.
+   *
+   * @param string aParentId
+   *        Id of the parent node to append the list to.
+   * @oaram array aList
+   *        Array that holds the objects you want to display. Each object must
+   *        have two properties: name and value.
+   * @param boolean aIgnoreCookie
+   *        If true, the key-value named "Cookie" is not added to the list.
+   * @returns void
+   */
+  _appendList: function NP_appendList(aParentId, aList, aIgnoreCookie)
+  {
+    let parent = this.document.getElementById(aParentId);
+    let doc = this.document;
+
+    aList.sort(function(a, b) {
+      return a.name.toLowerCase() < b.name.toLowerCase();
+    });
+
+    aList.forEach((aItem) => {
+      let name = aItem.name;
+      if (aIgnoreCookie && (name == "Cookie" || name == "Set-Cookie")) {
+        return;
+      }
+
+      let value = aItem.value;
+      let longString = null;
+      if (typeof value == "object" && value.type == "longString") {
+        value = value.initial;
+        longString = true;
+      }
+
+      /**
+       * The following code creates the HTML:
+       * <tr>
+       * <th scope="row" class="property-name">${line}:</th>
+       * <td class="property-value">${aList[line]}</td>
+       * </tr>
+       * and adds it to parent.
+       */
+      let row = doc.createElement("tr");
+      let textNode = doc.createTextNode(name + ":");
+      let th = doc.createElement("th");
+      th.setAttribute("scope", "row");
+      th.setAttribute("class", "property-name");
+      th.appendChild(textNode);
+      row.appendChild(th);
+
+      textNode = doc.createTextNode(value);
+      let td = doc.createElement("td");
+      td.setAttribute("class", "property-value");
+      td.appendChild(textNode);
+
+      if (longString) {
+        let a = doc.createElement("a");
+        a.href = "#";
+        a.className = "longStringEllipsis";
+        a.addEventListener("mousedown", this._longStringClick.bind(this, aItem));
+        a.textContent = l10n.getStr("longStringEllipsis");
+        td.appendChild(a);
+      }
+
+      row.appendChild(td);
+
+      parent.appendChild(row);
+    });
+  },
+
+  /**
+   * The click event handler for the ellipsis which allows the user to retrieve
+   * the full header value.
+   *
+   * @private
+   * @param object aHeader
+   *        The header object with the |name| and |value| properties.
+   * @param nsIDOMEvent aEvent
+   *        The DOM click event object.
+   */
+  _longStringClick: function NP__longStringClick(aHeader, aEvent)
+  {
+    aEvent.preventDefault();
+
+    let longString = this.webconsole.webConsoleClient.longString(aHeader.value);
+
+    longString.substring(longString.initial.length, longString.length,
+      function NP__onLongStringSubstring(aResponse)
+      {
+        if (aResponse.error) {
+          Cu.reportError("NP__onLongStringSubstring error: " + aResponse.error);
+          return;
+        }
+
+        aHeader.value = aHeader.value.initial + aResponse.substring;
+
+        let textNode = aEvent.target.previousSibling;
+        textNode.textContent += aResponse.substring;
+        textNode.parentNode.removeChild(aEvent.target);
+      });
+  },
+
+  /**
+   * Displays the node with id=aId.
+   *
+   * @private
+   * @param string aId
+   * @return nsIDOMElement
+   *         The element with id=aId.
+   */
+  _displayNode: function NP__displayNode(aId)
+  {
+    let elem = this.document.getElementById(aId);
+    elem.style.display = "block";
+  },
+
+  /**
+   * Sets the request URL, request method, the timing information when the
+   * request started and the request header content on the NetworkPanel.
+   * If the request header contains cookie data, a list of sent cookies is
+   * generated and a special sent cookie section is displayed + the cookie list
+   * added to it.
+   *
+   * @returns void
+   */
+  _displayRequestHeader: function NP__displayRequestHeader()
+  {
+    let request = this.httpActivity.request;
+    let requestTime = new Date(this.httpActivity.startedDateTime);
+
+    this._appendTextNode("headUrl", request.url);
+    this._appendTextNode("headMethod", request.method);
+    this._appendTextNode("requestHeadersInfo",
+                         l10n.timestampString(requestTime));
+
+    this._appendList("requestHeadersContent", request.headers, true);
+
+    if (request.cookies.length > 0) {
+      this._displayNode("requestCookie");
+      this._appendList("requestCookieContent", request.cookies);
+    }
+  },
+
+  /**
+   * Displays the request body section of the NetworkPanel and set the request
+   * body content on the NetworkPanel.
+   *
+   * @returns void
+   */
+  _displayRequestBody: function NP__displayRequestBody()
+  {
+    let postData = this.httpActivity.request.postData;
+    this._displayNode("requestBody");
+    this._appendTextNode("requestBodyContent", postData.text);
+  },
+
+  /*
+   * Displays the `sent form data` section. Parses the request header for the
+   * submitted form data displays it inside of the `sent form data` section.
+   *
+   * @returns void
+   */
+  _displayRequestForm: function NP__processRequestForm()
+  {
+    let postData = this.httpActivity.request.postData.text;
+    let requestBodyLines = postData.split("\n");
+    let formData = requestBodyLines[requestBodyLines.length - 1].
+                      replace(/\+/g, " ").split("&");
+
+    function unescapeText(aText)
+    {
+      try {
+        return decodeURIComponent(aText);
+      }
+      catch (ex) {
+        return decodeURIComponent(unescape(aText));
+      }
+    }
+
+    let formDataArray = [];
+    for (let i = 0; i < formData.length; i++) {
+      let data = formData[i];
+      let idx = data.indexOf("=");
+      let key = data.substring(0, idx);
+      let value = data.substring(idx + 1);
+      formDataArray.push({
+        name: unescapeText(key),
+        value: unescapeText(value)
+      });
+    }
+
+    this._appendList("requestFormDataContent", formDataArray);
+    this._displayNode("requestFormData");
+  },
+
+  /**
+   * Displays the response section of the NetworkPanel, sets the response status,
+   * the duration between the start of the request and the receiving of the
+   * response header as well as the response header content on the the NetworkPanel.
+   *
+   * @returns void
+   */
+  _displayResponseHeader: function NP__displayResponseHeader()
+  {
+    let timing = this.httpActivity.timings;
+    let response = this.httpActivity.response;
+
+    this._appendTextNode("headStatus",
+                         [response.httpVersion, response.status,
+                          response.statusText].join(" "));
+
+    // Calculate how much time it took from the request start, until the
+    // response started to be received.
+    let deltaDuration = 0;
+    ["dns", "connect", "send", "wait"].forEach(function (aValue) {
+      let ms = timing[aValue];
+      if (ms > -1) {
+        deltaDuration += ms;
+      }
+    });
+
+    this._appendTextNode("responseHeadersInfo",
+      this._format("durationMS", [deltaDuration]));
+
+    this._displayNode("responseContainer");
+    this._appendList("responseHeadersContent", response.headers, true);
+
+    if (response.cookies.length > 0) {
+      this._displayNode("responseCookie");
+      this._appendList("responseCookieContent", response.cookies);
+    }
+  },
+
+  /**
+   * Displays the respones image section, sets the source of the image displayed
+   * in the image response section to the request URL and the duration between
+   * the receiving of the response header and the end of the request. Once the
+   * image is loaded, the size of the requested image is set.
+   *
+   * @returns void
+   */
+  _displayResponseImage: function NP__displayResponseImage()
+  {
+    let self = this;
+    let timing = this.httpActivity.timings;
+    let request = this.httpActivity.request;
+    let response = this.httpActivity.response;
+    let cached = "";
+
+    if (this._isResponseCached) {
+      cached = "Cached";
+    }
+
+    let imageNode = this.document.getElementById("responseImage" +
+                                                 cached + "Node");
+
+    let text = response.content.text;
+    if (typeof text == "object" && text.type == "longString") {
+      this._showResponseBodyFetchLink();
+    }
+    else {
+      imageNode.setAttribute("src",
+        "data:" + this.contentType + ";base64," + text);
+    }
+
+    // This function is called to set the imageInfo.
+    function setImageInfo() {
+      self._appendTextNode("responseImage" + cached + "Info",
+        self._format("imageSizeDeltaDurationMS",
+          [ imageNode.width, imageNode.height, timing.receive ]
+        )
+      );
+    }
+
+    // Check if the image is already loaded.
+    if (imageNode.width != 0) {
+      setImageInfo();
+    }
+    else {
+      // Image is not loaded yet therefore add a load event.
+      imageNode.addEventListener("load", function imageNodeLoad() {
+        imageNode.removeEventListener("load", imageNodeLoad, false);
+        setImageInfo();
+      }, false);
+    }
+
+    this._displayNode("responseImage" + cached);
+  },
+
+  /**
+   * Displays the response body section, sets the the duration between
+   * the receiving of the response header and the end of the request as well as
+   * the content of the response body on the NetworkPanel.
+   *
+   * @returns void
+   */
+  _displayResponseBody: function NP__displayResponseBody()
+  {
+    let timing = this.httpActivity.timings;
+    let response = this.httpActivity.response;
+    let cached =  this._isResponseCached ? "Cached" : "";
+
+    this._appendTextNode("responseBody" + cached + "Info",
+      this._format("durationMS", [timing.receive]));
+
+    this._displayNode("responseBody" + cached);
+
+    let text = response.content.text;
+    if (typeof text == "object") {
+      text = text.initial;
+      this._showResponseBodyFetchLink();
+    }
+
+    this._appendTextNode("responseBody" + cached + "Content", text);
+  },
+
+  /**
+   * Show the "fetch response body" link.
+   * @private
+   */
+  _showResponseBodyFetchLink: function NP__showResponseBodyFetchLink()
+  {
+    let content = this.httpActivity.response.content;
+
+    let elem = this._appendTextNode("responseBodyFetchLink",
+      this._format("fetchRemainingResponseContentLink",
+                   [content.text.length - content.text.initial.length]));
+
+    elem.style.display = "block";
+    elem.addEventListener("mousedown", this._responseBodyFetch);
+  },
+
+  /**
+   * Click event handler for the link that allows users to fetch the remaining
+   * response body.
+   *
+   * @private
+   * @param nsIDOMEvent aEvent
+   */
+  _responseBodyFetch: function NP__responseBodyFetch(aEvent)
+  {
+    aEvent.target.style.display = "none";
+    aEvent.target.removeEventListener("mousedown", this._responseBodyFetch);
+
+    let content = this.httpActivity.response.content;
+    let longString = this.webconsole.webConsoleClient.longString(content.text);
+    longString.substring(longString.initial.length, longString.length,
+      (aResponse) =>
+      {
+        if (aResponse.error) {
+          Cu.reportError("NP__onLongStringSubstring error: " + aResponse.error);
+          return;
+        }
+
+        content.text = content.text.initial + aResponse.substring;
+        let cached =  this._isResponseCached ? "Cached" : "";
+
+        if (this._responseIsImage) {
+          let imageNode = this.document.getElementById("responseImage" +
+                                                       cached + "Node");
+          imageNode.src =
+            "data:" + this.contentType + ";base64," + content.text;
+        }
+        else {
+          this._appendTextNode("responseBody" + cached + "Content",
+                               aResponse.substring);
+        }
+      });
+  },
+
+  /**
+   * Displays the `Unknown Content-Type hint` and sets the duration between the
+   * receiving of the response header on the NetworkPanel.
+   *
+   * @returns void
+   */
+  _displayResponseBodyUnknownType: function NP__displayResponseBodyUnknownType()
+  {
+    let timing = this.httpActivity.timings;
+
+    this._displayNode("responseBodyUnknownType");
+    this._appendTextNode("responseBodyUnknownTypeInfo",
+      this._format("durationMS", [timing.receive]));
+
+    this._appendTextNode("responseBodyUnknownTypeContent",
+      this._format("responseBodyUnableToDisplay.content", [this.contentType]));
+  },
+
+  /**
+   * Displays the `no response body` section and sets the the duration between
+   * the receiving of the response header and the end of the request.
+   *
+   * @returns void
+   */
+  _displayNoResponseBody: function NP_displayNoResponseBody()
+  {
+    let timing = this.httpActivity.timings;
+
+    this._displayNode("responseNoBody");
+    this._appendTextNode("responseNoBodyInfo",
+      this._format("durationMS", [timing.receive]));
+  },
+
+  /**
+   * Updates the content of the NetworkPanel's iframe.
+   *
+   * @returns void
+   */
+  update: function NP_update()
+  {
+    if (!this.document || this.document.readyState != "complete") {
+      return;
+    }
+
+    let updates = this.httpActivity.updates;
+    let timing = this.httpActivity.timings;
+    let request = this.httpActivity.request;
+    let response = this.httpActivity.response;
+
+    switch (this._state) {
+      case this._INIT:
+        this._displayRequestHeader();
+        this._state = this._DISPLAYED_REQUEST_HEADER;
+        // FALL THROUGH
+
+      case this._DISPLAYED_REQUEST_HEADER:
+        // Process the request body if there is one.
+        if (!this.httpActivity.discardRequestBody && request.postData.text) {
+          this._updateRequestBody();
+          this._state = this._DISPLAYED_REQUEST_BODY;
+        }
+        // FALL THROUGH
+
+      case this._DISPLAYED_REQUEST_BODY:
+        if (!response.headers.length || !Object.keys(timing).length) {
+          break;
+        }
+        this._displayResponseHeader();
+        this._state = this._DISPLAYED_RESPONSE_HEADER;
+        // FALL THROUGH
+
+      case this._DISPLAYED_RESPONSE_HEADER:
+        if (updates.indexOf("responseContent") == -1 ||
+            updates.indexOf("eventTimings") == -1) {
+          break;
+        }
+
+        this._state = this._TRANSITION_CLOSED;
+        if (this.httpActivity.discardResponseBody) {
+          break;
+        }
+
+        if (!response.content || !response.content.text) {
+          this._displayNoResponseBody();
+        }
+        else if (this._responseIsImage) {
+          this._displayResponseImage();
+        }
+        else if (!this._isResponseBodyTextData) {
+          this._displayResponseBodyUnknownType();
+        }
+        else if (response.content.text) {
+          this._displayResponseBody();
+        }
+        break;
+    }
+
+    if (this._onUpdate) {
+      this._onUpdate();
+    }
+  },
+
+  /**
+   * Update the panel to hold the current information we have about the request
+   * body.
+   * @private
+   */
+  _updateRequestBody: function NP__updateRequestBody()
+  {
+    let postData = this.httpActivity.request.postData;
+    if (typeof postData.text == "object" && postData.text.type == "longString") {
+      let elem = this._appendTextNode("requestBodyFetchLink",
+        this._format("fetchRemainingRequestContentLink",
+                     [postData.text.length - postData.text.initial.length]));
+
+      elem.style.display = "block";
+      elem.addEventListener("mousedown", this._requestBodyFetch);
+      return;
+    }
+
+    // Check if we send some form data. If so, display the form data special.
+    if (this._isRequestBodyFormData) {
+      this._displayRequestForm();
+    }
+    else {
+      this._displayRequestBody();
+    }
+  },
+
+  /**
+   * Click event handler for the link that allows users to fetch the remaining
+   * request body.
+   *
+   * @private
+   * @param nsIDOMEvent aEvent
+   */
+  _requestBodyFetch: function NP__requestBodyFetch(aEvent)
+  {
+    aEvent.target.style.display = "none";
+    aEvent.target.removeEventListener("mousedown", this._responseBodyFetch);
+
+    let postData = this.httpActivity.request.postData;
+    let longString = this.webconsole.webConsoleClient.longString(postData.text);
+    longString.substring(longString.initial.length, longString.length,
+       (aResponse) =>
+      {
+        if (aResponse.error) {
+          Cu.reportError("NP__onLongStringSubstring error: " + aResponse.error);
+          return;
+        }
+
+        postData.text = postData.text.initial + aResponse.substring;
+        this._updateRequestBody();
+      });
+  },
+};
+
+/**
+ * Creates a DOMNode and sets all the attributes of aAttributes on the created
+ * element.
+ *
+ * @param nsIDOMDocument aDocument
+ *        Document to create the new DOMNode.
+ * @param string aTag
+ *        Name of the tag for the DOMNode.
+ * @param object aAttributes
+ *        Attributes set on the created DOMNode.
+ *
+ * @returns nsIDOMNode
+ */
+function createElement(aDocument, aTag, aAttributes)
+{
+  let node = aDocument.createElement(aTag);
+  if (aAttributes) {
+    for (let attr in aAttributes) {
+      node.setAttribute(attr, aAttributes[attr]);
+    }
+  }
+  return node;
+}
+
+/**
+ * Creates a new DOMNode and appends it to aParent.
+ *
+ * @param nsIDOMNode aParent
+ *        A parent node to append the created element.
+ * @param string aTag
+ *        Name of the tag for the DOMNode.
+ * @param object aAttributes
+ *        Attributes set on the created DOMNode.
+ *
+ * @returns nsIDOMNode
+ */
+function createAndAppendElement(aParent, aTag, aAttributes)
+{
+  let node = createElement(aParent.ownerDocument, aTag, aAttributes);
+  aParent.appendChild(node);
+  return node;
+}
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -184,16 +184,18 @@ skip-if = buildapp == 'mulet'
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_console_variables_view_while_debugging_and_inspecting.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_eval_in_debugger_stackframe.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_eval_in_debugger_stackframe2.js]
 [browser_jsterm_inspect.js]
 [browser_longstring_hang.js]
+[browser_netpanel_longstring_expand.js]
+skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_output_breaks_after_console_dir_uninspectable.js]
 [browser_output_longstring_expand.js]
 [browser_repeated_messages_accuracy.js]
 skip-if = buildapp == 'mulet'
 [browser_result_format_as_string.js]
 [browser_warn_user_about_replaced_api.js]
 [browser_webconsole_abbreviate_source_url.js]
 [browser_webconsole_allow_mixedcontent_securityerrors.js]
@@ -220,16 +222,17 @@ skip-if = e10s # Bug 1042253 - webconsol
 [browser_webconsole_bug_588342_document_focus.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_webconsole_bug_588730_text_node_insertion.js]
 [browser_webconsole_bug_588967_input_expansion.js]
 [browser_webconsole_bug_589162_css_filter.js]
 [browser_webconsole_bug_592442_closing_brackets.js]
 [browser_webconsole_bug_593003_iframe_wrong_hud.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+[browser_webconsole_bug_594477_clickable_output.js]
 [browser_webconsole_bug_594497_history_arrow_keys.js]
 [browser_webconsole_bug_595223_file_uri.js]
 [browser_webconsole_bug_595350_multiple_windows_and_tabs.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_webconsole_bug_595934_message_categories.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
@@ -237,24 +240,27 @@ skip-if = e10s # Bug 1042253 - webconsol
 [browser_webconsole_bug_597136_network_requests_from_chrome.js]
 [browser_webconsole_bug_597460_filter_scroll.js]
 [browser_webconsole_bug_597756_reopen_closed_tab.js]
 [browser_webconsole_bug_599725_response_headers.js]
 [browser_webconsole_bug_600183_charset.js]
 [browser_webconsole_bug_601177_log_levels.js]
 [browser_webconsole_bug_601352_scroll.js]
 [browser_webconsole_bug_601667_filter_buttons.js]
+[browser_webconsole_bug_602572_log_bodies_checkbox.js]
+skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_webconsole_bug_603750_websocket.js]
 [browser_webconsole_bug_611795.js]
 [browser_webconsole_bug_613013_console_api_iframe.js]
 [browser_webconsole_bug_613280_jsterm_copy.js]
 [browser_webconsole_bug_613642_maintain_scroll.js]
 [browser_webconsole_bug_613642_prune_scroll.js]
 [browser_webconsole_bug_614793_jsterm_scroll.js]
 [browser_webconsole_bug_618078_network_exceptions.js]
+[browser_webconsole_bug_618311_close_panels.js]
 [browser_webconsole_bug_621644_jsterm_dollar.js]
 [browser_webconsole_bug_622303_persistent_filters.js]
 [browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js]
 skip-if = os != "win"
 [browser_webconsole_bug_630733_response_redirect_headers.js]
 [browser_webconsole_bug_632275_getters_document_width.js]
 [browser_webconsole_bug_632347_iterators_generators.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
@@ -323,16 +329,17 @@ skip-if = buildapp == 'mulet' || e10s # 
 [browser_webconsole_inspect-parsed-documents.js]
 [browser_webconsole_js_input_expansion.js]
 [browser_webconsole_jsterm.js]
 skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
 [browser_webconsole_live_filtering_of_message_types.js]
 [browser_webconsole_live_filtering_on_search_strings.js]
 [browser_webconsole_message_node_id.js]
 [browser_webconsole_netlogging.js]
+[browser_webconsole_network_panel.js]
 [browser_webconsole_notifications.js]
 [browser_webconsole_open-links-without-callback.js]
 [browser_webconsole_promise.js]
 [browser_webconsole_output_copy_newlines.js]
 [browser_webconsole_output_order.js]
 [browser_webconsole_property_provider.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_webconsole_scratchpad_panel_link.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_netpanel_longstring_expand.js
@@ -0,0 +1,312 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Tests that the network panel works with LongStringActors.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/" +
+                 "test/test-console.html";
+const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/" +
+                 "test/test-image.png";
+
+const TEST_IMG_BASE64 =
+  "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAVRJ" +
+  "REFUOI2lk7FLw0AUxr+YpC1CBqcMWfsvCCLdXFzqEJCgDl1EQRGxg9AhSBEJONhFhG52UCuF" +
+  "Djq5dxD8FwoO0qGDOBQkl7vLOeWa2EQDffDBvTu+373Hu1OEEJgntGgxGD6J+7fLXKbt5VNU" +
+  "yhsKAChRBQcPFVFeWskFGH694mZroCQqCLlAwPxcgJBP254CmAD5B7C7dgHLMLF3uzoL4DQE" +
+  "od+Z5sP1FizDxGgyBqfhLID9AahX29J89bwPFgMsSEAQglAf9WobhPpScbPXr4FQHyzIADTs" +
+  "DizDRMPuIOC+zEeTMZo9BwH3EfAMACccbtfGaDKGZZg423yUZrdrg3EqxQlPr0BTdTR7joRE" +
+  "N2uqnlBmCwW1hIJagtev4f3zA16/JvfiigMSYyzqJXlw/XKUyOORMUaBor6YavgdjKa8xGOn" +
+  "idadmwtwsnMu18q83/kHSou+bFNDDr4AAAAASUVORK5CYII=";
+
+let testDriver;
+
+function test() {
+  loadTab(TEST_URI).then(() => {
+    openConsole().then(testNetworkPanel);
+  });
+}
+
+function testNetworkPanel() {
+  testDriver = testGen();
+  testDriver.next();
+}
+
+function checkIsVisible(aPanel, aList) {
+  for (let id in aList) {
+    let node = aPanel.document.getElementById(id);
+    let isVisible = aList[id];
+    is(node.style.display, (isVisible ? "block" : "none"),
+       id + " isVisible=" + isVisible);
+  }
+}
+
+function checkNodeContent(aPanel, aId, aContent) {
+  let node = aPanel.document.getElementById(aId);
+  if (node == null) {
+    ok(false, "Tried to access node " + aId + " that doesn't exist!");
+  } else if (node.textContent.indexOf(aContent) != -1) {
+    ok(true, "checking content of " + aId);
+  } else {
+    ok(false, "Got false value for " + aId + ": " + node.textContent +
+       " doesn't have " + aContent);
+  }
+}
+
+function checkNodeKeyValue(aPanel, aId, aKey, aValue) {
+  let node = aPanel.document.getElementById(aId);
+
+  let headers = node.querySelectorAll("th");
+  for (let i = 0; i < headers.length; i++) {
+    if (headers[i].textContent == (aKey + ":")) {
+      is(headers[i].nextElementSibling.textContent, aValue,
+         "checking content of " + aId + " for key " + aKey);
+      return;
+    }
+  }
+
+  ok(false, "content check failed for " + aId + ", key " + aKey);
+}
+
+function* testGen() {
+  let hud = HUDService.getHudByWindow(content);
+  let filterBox = hud.ui.filterBox;
+
+  let headerValue = (new Array(456)).join("fooz bar");
+  let headerValueGrip = {
+    type: "longString",
+    initial: headerValue.substr(0, 123),
+    length: headerValue.length,
+    actor: "faktor",
+    _fullString: headerValue,
+  };
+
+  let imageContentGrip = {
+    type: "longString",
+    initial: TEST_IMG_BASE64.substr(0, 143),
+    length: TEST_IMG_BASE64.length,
+    actor: "faktor2",
+    _fullString: TEST_IMG_BASE64,
+  };
+
+  let postDataValue = (new Array(123)).join("post me");
+  let postDataGrip = {
+    type: "longString",
+    initial: postDataValue.substr(0, 172),
+    length: postDataValue.length,
+    actor: "faktor3",
+    _fullString: postDataValue,
+  };
+
+  let httpActivity = {
+    updates: ["responseContent", "eventTimings"],
+    discardRequestBody: false,
+    discardResponseBody: false,
+    startedDateTime: (new Date()).toISOString(),
+    request: {
+      url: TEST_IMG,
+      method: "GET",
+      cookies: [],
+      headers: [
+        { name: "foo", value: "bar" },
+        { name: "loongstring", value: headerValueGrip },
+      ],
+      postData: { text: postDataGrip },
+    },
+    response: {
+      httpVersion: "HTTP/3.14",
+      status: 2012,
+      statusText: "ddahl likes tacos :)",
+      headers: [
+        { name: "Content-Type", value: "image/png" },
+      ],
+      content: { mimeType: "image/png", text: imageContentGrip },
+      cookies: [],
+    },
+    timings: { wait: 15, receive: 23 },
+  };
+
+  let networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
+
+  is(filterBox._netPanel, networkPanel,
+     "Network panel stored on the anchor object");
+
+  networkPanel._onUpdate = function() {
+    networkPanel._onUpdate = null;
+    executeSoon(function() {
+      testDriver.next();
+    });
+  };
+
+  yield undefined;
+
+  info("test 1: check if a header value is expandable");
+
+  checkIsVisible(networkPanel, {
+    requestCookie: false,
+    requestFormData: false,
+    requestBody: false,
+    requestBodyFetchLink: true,
+    responseContainer: true,
+    responseBody: false,
+    responseNoBody: false,
+    responseImage: true,
+    responseImageCached: false,
+    responseBodyFetchLink: true,
+  });
+
+  checkNodeKeyValue(networkPanel, "requestHeadersContent", "foo", "bar");
+  checkNodeKeyValue(networkPanel, "requestHeadersContent", "loongstring",
+                    headerValueGrip.initial + "[\u2026]");
+
+  let webConsoleClient = networkPanel.webconsole.webConsoleClient;
+  let longStringFn = webConsoleClient.longString;
+
+  let expectedGrip = headerValueGrip;
+
+  function longStringClientProvider(aLongString) {
+    is(aLongString, expectedGrip,
+       "longString grip is correct");
+
+    return {
+      initial: expectedGrip.initial,
+      length: expectedGrip.length,
+      substring: function(aStart, aEnd, aCallback) {
+        is(aStart, expectedGrip.initial.length,
+           "substring start is correct");
+        is(aEnd, expectedGrip.length,
+           "substring end is correct");
+
+        executeSoon(function() {
+          aCallback({
+            substring: expectedGrip._fullString.substring(aStart, aEnd),
+          });
+
+          executeSoon(function() {
+            testDriver.next();
+          });
+        });
+      },
+    };
+  }
+
+  webConsoleClient.longString = longStringClientProvider;
+
+  let clickable = networkPanel.document
+                  .querySelector("#requestHeadersContent .longStringEllipsis");
+  ok(clickable, "long string ellipsis is shown");
+
+  EventUtils.sendMouseEvent({ type: "mousedown"}, clickable,
+                             networkPanel.document.defaultView);
+
+  yield undefined;
+
+  clickable = networkPanel.document
+              .querySelector("#requestHeadersContent .longStringEllipsis");
+  ok(!clickable, "long string ellipsis is not shown");
+
+  checkNodeKeyValue(networkPanel, "requestHeadersContent", "loongstring",
+                    expectedGrip._fullString);
+
+  info("test 2: check that response body image fetching works");
+  expectedGrip = imageContentGrip;
+
+  let imgNode = networkPanel.document.getElementById("responseImageNode");
+  ok(!imgNode.getAttribute("src"), "no image is displayed");
+
+  clickable = networkPanel.document.querySelector("#responseBodyFetchLink");
+  EventUtils.sendMouseEvent({ type: "mousedown"}, clickable,
+                             networkPanel.document.defaultView);
+
+  yield undefined;
+
+  imgNode = networkPanel.document.getElementById("responseImageNode");
+  is(imgNode.getAttribute("src"), "data:image/png;base64," + TEST_IMG_BASE64,
+     "displayed image is correct");
+  is(clickable.style.display, "none", "#responseBodyFetchLink is not visible");
+
+  info("test 3: expand the request body");
+
+  expectedGrip = postDataGrip;
+
+  clickable = networkPanel.document.querySelector("#requestBodyFetchLink");
+  EventUtils.sendMouseEvent({ type: "mousedown"}, clickable,
+                             networkPanel.document.defaultView);
+  yield undefined;
+
+  is(clickable.style.display, "none", "#requestBodyFetchLink is not visible");
+
+  checkIsVisible(networkPanel, {
+    requestBody: true,
+    requestBodyFetchLink: false,
+  });
+
+  checkNodeContent(networkPanel, "requestBodyContent",
+                   expectedGrip._fullString);
+
+  webConsoleClient.longString = longStringFn;
+
+  networkPanel.panel.hidePopup();
+
+  info("test 4: reponse body long text");
+
+  httpActivity.response.content.mimeType = "text/plain";
+  httpActivity.response.headers[0].value = "text/plain";
+
+  expectedGrip = imageContentGrip;
+
+  // Reset response.content.text to avoid caching of the full string.
+  httpActivity.response.content.text = expectedGrip;
+
+  networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
+  is(filterBox._netPanel, networkPanel,
+     "Network panel stored on httpActivity object");
+
+  networkPanel._onUpdate = function() {
+    networkPanel._onUpdate = null;
+    executeSoon(function() {
+      testDriver.next();
+    });
+  };
+
+  yield undefined;
+
+  checkIsVisible(networkPanel, {
+    requestCookie: false,
+    requestFormData: false,
+    requestBody: true,
+    requestBodyFetchLink: false,
+    responseContainer: true,
+    responseBody: true,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: false,
+    responseBodyFetchLink: true,
+  });
+
+  checkNodeContent(networkPanel, "responseBodyContent", expectedGrip.initial);
+
+  webConsoleClient.longString = longStringClientProvider;
+
+  clickable = networkPanel.document.querySelector("#responseBodyFetchLink");
+  EventUtils.sendMouseEvent({ type: "mousedown"}, clickable,
+                             networkPanel.document.defaultView);
+
+  yield undefined;
+
+  webConsoleClient.longString = longStringFn;
+  is(clickable.style.display, "none", "#responseBodyFetchLink is not visible");
+  checkNodeContent(networkPanel, "responseBodyContent",
+                   expectedGrip._fullString);
+
+  networkPanel.panel.hidePopup();
+
+  // All done!
+  testDriver = null;
+  executeSoon(finishTest);
+
+  yield undefined;
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_594477_clickable_output.js
@@ -0,0 +1,132 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Contributor(s):
+ *  Mihai Șucan <mihai.sucan@gmail.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/" +
+                 "test/test-console.html";
+let HUD;
+let outputItem;
+let outputNode;
+
+let test = asyncTest(function* () {
+  yield loadTab(TEST_URI);
+
+  HUD = yield openConsole();
+  outputNode = HUD.outputNode;
+
+  // reload the tab
+  BrowserReload();
+  yield loadBrowser(gBrowser.selectedBrowser);
+
+  let event = yield clickEvents();
+  yield testClickAgain(event);
+  yield networkPanelHidden();
+
+  HUD = outputItem = outputNode = null;
+});
+
+function clickEvents() {
+  let deferred = promise.defer();
+
+  waitForMessages({
+    webconsole: HUD,
+    messages: [{
+      text: "test-console.html",
+      category: CATEGORY_NETWORK,
+      severity: SEVERITY_LOG,
+    }],
+  }).then(([result]) => {
+    let msg = [...result.matched][0];
+    outputItem = msg.querySelector(".message-body .url");
+    ok(outputItem, "found a network message");
+    document.addEventListener("popupshown", function onPanelShown(event) {
+      document.removeEventListener("popupshown", onPanelShown, false);
+      deferred.resolve(event);
+    }, false);
+
+    // Send the mousedown and click events such that the network panel opens.
+    EventUtils.sendMouseEvent({type: "mousedown"}, outputItem);
+    EventUtils.sendMouseEvent({type: "click"}, outputItem);
+  });
+
+  return deferred.promise;
+}
+
+function testClickAgain(event) {
+  info("testClickAgain");
+
+  let deferred = promise.defer();
+
+  document.addEventListener("popupshown", networkPanelShowFailure, false);
+
+  // The network panel should not open for the second time.
+  EventUtils.sendMouseEvent({type: "mousedown"}, outputItem);
+  EventUtils.sendMouseEvent({type: "click"}, outputItem);
+
+  executeSoon(function() {
+    document.addEventListener("popuphidden", function onHidden() {
+      document.removeEventListener("popuphidden", onHidden, false);
+      deferred.resolve();
+    }, false);
+    event.target.hidePopup();
+  });
+
+  return deferred.promise;
+}
+
+function networkPanelShowFailure() {
+  ok(false, "the network panel should not show");
+}
+
+function networkPanelHidden() {
+  let deferred = promise.defer();
+
+  info("networkPanelHidden");
+
+  // The network panel should not show because this is a mouse event that starts
+  // in a position and ends in another.
+  EventUtils.sendMouseEvent({type: "mousedown", clientX: 3, clientY: 4},
+    outputItem);
+  EventUtils.sendMouseEvent({type: "click", clientX: 5, clientY: 6},
+    outputItem);
+
+  // The network panel should not show because this is a middle-click.
+  EventUtils.sendMouseEvent({type: "mousedown", button: 1},
+    outputItem);
+  EventUtils.sendMouseEvent({type: "click", button: 1},
+    outputItem);
+
+  // The network panel should not show because this is a right-click.
+  EventUtils.sendMouseEvent({type: "mousedown", button: 2},
+    outputItem);
+  EventUtils.sendMouseEvent({type: "click", button: 2},
+    outputItem);
+
+  executeSoon(function() {
+    document.removeEventListener("popupshown", networkPanelShowFailure, false);
+
+    // Done with the network output. Now test the jsterm output and the property
+    // panel.
+    HUD.jsterm.execute("document").then((msg) => {
+      info("jsterm execute 'document' callback");
+
+      HUD.jsterm.once("variablesview-open", deferred.resolve);
+      outputItem = msg.querySelector(".message-body a");
+      ok(outputItem, "jsterm output message found");
+
+      // Send the mousedown and click events such that the property panel opens.
+      EventUtils.sendMouseEvent({type: "mousedown"}, outputItem);
+      EventUtils.sendMouseEvent({type: "click"}, outputItem);
+    });
+  });
+
+  return deferred.promise;
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_602572_log_bodies_checkbox.js
@@ -0,0 +1,186 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Contributor(s):
+ *   Mihai Șucan <mihai.sucan@gmail.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+"use strict";
+
+let menuitems = [];
+let menupopups = [];
+let huds = [];
+let tabs = [];
+let runCount = 0;
+
+const TEST_URI1 = "data:text/html;charset=utf-8,Web Console test for " +
+                  "bug 602572: log bodies checkbox. tab 1";
+const TEST_URI2 = "data:text/html;charset=utf-8,Web Console test for " +
+                  "bug 602572: log bodies checkbox. tab 2";
+
+function test() {
+  if (runCount == 0) {
+    requestLongerTimeout(2);
+  }
+
+  // open tab 2
+  function openTab() {
+    loadTab(TEST_URI2).then((tab) => {
+      tabs.push(tab.tab);
+      openConsole().then((hud) => {
+        hud.iframeWindow.requestAnimationFrame(startTest);
+      });
+    });
+  }
+
+  // open tab 1
+  loadTab(TEST_URI1).then((tab) => {
+    tabs.push(tab.tab);
+    openConsole().then((hud) => {
+      hud.iframeWindow.requestAnimationFrame(() => {
+        info("iframe1 root height " + hud.ui.rootElement.clientHeight);
+
+        openTab();
+      });
+    });
+  });
+}
+
+function startTest() {
+  // Find the relevant elements in the Web Console of tab 2.
+  let win2 = tabs[runCount * 2 + 1].linkedBrowser.contentWindow;
+  huds[1] = HUDService.getHudByWindow(win2);
+  info("startTest: iframe2 root height " + huds[1].ui.rootElement.clientHeight);
+
+  if (runCount == 0) {
+    menuitems[1] = huds[1].ui.rootElement.querySelector("#saveBodies");
+  } else {
+    menuitems[1] = huds[1].ui.rootElement
+                             .querySelector("#saveBodiesContextMenu");
+  }
+  menupopups[1] = menuitems[1].parentNode;
+
+  // Open the context menu from tab 2.
+  menupopups[1].addEventListener("popupshown", onpopupshown2, false);
+  executeSoon(function() {
+    menupopups[1].openPopup();
+  });
+}
+
+function onpopupshown2(evt) {
+  menupopups[1].removeEventListener(evt.type, onpopupshown2, false);
+
+  // By default bodies are not logged.
+  isnot(menuitems[1].getAttribute("checked"), "true",
+        "menuitems[1] is not checked");
+
+  ok(!huds[1].ui._saveRequestAndResponseBodies, "bodies are not logged");
+
+  // Enable body logging.
+  huds[1].ui.setSaveRequestAndResponseBodies(true).then(() => {
+    menupopups[1].hidePopup();
+  });
+
+  menupopups[1].addEventListener("popuphidden", function _onhidden(evtPopup) {
+    menupopups[1].removeEventListener(evtPopup.type, _onhidden, false);
+
+    info("menupopups[1] hidden");
+
+    // Reopen the context menu.
+    huds[1].ui.once("save-bodies-ui-toggled", () => testpopup2b(evtPopup));
+    menupopups[1].openPopup();
+  }, false);
+}
+
+function testpopup2b() {
+  is(menuitems[1].getAttribute("checked"), "true", "menuitems[1] is checked");
+
+  menupopups[1].addEventListener("popuphidden", function _onhidden(evtPopup) {
+    menupopups[1].removeEventListener(evtPopup.type, _onhidden, false);
+
+    info("menupopups[1] hidden");
+
+    // Switch to tab 1 and open the Web Console context menu from there.
+    gBrowser.selectedTab = tabs[runCount * 2];
+    waitForFocus(function() {
+      // Find the relevant elements in the Web Console of tab 1.
+      let win1 = tabs[runCount * 2].linkedBrowser.contentWindow;
+      huds[0] = HUDService.getHudByWindow(win1);
+
+      info("iframe1 root height " + huds[0].ui.rootElement.clientHeight);
+
+      menuitems[0] = huds[0].ui.rootElement.querySelector("#saveBodies");
+      menupopups[0] = huds[0].ui.rootElement.querySelector("menupopup");
+
+      menupopups[0].addEventListener("popupshown", onpopupshown1, false);
+      executeSoon(() => menupopups[0].openPopup());
+    }, tabs[runCount * 2].linkedBrowser.contentWindow);
+  }, false);
+
+  executeSoon(function() {
+    menupopups[1].hidePopup();
+  });
+}
+
+function onpopupshown1(evt) {
+  menupopups[0].removeEventListener(evt.type, onpopupshown1, false);
+
+  // The menuitem checkbox must not be in sync with the other tabs.
+  isnot(menuitems[0].getAttribute("checked"), "true",
+        "menuitems[0] is not checked");
+
+  // Enable body logging for tab 1 as well.
+  huds[0].ui.setSaveRequestAndResponseBodies(true).then(() => {
+    menupopups[0].hidePopup();
+  });
+
+  // Close the menu, and switch back to tab 2.
+  menupopups[0].addEventListener("popuphidden", function _onhidden(evtPopup) {
+    menupopups[0].removeEventListener(evtPopup.type, _onhidden, false);
+
+    info("menupopups[0] hidden");
+
+    gBrowser.selectedTab = tabs[runCount * 2 + 1];
+    waitForFocus(function() {
+      // Reopen the context menu from tab 2.
+      huds[1].ui.once("save-bodies-ui-toggled", () => testpopup2c(evtPopup));
+      menupopups[1].openPopup();
+    }, tabs[runCount * 2 + 1].linkedBrowser.contentWindow);
+  }, false);
+}
+
+function testpopup2c() {
+  is(menuitems[1].getAttribute("checked"), "true", "menuitems[1] is checked");
+
+  menupopups[1].addEventListener("popuphidden", function _onhidden(evtPopup) {
+    menupopups[1].removeEventListener(evtPopup.type, _onhidden, false);
+
+    info("menupopups[1] hidden");
+
+    // Done if on second run
+    closeConsole(gBrowser.selectedTab).then(function() {
+      if (runCount == 0) {
+        runCount++;
+        info("start second run");
+        executeSoon(test);
+      } else {
+        gBrowser.removeCurrentTab();
+        gBrowser.selectedTab = tabs[2];
+        gBrowser.removeCurrentTab();
+        gBrowser.selectedTab = tabs[1];
+        gBrowser.removeCurrentTab();
+        gBrowser.selectedTab = tabs[0];
+        gBrowser.removeCurrentTab();
+        huds = menuitems = menupopups = tabs = null;
+        executeSoon(finishTest);
+      }
+    });
+  }, false);
+
+  executeSoon(function() {
+    menupopups[1].hidePopup();
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_618311_close_panels.js
@@ -0,0 +1,93 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/" +
+                 "test/test-console.html";
+
+let test = asyncTest(function* () {
+  yield loadTab(TEST_URI);
+
+  let hud = yield openConsole();
+
+  BrowserReload();
+
+  let results = yield waitForMessages({
+    webconsole: hud,
+    messages: [{
+      text: "test-console.html",
+      category: CATEGORY_NETWORK,
+      severity: SEVERITY_LOG,
+    }],
+  });
+
+  yield performTest(hud, results);
+});
+
+function performTest(HUD, results) {
+  let deferred = promise.defer();
+
+  let networkMessage = [...results[0].matched][0];
+  ok(networkMessage, "network message element");
+
+  let networkLink = networkMessage.querySelector(".url");
+  ok(networkLink, "found network message link");
+
+  let popupset = document.getElementById("mainPopupSet");
+  ok(popupset, "found #mainPopupSet");
+
+  let popupsShown = 0;
+  let hiddenPopups = 0;
+
+  let onpopupshown = function() {
+    document.removeEventListener("popupshown", onpopupshown, false);
+    popupsShown++;
+
+    executeSoon(function() {
+      let popups = popupset.querySelectorAll("panel[hudId=" + HUD.hudId + "]");
+      is(popups.length, 1, "found one popup");
+
+      document.addEventListener("popuphidden", onpopuphidden, false);
+
+      registerCleanupFunction(function() {
+        is(hiddenPopups, 1, "correct number of popups hidden");
+        if (hiddenPopups != 1) {
+          document.removeEventListener("popuphidden", onpopuphidden, false);
+        }
+      });
+
+      executeSoon(closeConsole);
+    });
+  };
+
+  let onpopuphidden = function() {
+    document.removeEventListener("popuphidden", onpopuphidden, false);
+    hiddenPopups++;
+
+    executeSoon(function() {
+      let popups = popupset.querySelectorAll("panel[hudId=" + HUD.hudId + "]");
+      is(popups.length, 0, "no popups found");
+
+      executeSoon(deferred.resolve);
+    });
+  };
+
+  document.addEventListener("popupshown", onpopupshown, false);
+
+  registerCleanupFunction(function() {
+    is(popupsShown, 1, "correct number of popups shown");
+    if (popupsShown != 1) {
+      document.removeEventListener("popupshown", onpopupshown, false);
+    }
+  });
+
+  EventUtils.sendMouseEvent({ type: "mousedown" }, networkLink,
+                              HUD.iframeWindow);
+  EventUtils.sendMouseEvent({ type: "mouseup" }, networkLink, HUD.iframeWindow);
+  EventUtils.sendMouseEvent({ type: "click" }, networkLink, HUD.iframeWindow);
+
+  return deferred.promise;
+}
--- a/browser/devtools/webconsole/test/browser_webconsole_netlogging.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_netlogging.js
@@ -186,27 +186,25 @@ function testFormSubmission() {
     let form = content.document.querySelector("form");
     form.submit();
   }`);
 }
 
 function testNetworkPanel() {
   // Open the NetworkPanel. The functionality of the NetworkPanel is tested
   // within separate test files.
-  hud.ui.openNetworkPanel(lastRequest.actor).then(() => {
-    let toolbox = gDevTools.getToolbox(hud.target);
-    is(toolbox.currentToolId, "netmonitor", "Network panel was opened");
-    let panel = toolbox.getCurrentPanel();
-    let selected = panel.panelWin.NetMonitorView.RequestsMenu.selectedItem;
-    is(selected.attachment.method, lastRequest.request.method,
-       "The correct request is selected");
-    is(selected.attachment.url, lastRequest.request.url,
-       "The correct request is definitely selected");
+  let networkPanel = hud.ui.openNetworkPanel(hud.ui.filterBox, lastRequest);
+
+  networkPanel.panel.addEventListener("popupshown", function onPopupShown() {
+    networkPanel.panel.removeEventListener("popupshown", onPopupShown, true);
+
+    is(hud.ui.filterBox._netPanel, networkPanel,
+       "Network panel stored on anchor node");
+    ok(true, "NetworkPanel was opened");
 
     // All tests are done. Shutdown.
+    networkPanel.panel.hidePopup();
     lastRequest = null;
     HUDService.lastFinishedRequest.callback = null;
     browser = requestCallback = hud = null;
     executeSoon(finishTest);
-  }).then(null, error => {
-    ok(false, "Got an error: " + error.message + "\n" + error.stack);
-  });
+  }, true);
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_network_panel.js
@@ -0,0 +1,551 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Tests that the network panel works.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/" +
+                 "test/test-console.html";
+const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/" +
+                 "test/test-image.png";
+const TEST_ENCODING_ISO_8859_1 = "http://example.com/browser/browser/" +
+                                 "devtools/webconsole/test/" +
+                                 "test-encoding-ISO-8859-1.html";
+
+const TEST_IMG_BASE64 =
+  "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAVRJ" +
+  "REFUOI2lk7FLw0AUxr+YpC1CBqcMWfsvCCLdXFzqEJCgDl1EQRGxg9AhSBEJONhFhG52UCuF" +
+  "Djq5dxD8FwoO0qGDOBQkl7vLOeWa2EQDffDBvTu+373Hu1OEEJgntGgxGD6J+7fLXKbt5VNU" +
+  "yhsKAChRBQcPFVFeWskFGH694mZroCQqCLlAwPxcgJBP254CmAD5B7C7dgHLMLF3uzoL4DQE" +
+  "od+Z5sP1FizDxGgyBqfhLID9AahX29J89bwPFgMsSEAQglAf9WobhPpScbPXr4FQHyzIADTs" +
+  "DizDRMPuIOC+zEeTMZo9BwH3EfAMACccbtfGaDKGZZg423yUZrdrg3EqxQlPr0BTdTR7joRE" +
+  "N2uqnlBmCwW1hIJagtev4f3zA16/JvfiigMSYyzqJXlw/XKUyOORMUaBor6YavgdjKa8xGOn" +
+  "idadmwtwsnMu18q83/kHSou+bFNDDr4AAAAASUVORK5CYII=";
+
+let testDriver, hud;
+
+function test() {
+  loadTab(TEST_URI).then(() => {
+    openConsole().then(testNetworkPanel);
+  });
+}
+
+function testNetworkPanel(aHud) {
+  hud = aHud;
+  testDriver = testGen();
+  testDriver.next();
+}
+
+function checkIsVisible(aPanel, aList) {
+  for (let id in aList) {
+    let node = aPanel.document.getElementById(id);
+    let isVisible = aList[id];
+    is(node.style.display, (isVisible ? "block" : "none"),
+       id + " isVisible=" + isVisible);
+  }
+}
+
+function checkNodeContent(aPanel, aId, aContent) {
+  let node = aPanel.document.getElementById(aId);
+  if (node == null) {
+    ok(false, "Tried to access node " + aId + " that doesn't exist!");
+  } else if (node.textContent.indexOf(aContent) != -1) {
+    ok(true, "checking content of " + aId);
+  } else {
+    ok(false, "Got false value for " + aId + ": " + node.textContent +
+              " doesn't have " + aContent);
+  }
+}
+
+function checkNodeKeyValue(aPanel, aId, aKey, aValue) {
+  let node = aPanel.document.getElementById(aId);
+
+  let headers = node.querySelectorAll("th");
+  for (let i = 0; i < headers.length; i++) {
+    if (headers[i].textContent == (aKey + ":")) {
+      is(headers[i].nextElementSibling.textContent, aValue,
+         "checking content of " + aId + " for key " + aKey);
+      return;
+    }
+  }
+
+  ok(false, "content check failed for " + aId + ", key " + aKey);
+}
+
+function* testGen() {
+  let filterBox = hud.ui.filterBox;
+
+  let httpActivity = {
+    updates: [],
+    discardRequestBody: true,
+    discardResponseBody: true,
+    startedDateTime: (new Date()).toISOString(),
+    request: {
+      url: "http://www.testpage.com",
+      method: "GET",
+      cookies: [],
+      headers: [
+        { name: "foo", value: "bar" },
+      ],
+    },
+    response: {
+      headers: [],
+      content: {},
+      cookies: [],
+    },
+    timings: {},
+  };
+
+  let networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
+
+  is(filterBox._netPanel, networkPanel,
+     "Network panel stored on the anchor object");
+
+  networkPanel._onUpdate = function() {
+    networkPanel._onUpdate = null;
+    executeSoon(function() {
+      testDriver.next();
+    });
+  };
+
+  yield undefined;
+
+  info("test 1");
+
+  checkIsVisible(networkPanel, {
+    requestCookie: false,
+    requestFormData: false,
+    requestBody: false,
+    responseContainer: false,
+    responseBody: false,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: false
+  });
+
+  checkNodeContent(networkPanel, "header", "http://www.testpage.com");
+  checkNodeContent(networkPanel, "header", "GET");
+  checkNodeKeyValue(networkPanel, "requestHeadersContent", "foo", "bar");
+
+  // Test request body.
+  info("test 2: request body");
+  httpActivity.discardRequestBody = false;
+  httpActivity.request.postData = { text: "hello world" };
+  networkPanel.update();
+
+  checkIsVisible(networkPanel, {
+    requestBody: true,
+    requestFormData: false,
+    requestCookie: false,
+    responseContainer: false,
+    responseBody: false,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: false
+  });
+  checkNodeContent(networkPanel, "requestBodyContent", "hello world");
+
+  // Test response header.
+  info("test 3: response header");
+  httpActivity.timings.wait = 10;
+  httpActivity.response.httpVersion = "HTTP/3.14";
+  httpActivity.response.status = 999;
+  httpActivity.response.statusText = "earthquake win";
+  httpActivity.response.content.mimeType = "text/html";
+  httpActivity.response.headers.push(
+    { name: "Content-Type", value: "text/html" },
+    { name: "leaveHouses", value: "true" }
+  );
+
+  networkPanel.update();
+
+  checkIsVisible(networkPanel, {
+    requestBody: true,
+    requestFormData: false,
+    requestCookie: false,
+    responseContainer: true,
+    responseBody: false,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: false
+  });
+
+  checkNodeContent(networkPanel, "header", "HTTP/3.14 999 earthquake win");
+  checkNodeKeyValue(networkPanel, "responseHeadersContent", "leaveHouses",
+                    "true");
+  checkNodeContent(networkPanel, "responseHeadersInfo", "10ms");
+
+  info("test 4");
+
+  httpActivity.discardResponseBody = false;
+  httpActivity.timings.receive = 2;
+  networkPanel.update();
+
+  checkIsVisible(networkPanel, {
+    requestBody: true,
+    requestCookie: false,
+    requestFormData: false,
+    responseContainer: true,
+    responseBody: false,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: false
+  });
+
+  info("test 5");
+
+  httpActivity.updates.push("responseContent", "eventTimings");
+  networkPanel.update();
+
+  checkNodeContent(networkPanel, "responseNoBodyInfo", "2ms");
+  checkIsVisible(networkPanel, {
+    requestBody: true,
+    requestCookie: false,
+    responseContainer: true,
+    responseBody: false,
+    responseNoBody: true,
+    responseImage: false,
+    responseImageCached: false
+  });
+
+  networkPanel.panel.hidePopup();
+
+  // Second run: Test for cookies and response body.
+  info("test 6: cookies and response body");
+  httpActivity.request.cookies.push(
+    { name: "foo", value: "bar" },
+    { name: "hello", value: "world" }
+  );
+  httpActivity.response.content.text = "get out here";
+
+  networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
+  is(filterBox._netPanel, networkPanel,
+     "Network panel stored on httpActivity object");
+
+  networkPanel._onUpdate = function() {
+    networkPanel._onUpdate = null;
+    executeSoon(function() {
+      testDriver.next();
+    });
+  };
+
+  yield undefined;
+
+  checkIsVisible(networkPanel, {
+    requestBody: true,
+    requestFormData: false,
+    requestCookie: true,
+    responseContainer: true,
+    responseCookie: false,
+    responseBody: true,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: false
+  });
+
+  checkNodeKeyValue(networkPanel, "requestCookieContent", "foo", "bar");
+  checkNodeKeyValue(networkPanel, "requestCookieContent", "hello", "world");
+  checkNodeContent(networkPanel, "responseBodyContent", "get out here");
+  checkNodeContent(networkPanel, "responseBodyInfo", "2ms");
+
+  networkPanel.panel.hidePopup();
+
+  // Third run: Test for response cookies.
+  info("test 6b: response cookies");
+  httpActivity.response.cookies.push(
+    { name: "foobar", value: "boom" },
+    { name: "foobaz", value: "omg" }
+  );
+
+  networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
+  is(filterBox._netPanel, networkPanel,
+     "Network panel stored on httpActivity object");
+
+  networkPanel._onUpdate = function() {
+    networkPanel._onUpdate = null;
+    executeSoon(function() {
+      testDriver.next();
+    });
+  };
+
+  yield undefined;
+
+  checkIsVisible(networkPanel, {
+    requestBody: true,
+    requestFormData: false,
+    requestCookie: true,
+    responseContainer: true,
+    responseCookie: true,
+    responseBody: true,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: false,
+    responseBodyFetchLink: false,
+  });
+
+  checkNodeKeyValue(networkPanel, "responseCookieContent", "foobar", "boom");
+  checkNodeKeyValue(networkPanel, "responseCookieContent", "foobaz", "omg");
+
+  networkPanel.panel.hidePopup();
+
+  // Check image request.
+  info("test 7: image request");
+  httpActivity.response.headers[1].value = "image/png";
+  httpActivity.response.content.mimeType = "image/png";
+  httpActivity.response.content.text = TEST_IMG_BASE64;
+  httpActivity.request.url = TEST_IMG;
+
+  networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
+  networkPanel._onUpdate = function() {
+    networkPanel._onUpdate = null;
+    executeSoon(function() {
+      testDriver.next();
+    });
+  };
+
+  yield undefined;
+
+  checkIsVisible(networkPanel, {
+    requestBody: true,
+    requestFormData: false,
+    requestCookie: true,
+    responseContainer: true,
+    responseBody: false,
+    responseNoBody: false,
+    responseImage: true,
+    responseImageCached: false,
+    responseBodyFetchLink: false,
+  });
+
+  let imgNode = networkPanel.document.getElementById("responseImageNode");
+  is(imgNode.getAttribute("src"), "data:image/png;base64," + TEST_IMG_BASE64,
+      "Displayed image is correct");
+
+  function checkImageResponseInfo() {
+    checkNodeContent(networkPanel, "responseImageInfo", "2ms");
+    checkNodeContent(networkPanel, "responseImageInfo", "16x16px");
+  }
+
+  // Check if the image is loaded already.
+  imgNode.addEventListener("load", function onLoad() {
+    imgNode.removeEventListener("load", onLoad, false);
+    checkImageResponseInfo();
+    networkPanel.panel.hidePopup();
+    testDriver.next();
+  }, false);
+  yield undefined;
+
+  // Check cached image request.
+  info("test 8: cached image request");
+  httpActivity.response.httpVersion = "HTTP/1.1";
+  httpActivity.response.status = 304;
+  httpActivity.response.statusText = "Not Modified";
+
+  networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
+  networkPanel._onUpdate = function() {
+    networkPanel._onUpdate = null;
+    executeSoon(function() {
+      testDriver.next();
+    });
+  };
+
+  yield undefined;
+
+  checkIsVisible(networkPanel, {
+    requestBody: true,
+    requestFormData: false,
+    requestCookie: true,
+    responseContainer: true,
+    responseBody: false,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: true
+  });
+
+  imgNode = networkPanel.document.getElementById("responseImageCachedNode");
+  is(imgNode.getAttribute("src"), "data:image/png;base64," + TEST_IMG_BASE64,
+     "Displayed image is correct");
+
+  networkPanel.panel.hidePopup();
+
+  // Test sent form data.
+  info("test 9: sent form data");
+  httpActivity.request.postData.text = [
+    "Content-Type:      application/x-www-form-urlencoded",
+    "Content-Length: 59",
+    "name=rob&age=20"
+  ].join("\n");
+
+  networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
+  networkPanel._onUpdate = function() {
+    networkPanel._onUpdate = null;
+    executeSoon(function() {
+      testDriver.next();
+    });
+  };
+
+  yield undefined;
+
+  checkIsVisible(networkPanel, {
+    requestBody: false,
+    requestFormData: true,
+    requestCookie: true,
+    responseContainer: true,
+    responseBody: false,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: true
+  });
+
+  checkNodeKeyValue(networkPanel, "requestFormDataContent", "name", "rob");
+  checkNodeKeyValue(networkPanel, "requestFormDataContent", "age", "20");
+  networkPanel.panel.hidePopup();
+
+  // Test no space after Content-Type:
+  info("test 10: no space after Content-Type header in post data");
+  httpActivity.request.postData.text = "Content-Type:application/x-www-" +
+                                       "form-urlencoded\n";
+
+  networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
+  networkPanel._onUpdate = function() {
+    networkPanel._onUpdate = null;
+    executeSoon(function() {
+      testDriver.next();
+    });
+  };
+
+  yield undefined;
+
+  checkIsVisible(networkPanel, {
+    requestBody: false,
+    requestFormData: true,
+    requestCookie: true,
+    responseContainer: true,
+    responseBody: false,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: true
+  });
+
+  networkPanel.panel.hidePopup();
+
+  // Test cached data.
+
+  info("test 11: cached data");
+
+  httpActivity.request.url = TEST_ENCODING_ISO_8859_1;
+  httpActivity.response.headers[1].value = "application/json";
+  httpActivity.response.content.mimeType = "application/json";
+  httpActivity.response.content.text = "my cached data is here!";
+
+  networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
+  networkPanel._onUpdate = function() {
+    networkPanel._onUpdate = null;
+    executeSoon(function() {
+      testDriver.next();
+    });
+  };
+
+  yield undefined;
+
+  checkIsVisible(networkPanel, {
+    requestBody: false,
+    requestFormData: true,
+    requestCookie: true,
+    responseContainer: true,
+    responseBody: false,
+    responseBodyCached: true,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: false
+  });
+
+  checkNodeContent(networkPanel, "responseBodyCachedContent",
+                   "my cached data is here!");
+
+  networkPanel.panel.hidePopup();
+
+  // Test a response with a content type that can't be displayed in the
+  // NetworkPanel.
+  info("test 12: unknown content type");
+  httpActivity.response.headers[1].value = "application/x-shockwave-flash";
+  httpActivity.response.content.mimeType = "application/x-shockwave-flash";
+
+  networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
+  networkPanel._onUpdate = function() {
+    networkPanel._onUpdate = null;
+    executeSoon(function() {
+      testDriver.next();
+    });
+  };
+
+  yield undefined;
+
+  checkIsVisible(networkPanel, {
+    requestBody: false,
+    requestFormData: true,
+    requestCookie: true,
+    responseContainer: true,
+    responseBody: false,
+    responseBodyCached: false,
+    responseBodyUnknownType: true,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: false
+  });
+
+  let responseString =
+    WCUL10n.getFormatStr("NetworkPanel.responseBodyUnableToDisplay.content",
+                      ["application/x-shockwave-flash"]);
+  checkNodeContent(networkPanel,
+                   "responseBodyUnknownTypeContent",
+                   responseString);
+  networkPanel.panel.hidePopup();
+
+  /*
+
+  // This test disabled. See bug 603620.
+
+  // Test if the NetworkPanel figures out the content type based on an URL as
+  // well.
+  delete httpActivity.response.header["Content-Type"];
+  httpActivity.url = "http://www.test.com/someCrazyFile.swf?done=right&ending=txt";
+
+  networkPanel = hud.ui.openNetworkPanel(filterBox, httpActivity);
+  networkPanel.isDoneCallback = function NP_doneCallback() {
+    networkPanel.isDoneCallback = null;
+    testDriver.next();
+  }
+
+  yield undefined;
+
+  checkIsVisible(networkPanel, {
+    requestBody: false,
+    requestFormData: true,
+    requestCookie: true,
+    responseContainer: true,
+    responseBody: false,
+    responseBodyCached: false,
+    responseBodyUnknownType: true,
+    responseNoBody: false,
+    responseImage: false,
+    responseImageCached: false
+  });
+
+  // Systems without Flash installed will return an empty string here. Ignore.
+  if (networkPanel.document.getElementById("responseBodyUnknownTypeContent").textContent !== "")
+    checkNodeContent(networkPanel, "responseBodyUnknownTypeContent", responseString);
+  else
+    ok(true, "Flash not installed");
+
+  networkPanel.panel.hidePopup(); */
+
+  // All done!
+  testDriver = hud = null;
+  executeSoon(finishTest);
+
+  yield undefined;
+}
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -15,16 +15,18 @@ loader.lazyServiceGetter(this, "clipboar
                          "@mozilla.org/widget/clipboardhelper;1",
                          "nsIClipboardHelper");
 loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
 loader.lazyGetter(this, "EventEmitter", () => require("devtools/toolkit/event-emitter"));
 loader.lazyGetter(this, "AutocompletePopup",
                   () => require("devtools/shared/autocomplete-popup").AutocompletePopup);
 loader.lazyGetter(this, "ToolSidebar",
                   () => require("devtools/framework/sidebar").ToolSidebar);
+loader.lazyGetter(this, "NetworkPanel",
+                  () => require("devtools/webconsole/network-panel").NetworkPanel);
 loader.lazyGetter(this, "ConsoleOutput",
                   () => require("devtools/webconsole/console-output").ConsoleOutput);
 loader.lazyGetter(this, "Messages",
                   () => require("devtools/webconsole/console-output").Messages);
 loader.lazyGetter(this, "asyncStorage",
                   () => require("devtools/toolkit/shared/async-storage"));
 loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/toolkit/client/main", true);
 loader.lazyRequireGetter(this, "ObjectClient", "devtools/toolkit/client/main", true);
@@ -1666,17 +1668,21 @@ WebConsoleFrame.prototype = {
       messageNode.classList.add("mixed-content");
       this.makeMixedContentNode(body);
     }
 
     let statusNode = this.document.createElementNS(XHTML_NS, "a");
     statusNode.className = "status";
     body.appendChild(statusNode);
 
-    let onClick = () => this.openNetworkPanel(networkInfo.actor);
+    let onClick = () => {
+      if (!messageNode._panelOpen) {
+        this.openNetworkPanel(messageNode, networkInfo);
+      }
+    };
 
     this._addMessageLinkCallback(urlNode, onClick);
     this._addMessageLinkCallback(statusNode, onClick);
 
     networkInfo.node = messageNode;
 
     this._updateNetMessage(actorId);
 
@@ -1958,27 +1964,144 @@ WebConsoleFrame.prototype = {
     if (messageNode._netPanel) {
       messageNode._netPanel.update();
     }
 
     return updated;
   },
 
   /**
-   * Opens the network monitor and highlights the specified request.
+   * Opens a NetworkPanel.
    *
-   * @param string requestId
-   *        The actor ID of the network request.
+   * @param nsIDOMNode aNode
+   *        The message node you want the panel to be anchored to.
+   * @param object aHttpActivity
+   *        The HTTP activity object that holds network request and response
+   *        information. This object is given to the NetworkPanel constructor.
+   * @return object
+   *         The new NetworkPanel instance.
    */
-  openNetworkPanel: function WCF_openNetworkPanel(requestId)
+  openNetworkPanel: function WCF_openNetworkPanel(aNode, aHttpActivity)
   {
-    let toolbox = gDevTools.getToolbox(this.owner.target);
-    return toolbox.selectTool("netmonitor").then(panel => {
-      return panel.panelWin.NetMonitorController.inspectRequest(requestId);
-    });
+    let actor = aHttpActivity.actor;
+
+    if (actor) {
+      this.webConsoleClient.getRequestHeaders(actor, (aResponse) => {
+        if (aResponse.error) {
+          Cu.reportError("WCF_openNetworkPanel getRequestHeaders:" +
+                         aResponse.error);
+          return;
+        }
+
+        aHttpActivity.request.headers = aResponse.headers;
+
+        this.webConsoleClient.getRequestCookies(actor, onRequestCookies);
+      });
+    }
+
+    let onRequestCookies = (aResponse) => {
+      if (aResponse.error) {
+        Cu.reportError("WCF_openNetworkPanel getRequestCookies:" +
+                       aResponse.error);
+        return;
+      }
+
+      aHttpActivity.request.cookies = aResponse.cookies;
+
+      this.webConsoleClient.getResponseHeaders(actor, onResponseHeaders);
+    };
+
+    let onResponseHeaders = (aResponse) => {
+      if (aResponse.error) {
+        Cu.reportError("WCF_openNetworkPanel getResponseHeaders:" +
+                       aResponse.error);
+        return;
+      }
+
+      aHttpActivity.response.headers = aResponse.headers;
+
+      this.webConsoleClient.getResponseCookies(actor, onResponseCookies);
+    };
+
+    let onResponseCookies = (aResponse) => {
+      if (aResponse.error) {
+        Cu.reportError("WCF_openNetworkPanel getResponseCookies:" +
+                       aResponse.error);
+        return;
+      }
+
+      aHttpActivity.response.cookies = aResponse.cookies;
+
+      this.webConsoleClient.getRequestPostData(actor, onRequestPostData);
+    };
+
+    let onRequestPostData = (aResponse) => {
+      if (aResponse.error) {
+        Cu.reportError("WCF_openNetworkPanel getRequestPostData:" +
+                       aResponse.error);
+        return;
+      }
+
+      aHttpActivity.request.postData = aResponse.postData;
+      aHttpActivity.discardRequestBody = aResponse.postDataDiscarded;
+
+      this.webConsoleClient.getResponseContent(actor, onResponseContent);
+    };
+
+    let onResponseContent = (aResponse) => {
+      if (aResponse.error) {
+        Cu.reportError("WCF_openNetworkPanel getResponseContent:" +
+                       aResponse.error);
+        return;
+      }
+
+      aHttpActivity.response.content = aResponse.content;
+      aHttpActivity.discardResponseBody = aResponse.contentDiscarded;
+
+      this.webConsoleClient.getEventTimings(actor, onEventTimings);
+    };
+
+    let onEventTimings = (aResponse) => {
+      if (aResponse.error) {
+        Cu.reportError("WCF_openNetworkPanel getEventTimings:" +
+                       aResponse.error);
+        return;
+      }
+
+      aHttpActivity.timings = aResponse.timings;
+
+      openPanel();
+    };
+
+    let openPanel = () => {
+      aNode._netPanel = netPanel;
+
+      let panel = netPanel.panel;
+      panel.openPopup(aNode, "after_pointer", 0, 0, false, false);
+      panel.sizeTo(450, 500);
+      panel.setAttribute("hudId", this.hudId);
+
+      panel.addEventListener("popuphiding", function WCF_netPanel_onHide() {
+        panel.removeEventListener("popuphiding", WCF_netPanel_onHide);
+
+        aNode._panelOpen = false;
+        aNode._netPanel = null;
+      });
+
+      aNode._panelOpen = true;
+    };
+
+    let netPanel = new NetworkPanel(this.popupset, aHttpActivity, this);
+    netPanel.linkNode = aNode;
+
+    if (!actor) {
+      openPanel();
+    }
+
+    return netPanel;
   },
 
   /**
    * Handler for page location changes.
    *
    * @param string aURI
    *        New page location.
    * @param string aTitle
--- a/browser/locales/en-US/chrome/browser/devtools/webConsole.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/webConsole.dtd
@@ -6,16 +6,34 @@
   - 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. -->
 
 <!ENTITY window.title "Web Console">
 <!ENTITY browserConsole.title "Browser Console">
 
+<!ENTITY networkPanel.requestURLColon             "Request URL:">
+<!ENTITY networkPanel.requestMethodColon          "Request Method:">
+<!ENTITY networkPanel.statusCodeColon             "Status Code:">
+
+<!ENTITY networkPanel.requestHeaders              "Request Headers">
+<!ENTITY networkPanel.requestCookie               "Sent Cookie">
+<!ENTITY networkPanel.requestBody                 "Request Body">
+<!ENTITY networkPanel.requestFormData             "Sent Form Data">
+
+<!ENTITY networkPanel.responseHeaders             "Response Headers">
+<!ENTITY networkPanel.responseCookie              "Received Cookie">
+<!ENTITY networkPanel.responseBody                "Response Body">
+<!ENTITY networkPanel.responseBodyCached          "Cached Data">
+<!ENTITY networkPanel.responseBodyUnknownType     "Unknown Content Type">
+<!ENTITY networkPanel.responseNoBody              "No Response Body">
+<!ENTITY networkPanel.responseImage               "Received Image">
+<!ENTITY networkPanel.responseImageCached         "Cached Image">
+
 <!-- LOCALIZATION NOTE (saveBodies.label): You can see this string in the Web
    - Console context menu. -->
 <!ENTITY saveBodies.label     "Log Request and Response Bodies">
 <!ENTITY saveBodies.accesskey "L">
 
 <!-- LOCALIZATION NOTE (openURL.label): You can see this string in the Web
    - Console context menu. -->
 <!ENTITY openURL.label     "Open URL in New Tab">
--- a/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
@@ -35,22 +35,39 @@ update.accesskey=U
 cmd.commandkey=K
 webConsoleCmd.accesskey=W
 
 # LOCALIZATION NOTE (timestampFormat): %1$02S = hours (24-hour clock),
 # %2$02S = minutes, %3$02S = seconds, %4$03S = milliseconds.
 timestampFormat=%02S:%02S:%02S.%03S
 
 helperFuncUnsupportedTypeError=Can't call pprint on this type of object.
+NetworkPanel.label=Inspect Network Request
 
 # LOCALIZATION NOTE (NetworkPanel.deltaDurationMS): this string is used to
 # show the duration between two network events (e.g request and response
 # header or response header and response body). Parameters: %S is the duration.
 NetworkPanel.durationMS=%Sms
 
+# LOCALIZATION NOTE (NetworkPanel.imageSizeDeltaDurationMS): this string is
+# used to show the duration between the response header and the response body
+# event. It also shows the size of the received or cached image. Parameters:
+# %1$S is the width of the inspected image, %2$S is the height of the
+# inspected image, %3$S is the duration between the response header and the
+# response body event. Example: 150x100px, Δ50ms.
+NetworkPanel.imageSizeDeltaDurationMS=%1$Sx%2$Spx, Δ%3$Sms
+
+# LOCALIZATION NOTE (NetworkPanel.responseBodyUnableToDisplay.content): this
+# string is displayed within the response body section of the NetworkPanel if
+# the content type of the network request can't be displayed. E.g. any kind of
+# text is easy to display, but some audio or flash data received from the
+# server can't be displayed. Parameters: %S is the content type that can't be
+# displayed, examples are application/x-shockwave-flash, music/crescendo.
+NetworkPanel.responseBodyUnableToDisplay.content=Unable to display responses of type "%S"
+
 ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, console.warn, console.error) has been disabled by a script on this page.
 
 # LOCALIZATION NOTE (webConsoleWindowTitleAndURL): the Web Console floating
 # panel title. For RTL languages you need to set the LRM in the string to give
 # the URL the correct direction. Parameters: %S is the web page URL.
 webConsoleWindowTitleAndURL=Web Console - %S
 
 # LOCALIZATION NOTE (webConsoleXhrIndicator): the indicator displayed before
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -325,16 +325,17 @@ browser.jar:
   skin/classic/browser/devtools/command-eyedropper.png        (../shared/devtools/images/command-eyedropper.png)
   skin/classic/browser/devtools/command-eyedropper@2x.png     (../shared/devtools/images/command-eyedropper@2x.png)
   skin/classic/browser/devtools/command-rulers.png            (../shared/devtools/images/command-rulers.png)
   skin/classic/browser/devtools/command-rulers@2x.png         (../shared/devtools/images/command-rulers@2x.png)
   skin/classic/browser/devtools/alerticon-warning.png (../shared/devtools/images/alerticon-warning.png)
   skin/classic/browser/devtools/alerticon-warning@2x.png      (../shared/devtools/images/alerticon-warning@2x.png)
 * skin/classic/browser/devtools/ruleview.css          (../shared/devtools/ruleview.css)
 * skin/classic/browser/devtools/webconsole.css                  (../shared/devtools/webconsole.css)
+  skin/classic/browser/devtools/webconsole_networkpanel.css     (../shared/devtools/webconsole_networkpanel.css)
   skin/classic/browser/devtools/webconsole.svg                  (../shared/devtools/images/webconsole.svg)
   skin/classic/browser/devtools/commandline.css              (../shared/devtools/commandline.css)
   skin/classic/browser/devtools/markup-view.css       (../shared/devtools/markup-view.css)
   skin/classic/browser/devtools/editor-error.png       (../shared/devtools/images/editor-error.png)
   skin/classic/browser/devtools/editor-breakpoint.png  (../shared/devtools/images/editor-breakpoint.png)
   skin/classic/browser/devtools/editor-debug-location.png (../shared/devtools/images/editor-debug-location.png)
   skin/classic/browser/devtools/editor-debug-location@2x.png (../shared/devtools/images/editor-debug-location@2x.png)
   skin/classic/browser/devtools/breadcrumbs-divider@2x.png      (../shared/devtools/images/breadcrumbs-divider@2x.png)
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -427,16 +427,17 @@ browser.jar:
   skin/classic/browser/devtools/commandline.css             (../shared/devtools/commandline.css)
   skin/classic/browser/devtools/markup-view.css             (../shared/devtools/markup-view.css)
   skin/classic/browser/devtools/editor-error.png             (../shared/devtools/images/editor-error.png)
   skin/classic/browser/devtools/editor-breakpoint.png        (../shared/devtools/images/editor-breakpoint.png)
   skin/classic/browser/devtools/editor-breakpoint@2x.png        (../shared/devtools/images/editor-breakpoint@2x.png)
   skin/classic/browser/devtools/editor-debug-location.png    (../shared/devtools/images/editor-debug-location.png)
   skin/classic/browser/devtools/editor-debug-location@2x.png    (../shared/devtools/images/editor-debug-location@2x.png)
 * skin/classic/browser/devtools/webconsole.css                  (../shared/devtools/webconsole.css)
+  skin/classic/browser/devtools/webconsole_networkpanel.css     (../shared/devtools/webconsole_networkpanel.css)
   skin/classic/browser/devtools/webconsole.svg                  (../shared/devtools/images/webconsole.svg)
   skin/classic/browser/devtools/breadcrumbs-divider@2x.png      (../shared/devtools/images/breadcrumbs-divider@2x.png)
   skin/classic/browser/devtools/breadcrumbs-scrollbutton.png    (../shared/devtools/images/breadcrumbs-scrollbutton.png)
   skin/classic/browser/devtools/breadcrumbs-scrollbutton@2x.png (../shared/devtools/images/breadcrumbs-scrollbutton@2x.png)
   skin/classic/browser/devtools/animationinspector.css          (../shared/devtools/animationinspector.css)
 * skin/classic/browser/devtools/canvasdebugger.css          (../shared/devtools/canvasdebugger.css)
   skin/classic/browser/devtools/debugger.css                (../shared/devtools/debugger.css)
   skin/classic/browser/devtools/eyedropper.css              (../shared/devtools/eyedropper.css)
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/devtools/webconsole_networkpanel.css
@@ -0,0 +1,100 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+body {
+  font-family: sans-serif;
+  font-size: 11px;
+  background: #EEE;
+  color: #000;
+}
+
+#header {
+  padding: 5px;
+  overflow-x:auto;
+  display: block;
+}
+
+h1 {
+  font-size: 13px;
+  line-height: 15px;
+  padding: 3px 10px;
+  vertical-align: bottom;
+  margin: 0px;
+  background: linear-gradient(#BBB, #999);
+  border-radius: 2px;
+  text-shadow: #FFF 0px 1px 0px;
+}
+
+h1 .info {
+  font-size: 11px;
+  line-height: 15px;
+  vertical-align: bottom;
+  float: right;
+  color: #333;
+  padding-right: 3px;
+}
+
+.property-table {
+  padding: 2px 5px;
+  background: linear-gradient(#FFF, #F8F8F8);
+  color: #333;
+  width: 100%;
+  max-height: 330px;
+  overflow: auto;
+  display: block;
+}
+
+.property-name {
+  font-size: 11px;
+  font-weight: bold;
+  padding-right: 4px;
+  color: #000;
+  white-space: nowrap;
+  text-align: end;
+  vertical-align: top;
+  width: 10%;
+}
+
+.property-value {
+  padding-right: 5px;
+  font-size: 11px;
+  word-wrap: break-word;
+  width: 90%;
+}
+
+div.group {
+  margin-top: 10px;
+}
+
+div.group,
+#header {
+  background: #FFF;
+  border-color: #E1E1E1;
+  border-style: solid;
+  border-width: 1px;
+  box-shadow: 0 1px 1.5px rgba(0, 0, 0, 0.2);
+  border-radius: 4px 4px 4px 4px;
+}
+
+img#responseImageNode {
+  box-shadow: rgba(0,0,0,0.2) 0px 3px 3.5px;
+  max-width: 100%;
+}
+
+#responseImageNodeDiv {
+  padding: 5px;
+}
+
+#responseBodyFetchLink, #requestBodyFetchLink {
+  padding: 5px;
+  margin: 0;
+  cursor: pointer;
+  font-weight: bold;
+  font-size: 1.1em;
+  text-decoration: underline;
+}
+
+.longStringEllipsis {
+  margin-left: 0.6em;
+}
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -452,16 +452,17 @@ browser.jar:
         skin/classic/browser/devtools/command-rulers@2x.png         (../shared/devtools/images/command-rulers@2x.png)
         skin/classic/browser/devtools/markup-view.css               (../shared/devtools/markup-view.css)
         skin/classic/browser/devtools/editor-error.png              (../shared/devtools/images/editor-error.png)
         skin/classic/browser/devtools/editor-breakpoint.png         (../shared/devtools/images/editor-breakpoint.png)
         skin/classic/browser/devtools/editor-breakpoint@2x.png         (../shared/devtools/images/editor-breakpoint@2x.png)
         skin/classic/browser/devtools/editor-debug-location.png     (../shared/devtools/images/editor-debug-location.png)
         skin/classic/browser/devtools/editor-debug-location@2x.png     (../shared/devtools/images/editor-debug-location@2x.png)
 *       skin/classic/browser/devtools/webconsole.css                (../shared/devtools/webconsole.css)
+        skin/classic/browser/devtools/webconsole_networkpanel.css   (../shared/devtools/webconsole_networkpanel.css)
         skin/classic/browser/devtools/webconsole.svg                (../shared/devtools/images/webconsole.svg)
         skin/classic/browser/devtools/breadcrumbs-divider@2x.png    (../shared/devtools/images/breadcrumbs-divider@2x.png)
         skin/classic/browser/devtools/breadcrumbs-scrollbutton.png  (../shared/devtools/images/breadcrumbs-scrollbutton.png)
         skin/classic/browser/devtools/breadcrumbs-scrollbutton@2x.png (../shared/devtools/images/breadcrumbs-scrollbutton@2x.png)
         skin/classic/browser/devtools/animationinspector.css        (../shared/devtools/animationinspector.css)
         skin/classic/browser/devtools/eyedropper.css                (../shared/devtools/eyedropper.css)
 *       skin/classic/browser/devtools/canvasdebugger.css            (../shared/devtools/canvasdebugger.css)
         skin/classic/browser/devtools/debugger.css                  (../shared/devtools/debugger.css)