michael@0: /* -*- js2-basic-offset: 2; indent-tabs-mode: nil; -*- */ michael@0: /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const {Cc, Ci, Cu} = require("chrome"); michael@0: michael@0: loader.lazyGetter(this, "NetworkHelper", () => require("devtools/toolkit/webconsole/network-helper")); michael@0: loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); michael@0: loader.lazyServiceGetter(this, "mimeService", "@mozilla.org/mime;1", "nsIMIMEService"); michael@0: michael@0: let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils; michael@0: michael@0: const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties"; michael@0: let l10n = new WebConsoleUtils.l10n(STRINGS_URI); michael@0: michael@0: michael@0: /** michael@0: * Creates a new NetworkPanel. michael@0: * michael@0: * @constructor michael@0: * @param nsIDOMNode aParent michael@0: * Parent node to append the created panel to. michael@0: * @param object aHttpActivity michael@0: * HttpActivity to display in the panel. michael@0: * @param object aWebConsoleFrame michael@0: * The parent WebConsoleFrame object that owns this network panel michael@0: * instance. michael@0: */ michael@0: function NetworkPanel(aParent, aHttpActivity, aWebConsoleFrame) michael@0: { michael@0: let doc = aParent.ownerDocument; michael@0: this.httpActivity = aHttpActivity; michael@0: this.webconsole = aWebConsoleFrame; michael@0: this._longStringClick = this._longStringClick.bind(this); michael@0: this._responseBodyFetch = this._responseBodyFetch.bind(this); michael@0: this._requestBodyFetch = this._requestBodyFetch.bind(this); michael@0: michael@0: // Create the underlaying panel michael@0: this.panel = createElement(doc, "panel", { michael@0: label: l10n.getStr("NetworkPanel.label"), michael@0: titlebar: "normal", michael@0: noautofocus: "true", michael@0: noautohide: "true", michael@0: close: "true" michael@0: }); michael@0: michael@0: // Create the iframe that displays the NetworkPanel XHTML. michael@0: this.iframe = createAndAppendElement(this.panel, "iframe", { michael@0: src: "chrome://browser/content/devtools/NetworkPanel.xhtml", michael@0: type: "content", michael@0: flex: "1" michael@0: }); michael@0: michael@0: let self = this; michael@0: michael@0: // Destroy the panel when it's closed. michael@0: this.panel.addEventListener("popuphidden", function onPopupHide() { michael@0: self.panel.removeEventListener("popuphidden", onPopupHide, false); michael@0: self.panel.parentNode.removeChild(self.panel); michael@0: self.panel = null; michael@0: self.iframe = null; michael@0: self.httpActivity = null; michael@0: self.webconsole = null; michael@0: michael@0: if (self.linkNode) { michael@0: self.linkNode._panelOpen = false; michael@0: self.linkNode = null; michael@0: } michael@0: }, false); michael@0: michael@0: // Set the document object and update the content once the panel is loaded. michael@0: this.iframe.addEventListener("load", function onLoad() { michael@0: if (!self.iframe) { michael@0: return; michael@0: } michael@0: michael@0: self.iframe.removeEventListener("load", onLoad, true); michael@0: self.update(); michael@0: }, true); michael@0: michael@0: this.panel.addEventListener("popupshown", function onPopupShown() { michael@0: self.panel.removeEventListener("popupshown", onPopupShown, true); michael@0: self.update(); michael@0: }, true); michael@0: michael@0: // Create the footer. michael@0: let footer = createElement(doc, "hbox", { align: "end" }); michael@0: createAndAppendElement(footer, "spacer", { flex: 1 }); michael@0: michael@0: createAndAppendElement(footer, "resizer", { dir: "bottomend" }); michael@0: this.panel.appendChild(footer); michael@0: michael@0: aParent.appendChild(this.panel); michael@0: } michael@0: exports.NetworkPanel = NetworkPanel; michael@0: michael@0: NetworkPanel.prototype = michael@0: { michael@0: /** michael@0: * The current state of the output. michael@0: */ michael@0: _state: 0, michael@0: michael@0: /** michael@0: * State variables. michael@0: */ michael@0: _INIT: 0, michael@0: _DISPLAYED_REQUEST_HEADER: 1, michael@0: _DISPLAYED_REQUEST_BODY: 2, michael@0: _DISPLAYED_RESPONSE_HEADER: 3, michael@0: _TRANSITION_CLOSED: 4, michael@0: michael@0: _fromDataRegExp: /Content-Type\:\s*application\/x-www-form-urlencoded/, michael@0: michael@0: _contentType: null, michael@0: michael@0: /** michael@0: * Function callback invoked whenever the panel content is updated. This is michael@0: * used only by tests. michael@0: * michael@0: * @private michael@0: * @type function michael@0: */ michael@0: _onUpdate: null, michael@0: michael@0: get document() { michael@0: return this.iframe && this.iframe.contentWindow ? michael@0: this.iframe.contentWindow.document : null; michael@0: }, michael@0: michael@0: /** michael@0: * Small helper function that is nearly equal to l10n.getFormatStr michael@0: * except that it prefixes aName with "NetworkPanel.". michael@0: * michael@0: * @param string aName michael@0: * The name of an i10n string to format. This string is prefixed with michael@0: * "NetworkPanel." before calling the HUDService.getFormatStr function. michael@0: * @param array aArray michael@0: * Values used as placeholder for the i10n string. michael@0: * @returns string michael@0: * The i10n formated string. michael@0: */ michael@0: _format: function NP_format(aName, aArray) michael@0: { michael@0: return l10n.getFormatStr("NetworkPanel." + aName, aArray); michael@0: }, michael@0: michael@0: /** michael@0: * Returns the content type of the response body. This is based on the michael@0: * response.content.mimeType property. If this value is not available, then michael@0: * the content type is guessed by the file extension of the request URL. michael@0: * michael@0: * @return string michael@0: * Content type or empty string if no content type could be figured michael@0: * out. michael@0: */ michael@0: get contentType() michael@0: { michael@0: if (this._contentType) { michael@0: return this._contentType; michael@0: } michael@0: michael@0: let request = this.httpActivity.request; michael@0: let response = this.httpActivity.response; michael@0: michael@0: let contentType = ""; michael@0: let types = response.content ? michael@0: (response.content.mimeType || "").split(/,|;/) : []; michael@0: for (let i = 0; i < types.length; i++) { michael@0: if (types[i] in NetworkHelper.mimeCategoryMap) { michael@0: contentType = types[i]; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (contentType) { michael@0: this._contentType = contentType; michael@0: return contentType; michael@0: } michael@0: michael@0: // Try to get the content type from the request file extension. michael@0: let uri = NetUtil.newURI(request.url); michael@0: if ((uri instanceof Ci.nsIURL) && uri.fileExtension) { michael@0: try { michael@0: contentType = mimeService.getTypeFromExtension(uri.fileExtension); michael@0: } michael@0: catch(ex) { michael@0: // Added to prevent failures on OS X 64. No Flash? michael@0: Cu.reportError(ex); michael@0: } michael@0: } michael@0: michael@0: this._contentType = contentType; michael@0: return contentType; michael@0: }, michael@0: michael@0: /** michael@0: * michael@0: * @returns boolean michael@0: * True if the response is an image, false otherwise. michael@0: */ michael@0: get _responseIsImage() michael@0: { michael@0: return this.contentType && michael@0: NetworkHelper.mimeCategoryMap[this.contentType] == "image"; michael@0: }, michael@0: michael@0: /** michael@0: * michael@0: * @returns boolean michael@0: * True if the response body contains text, false otherwise. michael@0: */ michael@0: get _isResponseBodyTextData() michael@0: { michael@0: return this.contentType ? michael@0: NetworkHelper.isTextMimeType(this.contentType) : false; michael@0: }, michael@0: michael@0: /** michael@0: * Tells if the server response is cached. michael@0: * michael@0: * @returns boolean michael@0: * Returns true if the server responded that the request is already michael@0: * in the browser's cache, false otherwise. michael@0: */ michael@0: get _isResponseCached() michael@0: { michael@0: return this.httpActivity.response.status == 304; michael@0: }, michael@0: michael@0: /** michael@0: * Tells if the request body includes form data. michael@0: * michael@0: * @returns boolean michael@0: * Returns true if the posted body contains form data. michael@0: */ michael@0: get _isRequestBodyFormData() michael@0: { michael@0: let requestBody = this.httpActivity.request.postData.text; michael@0: if (typeof requestBody == "object" && requestBody.type == "longString") { michael@0: requestBody = requestBody.initial; michael@0: } michael@0: return this._fromDataRegExp.test(requestBody); michael@0: }, michael@0: michael@0: /** michael@0: * Appends the node with id=aId by the text aValue. michael@0: * michael@0: * @private michael@0: * @param string aId michael@0: * @param string aValue michael@0: * @return nsIDOMElement michael@0: * The DOM element with id=aId. michael@0: */ michael@0: _appendTextNode: function NP__appendTextNode(aId, aValue) michael@0: { michael@0: let textNode = this.document.createTextNode(aValue); michael@0: let elem = this.document.getElementById(aId); michael@0: elem.appendChild(textNode); michael@0: return elem; michael@0: }, michael@0: michael@0: /** michael@0: * Generates some HTML to display the key-value pair of the aList data. The michael@0: * generated HTML is added to node with id=aParentId. michael@0: * michael@0: * @param string aParentId michael@0: * Id of the parent node to append the list to. michael@0: * @oaram array aList michael@0: * Array that holds the objects you want to display. Each object must michael@0: * have two properties: name and value. michael@0: * @param boolean aIgnoreCookie michael@0: * If true, the key-value named "Cookie" is not added to the list. michael@0: * @returns void michael@0: */ michael@0: _appendList: function NP_appendList(aParentId, aList, aIgnoreCookie) michael@0: { michael@0: let parent = this.document.getElementById(aParentId); michael@0: let doc = this.document; michael@0: michael@0: aList.sort(function(a, b) { michael@0: return a.name.toLowerCase() < b.name.toLowerCase(); michael@0: }); michael@0: michael@0: aList.forEach(function(aItem) { michael@0: let name = aItem.name; michael@0: if (aIgnoreCookie && (name == "Cookie" || name == "Set-Cookie")) { michael@0: return; michael@0: } michael@0: michael@0: let value = aItem.value; michael@0: let longString = null; michael@0: if (typeof value == "object" && value.type == "longString") { michael@0: value = value.initial; michael@0: longString = true; michael@0: } michael@0: michael@0: /** michael@0: * The following code creates the HTML: michael@0: * michael@0: * ${line}: michael@0: * ${aList[line]} michael@0: * michael@0: * and adds it to parent. michael@0: */ michael@0: let row = doc.createElement("tr"); michael@0: let textNode = doc.createTextNode(name + ":"); michael@0: let th = doc.createElement("th"); michael@0: th.setAttribute("scope", "row"); michael@0: th.setAttribute("class", "property-name"); michael@0: th.appendChild(textNode); michael@0: row.appendChild(th); michael@0: michael@0: textNode = doc.createTextNode(value); michael@0: let td = doc.createElement("td"); michael@0: td.setAttribute("class", "property-value"); michael@0: td.appendChild(textNode); michael@0: michael@0: if (longString) { michael@0: let a = doc.createElement("a"); michael@0: a.href = "#"; michael@0: a.className = "longStringEllipsis"; michael@0: a.addEventListener("mousedown", this._longStringClick.bind(this, aItem)); michael@0: a.textContent = l10n.getStr("longStringEllipsis"); michael@0: td.appendChild(a); michael@0: } michael@0: michael@0: row.appendChild(td); michael@0: michael@0: parent.appendChild(row); michael@0: }.bind(this)); michael@0: }, michael@0: michael@0: /** michael@0: * The click event handler for the ellipsis which allows the user to retrieve michael@0: * the full header value. michael@0: * michael@0: * @private michael@0: * @param object aHeader michael@0: * The header object with the |name| and |value| properties. michael@0: * @param nsIDOMEvent aEvent michael@0: * The DOM click event object. michael@0: */ michael@0: _longStringClick: function NP__longStringClick(aHeader, aEvent) michael@0: { michael@0: aEvent.preventDefault(); michael@0: michael@0: let longString = this.webconsole.webConsoleClient.longString(aHeader.value); michael@0: michael@0: longString.substring(longString.initial.length, longString.length, michael@0: function NP__onLongStringSubstring(aResponse) michael@0: { michael@0: if (aResponse.error) { michael@0: Cu.reportError("NP__onLongStringSubstring error: " + aResponse.error); michael@0: return; michael@0: } michael@0: michael@0: aHeader.value = aHeader.value.initial + aResponse.substring; michael@0: michael@0: let textNode = aEvent.target.previousSibling; michael@0: textNode.textContent += aResponse.substring; michael@0: textNode.parentNode.removeChild(aEvent.target); michael@0: }); michael@0: }, michael@0: michael@0: /** michael@0: * Displays the node with id=aId. michael@0: * michael@0: * @private michael@0: * @param string aId michael@0: * @return nsIDOMElement michael@0: * The element with id=aId. michael@0: */ michael@0: _displayNode: function NP__displayNode(aId) michael@0: { michael@0: let elem = this.document.getElementById(aId); michael@0: elem.style.display = "block"; michael@0: }, michael@0: michael@0: /** michael@0: * Sets the request URL, request method, the timing information when the michael@0: * request started and the request header content on the NetworkPanel. michael@0: * If the request header contains cookie data, a list of sent cookies is michael@0: * generated and a special sent cookie section is displayed + the cookie list michael@0: * added to it. michael@0: * michael@0: * @returns void michael@0: */ michael@0: _displayRequestHeader: function NP__displayRequestHeader() michael@0: { michael@0: let request = this.httpActivity.request; michael@0: let requestTime = new Date(this.httpActivity.startedDateTime); michael@0: michael@0: this._appendTextNode("headUrl", request.url); michael@0: this._appendTextNode("headMethod", request.method); michael@0: this._appendTextNode("requestHeadersInfo", michael@0: l10n.timestampString(requestTime)); michael@0: michael@0: this._appendList("requestHeadersContent", request.headers, true); michael@0: michael@0: if (request.cookies.length > 0) { michael@0: this._displayNode("requestCookie"); michael@0: this._appendList("requestCookieContent", request.cookies); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Displays the request body section of the NetworkPanel and set the request michael@0: * body content on the NetworkPanel. michael@0: * michael@0: * @returns void michael@0: */ michael@0: _displayRequestBody: function NP__displayRequestBody() michael@0: { michael@0: let postData = this.httpActivity.request.postData; michael@0: this._displayNode("requestBody"); michael@0: this._appendTextNode("requestBodyContent", postData.text); michael@0: }, michael@0: michael@0: /* michael@0: * Displays the `sent form data` section. Parses the request header for the michael@0: * submitted form data displays it inside of the `sent form data` section. michael@0: * michael@0: * @returns void michael@0: */ michael@0: _displayRequestForm: function NP__processRequestForm() michael@0: { michael@0: let postData = this.httpActivity.request.postData.text; michael@0: let requestBodyLines = postData.split("\n"); michael@0: let formData = requestBodyLines[requestBodyLines.length - 1]. michael@0: replace(/\+/g, " ").split("&"); michael@0: michael@0: function unescapeText(aText) michael@0: { michael@0: try { michael@0: return decodeURIComponent(aText); michael@0: } michael@0: catch (ex) { michael@0: return decodeURIComponent(unescape(aText)); michael@0: } michael@0: } michael@0: michael@0: let formDataArray = []; michael@0: for (let i = 0; i < formData.length; i++) { michael@0: let data = formData[i]; michael@0: let idx = data.indexOf("="); michael@0: let key = data.substring(0, idx); michael@0: let value = data.substring(idx + 1); michael@0: formDataArray.push({ michael@0: name: unescapeText(key), michael@0: value: unescapeText(value) michael@0: }); michael@0: } michael@0: michael@0: this._appendList("requestFormDataContent", formDataArray); michael@0: this._displayNode("requestFormData"); michael@0: }, michael@0: michael@0: /** michael@0: * Displays the response section of the NetworkPanel, sets the response status, michael@0: * the duration between the start of the request and the receiving of the michael@0: * response header as well as the response header content on the the NetworkPanel. michael@0: * michael@0: * @returns void michael@0: */ michael@0: _displayResponseHeader: function NP__displayResponseHeader() michael@0: { michael@0: let timing = this.httpActivity.timings; michael@0: let response = this.httpActivity.response; michael@0: michael@0: this._appendTextNode("headStatus", michael@0: [response.httpVersion, response.status, michael@0: response.statusText].join(" ")); michael@0: michael@0: // Calculate how much time it took from the request start, until the michael@0: // response started to be received. michael@0: let deltaDuration = 0; michael@0: ["dns", "connect", "send", "wait"].forEach(function (aValue) { michael@0: let ms = timing[aValue]; michael@0: if (ms > -1) { michael@0: deltaDuration += ms; michael@0: } michael@0: }); michael@0: michael@0: this._appendTextNode("responseHeadersInfo", michael@0: this._format("durationMS", [deltaDuration])); michael@0: michael@0: this._displayNode("responseContainer"); michael@0: this._appendList("responseHeadersContent", response.headers, true); michael@0: michael@0: if (response.cookies.length > 0) { michael@0: this._displayNode("responseCookie"); michael@0: this._appendList("responseCookieContent", response.cookies); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Displays the respones image section, sets the source of the image displayed michael@0: * in the image response section to the request URL and the duration between michael@0: * the receiving of the response header and the end of the request. Once the michael@0: * image is loaded, the size of the requested image is set. michael@0: * michael@0: * @returns void michael@0: */ michael@0: _displayResponseImage: function NP__displayResponseImage() michael@0: { michael@0: let self = this; michael@0: let timing = this.httpActivity.timings; michael@0: let request = this.httpActivity.request; michael@0: let response = this.httpActivity.response; michael@0: let cached = ""; michael@0: michael@0: if (this._isResponseCached) { michael@0: cached = "Cached"; michael@0: } michael@0: michael@0: let imageNode = this.document.getElementById("responseImage" + michael@0: cached + "Node"); michael@0: michael@0: let text = response.content.text; michael@0: if (typeof text == "object" && text.type == "longString") { michael@0: this._showResponseBodyFetchLink(); michael@0: } michael@0: else { michael@0: imageNode.setAttribute("src", michael@0: "data:" + this.contentType + ";base64," + text); michael@0: } michael@0: michael@0: // This function is called to set the imageInfo. michael@0: function setImageInfo() { michael@0: self._appendTextNode("responseImage" + cached + "Info", michael@0: self._format("imageSizeDeltaDurationMS", michael@0: [ imageNode.width, imageNode.height, timing.receive ] michael@0: ) michael@0: ); michael@0: } michael@0: michael@0: // Check if the image is already loaded. michael@0: if (imageNode.width != 0) { michael@0: setImageInfo(); michael@0: } michael@0: else { michael@0: // Image is not loaded yet therefore add a load event. michael@0: imageNode.addEventListener("load", function imageNodeLoad() { michael@0: imageNode.removeEventListener("load", imageNodeLoad, false); michael@0: setImageInfo(); michael@0: }, false); michael@0: } michael@0: michael@0: this._displayNode("responseImage" + cached); michael@0: }, michael@0: michael@0: /** michael@0: * Displays the response body section, sets the the duration between michael@0: * the receiving of the response header and the end of the request as well as michael@0: * the content of the response body on the NetworkPanel. michael@0: * michael@0: * @returns void michael@0: */ michael@0: _displayResponseBody: function NP__displayResponseBody() michael@0: { michael@0: let timing = this.httpActivity.timings; michael@0: let response = this.httpActivity.response; michael@0: let cached = this._isResponseCached ? "Cached" : ""; michael@0: michael@0: this._appendTextNode("responseBody" + cached + "Info", michael@0: this._format("durationMS", [timing.receive])); michael@0: michael@0: this._displayNode("responseBody" + cached); michael@0: michael@0: let text = response.content.text; michael@0: if (typeof text == "object") { michael@0: text = text.initial; michael@0: this._showResponseBodyFetchLink(); michael@0: } michael@0: michael@0: this._appendTextNode("responseBody" + cached + "Content", text); michael@0: }, michael@0: michael@0: /** michael@0: * Show the "fetch response body" link. michael@0: * @private michael@0: */ michael@0: _showResponseBodyFetchLink: function NP__showResponseBodyFetchLink() michael@0: { michael@0: let content = this.httpActivity.response.content; michael@0: michael@0: let elem = this._appendTextNode("responseBodyFetchLink", michael@0: this._format("fetchRemainingResponseContentLink", michael@0: [content.text.length - content.text.initial.length])); michael@0: michael@0: elem.style.display = "block"; michael@0: elem.addEventListener("mousedown", this._responseBodyFetch); michael@0: }, michael@0: michael@0: /** michael@0: * Click event handler for the link that allows users to fetch the remaining michael@0: * response body. michael@0: * michael@0: * @private michael@0: * @param nsIDOMEvent aEvent michael@0: */ michael@0: _responseBodyFetch: function NP__responseBodyFetch(aEvent) michael@0: { michael@0: aEvent.target.style.display = "none"; michael@0: aEvent.target.removeEventListener("mousedown", this._responseBodyFetch); michael@0: michael@0: let content = this.httpActivity.response.content; michael@0: let longString = this.webconsole.webConsoleClient.longString(content.text); michael@0: longString.substring(longString.initial.length, longString.length, michael@0: function NP__onLongStringSubstring(aResponse) michael@0: { michael@0: if (aResponse.error) { michael@0: Cu.reportError("NP__onLongStringSubstring error: " + aResponse.error); michael@0: return; michael@0: } michael@0: michael@0: content.text = content.text.initial + aResponse.substring; michael@0: let cached = this._isResponseCached ? "Cached" : ""; michael@0: michael@0: if (this._responseIsImage) { michael@0: let imageNode = this.document.getElementById("responseImage" + michael@0: cached + "Node"); michael@0: imageNode.src = michael@0: "data:" + this.contentType + ";base64," + content.text; michael@0: } michael@0: else { michael@0: this._appendTextNode("responseBody" + cached + "Content", michael@0: aResponse.substring); michael@0: } michael@0: }.bind(this)); michael@0: }, michael@0: michael@0: /** michael@0: * Displays the `Unknown Content-Type hint` and sets the duration between the michael@0: * receiving of the response header on the NetworkPanel. michael@0: * michael@0: * @returns void michael@0: */ michael@0: _displayResponseBodyUnknownType: function NP__displayResponseBodyUnknownType() michael@0: { michael@0: let timing = this.httpActivity.timings; michael@0: michael@0: this._displayNode("responseBodyUnknownType"); michael@0: this._appendTextNode("responseBodyUnknownTypeInfo", michael@0: this._format("durationMS", [timing.receive])); michael@0: michael@0: this._appendTextNode("responseBodyUnknownTypeContent", michael@0: this._format("responseBodyUnableToDisplay.content", [this.contentType])); michael@0: }, michael@0: michael@0: /** michael@0: * Displays the `no response body` section and sets the the duration between michael@0: * the receiving of the response header and the end of the request. michael@0: * michael@0: * @returns void michael@0: */ michael@0: _displayNoResponseBody: function NP_displayNoResponseBody() michael@0: { michael@0: let timing = this.httpActivity.timings; michael@0: michael@0: this._displayNode("responseNoBody"); michael@0: this._appendTextNode("responseNoBodyInfo", michael@0: this._format("durationMS", [timing.receive])); michael@0: }, michael@0: michael@0: /** michael@0: * Updates the content of the NetworkPanel's iframe. michael@0: * michael@0: * @returns void michael@0: */ michael@0: update: function NP_update() michael@0: { michael@0: if (!this.document || this.document.readyState != "complete") { michael@0: return; michael@0: } michael@0: michael@0: let updates = this.httpActivity.updates; michael@0: let timing = this.httpActivity.timings; michael@0: let request = this.httpActivity.request; michael@0: let response = this.httpActivity.response; michael@0: michael@0: switch (this._state) { michael@0: case this._INIT: michael@0: this._displayRequestHeader(); michael@0: this._state = this._DISPLAYED_REQUEST_HEADER; michael@0: // FALL THROUGH michael@0: michael@0: case this._DISPLAYED_REQUEST_HEADER: michael@0: // Process the request body if there is one. michael@0: if (!this.httpActivity.discardRequestBody && request.postData.text) { michael@0: this._updateRequestBody(); michael@0: this._state = this._DISPLAYED_REQUEST_BODY; michael@0: } michael@0: // FALL THROUGH michael@0: michael@0: case this._DISPLAYED_REQUEST_BODY: michael@0: if (!response.headers.length || !Object.keys(timing).length) { michael@0: break; michael@0: } michael@0: this._displayResponseHeader(); michael@0: this._state = this._DISPLAYED_RESPONSE_HEADER; michael@0: // FALL THROUGH michael@0: michael@0: case this._DISPLAYED_RESPONSE_HEADER: michael@0: if (updates.indexOf("responseContent") == -1 || michael@0: updates.indexOf("eventTimings") == -1) { michael@0: break; michael@0: } michael@0: michael@0: this._state = this._TRANSITION_CLOSED; michael@0: if (this.httpActivity.discardResponseBody) { michael@0: break; michael@0: } michael@0: michael@0: if (!response.content || !response.content.text) { michael@0: this._displayNoResponseBody(); michael@0: } michael@0: else if (this._responseIsImage) { michael@0: this._displayResponseImage(); michael@0: } michael@0: else if (!this._isResponseBodyTextData) { michael@0: this._displayResponseBodyUnknownType(); michael@0: } michael@0: else if (response.content.text) { michael@0: this._displayResponseBody(); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: if (this._onUpdate) { michael@0: this._onUpdate(); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Update the panel to hold the current information we have about the request michael@0: * body. michael@0: * @private michael@0: */ michael@0: _updateRequestBody: function NP__updateRequestBody() michael@0: { michael@0: let postData = this.httpActivity.request.postData; michael@0: if (typeof postData.text == "object" && postData.text.type == "longString") { michael@0: let elem = this._appendTextNode("requestBodyFetchLink", michael@0: this._format("fetchRemainingRequestContentLink", michael@0: [postData.text.length - postData.text.initial.length])); michael@0: michael@0: elem.style.display = "block"; michael@0: elem.addEventListener("mousedown", this._requestBodyFetch); michael@0: return; michael@0: } michael@0: michael@0: // Check if we send some form data. If so, display the form data special. michael@0: if (this._isRequestBodyFormData) { michael@0: this._displayRequestForm(); michael@0: } michael@0: else { michael@0: this._displayRequestBody(); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Click event handler for the link that allows users to fetch the remaining michael@0: * request body. michael@0: * michael@0: * @private michael@0: * @param nsIDOMEvent aEvent michael@0: */ michael@0: _requestBodyFetch: function NP__requestBodyFetch(aEvent) michael@0: { michael@0: aEvent.target.style.display = "none"; michael@0: aEvent.target.removeEventListener("mousedown", this._responseBodyFetch); michael@0: michael@0: let postData = this.httpActivity.request.postData; michael@0: let longString = this.webconsole.webConsoleClient.longString(postData.text); michael@0: longString.substring(longString.initial.length, longString.length, michael@0: function NP__onLongStringSubstring(aResponse) michael@0: { michael@0: if (aResponse.error) { michael@0: Cu.reportError("NP__onLongStringSubstring error: " + aResponse.error); michael@0: return; michael@0: } michael@0: michael@0: postData.text = postData.text.initial + aResponse.substring; michael@0: this._updateRequestBody(); michael@0: }.bind(this)); michael@0: }, michael@0: }; michael@0: michael@0: /** michael@0: * Creates a DOMNode and sets all the attributes of aAttributes on the created michael@0: * element. michael@0: * michael@0: * @param nsIDOMDocument aDocument michael@0: * Document to create the new DOMNode. michael@0: * @param string aTag michael@0: * Name of the tag for the DOMNode. michael@0: * @param object aAttributes michael@0: * Attributes set on the created DOMNode. michael@0: * michael@0: * @returns nsIDOMNode michael@0: */ michael@0: function createElement(aDocument, aTag, aAttributes) michael@0: { michael@0: let node = aDocument.createElement(aTag); michael@0: if (aAttributes) { michael@0: for (let attr in aAttributes) { michael@0: node.setAttribute(attr, aAttributes[attr]); michael@0: } michael@0: } michael@0: return node; michael@0: } michael@0: michael@0: /** michael@0: * Creates a new DOMNode and appends it to aParent. michael@0: * michael@0: * @param nsIDOMNode aParent michael@0: * A parent node to append the created element. michael@0: * @param string aTag michael@0: * Name of the tag for the DOMNode. michael@0: * @param object aAttributes michael@0: * Attributes set on the created DOMNode. michael@0: * michael@0: * @returns nsIDOMNode michael@0: */ michael@0: function createAndAppendElement(aParent, aTag, aAttributes) michael@0: { michael@0: let node = createElement(aParent.ownerDocument, aTag, aAttributes); michael@0: aParent.appendChild(node); michael@0: return node; michael@0: }