1/*
2 * Copyright (C) 2007, 2008, 2010 Apple Inc.  All rights reserved.
3 * Copyright (C) 2009 Joseph Pecoraro
4 * Copyright (C) 2013 Samsung Electronics. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1.  Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 * 2.  Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 *     its contributors may be used to endorse or promote products derived
17 *     from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31importScript("ApplicationCacheItemsView.js");
32importScript("DOMStorageItemsView.js");
33importScript("DatabaseQueryView.js");
34importScript("DatabaseTableView.js");
35importScript("DirectoryContentView.js");
36importScript("IndexedDBViews.js");
37importScript("FileContentView.js");
38importScript("FileSystemView.js");
39
40/**
41 * @constructor
42 * @extends {WebInspector.Panel}
43 */
44WebInspector.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
126WebInspector.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 */
957WebInspector.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
966WebInspector.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 */
1086WebInspector.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
1094WebInspector.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 */
1130WebInspector.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
1137WebInspector.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 */
1250WebInspector.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
1259WebInspector.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 */
1421WebInspector.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
1427WebInspector.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 */
1464WebInspector.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
1471WebInspector.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 */
1491WebInspector.IndexedDBTreeElement = function(storagePanel)
1492{
1493    WebInspector.StorageCategoryTreeElement.call(this, storagePanel, WebInspector.UIString("IndexedDB"), "IndexedDB", ["indexed-db-storage-tree-item"]);
1494}
1495
1496WebInspector.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 */
1606WebInspector.FileSystemListTreeElement = function(storagePanel)
1607{
1608    WebInspector.StorageCategoryTreeElement.call(this, storagePanel, WebInspector.UIString("FileSystem"), "FileSystem", ["file-system-storage-tree-item"]);
1609}
1610
1611WebInspector.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 */
1679WebInspector.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
1687WebInspector.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 */
1786WebInspector.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
1794WebInspector.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 */
1911WebInspector.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
1920WebInspector.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 */
1973WebInspector.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
1979WebInspector.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 */
1998WebInspector.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
2004WebInspector.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 */
2047WebInspector.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
2055WebInspector.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 */
2079WebInspector.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
2087WebInspector.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 */
2134WebInspector.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
2141WebInspector.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 */
2172WebInspector.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
2181WebInspector.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 */
2195WebInspector.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
2204WebInspector.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 */
2270WebInspector.SearchResultsTreeElementsTraverser = function(rootElement)
2271{
2272    this._root = rootElement;
2273}
2274
2275WebInspector.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