Home | History | Annotate | Download | only in front_end
      1 /*
      2  * Copyright (C) 2010 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 /**
     27  * @constructor
     28  * @extends {WebInspector.View}
     29  */
     30 WebInspector.ApplicationCacheItemsView = function(model, frameId)
     31 {
     32     WebInspector.View.call(this);
     33 
     34     this._model = model;
     35 
     36     this.element.classList.add("storage-view");
     37     this.element.classList.add("table");
     38 
     39     // FIXME: Needs better tooltip. (Localized)
     40     this.deleteButton = new WebInspector.StatusBarButton(WebInspector.UIString("Delete"), "delete-storage-status-bar-item");
     41     this.deleteButton.visible = false;
     42     this.deleteButton.addEventListener("click", this._deleteButtonClicked, this);
     43 
     44     this.connectivityIcon = document.createElement("div");
     45     this.connectivityMessage = document.createElement("span");
     46     this.connectivityMessage.className = "storage-application-cache-connectivity";
     47     this.connectivityMessage.textContent = "";
     48 
     49     this.divider = document.createElement("span");
     50     this.divider.className = "status-bar-item status-bar-divider";
     51 
     52     this.statusIcon = document.createElement("div");
     53     this.statusMessage = document.createElement("span");
     54     this.statusMessage.className = "storage-application-cache-status";
     55     this.statusMessage.textContent = "";
     56 
     57     this._frameId = frameId;
     58 
     59     this._emptyView = new WebInspector.EmptyView(WebInspector.UIString("No Application Cache information available."));
     60     this._emptyView.show(this.element);
     61 
     62     this._markDirty();
     63 
     64     var status = this._model.frameManifestStatus(frameId);
     65     this.updateStatus(status);
     66 
     67     this.updateNetworkState(this._model.onLine);
     68 
     69     // FIXME: Status bar items don't work well enough yet, so they are being hidden.
     70     // http://webkit.org/b/41637 Web Inspector: Give Semantics to "Refresh" and "Delete" Buttons in ApplicationCache DataGrid
     71     this.deleteButton.element.style.display = "none";
     72 }
     73 
     74 WebInspector.ApplicationCacheItemsView.prototype = {
     75     get statusBarItems()
     76     {
     77         return [
     78             this.deleteButton.element,
     79             this.connectivityIcon, this.connectivityMessage, this.divider,
     80             this.statusIcon, this.statusMessage
     81         ];
     82     },
     83 
     84     wasShown: function()
     85     {
     86         this._maybeUpdate();
     87     },
     88 
     89     willHide: function()
     90     {
     91         this.deleteButton.visible = false;
     92     },
     93 
     94     _maybeUpdate: function()
     95     {
     96         if (!this.isShowing() || !this._viewDirty)
     97             return;
     98 
     99         this._update();
    100         this._viewDirty = false;
    101     },
    102 
    103     _markDirty: function()
    104     {
    105         this._viewDirty = true;
    106     },
    107 
    108     /**
    109      * @param {number} status
    110      */
    111     updateStatus: function(status)
    112     {
    113         var oldStatus = this._status;
    114         this._status = status;
    115 
    116         var statusInformation = {};
    117         // We should never have UNCACHED status, since we remove frames with UNCACHED application cache status from the tree.
    118         statusInformation[applicationCache.UNCACHED]    = { className: "red-ball", text: "UNCACHED" };
    119         statusInformation[applicationCache.IDLE]        = { className: "green-ball", text: "IDLE" };
    120         statusInformation[applicationCache.CHECKING]    = { className: "orange-ball",  text: "CHECKING" };
    121         statusInformation[applicationCache.DOWNLOADING] = { className: "orange-ball",  text: "DOWNLOADING" };
    122         statusInformation[applicationCache.UPDATEREADY] = { className: "green-ball",  text: "UPDATEREADY" };
    123         statusInformation[applicationCache.OBSOLETE]    = { className: "red-ball",      text: "OBSOLETE" };
    124 
    125         var info = statusInformation[status] || statusInformation[applicationCache.UNCACHED];
    126 
    127         this.statusIcon.className = "storage-application-cache-status-icon " + info.className;
    128         this.statusMessage.textContent = info.text;
    129 
    130         if (this.isShowing() && this._status === applicationCache.IDLE && (oldStatus === applicationCache.UPDATEREADY || !this._resources))
    131             this._markDirty();
    132         this._maybeUpdate();
    133     },
    134 
    135     /**
    136      * @param {boolean} isNowOnline
    137      */
    138     updateNetworkState: function(isNowOnline)
    139     {
    140         if (isNowOnline) {
    141             this.connectivityIcon.className = "storage-application-cache-connectivity-icon green-ball";
    142             this.connectivityMessage.textContent = WebInspector.UIString("Online");
    143         } else {
    144             this.connectivityIcon.className = "storage-application-cache-connectivity-icon red-ball";
    145             this.connectivityMessage.textContent = WebInspector.UIString("Offline");
    146         }
    147     },
    148 
    149     _update: function()
    150     {
    151         this._model.requestApplicationCache(this._frameId, this._updateCallback.bind(this));
    152     },
    153 
    154     /**
    155      * @param {?ApplicationCacheAgent.ApplicationCache} applicationCache
    156      */
    157     _updateCallback: function(applicationCache)
    158     {
    159         if (!applicationCache || !applicationCache.manifestURL) {
    160             delete this._manifest;
    161             delete this._creationTime;
    162             delete this._updateTime;
    163             delete this._size;
    164             delete this._resources;
    165 
    166             this._emptyView.show(this.element);
    167             this.deleteButton.visible = false;
    168             if (this._dataGrid)
    169                 this._dataGrid.element.classList.add("hidden");
    170             return;
    171         }
    172         // FIXME: are these variables needed anywhere else?
    173         this._manifest = applicationCache.manifestURL;
    174         this._creationTime = applicationCache.creationTime;
    175         this._updateTime = applicationCache.updateTime;
    176         this._size = applicationCache.size;
    177         this._resources = applicationCache.resources;
    178 
    179         if (!this._dataGrid)
    180             this._createDataGrid();
    181 
    182         this._populateDataGrid();
    183         this._dataGrid.autoSizeColumns(20, 80);
    184         this._dataGrid.element.classList.remove("hidden");
    185         this._emptyView.detach();
    186         this.deleteButton.visible = true;
    187 
    188         // FIXME: For Chrome, put creationTime and updateTime somewhere.
    189         // NOTE: localizedString has not yet been added.
    190         // WebInspector.UIString("(%s) Created: %s Updated: %s", this._size, this._creationTime, this._updateTime);
    191     },
    192 
    193     _createDataGrid: function()
    194     {
    195         var columns = [
    196             {title: WebInspector.UIString("Resource"), sort: WebInspector.DataGrid.Order.Ascending, sortable: true},
    197             {title: WebInspector.UIString("Type"), sortable: true},
    198             {title: WebInspector.UIString("Size"), align: WebInspector.DataGrid.Align.Right, sortable: true}
    199         ];
    200         this._dataGrid = new WebInspector.DataGrid(columns);
    201         this._dataGrid.show(this.element);
    202         this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._populateDataGrid, this);
    203     },
    204 
    205     _populateDataGrid: function()
    206     {
    207         var selectedResource = this._dataGrid.selectedNode ? this._dataGrid.selectedNode.resource : null;
    208         var sortDirection = this._dataGrid.isSortOrderAscending() ? 1 : -1;
    209 
    210         function numberCompare(field, resource1, resource2)
    211         {
    212             return sortDirection * (resource1[field] - resource2[field]);
    213         }
    214         function localeCompare(field, resource1, resource2)
    215         {
    216              return sortDirection * (resource1[field] + "").localeCompare(resource2[field] + "")
    217         }
    218 
    219         var comparator;
    220         switch (parseInt(this._dataGrid.sortColumnIdentifier(), 10)) {
    221             case 0: comparator = localeCompare.bind(this, "name"); break;
    222             case 1: comparator = localeCompare.bind(this, "type"); break;
    223             case 2: comparator = numberCompare.bind(this, "size"); break;
    224             default: localeCompare.bind(this, "resource"); // FIXME: comparator = ?
    225         }
    226 
    227         this._resources.sort(comparator);
    228         this._dataGrid.rootNode().removeChildren();
    229 
    230         var nodeToSelect;
    231         for (var i = 0; i < this._resources.length; ++i) {
    232             var data = {};
    233             var resource = this._resources[i];
    234             data[0] = resource.url;
    235             data[1] = resource.type;
    236             data[2] = Number.bytesToString(resource.size);
    237             var node = new WebInspector.DataGridNode(data);
    238             node.resource = resource;
    239             node.selectable = true;
    240             this._dataGrid.rootNode().appendChild(node);
    241             if (resource === selectedResource) {
    242                 nodeToSelect = node;
    243                 nodeToSelect.selected = true;
    244             }
    245         }
    246 
    247         if (!nodeToSelect && this._dataGrid.rootNode().children.length)
    248             this._dataGrid.rootNode().children[0].selected = true;
    249     },
    250 
    251     _deleteButtonClicked: function(event)
    252     {
    253         if (!this._dataGrid || !this._dataGrid.selectedNode)
    254             return;
    255 
    256         // FIXME: Delete Button semantics are not yet defined. (Delete a single, or all?)
    257         this._deleteCallback(this._dataGrid.selectedNode);
    258     },
    259 
    260     _deleteCallback: function(node)
    261     {
    262         // FIXME: Should we delete a single (selected) resource or all resources?
    263         // InspectorBackend.deleteCachedResource(...)
    264         // this._update();
    265     },
    266 
    267     __proto__: WebInspector.View.prototype
    268 }
    269 
    270