tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mbrowser/app/profile/firefox.js | 5-----
Mbrowser/base/content/browser-main.js | 1-
Mbrowser/components/tabbrowser/content/drag-and-drop.js | 20+++++++-------------
Dbrowser/components/tabbrowser/content/tab-stacking.js | 1245-------------------------------------------------------------------------------
Mbrowser/components/tabbrowser/content/tabs.js | 28++--------------------------
Mbrowser/components/tabbrowser/jar.mn | 1-
Mbrowser/components/tabbrowser/test/browser/tabs/browser_multiselect_tabs_reorder.js | 3---
Mbrowser/components/tabbrowser/test/browser/tabs/browser_tab_tooltips.js | 6+++---
Mbrowser/themes/shared/tabbrowser/tabs.css | 117+++-----------------------------------------------------------------------------
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,