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("network/requestHeadersView.css");
40 this.element.classList.add("request-headers-view");
42 this._request = request;
44 this._headersListElement = createElement("ol");
45 this._headersListElement.className = "outline-disclosure";
46 this.element.appendChild(this._headersListElement);
48 this._headersTreeOutline = new TreeOutline(this._headersListElement);
49 this._headersTreeOutline.expandTreeElementsWhenArrowing = true;
51 this._remoteAddressTreeElement = new TreeElement("", null, false);
52 this._remoteAddressTreeElement.selectable = false;
53 this._remoteAddressTreeElement.hidden = true;
54 this._headersTreeOutline.appendChild(this._remoteAddressTreeElement);
56 this._urlTreeElement = new TreeElement("", null, false);
57 this._urlTreeElement.selectable = false;
58 this._headersTreeOutline.appendChild(this._urlTreeElement);
60 this._requestMethodTreeElement = new TreeElement("", null, false);
61 this._requestMethodTreeElement.selectable = false;
62 this._headersTreeOutline.appendChild(this._requestMethodTreeElement);
64 this._statusCodeTreeElement = new TreeElement("", null, false);
65 this._statusCodeTreeElement.selectable = false;
66 this._headersTreeOutline.appendChild(this._statusCodeTreeElement);
68 this._requestHeadersTreeElement = new TreeElement("", null, true);
69 this._requestHeadersTreeElement.expanded = true;
70 this._requestHeadersTreeElement.selectable = false;
71 this._headersTreeOutline.appendChild(this._requestHeadersTreeElement);
73 this._decodeRequestParameters = true;
75 this._showRequestHeadersText = false;
76 this._showResponseHeadersText = false;
78 this._queryStringTreeElement = new TreeElement("", null, true);
79 this._queryStringTreeElement.expanded = true;
80 this._queryStringTreeElement.selectable = false;
81 this._queryStringTreeElement.hidden = true;
82 this._headersTreeOutline.appendChild(this._queryStringTreeElement);
84 this._formDataTreeElement = new TreeElement("", null, true);
85 this._formDataTreeElement.expanded = true;
86 this._formDataTreeElement.selectable = false;
87 this._formDataTreeElement.hidden = true;
88 this._headersTreeOutline.appendChild(this._formDataTreeElement);
90 this._requestPayloadTreeElement = new TreeElement(WebInspector.UIString("Request Payload"), null, true);
91 this._requestPayloadTreeElement.expanded = true;
92 this._requestPayloadTreeElement.selectable = false;
93 this._requestPayloadTreeElement.hidden = true;
94 this._headersTreeOutline.appendChild(this._requestPayloadTreeElement);
96 this._responseHeadersTreeElement = new TreeElement("", null, true);
97 this._responseHeadersTreeElement.expanded = true;
98 this._responseHeadersTreeElement.selectable = false;
99 this._headersTreeOutline.appendChild(this._responseHeadersTreeElement);
102 WebInspector.RequestHeadersView.prototype = {
106 this._request.addEventListener(WebInspector.NetworkRequest.Events.RemoteAddressChanged, this._refreshRemoteAddress, this);
107 this._request.addEventListener(WebInspector.NetworkRequest.Events.RequestHeadersChanged, this._refreshRequestHeaders, this);
108 this._request.addEventListener(WebInspector.NetworkRequest.Events.ResponseHeadersChanged, this._refreshResponseHeaders, this);
109 this._request.addEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._refreshHTTPInformation, this);
112 this._refreshQueryString();
113 this._refreshRequestHeaders();
114 this._refreshResponseHeaders();
115 this._refreshHTTPInformation();
116 this._refreshRemoteAddress();
121 this._request.removeEventListener(WebInspector.NetworkRequest.Events.RemoteAddressChanged, this._refreshRemoteAddress, this);
122 this._request.removeEventListener(WebInspector.NetworkRequest.Events.RequestHeadersChanged, this._refreshRequestHeaders, this);
123 this._request.removeEventListener(WebInspector.NetworkRequest.Events.ResponseHeadersChanged, this._refreshResponseHeaders, this);
124 this._request.removeEventListener(WebInspector.NetworkRequest.Events.FinishedLoading, this._refreshHTTPInformation, this);
128 * @param {string} name
129 * @param {string} value
130 * @return {!DocumentFragment}
132 _formatHeader: function(name, value)
134 var fragment = createDocumentFragment();
135 fragment.createChild("div", "header-name").textContent = name + ":";
136 fragment.createChild("div", "header-value source-code").textContent = value;
142 * @param {string} value
143 * @param {string} className
144 * @param {boolean} decodeParameters
147 _formatParameter: function(value, className, decodeParameters)
149 var errorDecoding = false;
151 if (decodeParameters) {
152 value = value.replace(/\+/g, " ");
153 if (value.indexOf("%") >= 0) {
155 value = decodeURIComponent(value);
157 errorDecoding = true;
161 var div = createElement("div");
162 div.className = className;
164 div.createChild("span", "error-message").textContent = WebInspector.UIString("(unable to decode value)");
166 div.textContent = value;
170 _refreshURL: function()
172 this._urlTreeElement.title = this._formatHeader(WebInspector.UIString("Request URL"), this._request.url);
175 _refreshQueryString: function()
177 var queryString = this._request.queryString();
178 var queryParameters = this._request.queryParameters;
179 this._queryStringTreeElement.hidden = !queryParameters;
181 this._refreshParams(WebInspector.UIString("Query String Parameters"), queryParameters, queryString, this._queryStringTreeElement);
184 _refreshFormData: function()
186 this._formDataTreeElement.hidden = true;
187 this._requestPayloadTreeElement.hidden = true;
189 var formData = this._request.requestFormData;
193 var formParameters = this._request.formParameters;
194 if (formParameters) {
195 this._formDataTreeElement.hidden = false;
196 this._refreshParams(WebInspector.UIString("Form Data"), formParameters, formData, this._formDataTreeElement);
198 this._requestPayloadTreeElement.hidden = false;
200 var json = JSON.parse(formData);
201 this._refreshRequestJSONPayload(json, formData);
203 this._populateTreeElementWithSourceText(this._requestPayloadTreeElement, formData);
209 * @param {!TreeElement} treeElement
210 * @param {?string} sourceText
212 _populateTreeElementWithSourceText: function(treeElement, sourceText)
214 var sourceTextElement = createElement("span");
215 sourceTextElement.classList.add("header-value");
216 sourceTextElement.classList.add("source-code");
217 sourceTextElement.textContent = String(sourceText || "").trim();
219 var sourceTreeElement = new TreeElement(sourceTextElement);
220 sourceTreeElement.selectable = false;
221 treeElement.removeChildren();
222 treeElement.appendChild(sourceTreeElement);
226 * @param {string} title
227 * @param {?Array.<!WebInspector.NetworkRequest.NameValue>} params
228 * @param {?string} sourceText
229 * @param {!TreeElement} paramsTreeElement
231 _refreshParams: function(title, params, sourceText, paramsTreeElement)
233 paramsTreeElement.removeChildren();
235 paramsTreeElement.listItemElement.removeChildren();
236 paramsTreeElement.listItemElement.createTextChild(title);
238 var headerCount = createElement("span");
239 headerCount.classList.add("header-count");
240 headerCount.textContent = WebInspector.UIString(" (%d)", params.length);
241 paramsTreeElement.listItemElement.appendChild(headerCount);
244 * @param {!Event} event
245 * @this {WebInspector.RequestHeadersView}
247 function toggleViewSource(event)
249 paramsTreeElement._viewSource = !paramsTreeElement._viewSource;
250 this._refreshParams(title, params, sourceText, paramsTreeElement);
253 paramsTreeElement.listItemElement.appendChild(this._createViewSourceToggle(paramsTreeElement._viewSource, toggleViewSource.bind(this)));
255 if (paramsTreeElement._viewSource) {
256 this._populateTreeElementWithSourceText(paramsTreeElement, sourceText);
260 var toggleTitle = this._decodeRequestParameters ? WebInspector.UIString("view URL encoded") : WebInspector.UIString("view decoded");
261 var toggleButton = this._createToggleButton(toggleTitle);
262 toggleButton.addEventListener("click", this._toggleURLDecoding.bind(this), false);
263 paramsTreeElement.listItemElement.appendChild(toggleButton);
265 for (var i = 0; i < params.length; ++i) {
266 var paramNameValue = createDocumentFragment();
267 var name = this._formatParameter(params[i].name + ":", "header-name", this._decodeRequestParameters);
268 var value = this._formatParameter(params[i].value, "header-value source-code", this._decodeRequestParameters);
269 paramNameValue.appendChild(name);
270 paramNameValue.appendChild(value);
272 var parmTreeElement = new TreeElement(paramNameValue, null, false);
273 parmTreeElement.selectable = false;
274 paramsTreeElement.appendChild(parmTreeElement);
279 * @param {*} parsedObject
280 * @param {string} sourceText
282 _refreshRequestJSONPayload: function(parsedObject, sourceText)
284 var treeElement = this._requestPayloadTreeElement;
285 treeElement.removeChildren();
287 var listItem = this._requestPayloadTreeElement.listItemElement;
288 listItem.removeChildren();
289 listItem.createTextChild(this._requestPayloadTreeElement.title);
292 * @param {!Event} event
293 * @this {WebInspector.RequestHeadersView}
295 function toggleViewSource(event)
297 treeElement._viewSource = !treeElement._viewSource;
298 this._refreshRequestJSONPayload(parsedObject, sourceText);
301 listItem.appendChild(this._createViewSourceToggle(treeElement._viewSource, toggleViewSource.bind(this)));
302 if (treeElement._viewSource) {
303 this._populateTreeElementWithSourceText(this._requestPayloadTreeElement, sourceText);
305 var object = WebInspector.RemoteObject.fromLocalObject(parsedObject);
306 var section = new WebInspector.ObjectPropertiesSection(object, object.description);
308 section.editable = false;
309 listItem.appendChild(section.element);
314 * @param {boolean} viewSource
315 * @param {function(!Event)} handler
318 _createViewSourceToggle: function(viewSource, handler)
320 var viewSourceToggleTitle = viewSource ? WebInspector.UIString("view parsed") : WebInspector.UIString("view source");
321 var viewSourceToggleButton = this._createToggleButton(viewSourceToggleTitle);
322 viewSourceToggleButton.addEventListener("click", handler, false);
323 return viewSourceToggleButton;
327 * @param {!Event} event
329 _toggleURLDecoding: function(event)
331 this._decodeRequestParameters = !this._decodeRequestParameters;
332 this._refreshQueryString();
333 this._refreshFormData();
336 _refreshRequestHeaders: function()
338 var treeElement = this._requestHeadersTreeElement;
340 var headers = this._request.requestHeaders();
341 headers = headers.slice();
342 headers.sort(function(a, b) { return a.name.toLowerCase().compareTo(b.name.toLowerCase()) });
343 var headersText = this._request.requestHeadersText();
345 if (this._showRequestHeadersText && headersText)
346 this._refreshHeadersText(WebInspector.UIString("Request Headers"), headers.length, headersText, treeElement);
348 this._refreshHeaders(WebInspector.UIString("Request Headers"), headers, treeElement, headersText === undefined);
351 var toggleButton = this._createHeadersToggleButton(this._showRequestHeadersText);
352 toggleButton.addEventListener("click", this._toggleRequestHeadersText.bind(this), false);
353 treeElement.listItemElement.appendChild(toggleButton);
356 this._refreshFormData();
359 _refreshResponseHeaders: function()
361 var treeElement = this._responseHeadersTreeElement;
362 var headers = this._request.sortedResponseHeaders;
363 var headersText = this._request.responseHeadersText;
365 if (this._showResponseHeadersText)
366 this._refreshHeadersText(WebInspector.UIString("Response Headers"), headers.length, headersText, treeElement);
368 this._refreshHeaders(WebInspector.UIString("Response Headers"), headers, treeElement);
371 var toggleButton = this._createHeadersToggleButton(this._showResponseHeadersText);
372 toggleButton.addEventListener("click", this._toggleResponseHeadersText.bind(this), false);
373 treeElement.listItemElement.appendChild(toggleButton);
377 _refreshHTTPInformation: function()
379 var requestMethodElement = this._requestMethodTreeElement;
380 requestMethodElement.hidden = !this._request.statusCode;
381 var statusCodeElement = this._statusCodeTreeElement;
382 statusCodeElement.hidden = !this._request.statusCode;
384 if (this._request.statusCode) {
385 var statusCodeFragment = createDocumentFragment();
386 statusCodeFragment.createChild("div", "header-name").textContent = WebInspector.UIString("Status Code") + ":";
388 var statusCodeImage = statusCodeFragment.createChild("div", "resource-status-image");
389 statusCodeImage.title = this._request.statusCode + " " + this._request.statusText;
391 if (this._request.statusCode < 300 || this._request.statusCode === 304)
392 statusCodeImage.classList.add("green-ball");
393 else if (this._request.statusCode < 400)
394 statusCodeImage.classList.add("orange-ball");
396 statusCodeImage.classList.add("red-ball");
398 requestMethodElement.title = this._formatHeader(WebInspector.UIString("Request Method"), this._request.requestMethod);
400 var statusTextElement = statusCodeFragment.createChild("div", "header-value source-code");
401 var statusText = this._request.statusCode + " " + this._request.statusText;
402 if (this._request.fetchedViaServiceWorker) {
403 statusText += " " + WebInspector.UIString("(from ServiceWorker)");
404 statusTextElement.classList.add("status-from-cache");
405 } else if (this._request.cached()) {
406 statusText += " " + WebInspector.UIString("(from cache)");
407 statusTextElement.classList.add("status-from-cache");
409 statusTextElement.textContent = statusText;
411 statusCodeElement.title = statusCodeFragment;
416 * @param {string} title
417 * @param {!TreeElement} headersTreeElement
418 * @param {number} headersLength
420 _refreshHeadersTitle: function(title, headersTreeElement, headersLength)
422 headersTreeElement.listItemElement.removeChildren();
423 headersTreeElement.listItemElement.createTextChild(title);
425 var headerCount = WebInspector.UIString(" (%d)", headersLength);
426 headersTreeElement.listItemElement.createChild("span", "header-count").textContent = headerCount;
430 * @param {string} title
431 * @param {!Array.<!WebInspector.NetworkRequest.NameValue>} headers
432 * @param {!TreeElement} headersTreeElement
433 * @param {boolean=} provisionalHeaders
435 _refreshHeaders: function(title, headers, headersTreeElement, provisionalHeaders)
437 headersTreeElement.removeChildren();
439 var length = headers.length;
440 this._refreshHeadersTitle(title, headersTreeElement, length);
442 if (provisionalHeaders) {
443 var cautionText = WebInspector.UIString("Provisional headers are shown");
444 var cautionFragment = createDocumentFragment();
445 cautionFragment.createChild("div", "warning-icon-small");
446 cautionFragment.createChild("div", "caution").textContent = cautionText;
447 var cautionTreeElement = new TreeElement(cautionFragment);
448 cautionTreeElement.selectable = false;
449 headersTreeElement.appendChild(cautionTreeElement);
452 headersTreeElement.hidden = !length && !provisionalHeaders;
453 for (var i = 0; i < length; ++i) {
454 var headerTreeElement = new TreeElement(this._formatHeader(headers[i].name, headers[i].value));
455 headerTreeElement.selectable = false;
456 headersTreeElement.appendChild(headerTreeElement);
461 * @param {string} title
462 * @param {number} count
463 * @param {string} headersText
464 * @param {!TreeElement} headersTreeElement
466 _refreshHeadersText: function(title, count, headersText, headersTreeElement)
468 this._populateTreeElementWithSourceText(headersTreeElement, headersText);
469 this._refreshHeadersTitle(title, headersTreeElement, count);
472 _refreshRemoteAddress: function()
474 var remoteAddress = this._request.remoteAddress();
475 var treeElement = this._remoteAddressTreeElement;
476 treeElement.hidden = !remoteAddress;
478 treeElement.title = this._formatHeader(WebInspector.UIString("Remote Address"), remoteAddress);
482 * @param {!Event} event
484 _toggleRequestHeadersText: function(event)
486 this._showRequestHeadersText = !this._showRequestHeadersText;
487 this._refreshRequestHeaders();
491 * @param {!Event} event
493 _toggleResponseHeadersText: function(event)
495 this._showResponseHeadersText = !this._showResponseHeadersText;
496 this._refreshResponseHeaders();
500 * @param {string} title
503 _createToggleButton: function(title)
505 var button = createElement("span");
506 button.classList.add("header-toggle");
507 button.textContent = title;
512 * @param {boolean} isHeadersTextShown
515 _createHeadersToggleButton: function(isHeadersTextShown)
517 var toggleTitle = isHeadersTextShown ? WebInspector.UIString("view parsed") : WebInspector.UIString("view source");
518 return this._createToggleButton(toggleTitle);
521 __proto__: WebInspector.VBox.prototype