Bug 1358816: Part 1 - Avoid layout flushes during tab move animations. r?florian
MozReview-Commit-ID: C4GidGfhgxB
--- 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;