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.
31 WebInspector.ResourceHeadersView = function(resource)
33 WebInspector.View.call(this);
34 this.element.addStyleClass("resource-headers-view");
36 this._resource = resource;
38 this._headersListElement = document.createElement("ol");
39 this._headersListElement.className = "outline-disclosure";
40 this.element.appendChild(this._headersListElement);
42 this._headersTreeOutline = new TreeOutline(this._headersListElement);
43 this._headersTreeOutline.expandTreeElementsWhenArrowing = true;
45 this._urlTreeElement = new TreeElement("", null, false);
46 this._urlTreeElement.selectable = false;
47 this._headersTreeOutline.appendChild(this._urlTreeElement);
49 this._requestMethodTreeElement = new TreeElement("", null, false);
50 this._requestMethodTreeElement.selectable = false;
51 this._headersTreeOutline.appendChild(this._requestMethodTreeElement);
53 this._statusCodeTreeElement = new TreeElement("", null, false);
54 this._statusCodeTreeElement.selectable = false;
55 this._headersTreeOutline.appendChild(this._statusCodeTreeElement);
57 this._requestHeadersTreeElement = new TreeElement("", null, true);
58 this._requestHeadersTreeElement.expanded = true;
59 this._requestHeadersTreeElement.selectable = false;
60 this._headersTreeOutline.appendChild(this._requestHeadersTreeElement);
62 this._decodeRequestParameters = true;
64 this._showRequestHeadersText = false;
65 this._showResponseHeadersText = false;
67 this._queryStringTreeElement = new TreeElement("", null, true);
68 this._queryStringTreeElement.expanded = true;
69 this._queryStringTreeElement.selectable = false;
70 this._queryStringTreeElement.hidden = true;
71 this._headersTreeOutline.appendChild(this._queryStringTreeElement);
73 this._urlFragmentTreeElement = new TreeElement("", null, true);
74 this._urlFragmentTreeElement.expanded = true;
75 this._urlFragmentTreeElement.selectable = false;
76 this._urlFragmentTreeElement.hidden = true;
77 this._headersTreeOutline.appendChild(this._urlFragmentTreeElement);
79 this._formDataTreeElement = new TreeElement("", null, true);
80 this._formDataTreeElement.expanded = true;
81 this._formDataTreeElement.selectable = false;
82 this._formDataTreeElement.hidden = true;
83 this._headersTreeOutline.appendChild(this._formDataTreeElement);
85 this._requestPayloadTreeElement = new TreeElement(WebInspector.UIString("Request Payload"), null, true);
86 this._requestPayloadTreeElement.expanded = true;
87 this._requestPayloadTreeElement.selectable = false;
88 this._requestPayloadTreeElement.hidden = true;
89 this._headersTreeOutline.appendChild(this._requestPayloadTreeElement);
91 this._responseHeadersTreeElement = new TreeElement("", null, true);
92 this._responseHeadersTreeElement.expanded = true;
93 this._responseHeadersTreeElement.selectable = false;
94 this._headersTreeOutline.appendChild(this._responseHeadersTreeElement);
96 resource.addEventListener("requestHeaders changed", this._refreshRequestHeaders, this);
97 resource.addEventListener("responseHeaders changed", this._refreshResponseHeaders, this);
98 resource.addEventListener("finished", this._refreshHTTPInformation, this);
101 this._refreshQueryString();
102 this._refreshUrlFragment();
103 this._refreshRequestHeaders();
104 this._refreshResponseHeaders();
105 this._refreshHTTPInformation();
108 WebInspector.ResourceHeadersView.prototype = {
110 _refreshURL: function()
112 this._urlTreeElement.titleHTML = "<div class=\"header-name\">" + WebInspector.UIString("Request URL") + ":</div>" +
113 "<div class=\"header-value source-code\">" + this._resource.url.escapeHTML() + "</div>";
116 _refreshQueryString: function()
118 var queryParameters = this._resource.queryParameters;
119 this._queryStringTreeElement.hidden = !queryParameters;
121 this._refreshParms(WebInspector.UIString("Query String Parameters"), queryParameters, this._queryStringTreeElement);
124 _refreshUrlFragment: function()
126 var urlFragment = this._resource.urlFragment;
127 this._urlFragmentTreeElement.hidden = !urlFragment;
132 var sectionTitle = WebInspector.UIString("URL fragment");
134 this._urlFragmentTreeElement.removeChildren();
135 this._urlFragmentTreeElement.listItemElement.removeChildren();
136 this._urlFragmentTreeElement.listItemElement.appendChild(document.createTextNode(sectionTitle));
138 var title = "<div class=\"header-name\">#:</div>";
139 title += "<div class=\"header-value source-code\">" + urlFragment.escapeHTML() + "</div>";
141 var fragmentTreeElement = new TreeElement(null, null, false);
142 fragmentTreeElement.titleHTML = title;
143 fragmentTreeElement.selectable = false;
144 this._urlFragmentTreeElement.appendChild(fragmentTreeElement);
147 _refreshFormData: function()
149 this._formDataTreeElement.hidden = true;
150 this._requestPayloadTreeElement.hidden = true;
152 var formData = this._resource.requestFormData;
156 var formParameters = this._resource.formParameters;
157 if (formParameters) {
158 this._formDataTreeElement.hidden = false;
159 this._refreshParms(WebInspector.UIString("Form Data"), formParameters, this._formDataTreeElement);
161 this._requestPayloadTreeElement.hidden = false;
162 this._refreshRequestPayload(formData);
166 _refreshRequestPayload: function(formData)
168 this._requestPayloadTreeElement.removeChildren();
170 var title = "<div class=\"raw-form-data header-value source-code\">" + formData.escapeHTML() + "</div>";
171 var parmTreeElement = new TreeElement(null, null, false);
172 parmTreeElement.titleHTML = title;
173 parmTreeElement.selectable = false;
174 this._requestPayloadTreeElement.appendChild(parmTreeElement);
177 _decodeURIComponent: function(value)
179 var errorDecoding = false;
181 if (value.indexOf("%") >= 0) {
183 value = decodeURIComponent(value);
185 errorDecoding = true;
188 value = value.replace(/\+/g, " ");
190 var valueEscaped = value.escapeHTML();
192 valueEscaped += " <span class=\"error-message\">" + WebInspector.UIString("(unable to decode value)").escapeHTML() + "</span>";
197 _refreshParms: function(title, parms, parmsTreeElement)
199 parmsTreeElement.removeChildren();
201 parmsTreeElement.listItemElement.removeChildren();
202 parmsTreeElement.listItemElement.appendChild(document.createTextNode(title));
204 var headerCount = document.createElement("span");
205 headerCount.addStyleClass("header-count");
206 headerCount.textContent = WebInspector.UIString(" (%d)", parms.length);
207 parmsTreeElement.listItemElement.appendChild(headerCount);
209 var toggleTitle = this._decodeRequestParameters ? WebInspector.UIString("view URL encoded") : WebInspector.UIString("view decoded");
210 var toggleButton = this._createToggleButton(toggleTitle);
211 toggleButton.addEventListener("click", this._toggleURLdecoding.bind(this));
212 parmsTreeElement.listItemElement.appendChild(toggleButton);
215 for (var i = 0; i < parms.length; ++i) {
216 var name = parms[i].name;
217 var value = parms[i].value;
219 var valueEscaped = this._decodeRequestParameters ? this._decodeURIComponent(value) : value.escapeHTML();
220 var nameEscaped = this._decodeRequestParameters ? this._decodeURIComponent(name) : name.escapeHTML();
222 var title = "<div class=\"header-name\">" + nameEscaped + ":</div>";
223 title += "<div class=\"header-value source-code\">" + valueEscaped + "</div>";
225 var parmTreeElement = new TreeElement(null, null, false);
226 parmTreeElement.titleHTML = title;
227 parmTreeElement.selectable = false;
228 parmsTreeElement.appendChild(parmTreeElement);
232 _toggleURLdecoding: function(event)
234 this._decodeRequestParameters = !this._decodeRequestParameters;
235 this._refreshQueryString();
236 this._refreshFormData();
239 _getHeaderValue: function(headers, key)
241 var lowerKey = key.toLowerCase();
242 for (var testKey in headers) {
243 if (testKey.toLowerCase() === lowerKey)
244 return headers[testKey];
248 _refreshRequestHeaders: function()
250 var additionalRow = null;
251 if (typeof this._resource.webSocketRequestKey3 !== "undefined")
252 additionalRow = {header: "(Key3)", value: this._resource.webSocketRequestKey3};
253 if (this._showRequestHeadersText)
254 this._refreshHeadersText(WebInspector.UIString("Request Headers"), this._resource.requestHeadersText, this._requestHeadersTreeElement);
256 this._refreshHeaders(WebInspector.UIString("Request Headers"), this._resource.sortedRequestHeaders, additionalRow, this._requestHeadersTreeElement);
258 if (this._resource.requestHeadersText) {
259 var toggleButton = this._createHeadersToggleButton(this._showRequestHeadersText);
260 toggleButton.addEventListener("click", this._toggleRequestHeadersText.bind(this));
261 this._requestHeadersTreeElement.listItemElement.appendChild(toggleButton);
264 this._refreshFormData();
267 _refreshResponseHeaders: function()
269 var additionalRow = null;
270 if (typeof this._resource.webSocketChallengeResponse !== "undefined")
271 additionalRow = {header: "(Challenge Response)", value: this._resource.webSocketChallengeResponse};
272 if (this._showResponseHeadersText)
273 this._refreshHeadersText(WebInspector.UIString("Response Headers"), this._resource.responseHeadersText, this._responseHeadersTreeElement);
275 this._refreshHeaders(WebInspector.UIString("Response Headers"), this._resource.sortedResponseHeaders, additionalRow, this._responseHeadersTreeElement);
277 if (this._resource.responseHeadersText) {
278 var toggleButton = this._createHeadersToggleButton(this._showResponseHeadersText);
279 toggleButton.addEventListener("click", this._toggleResponseHeadersText.bind(this));
280 this._responseHeadersTreeElement.listItemElement.appendChild(toggleButton);
284 _refreshHTTPInformation: function()
286 var requestMethodElement = this._requestMethodTreeElement;
287 requestMethodElement.hidden = !this._resource.statusCode;
288 var statusCodeElement = this._statusCodeTreeElement;
289 statusCodeElement.hidden = !this._resource.statusCode;
290 var statusCodeImage = "";
292 if (this._resource.statusCode) {
293 var statusImageSource = "";
294 if (this._resource.statusCode < 300 || this._resource.statusCode === 304)
295 statusImageSource = "Images/successGreenDot.png";
296 else if (this._resource.statusCode < 400)
297 statusImageSource = "Images/warningOrangeDot.png";
299 statusImageSource = "Images/errorRedDot.png";
301 var statusTextEscaped = this._resource.statusCode + " " + this._resource.statusText.escapeHTML();
302 statusCodeImage = "<img class=\"resource-status-image\" src=\"" + statusImageSource + "\" title=\"" + statusTextEscaped + "\">";
304 requestMethodElement.titleHTML = "<div class=\"header-name\">" + WebInspector.UIString("Request Method") + ":</div>" +
305 "<div class=\"header-value source-code\">" + this._resource.requestMethod + "</div>";
307 var fromCacheSpan = "";
308 if (this._resource.cached)
309 fromCacheSpan = " " + "<span class=\"status-from-cache\">" + WebInspector.UIString("(from cache)").escapeHTML() + "</span>";
311 statusCodeElement.titleHTML = "<div class=\"header-name\">" + WebInspector.UIString("Status Code") + ":</div>" +
312 statusCodeImage + "<div class=\"header-value source-code\">" + statusTextEscaped + fromCacheSpan + "</div>";
316 _refreshHeadersTitle: function(title, headersTreeElement, isHeadersTextShown, headersLength)
318 headersTreeElement.listItemElement.removeChildren();
319 headersTreeElement.listItemElement.appendChild(document.createTextNode(title));
321 if (!isHeadersTextShown) {
322 var headerCount = document.createElement("span");
323 headerCount.addStyleClass("header-count");
324 headerCount.textContent = WebInspector.UIString(" (%d)", headersLength);
325 headersTreeElement.listItemElement.appendChild(headerCount);
329 _refreshHeaders: function(title, headers, additionalRow, headersTreeElement)
331 headersTreeElement.removeChildren();
333 var length = headers.length;
334 this._refreshHeadersTitle(title, headersTreeElement, false, length);
335 headersTreeElement.hidden = !length;
336 for (var i = 0; i < length; ++i) {
337 var title = "<div class=\"header-name\">" + headers[i].header.escapeHTML() + ":</div>";
338 title += "<div class=\"header-value source-code\">" + headers[i].value.escapeHTML() + "</div>"
340 var headerTreeElement = new TreeElement(null, null, false);
341 headerTreeElement.titleHTML = title;
342 headerTreeElement.selectable = false;
343 headersTreeElement.appendChild(headerTreeElement);
347 var title = "<div class=\"header-name\">" + additionalRow.header.escapeHTML() + ":</div>";
348 title += "<div class=\"header-value source-code\">" + additionalRow.value.escapeHTML() + "</div>"
350 var headerTreeElement = new TreeElement(null, null, false);
351 headerTreeElement.titleHTML = title;
352 headerTreeElement.selectable = false;
353 headersTreeElement.appendChild(headerTreeElement);
357 _refreshHeadersText: function(title, headersText, headersTreeElement)
359 headersTreeElement.removeChildren();
361 this._refreshHeadersTitle(title, headersTreeElement, true);
362 var headerTreeElement = new TreeElement(null, null, false);
363 headerTreeElement.selectable = false;
364 headersTreeElement.appendChild(headerTreeElement);
365 headerTreeElement.listItemElement.addStyleClass("headers-text");
367 var headersTextElement = document.createElement("span");
368 headersTextElement.addStyleClass("header-value");
369 headersTextElement.addStyleClass("source-code");
370 headersTextElement.textContent = String(headersText).trim();
371 headerTreeElement.listItemElement.appendChild(headersTextElement);
374 _toggleRequestHeadersText: function(event)
376 this._showRequestHeadersText = !this._showRequestHeadersText;
377 this._refreshRequestHeaders();
380 _toggleResponseHeadersText: function(event)
382 this._showResponseHeadersText = !this._showResponseHeadersText;
383 this._refreshResponseHeaders();
386 _createToggleButton: function(title)
388 var button = document.createElement("span");
389 button.addStyleClass("header-toggle");
390 button.textContent = title;
394 _createHeadersToggleButton: function(isHeadersTextShown)
396 var toggleTitle = isHeadersTextShown ? WebInspector.UIString("view parsed") : WebInspector.UIString("view source");
397 return this._createToggleButton(toggleTitle);
401 WebInspector.ResourceHeadersView.prototype.__proto__ = WebInspector.View.prototype;