2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) IBM Corp. 2009 All rights reserved.
4 * Copyright (C) 2010 Google Inc. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * @extends {WebInspector.VBox}
34 * @param {!WebInspector.NetworkRequest} request
36 WebInspector.RequestHeadersView = function(request)
38 WebInspector.VBox.call(this);
39 this.registerRequiredCSS("resourceView.css");
40 this.registerRequiredCSS("requestHeadersView.css");
41 this.element.classList.add("request-headers-view");
43 this._request = request;
45 this._headersListElement = document.createElement("ol");
46 this._headersListElement.className = "outline-disclosure";
47 this.element.appendChild(this._headersListElement);
49 this._headersTreeOutline = new TreeOutline(this._headersListElement);
50 this._headersTreeOutline.expandTreeElementsWhenArrowing = true;
52 this._remoteAddressTreeElement = new TreeElement("", null, false);
53 this._remoteAddressTreeElement.selectable = false;
54 this._remoteAddressTreeElement.hidden = true;
55 this._headersTreeOutline.appendChild(this._remoteAddressTreeElement);
57 this._urlTreeElement = new TreeElement("", null, false);
58 this._urlTreeElement.selectable = false;
59 this._headersTreeOutline.appendChild(this._urlTreeElement);
61 this._requestMethodTreeElement = new TreeElement("", null, false);
62 this._requestMethodTreeElement.selectable = false;
63 this._headersTreeOutline.appendChild(this._requestMethodTreeElement);
65 this._statusCodeTreeElement = new TreeElement("", null, false);
66 this._statusCodeTreeElement.selectable = false;
67 this._headersTreeOutline.appendChild(this._statusCodeTreeElement);
69 this._requestHeadersTreeElement = new TreeElement("", null, true);
70 this._requestHeadersTreeElement.expanded = true;
71 this._requestHeadersTreeElement.selectable = false;
72 this._headersTreeOutline.appendChild(this._requestHeadersTreeElement);
74 this._decodeRequestParameters = true;
76 this._showRequestHeadersText = false;
77 this._showResponseHeadersText = false;
79 this._queryStringTreeElement = new TreeElement("", null, true);
80 this._queryStringTreeElement.expanded = true;
81 this._queryStringTreeElement.selectable = false;
82 this._queryStringTreeElement.hidden = true;
83 this._headersTreeOutline.appendChild(this._queryStringTreeElement);
85 this._formDataTreeElement = new TreeElement("", null, true);
86 this._formDataTreeElement.expanded = true;
87 this._formDataTreeElement.selectable = false;
88 this._formDataTreeElement.hidden = true;
89 this._headersTreeOutline.appendChild(this._formDataTreeElement);
91 this._requestPayloadTreeElement = new TreeElement(WebInspector.UIString("Request Payload"), null, true);
92 this._requestPayloadTreeElement.expanded = true;
93 this._requestPayloadTreeElement.selectable = false;
94 this._requestPayloadTreeElement.hidden = true;
95 this._headersTreeOutline.appendChild(this._requestPayloadTreeElement);
97 this._responseHeadersTreeElement = new TreeElement("", null, true);
98 this._responseHeadersTreeElement.expanded = true;
99 this._responseHeadersTreeElement.selectable = false;
100 this._headersTreeOutline.appendChild(this._responseHeadersTreeElement);
103 WebInspector.RequestHeadersView.prototype = {
107 this._request.addEventListener(WebInspector.NetworkRequest.Events.RemoteAddressChanged, this._refreshRemoteAddress, this);
108 this._request.addEventListener(WebInspector.NetworkRequest.Events.RequestHeadersChanged, this._refreshRequestHeaders, this);
109 this._request.addEventListener(WebInspector.NetworkRequest.Events.ResponseHeadersChanged, this._refreshResponseHeaders, this);
110 this._request.addEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._refreshHTTPInformation, this);
113 this._refreshQueryString();
114 this._refreshRequestHeaders();
115 this._refreshResponseHeaders();
116 this._refreshHTTPInformation();
117 this._refreshRemoteAddress();
122 this._request.removeEventListener(WebInspector.NetworkRequest.Events.RemoteAddressChanged, this._refreshRemoteAddress, this);
123 this._request.removeEventListener(WebInspector.NetworkRequest.Events.RequestHeadersChanged, this._refreshRequestHeaders, this);
124 this._request.removeEventListener(WebInspector.NetworkRequest.Events.ResponseHeadersChanged, this._refreshResponseHeaders, this);
125 this._request.removeEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._refreshHTTPInformation, this);
129 * @param {string} name
130 * @param {string} value
131 * @return {!DocumentFragment}
133 _formatHeader: function(name, value)
135 var fragment = document.createDocumentFragment();
136 fragment.createChild("div", "header-name").textContent = name + ":";
137 fragment.createChild("div", "header-value source-code").textContent = value;
143 * @param {string} value
144 * @param {string} className
145 * @param {boolean} decodeParameters
148 _formatParameter: function(value, className, decodeParameters)
150 var errorDecoding = false;
152 if (decodeParameters) {
153 value = value.replace(/\+/g, " ");
154 if (value.indexOf("%") >= 0) {
156 value = decodeURIComponent(value);
158 errorDecoding = true;
162 var div = document.createElement("div");
163 div.className = className;
165 div.createChild("span", "error-message").textContent = WebInspector.UIString("(unable to decode value)");
167 div.textContent = value;
171 _refreshURL: function()
173 this._urlTreeElement.title = this._formatHeader(WebInspector.UIString("Request URL"), this._request.url);
176 _refreshQueryString: function()
178 var queryString = this._request.queryString();
179 var queryParameters = this._request.queryParameters;
180 this._queryStringTreeElement.hidden = !queryParameters;
182 this._refreshParams(WebInspector.UIString("Query String Parameters"), queryParameters, queryString, this._queryStringTreeElement);
185 _refreshFormData: function()
187 this._formDataTreeElement.hidden = true;
188 this._requestPayloadTreeElement.hidden = true;
190 var formData = this._request.requestFormData;
194 var formParameters = this._request.formParameters;
195 if (formParameters) {
196 this._formDataTreeElement.hidden = false;
197 this._refreshParams(WebInspector.UIString("Form Data"), formParameters, formData, this._formDataTreeElement);
199 this._requestPayloadTreeElement.hidden = false;
201 var json = JSON.parse(formData);
202 this._refreshRequestJSONPayload(json, formData);
204 this._populateTreeElementWithSourceText(this._requestPayloadTreeElement, formData);
210 * @param {!TreeElement} treeElement
211 * @param {?string} sourceText
213 _populateTreeElementWithSourceText: function(treeElement, sourceText)
215 var sourceTextElement = document.createElement("span");
216 sourceTextElement.classList.add("header-value");
217 sourceTextElement.classList.add("source-code");
218 sourceTextElement.textContent = String(sourceText || "").trim();
220 var sourceTreeElement = new TreeElement(sourceTextElement);
221 sourceTreeElement.selectable = false;
222 treeElement.removeChildren();
223 treeElement.appendChild(sourceTreeElement);
227 * @param {string} title
228 * @param {?Array.<!WebInspector.NetworkRequest.NameValue>} params
229 * @param {?string} sourceText
230 * @param {!TreeElement} paramsTreeElement
232 _refreshParams: function(title, params, sourceText, paramsTreeElement)
234 paramsTreeElement.removeChildren();
236 paramsTreeElement.listItemElement.removeChildren();
237 paramsTreeElement.listItemElement.appendChild(document.createTextNode(title));
239 var headerCount = document.createElement("span");
240 headerCount.classList.add("header-count");
241 headerCount.textContent = WebInspector.UIString(" (%d)", params.length);
242 paramsTreeElement.listItemElement.appendChild(headerCount);
245 * @param {!Event} event
246 * @this {WebInspector.RequestHeadersView}
248 function toggleViewSource(event)
250 paramsTreeElement._viewSource = !paramsTreeElement._viewSource;
251 this._refreshParams(title, params, sourceText, paramsTreeElement);
254 paramsTreeElement.listItemElement.appendChild(this._createViewSourceToggle(paramsTreeElement._viewSource, toggleViewSource.bind(this)));
256 if (paramsTreeElement._viewSource) {
257 this._populateTreeElementWithSourceText(paramsTreeElement, sourceText);
261 var toggleTitle = this._decodeRequestParameters ? WebInspector.UIString("view URL encoded") : WebInspector.UIString("view decoded");
262 var toggleButton = this._createToggleButton(toggleTitle);
263 toggleButton.addEventListener("click", this._toggleURLDecoding.bind(this), false);
264 paramsTreeElement.listItemElement.appendChild(toggleButton);
266 for (var i = 0; i < params.length; ++i) {
267 var paramNameValue = document.createDocumentFragment();
268 var name = this._formatParameter(params[i].name + ":", "header-name", this._decodeRequestParameters);
269 var value = this._formatParameter(params[i].value, "header-value source-code", this._decodeRequestParameters);
270 paramNameValue.appendChild(name);
271 paramNameValue.appendChild(value);
273 var parmTreeElement = new TreeElement(paramNameValue, null, false);
274 parmTreeElement.selectable = false;
275 paramsTreeElement.appendChild(parmTreeElement);
280 * @param {*} parsedObject
281 * @param {string} sourceText
283 _refreshRequestJSONPayload: function(parsedObject, sourceText)
285 var treeElement = this._requestPayloadTreeElement;
286 treeElement.removeChildren();
288 var listItem = this._requestPayloadTreeElement.listItemElement;
289 listItem.removeChildren();
290 listItem.appendChild(document.createTextNode(this._requestPayloadTreeElement.title));
293 * @param {!Event} event
294 * @this {WebInspector.RequestHeadersView}
296 function toggleViewSource(event)
298 treeElement._viewSource = !treeElement._viewSource;
299 this._refreshRequestJSONPayload(parsedObject, sourceText);
302 listItem.appendChild(this._createViewSourceToggle(treeElement._viewSource, toggleViewSource.bind(this)));
303 if (treeElement._viewSource) {
304 this._populateTreeElementWithSourceText(this._requestPayloadTreeElement, sourceText);
306 var object = WebInspector.RemoteObject.fromLocalObject(parsedObject);
307 var section = new WebInspector.ObjectPropertiesSection(object, object.description);
309 section.editable = false;
310 listItem.appendChild(section.element);
315 * @param {boolean} viewSource
316 * @param {function(!Event)} handler
319 _createViewSourceToggle: function(viewSource, handler)
321 var viewSourceToggleTitle = viewSource ? WebInspector.UIString("view parsed") : WebInspector.UIString("view source");
322 var viewSourceToggleButton = this._createToggleButton(viewSourceToggleTitle);
323 viewSourceToggleButton.addEventListener("click", handler, false);
324 return viewSourceToggleButton;
328 * @param {!Event} event
330 _toggleURLDecoding: function(event)
332 this._decodeRequestParameters = !this._decodeRequestParameters;
333 this._refreshQueryString();
334 this._refreshFormData();
337 _refreshRequestHeaders: function()
339 var treeElement = this._requestHeadersTreeElement;
341 var headers = this._request.requestHeaders();
342 headers = headers.slice();
343 headers.sort(function(a, b) { return a.name.toLowerCase().compareTo(b.name.toLowerCase()) });
344 var headersText = this._request.requestHeadersText();
346 if (this._showRequestHeadersText && headersText)
347 this._refreshHeadersText(WebInspector.UIString("Request Headers"), headers.length, headersText, treeElement);
349 this._refreshHeaders(WebInspector.UIString("Request Headers"), headers, treeElement, headersText === undefined);
352 var toggleButton = this._createHeadersToggleButton(this._showRequestHeadersText);
353 toggleButton.addEventListener("click", this._toggleRequestHeadersText.bind(this), false);
354 treeElement.listItemElement.appendChild(toggleButton);
357 this._refreshFormData();
360 _refreshResponseHeaders: function()
362 var treeElement = this._responseHeadersTreeElement;
363 var headers = this._request.sortedResponseHeaders;
364 var headersText = this._request.responseHeadersText;
366 if (this._showResponseHeadersText)
367 this._refreshHeadersText(WebInspector.UIString("Response Headers"), headers.length, headersText, treeElement);
369 this._refreshHeaders(WebInspector.UIString("Response Headers"), headers, treeElement);
372 var toggleButton = this._createHeadersToggleButton(this._showResponseHeadersText);
373 toggleButton.addEventListener("click", this._toggleResponseHeadersText.bind(this), false);
374 treeElement.listItemElement.appendChild(toggleButton);
378 _refreshHTTPInformation: function()
380 var requestMethodElement = this._requestMethodTreeElement;
381 requestMethodElement.hidden = !this._request.statusCode;
382 var statusCodeElement = this._statusCodeTreeElement;
383 statusCodeElement.hidden = !this._request.statusCode;
385 if (this._request.statusCode) {
386 var statusCodeFragment = document.createDocumentFragment();
387 statusCodeFragment.createChild("div", "header-name").textContent = WebInspector.UIString("Status Code") + ":";
389 var statusCodeImage = statusCodeFragment.createChild("div", "resource-status-image");
390 statusCodeImage.title = this._request.statusCode + " " + this._request.statusText;
392 if (this._request.statusCode < 300 || this._request.statusCode === 304)
393 statusCodeImage.classList.add("green-ball");
394 else if (this._request.statusCode < 400)
395 statusCodeImage.classList.add("orange-ball");
397 statusCodeImage.classList.add("red-ball");
399 requestMethodElement.title = this._formatHeader(WebInspector.UIString("Request Method"), this._request.requestMethod);
401 var statusTextElement = statusCodeFragment.createChild("div", "header-value source-code");
402 var statusText = this._request.statusCode + " " + this._request.statusText;
403 if (this._request.cached) {
404 statusText += " " + WebInspector.UIString("(from cache)");
405 statusTextElement.classList.add("status-from-cache");
407 statusTextElement.textContent = statusText;
409 statusCodeElement.title = statusCodeFragment;
414 * @param {string} title
415 * @param {!TreeElement} headersTreeElement
416 * @param {number} headersLength
418 _refreshHeadersTitle: function(title, headersTreeElement, headersLength)
420 headersTreeElement.listItemElement.removeChildren();
421 headersTreeElement.listItemElement.createTextChild(title);
423 var headerCount = WebInspector.UIString(" (%d)", headersLength);
424 headersTreeElement.listItemElement.createChild("span", "header-count").textContent = headerCount;
428 * @param {string} title
429 * @param {!Array.<!WebInspector.NetworkRequest.NameValue>} headers
430 * @param {!TreeElement} headersTreeElement
431 * @param {boolean=} provisionalHeaders
433 _refreshHeaders: function(title, headers, headersTreeElement, provisionalHeaders)
435 headersTreeElement.removeChildren();
437 var length = headers.length;
438 this._refreshHeadersTitle(title, headersTreeElement, length);
440 if (provisionalHeaders) {
441 var cautionText = WebInspector.UIString("Provisional headers are shown");
442 var cautionFragment = document.createDocumentFragment();
443 cautionFragment.createChild("div", "warning-icon-small");
444 cautionFragment.createChild("div", "caution").textContent = cautionText;
445 var cautionTreeElement = new TreeElement(cautionFragment);
446 cautionTreeElement.selectable = false;
447 headersTreeElement.appendChild(cautionTreeElement);
450 headersTreeElement.hidden = !length && !provisionalHeaders;
451 for (var i = 0; i < length; ++i) {
452 var headerTreeElement = new TreeElement(this._formatHeader(headers[i].name, headers[i].value));
453 headerTreeElement.selectable = false;
454 headersTreeElement.appendChild(headerTreeElement);
459 * @param {string} title
460 * @param {number} count
461 * @param {string} headersText
462 * @param {!TreeElement} headersTreeElement
464 _refreshHeadersText: function(title, count, headersText, headersTreeElement)
466 this._populateTreeElementWithSourceText(headersTreeElement, headersText);
467 this._refreshHeadersTitle(title, headersTreeElement, count);
470 _refreshRemoteAddress: function()
472 var remoteAddress = this._request.remoteAddress();
473 var treeElement = this._remoteAddressTreeElement;
474 treeElement.hidden = !remoteAddress;
476 treeElement.title = this._formatHeader(WebInspector.UIString("Remote Address"), remoteAddress);
480 * @param {!Event} event
482 _toggleRequestHeadersText: function(event)
484 this._showRequestHeadersText = !this._showRequestHeadersText;
485 this._refreshRequestHeaders();
489 * @param {!Event} event
491 _toggleResponseHeadersText: function(event)
493 this._showResponseHeadersText = !this._showResponseHeadersText;
494 this._refreshResponseHeaders();
498 * @param {string} title
501 _createToggleButton: function(title)
503 var button = document.createElement("span");
504 button.classList.add("header-toggle");
505 button.textContent = title;
510 * @param {boolean} isHeadersTextShown
513 _createHeadersToggleButton: function(isHeadersTextShown)
515 var toggleTitle = isHeadersTextShown ? WebInspector.UIString("view parsed") : WebInspector.UIString("view source");
516 return this._createToggleButton(toggleTitle);
519 __proto__: WebInspector.VBox.prototype