Bug 1358816: Part 1 - Avoid layout flushes during tab move animations. r?florian draft
authorKris Maglione <maglione.k@gmail.com>
Sat, 22 Jul 2017 13:58:30 -0700
changeset 613812 bec00e5fabe9c882f533a73b6baa68f0854b8340
parent 613619 602b9b94d5b41e61ebb834bdc7e7b72eebaa378a
child 613813 ff979b98b4d2c934e0f224d8a88621daa2c6869f
push id69857
push usermaglione.k@gmail.com
push dateSat, 22 Jul 2017 21:16:41 +0000
reviewersflorian
bugs1358816
milestone56.0a1
Bug 1358816: Part 1 - Avoid layout flushes during tab move animations. r?florian MozReview-Commit-ID: C4GidGfhgxB
browser/base/content/tabbrowser.xml
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -6428,108 +6428,123 @@
       </method>
 
       <method name="_animateTabMove">
         <parameter name="event"/>
         <body><![CDATA[
           let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
 
           if (this.getAttribute("movingtab") != "true") {
-            this.setAttribute("movingtab", "true");
-            this.selectedItem = draggedTab;
+            requestAnimationFrame(() => {
+              this.setAttribute("movingtab", "true");
+              this.selectedItem = draggedTab;
+            });
           }
 
           if (!("animLastScreenX" in draggedTab._dragData))
             draggedTab._dragData.animLastScreenX = draggedTab._dragData.screenX;
 
           let screenX = event.screenX;
           if (screenX == draggedTab._dragData.animLastScreenX)
             return;
 
-          draggedTab._dragData.animLastScreenX = screenX;
-
-          let rtl = (window.getComputedStyle(this).direction == "rtl");
-          let pinned = draggedTab.pinned;
-          let numPinned = this.tabbrowser._numPinnedTabs;
-          let tabs = this.tabbrowser.visibleTabs
-                                    .slice(pinned ? 0 : numPinned,
-                                           pinned ? numPinned : undefined);
-          if (rtl)
-            tabs.reverse();
-          let tabWidth = draggedTab.getBoundingClientRect().width;
-          draggedTab._dragData.tabWidth = tabWidth;
-
-          // Move the dragged tab based on the mouse position.
-
-          let leftTab = tabs[0];
-          let rightTab = tabs[tabs.length - 1];
-          let tabScreenX = draggedTab.boxObject.screenX;
-          let translateX = screenX - draggedTab._dragData.screenX;
-          if (!pinned)
-            translateX += this.mTabstrip.scrollPosition - draggedTab._dragData.scrollX;
-          let leftBound = leftTab.boxObject.screenX - tabScreenX;
-          let rightBound = (rightTab.boxObject.screenX + rightTab.boxObject.width) -
-                           (tabScreenX + tabWidth);
-          translateX = Math.max(translateX, leftBound);
-          translateX = Math.min(translateX, rightBound);
-          draggedTab.style.transform = "translateX(" + translateX + "px)";
-          draggedTab._dragData.translateX = translateX;
-
-          // Determine what tab we're dragging over.
-          // * Point of reference is the center of the dragged tab. If that
-          //   point touches a background tab, the dragged tab would take that
-          //   tab's position when dropped.
-          // * We're doing a binary search in order to reduce the amount of
-          //   tabs we need to check.
-
-          let tabCenter = tabScreenX + translateX + tabWidth / 2;
-          let newIndex = -1;
-          let oldIndex = "animDropIndex" in draggedTab._dragData ?
-                         draggedTab._dragData.animDropIndex : draggedTab._tPos;
-          let low = 0;
-          let high = tabs.length - 1;
-          while (low <= high) {
-            let mid = Math.floor((low + high) / 2);
-            if (tabs[mid] == draggedTab &&
-                ++mid > high)
-              break;
-            let boxObject = tabs[mid].boxObject;
-            screenX = boxObject.screenX + getTabShift(tabs[mid], oldIndex);
-            if (screenX > tabCenter) {
-              high = mid - 1;
-            } else if (screenX + boxObject.width < tabCenter) {
-              low = mid + 1;
-            } else {
-              newIndex = tabs[mid]._tPos;
-              break;
-            }
-          }
-          if (newIndex >= oldIndex)
-            newIndex++;
-          if (newIndex < 0 || newIndex == oldIndex)
-            return;
-          draggedTab._dragData.animDropIndex = newIndex;
-
-          // Shift background tabs to leave a gap where the dragged tab
-          // would currently be dropped.
-
-          for (let tab of tabs) {
-            if (tab != draggedTab) {
-              let shift = getTabShift(tab, newIndex);
-              tab.style.transform = shift ? "translateX(" + shift + "px)" : "";
-            }
-          }
-
-          function getTabShift(tab, dropIndex) {
-            if (tab._tPos < draggedTab._tPos && tab._tPos >= dropIndex)
-              return rtl ? -tabWidth : tabWidth;
-            if (tab._tPos > draggedTab._tPos && tab._tPos < dropIndex)
-              return rtl ? tabWidth : -tabWidth;
-            return 0;
-          }
+          (async () => {
+            draggedTab._dragData.animLastScreenX = screenX;
+
+            let tabWidth, rtl;
+            let newIndex = -1;
+            let oldIndex = "animDropIndex" in draggedTab._dragData ?
+                           draggedTab._dragData.animDropIndex : draggedTab._tPos;
+
+            let pinned = draggedTab.pinned;
+            let numPinned = this.tabbrowser._numPinnedTabs;
+            let tabs = this.tabbrowser.visibleTabs
+                                      .slice(pinned ? 0 : numPinned,
+                                             pinned ? numPinned : undefined);
+
+            await PromiseUtils.layoutFlushed(document, "layout", () => {
+              rtl = (window.getComputedStyle(this).direction == "rtl");
+              if (rtl)
+                tabs.reverse();
+              tabWidth = draggedTab.getBoundingClientRect().width;
+              draggedTab._dragData.tabWidth = tabWidth;
+
+              // Move the dragged tab based on the mouse position.
+
+              let leftTab = tabs[0];
+              let rightTab = tabs[tabs.length - 1];
+              let tabScreenX = draggedTab.boxObject.screenX;
+              let translateX = screenX - draggedTab._dragData.screenX;
+              if (!pinned)
+                translateX += this.mTabstrip.scrollPosition - draggedTab._dragData.scrollX;
+              let leftBound = leftTab.boxObject.screenX - tabScreenX;
+              let rightBound = (rightTab.boxObject.screenX + rightTab.boxObject.width) -
+                               (tabScreenX + tabWidth);
+              translateX = Math.max(translateX, leftBound);
+              translateX = Math.min(translateX, rightBound);
+
+              requestAnimationFrame(() => {
+                draggedTab.style.transform = "translateX(" + translateX + "px)";
+                draggedTab._dragData.translateX = translateX;
+              });
+
+              // Determine what tab we're dragging over.
+              // * Point of reference is the center of the dragged tab. If that
+              //   point touches a background tab, the dragged tab would take that
+              //   tab's position when dropped.
+              // * We're doing a binary search in order to reduce the amount of
+              //   tabs we need to check.
+
+              let tabCenter = tabScreenX + translateX + tabWidth / 2;
+              let low = 0;
+              let high = tabs.length - 1;
+              while (low <= high) {
+                let mid = Math.floor((low + high) / 2);
+                if (tabs[mid] == draggedTab &&
+                    ++mid > high)
+                  break;
+                let boxObject = tabs[mid].boxObject;
+                screenX = boxObject.screenX + getTabShift(tabs[mid], oldIndex);
+                if (screenX > tabCenter) {
+                  high = mid - 1;
+                } else if (screenX + boxObject.width < tabCenter) {
+                  low = mid + 1;
+                } else {
+                  newIndex = tabs[mid]._tPos;
+                  break;
+                }
+              }
+            });
+
+            if (newIndex >= oldIndex)
+              newIndex++;
+            if (newIndex < 0 || newIndex == oldIndex)
+              return;
+            draggedTab._dragData.animDropIndex = newIndex;
+
+            // Shift background tabs to leave a gap where the dragged tab
+            // would currently be dropped.
+
+            requestAnimationFrame(() => {
+              for (let tab of tabs) {
+                if (tab != draggedTab) {
+                  let shift = getTabShift(tab, newIndex);
+                  tab.style.transform = shift ? "translateX(" + shift + "px)" : "";
+                }
+              }
+            });
+
+            function getTabShift(tab, dropIndex) {
+              if (tab._tPos < draggedTab._tPos && tab._tPos >= dropIndex)
+                return rtl ? -tabWidth : tabWidth;
+              if (tab._tPos > draggedTab._tPos && tab._tPos < dropIndex)
+                return rtl ? tabWidth : -tabWidth;
+              return 0;
+            }
+          })();
         ]]></body>
       </method>
 
       <method name="_finishAnimateTabMove">
         <body><![CDATA[
           if (this.getAttribute("movingtab") != "true")
             return;