diff --git a/src/browser/base/content/zen-assets.inc.xhtml b/src/browser/base/content/zen-assets.inc.xhtml
index c5613a674..33e923742 100644
--- a/src/browser/base/content/zen-assets.inc.xhtml
+++ b/src/browser/base/content/zen-assets.inc.xhtml
@@ -17,6 +17,7 @@
+
@@ -28,6 +29,7 @@
# Scripts used all over the browser
+
diff --git a/src/browser/base/content/zen-assets.jar.inc.mn b/src/browser/base/content/zen-assets.jar.inc.mn
index 3b7dbedc6..2f20814ab 100644
--- a/src/browser/base/content/zen-assets.jar.inc.mn
+++ b/src/browser/base/content/zen-assets.jar.inc.mn
@@ -20,6 +20,7 @@
content/browser/zen-components/ZenCommonUtils.mjs (zen-components/ZenCommonUtils.mjs)
content/browser/zen-components/ZenGradientGenerator.mjs (zen-components/ZenGradientGenerator.mjs)
content/browser/zen-components/ZenGlanceManager.mjs (zen-components/ZenGlanceManager.mjs)
+ content/browser/zen-components/ZenFolders.mjs (zen-components/ZenFolders.mjs)
content/browser/zen-components/ZenActorsManager.mjs (zen-components/ZenActorsManager.mjs)
content/browser/zen-components/ZenRices.mjs (zen-components/ZenRices.mjs)
content/browser/zen-components/ZenEmojies.mjs (zen-components/ZenEmojies.mjs)
@@ -37,6 +38,7 @@
content/browser/zen-styles/zen-sidebar.css (content/zen-styles/zen-sidebar.css)
content/browser/zen-styles/zen-toolbar.css (content/zen-styles/zen-toolbar.css)
content/browser/zen-styles/zen-decks.css (content/zen-styles/zen-decks.css)
+ content/browser/zen-styles/zen-folders.css (content/zen-styles/zen-folders.css)
content/browser/zen-styles/zen-glance.css (content/zen-styles/zen-glance.css)
content/browser/zen-styles/zen-browser-container.css (content/zen-styles/zen-browser-container.css)
content/browser/zen-styles/zen-workspaces.css (content/zen-styles/zen-workspaces.css)
diff --git a/src/browser/base/content/zen-styles/zen-animations.css b/src/browser/base/content/zen-styles/zen-animations.css
index 067582958..4d820f320 100644
--- a/src/browser/base/content/zen-styles/zen-animations.css
+++ b/src/browser/base/content/zen-styles/zen-animations.css
@@ -85,18 +85,6 @@
}
}
-@keyframes zen-deck-fadeIn {
- 0% {
- transform: scale(0.9);
- opacity: 0;
- }
-
- 100% {
- transform: scale(1);
- opacity: 1;
- }
-}
-
@keyframes better-sidebar-pinned-hide {
0% {
opacity: 1;
diff --git a/src/browser/base/content/zen-styles/zen-decks.css b/src/browser/base/content/zen-styles/zen-decks.css
index 888ba486c..6592cc796 100644
--- a/src/browser/base/content/zen-styles/zen-decks.css
+++ b/src/browser/base/content/zen-styles/zen-decks.css
@@ -36,10 +36,6 @@
overflow: hidden;
}
-#tabbrowser-tabpanels[zen-split-view='true'] > [zen-split-anim='true'] {
- animation: zen-deck-fadeIn 0.2s forwards ease-in-out;
-}
-
#tabbrowser-tabpanels[zen-split-view='true']:not([zen-split-resizing]) > [zen-split-anim='true'] {
transition: inset 0.09s ease-out !important;
& browser {
@@ -216,3 +212,46 @@
#zenSplitViewModifier:not([has-enabled-realloc])[toast] #zenSplitViewModifierEnabledToast {
display: revert;
}
+
+.zen-view-splitter-header {
+ display: flex;
+ width: 100%;
+ height: 20px;
+ /*TODO: update with appropriate color*/
+ background-color: var(--zen-colors-secondary);
+ align-items: center;
+ z-index: 100;
+}
+
+.zen-view-splitter-drag-handle {
+ flex-grow: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ cursor: move;
+}
+
+.zen-tab-unsplit-button {
+ display: block;
+ -moz-context-properties: fill, fill-opacity;
+ border-radius: var(--tab-border-radius);
+ /*TODO: update with appropriate color*/
+ color: inherit;
+ fill: currentColor;
+ padding: var(--tab-close-button-padding);
+ width: 24px;
+ height: 24px;
+ cursor: pointer;
+ appearance: none;
+ outline: var(--toolbarbutton-outline);
+ list-style-image: url('chrome://global/skin/icons/close-fill.svg') !important;
+ &:hover {
+ outline-color: var(--toolbarbutton-hover-outline-color);
+ }
+
+ &:hover:active {
+ outline-color: var(--toolbarbutton-active-outline-color);
+ }
+}
diff --git a/src/browser/base/content/zen-styles/zen-folders.css b/src/browser/base/content/zen-styles/zen-folders.css
new file mode 100644
index 000000000..7fdc116ed
--- /dev/null
+++ b/src/browser/base/content/zen-styles/zen-folders.css
@@ -0,0 +1,108 @@
+tab-group[split-view-group] {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(calc(var(--tab-pinned-min-width-expanded) + 20px), auto));
+ gap: 1px;
+ border-radius: var(--border-radius-medium);
+ padding: 0;
+ margin-inline: var(--tab-block-margin);
+ margin-block: var(--tab-block-margin);
+ min-height: var(--tab-min-height);
+ outline: var(--tab-outline);
+ outline-offset: var(--tab-outline-offset);
+ outline-color: var(--tab-selected-outline-color);
+ padding: 1px;
+ transition: scale 0.1s ease;
+
+ & > * {
+ --tab-min-height: 32px;
+ --tab-selected-bgcolor: var(--zen-toolbar-element-bg);
+ --tab-selected-shadow: none;
+ --border-radius-medium: calc(2px + var(--tab-border-radius));
+ --zen-active-tab-scale: 1;
+ }
+
+ &:has(tab[visuallyselected]) {
+ background-color: var(--tab-selected-bgcolor);
+ box-shadow: var(--tab-selected-shadow);
+ }
+
+ &:active {
+ scale: var(--zen-active-tab-scale);
+ }
+
+ &:hover {
+ background-color: var(--zen-toolbar-element-bg);
+ }
+
+ & .tab-close-button {
+ display: none !important;
+ }
+}
+
+tab-group[split-view-group] .tabbrowser-tab {
+ width: 100%;
+ max-width: unset;
+}
+
+tab-group[split-view-group] .tab-group-label-container {
+ display: none;
+ width: 100%;
+}
+
+tab-group[split-view-group] .tab-close-button {
+ display: block;
+ visibility: visible;
+}
+
+tab-group[split-view-group] .tab-group-line {
+ display: none;
+ background: transparent;
+}
+
+tab-group:not([split-view-group]) {
+ & .tab-group-label-container {
+ min-width: fit-content;
+ max-width: 100%;
+ height: fit-content !important;
+ display: flex;
+ justify-content: start;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ }
+
+ & .tab-group-label {
+ text-align: start;
+ flex-grow: 1 !important;
+ min-width: fit-content;
+ max-width: 100%;
+ font-size: 14px !important;
+ display: block !important;
+ padding-right: 8px;
+ }
+
+ & .tab-group-line {
+ display: none !important;
+ }
+
+ &[collapsed] .tabbrowser-tab {
+ display: none !important;
+ }
+
+ &:not([collapsed]) .tabbrowser-tab {
+ margin-left: 10px;
+ }
+
+ &:not([collapsed]) .tabbrowser-tab::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 2px;
+ height: 100%;
+ background-color: var(--tab-group-color);
+ }
+}
+
+.tab-group-line {
+ display: none !important;
+}
diff --git a/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css b/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css
index a6421a3f6..9c72d80ae 100644
--- a/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css
+++ b/src/browser/base/content/zen-styles/zen-tabs/vertical-tabs.css
@@ -265,7 +265,7 @@
}
&:active {
- scale: 0.98;
+ scale: var(--zen-active-tab-scale);
}
max-width: unset;
diff --git a/src/browser/base/content/zen-styles/zen-theme.css b/src/browser/base/content/zen-styles/zen-theme.css
index 5b7631680..ed93624a2 100644
--- a/src/browser/base/content/zen-styles/zen-theme.css
+++ b/src/browser/base/content/zen-styles/zen-theme.css
@@ -172,6 +172,7 @@
--arrowpanel-background: var(--zen-dialog-background) !important;
--zen-big-shadow: 0 0 9.73px 0px light-dark(rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.25));
+ --zen-active-tab-scale: 0.98;
/* Nativity */
--zen-native-content-radius: var(--zen-border-radius);
diff --git a/src/browser/base/zen-components/ZenFolders.mjs b/src/browser/base/zen-components/ZenFolders.mjs
new file mode 100644
index 000000000..5c848d741
--- /dev/null
+++ b/src/browser/base/zen-components/ZenFolders.mjs
@@ -0,0 +1,28 @@
+
+{
+ class ZenFolders {
+ constructor() {
+ this.#initEventListeners();
+ }
+
+ #initEventListeners() {
+ document.addEventListener('TabGrouped', this.#onTabGrouped.bind(this));
+ document.addEventListener('TabUngrouped', this.#onTabUngrouped.bind(this));
+ document.addEventListener('TabGroupRemoved', this.#onTabGroupRemoved.bind(this));
+ }
+
+ #onTabGrouped(event) {
+ const tab = event.target;
+ const group = tab.group;
+ group.pinned = tab.pinned;
+ }
+
+ #onTabUngrouped(event) {
+ }
+
+ #onTabGroupRemoved(event) {
+ }
+ }
+
+ window.gZenFolders = new ZenFolders();
+}
diff --git a/src/browser/base/zen-components/ZenPinnedTabManager.mjs b/src/browser/base/zen-components/ZenPinnedTabManager.mjs
index 5925b8447..7863df2eb 100644
--- a/src/browser/base/zen-components/ZenPinnedTabManager.mjs
+++ b/src/browser/base/zen-components/ZenPinnedTabManager.mjs
@@ -840,7 +840,8 @@
const pinnedTabsTarget = event.target.closest('#vertical-pinned-tabs-container');
const essentialTabsTarget = event.target.closest('#zen-essentials-container');
const tabsTarget = event.target.closest('#tabbrowser-arrowscrollbox');
- const targetTab = event.target.closest('.tabbrowser-tab');
+ let targetTab = event.target.closest('.tabbrowser-tab');
+ targetTab = targetTab?.group || targetTab;
if (event.target.closest('.zen-current-workspace-indicator')) {
this.removeTabContainersDragoverClass();
ZenWorkspaces.activeWorkspaceIndicator.setAttribute('open', true);
diff --git a/src/browser/base/zen-components/ZenViewSplitter.mjs b/src/browser/base/zen-components/ZenViewSplitter.mjs
index adc77c099..98f57a56c 100644
--- a/src/browser/base/zen-components/ZenViewSplitter.mjs
+++ b/src/browser/base/zen-components/ZenViewSplitter.mjs
@@ -120,8 +120,18 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
group.tabs.splice(tabIndex, 1);
this.resetTabState(tab, forUnsplit);
-
+ if (tab.group && tab.group.hasAttribute('split-view-group')) {
+ gBrowser.ungroupTab(tab);
+ }
if (group.tabs.length < 2) {
+ // We need to remove all remaining tabs from the group when unsplitting
+ let remainingTabs = [...group.tabs]; // Copy array since we'll modify it
+ for (let remainingTab of remainingTabs) {
+ if (remainingTab.group && remainingTab.group.hasAttribute('split-view-group')) {
+ gBrowser.ungroupTab(remainingTab);
+ }
+ this.resetTabState(remainingTab, forUnsplit);
+ }
this.removeGroup(groupIndex);
} else {
const node = this.getSplitNodeFromTab(tab);
@@ -192,13 +202,14 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
afterRearangeAction() {
document.getElementById('zenSplitViewModifier').hidePopup();
- gZenUIManager.showToast('zen-split-view-modifier-enabled-toast', {
+ ConfirmationHint.show(document.getElementById('zen-split-views-box'), 'zen-split-view-modifier-enabled-toast', {
descriptionId: 'zen-split-view-modifier-enabled-toast-description',
+ showDescription: true,
});
}
afterRearangeRemove() {
- gZenUIManager.showToast('zen-split-view-modifier-disabled-toast');
+ ConfirmationHint.show(document.getElementById('zen-split-views-box'), 'zen-split-view-modifier-disabled-toast');
}
toggleWrapperDisplay(value) {
@@ -208,7 +219,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
wrapper.setAttribute('hidden', !value);
}
- enableTabRearrangeView() {
+ enableTabRearrangeView(tabDrag = false) {
if (this.rearrangeViewEnabled) return;
this.rearrangeViewEnabled = true;
this.rearrangeViewView = this.currentView;
@@ -223,13 +234,19 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
b.style.pointerEvents = 'none';
b.style.opacity = '.85';
});
- this.tabBrowserPanel.addEventListener('dragstart', this.onBrowserDragStart);
+ if (!tabDrag) {
+ this.tabBrowserPanel.addEventListener('dragstart', this.onBrowserDragStart);
+ this.tabBrowserPanel.addEventListener('dragend', this.onBrowserDragEnd);
+ }
+
this.tabBrowserPanel.addEventListener('dragover', this.onBrowserDragOver);
this.tabBrowserPanel.addEventListener('drop', this.onBrowserDrop);
- this.tabBrowserPanel.addEventListener('dragend', this.onBrowserDragEnd);
+
this.tabBrowserPanel.addEventListener('click', this.disableTabRearrangeView);
window.addEventListener('keydown', this.disableTabRearrangeView);
- this.afterRearangeAction();
+ if (!tabDrag) {
+ this.afterRearangeAction();
+ }
}
disableTabRearrangeView = (event = null) => {
@@ -257,21 +274,57 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
});
this.rearrangeViewEnabled = false;
this.rearrangeViewView = null;
- this.afterRearangeRemove();
+ if (!event?.type === 'dragend') {
+ // Don't show toast if exiting from drag
+ this.afterRearangeRemove();
+ }
};
onBrowserDragStart = (event) => {
if (!this.splitViewActive) return;
- let browser = event.target.querySelector('browser');
- if (!browser) {
- return;
- }
- browser.style.opacity = '.2';
- const browserContainer = browser.closest('.browserSidebarContainer');
- event.dataTransfer.setData('text/plain', browserContainer.id);
- this._draggingTab = gBrowser.getTabForBrowser(browser);
- let dt = event.dataTransfer;
+ let browser;
+ let isSplitHeaderDrag = false;
+
+ const container = event.target.closest('.browserSidebarContainer[zen-split]');
+ if (container && event.offsetY < 20) {
+ // Split tab header drag case
+ const containerRect = container.getBoundingClientRect();
+ const clickX = event.clientX - containerRect.left;
+
+ // Only allow drag if click is NOT in right 20px (close button area)
+ if (clickX > containerRect.width - 22) {
+ return;
+ }
+
+ browser = container.querySelector('browser');
+ isSplitHeaderDrag = true;
+ } else {
+ // Regular browser drag case
+ browser = event.target.querySelector('browser');
+ }
+
+ if (!browser) return;
+
+ const tab = gBrowser.getTabForBrowser(browser);
+ if (!tab) return;
+
+ // Store the necessary state for drag end
+ this._dragState = {
+ tab,
+ browser,
+ isSplitHeaderDrag,
+ };
+
+ if (isSplitHeaderDrag) {
+ this.enableTabRearrangeView(true);
+ }
+
+ browser.style.opacity = '.2';
+ event.dataTransfer.setData('text/plain', browser.closest('.browserSidebarContainer').id);
+ this._draggingTab = tab;
+
+ // Canvas setup for drag image
let scale = window.devicePixelRatio;
let canvas = this._dndCanvas;
if (!canvas) {
@@ -294,8 +347,8 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
// On Windows and Mac we can update the drag image during a drag
// using updateDragImage. On Linux, we can use a panel.
if (platform === 'win' || platform === 'macosx') {
- captureListener = function () {
- dt.updateDragImage(canvas, dragImageOffset, dragImageOffset);
+ captureListener = () => {
+ event.dataTransfer.updateDragImage(canvas, dragImageOffset, dragImageOffset);
};
} else {
// Create a panel to use it in setDragImage
@@ -326,7 +379,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
PageThumbs.captureToCanvas(browser, canvas).catch((e) => console.error(e));
dragImageOffset = dragImageOffset * scale;
}
- dt.setDragImage(toDrag, dragImageOffset, dragImageOffset);
+ event.dataTransfer.setDragImage(toDrag, dragImageOffset, dragImageOffset);
return true;
};
@@ -364,10 +417,28 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
};
onBrowserDragEnd = (event) => {
- this.dropZone.removeAttribute('enabled');
- const draggingBrowser = this._draggingTab.linkedBrowser;
- draggingBrowser.style.opacity = '.85';
+ this.dropZone?.removeAttribute('enabled');
+
+ // If we don't have drag state, just clean up what we can
+ if (!this._dragState) {
+ this._draggingTab = null;
+ return;
+ }
+
+ const { tab, browser, isSplitHeaderDrag } = this._dragState;
+
+ if (browser) {
+ browser.style.opacity = isSplitHeaderDrag ? '1' : '.85';
+ }
+
+ // Handle split view specific cleanup
+ if (isSplitHeaderDrag) {
+ this.disableTabRearrangeView(event);
+ }
+
+ // Clear state
this._draggingTab = null;
+ this._dragState = null;
};
_oppositeSide(side) {
@@ -476,10 +547,12 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
*/
resetTabState(tab, forUnsplit) {
tab.splitView = false;
+ tab.removeAttribute('split-view');
tab.linkedBrowser.zenModeActive = false;
const container = tab.linkedBrowser.closest('.browserSidebarContainer');
+ this._removeHeader(container);
this.resetContainerStyle(container);
- container.removeEventListener('click', this.handleTabEvent);
+ container.removeEventListener('mousedown', this.handleTabEvent);
container.removeEventListener('mouseover', this.handleTabEvent);
if (!forUnsplit) {
tab.linkedBrowser.docShellIsActive = false;
@@ -631,7 +704,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
* @param {string} gridType - The type of grid layout.
*/
splitTabs(tabs, gridType) {
- tabs = tabs.filter((tab) => !tab.hasAttribute('zen-empty-tab'));
if (tabs.length < 2) {
return;
}
@@ -667,6 +739,16 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
};
this._data.push(splitData);
window.gBrowser.selectedTab = tabs[0];
+
+ // Add tabs to the split view group
+ let splitGroup = this._getSplitViewGroup(tabs);
+ if (splitGroup) {
+ for (const tab of tabs) {
+ if (!tab.group || tab.group !== splitGroup) {
+ gBrowser.moveTabToGroup(tab, splitGroup);
+ }
+ }
+ }
this.activateSplitView(splitData);
}
@@ -770,11 +852,45 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
applyGridToTabs(tabs) {
tabs.forEach((tab, index) => {
tab.splitView = true;
- const container = tab.linkedBrowser.closest('.browserSidebarContainer');
+ tab.setAttribute('split-view', 'true');
+ const container = tab.linkedBrowser.closest('.browserContainer');
+ if (!container.querySelector('.zen-view-splitter-header')) {
+ // insert a header into the container
+ const header = this._createHeader(container);
+ container.insertBefore(header, container.firstChild);
+ }
this.styleContainer(container);
});
}
+ /**
+ * Creates a header for the tab.
+ * @param container
+ * @returns {*|!Element|HTMLElement|HTMLUnknownElement|HTMLDirectoryElement|HTMLFontElement|HTMLFrameElement|HTMLFrameSetElement|HTMLPreElement|HTMLMarqueeElement|HTMLParamElement}
+ * @private
+ */
+ _createHeader(container) {
+ const header = document.createElement('div');
+ header.classList.add('zen-view-splitter-header');
+ const dragHandle = document.createElement('div');
+ dragHandle.classList.add('zen-view-splitter-drag-handle');
+ header.appendChild(dragHandle);
+ const removeButton = document.createXULElement('toolbarbutton');
+ removeButton.classList.add('zen-tab-unsplit-button');
+ removeButton.addEventListener('click', () => {
+ this.removeTabFromSplit(container);
+ });
+ header.appendChild(removeButton);
+ return header;
+ }
+
+ _removeHeader(container) {
+ const header = container.querySelector('.zen-view-splitter-header');
+ if (header) {
+ header.remove();
+ }
+ }
+
/**
* Apply grid layout to tabBrowserPanel
*
@@ -883,7 +999,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
*/
styleContainer(container) {
container.setAttribute('zen-split-anim', 'true');
- container.addEventListener('click', this.handleTabEvent);
+ container.addEventListener('mousedown', this.handleTabEvent);
container.addEventListener('mouseover', this.handleTabEvent);
}
@@ -896,7 +1012,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
if (this.rearrangeViewEnabled || (event.type === 'mouseover' && !this.canChangeTabOnHover)) {
return;
}
- const container = event.currentTarget;
+ const container = event.currentTarget.closest('.browserSidebarContainer');
const tab = window.gBrowser.tabs.find((t) => t.linkedBrowser.closest('.browserSidebarContainer') === container);
if (tab) {
window.gBrowser.selectedTab = tab;
@@ -977,9 +1093,15 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
const browser = tab.linkedBrowser.closest('.browserSidebarContainer');
if (active) {
browser.setAttribute('zen-split', 'true');
+
+ browser.addEventListener('dragstart', this.onBrowserDragStart);
+ browser.addEventListener('dragend', this.onBrowserDragEnd);
} else {
browser.removeAttribute('zen-split');
browser.removeAttribute('style');
+
+ browser.removeEventListener('dragstart', this.onBrowserDragStart);
+ browser.removeEventListener('dragend', this.onBrowserDragEnd);
}
}
}
@@ -1136,6 +1258,134 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
: [gBrowser.selectedTab, tabs[nextTabIndex]];
this.splitTabs(selected_tabs, gridType);
}
+
+ /**
+ * @description removes the tab from the split
+ * @param container - The container element
+ */
+ removeTabFromSplit = (container) => {
+ const browser = container.querySelector('browser');
+ if (browser) {
+ const tab = gBrowser.getTabForBrowser(browser);
+ if (tab) {
+ const groupIndex = this._data.findIndex((group) => group.tabs.includes(tab));
+ if (groupIndex >= 0) {
+ this.removeTabFromGroup(tab, groupIndex, true);
+ }
+ }
+ }
+ };
+
+ /**
+ * @description moves the tab to the split view if dragged on a browser
+ * @param event - The event
+ * @param draggedTab - The dragged tab
+ * @returns {boolean} true if the tab was moved to the split view
+ */
+ moveTabToSplitView(event, draggedTab) {
+ const dropTarget = document.elementFromPoint(event.clientX, event.clientY);
+ const browser = dropTarget?.closest('browser');
+
+ if (!browser) {
+ return false;
+ }
+
+ const droppedOnTab = gBrowser.getTabForBrowser(browser);
+ if (droppedOnTab && droppedOnTab !== draggedTab) {
+ // Calculate which side of the target browser the drop occurred
+ const browserRect = browser.getBoundingClientRect();
+ const hoverSide = this.calculateHoverSide(event.clientX, event.clientY, browserRect);
+
+ if (droppedOnTab.splitView) {
+ // Add to existing split view
+ const groupIndex = this._data.findIndex((group) => group.tabs.includes(droppedOnTab));
+ const group = this._data[groupIndex];
+
+ if (!group.tabs.includes(draggedTab)) {
+ // First move the tab to the split view group
+ let splitGroup = droppedOnTab.group;
+ if (splitGroup && (!draggedTab.group || draggedTab.group !== splitGroup)) {
+ gBrowser.moveTabToGroup(draggedTab, splitGroup);
+ }
+
+ const droppedOnSplitNode = this.getSplitNodeFromTab(droppedOnTab);
+ const parentNode = droppedOnSplitNode.parent;
+
+ // Then add the tab to the split view
+ group.tabs.push(draggedTab);
+
+ // If dropping on a side, create a new split in that direction
+ if (hoverSide !== 'center') {
+ const splitDirection = hoverSide === 'left' || hoverSide === 'right' ? 'row' : 'column';
+ if (parentNode.direction !== splitDirection) {
+ this.splitIntoNode(droppedOnSplitNode, new SplitLeafNode(draggedTab, 50), hoverSide, 0.5);
+ } else {
+ this.addTabToSplit(draggedTab, parentNode);
+ }
+ } else {
+ this.addTabToSplit(draggedTab, group.layoutTree);
+ }
+
+ this.activateSplitView(group, true);
+ }
+ } else {
+ // Create new split view with layout based on drop position
+ let gridType;
+ switch (hoverSide) {
+ case 'left':
+ case 'right':
+ gridType = 'vsep';
+ break;
+ case 'top':
+ case 'bottom':
+ gridType = 'hsep';
+ break;
+ default:
+ gridType = 'grid';
+ }
+
+ // Put tabs in correct order based on drop side
+ const tabs = ['left', 'top'].includes(hoverSide) ? [draggedTab, droppedOnTab] : [droppedOnTab, draggedTab];
+
+ this.splitTabs(tabs, gridType);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Gets or creates a tab group for split view tabs
+ * @param {Array} tabs Initial tabs to add to the group if creating new
+ * @returns {TabGroup} The tab group for split view tabs
+ */
+ _getSplitViewGroup(tabs) {
+ // if any of tabs is pinned, return null
+ if (tabs.some((tab) => tab.pinned)) {
+ return null;
+ }
+
+ // Try to find an existing split view group
+ let splitGroup = gBrowser.tabGroups.find(
+ (group) => group.getAttribute('split-view-group') && group.tabs.some((tab) => tabs.includes(tab) && tab.splitView)
+ );
+
+ if (splitGroup) {
+ return splitGroup;
+ }
+
+ // We can't create an empty group, so only create if we have tabs
+ if (tabs?.length) {
+ // Create a new group with the initial tabs
+ const group = gBrowser.addTabGroup(tabs, {
+ label: '',
+ showCreateUI: false,
+ });
+
+ group.setAttribute('split-view-group', true);
+ }
+
+ return null;
+ }
}
window.gZenViewSplitter = new ZenViewSplitter();
diff --git a/src/browser/base/zen-components/ZenWorkspaces.mjs b/src/browser/base/zen-components/ZenWorkspaces.mjs
index 8117d5a7c..116e6695a 100644
--- a/src/browser/base/zen-components/ZenWorkspaces.mjs
+++ b/src/browser/base/zen-components/ZenWorkspaces.mjs
@@ -2274,12 +2274,16 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
const containers = [essentialsContainer, ...pinnedContainers, ...normalContainers];
for (const container of containers) {
for (const tab of container.children) {
- if (tab.tagName === 'tab' || tab.tagName == 'tab-group') {
+ if (tab.tagName === 'tab') {
tabs.push(tab);
const glance = tab.querySelector('.tabbrowser-tab[glance-id]');
if (glance) {
tabs.push(glance);
}
+ } else if (tab.tagName == 'tab-group') {
+ for (const groupTab of tab.tabs) {
+ tabs.push(groupTab);
+ }
}
}
}
diff --git a/src/browser/components/sessionstore/TabState-sys-mjs.patch b/src/browser/components/sessionstore/TabState-sys-mjs.patch
index 60b1e0a1a..2fc82f1af 100644
--- a/src/browser/components/sessionstore/TabState-sys-mjs.patch
+++ b/src/browser/components/sessionstore/TabState-sys-mjs.patch
@@ -1,8 +1,13 @@
diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs
-index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..05dbb00375523819163bcd225074117ef02b8b37 100644
+index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..8b0781488e8f250666f1d542e3aa0cb34e018f24 100644
--- a/browser/components/sessionstore/TabState.sys.mjs
+++ b/browser/components/sessionstore/TabState.sys.mjs
-@@ -84,6 +84,14 @@ class _TabState {
+@@ -80,10 +80,18 @@ class _TabState {
+ tabData.muteReason = tab.muteReason;
+ }
+
+- if (tab.group) {
++ if (tab.group && !tab.group.hasAttribute("split-view-group")) {
tabData.groupId = tab.group.id;
}
diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch
index 367c5178e..53de592a2 100644
--- a/src/browser/components/tabbrowser/content/tabbrowser-js.patch
+++ b/src/browser/components/tabbrowser/content/tabbrowser-js.patch
@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
-index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c7547437c9f 100644
+index 628aa6596627c85efe361fc1ece8fd58f7ee653e..490762fa04b9d37d8d7dee3e8dc22e220fe02955 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -412,11 +412,50 @@
@@ -194,7 +194,15 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
// Additionally send pinned tab events
if (pinned) {
this._notifyPinnedStatus(t);
-@@ -3367,6 +3431,24 @@
+@@ -2918,6 +2982,7 @@
+ id = `${Date.now()}-${Math.round(Math.random() * 100)}`;
+ }
+ let group = this._createTabGroup(id, color, false, label);
++ group.pinned = tabs.some(tab => tab.pinned);
+ this.tabContainer.insertBefore(
+ group,
+ insertBefore?.group ?? insertBefore
+@@ -3367,6 +3432,24 @@
) {
tabWasReused = true;
tab = this.selectedTab;
@@ -219,7 +227,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (!tabData.pinned) {
this.unpinTab(tab);
} else {
-@@ -3380,6 +3462,7 @@
+@@ -3380,6 +3463,7 @@
restoreTabsLazily && !select && !tabData.pinned;
let url = "about:blank";
@@ -227,7 +235,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (tabData.entries?.length) {
let activeIndex = (tabData.index || tabData.entries.length) - 1;
// Ensure the index is in bounds.
-@@ -3415,7 +3498,24 @@
+@@ -3415,7 +3499,24 @@
skipLoad: true,
preferredRemoteType,
});
@@ -253,7 +261,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (select) {
tabToSelect = tab;
}
-@@ -3428,8 +3528,8 @@
+@@ -3428,8 +3529,8 @@
// inserted in the DOM. If the tab is not yet in the DOM,
// just insert it in the right place from the start.
if (!tab.parentNode) {
@@ -264,7 +272,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
tab.toggleAttribute("pinned", true);
this.tabContainer._invalidateCachedTabs();
// Then ensure all the tab open/pinning information is sent.
-@@ -3693,7 +3793,7 @@
+@@ -3693,7 +3794,7 @@
// Ensure we have an index if one was not provided.
if (typeof index != "number") {
// Move the new tab after another tab if needed, to the end otherwise.
@@ -273,7 +281,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (
!bulkOrderedOpen &&
((openerTab &&
-@@ -3744,7 +3844,7 @@
+@@ -3744,7 +3845,7 @@
}
/** @type {MozTabbrowserTab|undefined} */
@@ -282,7 +290,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
this.tabContainer._invalidateCachedTabs();
if (tabGroup) {
-@@ -4059,6 +4159,9 @@
+@@ -4059,6 +4160,9 @@
return;
}
@@ -292,7 +300,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
this.removeTabs(selectedTabs);
}
-@@ -4391,6 +4494,7 @@
+@@ -4391,6 +4495,7 @@
skipSessionStore,
} = {}
) {
@@ -300,7 +308,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (UserInteraction.running("browser.tabs.opening", window)) {
UserInteraction.finish("browser.tabs.opening", window);
}
-@@ -4407,6 +4511,12 @@
+@@ -4407,6 +4512,12 @@
TelemetryStopwatch.start("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);
}
@@ -313,7 +321,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
// Handle requests for synchronously removing an already
// asynchronously closing tab.
if (!animate && aTab.closing) {
-@@ -4421,7 +4531,9 @@
+@@ -4421,7 +4532,9 @@
// frame created for it (for example, by updating the visually selected
// state).
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
@@ -324,7 +332,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (
!this._beginRemoveTab(aTab, {
closeWindowFastpath: true,
-@@ -4435,7 +4547,6 @@
+@@ -4435,7 +4548,6 @@
TelemetryStopwatch.cancel("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);
return;
}
@@ -332,7 +340,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
let lockTabSizing =
!this.tabContainer.verticalMode &&
!aTab.pinned &&
-@@ -4574,14 +4685,14 @@
+@@ -4574,14 +4686,14 @@
!!this.tabsInCollapsedTabGroups.length;
if (
aTab.visible &&
@@ -349,7 +357,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (closeWindow) {
// We've already called beforeunload on all the relevant tabs if we get here,
-@@ -4605,6 +4716,7 @@
+@@ -4605,6 +4717,7 @@
newTab = true;
}
@@ -357,7 +365,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
aTab._endRemoveArgs = [closeWindow, newTab];
// swapBrowsersAndCloseOther will take care of closing the window without animation.
-@@ -4645,9 +4757,7 @@
+@@ -4645,9 +4758,7 @@
aTab._mouseleave();
if (newTab) {
@@ -368,7 +376,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
} else {
TabBarVisibility.update();
}
-@@ -4776,6 +4886,8 @@
+@@ -4776,6 +4887,8 @@
this.tabs[i]._tPos = i;
}
@@ -377,7 +385,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (!this._windowIsClosing) {
if (wasPinned) {
this.tabContainer._positionPinnedTabs();
-@@ -4994,7 +5106,7 @@
+@@ -4994,7 +5107,7 @@
!excludeTabs.has(aTab.owner) &&
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
) {
@@ -386,7 +394,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
}
// Try to find a remaining tab that comes after the given tab
-@@ -5016,7 +5128,7 @@
+@@ -5016,7 +5129,7 @@
}
if (tab) {
@@ -395,7 +403,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
}
// If no qualifying visible tab was found, see if there is a tab in
-@@ -5434,10 +5546,10 @@
+@@ -5434,10 +5547,10 @@
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
}
@@ -408,7 +416,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
aTab.selected ||
aTab.closing ||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
-@@ -5675,7 +5787,7 @@
+@@ -5675,7 +5788,7 @@
// Don't allow mixing pinned and unpinned tabs.
if (aTab.pinned) {
@@ -417,16 +425,27 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
} else {
aIndex = Math.max(aIndex, this.pinnedTabCount);
}
-@@ -5684,7 +5796,7 @@
+@@ -5684,8 +5797,8 @@
}
this._handleTabMove(aTab, () => {
- let neighbor = this.tabs[aIndex];
+- if (forceStandaloneTab && neighbor.group) {
+ let neighbor = this.tabs.filter(tab => !tab.hasAttribute("zen-glance-tab"))[aIndex];
- if (forceStandaloneTab && neighbor.group) {
++ if (forceStandaloneTab && neighbor.group || (neighbor.group && neighbor.group.hasAttribute("split-view-group"))) {
neighbor = neighbor.group;
}
-@@ -5721,6 +5833,8 @@
+ if (neighbor && aIndex >= aTab._tPos) {
+@@ -5697,7 +5810,7 @@
+ }
+
+ moveTabToGroup(aTab, aGroup) {
+- if (aTab.pinned) {
++ if (aTab.pinned != !!aGroup.pinned && !aTab.hasAttribute("zen-essential")) {
+ return;
+ }
+ if (aTab.group && aTab.group.id === aGroup.id) {
+@@ -5721,6 +5834,8 @@
moveActionCallback();
@@ -435,7 +454,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
// Clear tabs cache after moving nodes because the order of tabs may have
// changed.
this.tabContainer._invalidateCachedTabs();
-@@ -5771,7 +5885,7 @@
+@@ -5771,7 +5886,7 @@
createLazyBrowser,
};
@@ -444,7 +463,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
if (aIndex < numPinned || (aTab.pinned && aIndex == numPinned)) {
params.pinned = true;
}
-@@ -7415,6 +7529,7 @@
+@@ -7415,6 +7530,7 @@
aWebProgress.isTopLevel
) {
this.mTab.setAttribute("busy", "true");
@@ -452,7 +471,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
gBrowser._tabAttrModified(this.mTab, ["busy"]);
this.mTab._notselectedsinceload = !this.mTab.selected;
}
-@@ -8381,7 +8496,7 @@ var TabContextMenu = {
+@@ -8381,7 +8497,7 @@ var TabContextMenu = {
);
contextUnpinSelectedTabs.hidden =
!this.contextTab.pinned || !multiselectionContext;
@@ -461,7 +480,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
// Move Tab items
let contextMoveTabOptions = document.getElementById(
"context_moveTabOptions"
-@@ -8414,7 +8529,7 @@ var TabContextMenu = {
+@@ -8414,7 +8530,7 @@ var TabContextMenu = {
let contextMoveTabToStart = document.getElementById("context_moveToStart");
let isFirstTab =
tabsToMove[0] == visibleTabs[0] ||
@@ -470,7 +489,7 @@ index 628aa6596627c85efe361fc1ece8fd58f7ee653e..8bc9d9806ba6283bfa1b6b738b6c7c75
contextMoveTabToStart.disabled = isFirstTab && allSelectedTabsAdjacent;
document.getElementById("context_openTabInWindow").disabled =
-@@ -8647,6 +8762,7 @@ var TabContextMenu = {
+@@ -8647,6 +8763,7 @@ var TabContextMenu = {
if (this.contextTab.multiselected) {
gBrowser.removeMultiSelectedTabs();
} else {
diff --git a/src/browser/components/tabbrowser/content/tabs-js.patch b/src/browser/components/tabbrowser/content/tabs-js.patch
index a764883c3..24ee50c66 100644
--- a/src/browser/components/tabbrowser/content/tabs-js.patch
+++ b/src/browser/components/tabbrowser/content/tabs-js.patch
@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
-index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990d029cd67 100644
+index fa96568d366fd3608f9bd583fa793150bd815c8b..76b22ad907fe8b858300ec104af2b9b7b1d29a0e 100644
--- a/browser/components/tabbrowser/content/tabs.js
+++ b/browser/components/tabbrowser/content/tabs.js
@@ -94,7 +94,7 @@
@@ -91,7 +91,18 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
postTransitionCleanup();
} else {
let onTransitionEnd = transitionendEvent => {
-@@ -1285,7 +1297,8 @@
+@@ -1282,10 +1294,19 @@
+ this._finishMoveTogetherSelectedTabs(draggedTab);
+ this._finishAnimateTabMove();
+
++ if (event.ctrlKey && !dt.mozUserCancelled && dt.dropEffect == "none" && !this._isCustomizing) {
++ const moved = gZenViewSplitter.moveTabToSplitView(event, draggedTab);
++ if (moved) {
++ delete draggedTab._dragData;
++ return;
++ }
++ }
++
if (
dt.mozUserCancelled ||
dt.dropEffect != "none" ||
@@ -101,7 +112,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
) {
delete draggedTab._dragData;
return;
-@@ -1517,7 +1530,7 @@
+@@ -1517,7 +1538,7 @@
}
get newTabButton() {
@@ -110,7 +121,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
}
get verticalMode() {
-@@ -1537,7 +1550,7 @@
+@@ -1537,7 +1558,7 @@
if (this.#allTabs) {
return this.#allTabs;
}
@@ -119,7 +130,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
// remove arrowScrollbox periphery element
children.pop();
-@@ -1551,14 +1564,29 @@
+@@ -1551,14 +1572,29 @@
}
this.#allTabs = [
@@ -151,7 +162,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
return children.filter(node => node.tagName == "tab-group");
}
-@@ -1579,7 +1607,7 @@
+@@ -1579,7 +1615,7 @@
*/
get visibleTabs() {
if (!this.#visibleTabs) {
@@ -160,7 +171,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
}
return this.#visibleTabs;
}
-@@ -1613,10 +1641,8 @@
+@@ -1613,10 +1649,8 @@
return this.#focusableItems;
}
@@ -173,7 +184,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
let focusableItems = [];
for (let child of children) {
-@@ -1632,6 +1658,7 @@
+@@ -1632,6 +1666,7 @@
}
this.#focusableItems = [
@@ -181,7 +192,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
...verticalPinnedTabsContainer.children,
...focusableItems,
];
-@@ -1642,6 +1669,7 @@
+@@ -1642,6 +1677,7 @@
_invalidateCachedTabs() {
this.#allTabs = null;
this._invalidateCachedVisibleTabs();
@@ -189,7 +200,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
}
_invalidateCachedVisibleTabs() {
-@@ -1656,8 +1684,8 @@
+@@ -1656,8 +1692,8 @@
#isContainerVerticalPinnedExpanded(tab) {
return (
this.verticalMode &&
@@ -200,7 +211,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
);
}
-@@ -1672,7 +1700,7 @@
+@@ -1672,7 +1708,7 @@
if (node == null) {
// We have a container for non-tab elements at the end of the scrollbox.
@@ -209,7 +220,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
}
node.before(tab);
-@@ -1772,7 +1800,7 @@
+@@ -1772,7 +1808,7 @@
// There are separate "new tab" buttons for horizontal tabs toolbar, vertical tabs and
// for when the tab strip is overflowed (which is shared by vertical and horizontal tabs);
// Attach the long click popup to all of them.
@@ -218,7 +229,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
const newTab2 = this.newTabButton;
const newTabVertical = document.getElementById(
"vertical-tabs-newtab-button"
-@@ -1855,7 +1883,7 @@
+@@ -1855,7 +1891,7 @@
let rect = ele => {
return window.windowUtils.getBoundsWithoutFlushing(ele);
};
@@ -227,7 +238,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
if (tab && rect(tab).width <= this._tabClipWidth) {
this.setAttribute("closebuttons", "activetab");
} else {
-@@ -1867,10 +1895,12 @@
+@@ -1867,10 +1903,12 @@
_handleTabSelect(aInstant) {
let selectedTab = this.selectedItem;
@@ -240,7 +251,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
selectedTab._notselectedsinceload = false;
}
-@@ -1882,7 +1912,7 @@
+@@ -1882,7 +1920,7 @@
return;
}
@@ -249,7 +260,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
if (!tabs.length) {
return;
}
-@@ -1918,7 +1948,7 @@
+@@ -1918,7 +1956,7 @@
if (isEndTab && !this._hasTabTempMaxWidth) {
return;
}
@@ -258,7 +269,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
// Force tabs to stay the same width, unless we're closing the last tab,
// which case we need to let them expand just enough so that the overall
// tabbar width is the same.
-@@ -1933,7 +1963,7 @@
+@@ -1933,7 +1971,7 @@
let tabsToReset = [];
for (let i = numPinned; i < tabs.length; i++) {
let tab = tabs[i];
@@ -267,7 +278,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
if (!isEndTab) {
// keep tabs the same width
tab.style.transition = "none";
-@@ -1999,16 +2029,15 @@
+@@ -1999,16 +2037,15 @@
// Move pinned tabs to another container when the tabstrip is toggled to vertical
// and when session restore code calls _positionPinnedTabs; update styling whenever
// the number of pinned tabs changes.
@@ -290,7 +301,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
}
}
-@@ -2016,9 +2045,7 @@
+@@ -2016,9 +2053,7 @@
}
_resetVerticalPinnedTabs() {
@@ -301,7 +312,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
if (!verticalTabsContainer.children.length) {
return;
-@@ -2031,8 +2058,8 @@
+@@ -2031,8 +2066,8 @@
}
_positionPinnedTabs() {
@@ -312,7 +323,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
let absPositionHorizontalTabs =
this.overflowing && tabs.length > numPinned && numPinned > 0;
-@@ -2042,6 +2069,7 @@
+@@ -2042,6 +2077,7 @@
if (this.verticalMode) {
this._updateVerticalPinnedTabs();
} else if (absPositionHorizontalTabs) {
@@ -320,7 +331,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
let layoutData = this._pinnedTabsLayoutCache;
let uiDensity = document.documentElement.getAttribute("uidensity");
if (!layoutData || layoutData.uiDensity != uiDensity) {
-@@ -2113,7 +2141,7 @@
+@@ -2113,7 +2149,7 @@
return;
}
@@ -329,7 +340,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
let directionX = screenX > dragData.animLastScreenX;
let directionY = screenY > dragData.animLastScreenY;
-@@ -2296,10 +2324,11 @@
+@@ -2296,10 +2332,11 @@
}
let pinned = draggedTab.pinned;
@@ -345,7 +356,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
);
if (this.#rtlMode) {
-@@ -2541,8 +2570,9 @@
+@@ -2541,8 +2578,9 @@
);
}
@@ -357,7 +368,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
return;
}
-@@ -2604,7 +2634,7 @@
+@@ -2604,7 +2642,7 @@
movingTab._moveTogetherSelectedTabsData.newIndex = movingTabNewIndex;
movingTab._moveTogetherSelectedTabsData.animate = false;
};
@@ -366,7 +377,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
postTransitionCleanup();
} else {
let onTransitionEnd = transitionendEvent => {
-@@ -2707,9 +2737,9 @@
+@@ -2707,9 +2745,9 @@
function newIndex(aTab, index) {
// Don't allow mixing pinned and unpinned tabs.
if (aTab.pinned) {
@@ -378,7 +389,7 @@ index fa96568d366fd3608f9bd583fa793150bd815c8b..f92f3fba8deaebdc494a443de522e990
}
}
-@@ -2793,7 +2823,7 @@
+@@ -2793,7 +2831,7 @@
}
_notifyBackgroundTab(aTab) {