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._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 101 WebInspector.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 361 WebInspector.ResourceHeadersView.prototype.__proto__ = WebInspector.View.prototype; 362