• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
31WebInspector.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._formDataTreeElement = new TreeElement("", null, true);
74    this._formDataTreeElement.expanded = true;
75    this._formDataTreeElement.selectable = false;
76    this._formDataTreeElement.hidden = true;
77    this._headersTreeOutline.appendChild(this._formDataTreeElement);
78
79    this._requestPayloadTreeElement = new TreeElement(WebInspector.UIString("Request Payload"), null, true);
80    this._requestPayloadTreeElement.expanded = true;
81    this._requestPayloadTreeElement.selectable = false;
82    this._requestPayloadTreeElement.hidden = true;
83    this._headersTreeOutline.appendChild(this._requestPayloadTreeElement);
84
85    this._responseHeadersTreeElement = new TreeElement("", null, true);
86    this._responseHeadersTreeElement.expanded = true;
87    this._responseHeadersTreeElement.selectable = false;
88    this._headersTreeOutline.appendChild(this._responseHeadersTreeElement);
89
90    resource.addEventListener("requestHeaders changed", this._refreshRequestHeaders, this);
91    resource.addEventListener("responseHeaders changed", this._refreshResponseHeaders, this);
92    resource.addEventListener("finished", this._refreshHTTPInformation, this);
93
94    this._refreshURL();
95    this._refreshQueryString();
96    this._refreshRequestHeaders();
97    this._refreshResponseHeaders();
98    this._refreshHTTPInformation();
99}
100
101WebInspector.ResourceHeadersView.prototype = {
102
103    _refreshURL: function()
104    {
105        this._urlTreeElement.titleHTML = "<div class=\"header-name\">" + WebInspector.UIString("Request URL") + ":</div>" +
106            "<div class=\"header-value source-code\">" + this._resource.url.escapeHTML() + "</div>";
107    },
108
109    _refreshQueryString: function()
110    {
111        var queryParameters = this._resource.queryParameters;
112        this._queryStringTreeElement.hidden = !queryParameters;
113        if (queryParameters)
114            this._refreshParms(WebInspector.UIString("Query String Parameters"), queryParameters, this._queryStringTreeElement);
115    },
116
117    _refreshFormData: function()
118    {
119        this._formDataTreeElement.hidden = true;
120        this._requestPayloadTreeElement.hidden = true;
121
122        var formData = this._resource.requestFormData;
123        if (!formData)
124            return;
125
126        var formParameters = this._resource.formParameters;
127        if (formParameters) {
128            this._formDataTreeElement.hidden = false;
129            this._refreshParms(WebInspector.UIString("Form Data"), formParameters, this._formDataTreeElement);
130        } else {
131            this._requestPayloadTreeElement.hidden = false;
132            this._refreshRequestPayload(formData);
133        }
134    },
135
136    _refreshRequestPayload: function(formData)
137    {
138        this._requestPayloadTreeElement.removeChildren();
139
140        var title = "<div class=\"raw-form-data header-value source-code\">" + formData.escapeHTML() + "</div>";
141        var parmTreeElement = new TreeElement(null, null, false);
142        parmTreeElement.titleHTML = title;
143        parmTreeElement.selectable = false;
144        this._requestPayloadTreeElement.appendChild(parmTreeElement);
145    },
146
147    _refreshParms: function(title, parms, parmsTreeElement)
148    {
149        parmsTreeElement.removeChildren();
150
151        parmsTreeElement.listItemElement.removeChildren();
152        parmsTreeElement.listItemElement.appendChild(document.createTextNode(title));
153
154        var headerCount = document.createElement("span");
155        headerCount.addStyleClass("header-count");
156        headerCount.textContent = WebInspector.UIString(" (%d)", parms.length);
157        parmsTreeElement.listItemElement.appendChild(headerCount);
158
159        var toggleTitle = this._decodeRequestParameters ? WebInspector.UIString("view URL encoded") : WebInspector.UIString("view decoded");
160        var toggleButton = this._createToggleButton(toggleTitle);
161        toggleButton.addEventListener("click", this._toggleURLdecoding.bind(this));
162        parmsTreeElement.listItemElement.appendChild(toggleButton);
163
164
165        for (var i = 0; i < parms.length; ++i) {
166            var name = parms[i].name;
167            var value = parms[i].value;
168
169            var errorDecoding = false;
170            if (this._decodeRequestParameters) {
171                if (value.indexOf("%") >= 0) {
172                    try {
173                        value = decodeURIComponent(value);
174                    } catch(e) {
175                        errorDecoding = true;
176                    }
177                }
178
179                value = value.replace(/\+/g, " ");
180            }
181
182            valueEscaped = value.escapeHTML();
183            if (errorDecoding)
184                valueEscaped += " <span class=\"error-message\">" + WebInspector.UIString("(unable to decode value)").escapeHTML() + "</span>";
185
186            var title = "<div class=\"header-name\">" + name.escapeHTML() + ":</div>";
187            title += "<div class=\"header-value source-code\">" + valueEscaped + "</div>";
188
189            var parmTreeElement = new TreeElement(null, null, false);
190            parmTreeElement.titleHTML = title;
191            parmTreeElement.selectable = false;
192            parmsTreeElement.appendChild(parmTreeElement);
193        }
194    },
195
196    _toggleURLdecoding: function(event)
197    {
198        this._decodeRequestParameters = !this._decodeRequestParameters;
199        this._refreshQueryString();
200        this._refreshFormData();
201    },
202
203    _getHeaderValue: function(headers, key)
204    {
205        var lowerKey = key.toLowerCase();
206        for (var testKey in headers) {
207            if (testKey.toLowerCase() === lowerKey)
208                return headers[testKey];
209        }
210    },
211
212    _refreshRequestHeaders: function()
213    {
214        var additionalRow = null;
215        if (typeof this._resource.webSocketRequestKey3 !== "undefined")
216            additionalRow = {header: "(Key3)", value: this._resource.webSocketRequestKey3};
217        if (this._showRequestHeadersText)
218            this._refreshHeadersText(WebInspector.UIString("Request Headers"), this._resource.requestHeadersText, this._requestHeadersTreeElement);
219        else
220            this._refreshHeaders(WebInspector.UIString("Request Headers"), this._resource.sortedRequestHeaders, additionalRow, this._requestHeadersTreeElement);
221
222        if (this._resource.requestHeadersText) {
223            var toggleButton = this._createHeadersToggleButton(this._showRequestHeadersText);
224            toggleButton.addEventListener("click", this._toggleRequestHeadersText.bind(this));
225            this._requestHeadersTreeElement.listItemElement.appendChild(toggleButton);
226        }
227
228        this._refreshFormData();
229    },
230
231    _refreshResponseHeaders: function()
232    {
233        var additionalRow = null;
234        if (typeof this._resource.webSocketChallengeResponse !== "undefined")
235            additionalRow = {header: "(Challenge Response)", value: this._resource.webSocketChallengeResponse};
236        if (this._showResponseHeadersText)
237            this._refreshHeadersText(WebInspector.UIString("Response Headers"), this._resource.responseHeadersText, this._responseHeadersTreeElement);
238        else
239            this._refreshHeaders(WebInspector.UIString("Response Headers"), this._resource.sortedResponseHeaders, additionalRow, this._responseHeadersTreeElement);
240
241        if (this._resource.responseHeadersText) {
242            var toggleButton = this._createHeadersToggleButton(this._showResponseHeadersText);
243            toggleButton.addEventListener("click", this._toggleResponseHeadersText.bind(this));
244            this._responseHeadersTreeElement.listItemElement.appendChild(toggleButton);
245        }
246    },
247
248    _refreshHTTPInformation: function()
249    {
250        var requestMethodElement = this._requestMethodTreeElement;
251        requestMethodElement.hidden = !this._resource.statusCode;
252        var statusCodeElement = this._statusCodeTreeElement;
253        statusCodeElement.hidden = !this._resource.statusCode;
254        var statusCodeImage = "";
255
256        if (this._resource.statusCode) {
257            var statusImageSource = "";
258            if (this._resource.statusCode < 300 || this._resource.statusCode === 304)
259                statusImageSource = "Images/successGreenDot.png";
260            else if (this._resource.statusCode < 400)
261                statusImageSource = "Images/warningOrangeDot.png";
262            else
263                statusImageSource = "Images/errorRedDot.png";
264
265            var statusTextEscaped = this._resource.statusCode + " " + this._resource.statusText.escapeHTML();
266            statusCodeImage = "<img class=\"resource-status-image\" src=\"" + statusImageSource + "\" title=\"" + statusTextEscaped + "\">";
267
268            requestMethodElement.titleHTML = "<div class=\"header-name\">" + WebInspector.UIString("Request Method") + ":</div>" +
269                "<div class=\"header-value source-code\">" + this._resource.requestMethod + "</div>";
270
271            statusCodeElement.titleHTML = "<div class=\"header-name\">" + WebInspector.UIString("Status Code") + ":</div>" +
272                statusCodeImage + "<div class=\"header-value source-code\">" + statusTextEscaped + "</div>";
273        }
274    },
275
276    _refreshHeadersTitle: function(title, headersTreeElement, isHeadersTextShown, headersLength)
277    {
278        headersTreeElement.listItemElement.removeChildren();
279        headersTreeElement.listItemElement.appendChild(document.createTextNode(title));
280
281        if (!isHeadersTextShown) {
282            var headerCount = document.createElement("span");
283            headerCount.addStyleClass("header-count");
284            headerCount.textContent = WebInspector.UIString(" (%d)", headersLength);
285            headersTreeElement.listItemElement.appendChild(headerCount);
286        }
287    },
288
289    _refreshHeaders: function(title, headers, additionalRow, headersTreeElement)
290    {
291        headersTreeElement.removeChildren();
292
293        var length = headers.length;
294        this._refreshHeadersTitle(title, headersTreeElement, false, length);
295        headersTreeElement.hidden = !length;
296        for (var i = 0; i < length; ++i) {
297            var title = "<div class=\"header-name\">" + headers[i].header.escapeHTML() + ":</div>";
298            title += "<div class=\"header-value source-code\">" + headers[i].value.escapeHTML() + "</div>"
299
300            var headerTreeElement = new TreeElement(null, null, false);
301            headerTreeElement.titleHTML = title;
302            headerTreeElement.selectable = false;
303            headersTreeElement.appendChild(headerTreeElement);
304        }
305
306        if (additionalRow) {
307            var title = "<div class=\"header-name\">" + additionalRow.header.escapeHTML() + ":</div>";
308            title += "<div class=\"header-value source-code\">" + additionalRow.value.escapeHTML() + "</div>"
309
310            var headerTreeElement = new TreeElement(null, null, false);
311            headerTreeElement.titleHTML = title;
312            headerTreeElement.selectable = false;
313            headersTreeElement.appendChild(headerTreeElement);
314        }
315    },
316
317    _refreshHeadersText: function(title, headersText, headersTreeElement)
318    {
319        headersTreeElement.removeChildren();
320
321        this._refreshHeadersTitle(title, headersTreeElement, true);
322        var headerTreeElement = new TreeElement(null, null, false);
323        headerTreeElement.selectable = false;
324        headersTreeElement.appendChild(headerTreeElement);
325        headerTreeElement.listItemElement.addStyleClass("headers-text");
326
327        var headersTextElement = document.createElement("span");
328        headersTextElement.addStyleClass("header-value");
329        headersTextElement.addStyleClass("source-code");
330        headersTextElement.textContent = String(headersText).trim();
331        headerTreeElement.listItemElement.appendChild(headersTextElement);
332    },
333
334    _toggleRequestHeadersText: function(event)
335    {
336        this._showRequestHeadersText = !this._showRequestHeadersText;
337        this._refreshRequestHeaders();
338    },
339
340    _toggleResponseHeadersText: function(event)
341    {
342        this._showResponseHeadersText = !this._showResponseHeadersText;
343        this._refreshResponseHeaders();
344    },
345
346    _createToggleButton: function(title)
347    {
348        var button = document.createElement("span");
349        button.addStyleClass("header-toggle");
350        button.textContent = title;
351        return button;
352    },
353
354    _createHeadersToggleButton: function(isHeadersTextShown)
355    {
356        var toggleTitle = isHeadersTextShown ? WebInspector.UIString("view parsed") : WebInspector.UIString("view source");
357        return this._createToggleButton(toggleTitle);
358    }
359}
360
361WebInspector.ResourceHeadersView.prototype.__proto__ = WebInspector.View.prototype;
362