| /* |
| * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. |
| * Copyright (C) IBM Corp. 2009 All rights reserved. |
| * Copyright (C) 2010 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.VBox} |
| * @param {!WebInspector.NetworkRequest} request |
| */ |
| WebInspector.RequestHeadersView = function(request) |
| { |
| WebInspector.VBox.call(this); |
| this.registerRequiredCSS("network/requestHeadersView.css"); |
| this.element.classList.add("request-headers-view"); |
| |
| this._request = request; |
| this._decodeRequestParameters = true; |
| this._showRequestHeadersText = false; |
| this._showResponseHeadersText = false; |
| |
| this._requestHeaderFilterSetting = WebInspector.settings.createSetting("requestHeaderFilterSetting", ""); |
| |
| /** @type {?RegExp} */ |
| this._filterRegex = null; |
| if (Runtime.experiments.isEnabled("networkRequestHeadersFilterInDetailsView")) { |
| this._filterInput = this.element.createChild("input", "filter-input"); |
| this._filterInput.type = "text"; |
| this._filterInput.placeholder = WebInspector.UIString("Filter headers"); |
| this._filterInput.addEventListener("input", this._updateFilter.bind(this), false); |
| this._filterInput.addEventListener("keydown", this._onFilterKeyDown.bind(this), false); |
| this._filterInput.value = this._requestHeaderFilterSetting.get() || ""; |
| } |
| |
| var root = new TreeOutline(true); |
| root.element.classList.add("outline-disclosure"); |
| root.expandTreeElementsWhenArrowing = true; |
| this.element.appendChild(root.element); |
| |
| var generalCategory = new WebInspector.RequestHeadersView.Category(root, "general", WebInspector.UIString("General")); |
| generalCategory.hidden = false; |
| this._urlItem = generalCategory.createLeaf(); |
| this._requestMethodItem = generalCategory.createLeaf(); |
| this._statusCodeItem = generalCategory.createLeaf(); |
| this._remoteAddressItem = generalCategory.createLeaf(); |
| this._remoteAddressItem.hidden = true; |
| |
| this._responseHeadersCategory = new WebInspector.RequestHeadersView.Category(root, "responseHeaders", ""); |
| this._requestHeadersCategory = new WebInspector.RequestHeadersView.Category(root, "requestHeaders", ""); |
| this._queryStringCategory = new WebInspector.RequestHeadersView.Category(root, "queryString", ""); |
| this._formDataCategory = new WebInspector.RequestHeadersView.Category(root, "formData", ""); |
| this._requestPayloadCategory = new WebInspector.RequestHeadersView.Category(root, "requestPayload", WebInspector.UIString("Request Payload")); |
| |
| |
| if (Runtime.experiments.isEnabled("networkRequestHeadersFilterInDetailsView")) { |
| this._updateFilter(); |
| } |
| } |
| |
| WebInspector.RequestHeadersView.prototype = { |
| _updateFilter: function() |
| { |
| var text = this._filterInput.value; |
| this._requestHeaderFilterSetting.set(text); |
| this._filterRegex = text ? new RegExp(text.escapeForRegExp(), "i") : null; |
| this._updateHeaders(); |
| }, |
| |
| /** |
| * @param {!Event} event |
| */ |
| _onFilterKeyDown: function(event) |
| { |
| var text = this._filterInput.value; |
| if (!text) |
| return; |
| if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B") { |
| event.consume(true); |
| this._filterInput.value = ""; |
| this._updateFilter(); |
| } |
| }, |
| |
| _updateHeaders: function() |
| { |
| this._refreshRequestHeaders(); |
| this._refreshResponseHeaders(); |
| }, |
| |
| wasShown: function() |
| { |
| this._request.addEventListener(WebInspector.NetworkRequest.Events.RemoteAddressChanged, this._refreshRemoteAddress, this); |
| this._request.addEventListener(WebInspector.NetworkRequest.Events.RequestHeadersChanged, this._refreshRequestHeaders, this); |
| this._request.addEventListener(WebInspector.NetworkRequest.Events.ResponseHeadersChanged, this._refreshResponseHeaders, this); |
| this._request.addEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._refreshHTTPInformation, this); |
| |
| this._refreshURL(); |
| this._refreshQueryString(); |
| this._updateHeaders(); |
| this._refreshHTTPInformation(); |
| this._refreshRemoteAddress(); |
| }, |
| |
| willHide: function() |
| { |
| this._request.removeEventListener(WebInspector.NetworkRequest.Events.RemoteAddressChanged, this._refreshRemoteAddress, this); |
| this._request.removeEventListener(WebInspector.NetworkRequest.Events.RequestHeadersChanged, this._refreshRequestHeaders, this); |
| this._request.removeEventListener(WebInspector.NetworkRequest.Events.ResponseHeadersChanged, this._refreshResponseHeaders, this); |
| this._request.removeEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._refreshHTTPInformation, this); |
| }, |
| |
| /** |
| * @param {string} name |
| * @param {string} value |
| * @return {!DocumentFragment} |
| */ |
| _formatHeader: function(name, value) |
| { |
| var fragment = createDocumentFragment(); |
| fragment.createChild("div", "header-name").textContent = name + ":"; |
| fragment.createChild("div", "header-value source-code").textContent = value; |
| |
| return fragment; |
| }, |
| |
| /** |
| * @param {string} value |
| * @param {string} className |
| * @param {boolean} decodeParameters |
| * @return {!Element} |
| */ |
| _formatParameter: function(value, className, decodeParameters) |
| { |
| var errorDecoding = false; |
| |
| if (decodeParameters) { |
| value = value.replace(/\+/g, " "); |
| if (value.indexOf("%") >= 0) { |
| try { |
| value = decodeURIComponent(value); |
| } catch (e) { |
| errorDecoding = true; |
| } |
| } |
| } |
| var div = createElementWithClass("div", className); |
| if (errorDecoding) |
| div.createChild("span", "error-message").textContent = WebInspector.UIString("(unable to decode value)"); |
| else |
| div.textContent = value; |
| return div; |
| }, |
| |
| _refreshURL: function() |
| { |
| this._urlItem.title = this._formatHeader(WebInspector.UIString("Request URL"), this._request.url); |
| }, |
| |
| _refreshQueryString: function() |
| { |
| var queryString = this._request.queryString(); |
| var queryParameters = this._request.queryParameters; |
| this._queryStringCategory.hidden = !queryParameters; |
| if (queryParameters) |
| this._refreshParams(WebInspector.UIString("Query String Parameters"), queryParameters, queryString, this._queryStringCategory); |
| }, |
| |
| _refreshFormData: function() |
| { |
| this._formDataCategory.hidden = true; |
| this._requestPayloadCategory.hidden = true; |
| |
| var formData = this._request.requestFormData; |
| if (!formData) |
| return; |
| |
| var formParameters = this._request.formParameters; |
| if (formParameters) { |
| this._formDataCategory.hidden = false; |
| this._refreshParams(WebInspector.UIString("Form Data"), formParameters, formData, this._formDataCategory); |
| } else { |
| this._requestPayloadCategory.hidden = false; |
| try { |
| var json = JSON.parse(formData); |
| this._refreshRequestJSONPayload(json, formData); |
| } catch (e) { |
| this._populateTreeElementWithSourceText(this._requestPayloadCategory, formData); |
| } |
| } |
| }, |
| |
| /** |
| * @param {!TreeElement} treeElement |
| * @param {?string} sourceText |
| */ |
| _populateTreeElementWithSourceText: function(treeElement, sourceText) |
| { |
| var sourceTextElement = createElementWithClass("span", "header-value source-code"); |
| sourceTextElement.textContent = String(sourceText || "").trim(); |
| |
| var sourceTreeElement = new TreeElement(sourceTextElement); |
| sourceTreeElement.selectable = false; |
| treeElement.removeChildren(); |
| treeElement.appendChild(sourceTreeElement); |
| }, |
| |
| /** |
| * @param {string} title |
| * @param {?Array.<!WebInspector.NetworkRequest.NameValue>} params |
| * @param {?string} sourceText |
| * @param {!TreeElement} paramsTreeElement |
| */ |
| _refreshParams: function(title, params, sourceText, paramsTreeElement) |
| { |
| paramsTreeElement.removeChildren(); |
| |
| paramsTreeElement.listItemElement.removeChildren(); |
| paramsTreeElement.listItemElement.createTextChild(title); |
| |
| var headerCount = createElementWithClass("span", "header-count"); |
| headerCount.textContent = WebInspector.UIString("\u00A0(%d)", params.length); |
| paramsTreeElement.listItemElement.appendChild(headerCount); |
| |
| /** |
| * @param {!Event} event |
| * @this {WebInspector.RequestHeadersView} |
| */ |
| function toggleViewSource(event) |
| { |
| paramsTreeElement._viewSource = !paramsTreeElement._viewSource; |
| this._refreshParams(title, params, sourceText, paramsTreeElement); |
| event.consume(); |
| } |
| |
| paramsTreeElement.listItemElement.appendChild(this._createViewSourceToggle(paramsTreeElement._viewSource, toggleViewSource.bind(this))); |
| |
| if (paramsTreeElement._viewSource) { |
| this._populateTreeElementWithSourceText(paramsTreeElement, sourceText); |
| return; |
| } |
| |
| var toggleTitle = this._decodeRequestParameters ? WebInspector.UIString("view URL encoded") : WebInspector.UIString("view decoded"); |
| var toggleButton = this._createToggleButton(toggleTitle); |
| toggleButton.addEventListener("click", this._toggleURLDecoding.bind(this), false); |
| paramsTreeElement.listItemElement.appendChild(toggleButton); |
| |
| for (var i = 0; i < params.length; ++i) { |
| var paramNameValue = createDocumentFragment(); |
| var name = this._formatParameter(params[i].name + ":", "header-name", this._decodeRequestParameters); |
| var value = this._formatParameter(params[i].value, "header-value source-code", this._decodeRequestParameters); |
| paramNameValue.appendChild(name); |
| paramNameValue.appendChild(value); |
| |
| var parmTreeElement = new TreeElement(paramNameValue); |
| parmTreeElement.selectable = false; |
| paramsTreeElement.appendChild(parmTreeElement); |
| } |
| }, |
| |
| /** |
| * @param {*} parsedObject |
| * @param {string} sourceText |
| */ |
| _refreshRequestJSONPayload: function(parsedObject, sourceText) |
| { |
| var treeElement = this._requestPayloadCategory; |
| treeElement.removeChildren(); |
| |
| var listItem = this._requestPayloadCategory.listItemElement; |
| listItem.removeChildren(); |
| listItem.createTextChild(this._requestPayloadCategory.title); |
| |
| /** |
| * @param {!Event} event |
| * @this {WebInspector.RequestHeadersView} |
| */ |
| function toggleViewSource(event) |
| { |
| treeElement._viewSource = !treeElement._viewSource; |
| this._refreshRequestJSONPayload(parsedObject, sourceText); |
| event.consume(); |
| } |
| |
| listItem.appendChild(this._createViewSourceToggle(treeElement._viewSource, toggleViewSource.bind(this))); |
| if (treeElement._viewSource) { |
| this._populateTreeElementWithSourceText(this._requestPayloadCategory, sourceText); |
| } else { |
| var object = WebInspector.RemoteObject.fromLocalObject(parsedObject); |
| var section = new WebInspector.ObjectPropertiesSection(object, object.description); |
| section.expand(); |
| section.editable = false; |
| treeElement.appendChild(new TreeElement(section.element)); |
| } |
| }, |
| |
| /** |
| * @param {boolean} viewSource |
| * @param {function(!Event)} handler |
| * @return {!Element} |
| */ |
| _createViewSourceToggle: function(viewSource, handler) |
| { |
| var viewSourceToggleTitle = viewSource ? WebInspector.UIString("view parsed") : WebInspector.UIString("view source"); |
| var viewSourceToggleButton = this._createToggleButton(viewSourceToggleTitle); |
| viewSourceToggleButton.addEventListener("click", handler, false); |
| return viewSourceToggleButton; |
| }, |
| |
| /** |
| * @param {!Event} event |
| */ |
| _toggleURLDecoding: function(event) |
| { |
| this._decodeRequestParameters = !this._decodeRequestParameters; |
| this._refreshQueryString(); |
| this._refreshFormData(); |
| event.consume(); |
| }, |
| |
| _refreshRequestHeaders: function() |
| { |
| var treeElement = this._requestHeadersCategory; |
| |
| var headers = this._request.requestHeaders().slice(); |
| var filterRegex = this._filterRegex; |
| if (filterRegex) |
| headers = headers.filter(function(header) { return filterRegex.test(header.name) || filterRegex.test(header.value);}); |
| headers.sort(function(a, b) { return a.name.toLowerCase().compareTo(b.name.toLowerCase()); }); |
| var headersText = this._request.requestHeadersText(); |
| |
| if (this._showRequestHeadersText && headersText) |
| this._refreshHeadersText(WebInspector.UIString("Request Headers"), headers.length, headersText, treeElement); |
| else |
| this._refreshHeaders(WebInspector.UIString("Request Headers"), headers, treeElement, headersText === undefined); |
| |
| if (headersText) { |
| var toggleButton = this._createHeadersToggleButton(this._showRequestHeadersText); |
| toggleButton.addEventListener("click", this._toggleRequestHeadersText.bind(this), false); |
| treeElement.listItemElement.appendChild(toggleButton); |
| } |
| |
| this._refreshFormData(); |
| }, |
| |
| _refreshResponseHeaders: function() |
| { |
| var treeElement = this._responseHeadersCategory; |
| var headers = this._request.sortedResponseHeaders.slice(); |
| var filterRegex = this._filterRegex; |
| if (filterRegex) |
| headers = headers.filter(function(header) { return filterRegex.test(header.name) || filterRegex.test(header.value);}); |
| var headersText = this._request.responseHeadersText; |
| |
| if (this._showResponseHeadersText) |
| this._refreshHeadersText(WebInspector.UIString("Response Headers"), headers.length, headersText, treeElement); |
| else |
| this._refreshHeaders(WebInspector.UIString("Response Headers"), headers, treeElement); |
| |
| if (headersText) { |
| var toggleButton = this._createHeadersToggleButton(this._showResponseHeadersText); |
| toggleButton.addEventListener("click", this._toggleResponseHeadersText.bind(this), false); |
| treeElement.listItemElement.appendChild(toggleButton); |
| } |
| }, |
| |
| _refreshHTTPInformation: function() |
| { |
| var requestMethodElement = this._requestMethodItem; |
| requestMethodElement.hidden = !this._request.statusCode; |
| var statusCodeElement = this._statusCodeItem; |
| statusCodeElement.hidden = !this._request.statusCode; |
| |
| if (this._request.statusCode) { |
| var statusCodeFragment = createDocumentFragment(); |
| statusCodeFragment.createChild("div", "header-name").textContent = WebInspector.UIString("Status Code") + ":"; |
| |
| var statusCodeImage = statusCodeFragment.createChild("label", "resource-status-image", "dt-icon-label"); |
| statusCodeImage.title = this._request.statusCode + " " + this._request.statusText; |
| |
| if (this._request.statusCode < 300 || this._request.statusCode === 304) |
| statusCodeImage.type = "green-ball"; |
| else if (this._request.statusCode < 400) |
| statusCodeImage.type = "orange-ball"; |
| else |
| statusCodeImage.type = "red-ball"; |
| |
| requestMethodElement.title = this._formatHeader(WebInspector.UIString("Request Method"), this._request.requestMethod); |
| |
| var statusTextElement = statusCodeFragment.createChild("div", "header-value source-code"); |
| var statusText = this._request.statusCode + " " + this._request.statusText; |
| if (this._request.fetchedViaServiceWorker) { |
| statusText += " " + WebInspector.UIString("(from ServiceWorker)"); |
| statusTextElement.classList.add("status-from-cache"); |
| } else if (this._request.cached()) { |
| statusText += " " + WebInspector.UIString("(from cache)"); |
| statusTextElement.classList.add("status-from-cache"); |
| } |
| statusTextElement.textContent = statusText; |
| |
| statusCodeElement.title = statusCodeFragment; |
| } |
| }, |
| |
| /** |
| * @param {string} title |
| * @param {!TreeElement} headersTreeElement |
| * @param {number} headersLength |
| */ |
| _refreshHeadersTitle: function(title, headersTreeElement, headersLength) |
| { |
| headersTreeElement.listItemElement.removeChildren(); |
| headersTreeElement.listItemElement.createTextChild(title); |
| |
| var headerCount = WebInspector.UIString("\u00A0(%d)", headersLength); |
| headersTreeElement.listItemElement.createChild("span", "header-count").textContent = headerCount; |
| }, |
| |
| /** |
| * @param {string} title |
| * @param {!Array.<!WebInspector.NetworkRequest.NameValue>} headers |
| * @param {!TreeElement} headersTreeElement |
| * @param {boolean=} provisionalHeaders |
| */ |
| _refreshHeaders: function(title, headers, headersTreeElement, provisionalHeaders) |
| { |
| headersTreeElement.removeChildren(); |
| |
| var length = headers.length; |
| this._refreshHeadersTitle(title, headersTreeElement, length); |
| |
| if (provisionalHeaders) { |
| var cautionText = WebInspector.UIString("Provisional headers are shown"); |
| var cautionFragment = createDocumentFragment(); |
| cautionFragment.createChild("label", "", "dt-icon-label").type = "warning-icon"; |
| cautionFragment.createChild("div", "caution").textContent = cautionText; |
| var cautionTreeElement = new TreeElement(cautionFragment); |
| cautionTreeElement.selectable = false; |
| headersTreeElement.appendChild(cautionTreeElement); |
| } |
| |
| headersTreeElement.hidden = !length && !provisionalHeaders; |
| for (var i = 0; i < length; ++i) { |
| var headerTreeElement = new TreeElement(this._formatHeader(headers[i].name, headers[i].value)); |
| headerTreeElement.selectable = false; |
| headersTreeElement.appendChild(headerTreeElement); |
| } |
| }, |
| |
| /** |
| * @param {string} title |
| * @param {number} count |
| * @param {string} headersText |
| * @param {!TreeElement} headersTreeElement |
| */ |
| _refreshHeadersText: function(title, count, headersText, headersTreeElement) |
| { |
| this._populateTreeElementWithSourceText(headersTreeElement, headersText); |
| this._refreshHeadersTitle(title, headersTreeElement, count); |
| }, |
| |
| _refreshRemoteAddress: function() |
| { |
| var remoteAddress = this._request.remoteAddress(); |
| var treeElement = this._remoteAddressItem; |
| treeElement.hidden = !remoteAddress; |
| if (remoteAddress) |
| treeElement.title = this._formatHeader(WebInspector.UIString("Remote Address"), remoteAddress); |
| }, |
| |
| /** |
| * @param {!Event} event |
| */ |
| _toggleRequestHeadersText: function(event) |
| { |
| this._showRequestHeadersText = !this._showRequestHeadersText; |
| this._refreshRequestHeaders(); |
| event.consume(); |
| }, |
| |
| /** |
| * @param {!Event} event |
| */ |
| _toggleResponseHeadersText: function(event) |
| { |
| this._showResponseHeadersText = !this._showResponseHeadersText; |
| this._refreshResponseHeaders(); |
| event.consume(); |
| }, |
| |
| /** |
| * @param {string} title |
| * @return {!Element} |
| */ |
| _createToggleButton: function(title) |
| { |
| var button = createElementWithClass("span", "header-toggle"); |
| button.textContent = title; |
| return button; |
| }, |
| |
| /** |
| * @param {boolean} isHeadersTextShown |
| * @return {!Element} |
| */ |
| _createHeadersToggleButton: function(isHeadersTextShown) |
| { |
| var toggleTitle = isHeadersTextShown ? WebInspector.UIString("view parsed") : WebInspector.UIString("view source"); |
| return this._createToggleButton(toggleTitle); |
| }, |
| |
| __proto__: WebInspector.VBox.prototype |
| } |
| |
| /** |
| * @constructor |
| * @extends {TreeElement} |
| * @param {!TreeOutline} root |
| * @param {string} name |
| * @param {string=} title |
| */ |
| WebInspector.RequestHeadersView.Category = function(root, name, title) |
| { |
| TreeElement.call(this, title || "", true); |
| this.selectable = false; |
| this.toggleOnClick = true; |
| this.hidden = true; |
| this._expandedSetting = WebInspector.settings.createSetting("request-info-" + name + "-category-expanded", true); |
| this.expanded = this._expandedSetting.get(); |
| root.appendChild(this); |
| } |
| |
| WebInspector.RequestHeadersView.Category.prototype = { |
| /** |
| * @return {!TreeElement} |
| */ |
| createLeaf: function() |
| { |
| var leaf = new TreeElement(); |
| leaf.selectable = false; |
| this.appendChild(leaf); |
| return leaf; |
| }, |
| |
| onexpand: function() |
| { |
| this._expandedSetting.set(true); |
| }, |
| |
| oncollapse: function() |
| { |
| this._expandedSetting.set(false); |
| }, |
| |
| __proto__: TreeElement.prototype |
| } |