Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / resources / ResourcesPanel.js
1 /*
2  * Copyright (C) 2007, 2008, 2010 Apple Inc.  All rights reserved.
3  * Copyright (C) 2009 Joseph Pecoraro
4  * Copyright (C) 2013 Samsung Electronics. 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 /**
32  * @constructor
33  * @extends {WebInspector.PanelWithSidebarTree}
34  * @implements {WebInspector.TargetManager.Observer}
35  */
36 WebInspector.ResourcesPanel = function(database)
37 {
38     WebInspector.PanelWithSidebarTree.call(this, "resources");
39     this.registerRequiredCSS("resourcesPanel.css");
40
41     WebInspector.settings.resourcesLastSelectedItem = WebInspector.settings.createSetting("resourcesLastSelectedItem", {});
42
43     this.sidebarElement().classList.add("filter-all", "children", "small", "outline-disclosure");
44     this.sidebarTree.element.classList.remove("sidebar-tree");
45
46     this.resourcesListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Frames"), "Frames", ["frame-storage-tree-item"]);
47     this.sidebarTree.appendChild(this.resourcesListTreeElement);
48
49     this.databasesListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Web SQL"), "Databases", ["database-storage-tree-item"]);
50     this.sidebarTree.appendChild(this.databasesListTreeElement);
51
52     this.indexedDBListTreeElement = new WebInspector.IndexedDBTreeElement(this);
53     this.sidebarTree.appendChild(this.indexedDBListTreeElement);
54
55     this.localStorageListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Local Storage"), "LocalStorage", ["domstorage-storage-tree-item", "local-storage"]);
56     this.sidebarTree.appendChild(this.localStorageListTreeElement);
57
58     this.sessionStorageListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Session Storage"), "SessionStorage", ["domstorage-storage-tree-item", "session-storage"]);
59     this.sidebarTree.appendChild(this.sessionStorageListTreeElement);
60
61     this.cookieListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Cookies"), "Cookies", ["cookie-storage-tree-item"]);
62     this.sidebarTree.appendChild(this.cookieListTreeElement);
63
64     this.applicationCacheListTreeElement = new WebInspector.StorageCategoryTreeElement(this, WebInspector.UIString("Application Cache"), "ApplicationCache", ["application-cache-storage-tree-item"]);
65     this.sidebarTree.appendChild(this.applicationCacheListTreeElement);
66
67     if (Runtime.experiments.isEnabled("fileSystemInspection")) {
68         this.fileSystemListTreeElement = new WebInspector.FileSystemListTreeElement(this);
69         this.sidebarTree.appendChild(this.fileSystemListTreeElement);
70     }
71
72     var mainView = new WebInspector.VBox();
73     this.storageViews = mainView.element.createChild("div", "resources-main diff-container");
74     var statusBarContainer = mainView.element.createChild("div", "resources-status-bar");
75     this.storageViewStatusBarItemsContainer = statusBarContainer.createChild("div", "status-bar");
76     mainView.show(this.mainElement());
77
78     /** @type {!Map.<!WebInspector.Database, !Object.<string, !WebInspector.DatabaseTableView>>} */
79     this._databaseTableViews = new Map();
80     /** @type {!Map.<!WebInspector.Database, !WebInspector.DatabaseQueryView>} */
81     this._databaseQueryViews = new Map();
82     /** @type {!Map.<!WebInspector.Database, !WebInspector.DatabaseTreeElement>} */
83     this._databaseTreeElements = new Map();
84     /** @type {!Map.<!WebInspector.DOMStorage, !WebInspector.DOMStorageItemsView>} */
85     this._domStorageViews = new Map();
86     /** @type {!Map.<!WebInspector.DOMStorage, !WebInspector.DOMStorageTreeElement>} */
87     this._domStorageTreeElements = new Map();
88     /** @type {!Object.<string, !WebInspector.CookieItemsView>} */
89     this._cookieViews = {};
90     /** @type {!Object.<string, boolean>} */
91     this._domains = {};
92
93     this.sidebarElement().addEventListener("mousemove", this._onmousemove.bind(this), false);
94     this.sidebarElement().addEventListener("mouseout", this._onmouseout.bind(this), false);
95
96     /**
97      * @this {WebInspector.ResourcesPanel}
98      * @return {?WebInspector.SourceFrame}
99      */
100     function sourceFrameGetter()
101     {
102         var view = this.visibleView;
103         if (view && view instanceof WebInspector.SourceFrame)
104             return /** @type {!WebInspector.SourceFrame} */ (view);
105         return null;
106     }
107     WebInspector.GoToLineDialog.install(this, sourceFrameGetter.bind(this));
108
109     WebInspector.targetManager.observeTargets(this);
110 }
111
112 WebInspector.ResourcesPanel.prototype = {
113     /**
114      * @param {!WebInspector.Target} target
115      */
116     targetAdded: function(target)
117     {
118         if (this._target)
119             return;
120
121         if (target.resourceTreeModel.cachedResourcesLoaded())
122             this._cachedResourcesLoaded();
123
124         target.databaseModel.databases().forEach(this._addDatabase.bind(this));
125
126         target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._loadEventFired, this);
127         target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.CachedResourcesLoaded, this._cachedResourcesLoaded, this);
128         target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.WillLoadCachedResources, this._resetWithFrames, this);
129         target.databaseModel.addEventListener(WebInspector.DatabaseModel.Events.DatabaseAdded, this._databaseAdded, this);
130
131         this._target = target;
132     },
133
134     /**
135      * @param {!WebInspector.Target} target
136      */
137     targetRemoved: function(target)
138     {
139         if (target !== this._target)
140             return;
141         delete this._target;
142
143         target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._loadEventFired, this);
144         target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.CachedResourcesLoaded, this._cachedResourcesLoaded, this);
145         target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.WillLoadCachedResources, this._resetWithFrames, this);
146         target.databaseModel.removeEventListener(WebInspector.DatabaseModel.Events.DatabaseAdded, this._databaseAdded, this);
147
148         this._resetWithFrames();
149     },
150
151     /**
152      * @return {boolean}
153      */
154     canSearch: function()
155     {
156         return false;
157     },
158
159     wasShown: function()
160     {
161         WebInspector.Panel.prototype.wasShown.call(this);
162         this._initialize();
163     },
164
165     _initialize: function()
166     {
167         if (!this._initialized && this.isShowing() && this._cachedResourcesWereLoaded) {
168             var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.mainTarget());
169             this._populateResourceTree();
170             this._populateDOMStorageTree();
171             this._populateApplicationCacheTree(target);
172             this.indexedDBListTreeElement._initialize();
173             if (Runtime.experiments.isEnabled("fileSystemInspection"))
174                 this.fileSystemListTreeElement._initialize();
175             this._initDefaultSelection();
176             this._initialized = true;
177         }
178     },
179
180     _loadEventFired: function()
181     {
182         this._initDefaultSelection();
183     },
184
185     _initDefaultSelection: function()
186     {
187         if (!this._initialized)
188             return;
189
190         var itemURL = WebInspector.settings.resourcesLastSelectedItem.get();
191         if (itemURL) {
192             for (var treeElement = this.sidebarTree.children[0]; treeElement; treeElement = treeElement.traverseNextTreeElement(false, this.sidebarTree, true)) {
193                 if (treeElement.itemURL === itemURL) {
194                     treeElement.revealAndSelect(true);
195                     return;
196                 }
197             }
198         }
199
200         var mainResource = WebInspector.resourceTreeModel.inspectedPageURL() && this.resourcesListTreeElement && this.resourcesListTreeElement.expanded
201                 ? WebInspector.resourceTreeModel.resourceForURL(WebInspector.resourceTreeModel.inspectedPageURL())
202                 : null;
203         if (mainResource)
204             this.showResource(mainResource);
205     },
206
207     _resetWithFrames: function()
208     {
209         this.resourcesListTreeElement.removeChildren();
210         this._treeElementForFrameId = {};
211         this._reset();
212     },
213
214     _reset: function()
215     {
216         this._domains = {};
217         var queryViews = this._databaseQueryViews.values();
218         for (var i = 0; i < queryViews.length; ++i)
219             queryViews[i].removeEventListener(WebInspector.DatabaseQueryView.Events.SchemaUpdated, this._updateDatabaseTables, this);
220         this._databaseTableViews.clear();
221         this._databaseQueryViews.clear();
222         this._databaseTreeElements.clear();
223         this._domStorageViews.clear();
224         this._domStorageTreeElements.clear();
225         this._cookieViews = {};
226
227         this.databasesListTreeElement.removeChildren();
228         this.localStorageListTreeElement.removeChildren();
229         this.sessionStorageListTreeElement.removeChildren();
230         this.cookieListTreeElement.removeChildren();
231
232         if (this.visibleView && !(this.visibleView instanceof WebInspector.StorageCategoryView))
233             this.visibleView.detach();
234
235         this.storageViewStatusBarItemsContainer.removeChildren();
236
237         if (this.sidebarTree.selectedTreeElement)
238             this.sidebarTree.selectedTreeElement.deselect();
239     },
240
241     _populateResourceTree: function()
242     {
243         this._treeElementForFrameId = {};
244         WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameAdded, this._frameAdded, this);
245         WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, this._frameNavigated, this);
246         WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, this._frameDetached, this);
247         WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, this._resourceAdded, this);
248
249         /**
250          * @param {!WebInspector.ResourceTreeFrame} frame
251          * @this {WebInspector.ResourcesPanel}
252          */
253         function populateFrame(frame)
254         {
255             this._frameAdded({data:frame});
256             for (var i = 0; i < frame.childFrames.length; ++i)
257                 populateFrame.call(this, frame.childFrames[i]);
258
259             var resources = frame.resources();
260             for (var i = 0; i < resources.length; ++i)
261                 this._resourceAdded({data:resources[i]});
262         }
263         populateFrame.call(this, WebInspector.resourceTreeModel.mainFrame);
264     },
265
266     _frameAdded: function(event)
267     {
268         var frame = event.data;
269         var parentFrame = frame.parentFrame;
270
271         var parentTreeElement = parentFrame ? this._treeElementForFrameId[parentFrame.id] : this.resourcesListTreeElement;
272         if (!parentTreeElement) {
273             console.warn("No frame to route " + frame.url + " to.")
274             return;
275         }
276
277         var frameTreeElement = new WebInspector.FrameTreeElement(this, frame);
278         this._treeElementForFrameId[frame.id] = frameTreeElement;
279         parentTreeElement.appendChild(frameTreeElement);
280     },
281
282     _frameDetached: function(event)
283     {
284         var frame = event.data;
285         var frameTreeElement = this._treeElementForFrameId[frame.id];
286         if (!frameTreeElement)
287             return;
288
289         delete this._treeElementForFrameId[frame.id];
290         if (frameTreeElement.parent)
291             frameTreeElement.parent.removeChild(frameTreeElement);
292     },
293
294     _resourceAdded: function(event)
295     {
296         var resource = event.data;
297         var frameId = resource.frameId;
298
299         if (resource.statusCode >= 301 && resource.statusCode <= 303)
300             return;
301
302         var frameTreeElement = this._treeElementForFrameId[frameId];
303         if (!frameTreeElement) {
304             // This is a frame's main resource, it will be retained
305             // and re-added by the resource manager;
306             return;
307         }
308
309         frameTreeElement.appendResource(resource);
310     },
311
312     _frameNavigated: function(event)
313     {
314         var frame = event.data;
315
316         if (!frame.parentFrame)
317             this._reset();
318
319         var frameId = frame.id;
320         var frameTreeElement = this._treeElementForFrameId[frameId];
321         if (frameTreeElement)
322             frameTreeElement.frameNavigated(frame);
323
324         var applicationCacheFrameTreeElement = this._applicationCacheFrameElements[frameId];
325         if (applicationCacheFrameTreeElement)
326             applicationCacheFrameTreeElement.frameNavigated(frame);
327     },
328
329     _cachedResourcesLoaded: function()
330     {
331         this._cachedResourcesWereLoaded = true;
332         this._initialize();
333     },
334
335     /**
336      * @param {!WebInspector.Event} event
337      */
338     _databaseAdded: function(event)
339     {
340         var database = /** @type {!WebInspector.Database} */ (event.data);
341         this._addDatabase(database);
342     },
343
344     /**
345      * @param {!WebInspector.Database} database
346      */
347     _addDatabase: function(database)
348     {
349         var databaseTreeElement = new WebInspector.DatabaseTreeElement(this, database);
350         this._databaseTreeElements.set(database, databaseTreeElement);
351         this.databasesListTreeElement.appendChild(databaseTreeElement);
352     },
353
354     addDocumentURL: function(url)
355     {
356         var parsedURL = url.asParsedURL();
357         if (!parsedURL)
358             return;
359
360         var domain = parsedURL.host;
361         if (!this._domains[domain]) {
362             this._domains[domain] = true;
363
364             var cookieDomainTreeElement = new WebInspector.CookieTreeElement(this, domain);
365             this.cookieListTreeElement.appendChild(cookieDomainTreeElement);
366         }
367     },
368
369     /**
370      * @param {!WebInspector.Event} event
371      */
372     _domStorageAdded: function(event)
373     {
374         var domStorage = /** @type {!WebInspector.DOMStorage} */ (event.data);
375         this._addDOMStorage(domStorage);
376     },
377
378     /**
379      * @param {!WebInspector.DOMStorage} domStorage
380      */
381     _addDOMStorage: function(domStorage)
382     {
383         console.assert(!this._domStorageTreeElements.get(domStorage));
384
385         var domStorageTreeElement = new WebInspector.DOMStorageTreeElement(this, domStorage, (domStorage.isLocalStorage ? "local-storage" : "session-storage"));
386         this._domStorageTreeElements.set(domStorage, domStorageTreeElement);
387         if (domStorage.isLocalStorage)
388             this.localStorageListTreeElement.appendChild(domStorageTreeElement);
389         else
390             this.sessionStorageListTreeElement.appendChild(domStorageTreeElement);
391     },
392
393     /**
394      * @param {!WebInspector.Event} event
395      */
396     _domStorageRemoved: function(event)
397     {
398         var domStorage = /** @type {!WebInspector.DOMStorage} */ (event.data);
399         this._removeDOMStorage(domStorage);
400     },
401
402     /**
403      * @param {!WebInspector.DOMStorage} domStorage
404      */
405     _removeDOMStorage: function(domStorage)
406     {
407         var treeElement = this._domStorageTreeElements.get(domStorage);
408         if (!treeElement)
409             return;
410         var wasSelected = treeElement.selected;
411         var parentListTreeElement = treeElement.parent;
412         parentListTreeElement.removeChild(treeElement);
413         if (wasSelected)
414             parentListTreeElement.select();
415         this._domStorageTreeElements.remove(domStorage);
416         this._domStorageViews.remove(domStorage);
417     },
418
419     /**
420      * @param {!WebInspector.Database} database
421      */
422     selectDatabase: function(database)
423     {
424         if (database) {
425             this._showDatabase(database);
426             this._databaseTreeElements.get(database).select();
427         }
428     },
429
430     /**
431      * @param {!WebInspector.DOMStorage} domStorage
432      */
433     selectDOMStorage: function(domStorage)
434     {
435         if (domStorage) {
436             this._showDOMStorage(domStorage);
437             this._domStorageTreeElements.get(domStorage).select();
438         }
439     },
440
441     /**
442      * @param {!WebInspector.Resource} resource
443      * @param {number=} line
444      * @param {number=} column
445      * @return {boolean}
446      */
447     showResource: function(resource, line, column)
448     {
449         var resourceTreeElement = this._findTreeElementForResource(resource);
450         if (resourceTreeElement)
451             resourceTreeElement.revealAndSelect(true);
452
453         if (typeof line === "number") {
454             var resourceSourceFrame = this._resourceSourceFrameViewForResource(resource);
455             if (resourceSourceFrame)
456                 resourceSourceFrame.revealPosition(line, column, true);
457         }
458         return true;
459     },
460
461     _showResourceView: function(resource)
462     {
463         var view = this._resourceViewForResource(resource);
464         if (!view) {
465             this.visibleView.detach();
466             return;
467         }
468         this._innerShowView(view);
469     },
470
471     /**
472      * @param {!WebInspector.Resource} resource
473      * @return {?WebInspector.View}
474      */
475     _resourceViewForResource: function(resource)
476     {
477         if (WebInspector.ResourceView.hasTextContent(resource)) {
478             var treeElement = this._findTreeElementForResource(resource);
479             if (!treeElement)
480                 return null;
481             return treeElement.sourceView();
482         }
483         return WebInspector.ResourceView.nonSourceViewForResource(resource);
484     },
485
486     /**
487      * @param {!WebInspector.Resource} resource
488      * @return {?WebInspector.ResourceSourceFrame}
489      */
490     _resourceSourceFrameViewForResource: function(resource)
491     {
492         var resourceView = this._resourceViewForResource(resource);
493         if (resourceView && resourceView instanceof WebInspector.ResourceSourceFrame)
494             return /** @type {!WebInspector.ResourceSourceFrame} */ (resourceView);
495         return null;
496     },
497
498     /**
499      * @param {!WebInspector.Database} database
500      * @param {string=} tableName
501      */
502     _showDatabase: function(database, tableName)
503     {
504         if (!database)
505             return;
506
507         var view;
508         if (tableName) {
509             var tableViews = this._databaseTableViews.get(database);
510             if (!tableViews) {
511                 tableViews = /** @type {!Object.<string, !WebInspector.DatabaseTableView>} */ ({});
512                 this._databaseTableViews.set(database, tableViews);
513             }
514             view = tableViews[tableName];
515             if (!view) {
516                 view = new WebInspector.DatabaseTableView(database, tableName);
517                 tableViews[tableName] = view;
518             }
519         } else {
520             view = this._databaseQueryViews.get(database);
521             if (!view) {
522                 view = new WebInspector.DatabaseQueryView(database);
523                 this._databaseQueryViews.set(database, view);
524                 view.addEventListener(WebInspector.DatabaseQueryView.Events.SchemaUpdated, this._updateDatabaseTables, this);
525             }
526         }
527
528         this._innerShowView(view);
529     },
530
531     /**
532      * @param {!WebInspector.View} view
533      */
534     showIndexedDB: function(view)
535     {
536         this._innerShowView(view);
537     },
538
539     /**
540      * @param {!WebInspector.DOMStorage} domStorage
541      */
542     _showDOMStorage: function(domStorage)
543     {
544         if (!domStorage)
545             return;
546
547         var view;
548         view = this._domStorageViews.get(domStorage);
549         if (!view) {
550             view = new WebInspector.DOMStorageItemsView(domStorage);
551             this._domStorageViews.set(domStorage, view);
552         }
553
554         this._innerShowView(view);
555     },
556
557     /**
558      * @param {!WebInspector.CookieTreeElement} treeElement
559      * @param {string} cookieDomain
560      */
561     showCookies: function(treeElement, cookieDomain)
562     {
563         var view = this._cookieViews[cookieDomain];
564         if (!view) {
565             view = new WebInspector.CookieItemsView(treeElement, cookieDomain);
566             this._cookieViews[cookieDomain] = view;
567         }
568
569         this._innerShowView(view);
570     },
571
572     /**
573      * @param {string} cookieDomain
574      */
575     clearCookies: function(cookieDomain)
576     {
577         this._cookieViews[cookieDomain].clear();
578     },
579
580     showApplicationCache: function(frameId)
581     {
582         if (!this._applicationCacheViews[frameId])
583             this._applicationCacheViews[frameId] = new WebInspector.ApplicationCacheItemsView(this._applicationCacheModel, frameId);
584
585         this._innerShowView(this._applicationCacheViews[frameId]);
586     },
587
588     /**
589      *  @param {!WebInspector.View} view
590      */
591     showFileSystem: function(view)
592     {
593         this._innerShowView(view);
594     },
595
596     showCategoryView: function(categoryName)
597     {
598         if (!this._categoryView)
599             this._categoryView = new WebInspector.StorageCategoryView();
600         this._categoryView.setText(categoryName);
601         this._innerShowView(this._categoryView);
602     },
603
604     _innerShowView: function(view)
605     {
606         if (this.visibleView === view)
607             return;
608
609         if (this.visibleView)
610             this.visibleView.detach();
611
612         view.show(this.storageViews);
613         this.visibleView = view;
614
615         this.storageViewStatusBarItemsContainer.removeChildren();
616         var statusBarItems = view.statusBarItems || [];
617         for (var i = 0; i < statusBarItems.length; ++i)
618             this.storageViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
619     },
620
621     closeVisibleView: function()
622     {
623         if (!this.visibleView)
624             return;
625         this.visibleView.detach();
626         delete this.visibleView;
627     },
628
629     _updateDatabaseTables: function(event)
630     {
631         var database = event.data;
632
633         if (!database)
634             return;
635
636         var databasesTreeElement = this._databaseTreeElements.get(database);
637         if (!databasesTreeElement)
638             return;
639
640         databasesTreeElement.shouldRefreshChildren = true;
641         var tableViews = this._databaseTableViews.get(database);
642
643         if (!tableViews)
644             return;
645
646         var tableNamesHash = {};
647         var self = this;
648         function tableNamesCallback(tableNames)
649         {
650             var tableNamesLength = tableNames.length;
651             for (var i = 0; i < tableNamesLength; ++i)
652                 tableNamesHash[tableNames[i]] = true;
653
654             for (var tableName in tableViews) {
655                 if (!(tableName in tableNamesHash)) {
656                     if (self.visibleView === tableViews[tableName])
657                         self.closeVisibleView();
658                     delete tableViews[tableName];
659                 }
660             }
661         }
662         database.getTableNames(tableNamesCallback);
663     },
664
665     _populateDOMStorageTree: function()
666     {
667         WebInspector.domStorageModel.storages().forEach(this._addDOMStorage.bind(this));
668         WebInspector.domStorageModel.addEventListener(WebInspector.DOMStorageModel.Events.DOMStorageAdded, this._domStorageAdded, this);
669         WebInspector.domStorageModel.addEventListener(WebInspector.DOMStorageModel.Events.DOMStorageRemoved, this._domStorageRemoved, this);
670     },
671
672     /**
673      * @param {!WebInspector.Target} target
674      */
675     _populateApplicationCacheTree: function(target)
676     {
677         this._applicationCacheModel = new WebInspector.ApplicationCacheModel(target);
678
679         this._applicationCacheViews = {};
680         this._applicationCacheFrameElements = {};
681         this._applicationCacheManifestElements = {};
682
683         this._applicationCacheModel.addEventListener(WebInspector.ApplicationCacheModel.EventTypes.FrameManifestAdded, this._applicationCacheFrameManifestAdded, this);
684         this._applicationCacheModel.addEventListener(WebInspector.ApplicationCacheModel.EventTypes.FrameManifestRemoved, this._applicationCacheFrameManifestRemoved, this);
685
686         this._applicationCacheModel.addEventListener(WebInspector.ApplicationCacheModel.EventTypes.FrameManifestStatusUpdated, this._applicationCacheFrameManifestStatusChanged, this);
687         this._applicationCacheModel.addEventListener(WebInspector.ApplicationCacheModel.EventTypes.NetworkStateChanged, this._applicationCacheNetworkStateChanged, this);
688     },
689
690     _applicationCacheFrameManifestAdded: function(event)
691     {
692         var frameId = event.data;
693         var manifestURL = this._applicationCacheModel.frameManifestURL(frameId);
694         var status = this._applicationCacheModel.frameManifestStatus(frameId)
695
696         var manifestTreeElement = this._applicationCacheManifestElements[manifestURL]
697         if (!manifestTreeElement) {
698             manifestTreeElement = new WebInspector.ApplicationCacheManifestTreeElement(this, manifestURL);
699             this.applicationCacheListTreeElement.appendChild(manifestTreeElement);
700             this._applicationCacheManifestElements[manifestURL] = manifestTreeElement;
701         }
702
703         var frameTreeElement = new WebInspector.ApplicationCacheFrameTreeElement(this, frameId, manifestURL);
704         manifestTreeElement.appendChild(frameTreeElement);
705         manifestTreeElement.expand();
706         this._applicationCacheFrameElements[frameId] = frameTreeElement;
707     },
708
709     _applicationCacheFrameManifestRemoved: function(event)
710     {
711         var frameId = event.data;
712         var frameTreeElement = this._applicationCacheFrameElements[frameId];
713         if (!frameTreeElement)
714             return;
715
716         var manifestURL = frameTreeElement.manifestURL;
717         delete this._applicationCacheFrameElements[frameId];
718         delete this._applicationCacheViews[frameId];
719         frameTreeElement.parent.removeChild(frameTreeElement);
720
721         var manifestTreeElement = this._applicationCacheManifestElements[manifestURL];
722         if (manifestTreeElement.children.length !== 0)
723             return;
724
725         delete this._applicationCacheManifestElements[manifestURL];
726         manifestTreeElement.parent.removeChild(manifestTreeElement);
727     },
728
729     _applicationCacheFrameManifestStatusChanged: function(event)
730     {
731         var frameId = event.data;
732         var status = this._applicationCacheModel.frameManifestStatus(frameId)
733
734         if (this._applicationCacheViews[frameId])
735             this._applicationCacheViews[frameId].updateStatus(status);
736     },
737
738     _applicationCacheNetworkStateChanged: function(event)
739     {
740         var isNowOnline = event.data;
741
742         for (var manifestURL in this._applicationCacheViews)
743             this._applicationCacheViews[manifestURL].updateNetworkState(isNowOnline);
744     },
745
746     _findTreeElementForResource: function(resource)
747     {
748         function getParent(object)
749         {
750             // Redirects, XHRs do not belong to the tree, it is fine to silently return false here.
751             return null;
752         }
753
754         return this.sidebarTree.findTreeElement(resource, getParent);
755     },
756
757     showView: function(view)
758     {
759         if (view)
760             this.showResource(view.resource);
761     },
762
763     _onmousemove: function(event)
764     {
765         var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
766         if (!nodeUnderMouse)
767             return;
768
769         var listNode = nodeUnderMouse.enclosingNodeOrSelfWithNodeName("li");
770         if (!listNode)
771             return;
772
773         var element = listNode.treeElement;
774         if (this._previousHoveredElement === element)
775             return;
776
777         if (this._previousHoveredElement) {
778             this._previousHoveredElement.hovered = false;
779             delete this._previousHoveredElement;
780         }
781
782         if (element instanceof WebInspector.FrameTreeElement) {
783             this._previousHoveredElement = element;
784             element.hovered = true;
785         }
786     },
787
788     _onmouseout: function(event)
789     {
790         if (this._previousHoveredElement) {
791             this._previousHoveredElement.hovered = false;
792             delete this._previousHoveredElement;
793         }
794     },
795
796     __proto__: WebInspector.PanelWithSidebarTree.prototype
797 }
798
799 /**
800  * @constructor
801  * @implements {WebInspector.Revealer}
802  */
803 WebInspector.ResourcesPanel.ResourceRevealer = function()
804 {
805 }
806
807 WebInspector.ResourcesPanel.ResourceRevealer.prototype = {
808     /**
809      * @param {!Object} resource
810      * @param {number=} lineNumber
811      */
812     reveal: function(resource, lineNumber)
813     {
814         if (resource instanceof WebInspector.Resource) {
815             var panel = /** @type {?WebInspector.ResourcesPanel} */ (WebInspector.inspectorView.showPanel("resources"));
816             if (panel)
817                 panel.showResource(resource, lineNumber);
818         }
819     }
820 }
821
822 /**
823  * @constructor
824  * @extends {TreeElement}
825  * @param {!WebInspector.ResourcesPanel} storagePanel
826  * @param {?Object} representedObject
827  * @param {string} title
828  * @param {?Array.<string>=} iconClasses
829  * @param {boolean=} hasChildren
830  * @param {boolean=} noIcon
831  */
832 WebInspector.BaseStorageTreeElement = function(storagePanel, representedObject, title, iconClasses, hasChildren, noIcon)
833 {
834     TreeElement.call(this, "", representedObject, hasChildren);
835     this._storagePanel = storagePanel;
836     this._titleText = title;
837     this._iconClasses = iconClasses;
838     this._noIcon = noIcon;
839 }
840
841 WebInspector.BaseStorageTreeElement.prototype = {
842     onattach: function()
843     {
844         this.listItemElement.removeChildren();
845         if (this._iconClasses) {
846             for (var i = 0; i < this._iconClasses.length; ++i)
847                 this.listItemElement.classList.add(this._iconClasses[i]);
848         }
849
850         this.listItemElement.createChild("div", "selection");
851
852         if (!this._noIcon)
853             this.imageElement = this.listItemElement.createChild("img", "icon");
854
855         this.titleElement = this.listItemElement.createChild("div", "base-storage-tree-element-title");
856         this._titleTextNode = this.titleElement.createTextChild("");
857         this._updateTitle();
858         this._updateSubtitle();
859     },
860
861     get displayName()
862     {
863         return this._displayName;
864     },
865
866     _updateDisplayName: function()
867     {
868         this._displayName = this._titleText || "";
869         if (this._subtitleText)
870             this._displayName += " (" + this._subtitleText + ")";
871     },
872
873     _updateTitle: function()
874     {
875         this._updateDisplayName();
876
877         if (!this.titleElement)
878             return;
879
880         this._titleTextNode.textContent = this._titleText || "";
881     },
882
883     _updateSubtitle: function()
884     {
885         this._updateDisplayName();
886
887         if (!this.titleElement)
888             return;
889
890         if (this._subtitleText) {
891             if (!this._subtitleElement)
892                 this._subtitleElement = this.titleElement.createChild("span", "base-storage-tree-element-subtitle");
893             this._subtitleElement.textContent = "(" + this._subtitleText + ")";
894         } else if (this._subtitleElement) {
895             this._subtitleElement.remove();
896             delete this._subtitleElement;
897         }
898     },
899
900     /**
901      * @override
902      * @return {boolean}
903      */
904     onselect: function(selectedByUser)
905     {
906         if (!selectedByUser)
907             return false;
908         var itemURL = this.itemURL;
909         if (itemURL)
910             WebInspector.settings.resourcesLastSelectedItem.set(itemURL);
911         return false;
912     },
913
914     /**
915      * @override
916      */
917     onreveal: function()
918     {
919         if (this.listItemElement)
920             this.listItemElement.scrollIntoViewIfNeeded(false);
921     },
922
923     get titleText()
924     {
925         return this._titleText;
926     },
927
928     set titleText(titleText)
929     {
930         this._titleText = titleText;
931         this._updateTitle();
932     },
933
934     get subtitleText()
935     {
936         return this._subtitleText;
937     },
938
939     set subtitleText(subtitleText)
940     {
941         this._subtitleText = subtitleText;
942         this._updateSubtitle();
943     },
944
945     __proto__: TreeElement.prototype
946 }
947
948 /**
949  * @constructor
950  * @extends {WebInspector.BaseStorageTreeElement}
951  * @param {!WebInspector.ResourcesPanel} storagePanel
952  * @param {string} categoryName
953  * @param {string} settingsKey
954  * @param {?Array.<string>=} iconClasses
955  * @param {boolean=} noIcon
956  */
957 WebInspector.StorageCategoryTreeElement = function(storagePanel, categoryName, settingsKey, iconClasses, noIcon)
958 {
959     WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, categoryName, iconClasses, false, noIcon);
960     this._expandedSettingKey = "resources" + settingsKey + "Expanded";
961     WebInspector.settings[this._expandedSettingKey] = WebInspector.settings.createSetting(this._expandedSettingKey, settingsKey === "Frames");
962     this._categoryName = categoryName;
963     this._target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.mainTarget());
964 }
965
966 WebInspector.StorageCategoryTreeElement.prototype = {
967     /**
968      * @return {!WebInspector.Target}
969      */
970     target: function()
971     {
972         return this._target;
973     },
974
975     get itemURL()
976     {
977         return "category://" + this._categoryName;
978     },
979
980     /**
981      * @override
982      * @return {boolean}
983      */
984     onselect: function(selectedByUser)
985     {
986         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
987         this._storagePanel.showCategoryView(this._categoryName);
988         return false;
989     },
990
991     /**
992      * @override
993      */
994     onattach: function()
995     {
996         WebInspector.BaseStorageTreeElement.prototype.onattach.call(this);
997         if (WebInspector.settings[this._expandedSettingKey].get())
998             this.expand();
999     },
1000
1001     /**
1002      * @override
1003      */
1004     onexpand: function()
1005     {
1006         WebInspector.settings[this._expandedSettingKey].set(true);
1007     },
1008
1009     /**
1010      * @override
1011      */
1012     oncollapse: function()
1013     {
1014         WebInspector.settings[this._expandedSettingKey].set(false);
1015     },
1016
1017     __proto__: WebInspector.BaseStorageTreeElement.prototype
1018 }
1019
1020 /**
1021  * @constructor
1022  * @extends {WebInspector.BaseStorageTreeElement}
1023  */
1024 WebInspector.FrameTreeElement = function(storagePanel, frame)
1025 {
1026     WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, "", ["frame-storage-tree-item"]);
1027     this._frame = frame;
1028     this.frameNavigated(frame);
1029 }
1030
1031 WebInspector.FrameTreeElement.prototype = {
1032     frameNavigated: function(frame)
1033     {
1034         this.removeChildren();
1035         this._frameId = frame.id;
1036
1037         this.titleText = frame.name;
1038         this.subtitleText = new WebInspector.ParsedURL(frame.url).displayName;
1039
1040         this._categoryElements = {};
1041         this._treeElementForResource = {};
1042
1043         this._storagePanel.addDocumentURL(frame.url);
1044     },
1045
1046     get itemURL()
1047     {
1048         return "frame://" + encodeURI(this.displayName);
1049     },
1050
1051     /**
1052      * @override
1053      * @return {boolean}
1054      */
1055     onselect: function(selectedByUser)
1056     {
1057         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
1058         this._storagePanel.showCategoryView(this.displayName);
1059
1060         this.listItemElement.classList.remove("hovered");
1061         DOMAgent.hideHighlight();
1062         return false;
1063     },
1064
1065     set hovered(hovered)
1066     {
1067         if (hovered) {
1068             this.listItemElement.classList.add("hovered");
1069             DOMAgent.highlightFrame(this._frameId, WebInspector.Color.PageHighlight.Content.toProtocolRGBA(), WebInspector.Color.PageHighlight.ContentOutline.toProtocolRGBA());
1070         } else {
1071             this.listItemElement.classList.remove("hovered");
1072             DOMAgent.hideHighlight();
1073         }
1074     },
1075
1076     appendResource: function(resource)
1077     {
1078         if (resource.isHidden())
1079             return;
1080         var categoryName = resource.type.name();
1081         var categoryElement = resource.type === WebInspector.resourceTypes.Document ? this : this._categoryElements[categoryName];
1082         if (!categoryElement) {
1083             categoryElement = new WebInspector.StorageCategoryTreeElement(this._storagePanel, resource.type.categoryTitle(), categoryName, null, true);
1084             this._categoryElements[resource.type.name()] = categoryElement;
1085             this._insertInPresentationOrder(this, categoryElement);
1086         }
1087         var resourceTreeElement = new WebInspector.FrameResourceTreeElement(this._storagePanel, resource);
1088         this._insertInPresentationOrder(categoryElement, resourceTreeElement);
1089         this._treeElementForResource[resource.url] = resourceTreeElement;
1090     },
1091
1092     /**
1093      * @param {string} url
1094      * @return {?WebInspector.Resource}
1095      */
1096     resourceByURL: function(url)
1097     {
1098         var treeElement = this._treeElementForResource[url];
1099         return treeElement ? treeElement.representedObject : null;
1100     },
1101
1102     appendChild: function(treeElement)
1103     {
1104         this._insertInPresentationOrder(this, treeElement);
1105     },
1106
1107     _insertInPresentationOrder: function(parentTreeElement, childTreeElement)
1108     {
1109         // Insert in the alphabetical order, first frames, then resources. Document resource goes last.
1110         function typeWeight(treeElement)
1111         {
1112             if (treeElement instanceof WebInspector.StorageCategoryTreeElement)
1113                 return 2;
1114             if (treeElement instanceof WebInspector.FrameTreeElement)
1115                 return 1;
1116             return 3;
1117         }
1118
1119         function compare(treeElement1, treeElement2)
1120         {
1121             var typeWeight1 = typeWeight(treeElement1);
1122             var typeWeight2 = typeWeight(treeElement2);
1123
1124             var result;
1125             if (typeWeight1 > typeWeight2)
1126                 result = 1;
1127             else if (typeWeight1 < typeWeight2)
1128                 result = -1;
1129             else {
1130                 var title1 = treeElement1.displayName || treeElement1.titleText;
1131                 var title2 = treeElement2.displayName || treeElement2.titleText;
1132                 result = title1.localeCompare(title2);
1133             }
1134             return result;
1135         }
1136
1137         var children = parentTreeElement.children;
1138         var i;
1139         for (i = 0; i < children.length; ++i) {
1140             if (compare(childTreeElement, children[i]) < 0)
1141                 break;
1142         }
1143         parentTreeElement.insertChild(childTreeElement, i);
1144     },
1145
1146     __proto__: WebInspector.BaseStorageTreeElement.prototype
1147 }
1148
1149 /**
1150  * @constructor
1151  * @extends {WebInspector.BaseStorageTreeElement}
1152  */
1153 WebInspector.FrameResourceTreeElement = function(storagePanel, resource)
1154 {
1155     WebInspector.BaseStorageTreeElement.call(this, storagePanel, resource, resource.displayName, ["resource-sidebar-tree-item", "resources-type-" + resource.type.name()]);
1156     this._resource = resource;
1157     this._resource.addEventListener(WebInspector.Resource.Events.MessageAdded, this._consoleMessageAdded, this);
1158     this._resource.addEventListener(WebInspector.Resource.Events.MessagesCleared, this._consoleMessagesCleared, this);
1159     this.tooltip = resource.url;
1160 }
1161
1162 WebInspector.FrameResourceTreeElement.prototype = {
1163     get itemURL()
1164     {
1165         return this._resource.url;
1166     },
1167
1168     /**
1169      * @override
1170      * @return {boolean}
1171      */
1172     onselect: function(selectedByUser)
1173     {
1174         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
1175         this._storagePanel._showResourceView(this._resource);
1176         return false;
1177     },
1178
1179     /**
1180      * @override
1181      * @return {boolean}
1182      */
1183     ondblclick: function(event)
1184     {
1185         InspectorFrontendHost.openInNewTab(this._resource.url);
1186         return false;
1187     },
1188
1189     /**
1190      * @override
1191      */
1192     onattach: function()
1193     {
1194         WebInspector.BaseStorageTreeElement.prototype.onattach.call(this);
1195
1196         if (this._resource.type === WebInspector.resourceTypes.Image) {
1197             var iconElement = document.createElementWithClass("div", "icon");
1198             var previewImage = iconElement.createChild("img", "image-resource-icon-preview");
1199             this._resource.populateImageSource(previewImage);
1200             this.listItemElement.replaceChild(iconElement, this.imageElement);
1201         }
1202
1203         this._statusElement = document.createElementWithClass("div", "status");
1204         this.listItemElement.insertBefore(this._statusElement, this.titleElement);
1205
1206         this.listItemElement.draggable = true;
1207         this.listItemElement.addEventListener("dragstart", this._ondragstart.bind(this), false);
1208         this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
1209
1210         this._updateErrorsAndWarningsBubbles();
1211     },
1212
1213     /**
1214      * @param {!MouseEvent} event
1215      * @return {boolean}
1216      */
1217     _ondragstart: function(event)
1218     {
1219         event.dataTransfer.setData("text/plain", this._resource.content);
1220         event.dataTransfer.effectAllowed = "copy";
1221         return true;
1222     },
1223
1224     _handleContextMenuEvent: function(event)
1225     {
1226         var contextMenu = new WebInspector.ContextMenu(event);
1227         contextMenu.appendApplicableItems(this._resource);
1228         contextMenu.show();
1229     },
1230
1231     /**
1232      * @param {string} x
1233      */
1234     _setBubbleText: function(x)
1235     {
1236         if (!this._bubbleElement)
1237             this._bubbleElement = this._statusElement.createChild("div", "bubble-repeat-count");
1238         this._bubbleElement.textContent = x;
1239     },
1240
1241     _resetBubble: function()
1242     {
1243         if (this._bubbleElement) {
1244             this._bubbleElement.textContent = "";
1245             this._bubbleElement.classList.remove("warning");
1246             this._bubbleElement.classList.remove("error");
1247         }
1248     },
1249
1250     _updateErrorsAndWarningsBubbles: function()
1251     {
1252         if (this._storagePanel.currentQuery)
1253             return;
1254
1255         this._resetBubble();
1256
1257         if (this._resource.warnings || this._resource.errors)
1258             this._setBubbleText(this._resource.warnings + this._resource.errors);
1259
1260         if (this._resource.warnings)
1261             this._bubbleElement.classList.add("warning");
1262
1263         if (this._resource.errors)
1264             this._bubbleElement.classList.add("error");
1265     },
1266
1267     _consoleMessagesCleared: function()
1268     {
1269         // FIXME: move to the SourceFrame.
1270         if (this._sourceView)
1271             this._sourceView.clearMessages();
1272
1273         this._updateErrorsAndWarningsBubbles();
1274     },
1275
1276     _consoleMessageAdded: function(event)
1277     {
1278         var msg = event.data;
1279         if (this._sourceView)
1280             this._sourceView.addMessage(msg);
1281         this._updateErrorsAndWarningsBubbles();
1282     },
1283
1284     /**
1285      * @return {!WebInspector.ResourceSourceFrame}
1286      */
1287     sourceView: function()
1288     {
1289         if (!this._sourceView) {
1290             var sourceFrame = new WebInspector.ResourceSourceFrame(this._resource);
1291             sourceFrame.setHighlighterType(this._resource.canonicalMimeType());
1292             this._sourceView = sourceFrame;
1293             if (this._resource.messages) {
1294                 for (var i = 0; i < this._resource.messages.length; i++)
1295                     this._sourceView.addMessage(this._resource.messages[i]);
1296             }
1297         }
1298         return this._sourceView;
1299     },
1300
1301     __proto__: WebInspector.BaseStorageTreeElement.prototype
1302 }
1303
1304 /**
1305  * @constructor
1306  * @extends {WebInspector.BaseStorageTreeElement}
1307  * @param {!WebInspector.ResourcesPanel} storagePanel
1308  * @param {!WebInspector.Database} database
1309  */
1310 WebInspector.DatabaseTreeElement = function(storagePanel, database)
1311 {
1312     WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, database.name, ["database-storage-tree-item"], true);
1313     this._database = database;
1314 }
1315
1316 WebInspector.DatabaseTreeElement.prototype = {
1317     get itemURL()
1318     {
1319         return "database://" + encodeURI(this._database.name);
1320     },
1321
1322     /**
1323      * @override
1324      * @return {boolean}
1325      */
1326     onselect: function(selectedByUser)
1327     {
1328         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
1329         this._storagePanel._showDatabase(this._database);
1330         return false;
1331     },
1332
1333     /**
1334      * @override
1335      */
1336     onexpand: function()
1337     {
1338         this._updateChildren();
1339     },
1340
1341     _updateChildren: function()
1342     {
1343         this.removeChildren();
1344
1345         /**
1346          * @param {!Array.<string>} tableNames
1347          * @this {WebInspector.DatabaseTreeElement}
1348          */
1349         function tableNamesCallback(tableNames)
1350         {
1351             var tableNamesLength = tableNames.length;
1352             for (var i = 0; i < tableNamesLength; ++i)
1353                 this.appendChild(new WebInspector.DatabaseTableTreeElement(this._storagePanel, this._database, tableNames[i]));
1354         }
1355         this._database.getTableNames(tableNamesCallback.bind(this));
1356     },
1357
1358     __proto__: WebInspector.BaseStorageTreeElement.prototype
1359 }
1360
1361 /**
1362  * @constructor
1363  * @extends {WebInspector.BaseStorageTreeElement}
1364  */
1365 WebInspector.DatabaseTableTreeElement = function(storagePanel, database, tableName)
1366 {
1367     WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, tableName, ["database-table-storage-tree-item"]);
1368     this._database = database;
1369     this._tableName = tableName;
1370 }
1371
1372 WebInspector.DatabaseTableTreeElement.prototype = {
1373     get itemURL()
1374     {
1375         return "database://" + encodeURI(this._database.name) + "/" + encodeURI(this._tableName);
1376     },
1377
1378     /**
1379      * @override
1380      * @return {boolean}
1381      */
1382     onselect: function(selectedByUser)
1383     {
1384         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
1385         this._storagePanel._showDatabase(this._database, this._tableName);
1386         return false;
1387     },
1388
1389     __proto__: WebInspector.BaseStorageTreeElement.prototype
1390 }
1391
1392 /**
1393  * @constructor
1394  * @extends {WebInspector.StorageCategoryTreeElement}
1395  * @param {!WebInspector.ResourcesPanel} storagePanel
1396  */
1397 WebInspector.IndexedDBTreeElement = function(storagePanel)
1398 {
1399     WebInspector.StorageCategoryTreeElement.call(this, storagePanel, WebInspector.UIString("IndexedDB"), "IndexedDB", ["indexed-db-storage-tree-item"]);
1400 }
1401
1402 WebInspector.IndexedDBTreeElement.prototype = {
1403     _initialize: function()
1404     {
1405         WebInspector.targetManager.addModelListener(WebInspector.IndexedDBModel, WebInspector.IndexedDBModel.EventTypes.DatabaseAdded, this._indexedDBAdded, this);
1406         WebInspector.targetManager.addModelListener(WebInspector.IndexedDBModel, WebInspector.IndexedDBModel.EventTypes.DatabaseRemoved, this._indexedDBRemoved, this);
1407         WebInspector.targetManager.addModelListener(WebInspector.IndexedDBModel, WebInspector.IndexedDBModel.EventTypes.DatabaseLoaded, this._indexedDBLoaded, this);
1408         /** @type {!Array.<!WebInspector.IDBDatabaseTreeElement>} */
1409         this._idbDatabaseTreeElements = [];
1410
1411         var targets = WebInspector.targetManager.targets();
1412         for (var i = 0; i < targets.length; ++i) {
1413             var databases = targets[i].indexedDBModel.databases();
1414             for (var j = 0; j < databases.length; ++j)
1415                 this._addIndexedDB(targets[i].indexedDBModel, databases[j]);
1416         }
1417     },
1418
1419     onattach: function()
1420     {
1421         WebInspector.StorageCategoryTreeElement.prototype.onattach.call(this);
1422         this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
1423     },
1424
1425     _handleContextMenuEvent: function(event)
1426     {
1427         var contextMenu = new WebInspector.ContextMenu(event);
1428         contextMenu.appendItem(WebInspector.UIString("Refresh IndexedDB"), this.refreshIndexedDB.bind(this));
1429         contextMenu.show();
1430     },
1431
1432     refreshIndexedDB: function()
1433     {
1434         var targets = WebInspector.targetManager.targets();
1435         for (var i = 0; i < targets.length; ++i)
1436             targets[i].indexedDBModel.refreshDatabaseNames();
1437     },
1438
1439     /**
1440      * @param {!WebInspector.Event} event
1441      */
1442     _indexedDBAdded: function(event)
1443     {
1444         var databaseId = /** @type {!WebInspector.IndexedDBModel.DatabaseId} */ (event.data);
1445         var model = /** @type {!WebInspector.IndexedDBModel} */ (event.target);
1446         this._addIndexedDB(model, databaseId);
1447     },
1448
1449     /**
1450      * @param {!WebInspector.IndexedDBModel} model
1451      * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
1452      */
1453     _addIndexedDB: function(model, databaseId)
1454     {
1455         var idbDatabaseTreeElement = new WebInspector.IDBDatabaseTreeElement(this._storagePanel, model, databaseId);
1456         this._idbDatabaseTreeElements.push(idbDatabaseTreeElement);
1457         this.appendChild(idbDatabaseTreeElement);
1458         model.refreshDatabase(databaseId);
1459     },
1460
1461     /**
1462      * @param {!WebInspector.Event} event
1463      */
1464     _indexedDBRemoved: function(event)
1465     {
1466         var databaseId = /** @type {!WebInspector.IndexedDBModel.DatabaseId} */ (event.data);
1467         var model = /** @type {!WebInspector.IndexedDBModel} */ (event.target);
1468
1469         var idbDatabaseTreeElement = this._idbDatabaseTreeElement(model, databaseId)
1470         if (!idbDatabaseTreeElement)
1471             return;
1472
1473         idbDatabaseTreeElement.clear();
1474         this.removeChild(idbDatabaseTreeElement);
1475         this._idbDatabaseTreeElements.remove(idbDatabaseTreeElement);
1476     },
1477
1478     /**
1479      * @param {!WebInspector.Event} event
1480      */
1481     _indexedDBLoaded: function(event)
1482     {
1483         var database = /** @type {!WebInspector.IndexedDBModel.Database} */ (event.data);
1484         var model = /** @type {!WebInspector.IndexedDBModel} */ (event.target);
1485
1486         var idbDatabaseTreeElement = this._idbDatabaseTreeElement(model, database.databaseId)
1487         if (!idbDatabaseTreeElement)
1488             return;
1489
1490         idbDatabaseTreeElement.update(database);
1491     },
1492
1493     /**
1494      * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
1495      * @param {!WebInspector.IndexedDBModel} model
1496      * @return {?WebInspector.IDBDatabaseTreeElement}
1497      */
1498     _idbDatabaseTreeElement: function(model, databaseId)
1499     {
1500         var index = -1;
1501         for (var i = 0; i < this._idbDatabaseTreeElements.length; ++i) {
1502             if (this._idbDatabaseTreeElements[i]._databaseId.equals(databaseId) && this._idbDatabaseTreeElements[i]._model === model) {
1503                 index = i;
1504                 break;
1505             }
1506         }
1507         if (index !== -1)
1508             return this._idbDatabaseTreeElements[i];
1509         return null;
1510     },
1511
1512     __proto__: WebInspector.StorageCategoryTreeElement.prototype
1513 }
1514
1515 /**
1516  * @constructor
1517  * @extends {WebInspector.StorageCategoryTreeElement}
1518  * @param {!WebInspector.ResourcesPanel} storagePanel
1519  */
1520 WebInspector.FileSystemListTreeElement = function(storagePanel)
1521 {
1522     WebInspector.StorageCategoryTreeElement.call(this, storagePanel, WebInspector.UIString("FileSystem"), "FileSystem", ["file-system-storage-tree-item"]);
1523 }
1524
1525 WebInspector.FileSystemListTreeElement.prototype = {
1526     _initialize: function()
1527     {
1528         this._refreshFileSystem();
1529     },
1530
1531     onattach: function()
1532     {
1533         WebInspector.StorageCategoryTreeElement.prototype.onattach.call(this);
1534         this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
1535     },
1536
1537     _handleContextMenuEvent: function(event)
1538     {
1539         var contextMenu = new WebInspector.ContextMenu(event);
1540         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Refresh FileSystem list" : "Refresh FileSystem List"), this._refreshFileSystem.bind(this));
1541         contextMenu.show();
1542     },
1543
1544     _fileSystemAdded: function(event)
1545     {
1546         var fileSystem = /** @type {!WebInspector.FileSystemModel.FileSystem} */ (event.data);
1547         var fileSystemTreeElement = new WebInspector.FileSystemTreeElement(this._storagePanel, fileSystem);
1548         this.appendChild(fileSystemTreeElement);
1549     },
1550
1551     _fileSystemRemoved: function(event)
1552     {
1553         var fileSystem = /** @type {!WebInspector.FileSystemModel.FileSystem} */ (event.data);
1554         var fileSystemTreeElement = this._fileSystemTreeElementByName(fileSystem.name);
1555         if (!fileSystemTreeElement)
1556             return;
1557         fileSystemTreeElement.clear();
1558         this.removeChild(fileSystemTreeElement);
1559     },
1560
1561     _fileSystemTreeElementByName: function(fileSystemName)
1562     {
1563         for (var i = 0; i < this.children.length; ++i) {
1564             var child = /** @type {!WebInspector.FileSystemTreeElement} */ (this.children[i]);
1565             if (child.fileSystemName === fileSystemName)
1566                 return this.children[i];
1567         }
1568         return null;
1569     },
1570
1571     _refreshFileSystem: function()
1572     {
1573         if (!this._fileSystemModel) {
1574             this._fileSystemModel = new WebInspector.FileSystemModel(this.target());
1575             this._fileSystemModel.addEventListener(WebInspector.FileSystemModel.EventTypes.FileSystemAdded, this._fileSystemAdded, this);
1576             this._fileSystemModel.addEventListener(WebInspector.FileSystemModel.EventTypes.FileSystemRemoved, this._fileSystemRemoved, this);
1577         }
1578
1579         this._fileSystemModel.refreshFileSystemList();
1580     },
1581
1582     __proto__: WebInspector.StorageCategoryTreeElement.prototype
1583 }
1584
1585 /**
1586  * @constructor
1587  * @extends {WebInspector.BaseStorageTreeElement}
1588  * @param {!WebInspector.ResourcesPanel} storagePanel
1589  * @param {!WebInspector.IndexedDBModel} model
1590  * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
1591  */
1592 WebInspector.IDBDatabaseTreeElement = function(storagePanel, model, databaseId)
1593 {
1594     WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, databaseId.name + " - " + databaseId.securityOrigin, ["indexed-db-storage-tree-item"]);
1595     this._model = model;
1596     this._databaseId = databaseId;
1597     this._idbObjectStoreTreeElements = {};
1598 }
1599
1600 WebInspector.IDBDatabaseTreeElement.prototype = {
1601     get itemURL()
1602     {
1603         return "indexedDB://" + this._databaseId.securityOrigin + "/" + this._databaseId.name;
1604     },
1605
1606     onattach: function()
1607     {
1608         WebInspector.BaseStorageTreeElement.prototype.onattach.call(this);
1609         this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
1610     },
1611
1612     _handleContextMenuEvent: function(event)
1613     {
1614         var contextMenu = new WebInspector.ContextMenu(event);
1615         contextMenu.appendItem(WebInspector.UIString("Refresh IndexedDB"), this._refreshIndexedDB.bind(this));
1616         contextMenu.show();
1617     },
1618
1619     _refreshIndexedDB: function()
1620     {
1621         this._model.refreshDatabaseNames();
1622     },
1623
1624     /**
1625      * @param {!WebInspector.IndexedDBModel.Database} database
1626      */
1627     update: function(database)
1628     {
1629         this._database = database;
1630         var objectStoreNames = {};
1631         for (var objectStoreName in this._database.objectStores) {
1632             var objectStore = this._database.objectStores[objectStoreName];
1633             objectStoreNames[objectStore.name] = true;
1634             if (!this._idbObjectStoreTreeElements[objectStore.name]) {
1635                 var idbObjectStoreTreeElement = new WebInspector.IDBObjectStoreTreeElement(this._storagePanel, this._model, this._databaseId, objectStore);
1636                 this._idbObjectStoreTreeElements[objectStore.name] = idbObjectStoreTreeElement;
1637                 this.appendChild(idbObjectStoreTreeElement);
1638             }
1639             this._idbObjectStoreTreeElements[objectStore.name].update(objectStore);
1640         }
1641         for (var objectStoreName in this._idbObjectStoreTreeElements) {
1642             if (!objectStoreNames[objectStoreName])
1643                 this._objectStoreRemoved(objectStoreName);
1644         }
1645
1646         if (this.children.length) {
1647             this.hasChildren = true;
1648             this.expand();
1649         }
1650
1651         if (this._view)
1652             this._view.update(database);
1653
1654         this._updateTooltip();
1655     },
1656
1657     _updateTooltip: function()
1658     {
1659         this.tooltip = WebInspector.UIString("Version") + ": " + this._database.version;
1660     },
1661
1662     /**
1663      * @override
1664      * @return {boolean}
1665      */
1666     onselect: function(selectedByUser)
1667     {
1668         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
1669         if (!this._view)
1670             this._view = new WebInspector.IDBDatabaseView(this._database);
1671
1672         this._storagePanel.showIndexedDB(this._view);
1673         return false;
1674     },
1675
1676     /**
1677      * @param {string} objectStoreName
1678      */
1679     _objectStoreRemoved: function(objectStoreName)
1680     {
1681         var objectStoreTreeElement = this._idbObjectStoreTreeElements[objectStoreName];
1682         objectStoreTreeElement.clear();
1683         this.removeChild(objectStoreTreeElement);
1684         delete this._idbObjectStoreTreeElements[objectStoreName];
1685     },
1686
1687     clear: function()
1688     {
1689         for (var objectStoreName in this._idbObjectStoreTreeElements)
1690             this._objectStoreRemoved(objectStoreName);
1691     },
1692
1693     __proto__: WebInspector.BaseStorageTreeElement.prototype
1694 }
1695
1696 /**
1697  * @constructor
1698  * @extends {WebInspector.BaseStorageTreeElement}
1699  * @param {!WebInspector.ResourcesPanel} storagePanel
1700  * @param {!WebInspector.IndexedDBModel} model
1701  * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
1702  * @param {!WebInspector.IndexedDBModel.ObjectStore} objectStore
1703  */
1704 WebInspector.IDBObjectStoreTreeElement = function(storagePanel, model, databaseId, objectStore)
1705 {
1706     WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, objectStore.name, ["indexed-db-object-store-storage-tree-item"]);
1707     this._model = model;
1708     this._databaseId = databaseId;
1709     this._idbIndexTreeElements = {};
1710 }
1711
1712 WebInspector.IDBObjectStoreTreeElement.prototype = {
1713     get itemURL()
1714     {
1715         return "indexedDB://" + this._databaseId.securityOrigin + "/" + this._databaseId.name + "/" + this._objectStore.name;
1716     },
1717
1718     onattach: function()
1719     {
1720         WebInspector.BaseStorageTreeElement.prototype.onattach.call(this);
1721         this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
1722     },
1723
1724     _handleContextMenuEvent: function(event)
1725     {
1726         var contextMenu = new WebInspector.ContextMenu(event);
1727         contextMenu.appendItem(WebInspector.UIString("Clear"), this._clearObjectStore.bind(this));
1728         contextMenu.show();
1729     },
1730
1731     _clearObjectStore: function()
1732     {
1733         /**
1734          * @this {WebInspector.IDBObjectStoreTreeElement}
1735          */
1736         function callback() {
1737             this.update(this._objectStore);
1738         }
1739         this._model.clearObjectStore(this._databaseId, this._objectStore.name, callback.bind(this));
1740     },
1741
1742    /**
1743      * @param {!WebInspector.IndexedDBModel.ObjectStore} objectStore
1744      */
1745     update: function(objectStore)
1746     {
1747         this._objectStore = objectStore;
1748
1749         var indexNames = {};
1750         for (var indexName in this._objectStore.indexes) {
1751             var index = this._objectStore.indexes[indexName];
1752             indexNames[index.name] = true;
1753             if (!this._idbIndexTreeElements[index.name]) {
1754                 var idbIndexTreeElement = new WebInspector.IDBIndexTreeElement(this._storagePanel, this._model, this._databaseId, this._objectStore, index);
1755                 this._idbIndexTreeElements[index.name] = idbIndexTreeElement;
1756                 this.appendChild(idbIndexTreeElement);
1757             }
1758             this._idbIndexTreeElements[index.name].update(index);
1759         }
1760         for (var indexName in this._idbIndexTreeElements) {
1761             if (!indexNames[indexName])
1762                 this._indexRemoved(indexName);
1763         }
1764         for (var indexName in this._idbIndexTreeElements) {
1765             if (!indexNames[indexName]) {
1766                 this.removeChild(this._idbIndexTreeElements[indexName]);
1767                 delete this._idbIndexTreeElements[indexName];
1768             }
1769         }
1770
1771         if (this.children.length) {
1772             this.hasChildren = true;
1773             this.expand();
1774         }
1775
1776         if (this._view)
1777             this._view.update(this._objectStore);
1778
1779         this._updateTooltip();
1780     },
1781
1782     _updateTooltip: function()
1783     {
1784
1785         var keyPathString = this._objectStore.keyPathString;
1786         var tooltipString = keyPathString !== null ? (WebInspector.UIString("Key path: ") + keyPathString) : "";
1787         if (this._objectStore.autoIncrement)
1788             tooltipString += "\n" + WebInspector.UIString("autoIncrement");
1789         this.tooltip = tooltipString
1790     },
1791
1792     /**
1793      * @override
1794      * @return {boolean}
1795      */
1796     onselect: function(selectedByUser)
1797     {
1798         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
1799         if (!this._view)
1800             this._view = new WebInspector.IDBDataView(this._model, this._databaseId, this._objectStore, null);
1801
1802         this._storagePanel.showIndexedDB(this._view);
1803         return false;
1804     },
1805
1806     /**
1807      * @param {string} indexName
1808      */
1809     _indexRemoved: function(indexName)
1810     {
1811         var indexTreeElement = this._idbIndexTreeElements[indexName];
1812         indexTreeElement.clear();
1813         this.removeChild(indexTreeElement);
1814         delete this._idbIndexTreeElements[indexName];
1815     },
1816
1817     clear: function()
1818     {
1819         for (var indexName in this._idbIndexTreeElements)
1820             this._indexRemoved(indexName);
1821         if (this._view)
1822             this._view.clear();
1823     },
1824
1825     __proto__: WebInspector.BaseStorageTreeElement.prototype
1826 }
1827
1828 /**
1829  * @constructor
1830  * @extends {WebInspector.BaseStorageTreeElement}
1831  * @param {!WebInspector.ResourcesPanel} storagePanel
1832  * @param {!WebInspector.IndexedDBModel} model
1833  * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
1834  * @param {!WebInspector.IndexedDBModel.ObjectStore} objectStore
1835  * @param {!WebInspector.IndexedDBModel.Index} index
1836  */
1837 WebInspector.IDBIndexTreeElement = function(storagePanel, model, databaseId, objectStore, index)
1838 {
1839     WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, index.name, ["indexed-db-index-storage-tree-item"]);
1840     this._model = model;
1841     this._databaseId = databaseId;
1842     this._objectStore = objectStore;
1843     this._index = index;
1844 }
1845
1846 WebInspector.IDBIndexTreeElement.prototype = {
1847     get itemURL()
1848     {
1849         return "indexedDB://" + this._databaseId.securityOrigin + "/" + this._databaseId.name + "/" + this._objectStore.name + "/" + this._index.name;
1850     },
1851
1852     /**
1853      * @param {!WebInspector.IndexedDBModel.Index} index
1854      */
1855     update: function(index)
1856     {
1857         this._index = index;
1858
1859         if (this._view)
1860             this._view.update(this._index);
1861
1862         this._updateTooltip();
1863     },
1864
1865     _updateTooltip: function()
1866     {
1867         var tooltipLines = [];
1868         var keyPathString = this._index.keyPathString;
1869         tooltipLines.push(WebInspector.UIString("Key path: ") + keyPathString);
1870         if (this._index.unique)
1871             tooltipLines.push(WebInspector.UIString("unique"));
1872         if (this._index.multiEntry)
1873             tooltipLines.push(WebInspector.UIString("multiEntry"));
1874         this.tooltip = tooltipLines.join("\n");
1875     },
1876
1877     /**
1878      * @override
1879      * @return {boolean}
1880      */
1881     onselect: function(selectedByUser)
1882     {
1883         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
1884         if (!this._view)
1885             this._view = new WebInspector.IDBDataView(this._model, this._databaseId, this._objectStore, this._index);
1886
1887         this._storagePanel.showIndexedDB(this._view);
1888         return false;
1889     },
1890
1891     clear: function()
1892     {
1893         if (this._view)
1894             this._view.clear();
1895     },
1896
1897     __proto__: WebInspector.BaseStorageTreeElement.prototype
1898 }
1899
1900 /**
1901  * @constructor
1902  * @extends {WebInspector.BaseStorageTreeElement}
1903  */
1904 WebInspector.DOMStorageTreeElement = function(storagePanel, domStorage, className)
1905 {
1906     WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, domStorage.securityOrigin ? domStorage.securityOrigin : WebInspector.UIString("Local Files"), ["domstorage-storage-tree-item", className]);
1907     this._domStorage = domStorage;
1908 }
1909
1910 WebInspector.DOMStorageTreeElement.prototype = {
1911     get itemURL()
1912     {
1913         return "storage://" + this._domStorage.securityOrigin + "/" + (this._domStorage.isLocalStorage ? "local" : "session");
1914     },
1915
1916     /**
1917      * @override
1918      * @return {boolean}
1919      */
1920     onselect: function(selectedByUser)
1921     {
1922         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
1923         this._storagePanel._showDOMStorage(this._domStorage);
1924         return false;
1925     },
1926
1927     __proto__: WebInspector.BaseStorageTreeElement.prototype
1928 }
1929
1930 /**
1931  * @constructor
1932  * @extends {WebInspector.BaseStorageTreeElement}
1933  */
1934 WebInspector.CookieTreeElement = function(storagePanel, cookieDomain)
1935 {
1936     WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, cookieDomain ? cookieDomain : WebInspector.UIString("Local Files"), ["cookie-storage-tree-item"]);
1937     this._cookieDomain = cookieDomain;
1938 }
1939
1940 WebInspector.CookieTreeElement.prototype = {
1941     get itemURL()
1942     {
1943         return "cookies://" + this._cookieDomain;
1944     },
1945
1946     onattach: function()
1947     {
1948         WebInspector.BaseStorageTreeElement.prototype.onattach.call(this);
1949         this.listItemElement.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this), true);
1950     },
1951
1952     /**
1953      * @param {!Event} event
1954      */
1955     _handleContextMenuEvent: function(event)
1956     {
1957         var contextMenu = new WebInspector.ContextMenu(event);
1958         contextMenu.appendItem(WebInspector.UIString("Clear"), this._clearCookies.bind(this));
1959         contextMenu.show();
1960     },
1961
1962     /**
1963      * @param {string} domain
1964      */
1965     _clearCookies: function(domain)
1966     {
1967         this._storagePanel.clearCookies(this._cookieDomain);
1968     },
1969
1970     /**
1971      * @override
1972      * @return {boolean}
1973      */
1974     onselect: function(selectedByUser)
1975     {
1976         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
1977         this._storagePanel.showCookies(this, this._cookieDomain);
1978         return false;
1979     },
1980
1981     __proto__: WebInspector.BaseStorageTreeElement.prototype
1982 }
1983
1984 /**
1985  * @constructor
1986  * @extends {WebInspector.BaseStorageTreeElement}
1987  */
1988 WebInspector.ApplicationCacheManifestTreeElement = function(storagePanel, manifestURL)
1989 {
1990     var title = new WebInspector.ParsedURL(manifestURL).displayName;
1991     WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, title, ["application-cache-storage-tree-item"]);
1992     this.tooltip = manifestURL;
1993     this._manifestURL = manifestURL;
1994 }
1995
1996 WebInspector.ApplicationCacheManifestTreeElement.prototype = {
1997     get itemURL()
1998     {
1999         return "appcache://" + this._manifestURL;
2000     },
2001
2002     get manifestURL()
2003     {
2004         return this._manifestURL;
2005     },
2006
2007     /**
2008      * @override
2009      * @return {boolean}
2010      */
2011     onselect: function(selectedByUser)
2012     {
2013         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
2014         this._storagePanel.showCategoryView(this._manifestURL);
2015         return false;
2016     },
2017
2018     __proto__: WebInspector.BaseStorageTreeElement.prototype
2019 }
2020
2021 /**
2022  * @constructor
2023  * @extends {WebInspector.BaseStorageTreeElement}
2024  */
2025 WebInspector.ApplicationCacheFrameTreeElement = function(storagePanel, frameId, manifestURL)
2026 {
2027     WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, "", ["frame-storage-tree-item"]);
2028     this._frameId = frameId;
2029     this._manifestURL = manifestURL;
2030     this._refreshTitles();
2031 }
2032
2033 WebInspector.ApplicationCacheFrameTreeElement.prototype = {
2034     get itemURL()
2035     {
2036         return "appcache://" + this._manifestURL + "/" + encodeURI(this.displayName);
2037     },
2038
2039     get frameId()
2040     {
2041         return this._frameId;
2042     },
2043
2044     get manifestURL()
2045     {
2046         return this._manifestURL;
2047     },
2048
2049     _refreshTitles: function()
2050     {
2051         var frame = WebInspector.resourceTreeModel.frameForId(this._frameId);
2052         if (!frame) {
2053             this.subtitleText = WebInspector.UIString("new frame");
2054             return;
2055         }
2056         this.titleText = frame.name;
2057         this.subtitleText = new WebInspector.ParsedURL(frame.url).displayName;
2058     },
2059
2060     frameNavigated: function()
2061     {
2062         this._refreshTitles();
2063     },
2064
2065     /**
2066      * @override
2067      * @return {boolean}
2068      */
2069     onselect: function(selectedByUser)
2070     {
2071         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
2072         this._storagePanel.showApplicationCache(this._frameId);
2073         return false;
2074     },
2075
2076     __proto__: WebInspector.BaseStorageTreeElement.prototype
2077 }
2078
2079 /**
2080  * @constructor
2081  * @extends {WebInspector.BaseStorageTreeElement}
2082  * @param {!WebInspector.ResourcesPanel} storagePanel
2083  * @param {!WebInspector.FileSystemModel.FileSystem} fileSystem
2084  */
2085 WebInspector.FileSystemTreeElement = function(storagePanel, fileSystem)
2086 {
2087     var displayName = fileSystem.type + " - " + fileSystem.origin;
2088     WebInspector.BaseStorageTreeElement.call(this, storagePanel, null, displayName, ["file-system-storage-tree-item"]);
2089     this._fileSystem = fileSystem;
2090 }
2091
2092 WebInspector.FileSystemTreeElement.prototype = {
2093     get fileSystemName()
2094     {
2095         return this._fileSystem.name;
2096     },
2097
2098     get itemURL()
2099     {
2100         return "filesystem://" + this._fileSystem.name;
2101     },
2102
2103     /**
2104      * @override
2105      * @return {boolean}
2106      */
2107     onselect: function(selectedByUser)
2108     {
2109         WebInspector.BaseStorageTreeElement.prototype.onselect.call(this, selectedByUser);
2110         this._fileSystemView = new WebInspector.FileSystemView(this._fileSystem);
2111         this._storagePanel.showFileSystem(this._fileSystemView);
2112         return false;
2113     },
2114
2115     clear: function()
2116     {
2117         if (this.fileSystemView && this._storagePanel.visibleView === this.fileSystemView)
2118             this._storagePanel.closeVisibleView();
2119     },
2120
2121     __proto__: WebInspector.BaseStorageTreeElement.prototype
2122 }
2123
2124 /**
2125  * @constructor
2126  * @extends {WebInspector.VBox}
2127  */
2128 WebInspector.StorageCategoryView = function()
2129 {
2130     WebInspector.VBox.call(this);
2131
2132     this.element.classList.add("storage-view");
2133     this._emptyView = new WebInspector.EmptyView("");
2134     this._emptyView.show(this.element);
2135 }
2136
2137 WebInspector.StorageCategoryView.prototype = {
2138     setText: function(text)
2139     {
2140         this._emptyView.text = text;
2141     },
2142
2143     __proto__: WebInspector.VBox.prototype
2144 }