diff --git a/browser/components/tabbrowser/content/tabgroup.js b/browser/components/tabbrowser/content/tabgroup.js index aa8b22c558f9dbc0b0b1e63993477b9126fb6821..feb90bb75b78e2513362679a5b5da41fbf97ac20 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 = ` - - + + + @@ -69,20 +69,36 @@ } 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); - if (this._initialized) { - return; - } + if (!this._initialized) { this._initialized = true; this.saveOnWindowClose = true; @@ -114,11 +130,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.querySelector(".tab-group-container").appendChild(child); + for (let tab of this.tabs) { + if (tab.hasAttribute("zen-empty-tab") && tab.group === this) { + this.querySelector(".zen-tab-group-start").after(tab); + } + } + }; this.#updateLabelAriaAttributes(); @@ -143,6 +162,8 @@ // mounts after getting created by `Tabbrowser.adoptTabGroup`. this.#wasCreatedByAdoption = false; } + this.#observeTabChanges(); + } resetDefaultGroupName = () => { this.#defaultGroupName = ""; @@ -227,7 +248,10 @@ } }); } - this.#tabChangeObserver.observe(this, { childList: true }); + const container = this.querySelector(".tab-group-container"); + if (container) { + this.#tabChangeObserver.observe(container, { childList: true }); + } } get color() { @@ -321,6 +345,9 @@ } set collapsed(val) { + if (this.hasAttribute("split-view-group")) { + return; + } if (!!val == this.collapsed) { return; } @@ -406,7 +433,6 @@ tabGroupName, }) .then(result => { - this.dataset.tooltip = result; }); } @@ -425,7 +451,57 @@ * @returns {MozTabbrowserTab[]} */ get tabs() { - return Array.from(this.children).filter(node => node.matches("tab")); + // add other group tabs if they are under this group + let childs = Array.from(this.querySelector(".tab-group-container")?.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 childGroupsAndTabs() { + const result = []; + const container = this.querySelector(".tab-group-container"); + + 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 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; } /** @@ -506,7 +582,6 @@ addTabs(tabs, metricsContext) { for (let tab of tabs) { if (tab.pinned) { - tab.ownerGlobal.gBrowser.unpinTab(tab); } let tabToMove = this.ownerGlobal === tab.ownerGlobal @@ -569,7 +644,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(); @@ -638,5 +713,6 @@ } } + window.MozTabbrowserTabGroup = MozTabbrowserTabGroup; customElements.define("tab-group", MozTabbrowserTabGroup); }