commit 446d2cfd65488524730e14ea4d16d58d58b4ab13
parent dce014621e7f3de5de95ca075424d01026949b65
Author: Sandor Molnar <smolnar@mozilla.com>
Date: Tue, 21 Oct 2025 22:16:21 +0300
Revert "Bug 1983124 - Stack dragged tabs when dragging multiple tabs r=tabbrowser-reviewers,desktop-theme-reviewers,sclements,sthompson" for causing bc failures @ browser_tabdetach.js
This reverts commit 46ab4c9b906cc0663886c38171c2801c8d764f7e.
Diffstat:
9 files changed, 15 insertions(+), 1411 deletions(-)
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
@@ -1065,11 +1065,6 @@ pref("browser.tabs.dragDrop.expandGroup.delayMS", 350);
pref("browser.tabs.dragDrop.selectTab.delayMS", 350);
pref("browser.tabs.dragDrop.pinInteractionCue.delayMS", 500);
pref("browser.tabs.dragDrop.moveOverThresholdPercent", 80);
-#ifdef NIGHTLY_BUILD
-pref("browser.tabs.dragDrop.multiselectStacking", true);
-#else
-pref("browser.tabs.dragDrop.multiselectStacking", false);
-#endif
pref("browser.tabs.firefox-view.logLevel", "Warn");
diff --git a/browser/base/content/browser-main.js b/browser/base/content/browser-main.js
@@ -20,7 +20,6 @@
Services.scriptloader.loadSubScript("chrome://browser/content/browser-customtitlebar.js", this);
Services.scriptloader.loadSubScript("chrome://browser/content/browser-unified-extensions.js", this);
Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser/drag-and-drop.js", this);
- Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser/tab-stacking.js", this);
Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser/tab.js", this);
Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser/tabbrowser.js", this);
Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser/tabgroup.js", this);
diff --git a/browser/components/tabbrowser/content/drag-and-drop.js b/browser/components/tabbrowser/content/drag-and-drop.js
@@ -118,12 +118,12 @@
!draggedTab._dragData.fromTabList
) {
ind.hidden = true;
+
if (this.#isAnimatingMoveTogetherSelectedTabs()) {
// Wait for moving selected tabs together animation to finish.
return;
}
this.finishMoveTogetherSelectedTabs(draggedTab);
- this._updateTabStylesOnDrag(draggedTab, dropEffect);
if (dropEffect == "move") {
this.#setMovingTabMode(true);
@@ -491,7 +491,6 @@
}
} else {
moveTabs();
- this._tabbrowserTabs._notifyBackgroundTab(movingTabs.at(-1));
}
}
} else if (isTabGroupLabel(draggedTab)) {
@@ -1134,11 +1133,13 @@
expandGroupOnDrop: collapseTabGroupDuringDrag,
};
if (this._rtlMode) {
- // Reverse order to handle positioning in `_updateTabStylesOnDrag`
+ // Reverse order to handle positioning in `updateTabStylesOnDrag`
// and animation in `_animateTabMove`
tab._dragData.movingTabs.reverse();
}
+ this._updateTabStylesOnDrag(tab, event);
+
if (isMovingInTabStrip) {
this.#setMovingTabMode(true);
@@ -1166,13 +1167,7 @@
This function updates the position and widths of elements affected by this layout shift
when the tab is first selected to be dragged.
*/
- _updateTabStylesOnDrag(tab, dropEffect) {
- let tabStripItemElement = elementToMove(tab);
- tabStripItemElement.style.pointerEvents =
- dropEffect == "copy" ? "auto" : "";
- if (tabStripItemElement.hasAttribute("dragtarget")) {
- return;
- }
+ _updateTabStylesOnDrag(tab) {
let isPinned = tab.pinned;
let numPinned = gBrowser.pinnedTabCount;
let allTabs = this._tabbrowserTabs.ariaFocusableItems;
@@ -1229,9 +1224,7 @@
}
// Prevent flex rules from resizing non dragged tabs while the dragged
// tabs are positioned absolutely
- if (tabRect.width) {
- t.style.maxWidth = tabRect.width + "px";
- }
+ t.style.maxWidth = tabRect.width + "px";
// Prevent non-moving tab strip items from performing any animations
// at the very beginning of the drag operation; this prevents them
// from appearing to move while the dragged tabs are positioned absolutely
@@ -1256,6 +1249,7 @@
// Use .tab-group-label-container or .tabbrowser-tab for size/position
// calculations.
+ let tabStripItemElement = elementToMove(tab);
let rect =
window.windowUtils.getBoundsWithoutFlushing(tabStripItemElement);
// Vertical tabs live under the #sidebar-main element which gets animated and has a
diff --git a/browser/components/tabbrowser/content/tab-stacking.js b/browser/components/tabbrowser/content/tab-stacking.js
@@ -1,1245 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-// Wrap in a block to prevent leaking to window scope.
-{
- const isTab = element => gBrowser.isTab(element);
- const isTabGroupLabel = element => gBrowser.isTabGroupLabel(element);
-
- /**
- * The elements in the tab strip from `this.ariaFocusableItems` that contain
- * logical information are:
- *
- * - <tab> (.tabbrowser-tab)
- * - <tab-group> label element (.tab-group-label)
- *
- * The elements in the tab strip that contain the space inside of the <tabs>
- * element are:
- *
- * - <tab> (.tabbrowser-tab)
- * - <tab-group> label element wrapper (.tab-group-label-container)
- *
- * When working with tab strip items, if you need logical information, you
- * can get it directly, e.g. `element.elementIndex` or `element._tPos`. If
- * you need spatial information like position or dimensions, then you should
- * call this function. For example, `elementToMove(element).getBoundingClientRect()`
- * or `elementToMove(element).style.top`.
- *
- * @param {MozTabbrowserTab|typeof MozTabbrowserTabGroup.labelElement} element
- * @returns {MozTabbrowserTab|vbox}
- */
- const elementToMove = element => {
- if (isTab(element)) {
- return element;
- }
- if (isTabGroupLabel(element)) {
- return element.closest(".tab-group-label-container");
- }
- throw new Error(`Element "${element.tagName}" is not expected to move`);
- };
-
- window.TabStacking = class extends window.TabDragAndDrop {
- constructor(tabbrowserTabs) {
- super(tabbrowserTabs);
- }
-
- /**
- * Move together all selected tabs around the tab in param.
- */
- _moveTogetherSelectedTabs(tab) {
- let selectedTabs = gBrowser.selectedTabs;
- let tabIndex = selectedTabs.indexOf(tab);
- if (selectedTabs.some(t => t.pinned != tab.pinned)) {
- throw new Error(
- "Cannot move together a mix of pinned and unpinned tabs."
- );
- }
- let isGrid = this._isContainerVerticalPinnedGrid(tab);
- let animate = !gReduceMotion;
-
- tab._moveTogetherSelectedTabsData = {
- finished: !animate,
- };
-
- tab.toggleAttribute("multiselected-move-together", true);
-
- let addAnimationData = movingTab => {
- movingTab._moveTogetherSelectedTabsData = {
- translateX: 0,
- translateY: 0,
- animate: true,
- };
- movingTab.toggleAttribute("multiselected-move-together", true);
-
- let postTransitionCleanup = () => {
- movingTab._moveTogetherSelectedTabsData.animate = false;
- };
- if (gReduceMotion) {
- postTransitionCleanup();
- } else {
- let onTransitionEnd = transitionendEvent => {
- if (
- transitionendEvent.propertyName != "transform" ||
- transitionendEvent.originalTarget != movingTab
- ) {
- return;
- }
- movingTab.removeEventListener("transitionend", onTransitionEnd);
- postTransitionCleanup();
- };
-
- movingTab.addEventListener("transitionend", onTransitionEnd);
- }
-
- let tabRect = tab.getBoundingClientRect();
- let movingTabRect = movingTab.getBoundingClientRect();
- movingTab._moveTogetherSelectedTabsData.translateX =
- tabRect.x - movingTabRect.x;
- movingTab._moveTogetherSelectedTabsData.translateY =
- tabRect.y - movingTabRect.y;
- };
-
- let selectedIndices = selectedTabs.map(t => t.elementIndex);
- let currentIndex = 0;
- let draggedRect = tab.getBoundingClientRect();
- let translateX = 0;
- let translateY = 0;
-
- // The currentIndex represents the indexes for all visible tab strip items after the
- // selected tabs have moved together. These values make the math in _animateTabMove and
- // _animateExpandedPinnedTabMove possible and less prone to edge cases when dragging
- // multiple tabs.
- for (let unmovingTab of this._tabbrowserTabs.ariaFocusableItems) {
- if (unmovingTab.multiselected) {
- unmovingTab.currentIndex = tab.elementIndex;
- // Skip because this multiselected tab should
- // be shifted towards the dragged Tab.
- continue;
- }
- if (unmovingTab.elementIndex > selectedIndices[currentIndex]) {
- while (
- selectedIndices[currentIndex + 1] &&
- unmovingTab.elementIndex > selectedIndices[currentIndex + 1]
- ) {
- let currentRect = selectedTabs
- .find(t => t.elementIndex == selectedIndices[currentIndex])
- .getBoundingClientRect();
- // For everything but the grid, we need to work out the shift required based
- // on the size of the tabs being dragged together.
- translateY -= currentRect.height;
- translateX -= currentRect.width;
- currentIndex++;
- }
-
- // Find the new index of the tab once selected tabs have moved together to use
- // for positioning and animation
- let isAfterDraggedTab =
- unmovingTab.elementIndex - currentIndex > tab.elementIndex;
- let newIndex = isAfterDraggedTab
- ? unmovingTab.elementIndex - currentIndex
- : unmovingTab.elementIndex - currentIndex - 1;
- let newTranslateX = isAfterDraggedTab
- ? translateX
- : translateX - draggedRect.width;
- let newTranslateY = isAfterDraggedTab
- ? translateY
- : translateY - draggedRect.height;
- unmovingTab.currentIndex = newIndex;
- unmovingTab._moveTogetherSelectedTabsData = {
- translateX: 0,
- translateY: 0,
- };
- if (isGrid) {
- // For the grid, use the position of the tab with the old index to dictate the
- // translation needed for the background tab with the new index to move there.
- let unmovingTabRect = unmovingTab.getBoundingClientRect();
- let oldTabRect =
- this._tabbrowserTabs.ariaFocusableItems[
- newIndex
- ].getBoundingClientRect();
- unmovingTab._moveTogetherSelectedTabsData.translateX =
- oldTabRect.x - unmovingTabRect.x;
- unmovingTab._moveTogetherSelectedTabsData.translateY =
- oldTabRect.y - unmovingTabRect.y;
- } else if (this._tabbrowserTabs.verticalMode) {
- unmovingTab._moveTogetherSelectedTabsData.translateY =
- newTranslateY;
- } else {
- unmovingTab._moveTogetherSelectedTabsData.translateX =
- newTranslateX;
- }
- } else {
- unmovingTab.currentIndex = unmovingTab.elementIndex;
- }
- }
-
- // Animate left or top selected tabs
- for (let i = 0; i < tabIndex; i++) {
- let movingTab = selectedTabs[i];
- if (animate) {
- addAnimationData(movingTab);
- } else {
- gBrowser.moveTabBefore(movingTab, tab);
- }
- }
-
- // Animate right or bottom selected tabs
- for (let i = selectedTabs.length - 1; i > tabIndex; i--) {
- let movingTab = selectedTabs[i];
- if (animate) {
- addAnimationData(movingTab);
- } else {
- gBrowser.moveTabAfter(movingTab, tab);
- }
- }
-
- // Slide the relevant tabs to their new position.
- // non-moving tabs adjust for RTL
- for (let item of this._tabbrowserTabs.ariaFocusableItems) {
- if (
- !tab._dragData.movingTabsSet.has(item) &&
- (item._moveTogetherSelectedTabsData?.translateX ||
- item._moveTogetherSelectedTabsData?.translateY) &&
- ((item.pinned && tab.pinned) || (!item.pinned && !tab.pinned))
- ) {
- let element = elementToMove(item);
- if (isGrid) {
- element.style.transform = `translate(${(this._rtlMode ? -1 : 1) * item._moveTogetherSelectedTabsData.translateX}px, ${item._moveTogetherSelectedTabsData.translateY}px)`;
- } else if (this._tabbrowserTabs.verticalMode) {
- element.style.transform = `translateY(${item._moveTogetherSelectedTabsData.translateY}px)`;
- } else {
- element.style.transform = `translateX(${(this._rtlMode ? -1 : 1) * item._moveTogetherSelectedTabsData.translateX}px)`;
- }
- }
- }
- // moving tabs don't adjust for RTL
- for (let item of selectedTabs) {
- if (
- item._moveTogetherSelectedTabsData?.translateX ||
- item._moveTogetherSelectedTabsData?.translateY
- ) {
- let element = elementToMove(item);
- element.style.transform = `translate(${item._moveTogetherSelectedTabsData.translateX}px, ${item._moveTogetherSelectedTabsData.translateY}px)`;
- }
- }
- }
-
- finishMoveTogetherSelectedTabs(tab) {
- if (
- !tab._moveTogetherSelectedTabsData ||
- tab._moveTogetherSelectedTabsData.finished
- ) {
- return;
- }
-
- tab._moveTogetherSelectedTabsData.finished = true;
-
- let selectedTabs = gBrowser.selectedTabs;
- let tabIndex = selectedTabs.indexOf(tab);
-
- // Moving left or top tabs
- for (let i = 0; i < tabIndex; i++) {
- gBrowser.moveTabBefore(selectedTabs[i], tab);
- }
-
- // Moving right or bottom tabs
- for (let i = selectedTabs.length - 1; i > tabIndex; i--) {
- gBrowser.moveTabAfter(selectedTabs[i], tab);
- }
-
- for (let item of this._tabbrowserTabs.ariaFocusableItems) {
- delete item._moveTogetherSelectedTabsData;
- item = elementToMove(item);
- item.style.transform = "";
- item.removeAttribute("multiselected-move-together");
- }
- }
-
- /* In order to to drag tabs between both the pinned arrowscrollbox (pinned tab container)
- and unpinned arrowscrollbox (tabbrowser-arrowscrollbox), the dragged tabs need to be
- positioned absolutely. This results in a shift in the layout, filling the empty space.
- This function updates the position and widths of elements affected by this layout shift
- when the tab is first selected to be dragged.
- */
- _updateTabStylesOnDrag(tab, dropEffect) {
- let tabStripItemElement = elementToMove(tab);
- tabStripItemElement.style.pointerEvents =
- dropEffect == "copy" ? "auto" : "";
- if (tabStripItemElement.hasAttribute("dragtarget")) {
- return;
- }
- let isPinned = tab.pinned;
- let allTabs = this._tabbrowserTabs.ariaFocusableItems;
- let isGrid = this._isContainerVerticalPinnedGrid(tab);
- let periphery = document.getElementById(
- "tabbrowser-arrowscrollbox-periphery"
- );
-
- if (isPinned && this._tabbrowserTabs.verticalMode) {
- this._tabbrowserTabs.pinnedTabsContainer.setAttribute("dragActive", "");
- }
-
- // Ensure tab containers retain size while tabs are dragged out of the layout
- let pinnedRect = window.windowUtils.getBoundsWithoutFlushing(
- this._tabbrowserTabs.pinnedTabsContainer.scrollbox
- );
- let pinnedContainerRect = window.windowUtils.getBoundsWithoutFlushing(
- this._tabbrowserTabs.pinnedTabsContainer
- );
- let unpinnedRect = window.windowUtils.getBoundsWithoutFlushing(
- this._tabbrowserTabs.arrowScrollbox.scrollbox
- );
- let tabContainerRect = window.windowUtils.getBoundsWithoutFlushing(
- this._tabbrowserTabs
- );
-
- if (this._tabbrowserTabs.pinnedTabsContainer.firstChild) {
- this._tabbrowserTabs.pinnedTabsContainer.scrollbox.style.height =
- pinnedRect.height + "px";
- // Use "minHeight" so as not to interfere with user preferences for height.
- this._tabbrowserTabs.pinnedTabsContainer.style.minHeight =
- pinnedContainerRect.height + "px";
- this._tabbrowserTabs.pinnedTabsContainer.scrollbox.style.width =
- pinnedRect.width + "px";
- }
- this._tabbrowserTabs.arrowScrollbox.scrollbox.style.height =
- unpinnedRect.height + "px";
- this._tabbrowserTabs.arrowScrollbox.scrollbox.style.width =
- unpinnedRect.width + "px";
-
- let { movingTabs, movingTabsSet, expandGroupOnDrop } = tab._dragData;
- /** @type {(MozTabbrowserTab|typeof MozTabbrowserTabGroup.labelElement)[]} */
- let suppressTransitionsFor = [];
- /** @type {Map<MozTabbrowserTab, DOMRect>} */
-
- const tabsOrigBounds = new Map();
-
- for (let t of allTabs) {
- t = elementToMove(t);
- let tabRect = window.windowUtils.getBoundsWithoutFlushing(t);
-
- // record where all the tabs were before we position:absolute the moving tabs
- tabsOrigBounds.set(t, tabRect);
-
- // Prevent flex rules from resizing non dragged tabs while the dragged
- // tabs are positioned absolutely
- t.style.maxWidth = tabRect.width + "px";
- // Prevent non-moving tab strip items from performing any animations
- // at the very beginning of the drag operation; this prevents them
- // from appearing to move while the dragged tabs are positioned absolutely
- let isTabInCollapsingGroup = expandGroupOnDrop && t.group == tab.group;
- if (!movingTabsSet.has(t) && !isTabInCollapsingGroup) {
- t.style.transition = "none";
- suppressTransitionsFor.push(t);
- }
- }
-
- if (suppressTransitionsFor.length) {
- window
- .promiseDocumentFlushed(() => {})
- .then(() => {
- window.requestAnimationFrame(() => {
- for (let t of suppressTransitionsFor) {
- t.style.transition = "";
- }
- });
- });
- }
-
- // Use .tab-group-label-container or .tabbrowser-tab for size/position
- // calculations.
- let rect =
- window.windowUtils.getBoundsWithoutFlushing(tabStripItemElement);
- // Vertical tabs live under the #sidebar-main element which gets animated and has a
- // transform style property, making it the containing block for all its descendants.
- // Position:absolute elements need to account for this when updating position using
- // other measurements whose origin is the viewport or documentElement's 0,0
- let movingTabsOffsetX = window.windowUtils.getBoundsWithoutFlushing(
- tabStripItemElement.offsetParent
- ).x;
-
- for (let movingTab of movingTabs) {
- movingTab = elementToMove(movingTab);
- movingTab.style.width = rect.width + "px";
- // "dragtarget" contains the following rules which must only be set AFTER the above
- // elements have been adjusted. {z-index: 3 !important, position: absolute !important}
- movingTab.setAttribute("dragtarget", "");
- if (isTabGroupLabel(tab)) {
- if (this._tabbrowserTabs.verticalMode) {
- movingTab.style.top = rect.top - unpinnedRect.top + "px";
- } else {
- movingTab.style.left = rect.left - movingTabsOffsetX + "px";
- movingTab.style.height = rect.height + "px";
- }
- } else if (isGrid) {
- movingTab.style.top = rect.top - pinnedRect.top + "px";
- movingTab.style.left = rect.left + "px";
- } else if (this._tabbrowserTabs.verticalMode) {
- movingTab.style.top = rect.top - tabContainerRect.top + "px";
- } else if (this._rtlMode) {
- movingTab.style.left = rect.left + "px";
- } else {
- movingTab.style.left = rect.left + "px";
- }
- }
-
- if (movingTabs.length == 2) {
- tab.setAttribute("small-stack", "");
- } else if (movingTabs.length > 2) {
- tab.setAttribute("big-stack", "");
- }
-
- if (
- !isPinned &&
- this._tabbrowserTabs.arrowScrollbox.hasAttribute("overflowing")
- ) {
- if (this._tabbrowserTabs.verticalMode) {
- periphery.style.marginBlockStart = rect.height + "px";
- } else {
- periphery.style.marginInlineStart = rect.width + "px";
- }
- } else if (
- isPinned &&
- this._tabbrowserTabs.pinnedTabsContainer.hasAttribute("overflowing")
- ) {
- let pinnedPeriphery = document.createXULElement("hbox");
- pinnedPeriphery.id = "pinned-tabs-container-periphery";
- pinnedPeriphery.style.width = "100%";
- pinnedPeriphery.style.marginBlockStart = rect.height + "px";
- this._tabbrowserTabs.pinnedTabsContainer.appendChild(pinnedPeriphery);
- }
-
- let setElPosition = el => {
- let origBounds = tabsOrigBounds.get(el);
- if (!origBounds) {
- // No bounds saved for this tab
- return;
- }
- // We use getBoundingClientRect and force a reflow as we need to know their new positions
- // after making the moving tabs position:absolute
- let newBounds = el.getBoundingClientRect();
- let shiftX = origBounds.x - newBounds.x;
- let shiftY = origBounds.y - newBounds.y;
-
- if (!this._tabbrowserTabs.verticalMode || isGrid) {
- el.style.left = shiftX + "px";
- }
- if (this._tabbrowserTabs.verticalMode) {
- el.style.top = shiftY + "px";
- }
- };
-
- // Update tabs in the same container as the dragged tabs so as not
- // to fill the space when the dragged tabs become absolute
- for (let t of allTabs) {
- t = elementToMove(t);
- if (!t.hasAttribute("dragtarget")) {
- setElPosition(t);
- }
- }
-
- // Handle the new tab button filling the space when the dragged tab
- // position becomes absolute
- if (!this._tabbrowserTabs.overflowing && !isPinned) {
- if (this._tabbrowserTabs.verticalMode) {
- periphery.style.top = `${rect.height}px`;
- } else if (this._rtlMode) {
- periphery.style.left = `${-rect.width}px`;
- } else {
- periphery.style.left = `${rect.width}px`;
- }
- }
- }
-
- // eslint-disable-next-line complexity
- _animateTabMove(event) {
- let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
- let dragData = draggedTab._dragData;
- let movingTabs = dragData.movingTabs;
- let movingTabsSet = dragData.movingTabsSet;
-
- dragData.animLastScreenPos ??= this._tabbrowserTabs.verticalMode
- ? dragData.screenY
- : dragData.screenX;
- let screen = this._tabbrowserTabs.verticalMode
- ? event.screenY
- : event.screenX;
- if (screen == dragData.animLastScreenPos) {
- return;
- }
- let screenForward = screen > dragData.animLastScreenPos;
- dragData.animLastScreenPos = screen;
-
- this._clearDragOverGroupingTimer();
-
- let isPinned = draggedTab.pinned;
- let numPinned = gBrowser.pinnedTabCount;
- let allTabs = this._tabbrowserTabs.ariaFocusableItems;
- let tabs = allTabs.slice(
- isPinned ? 0 : numPinned,
- isPinned ? numPinned : undefined
- );
-
- if (this._rtlMode) {
- tabs.reverse();
- }
-
- let bounds = ele => window.windowUtils.getBoundsWithoutFlushing(ele);
- let logicalForward = screenForward != this._rtlMode;
- let screenAxis = this._tabbrowserTabs.verticalMode
- ? "screenY"
- : "screenX";
- let size = this._tabbrowserTabs.verticalMode ? "height" : "width";
- let translateAxis = this._tabbrowserTabs.verticalMode
- ? "translateY"
- : "translateX";
- let translateX = event.screenX - dragData.screenX;
- let translateY = event.screenY - dragData.screenY;
-
- // Move the dragged tab based on the mouse position.
- let periphery = document.getElementById(
- "tabbrowser-arrowscrollbox-periphery"
- );
- let endEdge = ele => ele[screenAxis] + bounds(ele)[size];
- let endScreen = endEdge(draggedTab);
- let startScreen = draggedTab[screenAxis];
- let { width: tabWidth, height: tabHeight } = bounds(
- elementToMove(draggedTab)
- );
- let tabSize = this._tabbrowserTabs.verticalMode ? tabHeight : tabWidth;
- let shiftSize = tabSize;
- dragData.tabWidth = tabWidth;
- dragData.tabHeight = tabHeight;
- dragData.translateX = translateX;
- dragData.translateY = translateY;
- let translate = screen - dragData[screenAxis];
-
- // Constrain the range over which the moving tabs can move between the edge of the tabstrip and periphery.
- // Add 1 to periphery so we don't overlap it.
- let startBound = this._rtlMode
- ? endEdge(periphery) + 1 - startScreen
- : this._tabbrowserTabs[screenAxis] - startScreen;
- let endBound = this._rtlMode
- ? endEdge(this._tabbrowserTabs) - endScreen
- : periphery[screenAxis] - 1 - endScreen;
- translate = Math.min(Math.max(translate, startBound), endBound);
-
- // Center the tab under the cursor if the tab is not under the cursor while dragging
- let draggedTabScreenAxis = draggedTab[screenAxis] + translate;
- if (
- (screen < draggedTabScreenAxis ||
- screen > draggedTabScreenAxis + tabSize) &&
- draggedTabScreenAxis + tabSize < endBound &&
- draggedTabScreenAxis > startBound
- ) {
- translate = screen - draggedTab[screenAxis] - tabSize / 2;
- // Ensure, after the above calculation, we are still within bounds
- translate = Math.min(Math.max(translate, startBound), endBound);
- }
-
- if (!gBrowser.pinnedTabCount && !this._dragToPinPromoCard.shouldRender) {
- let pinnedDropIndicatorMargin = parseFloat(
- window.getComputedStyle(this._pinnedDropIndicator).marginInline
- );
- this._checkWithinPinnedContainerBounds({
- firstMovingTabScreen: startScreen,
- lastMovingTabScreen: endScreen,
- pinnedTabsStartEdge: this._rtlMode
- ? endEdge(this._tabbrowserTabs.arrowScrollbox) +
- pinnedDropIndicatorMargin
- : this._tabbrowserTabs[screenAxis],
- pinnedTabsEndEdge: this._rtlMode
- ? endEdge(this._tabbrowserTabs)
- : this._tabbrowserTabs.arrowScrollbox[screenAxis] -
- pinnedDropIndicatorMargin,
- translate,
- draggedTab,
- });
- }
-
- for (let item of movingTabs) {
- item = elementToMove(item);
- item.style.transform = `${translateAxis}(${translate}px)`;
- }
-
- dragData.translatePos = translate;
-
- tabs = tabs.filter(t => !movingTabsSet.has(t) || t == draggedTab);
-
- /**
- * When the `draggedTab` is just starting to move, the `draggedTab` is in
- * its original location and the `dropElementIndex == draggedTab.elementIndex`.
- * Any tabs or tab group labels passed in as `item` will result in a 0 shift
- * because all of those items should also continue to appear in their original
- * locations.
- *
- * Once the `draggedTab` is more "backward" in the tab strip than its original
- * position, any tabs or tab group labels between the `draggedTab`'s original
- * `elementIndex` and the current `dropElementIndex` should shift "forward"
- * out of the way of the dragging tabs.
- *
- * When the `draggedTab` is more "forward" in the tab strip than its original
- * position, any tabs or tab group labels between the `draggedTab`'s original
- * `elementIndex` and the current `dropElementIndex` should shift "backward"
- * out of the way of the dragging tabs.
- *
- * @param {MozTabbrowserTab|MozTabbrowserTabGroup.label} item
- * @param {number} dropElementIndex
- * @returns {number}
- */
- let getTabShift = (item, dropElementIndex) => {
- if (!item?.currentIndex) {
- item.currentIndex = item.elementIndex;
- }
- if (
- item.currentIndex < draggedTab.elementIndex &&
- item.currentIndex >= dropElementIndex
- ) {
- return this._rtlMode ? -shiftSize : shiftSize;
- }
- if (
- item.currentIndex > draggedTab.elementIndex &&
- item.currentIndex < dropElementIndex
- ) {
- return this._rtlMode ? shiftSize : -shiftSize;
- }
- return 0;
- };
-
- let oldDropElementIndex =
- dragData.animDropElementIndex ?? draggedTab.elementIndex;
-
- /**
- * Returns the higher % by which one element overlaps another
- * in the tab strip.
- *
- * When element 1 is further forward in the tab strip:
- *
- * p1 p2 p1+s1 p2+s2
- * | | | |
- * ---------------------------------
- * ========================
- * s1
- * ===================
- * s2
- * ==========
- * overlap
- *
- * When element 2 is further forward in the tab strip:
- *
- * p2 p1 p2+s2 p1+s1
- * | | | |
- * ---------------------------------
- * ========================
- * s2
- * ===================
- * s1
- * ==========
- * overlap
- *
- * @param {number} p1
- * Position (x or y value in screen coordinates) of element 1.
- * @param {number} s1
- * Size (width or height) of element 1.
- * @param {number} p2
- * Position (x or y value in screen coordinates) of element 2.
- * @param {number} s2
- * Size (width or height) of element 1.
- * @returns {number}
- * Percent between 0.0 and 1.0 (inclusive) of element 1 or element 2
- * that is overlapped by the other element. If the elements have
- * different sizes, then this returns the larger overlap percentage.
- */
- function greatestOverlap(p1, s1, p2, s2) {
- let overlapSize;
- if (p1 < p2) {
- // element 1 starts first
- overlapSize = p1 + s1 - p2;
- } else {
- // element 2 starts first
- overlapSize = p2 + s2 - p1;
- }
-
- // No overlap if size is <= 0
- if (overlapSize <= 0) {
- return 0;
- }
-
- // Calculate the overlap fraction from each element's perspective.
- let overlapPercent = Math.max(overlapSize / s1, overlapSize / s2);
-
- return Math.min(overlapPercent, 1);
- }
-
- /**
- * Determine what tab/tab group label we're dragging over.
- *
- * When dragging right or downwards, the reference point for overlap is
- * the right or bottom edge of the most forward moving tab.
- *
- * When dragging left or upwards, the reference point for overlap is the
- * left or top edge of the most backward moving tab.
- *
- * @returns {Element|null}
- * The tab or tab group label that should be used to visually shift tab
- * strip elements out of the way of the dragged tab(s) during a drag
- * operation. Note: this is not used to determine where the dragged
- * tab(s) will be dropped, it is only used for visual animation at this
- * time.
- */
- let getOverlappedElement = () => {
- let point = (screenForward ? endScreen : startScreen) + translate;
- 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 element = tabs[mid];
- let elementForSize = elementToMove(element);
- screen =
- elementForSize[screenAxis] +
- getTabShift(element, oldDropElementIndex);
-
- if (screen > point) {
- high = mid - 1;
- } else if (screen + bounds(elementForSize)[size] < point) {
- low = mid + 1;
- } else {
- return element;
- }
- }
- return null;
- };
-
- let dropElement = getOverlappedElement();
-
- let newDropElementIndex;
- if (dropElement) {
- newDropElementIndex =
- dropElement?.currentIndex ?? dropElement.elementIndex;
- } else {
- // When the dragged element(s) moves past a tab strip item, the dragged
- // element's leading edge starts dragging over empty space, resulting in
- // no overlapping `dropElement`. In these cases, try to fall back to the
- // previous animation drop element index to avoid unstable animations
- // (tab strip items snapping back and forth to shift out of the way of
- // the dragged element(s)).
- newDropElementIndex = oldDropElementIndex;
-
- // We always want to have a `dropElement` so that we can determine where to
- // logically drop the dragged element(s).
- //
- // It's tempting to set `dropElement` to
- // `this.ariaFocusableItems.at(oldDropElementIndex)`, and that is correct
- // for most cases, but there are edge cases:
- //
- // 1) the drop element index range needs to be one larger than the number of
- // items that can move in the tab strip. The simplest example is when all
- // tabs are ungrouped and unpinned: for 5 tabs, the drop element index needs
- // to be able to go from 0 (become the first tab) to 5 (become the last tab).
- // `this.ariaFocusableItems.at(5)` would be `undefined` when dragging to the
- // end of the tab strip. In this specific case, it works to fall back to
- // setting the drop element to the last tab.
- //
- // 2) the `elementIndex` values of the tab strip items do not change during
- // the drag operation. When dragging the last tab or multiple tabs at the end
- // of the tab strip, having `dropElement` fall back to the last tab makes the
- // drop element one of the moving tabs. This can have some unexpected behavior
- // if not careful. Falling back to the last tab that's not moving (instead of
- // just the last tab) helps ensure that `dropElement` is always a stable target
- // to drop next to.
- //
- // 3) all of the elements in the tab strip are moving, in which case there can't
- // be a drop element and it should stay `undefined`.
- //
- // 4) we just started dragging and the `oldDropElementIndex` has its default
- // valuë of `movingTabs[0].elementIndex`. In this case, the drop element
- // shouldn't be a moving tab, so keep it `undefined`.
- let lastPossibleDropElement = this._rtlMode
- ? tabs.find(t => t != draggedTab)
- : tabs.findLast(t => t != draggedTab);
- let maxElementIndexForDropElement =
- lastPossibleDropElement?.currentIndex ??
- lastPossibleDropElement?.elementIndex;
- if (Number.isInteger(maxElementIndexForDropElement)) {
- let index = Math.min(
- oldDropElementIndex,
- maxElementIndexForDropElement
- );
- let oldDropElementCandidate = this._tabbrowserTabs.ariaFocusableItems
- .filter(t => !movingTabsSet.has(t) || t == draggedTab)
- .at(index);
- if (!movingTabsSet.has(oldDropElementCandidate)) {
- dropElement = oldDropElementCandidate;
- }
- }
- }
-
- let moveOverThreshold;
- let overlapPercent;
- let dropBefore;
- if (dropElement) {
- let dropElementForOverlap = elementToMove(dropElement);
-
- let dropElementScreen = dropElementForOverlap[screenAxis];
- let dropElementPos =
- dropElementScreen + getTabShift(dropElement, oldDropElementIndex);
- let dropElementSize = bounds(dropElementForOverlap)[size];
- let firstMovingTabPos = startScreen + translate;
- overlapPercent = greatestOverlap(
- firstMovingTabPos,
- shiftSize,
- dropElementPos,
- dropElementSize
- );
-
- moveOverThreshold = gBrowser._tabGroupsEnabled
- ? Services.prefs.getIntPref(
- "browser.tabs.dragDrop.moveOverThresholdPercent"
- ) / 100
- : 0.5;
- moveOverThreshold = Math.min(1, Math.max(0, moveOverThreshold));
- let shouldMoveOver = overlapPercent > moveOverThreshold;
- if (logicalForward && shouldMoveOver) {
- newDropElementIndex++;
- } else if (!logicalForward && !shouldMoveOver) {
- newDropElementIndex++;
- if (newDropElementIndex > oldDropElementIndex) {
- // FIXME: Not quite sure what's going on here, but this check
- // prevents jittery back-and-forth movement of background tabs
- // in certain cases.
- newDropElementIndex = oldDropElementIndex;
- }
- }
-
- // Recalculate the overlap with the updated drop index for when the
- // drop element moves over.
- dropElementPos =
- dropElementScreen + getTabShift(dropElement, newDropElementIndex);
- overlapPercent = greatestOverlap(
- firstMovingTabPos,
- shiftSize,
- dropElementPos,
- dropElementSize
- );
- dropBefore = firstMovingTabPos < dropElementPos;
- if (this._rtlMode) {
- dropBefore = !dropBefore;
- }
-
- // If dragging a group over another group, don't make it look like it is
- // possible to drop the dragged group inside the other group.
- if (
- isTabGroupLabel(draggedTab) &&
- dropElement?.group &&
- (!dropElement.group.collapsed ||
- (dropElement.group.collapsed && dropElement.group.hasActiveTab))
- ) {
- let overlappedGroup = dropElement.group;
-
- if (isTabGroupLabel(dropElement)) {
- dropBefore = true;
- newDropElementIndex =
- dropElement?.currentIndex ?? dropElement.elementIndex;
- } else {
- dropBefore = false;
- let lastVisibleTabInGroup = overlappedGroup.tabs.findLast(
- tab => tab.visible
- );
- newDropElementIndex =
- (lastVisibleTabInGroup?.currentIndex ??
- lastVisibleTabInGroup.elementIndex) + 1;
- }
-
- dropElement = overlappedGroup;
- }
-
- // Constrain drop direction at the boundary between pinned and
- // unpinned tabs so that they don't mix together.
- let isOutOfBounds = isPinned
- ? dropElement.elementIndex >= numPinned
- : dropElement.elementIndex < numPinned;
- if (isOutOfBounds) {
- // Drop after last pinned tab
- dropElement = this._tabbrowserTabs.ariaFocusableItems[numPinned - 1];
- dropBefore = false;
- }
- }
-
- if (
- gBrowser._tabGroupsEnabled &&
- isTab(draggedTab) &&
- !isPinned &&
- (!numPinned || newDropElementIndex > numPinned)
- ) {
- let dragOverGroupingThreshold = 1 - moveOverThreshold;
- let groupingDelay = Services.prefs.getIntPref(
- "browser.tabs.dragDrop.createGroup.delayMS"
- );
-
- // When dragging tab(s) over an ungrouped tab, signal to the user
- // that dropping the tab(s) will create a new tab group.
- let shouldCreateGroupOnDrop =
- !movingTabsSet.has(dropElement) &&
- isTab(dropElement) &&
- !dropElement?.group &&
- overlapPercent > dragOverGroupingThreshold;
-
- // When dragging tab(s) over a collapsed tab group label, signal to the
- // user that dropping the tab(s) will add them to the group.
- let shouldDropIntoCollapsedTabGroup =
- isTabGroupLabel(dropElement) &&
- dropElement.group.collapsed &&
- overlapPercent > dragOverGroupingThreshold;
-
- if (shouldCreateGroupOnDrop) {
- this._dragOverGroupingTimer = setTimeout(() => {
- this._triggerDragOverGrouping(dropElement);
- dragData.shouldCreateGroupOnDrop = true;
- this._setDragOverGroupColor(dragData.tabGroupCreationColor);
- }, groupingDelay);
- } else if (shouldDropIntoCollapsedTabGroup) {
- this._dragOverGroupingTimer = setTimeout(() => {
- this._triggerDragOverGrouping(dropElement);
- dragData.shouldDropIntoCollapsedTabGroup = true;
- this._setDragOverGroupColor(dropElement.group.color);
- }, groupingDelay);
- } else {
- this._tabbrowserTabs.removeAttribute("movingtab-group");
- this._resetGroupTarget(
- document.querySelector("[dragover-groupTarget]")
- );
-
- delete dragData.shouldCreateGroupOnDrop;
- delete dragData.shouldDropIntoCollapsedTabGroup;
-
- // Default to dropping into `dropElement`'s tab group, if it exists.
- let dropElementGroup = dropElement?.group;
- let colorCode = dropElementGroup?.color;
-
- let lastUnmovingTabInGroup = dropElementGroup?.tabs.findLast(
- t => !movingTabsSet.has(t)
- );
- if (
- isTab(dropElement) &&
- dropElementGroup &&
- dropElement == lastUnmovingTabInGroup &&
- !dropBefore &&
- overlapPercent < dragOverGroupingThreshold
- ) {
- // Dragging tab over the last tab of a tab group, but not enough
- // for it to drop into the tab group. Drop it after the tab group instead.
- dropElement = dropElementGroup;
- colorCode = undefined;
- } else if (isTabGroupLabel(dropElement)) {
- if (dropBefore) {
- // Dropping right before the tab group.
- dropElement = dropElementGroup;
- colorCode = undefined;
- } else if (dropElementGroup.collapsed) {
- // Dropping right after the collapsed tab group.
- dropElement = dropElementGroup;
- colorCode = undefined;
- } else {
- // Dropping right before the first tab in the tab group.
- dropElement = dropElementGroup.tabs[0];
- dropBefore = true;
- }
- }
- this._setDragOverGroupColor(colorCode);
- this._tabbrowserTabs.toggleAttribute(
- "movingtab-addToGroup",
- colorCode
- );
- this._tabbrowserTabs.toggleAttribute("movingtab-ungroup", !colorCode);
- }
- }
-
- if (
- newDropElementIndex == oldDropElementIndex &&
- dropBefore == dragData.dropBefore &&
- dropElement == dragData.dropElement
- ) {
- return;
- }
-
- dragData.dropElement = dropElement;
- dragData.dropBefore = dropBefore;
- dragData.animDropElementIndex = newDropElementIndex;
-
- // Shift background tabs to leave a gap where the dragged tab
- // would currently be dropped.
- for (let item of tabs) {
- if (item == draggedTab) {
- continue;
- }
- let shift = getTabShift(item, newDropElementIndex);
- let transform = shift ? `${translateAxis}(${shift}px)` : "";
- item = elementToMove(item);
- item.style.transform = transform;
- }
- }
-
- _animateExpandedPinnedTabMove(event) {
- let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
- let dragData = draggedTab._dragData;
- let movingTabs = dragData.movingTabs;
-
- dragData.animLastScreenX ??= dragData.screenX;
- dragData.animLastScreenY ??= dragData.screenY;
-
- let screenX = event.screenX;
- let screenY = event.screenY;
-
- if (
- screenY == dragData.animLastScreenY &&
- screenX == dragData.animLastScreenX
- ) {
- return;
- }
-
- let tabs = this._tabbrowserTabs.visibleTabs.slice(
- 0,
- gBrowser.pinnedTabCount
- );
-
- dragData.animLastScreenY = screenY;
- dragData.animLastScreenX = screenX;
-
- let { width: tabWidth, height: tabHeight } =
- draggedTab.getBoundingClientRect();
- let shiftSizeX = tabWidth;
- let shiftSizeY = tabHeight;
- dragData.tabWidth = tabWidth;
- dragData.tabHeight = tabHeight;
-
- // Move the dragged tab based on the mouse position.
- let periphery = document.getElementById(
- "tabbrowser-arrowscrollbox-periphery"
- );
- let endScreenX = draggedTab.screenX + tabWidth;
- let endScreenY = draggedTab.screenY + tabHeight;
- let startScreenX = draggedTab.screenX;
- let startScreenY = draggedTab.screenY;
- let translateX = screenX - dragData.screenX;
- let translateY = screenY - dragData.screenY;
- let startBoundX = this._tabbrowserTabs.screenX - startScreenX;
- let startBoundY = this._tabbrowserTabs.screenY - startScreenY;
- let endBoundX =
- this._tabbrowserTabs.screenX +
- window.windowUtils.getBoundsWithoutFlushing(this._tabbrowserTabs)
- .width -
- endScreenX;
- let endBoundY = periphery.screenY - endScreenY;
- translateX = Math.min(Math.max(translateX, startBoundX), endBoundX);
- translateY = Math.min(Math.max(translateY, startBoundY), endBoundY);
-
- // Center the tab under the cursor if the tab is not under the cursor while dragging
- if (
- screen < draggedTab.screenY + translateY ||
- screen > draggedTab.screenY + tabHeight + translateY
- ) {
- translateY = screen - draggedTab.screenY - tabHeight / 2;
- }
-
- for (let tab of movingTabs) {
- tab.style.transform = `translate(${translateX}px, ${translateY}px)`;
- }
-
- dragData.translateX = translateX;
- dragData.translateY = translateY;
-
- // Determine what tab we're dragging over.
- // * Single tab dragging: 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.
- // * Multiple tabs dragging: Tabs are stacked, so we can still use the above
- // point of reference, the center of the dragged tab.
- // * We're doing a binary search in order to reduce the amount of
- // tabs we need to check.
-
- tabs = tabs.filter(t => !movingTabs.includes(t) || t == draggedTab);
- let tabCenterX = startScreenX + translateX + tabWidth / 2;
- let tabCenterY = startScreenY + translateY + tabHeight / 2;
-
- let shiftNumber = this._maxTabsPerRow - 1;
-
- let getTabShift = (tab, dropIndex) => {
- if (!tab?.currentIndex) {
- tab.currentIndex = tab.elementIndex;
- }
- if (
- tab.currentIndex < draggedTab.elementIndex &&
- tab.currentIndex >= dropIndex
- ) {
- // If tab is at the end of a row, shift back and down
- let tabRow = Math.ceil((tab.currentIndex + 1) / this._maxTabsPerRow);
- let shiftedTabRow = Math.ceil(
- (tab.currentIndex + 2) / this._maxTabsPerRow
- );
- if (tab.currentIndex && tabRow != shiftedTabRow) {
- return [
- RTL_UI ? tabWidth * shiftNumber : -tabWidth * shiftNumber,
- shiftSizeY,
- ];
- }
- return [RTL_UI ? -shiftSizeX : shiftSizeX, 0];
- }
- if (
- tab.currentIndex > draggedTab.elementIndex &&
- tab.currentIndex < dropIndex
- ) {
- // If tab is not index 0 and at the start of a row, shift across and up
- let tabRow = Math.floor(tab.currentIndex / this._maxTabsPerRow);
- let shiftedTabRow = Math.floor(
- (tab.currentIndex - 1) / this._maxTabsPerRow
- );
- if (tab.currentIndex && tabRow != shiftedTabRow) {
- return [
- RTL_UI ? -tabWidth * shiftNumber : tabWidth * shiftNumber,
- -shiftSizeY,
- ];
- }
- return [RTL_UI ? shiftSizeX : -shiftSizeX, 0];
- }
- return [0, 0];
- };
-
- let low = 0;
- let high = tabs.length - 1;
- let newIndex = -1;
- let oldIndex = dragData.animDropElementIndex ?? draggedTab.elementIndex;
-
- while (low <= high) {
- let mid = Math.floor((low + high) / 2);
- if (tabs[mid] == draggedTab && ++mid > high) {
- break;
- }
- let [shiftX, shiftY] = getTabShift(tabs[mid], oldIndex);
- screenX = tabs[mid].screenX + shiftX;
- screenY = tabs[mid].screenY + shiftY;
-
- if (screenY + tabHeight < tabCenterY) {
- low = mid + 1;
- } else if (screenY > tabCenterY) {
- high = mid - 1;
- } else if (
- RTL_UI ? screenX + tabWidth < tabCenterX : screenX > tabCenterX
- ) {
- high = mid - 1;
- } else if (
- RTL_UI ? screenX > tabCenterX : screenX + tabWidth < tabCenterX
- ) {
- low = mid + 1;
- } else {
- newIndex = tabs[mid].currentIndex;
- break;
- }
- }
-
- if (newIndex >= oldIndex && newIndex < tabs.length) {
- newIndex++;
- }
-
- if (newIndex < 0) {
- newIndex = oldIndex;
- }
-
- if (newIndex == dragData.animDropElementIndex) {
- return;
- }
-
- dragData.animDropElementIndex = newIndex;
- dragData.dropElement = tabs[Math.min(newIndex, tabs.length - 1)];
- dragData.dropBefore = newIndex < tabs.length;
-
- // Shift background tabs to leave a gap where the dragged tab
- // would currently be dropped.
- for (let tab of tabs) {
- if (tab != draggedTab) {
- let [shiftX, shiftY] = getTabShift(tab, newIndex);
- tab.style.transform =
- shiftX || shiftY ? `translate(${shiftX}px, ${shiftY}px)` : "";
- }
- }
- }
-
- // If the tab is dropped in another window, we need to pass in the original window document
- _resetTabsAfterDrop(draggedTabDocument = document) {
- if (this._tabbrowserTabs.expandOnHover) {
- // Re-enable MousePosTracker after dropping
- MousePosTracker.addListener(document.defaultView.SidebarController);
- }
-
- let pinnedDropIndicator = draggedTabDocument.getElementById(
- "pinned-drop-indicator"
- );
- pinnedDropIndicator.removeAttribute("visible");
- pinnedDropIndicator.removeAttribute("interactive");
- draggedTabDocument.ownerGlobal.gBrowser.tabContainer.style.maxWidth = "";
- let allTabs = draggedTabDocument.getElementsByClassName("tabbrowser-tab");
- for (let tab of allTabs) {
- tab.style.width = "";
- tab.style.left = "";
- tab.style.top = "";
- tab.style.maxWidth = "";
- tab.style.pointerEvents = "";
- tab.removeAttribute("dragtarget");
- tab.removeAttribute("small-stack");
- tab.removeAttribute("big-stack");
- delete tab.currentIndex;
- }
- for (let label of draggedTabDocument.getElementsByClassName(
- "tab-group-label-container"
- )) {
- label.style.width = "";
- label.style.maxWidth = "";
- label.style.height = "";
- label.style.left = "";
- label.style.top = "";
- label.style.pointerEvents = "";
- label.removeAttribute("dragtarget");
- }
- for (let label of draggedTabDocument.getElementsByClassName(
- "tab-group-label"
- )) {
- delete label.currentIndex;
- }
- let periphery = draggedTabDocument.getElementById(
- "tabbrowser-arrowscrollbox-periphery"
- );
- periphery.style.marginBlockStart = "";
- periphery.style.marginInlineStart = "";
- periphery.style.left = "";
- periphery.style.top = "";
- let pinnedTabsContainer = draggedTabDocument.getElementById(
- "pinned-tabs-container"
- );
- let pinnedPeriphery = draggedTabDocument.getElementById(
- "pinned-tabs-container-periphery"
- );
- pinnedPeriphery && pinnedTabsContainer.removeChild(pinnedPeriphery);
- pinnedTabsContainer.removeAttribute("dragActive");
- pinnedTabsContainer.style.minHeight = "";
- draggedTabDocument.defaultView.SidebarController.updatePinnedTabsHeightOnResize();
- pinnedTabsContainer.scrollbox.style.height = "";
- pinnedTabsContainer.scrollbox.style.width = "";
- let arrowScrollbox = draggedTabDocument.getElementById(
- "tabbrowser-arrowscrollbox"
- );
- arrowScrollbox.scrollbox.style.height = "";
- arrowScrollbox.scrollbox.style.width = "";
- for (let groupLabel of draggedTabDocument.getElementsByClassName(
- "tab-group-label-container"
- )) {
- groupLabel.style.left = "";
- groupLabel.style.top = "";
- }
- }
- };
-}
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
@@ -216,24 +216,7 @@
this.tooltip = "tabbrowser-tab-tooltip";
- Services.prefs.addObserver(
- "browser.tabs.dragDrop.multiselectStacking",
- this.boundObserve
- );
- this.observe(
- null,
- "nsPref:changed",
- "browser.tabs.dragDrop.multiselectStacking"
- );
- }
-
- #initializeDragAndDrop() {
- this.tabDragAndDrop = Services.prefs.getBoolPref(
- "browser.tabs.dragDrop.multiselectStacking",
- true
- )
- ? new window.TabStacking(this)
- : new window.TabDragAndDrop(this);
+ this.tabDragAndDrop = new window.TabDragAndDrop(this);
this.tabDragAndDrop.init();
}
@@ -1161,12 +1144,9 @@
}
}
- observe(aSubject, aTopic, aData) {
+ observe(aSubject, aTopic) {
switch (aTopic) {
case "nsPref:changed": {
- if (aData == "browser.tabs.dragDrop.multiselectStacking") {
- this.#initializeDragAndDrop();
- }
// This is has to deal with changes in
// privacy.userContext.enabled and
// privacy.userContext.newTabContainerOnLeftClick.enabled.
@@ -1641,10 +1621,6 @@
destroy() {
if (this.boundObserve) {
Services.prefs.removeObserver("privacy.userContext", this.boundObserve);
- Services.prefs.removeObserver(
- "browser.tabs.dragDrop.multiselectStacking",
- this.boundObserve
- );
}
CustomizableUI.removeListener(this);
}
diff --git a/browser/components/tabbrowser/jar.mn b/browser/components/tabbrowser/jar.mn
@@ -7,7 +7,6 @@ browser.jar:
content/browser/tabbrowser/browser-ctrlTab.js (content/browser-ctrlTab.js)
content/browser/tabbrowser/browser-fullZoom.js (content/browser-fullZoom.js)
content/browser/tabbrowser/drag-and-drop.js (content/drag-and-drop.js)
- content/browser/tabbrowser/tab-stacking.js (content/tab-stacking.js)
content/browser/tabbrowser/tab.js (content/tab.js)
content/browser/tabbrowser/tab-hover-preview.mjs (content/tab-hover-preview.mjs)
content/browser/tabbrowser/tabbrowser.js (content/tabbrowser.js)
diff --git a/browser/components/tabbrowser/test/browser/tabs/browser_multiselect_tabs_reorder.js b/browser/components/tabbrowser/test/browser/tabs/browser_multiselect_tabs_reorder.js
@@ -2,9 +2,6 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
add_task(async function () {
- await SpecialPowers.pushPrefEnv({
- set: [["browser.tabs.dragDrop.multiselectStacking", false]],
- });
// Disable tab animations
gReduceMotionOverride = true;
diff --git a/browser/components/tabbrowser/test/browser/tabs/browser_tab_tooltips.js b/browser/components/tabbrowser/test/browser/tabs/browser_tab_tooltips.js
@@ -12,12 +12,12 @@ function openTooltip(node) {
event => event.originalTarget.nodeName == "tooltip"
);
window.windowUtils.disableNonTestMouseEvents(true);
- EventUtils.synthesizeMouse(node, 10, 10, { type: "mouseover" });
- EventUtils.synthesizeMouse(node, 10, 10, { type: "mousemove" });
+ EventUtils.synthesizeMouse(node, 2, 2, { type: "mouseover" });
+ EventUtils.synthesizeMouse(node, 4, 4, { type: "mousemove" });
EventUtils.synthesizeMouse(node, MOUSE_OFFSET, MOUSE_OFFSET, {
type: "mousemove",
});
- EventUtils.synthesizeMouse(node, 10, 10, { type: "mouseout" });
+ EventUtils.synthesizeMouse(node, 2, 2, { type: "mouseout" });
window.windowUtils.disableNonTestMouseEvents(false);
return tooltipShownPromise;
}
diff --git a/browser/themes/shared/tabbrowser/tabs.css b/browser/themes/shared/tabbrowser/tabs.css
@@ -30,7 +30,7 @@
--tab-pinned-min-width-expanded: calc(var(--tab-pinned-expanded-background-width) + 2 * var(--tab-pinned-margin-inline-expanded));
--tab-pinned-container-margin-inline-expanded: var(--space-small);
--tab-border-radius: var(--toolbarbutton-border-radius);
- --tab-overflow-clip-margin: 4px;
+ --tab-overflow-clip-margin: 2px;
--tab-close-button-padding: 6px;
--tab-block-margin: 4px;
--tab-icon-end-margin: 5.5px;
@@ -218,13 +218,6 @@
z-index: 3 !important;
position: absolute !important;
pointer-events: none; /* avoid blocking dragover events on scroll buttons */
-
- /* stylelint-disable-next-line media-query-no-invalid */
- @media -moz-pref("browser.tabs.dragDrop.multiselectStacking") {
- &[multiselected]:not([small-stack], [big-stack]) {
- visibility: hidden;
- }
- }
}
&:not([pinned], [fadein]) {
@@ -245,16 +238,8 @@
}
}
- &[multiselected-move-together][multiselected] {
- z-index: 3;
-
- &[visually-selected] {
- z-index: 4;
- }
-
- &:not([selected]) {
- z-index: 2;
- }
+ &[multiselected-move-together][multiselected]:not([selected]) {
+ z-index: 2;
}
/* stylelint-disable-next-line media-query-no-invalid */
@@ -892,102 +877,6 @@
}
}
- #tabbrowser-tabs[orient="vertical"]:not([movingtab-group]) & {
- .tabbrowser-tab[small-stack] > .tab-stack > & {
- box-shadow:
- 0 5px 0 -3px var(--tab-selected-bgcolor),
- 0 3px var(--focus-outline-color);
- }
-
- .tabbrowser-tab[big-stack] > .tab-stack > & {
- box-shadow:
- 0 5px 0 -3px var(--tab-selected-bgcolor),
- 0 3px var(--focus-outline-color),
- 0 8px 0 -3px var(--tab-selected-bgcolor),
- 0 6px var(--focus-outline-color);
- }
- }
-
- #tabbrowser-tabs[orient="horizontal"]:not([movingtab-group]) & {
- .tabbrowser-tab[small-stack] > .tab-stack > & {
- box-shadow:
- 5px 0 0 -3px var(--tab-selected-bgcolor),
- 3px 0 var(--focus-outline-color);
- }
-
- .tabbrowser-tab[big-stack] > .tab-stack > & {
- box-shadow:
- 5px 0 0 -3px var(--tab-selected-bgcolor),
- 3px 0 var(--focus-outline-color),
- 8px 0 0 -3px var(--tab-selected-bgcolor),
- 6px 0 var(--focus-outline-color);
- }
-
- &:-moz-locale-dir(rtl) {
- .tabbrowser-tab[small-stack] > .tab-stack > & {
- box-shadow:
- -5px 0 0 -3px var(--tab-selected-bgcolor),
- -3px 0 var(--focus-outline-color);
- }
-
- .tabbrowser-tab[big-stack] > .tab-stack > & {
- box-shadow:
- -5px 0 0 -3px var(--tab-selected-bgcolor),
- -3px 0 var(--focus-outline-color),
- -8px 0 0 -3px var(--tab-selected-bgcolor),
- -6px 0 var(--focus-outline-color);
- }
- }
- }
-
- #tabbrowser-tabs[orient="vertical"][movingtab-group] & {
- .tabbrowser-tab[small-stack] > .tab-stack > & {
- box-shadow:
- 0 5px 0 -3px var(--tab-selected-bgcolor),
- 0 3px var(--dragover-tab-group-color);
- }
-
- .tabbrowser-tab[big-stack] > .tab-stack > & {
- box-shadow:
- 0 5px 0 -3px var(--tab-selected-bgcolor),
- 0 3px var(--dragover-tab-group-color),
- 0 8px 0 -3px var(--tab-selected-bgcolor),
- 0 6px var(--dragover-tab-group-color);
- }
- }
-
- #tabbrowser-tabs[orient="horizontal"][movingtab-group] & {
- .tabbrowser-tab[small-stack] > .tab-stack > & {
- box-shadow:
- 5px 0 0 -3px var(--tab-selected-bgcolor),
- 3px 0 var(--dragover-tab-group-color);
- }
-
- .tabbrowser-tab[big-stack] > .tab-stack > & {
- box-shadow:
- 5px 0 0 -3px var(--tab-selected-bgcolor),
- 3px 0 var(--dragover-tab-group-color),
- 8px 0 0 -3px var(--tab-selected-bgcolor),
- 6px 0 var(--dragover-tab-group-color);
- }
-
- &:-moz-locale-dir(rtl) {
- .tabbrowser-tab[small-stack] > .tab-stack > & {
- box-shadow:
- -5px 0 0 -3px var(--tab-selected-bgcolor),
- -3px 0 var(--dragover-tab-group-color);
- }
-
- .tabbrowser-tab[big-stack] > .tab-stack > & {
- box-shadow:
- -5px 0 0 -3px var(--tab-selected-bgcolor),
- -3px 0 var(--dragover-tab-group-color),
- -8px 0 0 -3px var(--tab-selected-bgcolor),
- -6px 0 var(--dragover-tab-group-color);
- }
- }
- }
-
#tabbrowser-tabs[movingtab] & {
transition:
background-color 50ms ease,