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  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *
      9  * 1.  Redistributions of source code must retain the above copyright
     10  *     notice, this list of conditions and the following disclaimer.
     11  * 2.  Redistributions in binary form must reproduce the above copyright
     12  *     notice, this list of conditions and the following disclaimer in the
     13  *     documentation and/or other materials provided with the distribution.
     14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     15  *     its contributors may be used to endorse or promote products derived
     16  *     from this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 WebInspector.ResourceView = function(resource)
     31 {
     32     WebInspector.View.call(this);
     33 
     34     this.element.addStyleClass("resource-view");
     35 
     36     this.resource = resource;
     37 
     38     this.tabsElement = document.createElement("div");
     39     this.tabsElement.className = "scope-bar";
     40     this.element.appendChild(this.tabsElement);
     41 
     42     this.headersTabElement = document.createElement("li");
     43     this.headersTabElement.textContent = WebInspector.UIString("Headers");
     44     this.contentTabElement = document.createElement("li");
     45     this.contentTabElement.textContent = WebInspector.UIString("Content");
     46     this.tabsElement.appendChild(this.headersTabElement);
     47     this.tabsElement.appendChild(this.contentTabElement);
     48 
     49     this.headersTabElement.addEventListener("click", this._selectHeadersTab.bind(this), false);
     50     this.contentTabElement.addEventListener("click", this._selectContentTab.bind(this), false);
     51 
     52     this.headersElement = document.createElement("div");
     53     this.headersElement.className = "resource-view-headers";
     54     this.element.appendChild(this.headersElement);
     55 
     56     this.contentElement = document.createElement("div");
     57     this.contentElement.className = "resource-view-content";
     58     this.element.appendChild(this.contentElement);
     59 
     60     this.headersListElement = document.createElement("ol");
     61     this.headersListElement.className = "outline-disclosure";
     62     this.headersElement.appendChild(this.headersListElement);
     63 
     64     this.headersTreeOutline = new TreeOutline(this.headersListElement);
     65     this.headersTreeOutline.expandTreeElementsWhenArrowing = true;
     66 
     67     this.urlTreeElement = new TreeElement("", null, false);
     68     this.urlTreeElement.selectable = false;
     69     this.headersTreeOutline.appendChild(this.urlTreeElement);
     70 
     71     this.requestMethodTreeElement = new TreeElement("", null, false);
     72     this.requestMethodTreeElement.selectable = false;
     73     this.headersTreeOutline.appendChild(this.requestMethodTreeElement);
     74 
     75     this.statusCodeTreeElement = new TreeElement("", null, false);
     76     this.statusCodeTreeElement.selectable = false;
     77     this.headersTreeOutline.appendChild(this.statusCodeTreeElement);
     78 
     79     this.requestHeadersTreeElement = new TreeElement("", null, true);
     80     this.requestHeadersTreeElement.expanded = true;
     81     this.requestHeadersTreeElement.selectable = false;
     82     this.headersTreeOutline.appendChild(this.requestHeadersTreeElement);
     83 
     84     this._decodeHover = WebInspector.UIString("Double-Click to toggle between URL encoded and decoded formats");
     85     this._decodeRequestParameters = true;
     86 
     87     this.queryStringTreeElement = new TreeElement("", null, true);
     88     this.queryStringTreeElement.expanded = true;
     89     this.queryStringTreeElement.selectable = false;
     90     this.queryStringTreeElement.hidden = true;
     91     this.headersTreeOutline.appendChild(this.queryStringTreeElement);
     92 
     93     this.formDataTreeElement = new TreeElement("", null, true);
     94     this.formDataTreeElement.expanded = true;
     95     this.formDataTreeElement.selectable = false;
     96     this.formDataTreeElement.hidden = true;
     97     this.headersTreeOutline.appendChild(this.formDataTreeElement);
     98 
     99     this.requestPayloadTreeElement = new TreeElement(WebInspector.UIString("Request Payload"), null, true);
    100     this.requestPayloadTreeElement.expanded = true;
    101     this.requestPayloadTreeElement.selectable = false;
    102     this.requestPayloadTreeElement.hidden = true;
    103     this.headersTreeOutline.appendChild(this.requestPayloadTreeElement);
    104 
    105     this.responseHeadersTreeElement = new TreeElement("", null, true);
    106     this.responseHeadersTreeElement.expanded = true;
    107     this.responseHeadersTreeElement.selectable = false;
    108     this.headersTreeOutline.appendChild(this.responseHeadersTreeElement);
    109 
    110     this.headersVisible = true;
    111 
    112     resource.addEventListener("url changed", this._refreshURL, this);
    113     resource.addEventListener("requestHeaders changed", this._refreshRequestHeaders, this);
    114     resource.addEventListener("responseHeaders changed", this._refreshResponseHeaders, this);
    115     resource.addEventListener("finished", this._refreshHTTPInformation, this);
    116 
    117     this._refreshURL();
    118     this._refreshRequestHeaders();
    119     this._refreshResponseHeaders();
    120     this._refreshHTTPInformation();
    121     this._selectTab();
    122 }
    123 
    124 WebInspector.ResourceView.prototype = {
    125     attach: function()
    126     {
    127         if (!this.element.parentNode) {
    128             var parentElement = (document.getElementById("resource-views") || document.getElementById("script-resource-views"));
    129             if (parentElement)
    130                 parentElement.appendChild(this.element);
    131         }
    132     },
    133 
    134     show: function(parentElement)
    135     {
    136         WebInspector.View.prototype.show.call(this, parentElement);
    137         this._selectTab();
    138     },
    139 
    140     set headersVisible(x)
    141     {
    142         if (x === this._headersVisible)
    143             return;
    144         this._headersVisible = x;
    145         if (x)
    146             this.element.addStyleClass("headers-visible");
    147         else
    148             this.element.removeStyleClass("headers-visible");
    149         this._selectTab();
    150     },
    151 
    152     _selectTab: function()
    153     {
    154         if (this._headersVisible) {
    155             if (WebInspector.settings.resourceViewTab === "headers")
    156                 this._selectHeadersTab();
    157             else
    158                 this._selectContentTab();
    159         } else
    160             this._innerSelectContentTab();
    161     },
    162 
    163     _selectHeadersTab: function()
    164     {
    165         WebInspector.settings.resourceViewTab = "headers";
    166         this.headersTabElement.addStyleClass("selected");
    167         this.contentTabElement.removeStyleClass("selected");
    168         this.headersElement.removeStyleClass("hidden");
    169         this.contentElement.addStyleClass("hidden");
    170     },
    171 
    172     _selectContentTab: function()
    173     {
    174         WebInspector.settings.resourceViewTab = "content";
    175         this._innerSelectContentTab();
    176     },
    177 
    178     _innerSelectContentTab: function()
    179     {
    180         this.contentTabElement.addStyleClass("selected");
    181         this.headersTabElement.removeStyleClass("selected");
    182         this.contentElement.removeStyleClass("hidden");
    183         this.headersElement.addStyleClass("hidden");
    184         if ("resize" in this)
    185             this.resize();
    186         if ("contentTabSelected" in this)
    187             this.contentTabSelected();
    188     },
    189 
    190     _refreshURL: function()
    191     {
    192         this.urlTreeElement.title = "<div class=\"header-name\">" + WebInspector.UIString("Request URL") + ":</div>" +
    193             "<div class=\"header-value source-code\">" + this.resource.url.escapeHTML() + "</div>";
    194     },
    195 
    196     _refreshQueryString: function()
    197     {
    198         var url = this.resource.url;
    199         var hasQueryString = url.indexOf("?") >= 0;
    200 
    201         if (!hasQueryString) {
    202             this.queryStringTreeElement.hidden = true;
    203             return;
    204         }
    205 
    206         this.queryStringTreeElement.hidden = false;
    207         var parmString = url.split("?", 2)[1];
    208         this._refreshParms(WebInspector.UIString("Query String Parameters"), parmString, this.queryStringTreeElement);
    209     },
    210 
    211     _refreshFormData: function()
    212     {
    213         this.formDataTreeElement.hidden = true;
    214         this.requestPayloadTreeElement.hidden = true;
    215 
    216         var isFormData = this.resource.requestFormData;
    217         if (!isFormData)
    218             return;
    219 
    220         var isFormEncoded = false;
    221         var requestContentType = this._getHeaderValue(this.resource.requestHeaders, "Content-Type");
    222         if (requestContentType && requestContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i))
    223             isFormEncoded = true;
    224 
    225         if (isFormEncoded) {
    226             this.formDataTreeElement.hidden = false;
    227             this._refreshParms(WebInspector.UIString("Form Data"), this.resource.requestFormData, this.formDataTreeElement);
    228         } else {
    229             this.requestPayloadTreeElement.hidden = false;
    230             this._refreshRequestPayload(this.resource.requestFormData);
    231         }
    232     },
    233 
    234     _refreshRequestPayload: function(formData)
    235     {
    236         this.requestPayloadTreeElement.removeChildren();
    237 
    238         var title = "<div class=\"header-name\">&nbsp;</div>";
    239         title += "<div class=\"raw-form-data header-value source-code\">" + formData.escapeHTML() + "</div>";
    240         var parmTreeElement = new TreeElement(title, null, false);
    241         parmTreeElement.selectable = false;
    242         this.requestPayloadTreeElement.appendChild(parmTreeElement);
    243     },
    244 
    245     _refreshParms: function(title, parmString, parmsTreeElement)
    246     {
    247         var parms = parmString.split("&");
    248         for (var i = 0; i < parms.length; ++i) {
    249             var parm = parms[i];
    250             parm = parm.split("=", 2);
    251             if (parm.length == 1)
    252                 parm.push("");
    253             parms[i] = parm;
    254         }
    255 
    256         parmsTreeElement.removeChildren();
    257 
    258         parmsTreeElement.title = title + "<span class=\"header-count\">" + WebInspector.UIString(" (%d)", parms.length) + "</span>";
    259 
    260         for (var i = 0; i < parms.length; ++i) {
    261             var key = parms[i][0];
    262             var value = parms[i][1];
    263 
    264             var errorDecoding = false;
    265             if (this._decodeRequestParameters) {
    266                 if (value.indexOf("%") >= 0) {
    267                     try {
    268                         value = decodeURIComponent(value);
    269                     } catch(e) {
    270                         errorDecoding = true;
    271                     }
    272                 }
    273 
    274                 value = value.replace(/\+/g, " ");
    275             }
    276 
    277             valueEscaped = value.escapeHTML();
    278             if (errorDecoding)
    279                 valueEscaped += " <span class=\"error-message\">" + WebInspector.UIString("(unable to decode value)").escapeHTML() + "</span>";
    280 
    281             var title = "<div class=\"header-name\">" + key.escapeHTML() + ":</div>";
    282             title += "<div class=\"header-value source-code\">" + valueEscaped + "</div>";
    283 
    284             var parmTreeElement = new TreeElement(title, null, false);
    285             parmTreeElement.selectable = false;
    286             parmTreeElement.tooltip = this._decodeHover;
    287             parmTreeElement.ondblclick = this._toggleURLdecoding.bind(this);
    288             parmsTreeElement.appendChild(parmTreeElement);
    289         }
    290     },
    291 
    292     _toggleURLdecoding: function(event)
    293     {
    294         this._decodeRequestParameters = !this._decodeRequestParameters;
    295         this._refreshQueryString();
    296         this._refreshFormData();
    297     },
    298 
    299     _getHeaderValue: function(headers, key)
    300     {
    301         var lowerKey = key.toLowerCase();
    302         for (var testKey in headers) {
    303             if (testKey.toLowerCase() === lowerKey)
    304                 return headers[testKey];
    305         }
    306     },
    307 
    308     _refreshRequestHeaders: function()
    309     {
    310         this._refreshHeaders(WebInspector.UIString("Request Headers"), this.resource.sortedRequestHeaders, this.requestHeadersTreeElement);
    311         this._refreshFormData();
    312     },
    313 
    314     _refreshResponseHeaders: function()
    315     {
    316         this._refreshHeaders(WebInspector.UIString("Response Headers"), this.resource.sortedResponseHeaders, this.responseHeadersTreeElement);
    317     },
    318 
    319     _refreshHTTPInformation: function()
    320     {
    321         var requestMethodElement = this.requestMethodTreeElement;
    322         requestMethodElement.hidden = !this.resource.statusCode;
    323         var statusCodeElement = this.statusCodeTreeElement;
    324         statusCodeElement.hidden = !this.resource.statusCode;
    325         var statusCodeImage = "";
    326 
    327         if (this.resource.statusCode) {
    328             var statusImageSource = "";
    329             if (this.resource.statusCode < 300)
    330                 statusImageSource = "Images/successGreenDot.png";
    331             else if (this.resource.statusCode < 400)
    332                 statusImageSource = "Images/warningOrangeDot.png";
    333             else
    334                 statusImageSource = "Images/errorRedDot.png";
    335             statusCodeImage = "<img class=\"resource-status-image\" src=\"" + statusImageSource + "\" title=\"" + WebInspector.Resource.StatusTextForCode(this.resource.statusCode) + "\">";
    336 
    337             requestMethodElement.title = "<div class=\"header-name\">" + WebInspector.UIString("Request Method") + ":</div>" +
    338                 "<div class=\"header-value source-code\">" + this.resource.requestMethod + "</div>";
    339 
    340             statusCodeElement.title = "<div class=\"header-name\">" + WebInspector.UIString("Status Code") + ":</div>" +
    341                 statusCodeImage + "<div class=\"header-value source-code\">" + WebInspector.Resource.StatusTextForCode(this.resource.statusCode) + "</div>";
    342         }
    343     },
    344 
    345     _refreshHeaders: function(title, headers, headersTreeElement)
    346     {
    347         headersTreeElement.removeChildren();
    348 
    349         var length = headers.length;
    350         headersTreeElement.title = title.escapeHTML() + "<span class=\"header-count\">" + WebInspector.UIString(" (%d)", length) + "</span>";
    351         headersTreeElement.hidden = !length;
    352 
    353         var length = headers.length;
    354         for (var i = 0; i < length; ++i) {
    355             var title = "<div class=\"header-name\">" + headers[i].header.escapeHTML() + ":</div>";
    356             title += "<div class=\"header-value source-code\">" + headers[i].value.escapeHTML() + "</div>"
    357 
    358             var headerTreeElement = new TreeElement(title, null, false);
    359             headerTreeElement.selectable = false;
    360             headersTreeElement.appendChild(headerTreeElement);
    361         }
    362     }
    363 }
    364 
    365 WebInspector.ResourceView.prototype.__proto__ = WebInspector.View.prototype;
    366