Bug 1358721 - Avoid flushing layout in adjustTabstrip. r?mconley draft
authorDão Gottwald <dao@mozilla.com>
Mon, 29 May 2017 21:11:14 +0200
changeset 586100 f8240525464da00f93c1b98adc6c612f8ed2ee6a
parent 586049 34ac1a5d6576d6775491c8a882710a1520551da6
child 630893 6736d6b5e7f826dcb5e701548a0543f6cd5bfa72
push id61301
push userdgottwald@mozilla.com
push dateMon, 29 May 2017 19:11:46 +0000
reviewersmconley
bugs1358721
milestone55.0a1
Bug 1358721 - Avoid flushing layout in adjustTabstrip. r?mconley MozReview-Commit-ID: AYavUkdoCQq
browser/base/content/tabbrowser.xml
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -6016,44 +6016,62 @@
         <body><![CDATA[
           if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1)
             this.visible = window.toolbar.visible;
           else
             this.visible = true;
         ]]></body>
       </method>
 
+      <field name="_closeButtonsUpdatePending">false</field>
       <method name="adjustTabstrip">
         <body><![CDATA[
-          // If we're overflowing, tab widths don't change anymore, so we can
-          // return early to avoid flushing layout.
+          if (this._closeButtonsUpdatePending) {
+            return;
+          }
+          this._closeButtonsUpdatePending = true;
+
+          // If we're overflowing, tabs are at their minimum widths.
           if (this.getAttribute("overflow") == "true") {
-            this.setAttribute("closebuttons", "activetab");
+            window.requestAnimationFrame(() => {
+              this._closeButtonsUpdatePending = false;
+              this.setAttribute("closebuttons", "activetab");
+            });
             return;
           }
 
-          let numTabs = this.childNodes.length -
-                        this.tabbrowser._removingTabs.length;
-          if (numTabs > 2) {
-            // This is an optimization to avoid layout flushes by calling
-            // getBoundingClientRect() when we just opened a second tab. In
-            // this case it's highly unlikely that the tab width is smaller
-            // than mTabClipWidth and the tab close button obscures too much
-            // of the tab's label. In the edge case of the window being too
-            // narrow (or if tabClipWidth has been set to a way higher value),
-            // we'll correct the 'closebuttons' attribute after the tabopen
-            // animation has finished.
-
-            let tab = this.tabbrowser.visibleTabs[this.tabbrowser._numPinnedTabs];
-            if (tab && tab.getBoundingClientRect().width <= this.mTabClipWidth) {
-              this.setAttribute("closebuttons", "activetab");
-              return;
-            }
-          }
-          this.removeAttribute("closebuttons");
+          // Wait until after the next paint to get current layout data from
+          // getBoundsWithoutFlushing.
+          window.requestAnimationFrame(() => {
+            window.requestAnimationFrame(() => {
+              this._closeButtonsUpdatePending = false;
+
+              // The scrollbox may have started overflowing since we checked
+              // overflow earlier, so check again.
+              if (this.getAttribute("overflow") == "true") {
+                this.setAttribute("closebuttons", "activetab");
+                return;
+              }
+
+              // Check if tab widths are below the threshold where we want to
+              // remove close buttons from background tabs so that people don't
+              // accidentally close tabs by selecting them.
+              let rect = ele => {
+                return window.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIDOMWindowUtils)
+                             .getBoundsWithoutFlushing(ele);
+              };
+              let tab = this.tabbrowser.visibleTabs[this.tabbrowser._numPinnedTabs];
+              if (tab && rect(tab).width <= this.mTabClipWidth) {
+                this.setAttribute("closebuttons", "activetab");
+              } else {
+                this.removeAttribute("closebuttons");
+              }
+            });
+          });
         ]]></body>
       </method>
 
       <method name="_handleTabSelect">
         <parameter name="aSmoothScroll"/>
         <body><![CDATA[
           if (this.getAttribute("overflow") == "true")
             this.mTabstrip.ensureElementIsVisible(this.selectedItem, aSmoothScroll);