Bug 649654 - When tab bar underflow occurs and tab strip was scrolled to the beginning, resize tabs such that they can be closed in succession. r=fryn
authorithinc <ithinc@sohu.com>
Tue, 29 Jan 2013 18:14:45 -0800
changeset 120317 5294a43582ddec4e29110bfe30b94fd95e1bf09a
parent 120316 b1b6fb261d368957379045320a1bd8752456d899
child 120318 4f7371e936e0f2339d6649985515b90ed294bce4
push id24246
push userryanvm@gmail.com
push dateWed, 30 Jan 2013 13:05:37 +0000
treeherdermozilla-central@5f9775715519 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfryn
bugs649654
milestone21.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 649654 - When tab bar underflow occurs and tab strip was scrolled to the beginning, resize tabs such that they can be closed in succession. r=fryn
browser/base/content/tabbrowser.css
browser/base/content/tabbrowser.xml
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -42,21 +42,22 @@ tabpanels {
 }
 
 .tab-throbber:not([busy]),
 .tab-throbber[busy] + .tab-icon-image {
   display: none;
 }
 
 .closing-tabs-spacer {
+  min-width: 0;
   pointer-events: none;
 }
 
 .tabbrowser-tabs:not(:hover) > .tabbrowser-arrowscrollbox > .closing-tabs-spacer {
-  transition: width .15s ease-out;
+  transition: min-width 150ms ease-out;
 }
 
 /**
  * Optimization for tabs that are restored lazily. We can save a good amount of
  * memory that to-be-restored tabs would otherwise consume simply by setting
  * their browsers to 'display: none' as that will prevent them from having to
  * create a presentation and the like.
  */
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1775,20 +1775,16 @@
               this.tabs[i]._tPos = i;
 
             if (!this._windowIsClosing) {
               if (wasPinned)
                 this.tabContainer._positionPinnedTabs();
 
               // update tab close buttons state
               this.tabContainer.adjustTabstrip();
-
-              setTimeout(function(tabs) {
-                tabs._lastTabClosedByMouse = false;
-              }, 0, this.tabContainer);
             }
 
             // update first-tab/last-tab/beforeselected/afterselected attributes
             this.selectedTab._selected = true;
 
             // Removing the panel requires fixing up selectedPanel immediately
             // (see below), which would be hindered by the potentially expensive
             // browser removal. So we remove the browser and the panel in two
@@ -2823,20 +2819,23 @@
     </implementation>
 
     <handlers>
       <handler event="underflow" phase="capturing"><![CDATA[
         if (event.detail == 0)
           return; // Ignore vertical events
 
         var tabs = document.getBindingParent(this);
-        tabs.removeAttribute("overflow");
-
-        if (tabs._lastTabClosedByMouse)
-          tabs._expandSpacerBy(this._scrollButtonDown.clientWidth);
+
+        if (tabs.hasAttribute("dontresize") || tabs.hasAttribute("using-closing-tabs-spacer")) {
+          tabs.mTabstrip._scrollButtonUp.style.visibility = "hidden";
+          tabs.mTabstrip._scrollButtonDown.style.visibility = "hidden";
+        } else {
+          tabs.removeAttribute("overflow");
+        }
 
         tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser.removeTab,
                                               tabs.tabbrowser);
 
         tabs._positionPinnedTabs();
       ]]></handler>
       <handler event="overflow"><![CDATA[
         if (event.detail == 0)
@@ -2871,18 +2870,17 @@
         <children includes="tab"/>
 # This is to ensure anything extensions put here will go before the newtab
 # button, necessary due to the previous hack.
         <children/>
         <xul:toolbarbutton class="tabs-newtab-button"
                            command="cmd_newNavigatorTab"
                            onclick="checkForMiddleClick(this, event);"
                            tooltiptext="&newTabButton.tooltip;"/>
-        <xul:spacer class="closing-tabs-spacer" anonid="closing-tabs-spacer"
-                    style="width: 0;"/>
+        <xul:spacer class="closing-tabs-spacer" anonid="closing-tabs-spacer"/>
       </xul:arrowscrollbox>
     </content>
 
     <implementation implements="nsIDOMEventListener">
       <constructor>
         <![CDATA[
           this.mTabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth");
           this.mCloseButtons = Services.prefs.getIntPref("browser.tabs.closeButtons");
@@ -3069,18 +3067,16 @@
           } catch (e) {}
         ]]></body>
       </method>
 
       <field name="_closingTabsSpacer">
         document.getAnonymousElementByAttribute(this, "anonid", "closing-tabs-spacer");
       </field>
 
-      <field name="_lastTabClosedByMouse">false</field>
-
       <field name="_delayResizingRule" readonly="true"><![CDATA[
         const href = "chrome://browser/content/browser.css";
         const selector = ".tabbrowser-tabs[dontresize] > .tabbrowser-tab[fadein]:not([pinned])";
 
         // XXX: document.styleSheets is not iterable (see bug 738196)
         for (let sheet of Array.slice(document.styleSheets))
           if (sheet.href == href)
             for (let rule of Array.slice(sheet.cssRules))
@@ -3093,86 +3089,77 @@
         <body><![CDATA[
           var tabs = this.tabbrowser.visibleTabs;
           if (!tabs.length)
             return;
 
           var isEndTab = (aTab._tPos > tabs[tabs.length-1]._tPos);
           var tabWidth = aTab.getBoundingClientRect().width;
 
-          this._lastTabClosedByMouse = true;
-
-          if (this.getAttribute("overflow") == "true") {
-            // Don't need to do anything if we're in overflow mode and aren't scrolled
-            // all the way to the right, or if we're closing the last tab.
-            if (isEndTab || !this.mTabstrip._scrollButtonDown.disabled)
-              return;
-
-            // If the tab has an owner that will become the active tab, the owner will
-            // be to the left of it, so we actually want the left tab to slide over.
-            // This can't be done as easily in non-overflow mode, so we don't bother.
-            if (aTab.owner)
-              return;
-
-            this._expandSpacerBy(tabWidth);
-          } else { // non-overflow mode
-            // Locking is neither in effect nor needed, so let tabs expand normally.
-            if (isEndTab && !this.hasAttribute("dontresize"))
-              return;
-
-            // Let spacer grow to the maximum and lock it, then let tabs expand normally
-            if (isEndTab) {
-              let spacer = this._closingTabsSpacer;
-              spacer.style.MozBoxFlex = 1;
-              spacer.style.minWidth = getComputedStyle(spacer).width;
-              spacer.style.MozBoxFlex = "";
-
-              this.setAttribute("dontanimate", "true");
-              this.removeAttribute("dontresize");
-              this.clientTop;
-              this.removeAttribute("dontanimate");
-              return;
-            }
-
-            if (this.hasAttribute("dontresize"))
-              return;
-
+          // Locking is neither in effect nor needed, so let tabs expand normally.
+          if (isEndTab && !this.hasAttribute("dontresize"))
+            return;
+
+          // Let spacer grow to the maximum and lock it, then let tabs expand normally
+          if (isEndTab) {
+            let spacer = this._closingTabsSpacer;
+            spacer.style.MozBoxFlex = 1;
+            spacer.style.minWidth = getComputedStyle(spacer).width;
+            spacer.style.MozBoxFlex = "";
+
+            this.setAttribute("dontanimate", "true");
+            this.removeAttribute("dontresize");
+            this.clientTop;
+            this.removeAttribute("dontanimate");
+            return;
+          }
+
+          if (!this.hasAttribute("dontresize")) {
             this._delayResizingRule.style.setProperty("max-width", tabWidth + "px", "important");
             this.setAttribute("dontanimate", "true");
             this.setAttribute("dontresize", "true");
             this.clientTop; // flush styles to skip animation; see bug 649247
             this.removeAttribute("dontanimate");
-            this.tabbrowser.addEventListener("mousemove", this, false);
-            window.addEventListener("mouseout", this, false);
           }
-        ]]></body>
-      </method>
-
-      <method name="_expandSpacerBy">
-        <parameter name="pixels"/>
-        <body><![CDATA[
-          let spacer = this._closingTabsSpacer;
-          spacer.style.width = parseFloat(spacer.style.width) + pixels + "px";
-          this.setAttribute("using-closing-tabs-spacer", "true");
+
+          if (!this.mTabstrip._scrollButtonUp.disabled) {
+            let spacer = this._closingTabsSpacer;
+            let width = parseFloat(spacer.style.minWidth) || 0;
+            width += tabWidth;
+
+            if (!this.mTabstrip._scrollButtonDown.disabled) {
+              let scrollbox = this.mTabstrip._scrollbox;
+              width -= scrollbox.scrollLeftMax - scrollbox.scrollLeft;
+            }
+
+            if (width >= 0) {
+              spacer.style.minWidth = width + "px";
+              this.setAttribute("using-closing-tabs-spacer", "true");
+            }
+          }
+
           this.tabbrowser.addEventListener("mousemove", this, false);
           window.addEventListener("mouseout", this, false);
         ]]></body>
       </method>
 
       <method name="_unlockTabSizing">
         <body><![CDATA[
           this.tabbrowser.removeEventListener("mousemove", this, false);
           window.removeEventListener("mouseout", this, false);
 
-          if (this.hasAttribute("using-closing-tabs-spacer")) {
-            this.removeAttribute("using-closing-tabs-spacer");
-            this._closingTabsSpacer.style.width = 0;
+          this._closingTabsSpacer.style.minWidth = "";
+          this.removeAttribute("using-closing-tabs-spacer");
+          this.removeAttribute("dontresize");
+
+          if (this.hasAttribute("overflow") && this.mTabstrip._scrollbox.scrollWidth <= this.mTabstrip._scrollbox.clientWidth) {
+            this.mTabstrip._scrollButtonUp.style.visibility = "";
+            this.mTabstrip._scrollButtonDown.style.visibility = "";
+            this.removeAttribute("overflow");
           }
-          this._closingTabsSpacer.style.minWidth = "";
-          this.removeAttribute("dontresize");
         ]]></body>
       </method>
 
       <field name="_lastNumPinned">0</field>
       <method name="_positionPinnedTabs">
         <body><![CDATA[
           var numPinned = this.tabbrowser._numPinnedTabs;
           var doPosition = this.getAttribute("overflow") == "true" &&