2013-09-10 18:43:44 -07:00
|
|
|
/* -*- js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
|
2012-05-29 05:48:05 -07:00
|
|
|
/* 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";
|
|
|
|
|
2013-08-03 03:29:48 -07:00
|
|
|
const {Cc, Ci, Cu} = require("chrome");
|
2012-05-29 05:48:05 -07:00
|
|
|
|
2013-08-03 03:29:48 -07:00
|
|
|
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");
|
2012-05-29 05:48:05 -07:00
|
|
|
|
2013-08-03 03:29:48 -07:00
|
|
|
let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
|
2012-05-29 05:48:05 -07:00
|
|
|
|
2012-09-26 10:07:57 -07:00
|
|
|
const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
|
|
|
|
let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
|
2012-05-29 05:48:05 -07:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new NetworkPanel.
|
|
|
|
*
|
2012-11-05 08:41:59 -08:00
|
|
|
* @constructor
|
2012-05-29 05:48:05 -07:00
|
|
|
* @param nsIDOMNode aParent
|
|
|
|
* Parent node to append the created panel to.
|
|
|
|
* @param object aHttpActivity
|
|
|
|
* HttpActivity to display in the panel.
|
2012-11-05 08:41:59 -08:00
|
|
|
* @param object aWebConsoleFrame
|
|
|
|
* The parent WebConsoleFrame object that owns this network panel
|
|
|
|
* instance.
|
2012-05-29 05:48:05 -07:00
|
|
|
*/
|
2012-11-05 08:41:59 -08:00
|
|
|
function NetworkPanel(aParent, aHttpActivity, aWebConsoleFrame)
|
2012-05-29 05:48:05 -07:00
|
|
|
{
|
|
|
|
let doc = aParent.ownerDocument;
|
|
|
|
this.httpActivity = aHttpActivity;
|
2012-11-05 08:41:59 -08:00
|
|
|
this.webconsole = aWebConsoleFrame;
|
|
|
|
this._longStringClick = this._longStringClick.bind(this);
|
|
|
|
this._responseBodyFetch = this._responseBodyFetch.bind(this);
|
|
|
|
this._requestBodyFetch = this._requestBodyFetch.bind(this);
|
2012-05-29 05:48:05 -07:00
|
|
|
|
|
|
|
// 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", {
|
2013-04-30 08:56:00 -07:00
|
|
|
src: "chrome://browser/content/devtools/NetworkPanel.xhtml",
|
2012-05-29 05:48:05 -07:00
|
|
|
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;
|
2012-11-05 08:41:59 -08:00
|
|
|
self.webconsole = null;
|
2012-05-29 05:48:05 -07:00
|
|
|
|
|
|
|
if (self.linkNode) {
|
|
|
|
self.linkNode._panelOpen = false;
|
|
|
|
self.linkNode = null;
|
|
|
|
}
|
|
|
|
}, false);
|
|
|
|
|
|
|
|
// Set the document object and update the content once the panel is loaded.
|
2012-10-05 04:54:43 -07:00
|
|
|
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);
|
2012-05-29 05:48:05 -07:00
|
|
|
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);
|
|
|
|
}
|
2013-08-03 03:29:48 -07:00
|
|
|
exports.NetworkPanel = NetworkPanel;
|
2012-05-29 05:48:05 -07:00
|
|
|
|
|
|
|
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,
|
|
|
|
|
2012-10-05 04:54:43 -07:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
},
|
|
|
|
|
2012-05-29 05:48:05 -07:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2012-10-05 04:54:43 -07:00
|
|
|
let request = this.httpActivity.request;
|
|
|
|
let response = this.httpActivity.response;
|
2012-05-29 05:48:05 -07:00
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
2012-11-05 08:41:59 -08:00
|
|
|
return this.contentType ?
|
|
|
|
NetworkHelper.isTextMimeType(this.contentType) : false;
|
2012-05-29 05:48:05 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
2012-10-05 04:54:43 -07:00
|
|
|
return this.httpActivity.response.status == 304;
|
2012-05-29 05:48:05 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tells if the request body includes form data.
|
|
|
|
*
|
|
|
|
* @returns boolean
|
|
|
|
* Returns true if the posted body contains form data.
|
|
|
|
*/
|
|
|
|
get _isRequestBodyFormData()
|
|
|
|
{
|
2012-10-05 04:54:43 -07:00
|
|
|
let requestBody = this.httpActivity.request.postData.text;
|
2012-11-05 08:41:59 -08:00
|
|
|
if (typeof requestBody == "object" && requestBody.type == "longString") {
|
|
|
|
requestBody = requestBody.initial;
|
|
|
|
}
|
2012-05-29 05:48:05 -07:00
|
|
|
return this._fromDataRegExp.test(requestBody);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Appends the node with id=aId by the text aValue.
|
|
|
|
*
|
2012-11-05 08:41:59 -08:00
|
|
|
* @private
|
2012-05-29 05:48:05 -07:00
|
|
|
* @param string aId
|
|
|
|
* @param string aValue
|
2012-11-05 08:41:59 -08:00
|
|
|
* @return nsIDOMElement
|
|
|
|
* The DOM element with id=aId.
|
2012-05-29 05:48:05 -07:00
|
|
|
*/
|
2012-11-05 08:41:59 -08:00
|
|
|
_appendTextNode: function NP__appendTextNode(aId, aValue)
|
2012-05-29 05:48:05 -07:00
|
|
|
{
|
|
|
|
let textNode = this.document.createTextNode(aValue);
|
2012-11-05 08:41:59 -08:00
|
|
|
let elem = this.document.getElementById(aId);
|
|
|
|
elem.appendChild(textNode);
|
|
|
|
return elem;
|
2012-05-29 05:48:05 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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(function(aItem) {
|
|
|
|
let name = aItem.name;
|
2012-11-05 08:41:59 -08:00
|
|
|
if (aIgnoreCookie && (name == "Cookie" || name == "Set-Cookie")) {
|
2012-05-29 05:48:05 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-05 08:41:59 -08:00
|
|
|
let value = aItem.value;
|
|
|
|
let longString = null;
|
|
|
|
if (typeof value == "object" && value.type == "longString") {
|
|
|
|
value = value.initial;
|
|
|
|
longString = true;
|
|
|
|
}
|
|
|
|
|
2012-05-29 05:48:05 -07:00
|
|
|
/**
|
|
|
|
* 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);
|
2012-11-05 08:41:59 -08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-05-29 05:48:05 -07:00
|
|
|
row.appendChild(td);
|
|
|
|
|
|
|
|
parent.appendChild(row);
|
2012-11-05 08:41:59 -08:00
|
|
|
}.bind(this));
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
});
|
2012-05-29 05:48:05 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Displays the node with id=aId.
|
|
|
|
*
|
2012-11-05 08:41:59 -08:00
|
|
|
* @private
|
2012-05-29 05:48:05 -07:00
|
|
|
* @param string aId
|
2012-11-05 08:41:59 -08:00
|
|
|
* @return nsIDOMElement
|
|
|
|
* The element with id=aId.
|
2012-05-29 05:48:05 -07:00
|
|
|
*/
|
2012-11-05 08:41:59 -08:00
|
|
|
_displayNode: function NP__displayNode(aId)
|
2012-05-29 05:48:05 -07:00
|
|
|
{
|
2012-11-05 08:41:59 -08:00
|
|
|
let elem = this.document.getElementById(aId);
|
|
|
|
elem.style.display = "block";
|
2012-05-29 05:48:05 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
2012-10-05 04:54:43 -07:00
|
|
|
let request = this.httpActivity.request;
|
|
|
|
let requestTime = new Date(this.httpActivity.startedDateTime);
|
2012-05-29 05:48:05 -07:00
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
2012-10-05 04:54:43 -07:00
|
|
|
_displayRequestBody: function NP__displayRequestBody()
|
|
|
|
{
|
|
|
|
let postData = this.httpActivity.request.postData;
|
2012-05-29 05:48:05 -07:00
|
|
|
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
|
|
|
|
*/
|
2012-10-05 04:54:43 -07:00
|
|
|
_displayRequestForm: function NP__processRequestForm()
|
|
|
|
{
|
|
|
|
let postData = this.httpActivity.request.postData.text;
|
2012-05-29 05:48:05 -07:00
|
|
|
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()
|
|
|
|
{
|
2012-10-05 04:54:43 -07:00
|
|
|
let timing = this.httpActivity.timings;
|
|
|
|
let response = this.httpActivity.response;
|
2012-05-29 05:48:05 -07:00
|
|
|
|
|
|
|
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");
|
2012-11-05 08:41:59 -08:00
|
|
|
this._appendList("responseHeadersContent", response.headers, true);
|
|
|
|
|
|
|
|
if (response.cookies.length > 0) {
|
|
|
|
this._displayNode("responseCookie");
|
|
|
|
this._appendList("responseCookieContent", response.cookies);
|
|
|
|
}
|
2012-05-29 05:48:05 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
2012-10-05 04:54:43 -07:00
|
|
|
let timing = this.httpActivity.timings;
|
|
|
|
let request = this.httpActivity.request;
|
2012-11-05 08:41:59 -08:00
|
|
|
let response = this.httpActivity.response;
|
2012-05-29 05:48:05 -07:00
|
|
|
let cached = "";
|
|
|
|
|
|
|
|
if (this._isResponseCached) {
|
|
|
|
cached = "Cached";
|
|
|
|
}
|
|
|
|
|
2012-10-05 04:54:43 -07:00
|
|
|
let imageNode = this.document.getElementById("responseImage" +
|
|
|
|
cached + "Node");
|
2012-11-05 08:41:59 -08:00
|
|
|
|
|
|
|
let text = response.content.text;
|
|
|
|
if (typeof text == "object" && text.type == "longString") {
|
|
|
|
this._showResponseBodyFetchLink();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
imageNode.setAttribute("src",
|
|
|
|
"data:" + this.contentType + ";base64," + text);
|
|
|
|
}
|
2012-05-29 05:48:05 -07:00
|
|
|
|
|
|
|
// 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()
|
|
|
|
{
|
2012-10-05 04:54:43 -07:00
|
|
|
let timing = this.httpActivity.timings;
|
|
|
|
let response = this.httpActivity.response;
|
2012-05-29 05:48:05 -07:00
|
|
|
let cached = this._isResponseCached ? "Cached" : "";
|
|
|
|
|
|
|
|
this._appendTextNode("responseBody" + cached + "Info",
|
|
|
|
this._format("durationMS", [timing.receive]));
|
|
|
|
|
|
|
|
this._displayNode("responseBody" + cached);
|
2012-11-05 08:41:59 -08:00
|
|
|
|
|
|
|
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,
|
|
|
|
function NP__onLongStringSubstring(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);
|
|
|
|
}
|
|
|
|
}.bind(this));
|
2012-05-29 05:48:05 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
2012-10-05 04:54:43 -07:00
|
|
|
let timing = this.httpActivity.timings;
|
2012-05-29 05:48:05 -07:00
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
2012-10-05 04:54:43 -07:00
|
|
|
let timing = this.httpActivity.timings;
|
2012-05-29 05:48:05 -07:00
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
2013-09-10 04:08:53 -07:00
|
|
|
if (!this.document || this.document.readyState != "complete") {
|
2012-05-29 05:48:05 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-10-05 04:54:43 -07:00
|
|
|
let updates = this.httpActivity.updates;
|
|
|
|
let timing = this.httpActivity.timings;
|
|
|
|
let request = this.httpActivity.request;
|
|
|
|
let response = this.httpActivity.response;
|
2012-05-29 05:48:05 -07:00
|
|
|
|
|
|
|
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.
|
2012-10-05 04:54:43 -07:00
|
|
|
if (!this.httpActivity.discardRequestBody && request.postData.text) {
|
2012-11-05 08:41:59 -08:00
|
|
|
this._updateRequestBody();
|
2012-05-29 05:48:05 -07:00
|
|
|
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:
|
2012-10-05 04:54:43 -07:00
|
|
|
if (updates.indexOf("responseContent") == -1 ||
|
|
|
|
updates.indexOf("eventTimings") == -1) {
|
2012-05-29 05:48:05 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._state = this._TRANSITION_CLOSED;
|
2012-10-05 04:54:43 -07:00
|
|
|
if (this.httpActivity.discardResponseBody) {
|
2012-05-29 05:48:05 -07:00
|
|
|
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;
|
|
|
|
}
|
2012-10-05 04:54:43 -07:00
|
|
|
|
|
|
|
if (this._onUpdate) {
|
|
|
|
this._onUpdate();
|
|
|
|
}
|
2012-11-05 08:41:59 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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,
|
|
|
|
function NP__onLongStringSubstring(aResponse)
|
|
|
|
{
|
|
|
|
if (aResponse.error) {
|
|
|
|
Cu.reportError("NP__onLongStringSubstring error: " + aResponse.error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
postData.text = postData.text.initial + aResponse.substring;
|
|
|
|
this._updateRequestBody();
|
|
|
|
}.bind(this));
|
|
|
|
},
|
|
|
|
};
|
2012-05-29 05:48:05 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|