• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.VBox}
29 */
30WebInspector.ApplicationCacheItemsView = function(model, frameId)
31{
32    WebInspector.VBox.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
74WebInspector.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(null, "name"); break;
222            case 1: comparator = localeCompare.bind(null, "type"); break;
223            case 2: comparator = numberCompare.bind(null, "size"); break;
224            default: localeCompare.bind(null, "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.VBox.prototype
268}
269
270