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