Home | History | Annotate | Download | only in front-end
      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