diff --git a/browser/components/tabbrowser/content/tabgroup.js b/browser/components/tabbrowser/content/tabgroup.js index 3ca119e8dc72fac652c98505211864483d98add2..026b524be51170882e788a701095b938d6923f3e 100644 --- a/browser/components/tabbrowser/content/tabgroup.js +++ b/browser/components/tabbrowser/content/tabgroup.js @@ -14,11 +14,11 @@ class MozTabbrowserTabGroup extends MozXULElement { static markup = ` - - + + + @@ -71,21 +71,37 @@ } connectedCallback() { + if (this._lastGroup && this._lastGroup !== this.group) { + this._lastGroup.dispatchEvent( + new CustomEvent("FolderUngrouped", { + bubbles: true, + detail: this, + }) + ); + } + if (this.group && this._lastGroup != this.group) { + this._lastGroup = this.group; + this.group.dispatchEvent( + new CustomEvent("FolderGrouped", { + bubbles: true, + detail: this, + }) + ); + } else if (!this.group) { + this._lastGroup = null; + } // Always set the mutation observer to listen for tab change events, even // if we are already initialized. // This is needed to ensure events continue to fire even if the tab group is // moved from the horizontal to vertical tab layout or vice-versa, which // causes the component to be repositioned in the DOM. - this.#observeTabChanges(); // Similar to above, always set up TabSelect listener, as this gets // removed in disconnectedCallback this.ownerGlobal.addEventListener("TabSelect", this); this.addEventListener("SplitViewTabChange", this); - if (this._initialized) { - return; - } + if (!this._initialized) { this._initialized = true; this.saveOnWindowClose = true; @@ -112,11 +128,14 @@ this.#labelContainerElement.addEventListener("mouseover", this); this.#labelContainerElement.addEventListener("mouseout", this); - this.#labelElement.addEventListener("contextmenu", e => { - e.preventDefault(); - gBrowser.tabGroupMenu.openEditModal(this); - return false; - }); + this.appendChild = function (child) { + this.groupContainer.appendChild(child); + for (let tab of this.tabs) { + if (tab.hasAttribute("zen-empty-tab") && tab.group === this) { + this.groupStartElement.after(tab); + } + } + }; this.#updateLabelAriaAttributes(); @@ -130,17 +149,21 @@ let tabGroupCreateDetail = this.#wasCreatedByAdoption ? { isAdoptingGroup: true } : {}; + if (!this.hasAttribute('drag-image')) { this.dispatchEvent( new CustomEvent("TabGroupCreate", { bubbles: true, detail: tabGroupCreateDetail, }) ); + } // Reset `wasCreatedByAdoption` to default of false so that we only // claim that a tab group was created by adoption the first time it // mounts after getting created by `Tabbrowser.adoptTabGroup`. this.#wasCreatedByAdoption = false; } + this.#observeTabChanges(); + } resetDefaultGroupName = () => { this.#defaultGroupName = ""; @@ -178,7 +201,9 @@ this.dispatchEvent( new CustomEvent("TabGroupRemoved", { bubbles: true }) ); + gZenVerticalTabsManager.animateItemClose(this).then(() => { this.remove(); + }); Services.obs.notifyObservers( this, "browser-tabgroup-removed-from-dom" @@ -223,7 +248,10 @@ } }); } - this.#tabChangeObserver.observe(this, { childList: true }); + const container = this.groupContainer; + if (container) { + this.#tabChangeObserver.observe(container, { childList: true }); + } } get color() { @@ -317,6 +345,9 @@ } set collapsed(val) { + if (this.hasAttribute("split-view-group")) { + return; + } if (!!val == this.collapsed) { return; } @@ -403,7 +434,6 @@ tabGroupName, }) .then(result => { - this.dataset.tooltip = result; }); } @@ -478,13 +508,65 @@ * @returns {MozTabbrowserTab[]} */ get tabs() { - let childrenArray = Array.from(this.children); - for (let i = childrenArray.length - 1; i >= 0; i--) { - if (childrenArray[i].tagName == "tab-split-view-wrapper") { - childrenArray.splice(i, 1, ...childrenArray[i].tabs); + // add other group tabs if they are under this group + let childs = Array.from(this.groupContainer?.children ?? []); + const tabsCollect = []; + for (let item of childs) { + tabsCollect.push(item); + if (gBrowser.isTabGroup(item)) { + tabsCollect.push(...item.tabs); + } + } + return tabsCollect.filter(node => node.matches("tab")); + } + + get groupContainer() { + return this.querySelector(".tab-group-container"); + } + + get groupStartElement() { + return this.querySelector(".zen-tab-group-start"); + } + + get childGroupsAndTabs() { + const result = []; + const container = this.groupContainer; + + for (const item of Array.from(container.children)) { + if (gBrowser.isTab(item)) { + result.push(item); + } else if (gBrowser.isTabGroup(item)) { + const labelContainer = item.labelElement; + labelContainer.visible = item.visible; + if (gBrowser.isTabGroupLabel(labelContainer)) { + result.push(labelContainer); + } + result.push(...item.childGroupsAndTabs); } } - return childrenArray.filter(node => node.matches("tab")); + return result; + } + + get group() { + if (gBrowser.isTabGroup(this.parentElement?.parentElement)) { + return this.parentElement.parentElement; + } + return null; + } + + get visible() { + let currentGroup = this; + while (currentGroup?.group) { + currentGroup = currentGroup?.group; + if (currentGroup.collapsed) { + return false; + } + } + return true; + } + + get level() { + return this.group?.level + 1 || 0; } /** @@ -592,7 +674,6 @@ ); } else { if (tabOrSplitView.pinned) { - tabOrSplitView.ownerGlobal.gBrowser.unpinTab(tabOrSplitView); } let tabToMove = this.ownerGlobal === tabOrSplitView.ownerGlobal @@ -661,7 +742,7 @@ */ on_click(event) { let isToggleElement = - event.target === this.#labelElement || + this.labelElement.parentElement.contains(event.target) || event.target === this.#overflowCountLabel; if (isToggleElement && event.button === 0) { event.preventDefault(); @@ -740,5 +821,6 @@ } } + window.MozTabbrowserTabGroup = MozTabbrowserTabGroup; customElements.define("tab-group", MozTabbrowserTabGroup); }