04c8a4ef1fc02b6ed4cd852d7cfa93832ba6a7a4
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / network / RequestHeadersView.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 /**
32  * @constructor
33  * @extends {WebInspector.VBox}
34  * @param {!WebInspector.NetworkRequest} request
35  */
36 WebInspector.RequestHeadersView = function(request)
37 {
38     WebInspector.VBox.call(this);
39     this.registerRequiredCSS("resourceView.css");
40     this.registerRequiredCSS("requestHeadersView.css");
41     this.element.classList.add("request-headers-view");
42
43     this._request = request;
44
45     this._headersListElement = document.createElement("ol");
46     this._headersListElement.className = "outline-disclosure";
47     this.element.appendChild(this._headersListElement);
48
49     this._headersTreeOutline = new TreeOutline(this._headersListElement);
50     this._headersTreeOutline.expandTreeElementsWhenArrowing = true;
51
52     this._remoteAddressTreeElement = new TreeElement("", null, false);
53     this._remoteAddressTreeElement.selectable = false;
54     this._remoteAddressTreeElement.hidden = true;
55     this._headersTreeOutline.appendChild(this._remoteAddressTreeElement);
56
57     this._urlTreeElement = new TreeElement("", null, false);
58     this._urlTreeElement.selectable = false;
59     this._headersTreeOutline.appendChild(this._urlTreeElement);
60
61     this._requestMethodTreeElement = new TreeElement("", null, false);
62     this._requestMethodTreeElement.selectable = false;
63     this._headersTreeOutline.appendChild(this._requestMethodTreeElement);
64
65     this._statusCodeTreeElement = new TreeElement("", null, false);
66     this._statusCodeTreeElement.selectable = false;
67     this._headersTreeOutline.appendChild(this._statusCodeTreeElement);
68
69     this._requestHeadersTreeElement = new TreeElement("", null, true);
70     this._requestHeadersTreeElement.expanded = true;
71     this._requestHeadersTreeElement.selectable = false;
72     this._headersTreeOutline.appendChild(this._requestHeadersTreeElement);
73
74     this._decodeRequestParameters = true;
75
76     this._showRequestHeadersText = false;
77     this._showResponseHeadersText = false;
78
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);
84
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);
90
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);
96
97     this._responseHeadersTreeElement = new TreeElement("", null, true);
98     this._responseHeadersTreeElement.expanded = true;
99     this._responseHeadersTreeElement.selectable = false;
100     this._headersTreeOutline.appendChild(this._responseHeadersTreeElement);
101 }
102
103 WebInspector.RequestHeadersView.prototype = {
104
105     wasShown: function()
106     {
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);
111
112         this._refreshURL();
113         this._refreshQueryString();
114         this._refreshRequestHeaders();
115         this._refreshResponseHeaders();
116         this._refreshHTTPInformation();
117         this._refreshRemoteAddress();
118     },
119
120     willHide: function()
121     {
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);
126     },
127
128     /**
129      * @param {string} name
130      * @param {string} value
131      * @return {!DocumentFragment}
132      */
133     _formatHeader: function(name, value)
134     {
135         var fragment = document.createDocumentFragment();
136         fragment.createChild("div", "header-name").textContent = name + ":";
137         fragment.createChild("div", "header-value source-code").textContent = value;
138
139         return fragment;
140     },
141
142     /**
143      * @param {string} value
144      * @param {string} className
145      * @param {boolean} decodeParameters
146      * @return {!Element}
147      */
148     _formatParameter: function(value, className, decodeParameters)
149     {
150         var errorDecoding = false;
151
152         if (decodeParameters) {
153             value = value.replace(/\+/g, " ");
154             if (value.indexOf("%") >= 0) {
155                 try {
156                     value = decodeURIComponent(value);
157                 } catch (e) {
158                     errorDecoding = true;
159                 }
160             }
161         }
162         var div = document.createElement("div");
163         div.className = className;
164         if (errorDecoding)
165             div.createChild("span", "error-message").textContent = WebInspector.UIString("(unable to decode value)");
166         else
167             div.textContent = value;
168         return div;
169     },
170
171     _refreshURL: function()
172     {
173         this._urlTreeElement.title = this._formatHeader(WebInspector.UIString("Request URL"), this._request.url);
174     },
175
176     _refreshQueryString: function()
177     {
178         var queryString = this._request.queryString();
179         var queryParameters = this._request.queryParameters;
180         this._queryStringTreeElement.hidden = !queryParameters;
181         if (queryParameters)
182             this._refreshParams(WebInspector.UIString("Query String Parameters"), queryParameters, queryString, this._queryStringTreeElement);
183     },
184
185     _refreshFormData: function()
186     {
187         this._formDataTreeElement.hidden = true;
188         this._requestPayloadTreeElement.hidden = true;
189
190         var formData = this._request.requestFormData;
191         if (!formData)
192             return;
193
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);
198         } else {
199             this._requestPayloadTreeElement.hidden = false;
200             try {
201                 var json = JSON.parse(formData);
202                 this._refreshRequestJSONPayload(json, formData);
203             } catch (e) {
204                 this._populateTreeElementWithSourceText(this._requestPayloadTreeElement, formData);
205             }
206         }
207     },
208
209     /**
210      * @param {!TreeElement} treeElement
211      * @param {?string} sourceText
212      */
213     _populateTreeElementWithSourceText: function(treeElement, sourceText)
214     {
215         var sourceTextElement = document.createElement("span");
216         sourceTextElement.classList.add("header-value");
217         sourceTextElement.classList.add("source-code");
218         sourceTextElement.textContent = String(sourceText || "").trim();
219
220         var sourceTreeElement = new TreeElement(sourceTextElement);
221         sourceTreeElement.selectable = false;
222         treeElement.removeChildren();
223         treeElement.appendChild(sourceTreeElement);
224     },
225
226     /**
227      * @param {string} title
228      * @param {?Array.<!WebInspector.NetworkRequest.NameValue>} params
229      * @param {?string} sourceText
230      * @param {!TreeElement} paramsTreeElement
231      */
232     _refreshParams: function(title, params, sourceText, paramsTreeElement)
233     {
234         paramsTreeElement.removeChildren();
235
236         paramsTreeElement.listItemElement.removeChildren();
237         paramsTreeElement.listItemElement.appendChild(document.createTextNode(title));
238
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);
243
244         /**
245          * @param {!Event} event
246          * @this {WebInspector.RequestHeadersView}
247          */
248         function toggleViewSource(event)
249         {
250             paramsTreeElement._viewSource = !paramsTreeElement._viewSource;
251             this._refreshParams(title, params, sourceText, paramsTreeElement);
252         }
253
254         paramsTreeElement.listItemElement.appendChild(this._createViewSourceToggle(paramsTreeElement._viewSource, toggleViewSource.bind(this)));
255
256         if (paramsTreeElement._viewSource) {
257             this._populateTreeElementWithSourceText(paramsTreeElement, sourceText);
258             return;
259         }
260
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);
265
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);
272
273             var parmTreeElement = new TreeElement(paramNameValue, null, false);
274             parmTreeElement.selectable = false;
275             paramsTreeElement.appendChild(parmTreeElement);
276         }
277     },
278
279     /**
280      * @param {*} parsedObject
281      * @param {string} sourceText
282      */
283     _refreshRequestJSONPayload: function(parsedObject, sourceText)
284     {
285         var treeElement = this._requestPayloadTreeElement;
286         treeElement.removeChildren();
287
288         var listItem = this._requestPayloadTreeElement.listItemElement;
289         listItem.removeChildren();
290         listItem.appendChild(document.createTextNode(this._requestPayloadTreeElement.title));
291
292         /**
293          * @param {!Event} event
294          * @this {WebInspector.RequestHeadersView}
295          */
296         function toggleViewSource(event)
297         {
298             treeElement._viewSource = !treeElement._viewSource;
299             this._refreshRequestJSONPayload(parsedObject, sourceText);
300         }
301
302         listItem.appendChild(this._createViewSourceToggle(treeElement._viewSource, toggleViewSource.bind(this)));
303         if (treeElement._viewSource) {
304             this._populateTreeElementWithSourceText(this._requestPayloadTreeElement, sourceText);
305         } else {
306             var object = WebInspector.RemoteObject.fromLocalObject(parsedObject);
307             var section = new WebInspector.ObjectPropertiesSection(object, object.description);
308             section.expand();
309             section.editable = false;
310             listItem.appendChild(section.element);
311         }
312     },
313
314     /**
315      * @param {boolean} viewSource
316      * @param {function(!Event)} handler
317      * @return {!Element}
318      */
319     _createViewSourceToggle: function(viewSource, handler)
320     {
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;
325     },
326
327     /**
328      * @param {!Event} event
329      */
330     _toggleURLDecoding: function(event)
331     {
332         this._decodeRequestParameters = !this._decodeRequestParameters;
333         this._refreshQueryString();
334         this._refreshFormData();
335     },
336
337     _refreshRequestHeaders: function()
338     {
339         var treeElement = this._requestHeadersTreeElement;
340
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();
345
346         if (this._showRequestHeadersText && headersText)
347             this._refreshHeadersText(WebInspector.UIString("Request Headers"), headers.length, headersText, treeElement);
348         else
349             this._refreshHeaders(WebInspector.UIString("Request Headers"), headers, treeElement, headersText === undefined);
350
351         if (headersText) {
352             var toggleButton = this._createHeadersToggleButton(this._showRequestHeadersText);
353             toggleButton.addEventListener("click", this._toggleRequestHeadersText.bind(this), false);
354             treeElement.listItemElement.appendChild(toggleButton);
355         }
356
357         this._refreshFormData();
358     },
359
360     _refreshResponseHeaders: function()
361     {
362         var treeElement = this._responseHeadersTreeElement;
363         var headers = this._request.sortedResponseHeaders;
364         var headersText = this._request.responseHeadersText;
365
366         if (this._showResponseHeadersText)
367             this._refreshHeadersText(WebInspector.UIString("Response Headers"), headers.length, headersText, treeElement);
368         else
369             this._refreshHeaders(WebInspector.UIString("Response Headers"), headers, treeElement);
370
371         if (headersText) {
372             var toggleButton = this._createHeadersToggleButton(this._showResponseHeadersText);
373             toggleButton.addEventListener("click", this._toggleResponseHeadersText.bind(this), false);
374             treeElement.listItemElement.appendChild(toggleButton);
375         }
376     },
377
378     _refreshHTTPInformation: function()
379     {
380         var requestMethodElement = this._requestMethodTreeElement;
381         requestMethodElement.hidden = !this._request.statusCode;
382         var statusCodeElement = this._statusCodeTreeElement;
383         statusCodeElement.hidden = !this._request.statusCode;
384
385         if (this._request.statusCode) {
386             var statusCodeFragment = document.createDocumentFragment();
387             statusCodeFragment.createChild("div", "header-name").textContent = WebInspector.UIString("Status Code") + ":";
388
389             var statusCodeImage = statusCodeFragment.createChild("div", "resource-status-image");
390             statusCodeImage.title = this._request.statusCode + " " + this._request.statusText;
391
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");
396             else
397                 statusCodeImage.classList.add("red-ball");
398
399             requestMethodElement.title = this._formatHeader(WebInspector.UIString("Request Method"), this._request.requestMethod);
400
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");
406             }
407             statusTextElement.textContent = statusText;
408
409             statusCodeElement.title = statusCodeFragment;
410         }
411     },
412
413     /**
414      * @param {string} title
415      * @param {!TreeElement} headersTreeElement
416      * @param {number} headersLength
417      */
418     _refreshHeadersTitle: function(title, headersTreeElement, headersLength)
419     {
420         headersTreeElement.listItemElement.removeChildren();
421         headersTreeElement.listItemElement.createTextChild(title);
422
423         var headerCount = WebInspector.UIString(" (%d)", headersLength);
424         headersTreeElement.listItemElement.createChild("span", "header-count").textContent = headerCount;
425     },
426
427     /**
428      * @param {string} title
429      * @param {!Array.<!WebInspector.NetworkRequest.NameValue>} headers
430      * @param {!TreeElement} headersTreeElement
431      * @param {boolean=} provisionalHeaders
432      */
433     _refreshHeaders: function(title, headers, headersTreeElement, provisionalHeaders)
434     {
435         headersTreeElement.removeChildren();
436
437         var length = headers.length;
438         this._refreshHeadersTitle(title, headersTreeElement, length);
439
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);
448         }
449
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);
455         }
456     },
457
458     /**
459      * @param {string} title
460      * @param {number} count
461      * @param {string} headersText
462      * @param {!TreeElement} headersTreeElement
463      */
464     _refreshHeadersText: function(title, count, headersText, headersTreeElement)
465     {
466         this._populateTreeElementWithSourceText(headersTreeElement, headersText);
467         this._refreshHeadersTitle(title, headersTreeElement, count);
468     },
469
470     _refreshRemoteAddress: function()
471     {
472         var remoteAddress = this._request.remoteAddress();
473         var treeElement = this._remoteAddressTreeElement;
474         treeElement.hidden = !remoteAddress;
475         if (remoteAddress)
476             treeElement.title = this._formatHeader(WebInspector.UIString("Remote Address"), remoteAddress);
477     },
478
479     /**
480      * @param {!Event} event
481      */
482     _toggleRequestHeadersText: function(event)
483     {
484         this._showRequestHeadersText = !this._showRequestHeadersText;
485         this._refreshRequestHeaders();
486     },
487
488     /**
489      * @param {!Event} event
490      */
491     _toggleResponseHeadersText: function(event)
492     {
493         this._showResponseHeadersText = !this._showResponseHeadersText;
494         this._refreshResponseHeaders();
495     },
496
497     /**
498      * @param {string} title
499      * @return {!Element}
500      */
501     _createToggleButton: function(title)
502     {
503         var button = document.createElement("span");
504         button.classList.add("header-toggle");
505         button.textContent = title;
506         return button;
507     },
508
509     /**
510      * @param {boolean} isHeadersTextShown
511      * @return {!Element}
512      */
513     _createHeadersToggleButton: function(isHeadersTextShown)
514     {
515         var toggleTitle = isHeadersTextShown ? WebInspector.UIString("view parsed") : WebInspector.UIString("view source");
516         return this._createToggleButton(toggleTitle);
517     },
518
519     __proto__: WebInspector.VBox.prototype
520 }