Web Inspector: support runtime registration of tab type associations
authorbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Dec 2015 19:53:05 +0000 (19:53 +0000)
committerbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Dec 2015 19:53:05 +0000 (19:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=151594

Reviewed by Joseph Pecoraro.

We want to add special tabs that only exist in engineering builds
for debugging purposes. Though the relevant models and views can be
put in the Debug/ directory to exclude them from production builds,
there's no way to register tabs conditionally at runtime; tabs are
hardcoded.

This patch makes it possible to register new tab types at runtime.
First, WebInspector keeps a map of known, registered tab classes.
Details that were hardcoded before---whether to show in New Tab,
whether a tab can be instantiated given the active domains, UI text,
etc.---are now static methods on the base TabContentView or overidden
in its subclasses. Lastly, a public method allows code in Bootstrap.js
to register tabs at runtime. Doing so sends a notification so the
NewTabContentView can show the newly available tab item.

* UserInterface/Base/Main.js:
(WebInspector.contentLoaded):
(WebInspector.isTabTypeAllowed):
(WebInspector.knownTabClasses): Added, used by NewTabContentView.
(WebInspector._createTabContentViewForType): Renamed from _tabContentViewForType.
(WebInspector._rememberOpenTabs):
(WebInspector._updateNewTabButtonState):
(WebInspector._tryToRestorePendingTabs): Added.

Whenever a new tab is registered, try to restore pending tabs, since
an extra tab won't be added initially when production tabs are added.
But, it could have been saved in the Setting for opened tabs.

(WebInspector.showNewTabTab):
(WebInspector.isNewTabWithTypeAllowed):
(WebInspector.createNewTabWithType):
(WebInspector._tabContentViewForType): Deleted.
* UserInterface/Base/Object.js:
* UserInterface/Views/ConsoleTabContentView.js:
(WebInspector.ConsoleTabContentView):
(WebInspector.ConsoleTabContentView.tabInfo): Added.
* UserInterface/Views/DebuggerTabContentView.js:
(WebInspector.DebuggerTabContentView):
(WebInspector.DebuggerTabContentView.tabInfo): Added.
* UserInterface/Views/ElementsTabContentView.js:
(WebInspector.ElementsTabContentView):
(WebInspector.ElementsTabContentView.tabInfo): Added.
(WebInspector.ElementsTabContentView.isTabAllowed): Added.
* UserInterface/Views/NetworkTabContentView.js:
(WebInspector.NetworkTabContentView):
(WebInspector.NetworkTabContentView.tabInfo): Added.
(WebInspector.NetworkTabContentView.isTabAllowed): Added.
* UserInterface/Views/NewTabContentView.js:

Keep a list of shown tab items, so we don't have to query the DOM
to update enabled/disabled state. Put tree construction inside a
layout() override and dirty the view whenever known tab types change.

(WebInspector.NewTabContentView):
(WebInspector.NewTabContentView.tabInfo): Added.
(WebInspector.NewTabContentView.isEphemeral): Added.
(WebInspector.NewTabContentView.shouldSaveTab): Added.
(WebInspector.NewTabContentView.prototype.layout): Added.
(WebInspector.NewTabContentView.prototype._updateShownTabs): Added.
(WebInspector.NewTabContentView.prototype._allowableTabTypes):
(WebInspector.NewTabContentView.prototype._updateTabItems):
(WebInspector.NewTabContentView.prototype.get tabItemElements): Deleted.
* UserInterface/Views/ResourcesTabContentView.js:
(WebInspector.ResourcesTabContentView):
(WebInspector.ResourcesTabContentView.tabInfo): Added.
* UserInterface/Views/SearchTabContentView.js:
(WebInspector.SearchTabContentView):
(WebInspector.SearchTabContentView.tabInfo): Added.
(WebInspector.SearchTabContentView.isEphemeral): Added.
* UserInterface/Views/SettingsTabContentView.js:
(WebInspector.SettingsTabContentView.isTabAllowed): Added.
(WebInspector.SettingsTabContentView.shouldSaveTab): Added.
* UserInterface/Views/StorageTabContentView.js:
(WebInspector.StorageTabContentView):
(WebInspector.StorageTabContentView.tabInfo): Added.
(WebInspector.StorageTabContentView.isTabAllowed): Added.
* UserInterface/Views/TabBrowser.js:
(WebInspector.TabBrowser.showTabForContentView):

Add a workaround for <https://webkit.org/b/151876>. This bug is
revealed by the changes to NewTabContentView in this patch.

* UserInterface/Views/TabContentView.js:
(WebInspector.TabContentView.isTabAllowed): Added.
(WebInspector.TabContentView.isEphemeral): Added.
(WebInspector.TabContentView.shouldSaveTab): Added.
* UserInterface/Views/TimelineTabContentView.js:
(WebInspector.TimelineTabContentView):
(WebInspector.TimelineTabContentView.tabInfo): Added.
(WebInspector.TimelineTabContentView.isTabAllowed): Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@193432 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Base/Main.js
Source/WebInspectorUI/UserInterface/Base/Object.js
Source/WebInspectorUI/UserInterface/Views/ConsoleTabContentView.js
Source/WebInspectorUI/UserInterface/Views/DebuggerTabContentView.js
Source/WebInspectorUI/UserInterface/Views/ElementsTabContentView.js
Source/WebInspectorUI/UserInterface/Views/NetworkTabContentView.js
Source/WebInspectorUI/UserInterface/Views/NewTabContentView.js
Source/WebInspectorUI/UserInterface/Views/ResourcesTabContentView.js
Source/WebInspectorUI/UserInterface/Views/SearchTabContentView.js
Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js
Source/WebInspectorUI/UserInterface/Views/StorageTabContentView.js
Source/WebInspectorUI/UserInterface/Views/TabBrowser.js
Source/WebInspectorUI/UserInterface/Views/TabContentView.js
Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js

index aa77e24..0685e5a 100644 (file)
@@ -1,3 +1,101 @@
+2015-12-04  Brian Burg  <bburg@apple.com>
+
+        Web Inspector: support runtime registration of tab type associations
+        https://bugs.webkit.org/show_bug.cgi?id=151594
+
+        Reviewed by Joseph Pecoraro.
+
+        We want to add special tabs that only exist in engineering builds
+        for debugging purposes. Though the relevant models and views can be
+        put in the Debug/ directory to exclude them from production builds,
+        there's no way to register tabs conditionally at runtime; tabs are
+        hardcoded.
+
+        This patch makes it possible to register new tab types at runtime.
+        First, WebInspector keeps a map of known, registered tab classes.
+        Details that were hardcoded before---whether to show in New Tab,
+        whether a tab can be instantiated given the active domains, UI text,
+        etc.---are now static methods on the base TabContentView or overidden
+        in its subclasses. Lastly, a public method allows code in Bootstrap.js
+        to register tabs at runtime. Doing so sends a notification so the
+        NewTabContentView can show the newly available tab item.
+
+        * UserInterface/Base/Main.js:
+        (WebInspector.contentLoaded):
+        (WebInspector.isTabTypeAllowed):
+        (WebInspector.knownTabClasses): Added, used by NewTabContentView.
+        (WebInspector._createTabContentViewForType): Renamed from _tabContentViewForType.
+        (WebInspector._rememberOpenTabs):
+        (WebInspector._updateNewTabButtonState):
+        (WebInspector._tryToRestorePendingTabs): Added.
+
+        Whenever a new tab is registered, try to restore pending tabs, since
+        an extra tab won't be added initially when production tabs are added.
+        But, it could have been saved in the Setting for opened tabs.
+
+        (WebInspector.showNewTabTab):
+        (WebInspector.isNewTabWithTypeAllowed):
+        (WebInspector.createNewTabWithType):
+        (WebInspector._tabContentViewForType): Deleted.
+        * UserInterface/Base/Object.js:
+        * UserInterface/Views/ConsoleTabContentView.js:
+        (WebInspector.ConsoleTabContentView):
+        (WebInspector.ConsoleTabContentView.tabInfo): Added.
+        * UserInterface/Views/DebuggerTabContentView.js:
+        (WebInspector.DebuggerTabContentView):
+        (WebInspector.DebuggerTabContentView.tabInfo): Added.
+        * UserInterface/Views/ElementsTabContentView.js:
+        (WebInspector.ElementsTabContentView):
+        (WebInspector.ElementsTabContentView.tabInfo): Added.
+        (WebInspector.ElementsTabContentView.isTabAllowed): Added.
+        * UserInterface/Views/NetworkTabContentView.js:
+        (WebInspector.NetworkTabContentView):
+        (WebInspector.NetworkTabContentView.tabInfo): Added.
+        (WebInspector.NetworkTabContentView.isTabAllowed): Added.
+        * UserInterface/Views/NewTabContentView.js:
+
+        Keep a list of shown tab items, so we don't have to query the DOM
+        to update enabled/disabled state. Put tree construction inside a
+        layout() override and dirty the view whenever known tab types change.
+
+        (WebInspector.NewTabContentView):
+        (WebInspector.NewTabContentView.tabInfo): Added.
+        (WebInspector.NewTabContentView.isEphemeral): Added.
+        (WebInspector.NewTabContentView.shouldSaveTab): Added.
+        (WebInspector.NewTabContentView.prototype.layout): Added.
+        (WebInspector.NewTabContentView.prototype._updateShownTabs): Added.
+        (WebInspector.NewTabContentView.prototype._allowableTabTypes):
+        (WebInspector.NewTabContentView.prototype._updateTabItems):
+        (WebInspector.NewTabContentView.prototype.get tabItemElements): Deleted.
+        * UserInterface/Views/ResourcesTabContentView.js:
+        (WebInspector.ResourcesTabContentView):
+        (WebInspector.ResourcesTabContentView.tabInfo): Added.
+        * UserInterface/Views/SearchTabContentView.js:
+        (WebInspector.SearchTabContentView):
+        (WebInspector.SearchTabContentView.tabInfo): Added.
+        (WebInspector.SearchTabContentView.isEphemeral): Added.
+        * UserInterface/Views/SettingsTabContentView.js:
+        (WebInspector.SettingsTabContentView.isTabAllowed): Added.
+        (WebInspector.SettingsTabContentView.shouldSaveTab): Added.
+        * UserInterface/Views/StorageTabContentView.js:
+        (WebInspector.StorageTabContentView):
+        (WebInspector.StorageTabContentView.tabInfo): Added.
+        (WebInspector.StorageTabContentView.isTabAllowed): Added.
+        * UserInterface/Views/TabBrowser.js:
+        (WebInspector.TabBrowser.showTabForContentView):
+
+        Add a workaround for <https://webkit.org/b/151876>. This bug is
+        revealed by the changes to NewTabContentView in this patch.
+
+        * UserInterface/Views/TabContentView.js:
+        (WebInspector.TabContentView.isTabAllowed): Added.
+        (WebInspector.TabContentView.isEphemeral): Added.
+        (WebInspector.TabContentView.shouldSaveTab): Added.
+        * UserInterface/Views/TimelineTabContentView.js:
+        (WebInspector.TimelineTabContentView):
+        (WebInspector.TimelineTabContentView.tabInfo): Added.
+        (WebInspector.TimelineTabContentView.isTabAllowed): Added.
+
 2015-12-04  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: Remove untested and unused Worker inspection
index 5a3697b..bcd1bfa 100644 (file)
@@ -365,6 +365,26 @@ WebInspector.contentLoaded = function()
     this._updateToolbarHeight();
     this._setupViewHierarchy();
 
+    // These tabs are always available for selecting, modulo isTabAllowed().
+    // Other tabs may be engineering-only or toggled at runtime if incomplete.
+    let productionTabClasses = [
+        WebInspector.ConsoleTabContentView,
+        WebInspector.DebuggerTabContentView,
+        WebInspector.ElementsTabContentView,
+        WebInspector.NetworkTabContentView,
+        WebInspector.NewTabContentView,
+        WebInspector.ResourcesTabContentView,
+        WebInspector.SearchTabContentView,
+        WebInspector.StorageTabContentView,
+        WebInspector.TimelineTabContentView,
+    ];
+
+    this._knownTabClassesByType = new Map;
+    // Set tab classes directly. The public API triggers other updates and
+    // notifications that won't work or have no listeners at this point.
+    for (let tabClass of productionTabClasses)
+        this._knownTabClassesByType.set(tabClass.Type, tabClass);
+
     this._pendingOpenTabs = [];
 
     let openTabTypes = this._openTabsSetting.value;
@@ -376,7 +396,7 @@ WebInspector.contentLoaded = function()
             continue;
         }
 
-        let tabContentView = this._tabContentViewForType(tabType);
+        let tabContentView = this._createTabContentViewForType(tabType);
         if (!tabContentView)
             continue;
         this.tabBrowser.addTabForContentView(tabContentView, true);
@@ -417,57 +437,39 @@ WebInspector.contentLoaded = function()
 
 WebInspector.isTabTypeAllowed = function(tabType)
 {
-    switch (tabType) {
-    case WebInspector.ElementsTabContentView.Type:
-        return !!window.DOMAgent;
-    case WebInspector.NetworkTabContentView.Type:
-        return !!window.NetworkAgent && !!window.PageAgent;
-    case WebInspector.StorageTabContentView.Type:
-        return !!window.DOMStorageAgent || !!window.DatabaseAgent || !!window.IndexedDBAgent;
-    case WebInspector.TimelineTabContentView.Type:
-        return !!window.TimelineAgent;
-    }
+    let tabClass = this._knownTabClassesByType.get(tabType);
+    if (!tabClass)
+        return false;
 
-    return true;
+    return tabClass.isTabAllowed();
 };
 
-WebInspector._tabContentViewForType = function(tabType)
-{
-    switch (tabType) {
-    case WebInspector.ConsoleTabContentView.Type:
-        return new WebInspector.ConsoleTabContentView;
-    case WebInspector.DebuggerTabContentView.Type:
-        return new WebInspector.DebuggerTabContentView;
-    case WebInspector.ElementsTabContentView.Type:
-        return new WebInspector.ElementsTabContentView;
-    case WebInspector.NetworkTabContentView.Type:
-        return new WebInspector.NetworkTabContentView;
-    case WebInspector.NewTabContentView.Type:
-        return new WebInspector.NewTabContentView;
-    case WebInspector.ResourcesTabContentView.Type:
-        return new WebInspector.ResourcesTabContentView;
-    case WebInspector.SearchTabContentView.Type:
-        return new WebInspector.SearchTabContentView;
-    case WebInspector.StorageTabContentView.Type:
-        return new WebInspector.StorageTabContentView;
-    case WebInspector.TimelineTabContentView.Type:
-        return new WebInspector.TimelineTabContentView;
-    default:
+WebInspector.knownTabClasses = function()
+{
+    return new Set(this._knownTabClassesByType.values());
+}
+
+WebInspector._createTabContentViewForType = function(tabType)
+{
+    let tabClass = this._knownTabClassesByType.get(tabType);
+    if (!tabClass) {
         console.error("Unknown tab type", tabType);
+        return null;
     }
 
-    return null;
+    console.assert(WebInspector.TabContentView.isPrototypeOf(tabClass));
+    return new tabClass;
 };
 
 WebInspector._rememberOpenTabs = function()
 {
-    var openTabs = [];
+    let openTabs = [];
 
-    for (var tabBarItem of this.tabBar.tabBarItems) {
-        var tabContentView = tabBarItem.representedObject;
+    for (let tabBarItem of this.tabBar.tabBarItems) {
+        let tabContentView = tabBarItem.representedObject;
         if (!(tabContentView instanceof WebInspector.TabContentView))
             continue;
-        if (tabContentView instanceof WebInspector.SettingsTabContentView || tabContentView instanceof WebInspector.NewTabContentView)
+        if (!tabContentView.constructor.shouldSaveTab())
             continue;
         console.assert(tabContentView.type, "Tab type can't be null, undefined, or empty string", tabContentView.type, tabContentView);
         openTabs.push(tabContentView.type);
@@ -482,11 +484,10 @@ WebInspector._rememberOpenTabs = function()
 
 WebInspector._updateNewTabButtonState = function(event)
 {
-    var newTabAllowed = this.isNewTabWithTypeAllowed(WebInspector.ConsoleTabContentView.Type) || this.isNewTabWithTypeAllowed(WebInspector.ElementsTabContentView.Type)
-        || this.isNewTabWithTypeAllowed(WebInspector.ResourcesTabContentView.Type) || this.isNewTabWithTypeAllowed(WebInspector.StorageTabContentView.Type)
-        || this.isNewTabWithTypeAllowed(WebInspector.TimelineTabContentView.Type) || this.isNewTabWithTypeAllowed(WebInspector.DebuggerTabContentView.Type)
-        || this.isNewTabWithTypeAllowed(WebInspector.NetworkTabContentView.Type);
-    this.tabBar.newTabItem.disabled = !newTabAllowed;
+    let allTabs = [...this._knownTabClassesByType.values()];
+    let addableTabs = allTabs.filter((tabClass) => !tabClass.isEphemeral());
+    let canMakeNewTab = addableTabs.some((tabClass) => this.isNewTabWithTypeAllowed(tabClass.Type));
+    this.tabBar.newTabItem.disabled = !canMakeNewTab;
 };
 
 WebInspector._newTabItemClicked = function(event)
@@ -500,9 +501,32 @@ WebInspector._openDefaultTab = function(event)
     this.showNewTabTab();
 };
 
+WebInspector._tryToRestorePendingTabs = function()
+{
+    let stillPendingOpenTabs = [];
+    for (let {tabType, index} of this._pendingOpenTabs) {
+        if (!this.isTabTypeAllowed(tabType)) {
+            stillPendingOpenTabs.push({tabType, index});
+            continue;
+        }
+
+        let tabContentView = this._createTabContentViewForType(tabType);
+        if (!tabContentView)
+            continue;
+
+        this.tabBrowser.addTabForContentView(tabContentView, true, index);
+
+        tabContentView.restoreStateFromCookie(WebInspector.StateRestorationType.Load);
+    }
+
+    this._pendingOpenTabs = stillPendingOpenTabs;
+
+    this._updateNewTabButtonState();
+}
+
 WebInspector.showNewTabTab = function(shouldAnimate)
 {
-    var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.NewTabContentView);
+    let tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.NewTabContentView);
     if (!tabContentView)
         tabContentView = new WebInspector.NewTabContentView;
     this.tabBrowser.showTabForContentView(tabContentView, !shouldAnimate);
@@ -510,15 +534,16 @@ WebInspector.showNewTabTab = function(shouldAnimate)
 
 WebInspector.isNewTabWithTypeAllowed = function(tabType)
 {
-    if (!this.isTabTypeAllowed(tabType))
+    let tabClass = this._knownTabClassesByType.get(tabType);
+    if (!tabClass || !tabClass.isTabAllowed())
         return false;
 
     // Only allow one tab per class for now.
-    for (var tabBarItem of this.tabBar.tabBarItems) {
-        var tabContentView = tabBarItem.representedObject;
+    for (let tabBarItem of this.tabBar.tabBarItems) {
+        let tabContentView = tabBarItem.representedObject;
         if (!(tabContentView instanceof WebInspector.TabContentView))
             continue;
-        if (tabContentView.type === tabType)
+        if (tabContentView.constructor === tabClass)
             return false;
     }
 
@@ -533,7 +558,7 @@ WebInspector.createNewTabWithType = function(tabType, options = {})
     console.assert(!referencedView || referencedView instanceof WebInspector.TabContentView, referencedView);
     console.assert(!shouldReplaceTab || referencedView, "Must provide a reference view to replace a tab.");
 
-    let tabContentView = this._tabContentViewForType(tabType);
+    let tabContentView = this._createTabContentViewForType(tabType);
     const suppressAnimations = true;
     let insertionIndex = referencedView ? this.tabBar.tabBarItems.indexOf(referencedView.tabBarItem) : undefined;
     this.tabBrowser.addTabForContentView(tabContentView, suppressAnimations, insertionIndex);
@@ -545,6 +570,21 @@ WebInspector.createNewTabWithType = function(tabType, options = {})
         this.tabBrowser.showTabForContentView(tabContentView);
 };
 
+WebInspector.registerTabClass = function(tabClass)
+{
+    console.assert(WebInspector.TabContentView.isPrototypeOf(tabClass));
+    if (!WebInspector.TabContentView.isPrototypeOf(tabClass))
+        return;
+
+    if (this._knownTabClassesByType.has(tabClass.Type))
+        return;
+
+    this._knownTabClassesByType.set(tabClass.Type, tabClass);
+
+    this._tryToRestorePendingTabs();
+    this.notifications.dispatchEventToListeners(WebInspector.Notification.TabTypesChanged);
+}
+
 WebInspector.activateExtraDomains = function(domains)
 {
     this.hasExtraDomains = true;
@@ -561,26 +601,7 @@ WebInspector.activateExtraDomains = function(domains)
 
     this._updateReloadToolbarButton();
     this._updateDownloadToolbarButton();
-
-    let stillPendingOpenTabs = [];
-    for (let {tabType, index} of this._pendingOpenTabs) {
-        if (!this.isTabTypeAllowed(tabType)) {
-            stillPendingOpenTabs.push({tabType, index});
-            continue;
-        }
-
-        let tabContentView = this._tabContentViewForType(tabType);
-        if (!tabContentView)
-            continue;
-
-        this.tabBrowser.addTabForContentView(tabContentView, true, index);
-
-        tabContentView.restoreStateFromCookie(WebInspector.StateRestorationType.Load);
-    }
-
-    this._pendingOpenTabs = stillPendingOpenTabs;
-
-    this._updateNewTabButtonState();
+    this._tryToRestorePendingTabs();
 };
 
 WebInspector.contentBrowserTreeElementForRepresentedObject = function(contentBrowser, representedObject)
index 979569e..1a2b995 100644 (file)
@@ -208,4 +208,5 @@ WebInspector.Notification = {
     PageArchiveStarted: "page-archive-started",
     PageArchiveEnded: "page-archive-ended",
     ExtraDomainsActivated: "extra-domains-activated",
+    TabTypesChanged: "tab-types-changed",
 };
index c930148..d9cd029 100644 (file)
@@ -27,11 +27,20 @@ WebInspector.ConsoleTabContentView = class ConsoleTabContentView extends WebInsp
 {
     constructor(identifier)
     {
-        var tabBarItem = new WebInspector.TabBarItem("Images/Console.svg", WebInspector.UIString("Console"));
+        let {image, title} = WebInspector.ConsoleTabContentView.tabInfo();
+        let tabBarItem = new WebInspector.TabBarItem(image, title);
 
         super(identifier || "console", "console", tabBarItem, null, null, true);
     }
 
+    static tabInfo()
+    {
+        return {
+            image: "Images/Console.svg",
+            title: WebInspector.UIString("Console"),
+        };
+    }
+
     // Public
 
     get type()
index a58b962..3ca7b24 100644 (file)
@@ -27,12 +27,21 @@ WebInspector.DebuggerTabContentView = class DebuggerTabContentView extends WebIn
 {
     constructor(identifier)
     {
-        var tabBarItem = new WebInspector.TabBarItem("Images/Debugger.svg", WebInspector.UIString("Debugger"));
-        var detailsSidebarPanels = [WebInspector.resourceDetailsSidebarPanel, WebInspector.scopeChainDetailsSidebarPanel, WebInspector.probeDetailsSidebarPanel];
+        let {image, title} = WebInspector.DebuggerTabContentView.tabInfo();
+        let tabBarItem = new WebInspector.TabBarItem(image, title);
+        let detailsSidebarPanels = [WebInspector.resourceDetailsSidebarPanel, WebInspector.scopeChainDetailsSidebarPanel, WebInspector.probeDetailsSidebarPanel];
 
         super(identifier || "debugger", "debugger", tabBarItem, WebInspector.DebuggerSidebarPanel, detailsSidebarPanels);
     }
 
+    static tabInfo()
+    {
+        return {
+            image: "Images/Debugger.svg",
+            title: WebInspector.UIString("Debugger"),
+        };
+    }
+
     // Public
 
     get type()
index 1d5efa9..a3a17ba 100644 (file)
@@ -27,8 +27,9 @@ WebInspector.ElementsTabContentView = class ElementsTabContentView extends WebIn
 {
     constructor(identifier)
     {
-        var tabBarItem = new WebInspector.TabBarItem("Images/Elements.svg", WebInspector.UIString("Elements"));
-        var detailsSidebarPanels = [WebInspector.domNodeDetailsSidebarPanel, WebInspector.cssStyleDetailsSidebarPanel];
+        let {image, title} = WebInspector.ElementsTabContentView.tabInfo();
+        let tabBarItem = new WebInspector.TabBarItem(image, title);
+        let detailsSidebarPanels = [WebInspector.domNodeDetailsSidebarPanel, WebInspector.cssStyleDetailsSidebarPanel];
 
         if (WebInspector.layerTreeDetailsSidebarPanel)
             detailsSidebarPanels.push(WebInspector.layerTreeDetailsSidebarPanel);
@@ -41,6 +42,19 @@ WebInspector.ElementsTabContentView = class ElementsTabContentView extends WebIn
         this._showDOMTreeContentView();
     }
 
+    static tabInfo()
+    {
+        return {
+            image: "Images/Elements.svg",
+            title: WebInspector.UIString("Elements"),
+        };
+    }
+
+    static isTabAllowed()
+    {
+        return !!window.DOMAgent;
+    }
+
     // Public
 
     get type()
index d153c12..fc4fcd7 100644 (file)
@@ -27,12 +27,26 @@ WebInspector.NetworkTabContentView = class NetworkTabContentView extends WebInsp
 {
     constructor(identifier)
     {
-        var tabBarItem = new WebInspector.TabBarItem("Images/Network.svg", WebInspector.UIString("Network"));
-        var detailsSidebarPanels = [WebInspector.resourceDetailsSidebarPanel, WebInspector.probeDetailsSidebarPanel];
+        let {image, title} = WebInspector.NetworkTabContentView.tabInfo();
+        let tabBarItem = new WebInspector.TabBarItem(image, title);
+        let detailsSidebarPanels = [WebInspector.resourceDetailsSidebarPanel, WebInspector.probeDetailsSidebarPanel];
 
         super(identifier || "network", "network", tabBarItem, WebInspector.NetworkSidebarPanel, detailsSidebarPanels);
     }
 
+    static tabInfo()
+    {
+        return {
+            image: "Images/Network.svg",
+            title: WebInspector.UIString("Network"),
+        };
+    }
+
+    static isTabAllowed()
+    {
+        return !!window.NetworkAgent && !!window.PageAgent;
+    }
+
     // Public
 
     get type()
index 1db3019..a722920 100644 (file)
@@ -27,43 +27,34 @@ WebInspector.NewTabContentView = class NewTabContentView extends WebInspector.Ta
 {
     constructor(identifier)
     {
-        var tabBarItem = new WebInspector.TabBarItem("Images/NewTab.svg", WebInspector.UIString("New Tab"));
+        let {image, title} = WebInspector.NewTabContentView.tabInfo();
+        let tabBarItem = new WebInspector.TabBarItem(image, title);
         tabBarItem.isDefaultTab = true;
 
         super(identifier || "new-tab", "new-tab", tabBarItem);
 
-        var allowedNewTabs = [
-            {image: "Images/Console.svg", title: WebInspector.UIString("Console"), type: WebInspector.ConsoleTabContentView.Type},
-            {image: "Images/Debugger.svg", title: WebInspector.UIString("Debugger"), type: WebInspector.DebuggerTabContentView.Type},
-            {image: "Images/Elements.svg", title: WebInspector.UIString("Elements"), type: WebInspector.ElementsTabContentView.Type},
-            {image: "Images/Network.svg", title: WebInspector.UIString("Network"), type: WebInspector.NetworkTabContentView.Type},
-            {image: "Images/Resources.svg", title: WebInspector.UIString("Resources"), type: WebInspector.ResourcesTabContentView.Type},
-            {image: "Images/Storage.svg", title: WebInspector.UIString("Storage"), type: WebInspector.StorageTabContentView.Type},
-            {image: "Images/Timeline.svg", title: WebInspector.UIString("Timelines"), type: WebInspector.TimelineTabContentView.Type}
-        ];
+        WebInspector.notifications.addEventListener(WebInspector.Notification.TabTypesChanged, this._updateShownTabs.bind(this));
 
-        allowedNewTabs.sort(function(a, b) { return a.title.localeCompare(b.title); });
-
-        for (var info of allowedNewTabs) {
-            if (!WebInspector.isTabTypeAllowed(info.type))
-                continue;
-
-            var tabItemElement = document.createElement("div");
-            tabItemElement.classList.add(WebInspector.NewTabContentView.TabItemStyleClassName);
-            tabItemElement.addEventListener("click", this._createNewTabWithType.bind(this, info.type));
-            tabItemElement[WebInspector.NewTabContentView.TypeSymbol] = info.type;
-
-            var boxElement = tabItemElement.appendChild(document.createElement("div"));
-            boxElement.classList.add("box");
+        this._tabElementsByTabClass = new Map;
+        this._updateShownTabs();
+    }
 
-            var imageElement = boxElement.appendChild(document.createElement("img"));
-            imageElement.src = info.image;
+    static tabInfo()
+    {
+        return {
+            image: "Images/NewTab.svg",
+            title: WebInspector.UIString("New Tab"),
+        };
+    }
 
-            var labelElement = tabItemElement.appendChild(document.createElement("label"));
-            labelElement.textContent = info.title;
+    static isEphemeral()
+    {
+        return true;
+    }
 
-            this.element.appendChild(tabItemElement);
-        }
+    static shouldSaveTab()
+    {
+        return false;
     }
 
     // Public
@@ -93,9 +84,32 @@ WebInspector.NewTabContentView = class NewTabContentView extends WebInspector.Ta
         return false;
     }
 
-    get tabItemElements()
+    layout()
     {
-        return Array.from(this.element.querySelectorAll("." + WebInspector.NewTabContentView.TabItemStyleClassName));
+        this._tabElementsByTabClass.clear();
+        this.element.removeChildren();
+
+        for (let tabClass of this._shownTabClasses) {
+            let tabItemElement = document.createElement("div");
+            tabItemElement.classList.add("tab-item");
+            tabItemElement.addEventListener("click", this._createNewTabWithType.bind(this, tabClass.Type));
+            tabItemElement[WebInspector.NewTabContentView.TypeSymbol] = tabClass.Type;
+
+            let boxElement = tabItemElement.appendChild(document.createElement("div"));
+            boxElement.classList.add("box");
+
+            let info = tabClass.tabInfo();
+            let imageElement = boxElement.appendChild(document.createElement("img"));
+            imageElement.src = info.image;
+
+            let labelElement = tabItemElement.appendChild(document.createElement("label"));
+            labelElement.textContent = info.title;
+
+            this.element.appendChild(tabItemElement);
+            this._tabElementsByTabClass.set(tabClass, tabItemElement);
+        }
+
+        this._updateTabItems();
     }
 
     // Private
@@ -114,19 +128,29 @@ WebInspector.NewTabContentView = class NewTabContentView extends WebInspector.Ta
         WebInspector.createNewTabWithType(tabType, options);
     }
 
+    _updateShownTabs()
+    {
+        let allTabClasses = [...WebInspector.knownTabClasses()];
+        let allowedTabClasses = allTabClasses.filter((tabClass) => tabClass.isTabAllowed() && !tabClass.isEphemeral());
+        allowedTabClasses.sort((a, b) => a.tabInfo().title.localeCompare(b.tabInfo().title));
+
+        if (Object.shallowEqual(this._shownTabClasses, allowedTabClasses))
+            return;
+
+        this._shownTabClasses = allowedTabClasses;
+        this.needsLayout();
+    }
+
     _allowableTabTypes()
     {
-        let tabItemElements = this.tabItemElements;
-        let tabTypes = tabItemElements.map((tabItemElement) => tabItemElement[WebInspector.NewTabContentView.TypeSymbol]);
+        let tabTypes = this._shownTabClasses.map((tabClass) => tabClass.Type);
         return tabTypes.filter((type) => WebInspector.isNewTabWithTypeAllowed(type));
     }
 
     _updateTabItems()
     {
-        let tabItemElements = this.tabItemElements;
-        for (let tabItemElement of tabItemElements) {
-            let type = tabItemElement[WebInspector.NewTabContentView.TypeSymbol];
-            let allowed = WebInspector.isNewTabWithTypeAllowed(type);
+        for (let [tabClass, tabItemElement] of this._tabElementsByTabClass.entries()) {
+            let allowed = WebInspector.isNewTabWithTypeAllowed(tabClass.Type);
             tabItemElement.classList.toggle(WebInspector.NewTabContentView.DisabledStyleClassName, !allowed);
         }
     }
index 1dab57d..c86235f 100644 (file)
@@ -27,8 +27,9 @@ WebInspector.ResourcesTabContentView = class ResourcesTabContentView extends Web
 {
     constructor(identifier)
     {
-        var tabBarItem = new WebInspector.TabBarItem("Images/Resources.svg", WebInspector.UIString("Resources"));
-        var detailsSidebarPanels = [WebInspector.resourceDetailsSidebarPanel, WebInspector.probeDetailsSidebarPanel];
+        let {image, title} = WebInspector.ResourcesTabContentView.tabInfo();
+        let tabBarItem = new WebInspector.TabBarItem(image, title);
+        let detailsSidebarPanels = [WebInspector.resourceDetailsSidebarPanel, WebInspector.probeDetailsSidebarPanel];
 
         // FIXME: Until ContentFlows are moved to the Elements tab, these details sidebar panels need to be included.
         detailsSidebarPanels = detailsSidebarPanels.concat([WebInspector.domNodeDetailsSidebarPanel, WebInspector.cssStyleDetailsSidebarPanel]);
@@ -38,6 +39,14 @@ WebInspector.ResourcesTabContentView = class ResourcesTabContentView extends Web
         super(identifier || "resources", "resources", tabBarItem, WebInspector.ResourceSidebarPanel, detailsSidebarPanels);
     }
 
+    static tabInfo()
+    {
+        return {
+            image: "Images/Resources.svg",
+            title: WebInspector.UIString("Resources"),
+        };
+    }
+
     // Public
 
     get type()
index 4e76194..e4b43fc 100644 (file)
@@ -27,8 +27,9 @@ WebInspector.SearchTabContentView = class SearchTabContentView extends WebInspec
 {
     constructor(identifier)
     {
-        var tabBarItem = new WebInspector.TabBarItem("Images/SearchResults.svg", WebInspector.UIString("Search"));
-        var detailsSidebarPanels = [WebInspector.resourceDetailsSidebarPanel, WebInspector.probeDetailsSidebarPanel,
+        let {image, title} = WebInspector.SearchTabContentView.tabInfo();
+        let tabBarItem = new WebInspector.TabBarItem(image, title);
+        let detailsSidebarPanels = [WebInspector.resourceDetailsSidebarPanel, WebInspector.probeDetailsSidebarPanel,
             WebInspector.domNodeDetailsSidebarPanel, WebInspector.cssStyleDetailsSidebarPanel];
 
         if (WebInspector.layerTreeDetailsSidebarPanel)
@@ -37,6 +38,19 @@ WebInspector.SearchTabContentView = class SearchTabContentView extends WebInspec
         super(identifier || "search", "search", tabBarItem, WebInspector.SearchSidebarPanel, detailsSidebarPanels);
     }
 
+    static tabInfo()
+    {
+        return {
+            image: "Images/SearchResults.svg",
+            title: WebInspector.UIString("Search"),
+        };
+    }
+
+    static isEphemeral()
+    {
+        return true;
+    }
+
     // Public
 
     get type()
index 88a6e0a..0af4ada 100644 (file)
@@ -32,6 +32,17 @@ WebInspector.SettingsTabContentView = class SettingsTabContentView extends WebIn
         super(identifier || "settings", "settings", tabBarItem);
     }
 
+    static isTabAllowed()
+    {
+        // FIXME (149284): This tab isn't ready to be shown yet.
+        return false;
+    }
+
+    static shouldSaveTab()
+    {
+        return false;
+    }
+
     // Public
 
     get type()
index 31e536f..930cfef 100644 (file)
@@ -27,12 +27,26 @@ WebInspector.StorageTabContentView = class StorageTabContentView extends WebInsp
 {
     constructor(identifier)
     {
-        var tabBarItem = new WebInspector.TabBarItem("Images/Storage.svg", WebInspector.UIString("Storage"));
-        var detailsSidebarPanels = [WebInspector.applicationCacheDetailsSidebarPanel];
+        let {image, title} = WebInspector.StorageTabContentView.tabInfo();
+        let tabBarItem = new WebInspector.TabBarItem(image, title);
+        let detailsSidebarPanels = [WebInspector.applicationCacheDetailsSidebarPanel];
 
         super(identifier || "storage", "storage", tabBarItem, WebInspector.StorageSidebarPanel, detailsSidebarPanels);
     }
 
+    static tabInfo()
+    {
+        return {
+            image: "Images/Storage.svg",
+            title: WebInspector.UIString("Storage"),
+        };
+    }
+
+    static isTabAllowed()
+    {
+        return !!window.DOMStorageAgent || !!window.DatabaseAgent || !!window.IndexedDBAgent;
+    }
+
     // Public
 
     get type()
index 6e0602b..56dbb5f 100644 (file)
@@ -160,6 +160,13 @@ WebInspector.TabBrowser = class TabBrowser extends WebInspector.View
 
         this._tabBar.selectedTabBarItem = tabContentView.tabBarItem;
 
+        // FIXME: this is a workaround for <https://webkit.org/b/151876>.
+        // Without this extra call, we might never lay out the child tab
+        // if it has already marked itself as dirty in the same run loop
+        // as it is attached. It will schedule a layout, but when the rAF
+        // fires the parent will abort the layout because the counter is
+        // out of sync.
+        this.needsLayout();
         return true;
     }
 
index 91296ea..d9f768d 100644 (file)
@@ -55,6 +55,24 @@ WebInspector.TabContentView = class TabContentView extends WebInspector.ContentV
         this._cookieSetting = new WebInspector.Setting(identifier + "-tab-cookie", {});
     }
 
+    static isTabAllowed()
+    {
+        // Returns false if a necessary domain or other features are unavailable.
+        return true;
+    }
+
+    static isEphemeral()
+    {
+        // Returns true if the tab should not be shown in the new tab content view.
+        return false;
+    }
+
+    static shouldSaveTab()
+    {
+        // Returns false if the tab should not be restored when re-opening the Inspector.
+        return true;
+    }
+
     // Public
 
     get type()
index 7468d45..66a1a9d 100644 (file)
@@ -27,12 +27,26 @@ WebInspector.TimelineTabContentView = class TimelineTabContentView extends WebIn
 {
     constructor(identifier)
     {
-        var tabBarItem = new WebInspector.TabBarItem("Images/Timeline.svg", WebInspector.UIString("Timelines"));
-        var detailsSidebarPanels = [WebInspector.resourceDetailsSidebarPanel, WebInspector.probeDetailsSidebarPanel];
+        let {image, title} = WebInspector.TimelineTabContentView.tabInfo();
+        let tabBarItem = new WebInspector.TabBarItem(image, title);
+        let detailsSidebarPanels = [WebInspector.resourceDetailsSidebarPanel, WebInspector.probeDetailsSidebarPanel];
 
         super(identifier || "timeline", "timeline", tabBarItem, WebInspector.TimelineSidebarPanel, detailsSidebarPanels);
     }
 
+    static tabInfo()
+    {
+        return {
+            image: "Images/Timeline.svg",
+            title: WebInspector.UIString("Timelines"),
+        };
+    }
+
+    static isTabAllowed()
+    {
+        return !!window.TimelineAgent;
+    }
+
     // Public
 
     get type()