commit 31fc5494cd3534aecaca69155b4bb99a3bb989e8
parent b9908103c43dfa6f421a08de195185cbb68eaed7
Author: Jeremy Swinarton <jswinarton@mozilla.com>
Date: Mon, 6 Oct 2025 16:04:35 +0000
Bug 1988756: Replace menupopup with panel to improve TGHP animation and event consumption r=sthompson,tabbrowser-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D267009
Diffstat:
4 files changed, 67 insertions(+), 54 deletions(-)
diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml
@@ -536,10 +536,21 @@
<html:div class="tab-preview-thumbnail-container"></html:div>
</panel>
- <menupopup id="tabgroup-preview-panel"
- class="toolbar-menupopup"
- consumeoutsideclicks="never">
- </menupopup>
+ <panel id="tabgroup-preview-panel"
+ type="arrow"
+ class="toolbar-menupopup animatable-menupopup"
+ noautofocus="true"
+ norolluponanchor="true"
+ consumeoutsideclicks="false">
+ <arrowscrollbox id="tabgroup-panel-content"
+ class="menupopup-arrowscrollbox"
+ flex="1"
+ orient="vertical"
+ smoothscroll="false">
+ </arrowscrollbox>
+ </panel>
+
+
<html:template id="pageActionPanelTemplate">
<panel id="pageActionPanel"
diff --git a/browser/components/tabbrowser/content/tab-hover-preview.mjs b/browser/components/tabbrowser/content/tab-hover-preview.mjs
@@ -482,6 +482,7 @@ class TabGroupPanel extends Panel {
super();
this.panelElement = panel;
+ this.panelContent = panel.querySelector("#tabgroup-panel-content");
this.win = this.panelElement.ownerGlobal;
this.#panelSet = panelSet;
@@ -495,7 +496,7 @@ class TabGroupPanel extends Panel {
this.#group = group;
this.#movePanel();
- this.#updatePanelContents();
+ this.#updatePanelContent();
this.#panelSet.panelOpener.execute(() => {
if (!this.#panelSet.shouldActivate() || !this.#group.collapsed) {
@@ -564,33 +565,34 @@ class TabGroupPanel extends Panel {
Glean.tabgroup.groupInteractions.hover_preview.add();
}
- #updatePanelContents() {
+ #updatePanelContent() {
const fragment = this.win.document.createDocumentFragment();
for (let tab of this.#group.tabs) {
- let menuitem = this.win.document.createXULElement("menuitem");
- menuitem.setAttribute("label", tab.label);
- menuitem.setAttribute(
+ let tabbutton = this.win.document.createXULElement("toolbarbutton");
+ tabbutton.setAttribute("label", tab.label);
+ tabbutton.setAttribute(
"image",
"page-icon:" + tab.linkedBrowser.currentURI.spec
);
- menuitem.setAttribute("tooltiptext", tab.label);
- menuitem.classList.add("menuitem-iconic", "menuitem-with-favicon");
+ tabbutton.setAttribute("tooltiptext", tab.label);
+ tabbutton.classList.add("subviewbutton", "subviewbutton-iconic");
if (tab == this.win.gBrowser.selectedTab) {
- menuitem.classList.add("active-tab");
+ tabbutton.classList.add("active-tab");
}
- menuitem.tab = tab;
- fragment.appendChild(menuitem);
+ tabbutton.tab = tab;
+ fragment.appendChild(tabbutton);
}
- this.panelElement.replaceChildren(fragment);
+ this.panelContent.replaceChildren(fragment);
}
handleEvent(event) {
if (event.type == "command") {
this.win.gBrowser.selectedTab = event.target.tab;
+ this.deactivate({ force: true });
} else if (event.type == "mouseout" && event.target == this.panelElement) {
this.deactivate();
} else if (TabGroupPanel.PANEL_UPDATE_EVENTS.includes(event.type)) {
- this.#updatePanelContents();
+ this.#updatePanelContent();
}
}
diff --git a/browser/components/tabbrowser/test/browser/tabs/browser_tab_preview.js b/browser/components/tabbrowser/test/browser/tabs/browser_tab_preview.js
@@ -109,9 +109,6 @@ async function resetState() {
await hiddenEvent;
}
- let openPanels = getOpenPanels();
- Assert.ok(!openPanels.length, `sanity check: no panels open`);
-
await Services.fog.testFlushAllChildren();
Services.fog.testResetFOG();
}
@@ -573,40 +570,41 @@ add_task(async function tabGroupPanelAppearsOnTabGroupHover() {
const previewPanel = window.document.getElementById(
TAB_GROUP_PREVIEW_PANEL_ID
);
+ const panelContent = previewPanel.querySelector("#tabgroup-panel-content");
Assert.equal(
- previewPanel.children.length,
+ panelContent.children.length,
2,
- "Preview panel has one menuitem for each tab in the group"
+ "Preview panel has one toolbarbutton for each tab in the group"
);
Assert.equal(
- previewPanel.children[0].tagName,
- "menuitem",
- "First child is a menuitem"
+ panelContent.children[0].tagName,
+ "toolbarbutton",
+ "First child is a toolbarbutton"
);
Assert.equal(
- previewPanel.children[0].label,
+ panelContent.children[0].label,
tab1.label,
"First child has correct label"
);
Assert.equal(
- previewPanel.children[0].getAttribute("tooltiptext"),
- previewPanel.children[0].label,
+ panelContent.children[0].getAttribute("tooltiptext"),
+ panelContent.children[0].label,
"First child has a tooltip that is identical to the label"
);
Assert.equal(
- previewPanel.children[1].tagName,
- "menuitem",
- "Second child is a menuitem"
+ panelContent.children[1].tagName,
+ "toolbarbutton",
+ "Second child is a toolbarbutton"
);
Assert.equal(
- previewPanel.children[1].label,
+ panelContent.children[1].label,
tab2.label,
"Second child has correct label"
);
Assert.equal(
- previewPanel.children[1].getAttribute("tooltiptext"),
- previewPanel.children[1].label,
+ panelContent.children[1].getAttribute("tooltiptext"),
+ panelContent.children[1].label,
"Second child has a tooltip that is identical to the label"
);
@@ -721,9 +719,10 @@ add_task(async function tabGroupPanelClickElementSwitchesTabs() {
const previewPanel = window.document.getElementById(
TAB_GROUP_PREVIEW_PANEL_ID
);
+ const panelContent = previewPanel.querySelector("#tabgroup-panel-content");
let tabSelectEvent = BrowserTestUtils.waitForEvent(group, "TabSelect");
- previewPanel.children[1].click();
+ panelContent.children[1].click();
await tabSelectEvent;
Assert.equal(
@@ -789,13 +788,14 @@ add_task(async function tabGroupPanelUpdatesTests() {
const previewPanel = window.document.getElementById(
TAB_GROUP_PREVIEW_PANEL_ID
);
+ const panelContent = previewPanel.querySelector("#tabgroup-panel-content");
await openGroupPreview(group);
- Assert.equal(previewPanel.children.length, 1, "Panel has one tab");
+ Assert.equal(panelContent.children.length, 1, "Panel has one tab");
Assert.equal(
- previewPanel.children[0].tab,
+ panelContent.children[0].tab,
groupedTab,
- "Menuitem is associated with the correct tab"
+ "toolbarbutton is associated with the correct tab"
);
info(
@@ -815,9 +815,9 @@ add_task(async function tabGroupPanelUpdatesTests() {
);
await tabRenameEvent;
Assert.equal(
- previewPanel.children[0].label,
+ panelContent.children[0].label,
newTitle,
- "Panel menuitem has updated label"
+ "Panel toolbarbutton has updated label"
);
info("Test that adding a tab to the group adds the tab to the group's panel");
@@ -826,14 +826,14 @@ add_task(async function tabGroupPanelUpdatesTests() {
await TabOpenEvent;
Assert.equal(
- previewPanel.children.length,
+ panelContent.children.length,
2,
"Panel has two tabs after tab open"
);
Assert.equal(
- previewPanel.children[1].tab,
+ panelContent.children[1].tab,
newTab,
- "New menuitem is associated with the new tab"
+ "New toolbarbutton is associated with the new tab"
);
info(
@@ -844,14 +844,14 @@ add_task(async function tabGroupPanelUpdatesTests() {
await TabCloseEvent;
Assert.equal(
- previewPanel.children.length,
+ panelContent.children.length,
1,
"Panel has one tab after tab close"
);
Assert.equal(
- previewPanel.children[0].tab,
+ panelContent.children[0].tab,
groupedTab,
- "Menuitem is associated with the original tab"
+ "toolbarbutton is associated with the original tab"
);
info(
@@ -863,11 +863,11 @@ add_task(async function tabGroupPanelUpdatesTests() {
gBrowser.moveTabToGroup(newTab, group);
await tabGroupedEvent;
- Assert.equal(previewPanel.children.length, 2, "Panel has two tabs");
+ Assert.equal(panelContent.children.length, 2, "Panel has two tabs");
Assert.equal(
- previewPanel.children[1].tab,
+ panelContent.children[1].tab,
newTab,
- "Newly grouped tab is associated with the second menuitem"
+ "Newly grouped tab is associated with the second toolbarbutton"
);
info("Test that moving tabs within the group updates the panel");
@@ -877,12 +877,12 @@ add_task(async function tabGroupPanelUpdatesTests() {
await tabMoveEvent;
Assert.equal(
- previewPanel.children[0].tab,
+ panelContent.children[0].tab,
newTab,
"New tab has moved to first position in the preview panel"
);
Assert.equal(
- previewPanel.children[1].tab,
+ panelContent.children[1].tab,
groupedTab,
"Original tab has moved to second position in the preview panel"
);
@@ -896,7 +896,7 @@ add_task(async function tabGroupPanelUpdatesTests() {
await tabSelectEvent;
Assert.ok(
- previewPanel.children[1].classList.contains("active-tab"),
+ panelContent.children[1].classList.contains("active-tab"),
"Selected tab has the active tab class set"
);
@@ -907,9 +907,9 @@ add_task(async function tabGroupPanelUpdatesTests() {
gBrowser.ungroupTab(newTab);
await tabUngroupedEvent;
- Assert.equal(previewPanel.children.length, 1, "Panel has one tab");
+ Assert.equal(panelContent.children.length, 1, "Panel has one tab");
Assert.equal(
- previewPanel.children[0].tab,
+ panelContent.children[0].tab,
groupedTab,
"Tab in the panel is the original tab"
);
diff --git a/browser/themes/shared/tabbrowser/tab-hover-preview.css b/browser/themes/shared/tabbrowser/tab-hover-preview.css
@@ -57,6 +57,6 @@
}
}
-#tabgroup-preview-panel > menuitem.active-tab {
+#tabgroup-panel-content > toolbarbutton.active-tab {
font-weight: var(--font-weight-bold);
}