browser/devtools/webconsole/network-panel.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
michael@0 2 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 "use strict";
michael@0 8
michael@0 9 const {Cc, Ci, Cu} = require("chrome");
michael@0 10
michael@0 11 loader.lazyGetter(this, "NetworkHelper", () => require("devtools/toolkit/webconsole/network-helper"));
michael@0 12 loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
michael@0 13 loader.lazyServiceGetter(this, "mimeService", "@mozilla.org/mime;1", "nsIMIMEService");
michael@0 14
michael@0 15 let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
michael@0 16
michael@0 17 const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
michael@0 18 let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
michael@0 19
michael@0 20
michael@0 21 /**
michael@0 22 * Creates a new NetworkPanel.
michael@0 23 *
michael@0 24 * @constructor
michael@0 25 * @param nsIDOMNode aParent
michael@0 26 * Parent node to append the created panel to.
michael@0 27 * @param object aHttpActivity
michael@0 28 * HttpActivity to display in the panel.
michael@0 29 * @param object aWebConsoleFrame
michael@0 30 * The parent WebConsoleFrame object that owns this network panel
michael@0 31 * instance.
michael@0 32 */
michael@0 33 function NetworkPanel(aParent, aHttpActivity, aWebConsoleFrame)
michael@0 34 {
michael@0 35 let doc = aParent.ownerDocument;
michael@0 36 this.httpActivity = aHttpActivity;
michael@0 37 this.webconsole = aWebConsoleFrame;
michael@0 38 this._longStringClick = this._longStringClick.bind(this);
michael@0 39 this._responseBodyFetch = this._responseBodyFetch.bind(this);
michael@0 40 this._requestBodyFetch = this._requestBodyFetch.bind(this);
michael@0 41
michael@0 42 // Create the underlaying panel
michael@0 43 this.panel = createElement(doc, "panel", {
michael@0 44 label: l10n.getStr("NetworkPanel.label"),
michael@0 45 titlebar: "normal",
michael@0 46 noautofocus: "true",
michael@0 47 noautohide: "true",
michael@0 48 close: "true"
michael@0 49 });
michael@0 50
michael@0 51 // Create the iframe that displays the NetworkPanel XHTML.
michael@0 52 this.iframe = createAndAppendElement(this.panel, "iframe", {
michael@0 53 src: "chrome://browser/content/devtools/NetworkPanel.xhtml",
michael@0 54 type: "content",
michael@0 55 flex: "1"
michael@0 56 });
michael@0 57
michael@0 58 let self = this;
michael@0 59
michael@0 60 // Destroy the panel when it's closed.
michael@0 61 this.panel.addEventListener("popuphidden", function onPopupHide() {
michael@0 62 self.panel.removeEventListener("popuphidden", onPopupHide, false);
michael@0 63 self.panel.parentNode.removeChild(self.panel);
michael@0 64 self.panel = null;
michael@0 65 self.iframe = null;
michael@0 66 self.httpActivity = null;
michael@0 67 self.webconsole = null;
michael@0 68
michael@0 69 if (self.linkNode) {
michael@0 70 self.linkNode._panelOpen = false;
michael@0 71 self.linkNode = null;
michael@0 72 }
michael@0 73 }, false);
michael@0 74
michael@0 75 // Set the document object and update the content once the panel is loaded.
michael@0 76 this.iframe.addEventListener("load", function onLoad() {
michael@0 77 if (!self.iframe) {
michael@0 78 return;
michael@0 79 }
michael@0 80
michael@0 81 self.iframe.removeEventListener("load", onLoad, true);
michael@0 82 self.update();
michael@0 83 }, true);
michael@0 84
michael@0 85 this.panel.addEventListener("popupshown", function onPopupShown() {
michael@0 86 self.panel.removeEventListener("popupshown", onPopupShown, true);
michael@0 87 self.update();
michael@0 88 }, true);
michael@0 89
michael@0 90 // Create the footer.
michael@0 91 let footer = createElement(doc, "hbox", { align: "end" });
michael@0 92 createAndAppendElement(footer, "spacer", { flex: 1 });
michael@0 93
michael@0 94 createAndAppendElement(footer, "resizer", { dir: "bottomend" });
michael@0 95 this.panel.appendChild(footer);
michael@0 96
michael@0 97 aParent.appendChild(this.panel);
michael@0 98 }
michael@0 99 exports.NetworkPanel = NetworkPanel;
michael@0 100
michael@0 101 NetworkPanel.prototype =
michael@0 102 {
michael@0 103 /**
michael@0 104 * The current state of the output.
michael@0 105 */
michael@0 106 _state: 0,
michael@0 107
michael@0 108 /**
michael@0 109 * State variables.
michael@0 110 */
michael@0 111 _INIT: 0,
michael@0 112 _DISPLAYED_REQUEST_HEADER: 1,
michael@0 113 _DISPLAYED_REQUEST_BODY: 2,
michael@0 114 _DISPLAYED_RESPONSE_HEADER: 3,
michael@0 115 _TRANSITION_CLOSED: 4,
michael@0 116
michael@0 117 _fromDataRegExp: /Content-Type\:\s*application\/x-www-form-urlencoded/,
michael@0 118
michael@0 119 _contentType: null,
michael@0 120
michael@0 121 /**
michael@0 122 * Function callback invoked whenever the panel content is updated. This is
michael@0 123 * used only by tests.
michael@0 124 *
michael@0 125 * @private
michael@0 126 * @type function
michael@0 127 */
michael@0 128 _onUpdate: null,
michael@0 129
michael@0 130 get document() {
michael@0 131 return this.iframe && this.iframe.contentWindow ?
michael@0 132 this.iframe.contentWindow.document : null;
michael@0 133 },
michael@0 134
michael@0 135 /**
michael@0 136 * Small helper function that is nearly equal to l10n.getFormatStr
michael@0 137 * except that it prefixes aName with "NetworkPanel.".
michael@0 138 *
michael@0 139 * @param string aName
michael@0 140 * The name of an i10n string to format. This string is prefixed with
michael@0 141 * "NetworkPanel." before calling the HUDService.getFormatStr function.
michael@0 142 * @param array aArray
michael@0 143 * Values used as placeholder for the i10n string.
michael@0 144 * @returns string
michael@0 145 * The i10n formated string.
michael@0 146 */
michael@0 147 _format: function NP_format(aName, aArray)
michael@0 148 {
michael@0 149 return l10n.getFormatStr("NetworkPanel." + aName, aArray);
michael@0 150 },
michael@0 151
michael@0 152 /**
michael@0 153 * Returns the content type of the response body. This is based on the
michael@0 154 * response.content.mimeType property. If this value is not available, then
michael@0 155 * the content type is guessed by the file extension of the request URL.
michael@0 156 *
michael@0 157 * @return string
michael@0 158 * Content type or empty string if no content type could be figured
michael@0 159 * out.
michael@0 160 */
michael@0 161 get contentType()
michael@0 162 {
michael@0 163 if (this._contentType) {
michael@0 164 return this._contentType;
michael@0 165 }
michael@0 166
michael@0 167 let request = this.httpActivity.request;
michael@0 168 let response = this.httpActivity.response;
michael@0 169
michael@0 170 let contentType = "";
michael@0 171 let types = response.content ?
michael@0 172 (response.content.mimeType || "").split(/,|;/) : [];
michael@0 173 for (let i = 0; i < types.length; i++) {
michael@0 174 if (types[i] in NetworkHelper.mimeCategoryMap) {
michael@0 175 contentType = types[i];
michael@0 176 break;
michael@0 177 }
michael@0 178 }
michael@0 179
michael@0 180 if (contentType) {
michael@0 181 this._contentType = contentType;
michael@0 182 return contentType;
michael@0 183 }
michael@0 184
michael@0 185 // Try to get the content type from the request file extension.
michael@0 186 let uri = NetUtil.newURI(request.url);
michael@0 187 if ((uri instanceof Ci.nsIURL) && uri.fileExtension) {
michael@0 188 try {
michael@0 189 contentType = mimeService.getTypeFromExtension(uri.fileExtension);
michael@0 190 }
michael@0 191 catch(ex) {
michael@0 192 // Added to prevent failures on OS X 64. No Flash?
michael@0 193 Cu.reportError(ex);
michael@0 194 }
michael@0 195 }
michael@0 196
michael@0 197 this._contentType = contentType;
michael@0 198 return contentType;
michael@0 199 },
michael@0 200
michael@0 201 /**
michael@0 202 *
michael@0 203 * @returns boolean
michael@0 204 * True if the response is an image, false otherwise.
michael@0 205 */
michael@0 206 get _responseIsImage()
michael@0 207 {
michael@0 208 return this.contentType &&
michael@0 209 NetworkHelper.mimeCategoryMap[this.contentType] == "image";
michael@0 210 },
michael@0 211
michael@0 212 /**
michael@0 213 *
michael@0 214 * @returns boolean
michael@0 215 * True if the response body contains text, false otherwise.
michael@0 216 */
michael@0 217 get _isResponseBodyTextData()
michael@0 218 {
michael@0 219 return this.contentType ?
michael@0 220 NetworkHelper.isTextMimeType(this.contentType) : false;
michael@0 221 },
michael@0 222
michael@0 223 /**
michael@0 224 * Tells if the server response is cached.
michael@0 225 *
michael@0 226 * @returns boolean
michael@0 227 * Returns true if the server responded that the request is already
michael@0 228 * in the browser's cache, false otherwise.
michael@0 229 */
michael@0 230 get _isResponseCached()
michael@0 231 {
michael@0 232 return this.httpActivity.response.status == 304;
michael@0 233 },
michael@0 234
michael@0 235 /**
michael@0 236 * Tells if the request body includes form data.
michael@0 237 *
michael@0 238 * @returns boolean
michael@0 239 * Returns true if the posted body contains form data.
michael@0 240 */
michael@0 241 get _isRequestBodyFormData()
michael@0 242 {
michael@0 243 let requestBody = this.httpActivity.request.postData.text;
michael@0 244 if (typeof requestBody == "object" && requestBody.type == "longString") {
michael@0 245 requestBody = requestBody.initial;
michael@0 246 }
michael@0 247 return this._fromDataRegExp.test(requestBody);
michael@0 248 },
michael@0 249
michael@0 250 /**
michael@0 251 * Appends the node with id=aId by the text aValue.
michael@0 252 *
michael@0 253 * @private
michael@0 254 * @param string aId
michael@0 255 * @param string aValue
michael@0 256 * @return nsIDOMElement
michael@0 257 * The DOM element with id=aId.
michael@0 258 */
michael@0 259 _appendTextNode: function NP__appendTextNode(aId, aValue)
michael@0 260 {
michael@0 261 let textNode = this.document.createTextNode(aValue);
michael@0 262 let elem = this.document.getElementById(aId);
michael@0 263 elem.appendChild(textNode);
michael@0 264 return elem;
michael@0 265 },
michael@0 266
michael@0 267 /**
michael@0 268 * Generates some HTML to display the key-value pair of the aList data. The
michael@0 269 * generated HTML is added to node with id=aParentId.
michael@0 270 *
michael@0 271 * @param string aParentId
michael@0 272 * Id of the parent node to append the list to.
michael@0 273 * @oaram array aList
michael@0 274 * Array that holds the objects you want to display. Each object must
michael@0 275 * have two properties: name and value.
michael@0 276 * @param boolean aIgnoreCookie
michael@0 277 * If true, the key-value named "Cookie" is not added to the list.
michael@0 278 * @returns void
michael@0 279 */
michael@0 280 _appendList: function NP_appendList(aParentId, aList, aIgnoreCookie)
michael@0 281 {
michael@0 282 let parent = this.document.getElementById(aParentId);
michael@0 283 let doc = this.document;
michael@0 284
michael@0 285 aList.sort(function(a, b) {
michael@0 286 return a.name.toLowerCase() < b.name.toLowerCase();
michael@0 287 });
michael@0 288
michael@0 289 aList.forEach(function(aItem) {
michael@0 290 let name = aItem.name;
michael@0 291 if (aIgnoreCookie && (name == "Cookie" || name == "Set-Cookie")) {
michael@0 292 return;
michael@0 293 }
michael@0 294
michael@0 295 let value = aItem.value;
michael@0 296 let longString = null;
michael@0 297 if (typeof value == "object" && value.type == "longString") {
michael@0 298 value = value.initial;
michael@0 299 longString = true;
michael@0 300 }
michael@0 301
michael@0 302 /**
michael@0 303 * The following code creates the HTML:
michael@0 304 * <tr>
michael@0 305 * <th scope="row" class="property-name">${line}:</th>
michael@0 306 * <td class="property-value">${aList[line]}</td>
michael@0 307 * </tr>
michael@0 308 * and adds it to parent.
michael@0 309 */
michael@0 310 let row = doc.createElement("tr");
michael@0 311 let textNode = doc.createTextNode(name + ":");
michael@0 312 let th = doc.createElement("th");
michael@0 313 th.setAttribute("scope", "row");
michael@0 314 th.setAttribute("class", "property-name");
michael@0 315 th.appendChild(textNode);
michael@0 316 row.appendChild(th);
michael@0 317
michael@0 318 textNode = doc.createTextNode(value);
michael@0 319 let td = doc.createElement("td");
michael@0 320 td.setAttribute("class", "property-value");
michael@0 321 td.appendChild(textNode);
michael@0 322
michael@0 323 if (longString) {
michael@0 324 let a = doc.createElement("a");
michael@0 325 a.href = "#";
michael@0 326 a.className = "longStringEllipsis";
michael@0 327 a.addEventListener("mousedown", this._longStringClick.bind(this, aItem));
michael@0 328 a.textContent = l10n.getStr("longStringEllipsis");
michael@0 329 td.appendChild(a);
michael@0 330 }
michael@0 331
michael@0 332 row.appendChild(td);
michael@0 333
michael@0 334 parent.appendChild(row);
michael@0 335 }.bind(this));
michael@0 336 },
michael@0 337
michael@0 338 /**
michael@0 339 * The click event handler for the ellipsis which allows the user to retrieve
michael@0 340 * the full header value.
michael@0 341 *
michael@0 342 * @private
michael@0 343 * @param object aHeader
michael@0 344 * The header object with the |name| and |value| properties.
michael@0 345 * @param nsIDOMEvent aEvent
michael@0 346 * The DOM click event object.
michael@0 347 */
michael@0 348 _longStringClick: function NP__longStringClick(aHeader, aEvent)
michael@0 349 {
michael@0 350 aEvent.preventDefault();
michael@0 351
michael@0 352 let longString = this.webconsole.webConsoleClient.longString(aHeader.value);
michael@0 353
michael@0 354 longString.substring(longString.initial.length, longString.length,
michael@0 355 function NP__onLongStringSubstring(aResponse)
michael@0 356 {
michael@0 357 if (aResponse.error) {
michael@0 358 Cu.reportError("NP__onLongStringSubstring error: " + aResponse.error);
michael@0 359 return;
michael@0 360 }
michael@0 361
michael@0 362 aHeader.value = aHeader.value.initial + aResponse.substring;
michael@0 363
michael@0 364 let textNode = aEvent.target.previousSibling;
michael@0 365 textNode.textContent += aResponse.substring;
michael@0 366 textNode.parentNode.removeChild(aEvent.target);
michael@0 367 });
michael@0 368 },
michael@0 369
michael@0 370 /**
michael@0 371 * Displays the node with id=aId.
michael@0 372 *
michael@0 373 * @private
michael@0 374 * @param string aId
michael@0 375 * @return nsIDOMElement
michael@0 376 * The element with id=aId.
michael@0 377 */
michael@0 378 _displayNode: function NP__displayNode(aId)
michael@0 379 {
michael@0 380 let elem = this.document.getElementById(aId);
michael@0 381 elem.style.display = "block";
michael@0 382 },
michael@0 383
michael@0 384 /**
michael@0 385 * Sets the request URL, request method, the timing information when the
michael@0 386 * request started and the request header content on the NetworkPanel.
michael@0 387 * If the request header contains cookie data, a list of sent cookies is
michael@0 388 * generated and a special sent cookie section is displayed + the cookie list
michael@0 389 * added to it.
michael@0 390 *
michael@0 391 * @returns void
michael@0 392 */
michael@0 393 _displayRequestHeader: function NP__displayRequestHeader()
michael@0 394 {
michael@0 395 let request = this.httpActivity.request;
michael@0 396 let requestTime = new Date(this.httpActivity.startedDateTime);
michael@0 397
michael@0 398 this._appendTextNode("headUrl", request.url);
michael@0 399 this._appendTextNode("headMethod", request.method);
michael@0 400 this._appendTextNode("requestHeadersInfo",
michael@0 401 l10n.timestampString(requestTime));
michael@0 402
michael@0 403 this._appendList("requestHeadersContent", request.headers, true);
michael@0 404
michael@0 405 if (request.cookies.length > 0) {
michael@0 406 this._displayNode("requestCookie");
michael@0 407 this._appendList("requestCookieContent", request.cookies);
michael@0 408 }
michael@0 409 },
michael@0 410
michael@0 411 /**
michael@0 412 * Displays the request body section of the NetworkPanel and set the request
michael@0 413 * body content on the NetworkPanel.
michael@0 414 *
michael@0 415 * @returns void
michael@0 416 */
michael@0 417 _displayRequestBody: function NP__displayRequestBody()
michael@0 418 {
michael@0 419 let postData = this.httpActivity.request.postData;
michael@0 420 this._displayNode("requestBody");
michael@0 421 this._appendTextNode("requestBodyContent", postData.text);
michael@0 422 },
michael@0 423
michael@0 424 /*
michael@0 425 * Displays the `sent form data` section. Parses the request header for the
michael@0 426 * submitted form data displays it inside of the `sent form data` section.
michael@0 427 *
michael@0 428 * @returns void
michael@0 429 */
michael@0 430 _displayRequestForm: function NP__processRequestForm()
michael@0 431 {
michael@0 432 let postData = this.httpActivity.request.postData.text;
michael@0 433 let requestBodyLines = postData.split("\n");
michael@0 434 let formData = requestBodyLines[requestBodyLines.length - 1].
michael@0 435 replace(/\+/g, " ").split("&");
michael@0 436
michael@0 437 function unescapeText(aText)
michael@0 438 {
michael@0 439 try {
michael@0 440 return decodeURIComponent(aText);
michael@0 441 }
michael@0 442 catch (ex) {
michael@0 443 return decodeURIComponent(unescape(aText));
michael@0 444 }
michael@0 445 }
michael@0 446
michael@0 447 let formDataArray = [];
michael@0 448 for (let i = 0; i < formData.length; i++) {
michael@0 449 let data = formData[i];
michael@0 450 let idx = data.indexOf("=");
michael@0 451 let key = data.substring(0, idx);
michael@0 452 let value = data.substring(idx + 1);
michael@0 453 formDataArray.push({
michael@0 454 name: unescapeText(key),
michael@0 455 value: unescapeText(value)
michael@0 456 });
michael@0 457 }
michael@0 458
michael@0 459 this._appendList("requestFormDataContent", formDataArray);
michael@0 460 this._displayNode("requestFormData");
michael@0 461 },
michael@0 462
michael@0 463 /**
michael@0 464 * Displays the response section of the NetworkPanel, sets the response status,
michael@0 465 * the duration between the start of the request and the receiving of the
michael@0 466 * response header as well as the response header content on the the NetworkPanel.
michael@0 467 *
michael@0 468 * @returns void
michael@0 469 */
michael@0 470 _displayResponseHeader: function NP__displayResponseHeader()
michael@0 471 {
michael@0 472 let timing = this.httpActivity.timings;
michael@0 473 let response = this.httpActivity.response;
michael@0 474
michael@0 475 this._appendTextNode("headStatus",
michael@0 476 [response.httpVersion, response.status,
michael@0 477 response.statusText].join(" "));
michael@0 478
michael@0 479 // Calculate how much time it took from the request start, until the
michael@0 480 // response started to be received.
michael@0 481 let deltaDuration = 0;
michael@0 482 ["dns", "connect", "send", "wait"].forEach(function (aValue) {
michael@0 483 let ms = timing[aValue];
michael@0 484 if (ms > -1) {
michael@0 485 deltaDuration += ms;
michael@0 486 }
michael@0 487 });
michael@0 488
michael@0 489 this._appendTextNode("responseHeadersInfo",
michael@0 490 this._format("durationMS", [deltaDuration]));
michael@0 491
michael@0 492 this._displayNode("responseContainer");
michael@0 493 this._appendList("responseHeadersContent", response.headers, true);
michael@0 494
michael@0 495 if (response.cookies.length > 0) {
michael@0 496 this._displayNode("responseCookie");
michael@0 497 this._appendList("responseCookieContent", response.cookies);
michael@0 498 }
michael@0 499 },
michael@0 500
michael@0 501 /**
michael@0 502 * Displays the respones image section, sets the source of the image displayed
michael@0 503 * in the image response section to the request URL and the duration between
michael@0 504 * the receiving of the response header and the end of the request. Once the
michael@0 505 * image is loaded, the size of the requested image is set.
michael@0 506 *
michael@0 507 * @returns void
michael@0 508 */
michael@0 509 _displayResponseImage: function NP__displayResponseImage()
michael@0 510 {
michael@0 511 let self = this;
michael@0 512 let timing = this.httpActivity.timings;
michael@0 513 let request = this.httpActivity.request;
michael@0 514 let response = this.httpActivity.response;
michael@0 515 let cached = "";
michael@0 516
michael@0 517 if (this._isResponseCached) {
michael@0 518 cached = "Cached";
michael@0 519 }
michael@0 520
michael@0 521 let imageNode = this.document.getElementById("responseImage" +
michael@0 522 cached + "Node");
michael@0 523
michael@0 524 let text = response.content.text;
michael@0 525 if (typeof text == "object" && text.type == "longString") {
michael@0 526 this._showResponseBodyFetchLink();
michael@0 527 }
michael@0 528 else {
michael@0 529 imageNode.setAttribute("src",
michael@0 530 "data:" + this.contentType + ";base64," + text);
michael@0 531 }
michael@0 532
michael@0 533 // This function is called to set the imageInfo.
michael@0 534 function setImageInfo() {
michael@0 535 self._appendTextNode("responseImage" + cached + "Info",
michael@0 536 self._format("imageSizeDeltaDurationMS",
michael@0 537 [ imageNode.width, imageNode.height, timing.receive ]
michael@0 538 )
michael@0 539 );
michael@0 540 }
michael@0 541
michael@0 542 // Check if the image is already loaded.
michael@0 543 if (imageNode.width != 0) {
michael@0 544 setImageInfo();
michael@0 545 }
michael@0 546 else {
michael@0 547 // Image is not loaded yet therefore add a load event.
michael@0 548 imageNode.addEventListener("load", function imageNodeLoad() {
michael@0 549 imageNode.removeEventListener("load", imageNodeLoad, false);
michael@0 550 setImageInfo();
michael@0 551 }, false);
michael@0 552 }
michael@0 553
michael@0 554 this._displayNode("responseImage" + cached);
michael@0 555 },
michael@0 556
michael@0 557 /**
michael@0 558 * Displays the response body section, sets the the duration between
michael@0 559 * the receiving of the response header and the end of the request as well as
michael@0 560 * the content of the response body on the NetworkPanel.
michael@0 561 *
michael@0 562 * @returns void
michael@0 563 */
michael@0 564 _displayResponseBody: function NP__displayResponseBody()
michael@0 565 {
michael@0 566 let timing = this.httpActivity.timings;
michael@0 567 let response = this.httpActivity.response;
michael@0 568 let cached = this._isResponseCached ? "Cached" : "";
michael@0 569
michael@0 570 this._appendTextNode("responseBody" + cached + "Info",
michael@0 571 this._format("durationMS", [timing.receive]));
michael@0 572
michael@0 573 this._displayNode("responseBody" + cached);
michael@0 574
michael@0 575 let text = response.content.text;
michael@0 576 if (typeof text == "object") {
michael@0 577 text = text.initial;
michael@0 578 this._showResponseBodyFetchLink();
michael@0 579 }
michael@0 580
michael@0 581 this._appendTextNode("responseBody" + cached + "Content", text);
michael@0 582 },
michael@0 583
michael@0 584 /**
michael@0 585 * Show the "fetch response body" link.
michael@0 586 * @private
michael@0 587 */
michael@0 588 _showResponseBodyFetchLink: function NP__showResponseBodyFetchLink()
michael@0 589 {
michael@0 590 let content = this.httpActivity.response.content;
michael@0 591
michael@0 592 let elem = this._appendTextNode("responseBodyFetchLink",
michael@0 593 this._format("fetchRemainingResponseContentLink",
michael@0 594 [content.text.length - content.text.initial.length]));
michael@0 595
michael@0 596 elem.style.display = "block";
michael@0 597 elem.addEventListener("mousedown", this._responseBodyFetch);
michael@0 598 },
michael@0 599
michael@0 600 /**
michael@0 601 * Click event handler for the link that allows users to fetch the remaining
michael@0 602 * response body.
michael@0 603 *
michael@0 604 * @private
michael@0 605 * @param nsIDOMEvent aEvent
michael@0 606 */
michael@0 607 _responseBodyFetch: function NP__responseBodyFetch(aEvent)
michael@0 608 {
michael@0 609 aEvent.target.style.display = "none";
michael@0 610 aEvent.target.removeEventListener("mousedown", this._responseBodyFetch);
michael@0 611
michael@0 612 let content = this.httpActivity.response.content;
michael@0 613 let longString = this.webconsole.webConsoleClient.longString(content.text);
michael@0 614 longString.substring(longString.initial.length, longString.length,
michael@0 615 function NP__onLongStringSubstring(aResponse)
michael@0 616 {
michael@0 617 if (aResponse.error) {
michael@0 618 Cu.reportError("NP__onLongStringSubstring error: " + aResponse.error);
michael@0 619 return;
michael@0 620 }
michael@0 621
michael@0 622 content.text = content.text.initial + aResponse.substring;
michael@0 623 let cached = this._isResponseCached ? "Cached" : "";
michael@0 624
michael@0 625 if (this._responseIsImage) {
michael@0 626 let imageNode = this.document.getElementById("responseImage" +
michael@0 627 cached + "Node");
michael@0 628 imageNode.src =
michael@0 629 "data:" + this.contentType + ";base64," + content.text;
michael@0 630 }
michael@0 631 else {
michael@0 632 this._appendTextNode("responseBody" + cached + "Content",
michael@0 633 aResponse.substring);
michael@0 634 }
michael@0 635 }.bind(this));
michael@0 636 },
michael@0 637
michael@0 638 /**
michael@0 639 * Displays the `Unknown Content-Type hint` and sets the duration between the
michael@0 640 * receiving of the response header on the NetworkPanel.
michael@0 641 *
michael@0 642 * @returns void
michael@0 643 */
michael@0 644 _displayResponseBodyUnknownType: function NP__displayResponseBodyUnknownType()
michael@0 645 {
michael@0 646 let timing = this.httpActivity.timings;
michael@0 647
michael@0 648 this._displayNode("responseBodyUnknownType");
michael@0 649 this._appendTextNode("responseBodyUnknownTypeInfo",
michael@0 650 this._format("durationMS", [timing.receive]));
michael@0 651
michael@0 652 this._appendTextNode("responseBodyUnknownTypeContent",
michael@0 653 this._format("responseBodyUnableToDisplay.content", [this.contentType]));
michael@0 654 },
michael@0 655
michael@0 656 /**
michael@0 657 * Displays the `no response body` section and sets the the duration between
michael@0 658 * the receiving of the response header and the end of the request.
michael@0 659 *
michael@0 660 * @returns void
michael@0 661 */
michael@0 662 _displayNoResponseBody: function NP_displayNoResponseBody()
michael@0 663 {
michael@0 664 let timing = this.httpActivity.timings;
michael@0 665
michael@0 666 this._displayNode("responseNoBody");
michael@0 667 this._appendTextNode("responseNoBodyInfo",
michael@0 668 this._format("durationMS", [timing.receive]));
michael@0 669 },
michael@0 670
michael@0 671 /**
michael@0 672 * Updates the content of the NetworkPanel's iframe.
michael@0 673 *
michael@0 674 * @returns void
michael@0 675 */
michael@0 676 update: function NP_update()
michael@0 677 {
michael@0 678 if (!this.document || this.document.readyState != "complete") {
michael@0 679 return;
michael@0 680 }
michael@0 681
michael@0 682 let updates = this.httpActivity.updates;
michael@0 683 let timing = this.httpActivity.timings;
michael@0 684 let request = this.httpActivity.request;
michael@0 685 let response = this.httpActivity.response;
michael@0 686
michael@0 687 switch (this._state) {
michael@0 688 case this._INIT:
michael@0 689 this._displayRequestHeader();
michael@0 690 this._state = this._DISPLAYED_REQUEST_HEADER;
michael@0 691 // FALL THROUGH
michael@0 692
michael@0 693 case this._DISPLAYED_REQUEST_HEADER:
michael@0 694 // Process the request body if there is one.
michael@0 695 if (!this.httpActivity.discardRequestBody && request.postData.text) {
michael@0 696 this._updateRequestBody();
michael@0 697 this._state = this._DISPLAYED_REQUEST_BODY;
michael@0 698 }
michael@0 699 // FALL THROUGH
michael@0 700
michael@0 701 case this._DISPLAYED_REQUEST_BODY:
michael@0 702 if (!response.headers.length || !Object.keys(timing).length) {
michael@0 703 break;
michael@0 704 }
michael@0 705 this._displayResponseHeader();
michael@0 706 this._state = this._DISPLAYED_RESPONSE_HEADER;
michael@0 707 // FALL THROUGH
michael@0 708
michael@0 709 case this._DISPLAYED_RESPONSE_HEADER:
michael@0 710 if (updates.indexOf("responseContent") == -1 ||
michael@0 711 updates.indexOf("eventTimings") == -1) {
michael@0 712 break;
michael@0 713 }
michael@0 714
michael@0 715 this._state = this._TRANSITION_CLOSED;
michael@0 716 if (this.httpActivity.discardResponseBody) {
michael@0 717 break;
michael@0 718 }
michael@0 719
michael@0 720 if (!response.content || !response.content.text) {
michael@0 721 this._displayNoResponseBody();
michael@0 722 }
michael@0 723 else if (this._responseIsImage) {
michael@0 724 this._displayResponseImage();
michael@0 725 }
michael@0 726 else if (!this._isResponseBodyTextData) {
michael@0 727 this._displayResponseBodyUnknownType();
michael@0 728 }
michael@0 729 else if (response.content.text) {
michael@0 730 this._displayResponseBody();
michael@0 731 }
michael@0 732 break;
michael@0 733 }
michael@0 734
michael@0 735 if (this._onUpdate) {
michael@0 736 this._onUpdate();
michael@0 737 }
michael@0 738 },
michael@0 739
michael@0 740 /**
michael@0 741 * Update the panel to hold the current information we have about the request
michael@0 742 * body.
michael@0 743 * @private
michael@0 744 */
michael@0 745 _updateRequestBody: function NP__updateRequestBody()
michael@0 746 {
michael@0 747 let postData = this.httpActivity.request.postData;
michael@0 748 if (typeof postData.text == "object" && postData.text.type == "longString") {
michael@0 749 let elem = this._appendTextNode("requestBodyFetchLink",
michael@0 750 this._format("fetchRemainingRequestContentLink",
michael@0 751 [postData.text.length - postData.text.initial.length]));
michael@0 752
michael@0 753 elem.style.display = "block";
michael@0 754 elem.addEventListener("mousedown", this._requestBodyFetch);
michael@0 755 return;
michael@0 756 }
michael@0 757
michael@0 758 // Check if we send some form data. If so, display the form data special.
michael@0 759 if (this._isRequestBodyFormData) {
michael@0 760 this._displayRequestForm();
michael@0 761 }
michael@0 762 else {
michael@0 763 this._displayRequestBody();
michael@0 764 }
michael@0 765 },
michael@0 766
michael@0 767 /**
michael@0 768 * Click event handler for the link that allows users to fetch the remaining
michael@0 769 * request body.
michael@0 770 *
michael@0 771 * @private
michael@0 772 * @param nsIDOMEvent aEvent
michael@0 773 */
michael@0 774 _requestBodyFetch: function NP__requestBodyFetch(aEvent)
michael@0 775 {
michael@0 776 aEvent.target.style.display = "none";
michael@0 777 aEvent.target.removeEventListener("mousedown", this._responseBodyFetch);
michael@0 778
michael@0 779 let postData = this.httpActivity.request.postData;
michael@0 780 let longString = this.webconsole.webConsoleClient.longString(postData.text);
michael@0 781 longString.substring(longString.initial.length, longString.length,
michael@0 782 function NP__onLongStringSubstring(aResponse)
michael@0 783 {
michael@0 784 if (aResponse.error) {
michael@0 785 Cu.reportError("NP__onLongStringSubstring error: " + aResponse.error);
michael@0 786 return;
michael@0 787 }
michael@0 788
michael@0 789 postData.text = postData.text.initial + aResponse.substring;
michael@0 790 this._updateRequestBody();
michael@0 791 }.bind(this));
michael@0 792 },
michael@0 793 };
michael@0 794
michael@0 795 /**
michael@0 796 * Creates a DOMNode and sets all the attributes of aAttributes on the created
michael@0 797 * element.
michael@0 798 *
michael@0 799 * @param nsIDOMDocument aDocument
michael@0 800 * Document to create the new DOMNode.
michael@0 801 * @param string aTag
michael@0 802 * Name of the tag for the DOMNode.
michael@0 803 * @param object aAttributes
michael@0 804 * Attributes set on the created DOMNode.
michael@0 805 *
michael@0 806 * @returns nsIDOMNode
michael@0 807 */
michael@0 808 function createElement(aDocument, aTag, aAttributes)
michael@0 809 {
michael@0 810 let node = aDocument.createElement(aTag);
michael@0 811 if (aAttributes) {
michael@0 812 for (let attr in aAttributes) {
michael@0 813 node.setAttribute(attr, aAttributes[attr]);
michael@0 814 }
michael@0 815 }
michael@0 816 return node;
michael@0 817 }
michael@0 818
michael@0 819 /**
michael@0 820 * Creates a new DOMNode and appends it to aParent.
michael@0 821 *
michael@0 822 * @param nsIDOMNode aParent
michael@0 823 * A parent node to append the created element.
michael@0 824 * @param string aTag
michael@0 825 * Name of the tag for the DOMNode.
michael@0 826 * @param object aAttributes
michael@0 827 * Attributes set on the created DOMNode.
michael@0 828 *
michael@0 829 * @returns nsIDOMNode
michael@0 830 */
michael@0 831 function createAndAppendElement(aParent, aTag, aAttributes)
michael@0 832 {
michael@0 833 let node = createElement(aParent.ownerDocument, aTag, aAttributes);
michael@0 834 aParent.appendChild(node);
michael@0 835 return node;
michael@0 836 }

mercurial