initial import
[vuplus_webkit] / Source / WebCore / inspector / front-end / ResourceHeadersView.js
1 /*
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.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
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.
18  *
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.
29  */
30
31 WebInspector.ResourceHeadersView = function(resource)
32 {
33     WebInspector.View.call(this);
34     this.element.addStyleClass("resource-headers-view");
35
36     this._resource = resource;
37
38     this._headersListElement = document.createElement("ol");
39     this._headersListElement.className = "outline-disclosure";
40     this.element.appendChild(this._headersListElement);
41
42     this._headersTreeOutline = new TreeOutline(this._headersListElement);
43     this._headersTreeOutline.expandTreeElementsWhenArrowing = true;
44
45     this._urlTreeElement = new TreeElement("", null, false);
46     this._urlTreeElement.selectable = false;
47     this._headersTreeOutline.appendChild(this._urlTreeElement);
48
49     this._requestMethodTreeElement = new TreeElement("", null, false);
50     this._requestMethodTreeElement.selectable = false;
51     this._headersTreeOutline.appendChild(this._requestMethodTreeElement);
52
53     this._statusCodeTreeElement = new TreeElement("", null, false);
54     this._statusCodeTreeElement.selectable = false;
55     this._headersTreeOutline.appendChild(this._statusCodeTreeElement);
56
57     this._requestHeadersTreeElement = new TreeElement("", null, true);
58     this._requestHeadersTreeElement.expanded = true;
59     this._requestHeadersTreeElement.selectable = false;
60     this._headersTreeOutline.appendChild(this._requestHeadersTreeElement);
61
62     this._decodeRequestParameters = true;
63
64     this._showRequestHeadersText = false;
65     this._showResponseHeadersText = false;
66
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);
72
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);
78
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);
84
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);
90
91     this._responseHeadersTreeElement = new TreeElement("", null, true);
92     this._responseHeadersTreeElement.expanded = true;
93     this._responseHeadersTreeElement.selectable = false;
94     this._headersTreeOutline.appendChild(this._responseHeadersTreeElement);
95
96     resource.addEventListener("requestHeaders changed", this._refreshRequestHeaders, this);
97     resource.addEventListener("responseHeaders changed", this._refreshResponseHeaders, this);
98     resource.addEventListener("finished", this._refreshHTTPInformation, this);
99
100     this._refreshURL();
101     this._refreshQueryString();
102     this._refreshUrlFragment();
103     this._refreshRequestHeaders();
104     this._refreshResponseHeaders();
105     this._refreshHTTPInformation();
106 }
107
108 WebInspector.ResourceHeadersView.prototype = {
109
110     _refreshURL: function()
111     {
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>";
114     },
115
116     _refreshQueryString: function()
117     {
118         var queryParameters = this._resource.queryParameters;
119         this._queryStringTreeElement.hidden = !queryParameters;
120         if (queryParameters)
121             this._refreshParms(WebInspector.UIString("Query String Parameters"), queryParameters, this._queryStringTreeElement);
122     },
123
124     _refreshUrlFragment: function()
125     {
126         var urlFragment = this._resource.urlFragment;
127         this._urlFragmentTreeElement.hidden = !urlFragment;
128
129         if (!urlFragment)
130             return;
131
132         var sectionTitle = WebInspector.UIString("URL fragment");
133
134         this._urlFragmentTreeElement.removeChildren();
135         this._urlFragmentTreeElement.listItemElement.removeChildren();
136         this._urlFragmentTreeElement.listItemElement.appendChild(document.createTextNode(sectionTitle));
137
138         var title = "<div class=\"header-name\">#:</div>";
139         title += "<div class=\"header-value source-code\">" + urlFragment.escapeHTML() + "</div>";
140
141         var fragmentTreeElement = new TreeElement(null, null, false);
142         fragmentTreeElement.titleHTML = title;
143         fragmentTreeElement.selectable = false;
144         this._urlFragmentTreeElement.appendChild(fragmentTreeElement);
145     },
146
147     _refreshFormData: function()
148     {
149         this._formDataTreeElement.hidden = true;
150         this._requestPayloadTreeElement.hidden = true;
151
152         var formData = this._resource.requestFormData;
153         if (!formData)
154             return;
155
156         var formParameters = this._resource.formParameters;
157         if (formParameters) {
158             this._formDataTreeElement.hidden = false;
159             this._refreshParms(WebInspector.UIString("Form Data"), formParameters, this._formDataTreeElement);
160         } else {
161             this._requestPayloadTreeElement.hidden = false;
162             this._refreshRequestPayload(formData);
163         }
164     },
165
166     _refreshRequestPayload: function(formData)
167     {
168         this._requestPayloadTreeElement.removeChildren();
169
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);
175     },
176
177     _decodeURIComponent: function(value)
178     {
179         var errorDecoding = false;
180
181         if (value.indexOf("%") >= 0) {
182             try {
183                 value = decodeURIComponent(value);
184             } catch(e) {
185                 errorDecoding = true;
186             }
187         }
188         value = value.replace(/\+/g, " ");
189
190         var valueEscaped = value.escapeHTML();
191         if (errorDecoding)
192             valueEscaped += " <span class=\"error-message\">" + WebInspector.UIString("(unable to decode value)").escapeHTML() + "</span>";
193
194         return valueEscaped;
195     },
196
197     _refreshParms: function(title, parms, parmsTreeElement)
198     {
199         parmsTreeElement.removeChildren();
200
201         parmsTreeElement.listItemElement.removeChildren();
202         parmsTreeElement.listItemElement.appendChild(document.createTextNode(title));
203
204         var headerCount = document.createElement("span");
205         headerCount.addStyleClass("header-count");
206         headerCount.textContent = WebInspector.UIString(" (%d)", parms.length);
207         parmsTreeElement.listItemElement.appendChild(headerCount);
208
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);
213
214
215         for (var i = 0; i < parms.length; ++i) {
216             var name = parms[i].name;
217             var value = parms[i].value;
218
219             var valueEscaped = this._decodeRequestParameters ? this._decodeURIComponent(value) : value.escapeHTML();
220             var nameEscaped = this._decodeRequestParameters ? this._decodeURIComponent(name) : name.escapeHTML();
221
222             var title = "<div class=\"header-name\">" + nameEscaped + ":</div>";
223             title += "<div class=\"header-value source-code\">" + valueEscaped + "</div>";
224
225             var parmTreeElement = new TreeElement(null, null, false);
226             parmTreeElement.titleHTML = title;
227             parmTreeElement.selectable = false;
228             parmsTreeElement.appendChild(parmTreeElement);
229         }
230     },
231
232     _toggleURLdecoding: function(event)
233     {
234         this._decodeRequestParameters = !this._decodeRequestParameters;
235         this._refreshQueryString();
236         this._refreshFormData();
237     },
238
239     _getHeaderValue: function(headers, key)
240     {
241         var lowerKey = key.toLowerCase();
242         for (var testKey in headers) {
243             if (testKey.toLowerCase() === lowerKey)
244                 return headers[testKey];
245         }
246     },
247
248     _refreshRequestHeaders: function()
249     {
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);
255         else
256             this._refreshHeaders(WebInspector.UIString("Request Headers"), this._resource.sortedRequestHeaders, additionalRow, this._requestHeadersTreeElement);
257
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);
262         }
263
264         this._refreshFormData();
265     },
266
267     _refreshResponseHeaders: function()
268     {
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);
274         else
275             this._refreshHeaders(WebInspector.UIString("Response Headers"), this._resource.sortedResponseHeaders, additionalRow, this._responseHeadersTreeElement);
276
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);
281         }
282     },
283
284     _refreshHTTPInformation: function()
285     {
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 = "";
291
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";
298             else
299                 statusImageSource = "Images/errorRedDot.png";
300
301             var statusTextEscaped = this._resource.statusCode + " " + this._resource.statusText.escapeHTML();
302             statusCodeImage = "<img class=\"resource-status-image\" src=\"" + statusImageSource + "\" title=\"" + statusTextEscaped + "\">";
303
304             requestMethodElement.titleHTML = "<div class=\"header-name\">" + WebInspector.UIString("Request Method") + ":</div>" +
305                 "<div class=\"header-value source-code\">" + this._resource.requestMethod + "</div>";
306
307             var fromCacheSpan = "";
308             if (this._resource.cached)
309                 fromCacheSpan = " " + "<span class=\"status-from-cache\">" + WebInspector.UIString("(from cache)").escapeHTML() + "</span>";
310
311             statusCodeElement.titleHTML = "<div class=\"header-name\">" + WebInspector.UIString("Status Code") + ":</div>" +
312                 statusCodeImage + "<div class=\"header-value source-code\">" + statusTextEscaped + fromCacheSpan + "</div>";
313         }
314     },
315
316     _refreshHeadersTitle: function(title, headersTreeElement, isHeadersTextShown, headersLength)
317     {
318         headersTreeElement.listItemElement.removeChildren();
319         headersTreeElement.listItemElement.appendChild(document.createTextNode(title));
320
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);
326         }
327     },
328
329     _refreshHeaders: function(title, headers, additionalRow, headersTreeElement)
330     {
331         headersTreeElement.removeChildren();
332
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>"
339
340             var headerTreeElement = new TreeElement(null, null, false);
341             headerTreeElement.titleHTML = title;
342             headerTreeElement.selectable = false;
343             headersTreeElement.appendChild(headerTreeElement);
344         }
345
346         if (additionalRow) {
347             var title = "<div class=\"header-name\">" + additionalRow.header.escapeHTML() + ":</div>";
348             title += "<div class=\"header-value source-code\">" + additionalRow.value.escapeHTML() + "</div>"
349
350             var headerTreeElement = new TreeElement(null, null, false);
351             headerTreeElement.titleHTML = title;
352             headerTreeElement.selectable = false;
353             headersTreeElement.appendChild(headerTreeElement);
354         }
355     },
356
357     _refreshHeadersText: function(title, headersText, headersTreeElement)
358     {
359         headersTreeElement.removeChildren();
360
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");
366
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);
372     },
373
374     _toggleRequestHeadersText: function(event)
375     {
376         this._showRequestHeadersText = !this._showRequestHeadersText;
377         this._refreshRequestHeaders();
378     },
379
380     _toggleResponseHeadersText: function(event)
381     {
382         this._showResponseHeadersText = !this._showResponseHeadersText;
383         this._refreshResponseHeaders();
384     },
385
386     _createToggleButton: function(title)
387     {
388         var button = document.createElement("span");
389         button.addStyleClass("header-toggle");
390         button.textContent = title;
391         return button;
392     },
393
394     _createHeadersToggleButton: function(isHeadersTextShown)
395     {
396         var toggleTitle = isHeadersTextShown ? WebInspector.UIString("view parsed") : WebInspector.UIString("view source");
397         return this._createToggleButton(toggleTitle);
398     }
399 }
400
401 WebInspector.ResourceHeadersView.prototype.__proto__ = WebInspector.View.prototype;