Web Inspector: TabBar redesign: remove New Tab button and add experimental feature...
authormattbaker@apple.com <mattbaker@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Feb 2018 21:04:52 +0000 (21:04 +0000)
committermattbaker@apple.com <mattbaker@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Feb 2018 21:04:52 +0000 (21:04 +0000)
https://bugs.webkit.org/show_bug.cgi?id=182342
<rdar://problem/37078662>

Reviewed by Devin Rousso.

This patch adds a new experimental setting group, "User Interface", with
a single setting, "Enable New TabBar". When enabled, the New Tab button is
no longer available in the top-level TabBar. The 'open tabs' context menu
no longer allows the last non-ephemeral open tab to be closed (unchecked).

* Localizations/en.lproj/localizedStrings.js:

* UserInterface/Base/Main.js:
(WI.contentLoaded):
(WI._tryToRestorePendingTabs):
Retain legacy behavior behind experimental feature setting.
(WI.isNewTabWithTypeAllowed):

* UserInterface/Base/Setting.js:
* UserInterface/Main.html:

* UserInterface/Views/CanvasTabContentView.js:
(WI.CanvasTabContentView):
* UserInterface/Views/ConsoleTabContentView.js:
(WI.ConsoleTabContentView):
* UserInterface/Views/DebuggerTabContentView.js:
(WI.DebuggerTabContentView):
* UserInterface/Views/ElementsTabContentView.js:
(WI.ElementsTabContentView):

* UserInterface/Views/GeneralTabBarItem.js:
(WI.GeneralTabBarItem):
(WI.GeneralTabBarItem.prototype.fromTabInfo):
(WI.GeneralTabBarItem.prototype.get isEphemeral):
(WI.GeneralTabBarItem.fromTabContentViewConstructor): Deleted.

* UserInterface/Views/LayersTabContentView.js:
(WI.LayersTabContentView):

* UserInterface/Views/LegacyTabBar.js: Copied from Source/WebInspectorUI/UserInterface/Views/TabBar.js.
(WI.LegacyTabBar):
(WI.LegacyTabBar.prototype.get newTabTabBarItem):
(WI.LegacyTabBar.prototype.updateNewTabTabBarItemState):
(WI.LegacyTabBar.prototype.addTabBarItem):
(WI.LegacyTabBar.prototype.insertTabBarItem.animateTabs):
(WI.LegacyTabBar.prototype.insertTabBarItem.removeStyles):
(WI.LegacyTabBar.prototype.insertTabBarItem):
(WI.LegacyTabBar.prototype.removeTabBarItem.animateTabs):
(WI.LegacyTabBar.prototype.removeTabBarItem.removeStyles):
(WI.LegacyTabBar.prototype.removeTabBarItem):
(WI.LegacyTabBar.prototype.selectPreviousTab):
(WI.LegacyTabBar.prototype.selectNextTab):
(WI.LegacyTabBar.prototype.get selectedTabBarItem):
(WI.LegacyTabBar.prototype.set selectedTabBarItem):
(WI.LegacyTabBar.prototype.get tabBarItems):
(WI.LegacyTabBar.prototype.get normalTabCount):
(WI.LegacyTabBar.prototype.layout.forceItemHidden):
(WI.LegacyTabBar.prototype.layout):
(WI.LegacyTabBar.prototype._tabBarItemsFromLeftToRight):
(WI.LegacyTabBar.prototype._findTabBarItem):
(WI.LegacyTabBar.prototype._hasMoreThanOneNormalTab):
(WI.LegacyTabBar.prototype._openDefaultTab):
(WI.LegacyTabBar.prototype._recordTabBarItemSizesAndPositions):
(WI.LegacyTabBar.prototype._applyTabBarItemSizesAndPositions):
(WI.LegacyTabBar.prototype._clearTabBarItemSizesAndPositions):
(WI.LegacyTabBar.prototype._finishExpandingTabsAfterClose.):
(WI.LegacyTabBar.prototype._finishExpandingTabsAfterClose):
(WI.LegacyTabBar.prototype._handleMouseDown):
(WI.LegacyTabBar.prototype._handleClick):
(WI.LegacyTabBar.prototype._handleMouseMoved):
(WI.LegacyTabBar.prototype._handleMouseUp):
(WI.LegacyTabBar.prototype._handleMouseLeave):
(WI.LegacyTabBar.prototype._handleContextMenu):
(WI.LegacyTabBar.prototype._handleNewTabClick):
(WI.LegacyTabBar.prototype._handleTabPickerTabContextMenu):
(WI.LegacyTabBar.prototype._handleNewTabMouseEnter):

* UserInterface/Views/NetworkTabContentView.js:
(WI.NetworkTabContentView):
* UserInterface/Views/NewTabContentView.js:
(WI.NewTabContentView):
(WI.NewTabContentView.tabInfo):
(WI.NewTabContentView.isEphemeral): Deleted.
* UserInterface/Views/ResourcesTabContentView.js:
(WI.ResourcesTabContentView):
* UserInterface/Views/SearchTabContentView.js:
(WI.SearchTabContentView):
(WI.SearchTabContentView.tabInfo):
(WI.SearchTabContentView.isEphemeral): Deleted.

* UserInterface/Views/SettingsTabContentView.js:
(WI.SettingsTabContentView.tabInfo):
(WI.SettingsTabContentView.prototype._createExperimentalSettingsView):
(WI.SettingsTabContentView.isEphemeral): Deleted.

* UserInterface/Views/StorageTabContentView.js:
(WI.StorageTabContentView):

* UserInterface/Views/TabBar.css:
* UserInterface/Views/TabBar.js:
(WI.TabBar):
(WI.TabBar.prototype.insertTabBarItem):
(WI.TabBar.prototype.removeTabBarItem):
(WI.TabBar.prototype.set selectedTabBarItem):
(WI.TabBar.prototype.get normalNonEphemeralTabCount):
(WI.TabBar.prototype._handleMouseDown):
(WI.TabBar.prototype._handleClick):
(WI.TabBar.prototype._handleMouseMoved):
(WI.TabBar.prototype._handleMouseLeave):
(WI.TabBar.prototype._handleContextMenu):
(WI.TabBar.prototype._handleTabPickerTabContextMenu):
(WI.TabBar.prototype.get newTabTabBarItem): Deleted.
(WI.TabBar.prototype.updateNewTabTabBarItemState): Deleted.
(WI.TabBar.prototype._openDefaultTab): Deleted.
(WI.TabBar.prototype._handleNewTabClick): Deleted.
(WI.TabBar.prototype._handleNewTabMouseEnter): Deleted.
Remove support for the New Tab button and default tab. Without a default
tab, there is nothing to display when no tabs are open, so prevent the
last non-pinned tab from being removed.

* UserInterface/Views/TabBrowser.js:
(WI.TabBrowser._tabBarItemRemoved):
* UserInterface/Views/TabContentView.js:
(WI.TabContentView.isEphemeral): Deleted.
* UserInterface/Views/TimelineTabContentView.js:
(WI.TimelineTabContentView):

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

23 files changed:
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Base/Main.js
Source/WebInspectorUI/UserInterface/Base/Setting.js
Source/WebInspectorUI/UserInterface/Main.html
Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.js
Source/WebInspectorUI/UserInterface/Views/ConsoleTabContentView.js
Source/WebInspectorUI/UserInterface/Views/DebuggerTabContentView.js
Source/WebInspectorUI/UserInterface/Views/ElementsTabContentView.js
Source/WebInspectorUI/UserInterface/Views/GeneralTabBarItem.js
Source/WebInspectorUI/UserInterface/Views/LayersTabContentView.js
Source/WebInspectorUI/UserInterface/Views/LegacyTabBar.js [new file with mode: 0644]
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/TabBar.css
Source/WebInspectorUI/UserInterface/Views/TabBar.js
Source/WebInspectorUI/UserInterface/Views/TabBrowser.js
Source/WebInspectorUI/UserInterface/Views/TabContentView.js
Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js

index db3fefd..bb6c332 100644 (file)
@@ -1,3 +1,133 @@
+2018-02-02  Matt Baker  <mattbaker@apple.com>
+
+        Web Inspector: TabBar redesign: remove New Tab button and add experimental feature flag
+        https://bugs.webkit.org/show_bug.cgi?id=182342
+        <rdar://problem/37078662>
+
+        Reviewed by Devin Rousso.
+
+        This patch adds a new experimental setting group, "User Interface", with
+        a single setting, "Enable New TabBar". When enabled, the New Tab button is
+        no longer available in the top-level TabBar. The 'open tabs' context menu
+        no longer allows the last non-ephemeral open tab to be closed (unchecked).
+
+        * Localizations/en.lproj/localizedStrings.js:
+
+        * UserInterface/Base/Main.js:
+        (WI.contentLoaded):
+        (WI._tryToRestorePendingTabs):
+        Retain legacy behavior behind experimental feature setting.
+        (WI.isNewTabWithTypeAllowed):
+
+        * UserInterface/Base/Setting.js:
+        * UserInterface/Main.html:
+
+        * UserInterface/Views/CanvasTabContentView.js:
+        (WI.CanvasTabContentView):
+        * UserInterface/Views/ConsoleTabContentView.js:
+        (WI.ConsoleTabContentView):
+        * UserInterface/Views/DebuggerTabContentView.js:
+        (WI.DebuggerTabContentView):
+        * UserInterface/Views/ElementsTabContentView.js:
+        (WI.ElementsTabContentView):
+
+        * UserInterface/Views/GeneralTabBarItem.js:
+        (WI.GeneralTabBarItem):
+        (WI.GeneralTabBarItem.prototype.fromTabInfo):
+        (WI.GeneralTabBarItem.prototype.get isEphemeral):
+        (WI.GeneralTabBarItem.fromTabContentViewConstructor): Deleted.
+
+        * UserInterface/Views/LayersTabContentView.js:
+        (WI.LayersTabContentView):
+
+        * UserInterface/Views/LegacyTabBar.js: Copied from Source/WebInspectorUI/UserInterface/Views/TabBar.js.
+        (WI.LegacyTabBar):
+        (WI.LegacyTabBar.prototype.get newTabTabBarItem):
+        (WI.LegacyTabBar.prototype.updateNewTabTabBarItemState):
+        (WI.LegacyTabBar.prototype.addTabBarItem):
+        (WI.LegacyTabBar.prototype.insertTabBarItem.animateTabs):
+        (WI.LegacyTabBar.prototype.insertTabBarItem.removeStyles):
+        (WI.LegacyTabBar.prototype.insertTabBarItem):
+        (WI.LegacyTabBar.prototype.removeTabBarItem.animateTabs):
+        (WI.LegacyTabBar.prototype.removeTabBarItem.removeStyles):
+        (WI.LegacyTabBar.prototype.removeTabBarItem):
+        (WI.LegacyTabBar.prototype.selectPreviousTab):
+        (WI.LegacyTabBar.prototype.selectNextTab):
+        (WI.LegacyTabBar.prototype.get selectedTabBarItem):
+        (WI.LegacyTabBar.prototype.set selectedTabBarItem):
+        (WI.LegacyTabBar.prototype.get tabBarItems):
+        (WI.LegacyTabBar.prototype.get normalTabCount):
+        (WI.LegacyTabBar.prototype.layout.forceItemHidden):
+        (WI.LegacyTabBar.prototype.layout):
+        (WI.LegacyTabBar.prototype._tabBarItemsFromLeftToRight):
+        (WI.LegacyTabBar.prototype._findTabBarItem):
+        (WI.LegacyTabBar.prototype._hasMoreThanOneNormalTab):
+        (WI.LegacyTabBar.prototype._openDefaultTab):
+        (WI.LegacyTabBar.prototype._recordTabBarItemSizesAndPositions):
+        (WI.LegacyTabBar.prototype._applyTabBarItemSizesAndPositions):
+        (WI.LegacyTabBar.prototype._clearTabBarItemSizesAndPositions):
+        (WI.LegacyTabBar.prototype._finishExpandingTabsAfterClose.):
+        (WI.LegacyTabBar.prototype._finishExpandingTabsAfterClose):
+        (WI.LegacyTabBar.prototype._handleMouseDown):
+        (WI.LegacyTabBar.prototype._handleClick):
+        (WI.LegacyTabBar.prototype._handleMouseMoved):
+        (WI.LegacyTabBar.prototype._handleMouseUp):
+        (WI.LegacyTabBar.prototype._handleMouseLeave):
+        (WI.LegacyTabBar.prototype._handleContextMenu):
+        (WI.LegacyTabBar.prototype._handleNewTabClick):
+        (WI.LegacyTabBar.prototype._handleTabPickerTabContextMenu):
+        (WI.LegacyTabBar.prototype._handleNewTabMouseEnter):
+
+        * UserInterface/Views/NetworkTabContentView.js:
+        (WI.NetworkTabContentView):
+        * UserInterface/Views/NewTabContentView.js:
+        (WI.NewTabContentView):
+        (WI.NewTabContentView.tabInfo):
+        (WI.NewTabContentView.isEphemeral): Deleted.
+        * UserInterface/Views/ResourcesTabContentView.js:
+        (WI.ResourcesTabContentView):
+        * UserInterface/Views/SearchTabContentView.js:
+        (WI.SearchTabContentView):
+        (WI.SearchTabContentView.tabInfo):
+        (WI.SearchTabContentView.isEphemeral): Deleted.
+
+        * UserInterface/Views/SettingsTabContentView.js:
+        (WI.SettingsTabContentView.tabInfo):
+        (WI.SettingsTabContentView.prototype._createExperimentalSettingsView):
+        (WI.SettingsTabContentView.isEphemeral): Deleted.
+
+        * UserInterface/Views/StorageTabContentView.js:
+        (WI.StorageTabContentView):
+
+        * UserInterface/Views/TabBar.css:
+        * UserInterface/Views/TabBar.js:
+        (WI.TabBar):
+        (WI.TabBar.prototype.insertTabBarItem):
+        (WI.TabBar.prototype.removeTabBarItem):
+        (WI.TabBar.prototype.set selectedTabBarItem):
+        (WI.TabBar.prototype.get normalNonEphemeralTabCount):
+        (WI.TabBar.prototype._handleMouseDown):
+        (WI.TabBar.prototype._handleClick):
+        (WI.TabBar.prototype._handleMouseMoved):
+        (WI.TabBar.prototype._handleMouseLeave):
+        (WI.TabBar.prototype._handleContextMenu):
+        (WI.TabBar.prototype._handleTabPickerTabContextMenu):
+        (WI.TabBar.prototype.get newTabTabBarItem): Deleted.
+        (WI.TabBar.prototype.updateNewTabTabBarItemState): Deleted.
+        (WI.TabBar.prototype._openDefaultTab): Deleted.
+        (WI.TabBar.prototype._handleNewTabClick): Deleted.
+        (WI.TabBar.prototype._handleNewTabMouseEnter): Deleted.
+        Remove support for the New Tab button and default tab. Without a default
+        tab, there is nothing to display when no tabs are open, so prevent the
+        last non-pinned tab from being removed.
+
+        * UserInterface/Views/TabBrowser.js:
+        (WI.TabBrowser._tabBarItemRemoved):
+        * UserInterface/Views/TabContentView.js:
+        (WI.TabContentView.isEphemeral): Deleted.
+        * UserInterface/Views/TimelineTabContentView.js:
+        (WI.TimelineTabContentView):
+
 2018-01-30  Devin Rousso  <webkit@devinrousso.com>
 
         Web Inspector: Replace Object.shallowMerge with ES2018 spread operator
index 6ef9001..0104bad 100644 (file)
@@ -367,6 +367,7 @@ localizedStrings["Enable Breakpoint"] = "Enable Breakpoint";
 localizedStrings["Enable Breakpoints"] = "Enable Breakpoints";
 localizedStrings["Enable Event Listener"] = "Enable Event Listener";
 localizedStrings["Enable Layers Tab"] = "Enable Layers Tab";
+localizedStrings["Enable New Tab Bar"] = "Enable New Tab Bar";
 localizedStrings["Enable Program"] = "Enable Program";
 localizedStrings["Enable all breakpoints (%s)"] = "Enable all breakpoints (%s)";
 localizedStrings["Enable breakpoints"] = "Enable breakpoints";
@@ -1008,6 +1009,7 @@ localizedStrings["Use Default Media Styles"] = "Use Default Media Styles";
 localizedStrings["Use the resource cache when loading resources"] = "Use the resource cache when loading resources";
 localizedStrings["User Agent"] = "User Agent";
 localizedStrings["User Agent Stylesheet"] = "User Agent Stylesheet";
+localizedStrings["User Interface:"] = "User Interface:";
 localizedStrings["User Stylesheet"] = "User Stylesheet";
 localizedStrings["Using Keyword Value"] = "Using Keyword Value";
 localizedStrings["Using previous selector ā€œ%sā€"] = "Using previous selector ā€œ%sā€";
index 28371f6..6b86014 100644 (file)
@@ -270,8 +270,12 @@ WI.contentLoaded = function()
     // Create the user interface elements.
     this.toolbar = new WI.Toolbar(document.getElementById("toolbar"));
 
-    this.tabBar = new WI.TabBar(document.getElementById("tab-bar"));
-    this.tabBar.addEventListener(WI.TabBar.Event.OpenDefaultTab, this._openDefaultTab, this);
+    if (WI.settings.experimentalEnableNewTabBar.value)
+        this.tabBar = new WI.TabBar(document.getElementById("tab-bar"));
+    else {
+        this.tabBar = new WI.LegacyTabBar(document.getElementById("tab-bar"));
+        this.tabBar.addEventListener(WI.TabBar.Event.OpenDefaultTab, this._openDefaultTab, this);
+    }
 
     this._contentElement = document.getElementById("content");
     this._contentElement.setAttribute("role", "main");
@@ -601,7 +605,8 @@ WI._tryToRestorePendingTabs = function()
 
     this._pendingOpenTabs = stillPendingOpenTabs;
 
-    this.tabBrowser.tabBar.updateNewTabTabBarItemState();
+    if (!WI.settings.experimentalEnableNewTabBar.value)
+        this.tabBrowser.tabBar.updateNewTabTabBarItemState();
 };
 
 WI.showNewTabTab = function(options)
@@ -632,7 +637,7 @@ WI.isNewTabWithTypeAllowed = function(tabType)
 
     if (tabClass === WI.NewTabContentView) {
         let allTabs = Array.from(this.knownTabClasses());
-        let addableTabs = allTabs.filter((tabClass) => !tabClass.isEphemeral());
+        let addableTabs = allTabs.filter((tabClass) => !tabClass.tabInfo().isEphemeral);
         let canMakeNewTab = addableTabs.some((tabClass) => WI.isNewTabWithTypeAllowed(tabClass.Type));
         return canMakeNewTab;
     }
index f25b3d7..9015fad 100644 (file)
@@ -126,4 +126,5 @@ WI.settings = {
     experimentalEnableLayersTab: new WI.Setting("experimental-enable-layers-tab", false),
     experimentalLegacyStyleEditor: new WI.Setting("experimental-legacy-style-editor", false),
     experimentalLegacyVisualSidebar: new WI.Setting("experimental-legacy-visual-sidebar", false),
+    experimentalEnableNewTabBar: new WI.Setting("experimental-enable-new-tab-bar", false),
 };
index 33bec4e..e15acf2 100644 (file)
     <script src="Views/TreeOutline.js"></script>
     <script src="Views/TreeOutlineGroup.js"></script>
 
+    <script src="Views/LegacyTabBar.js"></script>
+
     <script src="Views/ButtonNavigationItem.js"></script>
     <script src="Views/DatabaseUserQueryViewBase.js"></script>
     <script src="Views/DatabaseUserQueryErrorView.js"></script>
index c4dea07..8364b95 100644 (file)
@@ -29,7 +29,7 @@ WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
     {
         console.assert(!representedObject || representedObject instanceof WI.Canvas);
 
-        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.CanvasTabContentView);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.CanvasTabContentView.tabInfo());
 
         const navigationSidebarPanelConstructor = WI.RecordingNavigationSidebarPanel;
         const detailsSidebarPanelConstructors = [WI.RecordingStateDetailsSidebarPanel, WI.RecordingTraceDetailsSidebarPanel, WI.CanvasDetailsSidebarPanel];
index 84d3e38..125986a 100644 (file)
@@ -27,7 +27,7 @@ WI.ConsoleTabContentView = class ConsoleTabContentView extends WI.ContentBrowser
 {
     constructor(identifier)
     {
-        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.ConsoleTabContentView);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.ConsoleTabContentView.tabInfo());
 
         super(identifier || "console", "console", tabBarItem, null, null, true);
     }
index ede7d2e..97e6436 100644 (file)
@@ -27,7 +27,7 @@ WI.DebuggerTabContentView = class DebuggerTabContentView extends WI.ContentBrows
 {
     constructor(identifier)
     {
-        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.DebuggerTabContentView);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.DebuggerTabContentView.tabInfo());
         let detailsSidebarPanelConstructors = [WI.ScopeChainDetailsSidebarPanel, WI.ResourceDetailsSidebarPanel, WI.ProbeDetailsSidebarPanel];
 
         super(identifier || "debugger", "debugger", tabBarItem, WI.DebuggerSidebarPanel, detailsSidebarPanelConstructors);
index e04de37..1e2d6c1 100644 (file)
@@ -27,7 +27,7 @@ WI.ElementsTabContentView = class ElementsTabContentView extends WI.ContentBrows
 {
     constructor(identifier)
     {
-        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.ElementsTabContentView);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.ElementsTabContentView.tabInfo());
         let detailsSidebarPanelConstructors = [WI.RulesStyleDetailsSidebarPanel, WI.ComputedStyleDetailsSidebarPanel, WI.DOMNodeDetailsSidebarPanel];
 
         if (window.LayerTreeAgent && !WI.settings.experimentalEnableLayersTab.value)
index fd05dbd..8b33fea 100644 (file)
 
 WI.GeneralTabBarItem = class GeneralTabBarItem extends WI.TabBarItem
 {
-    constructor(image, title, isEphemeral)
+    constructor(image, title, isEphemeral = false)
     {
         super(image, title);
 
-        if (isEphemeral) {
+        this._isEphemeral = isEphemeral;
+
+        if (this._isEphemeral) {
             this.element.classList.add("ephemeral");
 
             let closeButtonElement = document.createElement("div");
@@ -42,15 +44,15 @@ WI.GeneralTabBarItem = class GeneralTabBarItem extends WI.TabBarItem
         }
     }
 
-    static fromTabContentViewConstructor(constructor)
+    static fromTabInfo({image, title, isEphemeral})
     {
-        let {image, title} = constructor.tabInfo();
-        let isEphemeral = constructor.isEphemeral();
         return new WI.GeneralTabBarItem(image, title, isEphemeral);
     }
 
     // Public
 
+    get isEphemeral() { return this._isEphemeral; }
+
     get title()
     {
         return super.title;
index 6d26d4c..285b1c4 100644 (file)
@@ -27,7 +27,7 @@ WI.LayersTabContentView = class LayersTabContentView extends WI.ContentBrowserTa
 {
     constructor()
     {
-        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.LayersTabContentView);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.LayersTabContentView.tabInfo());
 
         const navigationSidebarPanelConstructor = null;
         const detailsSidebarPanelConstructors = [WI.LayerDetailsSidebarPanel];
diff --git a/Source/WebInspectorUI/UserInterface/Views/LegacyTabBar.js b/Source/WebInspectorUI/UserInterface/Views/LegacyTabBar.js
new file mode 100644 (file)
index 0000000..59ce8ac
--- /dev/null
@@ -0,0 +1,856 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WI.LegacyTabBar = class LegacyTabBar extends WI.View
+{
+    constructor(element, tabBarItems)
+    {
+        super(element);
+
+        this.element.classList.add("tab-bar");
+        this.element.setAttribute("role", "tablist");
+        this.element.addEventListener("mousedown", this._handleMouseDown.bind(this));
+        this.element.addEventListener("click", this._handleClick.bind(this));
+        this.element.addEventListener("mouseleave", this._handleMouseLeave.bind(this));
+        this.element.addEventListener("contextmenu", this._handleContextMenu.bind(this));
+
+        this.element.createChild("div", "top-border");
+
+        this._tabBarItems = [];
+        this._hiddenTabBarItems = [];
+
+        if (tabBarItems) {
+            for (let tabBarItem in tabBarItems)
+                this.addTabBarItem(tabBarItem);
+        }
+
+        this.addTabBarItem(WI.settingsTabContentView.tabBarItem, {suppressAnimations: true});
+
+        this._newTabTabBarItem = new WI.PinnedTabBarItem("Images/NewTabPlus.svg", WI.UIString("Create a new tab"));
+        this._newTabTabBarItem.element.addEventListener("mouseenter", this._handleNewTabMouseEnter.bind(this));
+        this._newTabTabBarItem.element.addEventListener("click", this._handleNewTabClick.bind(this));
+        this.addTabBarItem(this._newTabTabBarItem, {suppressAnimations: true});
+
+        this._tabPickerTabBarItem = new WI.PinnedTabBarItem("Images/TabPicker.svg", WI.UIString("Show hidden tabs"));
+        this._tabPickerTabBarItem.element.classList.add("tab-picker");
+        this._tabPickerTabBarItem.element.addEventListener("contextmenu", this._handleTabPickerTabContextMenu.bind(this));
+        this.addTabBarItem(this._tabPickerTabBarItem, {suppressAnimations: true});
+    }
+
+    // Public
+
+    get newTabTabBarItem() { return this._newTabTabBarItem; }
+
+    updateNewTabTabBarItemState()
+    {
+        let newTabExists = !WI.isNewTabWithTypeAllowed(WI.NewTabContentView.Type);
+        this._newTabTabBarItem.disabled = newTabExists;
+    }
+
+    addTabBarItem(tabBarItem, options = {})
+    {
+        return this.insertTabBarItem(tabBarItem, this._tabBarItems.length, options);
+    }
+
+    insertTabBarItem(tabBarItem, index, options = {})
+    {
+        console.assert(tabBarItem instanceof WI.TabBarItem);
+        if (!(tabBarItem instanceof WI.TabBarItem))
+            return null;
+
+        if (tabBarItem.parentTabBar === this)
+            return null;
+
+        if (this._tabAnimatedClosedSinceMouseEnter) {
+            // Delay adding the new tab until we can expand the tabs after a closed tab.
+            this._finishExpandingTabsAfterClose().then(() => {
+                this.insertTabBarItem(tabBarItem, index, options);
+            });
+            return null;
+        }
+
+        if (tabBarItem.parentTabBar)
+            tabBarItem.parentTabBar.removeTabBarItem(tabBarItem);
+
+        tabBarItem.parentTabBar = this;
+
+        index = Number.constrain(index, 0, this.normalTabCount);
+
+        if (this.element.classList.contains("animating")) {
+            requestAnimationFrame(removeStyles.bind(this));
+            options.suppressAnimations = true;
+        }
+
+        var beforeTabSizesAndPositions;
+        if (!options.suppressAnimations)
+            beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+        this._tabBarItems.splice(index, 0, tabBarItem);
+
+        var nextSibling = this._tabBarItems[index + 1];
+        let nextSiblingElement = nextSibling ? nextSibling.element : this._tabBarItems.lastValue.element;
+
+        if (this.element.isAncestor(nextSiblingElement))
+            this.element.insertBefore(tabBarItem.element, nextSiblingElement);
+        else
+            this.element.appendChild(tabBarItem.element);
+
+        this.element.classList.toggle("single-tab", !this._hasMoreThanOneNormalTab());
+
+        tabBarItem.element.style.left = null;
+        tabBarItem.element.style.width = null;
+
+        function animateTabs()
+        {
+            this.element.classList.add("animating");
+            this.element.classList.add("inserting-tab");
+
+            this._applyTabBarItemSizesAndPositions(afterTabSizesAndPositions);
+
+            this.element.addEventListener("webkitTransitionEnd", removeStylesListener);
+        }
+
+        function removeStyles()
+        {
+            this.element.classList.remove("static-layout");
+            this.element.classList.remove("animating");
+            this.element.classList.remove("inserting-tab");
+
+            tabBarItem.element.classList.remove("being-inserted");
+
+            this._clearTabBarItemSizesAndPositions();
+
+            this.element.removeEventListener("webkitTransitionEnd", removeStylesListener);
+        }
+
+        if (!options.suppressAnimations) {
+            var afterTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+            this.updateLayout();
+
+            let tabBarItems = this._tabBarItemsFromLeftToRight();
+            let previousTabBarItem = tabBarItems[tabBarItems.indexOf(tabBarItem) - 1] || null;
+            let previousTabBarItemSizeAndPosition = previousTabBarItem ? beforeTabSizesAndPositions.get(previousTabBarItem) : null;
+
+            if (previousTabBarItemSizeAndPosition)
+                beforeTabSizesAndPositions.set(tabBarItem, {left: previousTabBarItemSizeAndPosition.left + previousTabBarItemSizeAndPosition.width, width: 0});
+            else
+                beforeTabSizesAndPositions.set(tabBarItem, {left: 0, width: 0});
+
+            this.element.classList.add("static-layout");
+            tabBarItem.element.classList.add("being-inserted");
+
+            this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions);
+
+            var removeStylesListener = removeStyles.bind(this);
+
+            requestAnimationFrame(animateTabs.bind(this));
+        } else
+            this.needsLayout();
+
+        if (!(tabBarItem instanceof WI.PinnedTabBarItem))
+            this.updateNewTabTabBarItemState();
+
+        this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemAdded, {tabBarItem});
+
+        return tabBarItem;
+    }
+
+    removeTabBarItem(tabBarItemOrIndex, options = {})
+    {
+        let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
+        if (!tabBarItem || tabBarItem instanceof WI.PinnedTabBarItem)
+            return null;
+
+        tabBarItem.parentTabBar = null;
+
+        if (this._selectedTabBarItem === tabBarItem) {
+            var index = this._tabBarItems.indexOf(tabBarItem);
+            var nextTabBarItem = this._tabBarItems[index + 1];
+            if (!nextTabBarItem || nextTabBarItem instanceof WI.PinnedTabBarItem)
+                nextTabBarItem = this._tabBarItems[index - 1];
+
+            this.selectedTabBarItem = nextTabBarItem;
+        }
+
+        if (this.element.classList.contains("animating")) {
+            requestAnimationFrame(removeStyles.bind(this));
+            options.suppressAnimations = true;
+        }
+
+        var beforeTabSizesAndPositions;
+        if (!options.suppressAnimations)
+            beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+        // Subtract 1 from normalTabCount since arrays begin indexing at 0.
+        let wasLastNormalTab = this._tabBarItems.indexOf(tabBarItem) === this.normalTabCount - 1;
+
+        this._tabBarItems.remove(tabBarItem);
+        tabBarItem.element.remove();
+
+        var hasMoreThanOneNormalTab = this._hasMoreThanOneNormalTab();
+        this.element.classList.toggle("single-tab", !hasMoreThanOneNormalTab);
+
+        const shouldOpenDefaultTab = !tabBarItem.isDefaultTab && !this.normalTabCount;
+        if (shouldOpenDefaultTab)
+            options.suppressAnimations = true;
+
+        if (!hasMoreThanOneNormalTab || wasLastNormalTab || !options.suppressExpansion) {
+            if (!options.suppressAnimations) {
+                this._tabAnimatedClosedSinceMouseEnter = true;
+                this._finishExpandingTabsAfterClose(beforeTabSizesAndPositions);
+            } else
+                this.needsLayout();
+
+            this.updateNewTabTabBarItemState();
+
+            this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemRemoved, {tabBarItem});
+
+            if (shouldOpenDefaultTab)
+                this._openDefaultTab();
+
+            return tabBarItem;
+        }
+
+        var lastNormalTabBarItem;
+
+        function animateTabs()
+        {
+            this.element.classList.add("animating");
+            this.element.classList.add("closing-tab");
+
+            // For RTL, we need to place extra space between pinned tab and first normal tab.
+            // From left to right there is pinned tabs, extra space, then normal tabs. Compute
+            // how much extra space we need to additionally add for normal tab items.
+            let extraSpaceBetweenNormalAndPinnedTabs = 0;
+            if (WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL) {
+                extraSpaceBetweenNormalAndPinnedTabs = this.element.getBoundingClientRect().width;
+                for (let currentTabBarItem of this._tabBarItemsFromLeftToRight())
+                    extraSpaceBetweenNormalAndPinnedTabs -= currentTabBarItem.element.getBoundingClientRect().width;
+            }
+
+            let left = 0;
+            for (let currentTabBarItem of this._tabBarItemsFromLeftToRight()) {
+                let sizeAndPosition = beforeTabSizesAndPositions.get(currentTabBarItem);
+
+                if (!(currentTabBarItem instanceof WI.PinnedTabBarItem)) {
+                    currentTabBarItem.element.style.left = extraSpaceBetweenNormalAndPinnedTabs + left + "px";
+                    left += sizeAndPosition.width;
+                    lastNormalTabBarItem = currentTabBarItem;
+                } else
+                    left = sizeAndPosition.left + sizeAndPosition.width;
+            }
+
+            // The selected tab and last tab need to draw a right border as well, so make them 1px wider.
+            if (this._selectedTabBarItem)
+                this._selectedTabBarItem.element.style.width = (parseFloat(this._selectedTabBarItem.element.style.width) + 1) + "px";
+
+            if (lastNormalTabBarItem !== this._selectedTabBarItem)
+                lastNormalTabBarItem.element.style.width = (parseFloat(lastNormalTabBarItem.element.style.width) + 1) + "px";
+
+            this.element.addEventListener("webkitTransitionEnd", removeStylesListener);
+        }
+
+        function removeStyles()
+        {
+            // The selected tab needs to stop drawing the right border, so make it 1px smaller. Only if it isn't the last.
+            if (this._selectedTabBarItem && this._selectedTabBarItem !== lastNormalTabBarItem)
+                this._selectedTabBarItem.element.style.width = (parseFloat(this._selectedTabBarItem.element.style.width) - 1) + "px";
+
+            this.element.classList.remove("animating");
+            this.element.classList.remove("closing-tab");
+
+            this.updateLayout();
+
+            this.element.removeEventListener("webkitTransitionEnd", removeStylesListener);
+        }
+
+        if (!options.suppressAnimations) {
+            this.element.classList.add("static-layout");
+
+            this._tabAnimatedClosedSinceMouseEnter = true;
+
+            this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions);
+
+            var removeStylesListener = removeStyles.bind(this);
+
+            requestAnimationFrame(animateTabs.bind(this));
+        } else
+            this.needsLayout();
+
+        this.updateNewTabTabBarItemState();
+
+        this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemRemoved, {tabBarItem});
+
+        if (shouldOpenDefaultTab)
+            this._openDefaultTab();
+
+        return tabBarItem;
+    }
+
+    selectPreviousTab()
+    {
+        if (this._tabBarItems.length <= 1)
+            return;
+
+        var startIndex = this._tabBarItems.indexOf(this._selectedTabBarItem);
+        var newIndex = startIndex;
+        do {
+            if (newIndex === 0)
+                newIndex = this._tabBarItems.length - 1;
+            else
+                newIndex--;
+
+            if (!(this._tabBarItems[newIndex] instanceof WI.PinnedTabBarItem))
+                break;
+        } while (newIndex !== startIndex);
+
+        if (newIndex === startIndex)
+            return;
+
+        this.selectedTabBarItem = this._tabBarItems[newIndex];
+    }
+
+    selectNextTab()
+    {
+        if (this._tabBarItems.length <= 1)
+            return;
+
+        var startIndex = this._tabBarItems.indexOf(this._selectedTabBarItem);
+        var newIndex = startIndex;
+        do {
+            if (newIndex === this._tabBarItems.length - 1)
+                newIndex = 0;
+            else
+                newIndex++;
+
+            if (!(this._tabBarItems[newIndex] instanceof WI.PinnedTabBarItem))
+                break;
+        } while (newIndex !== startIndex);
+
+        if (newIndex === startIndex)
+            return;
+
+        this.selectedTabBarItem = this._tabBarItems[newIndex];
+    }
+
+    get selectedTabBarItem()
+    {
+        return this._selectedTabBarItem;
+    }
+
+    set selectedTabBarItem(tabBarItemOrIndex)
+    {
+        let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
+        if (tabBarItem === this._newTabTabBarItem) {
+            // Get the item before the New-Tab item since it is not selectable.
+            tabBarItem = this._tabBarItems[this.normalTabCount - 1];
+        }
+
+        if (this._selectedTabBarItem === tabBarItem)
+            return;
+
+        if (this._selectedTabBarItem)
+            this._selectedTabBarItem.selected = false;
+
+        this._selectedTabBarItem = tabBarItem || null;
+
+        if (this._selectedTabBarItem) {
+            this._selectedTabBarItem.selected = true;
+            if (this._selectedTabBarItem.element.classList.contains("hidden"))
+                this.needsLayout();
+        }
+
+        this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemSelected);
+    }
+
+    get tabBarItems()
+    {
+        return this._tabBarItems;
+    }
+
+    get normalTabCount()
+    {
+        return this._tabBarItems.filter((item) => !(item instanceof WI.PinnedTabBarItem)).length;
+    }
+
+    // Protected
+
+    layout()
+    {
+        if (this.element.classList.contains("static-layout"))
+            return;
+
+        this.element.classList.add("calculate-width");
+        this.element.classList.remove("collapsed");
+
+        function forceItemHidden(item, hidden) {
+            item.element.classList.toggle("hidden", !!hidden);
+        }
+
+        for (let item of this._tabBarItems)
+            forceItemHidden(item, item === this._tabPickerTabBarItem);
+
+        function measureItemWidth(item) {
+            if (!item[WI.TabBar.CachedWidthSymbol])
+                item[WI.TabBar.CachedWidthSymbol] = item.element.realOffsetWidth;
+            return item[WI.TabBar.CachedWidthSymbol];
+        }
+
+        let recalculateItemWidths = () => {
+            return this._tabBarItems.reduce((total, item) => {
+                item[WI.TabBar.CachedWidthSymbol] = undefined;
+                return total + measureItemWidth(item);
+            }, 0);
+        };
+
+        this._hiddenTabBarItems = [];
+
+        let totalItemWidth = recalculateItemWidths();
+        let barWidth = this.element.realOffsetWidth;
+
+        if (totalItemWidth > barWidth) {
+            this.element.classList.add("collapsed");
+            totalItemWidth = recalculateItemWidths();
+            if (totalItemWidth > barWidth) {
+                forceItemHidden(this._tabPickerTabBarItem, false);
+                totalItemWidth += measureItemWidth(this._tabPickerTabBarItem);
+            }
+
+            let tabBarItems = this._tabBarItemsFromLeftToRight();
+            let index = tabBarItems.length;
+            while (totalItemWidth > barWidth && --index >= 0) {
+                let item = tabBarItems[index];
+                if (item === this.selectedTabBarItem || item instanceof WI.PinnedTabBarItem)
+                    continue;
+
+                totalItemWidth -= measureItemWidth(item);
+                forceItemHidden(item, true);
+
+                this._hiddenTabBarItems.push(item);
+            }
+        }
+
+        this.element.classList.remove("calculate-width");
+    }
+
+    // Private
+
+    _tabBarItemsFromLeftToRight()
+    {
+        return WI.resolvedLayoutDirection() === WI.LayoutDirection.LTR ? this._tabBarItems : this._tabBarItems.slice().reverse();
+    }
+
+    _findTabBarItem(tabBarItemOrIndex)
+    {
+        if (typeof tabBarItemOrIndex === "number")
+            return this._tabBarItems[tabBarItemOrIndex] || null;
+
+        if (tabBarItemOrIndex instanceof WI.TabBarItem) {
+            if (this._tabBarItems.includes(tabBarItemOrIndex))
+                return tabBarItemOrIndex;
+        }
+
+        return null;
+    }
+
+    _hasMoreThanOneNormalTab()
+    {
+        let normalTabCount = 0;
+        for (let tabBarItem of this._tabBarItems) {
+            if (tabBarItem instanceof WI.PinnedTabBarItem)
+                continue;
+
+            ++normalTabCount;
+            if (normalTabCount >= 2)
+                return true;
+        }
+
+        return false;
+    }
+
+    _openDefaultTab()
+    {
+        this.dispatchEventToListeners(WI.TabBar.Event.OpenDefaultTab);
+    }
+
+    _recordTabBarItemSizesAndPositions()
+    {
+        var tabBarItemSizesAndPositions = new Map;
+
+        const barRect = this.element.getBoundingClientRect();
+
+        for (var tabBarItem of this._tabBarItems) {
+            var boundingRect = tabBarItem.element.getBoundingClientRect();
+            tabBarItemSizesAndPositions.set(tabBarItem, {left: boundingRect.left - barRect.left, width: boundingRect.width});
+        }
+
+        return tabBarItemSizesAndPositions;
+    }
+
+    _applyTabBarItemSizesAndPositions(tabBarItemSizesAndPositions, skipTabBarItem)
+    {
+        for (var [tabBarItem, sizeAndPosition] of tabBarItemSizesAndPositions) {
+            if (skipTabBarItem && tabBarItem === skipTabBarItem)
+                continue;
+            tabBarItem.element.style.left = sizeAndPosition.left + "px";
+            tabBarItem.element.style.width = sizeAndPosition.width + "px";
+        }
+    }
+
+    _clearTabBarItemSizesAndPositions(skipTabBarItem)
+    {
+        for (var tabBarItem of this._tabBarItems) {
+            if (skipTabBarItem && tabBarItem === skipTabBarItem)
+                continue;
+            tabBarItem.element.style.left = null;
+            tabBarItem.element.style.width = null;
+        }
+    }
+
+    _finishExpandingTabsAfterClose(beforeTabSizesAndPositions)
+    {
+        return new Promise(function(resolve, reject) {
+            console.assert(this._tabAnimatedClosedSinceMouseEnter);
+            this._tabAnimatedClosedSinceMouseEnter = false;
+
+            if (!beforeTabSizesAndPositions)
+                beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+            this.element.classList.remove("static-layout");
+            this._clearTabBarItemSizesAndPositions();
+
+            var afterTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+            this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions);
+            this.element.classList.add("static-layout");
+
+            function animateTabs()
+            {
+                this.element.classList.add("static-layout");
+                this.element.classList.add("animating");
+                this.element.classList.add("expanding-tabs");
+
+                this._applyTabBarItemSizesAndPositions(afterTabSizesAndPositions);
+
+                this.element.addEventListener("webkitTransitionEnd", removeStylesListener);
+            }
+
+            function removeStyles()
+            {
+                this.element.classList.remove("static-layout");
+                this.element.classList.remove("animating");
+                this.element.classList.remove("expanding-tabs");
+
+                this._clearTabBarItemSizesAndPositions();
+
+                this.updateLayout();
+
+                this.element.removeEventListener("webkitTransitionEnd", removeStylesListener);
+
+                resolve();
+            }
+
+            var removeStylesListener = removeStyles.bind(this);
+
+            requestAnimationFrame(animateTabs.bind(this));
+        }.bind(this));
+    }
+
+    _handleMouseDown(event)
+    {
+        // Only consider left mouse clicks for tab movement.
+        if (event.button !== 0 || event.ctrlKey)
+            return;
+
+        let itemElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.StyleClassName);
+        if (!itemElement)
+            return;
+
+        let tabBarItem = itemElement[WI.TabBarItem.ElementReferenceSymbol];
+        if (!tabBarItem)
+            return;
+
+        if (tabBarItem.disabled)
+            return;
+
+        if (tabBarItem === this._newTabTabBarItem)
+            return;
+
+        if (tabBarItem === this._tabPickerTabBarItem) {
+            if (!this._hiddenTabBarItems.length)
+                return;
+
+            let contextMenu = WI.ContextMenu.createFromEvent(event);
+            for (let item of this._hiddenTabBarItems)
+                contextMenu.appendItem(item.title, () => this.selectedTabBarItem = item);
+
+            contextMenu.show();
+            return;
+        }
+
+        let closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.CloseButtonStyleClassName);
+        if (closeButtonElement)
+            return;
+
+        this.selectedTabBarItem = tabBarItem;
+
+        if (tabBarItem instanceof WI.PinnedTabBarItem || !this._hasMoreThanOneNormalTab())
+            return;
+
+        this._firstNormalTabItemIndex = 0;
+        for (let i = 0; i < this._tabBarItems.length; ++i) {
+            if (this._tabBarItems[i] instanceof WI.PinnedTabBarItem)
+                continue;
+
+            this._firstNormalTabItemIndex = i;
+            break;
+        }
+
+        this._mouseIsDown = true;
+
+        this._mouseMovedEventListener = this._handleMouseMoved.bind(this);
+        this._mouseUpEventListener = this._handleMouseUp.bind(this);
+
+        // Register these listeners on the document so we can track the mouse if it leaves the tab bar.
+        document.addEventListener("mousemove", this._mouseMovedEventListener, true);
+        document.addEventListener("mouseup", this._mouseUpEventListener, true);
+
+        event.preventDefault();
+        event.stopPropagation();
+    }
+
+    _handleClick(event)
+    {
+        var itemElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.StyleClassName);
+        if (!itemElement)
+            return;
+
+        var tabBarItem = itemElement[WI.TabBarItem.ElementReferenceSymbol];
+        if (!tabBarItem)
+            return;
+
+        if (tabBarItem.disabled)
+            return;
+
+        const clickedMiddleButton = event.button === 1;
+
+        var closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.CloseButtonStyleClassName);
+        if (closeButtonElement || clickedMiddleButton) {
+            // Disallow closing the default tab if it is the only tab.
+            if (tabBarItem.isDefaultTab && this.element.classList.contains("single-tab"))
+                return;
+
+            if (!event.altKey) {
+                this.removeTabBarItem(tabBarItem, {suppressExpansion: true});
+                return;
+            }
+
+            for (let i = this._tabBarItems.length - 1; i >= 0; --i) {
+                let item = this._tabBarItems[i];
+                if (item === tabBarItem || item instanceof WI.PinnedTabBarItem)
+                    continue;
+                this.removeTabBarItem(item);
+            }
+        }
+    }
+
+    _handleMouseMoved(event)
+    {
+        console.assert(event.button === 0);
+        console.assert(this._mouseIsDown);
+        if (!this._mouseIsDown)
+            return;
+
+        console.assert(this._selectedTabBarItem);
+        if (!this._selectedTabBarItem)
+            return;
+
+        event.preventDefault();
+        event.stopPropagation();
+
+        if (!this.element.classList.contains("static-layout")) {
+            this._applyTabBarItemSizesAndPositions(this._recordTabBarItemSizesAndPositions());
+            this.element.classList.add("static-layout");
+            this.element.classList.add("dragging-tab");
+        }
+
+        if (this._mouseOffset === undefined)
+            this._mouseOffset = event.pageX - this._selectedTabBarItem.element.totalOffsetLeft;
+
+        var tabBarMouseOffset = event.pageX - this.element.totalOffsetLeft;
+        var newLeft = tabBarMouseOffset - this._mouseOffset;
+
+        this._selectedTabBarItem.element.style.left = newLeft + "px";
+
+        var selectedTabMidX = newLeft + (this._selectedTabBarItem.element.realOffsetWidth / 2);
+
+        var currentIndex = this._tabBarItems.indexOf(this._selectedTabBarItem);
+        var newIndex = currentIndex;
+
+        for (let tabBarItem of this._tabBarItems) {
+            if (tabBarItem === this._selectedTabBarItem)
+                continue;
+
+            var tabBarItemRect = tabBarItem.element.getBoundingClientRect();
+
+            if (selectedTabMidX < tabBarItemRect.left || selectedTabMidX > tabBarItemRect.right)
+                continue;
+
+            newIndex = this._tabBarItems.indexOf(tabBarItem);
+            break;
+        }
+
+        // Subtract 1 from normalTabCount since arrays begin indexing at 0.
+        newIndex = Number.constrain(newIndex, this._firstNormalTabItemIndex, this.normalTabCount - 1);
+
+        if (currentIndex === newIndex)
+            return;
+
+        this._tabBarItems.splice(currentIndex, 1);
+        this._tabBarItems.splice(newIndex, 0, this._selectedTabBarItem);
+
+        let nextSibling = this._tabBarItems[newIndex + 1];
+        let nextSiblingElement = nextSibling ? nextSibling.element : this._newTabTabBarItem.element;
+
+        this.element.insertBefore(this._selectedTabBarItem.element, nextSiblingElement);
+
+        // FIXME: Animate the tabs that move to make room for the selected tab. This was causing me trouble when I tried.
+
+        let left = 0;
+        for (let tabBarItem of this._tabBarItemsFromLeftToRight()) {
+            if (tabBarItem !== this._selectedTabBarItem && tabBarItem !== this._newTabTabBarItem && parseFloat(tabBarItem.element.style.left) !== left)
+                tabBarItem.element.style.left = left + "px";
+            left += parseFloat(tabBarItem.element.style.width);
+        }
+    }
+
+    _handleMouseUp(event)
+    {
+        console.assert(event.button === 0);
+        console.assert(this._mouseIsDown);
+        if (!this._mouseIsDown)
+            return;
+
+        this.element.classList.remove("dragging-tab");
+
+        if (!this._tabAnimatedClosedSinceMouseEnter) {
+            this.element.classList.remove("static-layout");
+            this._clearTabBarItemSizesAndPositions();
+        } else {
+            let left = 0;
+            for (let tabBarItem of this._tabBarItemsFromLeftToRight()) {
+                if (tabBarItem === this._selectedTabBarItem)
+                    tabBarItem.element.style.left = left + "px";
+                left += parseFloat(tabBarItem.element.style.width);
+            }
+        }
+
+        this._mouseIsDown = false;
+        this._mouseOffset = undefined;
+
+        document.removeEventListener("mousemove", this._mouseMovedEventListener, true);
+        document.removeEventListener("mouseup", this._mouseUpEventListener, true);
+
+        this._mouseMovedEventListener = null;
+        this._mouseUpEventListener = null;
+
+        event.preventDefault();
+        event.stopPropagation();
+
+        this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemsReordered);
+    }
+
+    _handleMouseLeave(event)
+    {
+        if (this._mouseIsDown || !this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating"))
+            return;
+
+        // This event can still fire when the mouse is inside the element if DOM nodes are added, removed or generally change inside.
+        // Check if the mouse really did leave the element by checking the bounds.
+        // FIXME: Is this a WebKit bug or correct behavior?
+        const barRect = this.element.getBoundingClientRect();
+        const newTabItemRect = this._newTabTabBarItem.element.getBoundingClientRect();
+        if (event.pageY > barRect.top && event.pageY < barRect.bottom && event.pageX > barRect.left && event.pageX < (newTabItemRect ? newTabItemRect.right : barRect.right))
+            return;
+
+        this._finishExpandingTabsAfterClose();
+    }
+
+    _handleContextMenu(event)
+    {
+        let contextMenu = WI.ContextMenu.createFromEvent(event);
+
+        for (let tabClass of WI.knownTabClasses()) {
+            if (tabClass.tabInfo().isEphemeral)
+                continue;
+
+            let openTabBarItem = null;
+            for (let tabBarItem of this._tabBarItems) {
+                let tabContentView = tabBarItem.representedObject;
+                if (!(tabContentView instanceof WI.TabContentView))
+                    continue;
+
+                if (tabContentView.type === tabClass.Type) {
+                    openTabBarItem = tabBarItem;
+                    break;
+                }
+            }
+
+            contextMenu.appendCheckboxItem(tabClass.tabInfo().title, () => {
+                if (openTabBarItem)
+                    this.removeTabBarItem(openTabBarItem);
+                else
+                    WI.createNewTabWithType(tabClass.Type, {shouldShowNewTab: true});
+            }, !!openTabBarItem);
+        }
+    }
+
+    _handleNewTabClick(event)
+    {
+        WI.showNewTabTab();
+    }
+
+    _handleTabPickerTabContextMenu(event)
+    {
+        if (!this._hiddenTabBarItems.length)
+            return;
+
+        let contextMenu = WI.ContextMenu.createFromEvent(event);
+        for (let item of this._hiddenTabBarItems) {
+            contextMenu.appendItem(item.title, () => {
+                this.selectedTabBarItem = item;
+            });
+        }
+    }
+
+    _handleNewTabMouseEnter(event)
+    {
+        if (!this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating"))
+            return;
+
+        this._finishExpandingTabsAfterClose();
+    }
+};
index 75f5399..e9b8c4e 100644 (file)
@@ -27,7 +27,7 @@ WI.NetworkTabContentView = class NetworkTabContentView extends WI.TabContentView
 {
     constructor(identifier)
     {
-        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.NetworkTabContentView);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.NetworkTabContentView.tabInfo());
 
         super(identifier || "network", "network", tabBarItem);
 
index effbe56..5626649 100644 (file)
@@ -27,7 +27,7 @@ WI.NewTabContentView = class NewTabContentView extends WI.TabContentView
 {
     constructor(identifier)
     {
-        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.NewTabContentView);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.NewTabContentView.tabInfo());
         tabBarItem.isDefaultTab = true;
 
         super(identifier || "new-tab", "new-tab", tabBarItem);
@@ -35,7 +35,7 @@ WI.NewTabContentView = class NewTabContentView extends WI.TabContentView
         this._tabElementsByTabClass = new Map;
 
         let allTabClasses = Array.from(WI.knownTabClasses());
-        this._shownTabClasses = allTabClasses.filter((tabClass) => tabClass.isTabAllowed() && !tabClass.isEphemeral());
+        this._shownTabClasses = allTabClasses.filter((tabClass) => tabClass.isTabAllowed() && !tabClass.tabInfo().isEphemeral);
         this._shownTabClasses.sort((a, b) => a.tabInfo().title.extendedLocaleCompare(b.tabInfo().title));
     }
 
@@ -44,14 +44,10 @@ WI.NewTabContentView = class NewTabContentView extends WI.TabContentView
         return {
             image: "Images/NewTab.svg",
             title: WI.UIString("New Tab"),
+            isEphemeral: true,
         };
     }
 
-    static isEphemeral()
-    {
-        return true;
-    }
-
     static shouldSaveTab()
     {
         return false;
index b0822da..4858ffd 100644 (file)
@@ -27,7 +27,7 @@ WI.ResourcesTabContentView = class ResourcesTabContentView extends WI.ContentBro
 {
     constructor(identifier)
     {
-        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.ResourcesTabContentView);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.ResourcesTabContentView.tabInfo());
         const detailsSidebarPanelConstructors = [WI.ResourceDetailsSidebarPanel, WI.ProbeDetailsSidebarPanel];
         super(identifier || "resources", "resources", tabBarItem, WI.ResourceSidebarPanel, detailsSidebarPanelConstructors);
     }
index d0eca6d..325662b 100644 (file)
@@ -27,7 +27,7 @@ WI.SearchTabContentView = class SearchTabContentView extends WI.ContentBrowserTa
 {
     constructor(identifier)
     {
-        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.SearchTabContentView);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.SearchTabContentView.tabInfo());
         let detailsSidebarPanelConstructors = [WI.ResourceDetailsSidebarPanel, WI.ProbeDetailsSidebarPanel,
             WI.DOMNodeDetailsSidebarPanel, WI.ComputedStyleDetailsSidebarPanel, WI.RulesStyleDetailsSidebarPanel];
 
@@ -44,14 +44,10 @@ WI.SearchTabContentView = class SearchTabContentView extends WI.ContentBrowserTa
         return {
             image: "Images/SearchResults.svg",
             title: WI.UIString("Search"),
+            isEphemeral: true,
         };
     }
 
-    static isEphemeral()
-    {
-        return true;
-    }
-
     // Public
 
     get type()
index edb467a..221cd15 100644 (file)
@@ -44,14 +44,10 @@ WI.SettingsTabContentView = class SettingsTabContentView extends WI.TabContentVi
         return {
             image: "Images/Gear.svg",
             title: WI.UIString("Settings"),
+            isEphemeral: true,
         };
     }
 
-    static isEphemeral()
-    {
-        return true;
-    }
-
     static shouldSaveTab()
     {
         return false;
@@ -254,6 +250,9 @@ WI.SettingsTabContentView = class SettingsTabContentView extends WI.TabContentVi
             experimentalSettingsView.addSeparator();
         }
 
+        experimentalSettingsView.addSetting(WI.UIString("User Interface:"), WI.settings.experimentalEnableNewTabBar, WI.UIString("Enable New Tab Bar"));
+        experimentalSettingsView.addSeparator();
+
         let reloadInspectorButton = document.createElement("button");
         reloadInspectorButton.textContent = WI.UIString("Reload Web Inspector");
         reloadInspectorButton.addEventListener("click", () => { window.location.reload(); });
@@ -271,6 +270,7 @@ WI.SettingsTabContentView = class SettingsTabContentView extends WI.TabContentVi
         listenForChange(WI.settings.experimentalLegacyStyleEditor);
         listenForChange(WI.settings.experimentalLegacyVisualSidebar);
         listenForChange(WI.settings.experimentalEnableLayersTab);
+        listenForChange(WI.settings.experimentalEnableNewTabBar);
 
         this.addSettingsView(experimentalSettingsView);
     }
index 0f1369c..37e373c 100644 (file)
@@ -27,7 +27,7 @@ WI.StorageTabContentView = class StorageTabContentView extends WI.ContentBrowser
 {
     constructor(identifier)
     {
-        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.StorageTabContentView);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.StorageTabContentView.tabInfo());
         let detailsSidebarPanelConstructors = [WI.ApplicationCacheDetailsSidebarPanel, WI.IndexedDatabaseDetailsSidebarPanel];
 
         super(identifier || "storage", "storage", tabBarItem, WI.StorageSidebarPanel, detailsSidebarPanelConstructors);
index aff0d51..b079a31 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
index 6f8dc3b..740431b 100644 (file)
@@ -48,11 +48,6 @@ WI.TabBar = class TabBar extends WI.View
 
         this.addTabBarItem(WI.settingsTabContentView.tabBarItem, {suppressAnimations: true});
 
-        this._newTabTabBarItem = new WI.PinnedTabBarItem("Images/NewTabPlus.svg", WI.UIString("Create a new tab"));
-        this._newTabTabBarItem.element.addEventListener("mouseenter", this._handleNewTabMouseEnter.bind(this));
-        this._newTabTabBarItem.element.addEventListener("click", this._handleNewTabClick.bind(this));
-        this.addTabBarItem(this._newTabTabBarItem, {suppressAnimations: true});
-
         this._tabPickerTabBarItem = new WI.PinnedTabBarItem("Images/TabPicker.svg", WI.UIString("Show hidden tabs"));
         this._tabPickerTabBarItem.element.classList.add("tab-picker");
         this._tabPickerTabBarItem.element.addEventListener("contextmenu", this._handleTabPickerTabContextMenu.bind(this));
@@ -61,14 +56,6 @@ WI.TabBar = class TabBar extends WI.View
 
     // Public
 
-    get newTabTabBarItem() { return this._newTabTabBarItem; }
-
-    updateNewTabTabBarItemState()
-    {
-        let newTabExists = !WI.isNewTabWithTypeAllowed(WI.NewTabContentView.Type);
-        this._newTabTabBarItem.disabled = newTabExists;
-    }
-
     addTabBarItem(tabBarItem, options = {})
     {
         return this.insertTabBarItem(tabBarItem, this._tabBarItems.length, options);
@@ -170,9 +157,6 @@ WI.TabBar = class TabBar extends WI.View
         } else
             this.needsLayout();
 
-        if (!(tabBarItem instanceof WI.PinnedTabBarItem))
-            this.updateNewTabTabBarItemState();
-
         this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemAdded, {tabBarItem});
 
         return tabBarItem;
@@ -184,6 +168,9 @@ WI.TabBar = class TabBar extends WI.View
         if (!tabBarItem || tabBarItem instanceof WI.PinnedTabBarItem)
             return null;
 
+        if (!tabBarItem.isEphemeral && this.normalNonEphemeralTabCount === 1)
+            return null;
+
         tabBarItem.parentTabBar = null;
 
         if (this._selectedTabBarItem === tabBarItem) {
@@ -213,10 +200,6 @@ WI.TabBar = class TabBar extends WI.View
         var hasMoreThanOneNormalTab = this._hasMoreThanOneNormalTab();
         this.element.classList.toggle("single-tab", !hasMoreThanOneNormalTab);
 
-        const shouldOpenDefaultTab = !tabBarItem.isDefaultTab && !this.normalTabCount;
-        if (shouldOpenDefaultTab)
-            options.suppressAnimations = true;
-
         if (!hasMoreThanOneNormalTab || wasLastNormalTab || !options.suppressExpansion) {
             if (!options.suppressAnimations) {
                 this._tabAnimatedClosedSinceMouseEnter = true;
@@ -224,13 +207,7 @@ WI.TabBar = class TabBar extends WI.View
             } else
                 this.needsLayout();
 
-            this.updateNewTabTabBarItemState();
-
             this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemRemoved, {tabBarItem});
-
-            if (shouldOpenDefaultTab)
-                this._openDefaultTab();
-
             return tabBarItem;
         }
 
@@ -300,13 +277,8 @@ WI.TabBar = class TabBar extends WI.View
         } else
             this.needsLayout();
 
-        this.updateNewTabTabBarItemState();
-
         this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemRemoved, {tabBarItem});
 
-        if (shouldOpenDefaultTab)
-            this._openDefaultTab();
-
         return tabBarItem;
     }
 
@@ -364,11 +336,6 @@ WI.TabBar = class TabBar extends WI.View
     set selectedTabBarItem(tabBarItemOrIndex)
     {
         let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
-        if (tabBarItem === this._newTabTabBarItem) {
-            // Get the item before the New-Tab item since it is not selectable.
-            tabBarItem = this._tabBarItems[this.normalTabCount - 1];
-        }
-
         if (this._selectedTabBarItem === tabBarItem)
             return;
 
@@ -396,6 +363,11 @@ WI.TabBar = class TabBar extends WI.View
         return this._tabBarItems.filter((item) => !(item instanceof WI.PinnedTabBarItem)).length;
     }
 
+    get normalNonEphemeralTabCount()
+    {
+        return this._tabBarItems.filter((item) => !item.isEphemeral && !(item instanceof WI.PinnedTabBarItem)).length;
+    }
+
     // Protected
 
     layout()
@@ -491,11 +463,6 @@ WI.TabBar = class TabBar extends WI.View
         return false;
     }
 
-    _openDefaultTab()
-    {
-        this.dispatchEventToListeners(WI.TabBar.Event.OpenDefaultTab);
-    }
-
     _recordTabBarItemSizesAndPositions()
     {
         var tabBarItemSizesAndPositions = new Map;
@@ -596,9 +563,6 @@ WI.TabBar = class TabBar extends WI.View
         if (tabBarItem.disabled)
             return;
 
-        if (tabBarItem === this._newTabTabBarItem)
-            return;
-
         if (tabBarItem === this._tabPickerTabBarItem) {
             if (!this._hiddenTabBarItems.length)
                 return;
@@ -659,8 +623,8 @@ WI.TabBar = class TabBar extends WI.View
 
         var closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.CloseButtonStyleClassName);
         if (closeButtonElement || clickedMiddleButton) {
-            // Disallow closing the default tab if it is the only tab.
-            if (tabBarItem.isDefaultTab && this.element.classList.contains("single-tab"))
+            // Disallow closing the only tab.
+            if (this.element.classList.contains("single-tab"))
                 return;
 
             if (!event.altKey) {
@@ -733,7 +697,7 @@ WI.TabBar = class TabBar extends WI.View
         this._tabBarItems.splice(newIndex, 0, this._selectedTabBarItem);
 
         let nextSibling = this._tabBarItems[newIndex + 1];
-        let nextSiblingElement = nextSibling ? nextSibling.element : this._newTabTabBarItem.element;
+        let nextSiblingElement = nextSibling ? nextSibling.element : null;
 
         this.element.insertBefore(this._selectedTabBarItem.element, nextSiblingElement);
 
@@ -741,7 +705,7 @@ WI.TabBar = class TabBar extends WI.View
 
         let left = 0;
         for (let tabBarItem of this._tabBarItemsFromLeftToRight()) {
-            if (tabBarItem !== this._selectedTabBarItem && tabBarItem !== this._newTabTabBarItem && parseFloat(tabBarItem.element.style.left) !== left)
+            if (tabBarItem !== this._selectedTabBarItem && parseFloat(tabBarItem.element.style.left) !== left)
                 tabBarItem.element.style.left = left + "px";
             left += parseFloat(tabBarItem.element.style.width);
         }
@@ -792,8 +756,7 @@ WI.TabBar = class TabBar extends WI.View
         // Check if the mouse really did leave the element by checking the bounds.
         // FIXME: Is this a WebKit bug or correct behavior?
         const barRect = this.element.getBoundingClientRect();
-        const newTabItemRect = this._newTabTabBarItem.element.getBoundingClientRect();
-        if (event.pageY > barRect.top && event.pageY < barRect.bottom && event.pageX > barRect.left && event.pageX < (newTabItemRect ? newTabItemRect.right : barRect.right))
+        if (event.pageY > barRect.top && event.pageY < barRect.bottom && event.pageX > barRect.left && event.pageX < barRect.right)
             return;
 
         this._finishExpandingTabsAfterClose();
@@ -804,7 +767,7 @@ WI.TabBar = class TabBar extends WI.View
         let contextMenu = WI.ContextMenu.createFromEvent(event);
 
         for (let tabClass of WI.knownTabClasses()) {
-            if (tabClass.isEphemeral())
+            if (tabClass.tabInfo().isEphemeral)
                 continue;
 
             let openTabBarItem = null;
@@ -819,20 +782,17 @@ WI.TabBar = class TabBar extends WI.View
                 }
             }
 
+            let checked = !!openTabBarItem;
+            let disabled = checked && this.normalNonEphemeralTabCount === 1;
             contextMenu.appendCheckboxItem(tabClass.tabInfo().title, () => {
                 if (openTabBarItem)
                     this.removeTabBarItem(openTabBarItem);
                 else
                     WI.createNewTabWithType(tabClass.Type, {shouldShowNewTab: true});
-            }, !!openTabBarItem);
+            }, checked, disabled);
         }
     }
 
-    _handleNewTabClick(event)
-    {
-        WI.showNewTabTab();
-    }
-
     _handleTabPickerTabContextMenu(event)
     {
         if (!this._hiddenTabBarItems.length)
@@ -845,14 +805,6 @@ WI.TabBar = class TabBar extends WI.View
             });
         }
     }
-
-    _handleNewTabMouseEnter(event)
-    {
-        if (!this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating"))
-            return;
-
-        this._finishExpandingTabsAfterClose();
-    }
 };
 
 WI.TabBar.CachedWidthSymbol = Symbol("cached-width");
index a02f501..a68fd92 100644 (file)
@@ -283,7 +283,7 @@ WI.TabBrowser = class TabBrowser extends WI.View
 
         this._recentTabContentViews.remove(tabContentView);
 
-        if (!tabContentView.constructor.isEphemeral())
+        if (!tabContentView.constructor.tabInfo().isEphemeral)
             this._closedTabClasses.add(tabContentView.constructor);
 
         this._contentViewContainer.closeContentView(tabContentView);
index 31592db..672c905 100644 (file)
@@ -65,12 +65,6 @@ WI.TabContentView = class TabContentView extends WI.ContentView
         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.
index 7207ec3..8cef9b3 100644 (file)
@@ -27,7 +27,7 @@ WI.TimelineTabContentView = class TimelineTabContentView extends WI.ContentBrows
 {
     constructor(identifier)
     {
-        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.TimelineTabContentView);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.TimelineTabContentView.tabInfo());
         let detailsSidebarPanelConstructors = [WI.ResourceDetailsSidebarPanel, WI.ProbeDetailsSidebarPanel];
 
         super(identifier || "timeline", "timeline", tabBarItem, null, detailsSidebarPanelConstructors);