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.

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

mercurial