Source/WebInspectorUI/ChangeLog

 12018-01-31 Matt Baker <mattbaker@apple.com>
 2
 3 Web Inspector: TabBar redesign: remove New Tab button and add experimental feature flag
 4 https://bugs.webkit.org/show_bug.cgi?id=182342
 5 <rdar://problem/37078662>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 This patch adds a new experimental setting group, "User Interface", with
 10 a single setting, "Enable New TabBar". When enabled, the New Tab button is
 11 no longer available in the top-level TabBar. The context menu for choosing
 12 the set of open tabs no longer allows the last open tab to be closed (unchecked).
 13
 14 * Localizations/en.lproj/localizedStrings.js:
 15
 16 * UserInterface/Base/Main.js:
 17 (WI.contentLoaded):
 18 (WI._tryToRestorePendingTabs):
 19 Retain legacy behavior behind experiment feature setting.
 20
 21 * UserInterface/Base/Setting.js:
 22 * UserInterface/Main.html:
 23 * UserInterface/Views/LegacyTabBar.js: Copied from Source/WebInspectorUI/UserInterface/Views/TabBar.js.
 24 (WI.LegacyTabBar):
 25 (WI.LegacyTabBar.prototype.get newTabTabBarItem):
 26 (WI.LegacyTabBar.prototype.updateNewTabTabBarItemState):
 27 (WI.LegacyTabBar.prototype.addTabBarItem):
 28 (WI.LegacyTabBar.prototype.insertTabBarItem.animateTabs):
 29 (WI.LegacyTabBar.prototype.insertTabBarItem.removeStyles):
 30 (WI.LegacyTabBar.prototype.insertTabBarItem):
 31 (WI.LegacyTabBar.prototype.removeTabBarItem.animateTabs):
 32 (WI.LegacyTabBar.prototype.removeTabBarItem.removeStyles):
 33 (WI.LegacyTabBar.prototype.removeTabBarItem):
 34 (WI.LegacyTabBar.prototype.selectPreviousTab):
 35 (WI.LegacyTabBar.prototype.selectNextTab):
 36 (WI.LegacyTabBar.prototype.get selectedTabBarItem):
 37 (WI.LegacyTabBar.prototype.set selectedTabBarItem):
 38 (WI.LegacyTabBar.prototype.get tabBarItems):
 39 (WI.LegacyTabBar.prototype.get normalTabCount):
 40 (WI.LegacyTabBar.prototype.layout.forceItemHidden):
 41 (WI.LegacyTabBar.prototype.layout):
 42 (WI.LegacyTabBar.prototype._tabBarItemsFromLeftToRight):
 43 (WI.LegacyTabBar.prototype._findTabBarItem):
 44 (WI.LegacyTabBar.prototype._hasMoreThanOneNormalTab):
 45 (WI.LegacyTabBar.prototype._openDefaultTab):
 46 (WI.LegacyTabBar.prototype._recordTabBarItemSizesAndPositions):
 47 (WI.LegacyTabBar.prototype._applyTabBarItemSizesAndPositions):
 48 (WI.LegacyTabBar.prototype._clearTabBarItemSizesAndPositions):
 49 (WI.LegacyTabBar.prototype._finishExpandingTabsAfterClose.):
 50 (WI.LegacyTabBar.prototype._finishExpandingTabsAfterClose):
 51 (WI.LegacyTabBar.prototype._handleMouseDown):
 52 (WI.LegacyTabBar.prototype._handleClick):
 53 (WI.LegacyTabBar.prototype._handleMouseMoved):
 54 (WI.LegacyTabBar.prototype._handleMouseUp):
 55 (WI.LegacyTabBar.prototype._handleMouseLeave):
 56 (WI.LegacyTabBar.prototype._handleContextMenu):
 57 (WI.LegacyTabBar.prototype._handleNewTabClick):
 58 (WI.LegacyTabBar.prototype._handleTabPickerTabContextMenu):
 59 (WI.LegacyTabBar.prototype._handleNewTabMouseEnter):
 60
 61 * UserInterface/Views/SettingsTabContentView.js:
 62 (WI.SettingsTabContentView.prototype._createExperimentalSettingsView):
 63
 64 * UserInterface/Views/TabBar.css:
 65 * UserInterface/Views/TabBar.js:
 66 (WI.TabBar):
 67 (WI.TabBar.prototype.insertTabBarItem):
 68 (WI.TabBar.prototype.removeTabBarItem):
 69 (WI.TabBar.prototype.set selectedTabBarItem):
 70 (WI.TabBar.prototype._handleMouseDown):
 71 (WI.TabBar.prototype._handleClick):
 72 (WI.TabBar.prototype._handleMouseMoved):
 73 (WI.TabBar.prototype._handleMouseLeave):
 74 (WI.TabBar.prototype._handleContextMenu):
 75 (WI.TabBar.prototype._handleTabPickerTabContextMenu):
 76 (WI.TabBar.prototype.get newTabTabBarItem): Deleted.
 77 (WI.TabBar.prototype.updateNewTabTabBarItemState): Deleted.
 78 (WI.TabBar.prototype._openDefaultTab): Deleted.
 79 (WI.TabBar.prototype._handleNewTabClick): Deleted.
 80 (WI.TabBar.prototype._handleNewTabMouseEnter): Deleted.
 81 Remove support for the New Tab button and default tab. Without a default
 82 tab, there is nothing to display when no tabs are open, so prevent the
 83 last non-pinned tab from being removed.
 84
1852018-01-30 Devin Rousso <webkit@devinrousso.com>
286
387 Web Inspector: Replace Object.shallowMerge with ES2018 spread operator

Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

@@localizedStrings["Enable Breakpoint"] = "Enable Breakpoint";
367367localizedStrings["Enable Breakpoints"] = "Enable Breakpoints";
368368localizedStrings["Enable Event Listener"] = "Enable Event Listener";
369369localizedStrings["Enable Layers Tab"] = "Enable Layers Tab";
 370localizedStrings["Enable New Tab Bar"] = "Enable New Tab Bar";
370371localizedStrings["Enable Program"] = "Enable Program";
371372localizedStrings["Enable all breakpoints (%s)"] = "Enable all breakpoints (%s)";
372373localizedStrings["Enable breakpoints"] = "Enable breakpoints";

@@localizedStrings["Use Default Media Styles"] = "Use Default Media Styles";
10081009localizedStrings["Use the resource cache when loading resources"] = "Use the resource cache when loading resources";
10091010localizedStrings["User Agent"] = "User Agent";
10101011localizedStrings["User Agent Stylesheet"] = "User Agent Stylesheet";
 1012localizedStrings["User Interface:"] = "User Interface:";
10111013localizedStrings["User Stylesheet"] = "User Stylesheet";
10121014localizedStrings["Using Keyword Value"] = "Using Keyword Value";
10131015localizedStrings["Using previous selector ā€œ%sā€"] = "Using previous selector ā€œ%sā€";

Source/WebInspectorUI/UserInterface/Base/Main.js

@@WI.contentLoaded = function()
270270 // Create the user interface elements.
271271 this.toolbar = new WI.Toolbar(document.getElementById("toolbar"));
272272
273  this.tabBar = new WI.TabBar(document.getElementById("tab-bar"));
274  this.tabBar.addEventListener(WI.TabBar.Event.OpenDefaultTab, this._openDefaultTab, this);
 273 if (WI.settings.experimentalEnableNewTabBar.value)
 274 this.tabBar = new WI.TabBar(document.getElementById("tab-bar"));
 275 else {
 276 this.tabBar = new WI.LegacyTabBar(document.getElementById("tab-bar"));
 277 this.tabBar.addEventListener(WI.TabBar.Event.OpenDefaultTab, this._openDefaultTab, this);
 278 }
275279
276280 this._contentElement = document.getElementById("content");
277281 this._contentElement.setAttribute("role", "main");

@@WI._tryToRestorePendingTabs = function()
601605
602606 this._pendingOpenTabs = stillPendingOpenTabs;
603607
604  this.tabBrowser.tabBar.updateNewTabTabBarItemState();
 608 if (!WI.settings.experimentalEnableNewTabBar.value)
 609 this.tabBrowser.tabBar.updateNewTabTabBarItemState();
605610};
606611
607612WI.showNewTabTab = function(options)

Source/WebInspectorUI/UserInterface/Base/Setting.js

@@WI.settings = {
126126 experimentalEnableLayersTab: new WI.Setting("experimental-enable-layers-tab", false),
127127 experimentalLegacyStyleEditor: new WI.Setting("experimental-legacy-style-editor", false),
128128 experimentalLegacyVisualSidebar: new WI.Setting("experimental-legacy-visual-sidebar", false),
 129 experimentalEnableNewTabBar: new WI.Setting("experimental-enable-new-tab-bar", false),
129130};

Source/WebInspectorUI/UserInterface/Main.html

479479 <script src="Views/TreeOutline.js"></script>
480480 <script src="Views/TreeOutlineGroup.js"></script>
481481
 482 <script src="Views/LegacyTabBar.js"></script>
 483
482484 <script src="Views/ButtonNavigationItem.js"></script>
483485 <script src="Views/DatabaseUserQueryViewBase.js"></script>
484486 <script src="Views/DatabaseUserQueryErrorView.js"></script>

Source/WebInspectorUI/UserInterface/Views/LegacyTabBar.js

 1/*
 2 * Copyright (C) 2018 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26WI.LegacyTabBar = class LegacyTabBar extends WI.View
 27{
 28 constructor(element, tabBarItems)
 29 {
 30 super(element);
 31
 32 this.element.classList.add("tab-bar");
 33 this.element.setAttribute("role", "tablist");
 34 this.element.addEventListener("mousedown", this._handleMouseDown.bind(this));
 35 this.element.addEventListener("click", this._handleClick.bind(this));
 36 this.element.addEventListener("mouseleave", this._handleMouseLeave.bind(this));
 37 this.element.addEventListener("contextmenu", this._handleContextMenu.bind(this));
 38
 39 this.element.createChild("div", "top-border");
 40
 41 this._tabBarItems = [];
 42 this._hiddenTabBarItems = [];
 43
 44 if (tabBarItems) {
 45 for (let tabBarItem in tabBarItems)
 46 this.addTabBarItem(tabBarItem);
 47 }
 48
 49 this.addTabBarItem(WI.settingsTabContentView.tabBarItem, {suppressAnimations: true});
 50
 51 this._newTabTabBarItem = new WI.PinnedTabBarItem("Images/NewTabPlus.svg", WI.UIString("Create a new tab"));
 52 this._newTabTabBarItem.element.addEventListener("mouseenter", this._handleNewTabMouseEnter.bind(this));
 53 this._newTabTabBarItem.element.addEventListener("click", this._handleNewTabClick.bind(this));
 54 this.addTabBarItem(this._newTabTabBarItem, {suppressAnimations: true});
 55
 56 this._tabPickerTabBarItem = new WI.PinnedTabBarItem("Images/TabPicker.svg", WI.UIString("Show hidden tabs"));
 57 this._tabPickerTabBarItem.element.classList.add("tab-picker");
 58 this._tabPickerTabBarItem.element.addEventListener("contextmenu", this._handleTabPickerTabContextMenu.bind(this));
 59 this.addTabBarItem(this._tabPickerTabBarItem, {suppressAnimations: true});
 60 }
 61
 62 // Public
 63
 64 get newTabTabBarItem() { return this._newTabTabBarItem; }
 65
 66 updateNewTabTabBarItemState()
 67 {
 68 let newTabExists = !WI.isNewTabWithTypeAllowed(WI.NewTabContentView.Type);
 69 this._newTabTabBarItem.disabled = newTabExists;
 70 }
 71
 72 addTabBarItem(tabBarItem, options = {})
 73 {
 74 return this.insertTabBarItem(tabBarItem, this._tabBarItems.length, options);
 75 }
 76
 77 insertTabBarItem(tabBarItem, index, options = {})
 78 {
 79 console.assert(tabBarItem instanceof WI.TabBarItem);
 80 if (!(tabBarItem instanceof WI.TabBarItem))
 81 return null;
 82
 83 if (tabBarItem.parentTabBar === this)
 84 return null;
 85
 86 if (this._tabAnimatedClosedSinceMouseEnter) {
 87 // Delay adding the new tab until we can expand the tabs after a closed tab.
 88 this._finishExpandingTabsAfterClose().then(() => {
 89 this.insertTabBarItem(tabBarItem, index, options);
 90 });
 91 return null;
 92 }
 93
 94 if (tabBarItem.parentTabBar)
 95 tabBarItem.parentTabBar.removeTabBarItem(tabBarItem);
 96
 97 tabBarItem.parentTabBar = this;
 98
 99 index = Number.constrain(index, 0, this.normalTabCount);
 100
 101 if (this.element.classList.contains("animating")) {
 102 requestAnimationFrame(removeStyles.bind(this));
 103 options.suppressAnimations = true;
 104 }
 105
 106 var beforeTabSizesAndPositions;
 107 if (!options.suppressAnimations)
 108 beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
 109
 110 this._tabBarItems.splice(index, 0, tabBarItem);
 111
 112 var nextSibling = this._tabBarItems[index + 1];
 113 let nextSiblingElement = nextSibling ? nextSibling.element : this._tabBarItems.lastValue.element;
 114
 115 if (this.element.isAncestor(nextSiblingElement))
 116 this.element.insertBefore(tabBarItem.element, nextSiblingElement);
 117 else
 118 this.element.appendChild(tabBarItem.element);
 119
 120 this.element.classList.toggle("single-tab", !this._hasMoreThanOneNormalTab());
 121
 122 tabBarItem.element.style.left = null;
 123 tabBarItem.element.style.width = null;
 124
 125 function animateTabs()
 126 {
 127 this.element.classList.add("animating");
 128 this.element.classList.add("inserting-tab");
 129
 130 this._applyTabBarItemSizesAndPositions(afterTabSizesAndPositions);
 131
 132 this.element.addEventListener("webkitTransitionEnd", removeStylesListener);
 133 }
 134
 135 function removeStyles()
 136 {
 137 this.element.classList.remove("static-layout");
 138 this.element.classList.remove("animating");
 139 this.element.classList.remove("inserting-tab");
 140
 141 tabBarItem.element.classList.remove("being-inserted");
 142
 143 this._clearTabBarItemSizesAndPositions();
 144
 145 this.element.removeEventListener("webkitTransitionEnd", removeStylesListener);
 146 }
 147
 148 if (!options.suppressAnimations) {
 149 var afterTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
 150
 151 this.updateLayout();
 152
 153 let tabBarItems = this._tabBarItemsFromLeftToRight();
 154 let previousTabBarItem = tabBarItems[tabBarItems.indexOf(tabBarItem) - 1] || null;
 155 let previousTabBarItemSizeAndPosition = previousTabBarItem ? beforeTabSizesAndPositions.get(previousTabBarItem) : null;
 156
 157 if (previousTabBarItemSizeAndPosition)
 158 beforeTabSizesAndPositions.set(tabBarItem, {left: previousTabBarItemSizeAndPosition.left + previousTabBarItemSizeAndPosition.width, width: 0});
 159 else
 160 beforeTabSizesAndPositions.set(tabBarItem, {left: 0, width: 0});
 161
 162 this.element.classList.add("static-layout");
 163 tabBarItem.element.classList.add("being-inserted");
 164
 165 this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions);
 166
 167 var removeStylesListener = removeStyles.bind(this);
 168
 169 requestAnimationFrame(animateTabs.bind(this));
 170 } else
 171 this.needsLayout();
 172
 173 if (!(tabBarItem instanceof WI.PinnedTabBarItem))
 174 this.updateNewTabTabBarItemState();
 175
 176 this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemAdded, {tabBarItem});
 177
 178 return tabBarItem;
 179 }
 180
 181 removeTabBarItem(tabBarItemOrIndex, options = {})
 182 {
 183 let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
 184 if (!tabBarItem || tabBarItem instanceof WI.PinnedTabBarItem)
 185 return null;
 186
 187 tabBarItem.parentTabBar = null;
 188
 189 if (this._selectedTabBarItem === tabBarItem) {
 190 var index = this._tabBarItems.indexOf(tabBarItem);
 191 var nextTabBarItem = this._tabBarItems[index + 1];
 192 if (!nextTabBarItem || nextTabBarItem instanceof WI.PinnedTabBarItem)
 193 nextTabBarItem = this._tabBarItems[index - 1];
 194
 195 this.selectedTabBarItem = nextTabBarItem;
 196 }
 197
 198 if (this.element.classList.contains("animating")) {
 199 requestAnimationFrame(removeStyles.bind(this));
 200 options.suppressAnimations = true;
 201 }
 202
 203 var beforeTabSizesAndPositions;
 204 if (!options.suppressAnimations)
 205 beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
 206
 207 // Subtract 1 from normalTabCount since arrays begin indexing at 0.
 208 let wasLastNormalTab = this._tabBarItems.indexOf(tabBarItem) === this.normalTabCount - 1;
 209
 210 this._tabBarItems.remove(tabBarItem);
 211 tabBarItem.element.remove();
 212
 213 var hasMoreThanOneNormalTab = this._hasMoreThanOneNormalTab();
 214 this.element.classList.toggle("single-tab", !hasMoreThanOneNormalTab);
 215
 216 const shouldOpenDefaultTab = !tabBarItem.isDefaultTab && !this.normalTabCount;
 217 if (shouldOpenDefaultTab)
 218 options.suppressAnimations = true;
 219
 220 if (!hasMoreThanOneNormalTab || wasLastNormalTab || !options.suppressExpansion) {
 221 if (!options.suppressAnimations) {
 222 this._tabAnimatedClosedSinceMouseEnter = true;
 223 this._finishExpandingTabsAfterClose(beforeTabSizesAndPositions);
 224 } else
 225 this.needsLayout();
 226
 227 this.updateNewTabTabBarItemState();
 228
 229 this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemRemoved, {tabBarItem});
 230
 231 if (shouldOpenDefaultTab)
 232 this._openDefaultTab();
 233
 234 return tabBarItem;
 235 }
 236
 237 var lastNormalTabBarItem;
 238
 239 function animateTabs()
 240 {
 241 this.element.classList.add("animating");
 242 this.element.classList.add("closing-tab");
 243
 244 // For RTL, we need to place extra space between pinned tab and first normal tab.
 245 // From left to right there is pinned tabs, extra space, then normal tabs. Compute
 246 // how much extra space we need to additionally add for normal tab items.
 247 let extraSpaceBetweenNormalAndPinnedTabs = 0;
 248 if (WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL) {
 249 extraSpaceBetweenNormalAndPinnedTabs = this.element.getBoundingClientRect().width;
 250 for (let currentTabBarItem of this._tabBarItemsFromLeftToRight())
 251 extraSpaceBetweenNormalAndPinnedTabs -= currentTabBarItem.element.getBoundingClientRect().width;
 252 }
 253
 254 let left = 0;
 255 for (let currentTabBarItem of this._tabBarItemsFromLeftToRight()) {
 256 let sizeAndPosition = beforeTabSizesAndPositions.get(currentTabBarItem);
 257
 258 if (!(currentTabBarItem instanceof WI.PinnedTabBarItem)) {
 259 currentTabBarItem.element.style.left = extraSpaceBetweenNormalAndPinnedTabs + left + "px";
 260 left += sizeAndPosition.width;
 261 lastNormalTabBarItem = currentTabBarItem;
 262 } else
 263 left = sizeAndPosition.left + sizeAndPosition.width;
 264 }
 265
 266 // The selected tab and last tab need to draw a right border as well, so make them 1px wider.
 267 if (this._selectedTabBarItem)
 268 this._selectedTabBarItem.element.style.width = (parseFloat(this._selectedTabBarItem.element.style.width) + 1) + "px";
 269
 270 if (lastNormalTabBarItem !== this._selectedTabBarItem)
 271 lastNormalTabBarItem.element.style.width = (parseFloat(lastNormalTabBarItem.element.style.width) + 1) + "px";
 272
 273 this.element.addEventListener("webkitTransitionEnd", removeStylesListener);
 274 }
 275
 276 function removeStyles()
 277 {
 278 // The selected tab needs to stop drawing the right border, so make it 1px smaller. Only if it isn't the last.
 279 if (this._selectedTabBarItem && this._selectedTabBarItem !== lastNormalTabBarItem)
 280 this._selectedTabBarItem.element.style.width = (parseFloat(this._selectedTabBarItem.element.style.width) - 1) + "px";
 281
 282 this.element.classList.remove("animating");
 283 this.element.classList.remove("closing-tab");
 284
 285 this.updateLayout();
 286
 287 this.element.removeEventListener("webkitTransitionEnd", removeStylesListener);
 288 }
 289
 290 if (!options.suppressAnimations) {
 291 this.element.classList.add("static-layout");
 292
 293 this._tabAnimatedClosedSinceMouseEnter = true;
 294
 295 this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions);
 296
 297 var removeStylesListener = removeStyles.bind(this);
 298
 299 requestAnimationFrame(animateTabs.bind(this));
 300 } else
 301 this.needsLayout();
 302
 303 this.updateNewTabTabBarItemState();
 304
 305 this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemRemoved, {tabBarItem});
 306
 307 if (shouldOpenDefaultTab)
 308 this._openDefaultTab();
 309
 310 return tabBarItem;
 311 }
 312
 313 selectPreviousTab()
 314 {
 315 if (this._tabBarItems.length <= 1)
 316 return;
 317
 318 var startIndex = this._tabBarItems.indexOf(this._selectedTabBarItem);
 319 var newIndex = startIndex;
 320 do {
 321 if (newIndex === 0)
 322 newIndex = this._tabBarItems.length - 1;
 323 else
 324 newIndex--;
 325
 326 if (!(this._tabBarItems[newIndex] instanceof WI.PinnedTabBarItem))
 327 break;
 328 } while (newIndex !== startIndex);
 329
 330 if (newIndex === startIndex)
 331 return;
 332
 333 this.selectedTabBarItem = this._tabBarItems[newIndex];
 334 }
 335
 336 selectNextTab()
 337 {
 338 if (this._tabBarItems.length <= 1)
 339 return;
 340
 341 var startIndex = this._tabBarItems.indexOf(this._selectedTabBarItem);
 342 var newIndex = startIndex;
 343 do {
 344 if (newIndex === this._tabBarItems.length - 1)
 345 newIndex = 0;
 346 else
 347 newIndex++;
 348
 349 if (!(this._tabBarItems[newIndex] instanceof WI.PinnedTabBarItem))
 350 break;
 351 } while (newIndex !== startIndex);
 352
 353 if (newIndex === startIndex)
 354 return;
 355
 356 this.selectedTabBarItem = this._tabBarItems[newIndex];
 357 }
 358
 359 get selectedTabBarItem()
 360 {
 361 return this._selectedTabBarItem;
 362 }
 363
 364 set selectedTabBarItem(tabBarItemOrIndex)
 365 {
 366 let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
 367 if (tabBarItem === this._newTabTabBarItem) {
 368 // Get the item before the New-Tab item since it is not selectable.
 369 tabBarItem = this._tabBarItems[this.normalTabCount - 1];
 370 }
 371
 372 if (this._selectedTabBarItem === tabBarItem)
 373 return;
 374
 375 if (this._selectedTabBarItem)
 376 this._selectedTabBarItem.selected = false;
 377
 378 this._selectedTabBarItem = tabBarItem || null;
 379
 380 if (this._selectedTabBarItem) {
 381 this._selectedTabBarItem.selected = true;
 382 if (this._selectedTabBarItem.element.classList.contains("hidden"))
 383 this.needsLayout();
 384 }
 385
 386 this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemSelected);
 387 }
 388
 389 get tabBarItems()
 390 {
 391 return this._tabBarItems;
 392 }
 393
 394 get normalTabCount()
 395 {
 396 return this._tabBarItems.filter((item) => !(item instanceof WI.PinnedTabBarItem)).length;
 397 }
 398
 399 // Protected
 400
 401 layout()
 402 {
 403 if (this.element.classList.contains("static-layout"))
 404 return;
 405
 406 this.element.classList.add("calculate-width");
 407 this.element.classList.remove("collapsed");
 408
 409 function forceItemHidden(item, hidden) {
 410 item.element.classList.toggle("hidden", !!hidden);
 411 }
 412
 413 for (let item of this._tabBarItems)
 414 forceItemHidden(item, item === this._tabPickerTabBarItem);
 415
 416 function measureItemWidth(item) {
 417 if (!item[WI.TabBar.CachedWidthSymbol])
 418 item[WI.TabBar.CachedWidthSymbol] = item.element.realOffsetWidth;
 419 return item[WI.TabBar.CachedWidthSymbol];
 420 }
 421
 422 let recalculateItemWidths = () => {
 423 return this._tabBarItems.reduce((total, item) => {
 424 item[WI.TabBar.CachedWidthSymbol] = undefined;
 425 return total + measureItemWidth(item);
 426 }, 0);
 427 };
 428
 429 this._hiddenTabBarItems = [];
 430
 431 let totalItemWidth = recalculateItemWidths();
 432 let barWidth = this.element.realOffsetWidth;
 433
 434 if (totalItemWidth > barWidth) {
 435 this.element.classList.add("collapsed");
 436 totalItemWidth = recalculateItemWidths();
 437 if (totalItemWidth > barWidth) {
 438 forceItemHidden(this._tabPickerTabBarItem, false);
 439 totalItemWidth += measureItemWidth(this._tabPickerTabBarItem);
 440 }
 441
 442 let tabBarItems = this._tabBarItemsFromLeftToRight();
 443 let index = tabBarItems.length;
 444 while (totalItemWidth > barWidth && --index >= 0) {
 445 let item = tabBarItems[index];
 446 if (item === this.selectedTabBarItem || item instanceof WI.PinnedTabBarItem)
 447 continue;
 448
 449 totalItemWidth -= measureItemWidth(item);
 450 forceItemHidden(item, true);
 451
 452 this._hiddenTabBarItems.push(item);
 453 }
 454 }
 455
 456 this.element.classList.remove("calculate-width");
 457 }
 458
 459 // Private
 460
 461 _tabBarItemsFromLeftToRight()
 462 {
 463 return WI.resolvedLayoutDirection() === WI.LayoutDirection.LTR ? this._tabBarItems : this._tabBarItems.slice().reverse();
 464 }
 465
 466 _findTabBarItem(tabBarItemOrIndex)
 467 {
 468 if (typeof tabBarItemOrIndex === "number")
 469 return this._tabBarItems[tabBarItemOrIndex] || null;
 470
 471 if (tabBarItemOrIndex instanceof WI.TabBarItem) {
 472 if (this._tabBarItems.includes(tabBarItemOrIndex))
 473 return tabBarItemOrIndex;
 474 }
 475
 476 return null;
 477 }
 478
 479 _hasMoreThanOneNormalTab()
 480 {
 481 let normalTabCount = 0;
 482 for (let tabBarItem of this._tabBarItems) {
 483 if (tabBarItem instanceof WI.PinnedTabBarItem)
 484 continue;
 485
 486 ++normalTabCount;
 487 if (normalTabCount >= 2)
 488 return true;
 489 }
 490
 491 return false;
 492 }
 493
 494 _openDefaultTab()
 495 {
 496 this.dispatchEventToListeners(WI.TabBar.Event.OpenDefaultTab);
 497 }
 498
 499 _recordTabBarItemSizesAndPositions()
 500 {
 501 var tabBarItemSizesAndPositions = new Map;
 502
 503 const barRect = this.element.getBoundingClientRect();
 504
 505 for (var tabBarItem of this._tabBarItems) {
 506 var boundingRect = tabBarItem.element.getBoundingClientRect();
 507 tabBarItemSizesAndPositions.set(tabBarItem, {left: boundingRect.left - barRect.left, width: boundingRect.width});
 508 }
 509
 510 return tabBarItemSizesAndPositions;
 511 }
 512
 513 _applyTabBarItemSizesAndPositions(tabBarItemSizesAndPositions, skipTabBarItem)
 514 {
 515 for (var [tabBarItem, sizeAndPosition] of tabBarItemSizesAndPositions) {
 516 if (skipTabBarItem && tabBarItem === skipTabBarItem)
 517 continue;
 518 tabBarItem.element.style.left = sizeAndPosition.left + "px";
 519 tabBarItem.element.style.width = sizeAndPosition.width + "px";
 520 }
 521 }
 522
 523 _clearTabBarItemSizesAndPositions(skipTabBarItem)
 524 {
 525 for (var tabBarItem of this._tabBarItems) {
 526 if (skipTabBarItem && tabBarItem === skipTabBarItem)
 527 continue;
 528 tabBarItem.element.style.left = null;
 529 tabBarItem.element.style.width = null;
 530 }
 531 }
 532
 533 _finishExpandingTabsAfterClose(beforeTabSizesAndPositions)
 534 {
 535 return new Promise(function(resolve, reject) {
 536 console.assert(this._tabAnimatedClosedSinceMouseEnter);
 537 this._tabAnimatedClosedSinceMouseEnter = false;
 538
 539 if (!beforeTabSizesAndPositions)
 540 beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
 541
 542 this.element.classList.remove("static-layout");
 543 this._clearTabBarItemSizesAndPositions();
 544
 545 var afterTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
 546
 547 this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions);
 548 this.element.classList.add("static-layout");
 549
 550 function animateTabs()
 551 {
 552 this.element.classList.add("static-layout");
 553 this.element.classList.add("animating");
 554 this.element.classList.add("expanding-tabs");
 555
 556 this._applyTabBarItemSizesAndPositions(afterTabSizesAndPositions);
 557
 558 this.element.addEventListener("webkitTransitionEnd", removeStylesListener);
 559 }
 560
 561 function removeStyles()
 562 {
 563 this.element.classList.remove("static-layout");
 564 this.element.classList.remove("animating");
 565 this.element.classList.remove("expanding-tabs");
 566
 567 this._clearTabBarItemSizesAndPositions();
 568
 569 this.updateLayout();
 570
 571 this.element.removeEventListener("webkitTransitionEnd", removeStylesListener);
 572
 573 resolve();
 574 }
 575
 576 var removeStylesListener = removeStyles.bind(this);
 577
 578 requestAnimationFrame(animateTabs.bind(this));
 579 }.bind(this));
 580 }
 581
 582 _handleMouseDown(event)
 583 {
 584 // Only consider left mouse clicks for tab movement.
 585 if (event.button !== 0 || event.ctrlKey)
 586 return;
 587
 588 let itemElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.StyleClassName);
 589 if (!itemElement)
 590 return;
 591
 592 let tabBarItem = itemElement[WI.TabBarItem.ElementReferenceSymbol];
 593 if (!tabBarItem)
 594 return;
 595
 596 if (tabBarItem.disabled)
 597 return;
 598
 599 if (tabBarItem === this._newTabTabBarItem)
 600 return;
 601
 602 if (tabBarItem === this._tabPickerTabBarItem) {
 603 if (!this._hiddenTabBarItems.length)
 604 return;
 605
 606 let contextMenu = WI.ContextMenu.createFromEvent(event);
 607 for (let item of this._hiddenTabBarItems)
 608 contextMenu.appendItem(item.title, () => this.selectedTabBarItem = item);
 609
 610 contextMenu.show();
 611 return;
 612 }
 613
 614 let closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.CloseButtonStyleClassName);
 615 if (closeButtonElement)
 616 return;
 617
 618 this.selectedTabBarItem = tabBarItem;
 619
 620 if (tabBarItem instanceof WI.PinnedTabBarItem || !this._hasMoreThanOneNormalTab())
 621 return;
 622
 623 this._firstNormalTabItemIndex = 0;
 624 for (let i = 0; i < this._tabBarItems.length; ++i) {
 625 if (this._tabBarItems[i] instanceof WI.PinnedTabBarItem)
 626 continue;
 627
 628 this._firstNormalTabItemIndex = i;
 629 break;
 630 }
 631
 632 this._mouseIsDown = true;
 633
 634 this._mouseMovedEventListener = this._handleMouseMoved.bind(this);
 635 this._mouseUpEventListener = this._handleMouseUp.bind(this);
 636
 637 // Register these listeners on the document so we can track the mouse if it leaves the tab bar.
 638 document.addEventListener("mousemove", this._mouseMovedEventListener, true);
 639 document.addEventListener("mouseup", this._mouseUpEventListener, true);
 640
 641 event.preventDefault();
 642 event.stopPropagation();
 643 }
 644
 645 _handleClick(event)
 646 {
 647 var itemElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.StyleClassName);
 648 if (!itemElement)
 649 return;
 650
 651 var tabBarItem = itemElement[WI.TabBarItem.ElementReferenceSymbol];
 652 if (!tabBarItem)
 653 return;
 654
 655 if (tabBarItem.disabled)
 656 return;
 657
 658 const clickedMiddleButton = event.button === 1;
 659
 660 var closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.CloseButtonStyleClassName);
 661 if (closeButtonElement || clickedMiddleButton) {
 662 // Disallow closing the default tab if it is the only tab.
 663 if (tabBarItem.isDefaultTab && this.element.classList.contains("single-tab"))
 664 return;
 665
 666 if (!event.altKey) {
 667 this.removeTabBarItem(tabBarItem, {suppressExpansion: true});
 668 return;
 669 }
 670
 671 for (let i = this._tabBarItems.length - 1; i >= 0; --i) {
 672 let item = this._tabBarItems[i];
 673 if (item === tabBarItem || item instanceof WI.PinnedTabBarItem)
 674 continue;
 675 this.removeTabBarItem(item);
 676 }
 677 }
 678 }
 679
 680 _handleMouseMoved(event)
 681 {
 682 console.assert(event.button === 0);
 683 console.assert(this._mouseIsDown);
 684 if (!this._mouseIsDown)
 685 return;
 686
 687 console.assert(this._selectedTabBarItem);
 688 if (!this._selectedTabBarItem)
 689 return;
 690
 691 event.preventDefault();
 692 event.stopPropagation();
 693
 694 if (!this.element.classList.contains("static-layout")) {
 695 this._applyTabBarItemSizesAndPositions(this._recordTabBarItemSizesAndPositions());
 696 this.element.classList.add("static-layout");
 697 this.element.classList.add("dragging-tab");
 698 }
 699
 700 if (this._mouseOffset === undefined)
 701 this._mouseOffset = event.pageX - this._selectedTabBarItem.element.totalOffsetLeft;
 702
 703 var tabBarMouseOffset = event.pageX - this.element.totalOffsetLeft;
 704 var newLeft = tabBarMouseOffset - this._mouseOffset;
 705
 706 this._selectedTabBarItem.element.style.left = newLeft + "px";
 707
 708 var selectedTabMidX = newLeft + (this._selectedTabBarItem.element.realOffsetWidth / 2);
 709
 710 var currentIndex = this._tabBarItems.indexOf(this._selectedTabBarItem);
 711 var newIndex = currentIndex;
 712
 713 for (let tabBarItem of this._tabBarItems) {
 714 if (tabBarItem === this._selectedTabBarItem)
 715 continue;
 716
 717 var tabBarItemRect = tabBarItem.element.getBoundingClientRect();
 718
 719 if (selectedTabMidX < tabBarItemRect.left || selectedTabMidX > tabBarItemRect.right)
 720 continue;
 721
 722 newIndex = this._tabBarItems.indexOf(tabBarItem);
 723 break;
 724 }
 725
 726 // Subtract 1 from normalTabCount since arrays begin indexing at 0.
 727 newIndex = Number.constrain(newIndex, this._firstNormalTabItemIndex, this.normalTabCount - 1);
 728
 729 if (currentIndex === newIndex)
 730 return;
 731
 732 this._tabBarItems.splice(currentIndex, 1);
 733 this._tabBarItems.splice(newIndex, 0, this._selectedTabBarItem);
 734
 735 let nextSibling = this._tabBarItems[newIndex + 1];
 736 let nextSiblingElement = nextSibling ? nextSibling.element : this._newTabTabBarItem.element;
 737
 738 this.element.insertBefore(this._selectedTabBarItem.element, nextSiblingElement);
 739
 740 // FIXME: Animate the tabs that move to make room for the selected tab. This was causing me trouble when I tried.
 741
 742 let left = 0;
 743 for (let tabBarItem of this._tabBarItemsFromLeftToRight()) {
 744 if (tabBarItem !== this._selectedTabBarItem && tabBarItem !== this._newTabTabBarItem && parseFloat(tabBarItem.element.style.left) !== left)
 745 tabBarItem.element.style.left = left + "px";
 746 left += parseFloat(tabBarItem.element.style.width);
 747 }
 748 }
 749
 750 _handleMouseUp(event)
 751 {
 752 console.assert(event.button === 0);
 753 console.assert(this._mouseIsDown);
 754 if (!this._mouseIsDown)
 755 return;
 756
 757 this.element.classList.remove("dragging-tab");
 758
 759 if (!this._tabAnimatedClosedSinceMouseEnter) {
 760 this.element.classList.remove("static-layout");
 761 this._clearTabBarItemSizesAndPositions();
 762 } else {
 763 let left = 0;
 764 for (let tabBarItem of this._tabBarItemsFromLeftToRight()) {
 765 if (tabBarItem === this._selectedTabBarItem)
 766 tabBarItem.element.style.left = left + "px";
 767 left += parseFloat(tabBarItem.element.style.width);
 768 }
 769 }
 770
 771 this._mouseIsDown = false;
 772 this._mouseOffset = undefined;
 773
 774 document.removeEventListener("mousemove", this._mouseMovedEventListener, true);
 775 document.removeEventListener("mouseup", this._mouseUpEventListener, true);
 776
 777 this._mouseMovedEventListener = null;
 778 this._mouseUpEventListener = null;
 779
 780 event.preventDefault();
 781 event.stopPropagation();
 782
 783 this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemsReordered);
 784 }
 785
 786 _handleMouseLeave(event)
 787 {
 788 if (this._mouseIsDown || !this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating"))
 789 return;
 790
 791 // This event can still fire when the mouse is inside the element if DOM nodes are added, removed or generally change inside.
 792 // Check if the mouse really did leave the element by checking the bounds.
 793 // FIXME: Is this a WebKit bug or correct behavior?
 794 const barRect = this.element.getBoundingClientRect();
 795 const newTabItemRect = this._newTabTabBarItem.element.getBoundingClientRect();
 796 if (event.pageY > barRect.top && event.pageY < barRect.bottom && event.pageX > barRect.left && event.pageX < (newTabItemRect ? newTabItemRect.right : barRect.right))
 797 return;
 798
 799 this._finishExpandingTabsAfterClose();
 800 }
 801
 802 _handleContextMenu(event)
 803 {
 804 let contextMenu = WI.ContextMenu.createFromEvent(event);
 805
 806 for (let tabClass of WI.knownTabClasses()) {
 807 if (tabClass.isEphemeral())
 808 continue;
 809
 810 let openTabBarItem = null;
 811 for (let tabBarItem of this._tabBarItems) {
 812 let tabContentView = tabBarItem.representedObject;
 813 if (!(tabContentView instanceof WI.TabContentView))
 814 continue;
 815
 816 if (tabContentView.type === tabClass.Type) {
 817 openTabBarItem = tabBarItem;
 818 break;
 819 }
 820 }
 821
 822 contextMenu.appendCheckboxItem(tabClass.tabInfo().title, () => {
 823 if (openTabBarItem)
 824 this.removeTabBarItem(openTabBarItem);
 825 else
 826 WI.createNewTabWithType(tabClass.Type, {shouldShowNewTab: true});
 827 }, !!openTabBarItem);
 828 }
 829 }
 830
 831 _handleNewTabClick(event)
 832 {
 833 WI.showNewTabTab();
 834 }
 835
 836 _handleTabPickerTabContextMenu(event)
 837 {
 838 if (!this._hiddenTabBarItems.length)
 839 return;
 840
 841 let contextMenu = WI.ContextMenu.createFromEvent(event);
 842 for (let item of this._hiddenTabBarItems) {
 843 contextMenu.appendItem(item.title, () => {
 844 this.selectedTabBarItem = item;
 845 });
 846 }
 847 }
 848
 849 _handleNewTabMouseEnter(event)
 850 {
 851 if (!this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating"))
 852 return;
 853
 854 this._finishExpandingTabsAfterClose();
 855 }
 856};

Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js

@@WI.SettingsTabContentView = class SettingsTabContentView extends WI.TabContentVi
254254 experimentalSettingsView.addSeparator();
255255 }
256256
 257 experimentalSettingsView.addSetting(WI.UIString("User Interface:"), WI.settings.experimentalEnableNewTabBar, WI.UIString("Enable New Tab Bar"));
 258 experimentalSettingsView.addSeparator();
 259
257260 let reloadInspectorButton = document.createElement("button");
258261 reloadInspectorButton.textContent = WI.UIString("Reload Web Inspector");
259262 reloadInspectorButton.addEventListener("click", () => { window.location.reload(); });

@@WI.SettingsTabContentView = class SettingsTabContentView extends WI.TabContentVi
271274 listenForChange(WI.settings.experimentalLegacyStyleEditor);
272275 listenForChange(WI.settings.experimentalLegacyVisualSidebar);
273276 listenForChange(WI.settings.experimentalEnableLayersTab);
 277 listenForChange(WI.settings.experimentalEnableNewTabBar);
274278
275279 this.addSettingsView(experimentalSettingsView);
276280 }

Source/WebInspectorUI/UserInterface/Views/TabBar.css

11/*
2  * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
 2 * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
33 *
44 * Redistribution and use in source and binary forms, with or without
55 * modification, are permitted provided that the following conditions

Source/WebInspectorUI/UserInterface/Views/TabBar.js

@@WI.TabBar = class TabBar extends WI.View
4848
4949 this.addTabBarItem(WI.settingsTabContentView.tabBarItem, {suppressAnimations: true});
5050
51  this._newTabTabBarItem = new WI.PinnedTabBarItem("Images/NewTabPlus.svg", WI.UIString("Create a new tab"));
52  this._newTabTabBarItem.element.addEventListener("mouseenter", this._handleNewTabMouseEnter.bind(this));
53  this._newTabTabBarItem.element.addEventListener("click", this._handleNewTabClick.bind(this));
54  this.addTabBarItem(this._newTabTabBarItem, {suppressAnimations: true});
55 
5651 this._tabPickerTabBarItem = new WI.PinnedTabBarItem("Images/TabPicker.svg", WI.UIString("Show hidden tabs"));
5752 this._tabPickerTabBarItem.element.classList.add("tab-picker");
5853 this._tabPickerTabBarItem.element.addEventListener("contextmenu", this._handleTabPickerTabContextMenu.bind(this));

@@WI.TabBar = class TabBar extends WI.View
6156
6257 // Public
6358
64  get newTabTabBarItem() { return this._newTabTabBarItem; }
65 
66  updateNewTabTabBarItemState()
67  {
68  let newTabExists = !WI.isNewTabWithTypeAllowed(WI.NewTabContentView.Type);
69  this._newTabTabBarItem.disabled = newTabExists;
70  }
71 
7259 addTabBarItem(tabBarItem, options = {})
7360 {
7461 return this.insertTabBarItem(tabBarItem, this._tabBarItems.length, options);

@@WI.TabBar = class TabBar extends WI.View
170157 } else
171158 this.needsLayout();
172159
173  if (!(tabBarItem instanceof WI.PinnedTabBarItem))
174  this.updateNewTabTabBarItemState();
175 
176160 this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemAdded, {tabBarItem});
177161
178162 return tabBarItem;

@@WI.TabBar = class TabBar extends WI.View
184168 if (!tabBarItem || tabBarItem instanceof WI.PinnedTabBarItem)
185169 return null;
186170
 171 if (this.normalTabCount === 1)
 172 return null;
 173
187174 tabBarItem.parentTabBar = null;
188175
189176 if (this._selectedTabBarItem === tabBarItem) {

@@WI.TabBar = class TabBar extends WI.View
213200 var hasMoreThanOneNormalTab = this._hasMoreThanOneNormalTab();
214201 this.element.classList.toggle("single-tab", !hasMoreThanOneNormalTab);
215202
216  const shouldOpenDefaultTab = !tabBarItem.isDefaultTab && !this.normalTabCount;
217  if (shouldOpenDefaultTab)
218  options.suppressAnimations = true;
219 
220203 if (!hasMoreThanOneNormalTab || wasLastNormalTab || !options.suppressExpansion) {
221204 if (!options.suppressAnimations) {
222205 this._tabAnimatedClosedSinceMouseEnter = true;

@@WI.TabBar = class TabBar extends WI.View
224207 } else
225208 this.needsLayout();
226209
227  this.updateNewTabTabBarItemState();
228 
229210 this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemRemoved, {tabBarItem});
230 
231  if (shouldOpenDefaultTab)
232  this._openDefaultTab();
233 
234211 return tabBarItem;
235212 }
236213

@@WI.TabBar = class TabBar extends WI.View
300277 } else
301278 this.needsLayout();
302279
303  this.updateNewTabTabBarItemState();
304 
305280 this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemRemoved, {tabBarItem});
306281
307  if (shouldOpenDefaultTab)
308  this._openDefaultTab();
309 
310282 return tabBarItem;
311283 }
312284

@@WI.TabBar = class TabBar extends WI.View
364336 set selectedTabBarItem(tabBarItemOrIndex)
365337 {
366338 let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
367  if (tabBarItem === this._newTabTabBarItem) {
368  // Get the item before the New-Tab item since it is not selectable.
369  tabBarItem = this._tabBarItems[this.normalTabCount - 1];
370  }
371 
372339 if (this._selectedTabBarItem === tabBarItem)
373340 return;
374341

@@WI.TabBar = class TabBar extends WI.View
491458 return false;
492459 }
493460
494  _openDefaultTab()
495  {
496  this.dispatchEventToListeners(WI.TabBar.Event.OpenDefaultTab);
497  }
498 
499461 _recordTabBarItemSizesAndPositions()
500462 {
501463 var tabBarItemSizesAndPositions = new Map;

@@WI.TabBar = class TabBar extends WI.View
596558 if (tabBarItem.disabled)
597559 return;
598560
599  if (tabBarItem === this._newTabTabBarItem)
600  return;
601 
602561 if (tabBarItem === this._tabPickerTabBarItem) {
603562 if (!this._hiddenTabBarItems.length)
604563 return;

@@WI.TabBar = class TabBar extends WI.View
659618
660619 var closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.CloseButtonStyleClassName);
661620 if (closeButtonElement || clickedMiddleButton) {
662  // Disallow closing the default tab if it is the only tab.
663  if (tabBarItem.isDefaultTab && this.element.classList.contains("single-tab"))
 621 // Disallow closing the only tab.
 622 if (this.element.classList.contains("single-tab"))
664623 return;
665624
666625 if (!event.altKey) {

@@WI.TabBar = class TabBar extends WI.View
733692 this._tabBarItems.splice(newIndex, 0, this._selectedTabBarItem);
734693
735694 let nextSibling = this._tabBarItems[newIndex + 1];
736  let nextSiblingElement = nextSibling ? nextSibling.element : this._newTabTabBarItem.element;
 695 let nextSiblingElement = nextSibling ? nextSibling.element : null;
737696
738697 this.element.insertBefore(this._selectedTabBarItem.element, nextSiblingElement);
739698

@@WI.TabBar = class TabBar extends WI.View
741700
742701 let left = 0;
743702 for (let tabBarItem of this._tabBarItemsFromLeftToRight()) {
744  if (tabBarItem !== this._selectedTabBarItem && tabBarItem !== this._newTabTabBarItem && parseFloat(tabBarItem.element.style.left) !== left)
 703 if (tabBarItem !== this._selectedTabBarItem && parseFloat(tabBarItem.element.style.left) !== left)
745704 tabBarItem.element.style.left = left + "px";
746705 left += parseFloat(tabBarItem.element.style.width);
747706 }

@@WI.TabBar = class TabBar extends WI.View
792751 // Check if the mouse really did leave the element by checking the bounds.
793752 // FIXME: Is this a WebKit bug or correct behavior?
794753 const barRect = this.element.getBoundingClientRect();
795  const newTabItemRect = this._newTabTabBarItem.element.getBoundingClientRect();
796  if (event.pageY > barRect.top && event.pageY < barRect.bottom && event.pageX > barRect.left && event.pageX < (newTabItemRect ? newTabItemRect.right : barRect.right))
 754 if (event.pageY > barRect.top && event.pageY < barRect.bottom && event.pageX > barRect.left && event.pageX < barRect.right)
797755 return;
798756
799757 this._finishExpandingTabsAfterClose();

@@WI.TabBar = class TabBar extends WI.View
819777 }
820778 }
821779
 780 let checked = !!openTabBarItem;
 781 let disabled = checked && this.normalTabCount === 1;
822782 contextMenu.appendCheckboxItem(tabClass.tabInfo().title, () => {
823783 if (openTabBarItem)
824784 this.removeTabBarItem(openTabBarItem);
825785 else
826786 WI.createNewTabWithType(tabClass.Type, {shouldShowNewTab: true});
827  }, !!openTabBarItem);
 787 }, !!openTabBarItem, disabled);
828788 }
829789 }
830790
831  _handleNewTabClick(event)
832  {
833  WI.showNewTabTab();
834  }
835 
836791 _handleTabPickerTabContextMenu(event)
837792 {
838793 if (!this._hiddenTabBarItems.length)

@@WI.TabBar = class TabBar extends WI.View
845800 });
846801 }
847802 }
848 
849  _handleNewTabMouseEnter(event)
850  {
851  if (!this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating"))
852  return;
853 
854  this._finishExpandingTabsAfterClose();
855  }
856803};
857804
858805WI.TabBar.CachedWidthSymbol = Symbol("cached-width");