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