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