Bug 1961161: Additional metrics for closing tabs within groups r=dwalker,fxview-reviewers,tabbrowser-reviewers,nsharpley
Differential Revision: https://phabricator.services.mozilla.com/D248438
This commit is contained in:
committed by
jswinarton@mozilla.com
parent
44151f9298
commit
1f70d01002
@@ -396,7 +396,9 @@ var BrowserCommands = {
|
||||
|
||||
// In a multi-select context, close all selected tabs
|
||||
if (gBrowser.multiSelectedTabsCount) {
|
||||
gBrowser.removeMultiSelectedTabs();
|
||||
gBrowser.removeMultiSelectedTabs(
|
||||
gBrowser.TabMetrics.userTriggeredContext()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -414,7 +416,10 @@ var BrowserCommands = {
|
||||
}
|
||||
|
||||
// If the current tab is the last one, this will close the window.
|
||||
gBrowser.removeCurrentTab({ animate: true });
|
||||
gBrowser.removeCurrentTab({
|
||||
animate: true,
|
||||
...gBrowser.TabMetrics.userTriggeredContext(),
|
||||
});
|
||||
},
|
||||
|
||||
tryToCloseWindow(event) {
|
||||
|
||||
@@ -88,16 +88,28 @@ document.addEventListener(
|
||||
TabContextMenu.closeContextTabs();
|
||||
break;
|
||||
case "context_closeDuplicateTabs":
|
||||
gBrowser.removeDuplicateTabs(TabContextMenu.contextTab);
|
||||
gBrowser.removeDuplicateTabs(
|
||||
TabContextMenu.contextTab,
|
||||
lazy.TabMetrics.userTriggeredContext()
|
||||
);
|
||||
break;
|
||||
case "context_closeTabsToTheStart":
|
||||
gBrowser.removeTabsToTheStartFrom(TabContextMenu.contextTab);
|
||||
gBrowser.removeTabsToTheStartFrom(
|
||||
TabContextMenu.contextTab,
|
||||
lazy.TabMetrics.userTriggeredContext()
|
||||
);
|
||||
break;
|
||||
case "context_closeTabsToTheEnd":
|
||||
gBrowser.removeTabsToTheEndFrom(TabContextMenu.contextTab);
|
||||
gBrowser.removeTabsToTheEndFrom(
|
||||
TabContextMenu.contextTab,
|
||||
lazy.TabMetrics.userTriggeredContext()
|
||||
);
|
||||
break;
|
||||
case "context_closeOtherTabs":
|
||||
gBrowser.removeAllTabsBut(TabContextMenu.contextTab);
|
||||
gBrowser.removeAllTabsBut(
|
||||
TabContextMenu.contextTab,
|
||||
lazy.TabMetrics.userTriggeredContext()
|
||||
);
|
||||
break;
|
||||
case "context_unloadTab":
|
||||
TabContextMenu.explicitUnloadTabs();
|
||||
|
||||
@@ -26,6 +26,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
NonPrivateTabs: "resource:///modules/OpenTabs.sys.mjs",
|
||||
getTabsTargetForWindow: "resource:///modules/OpenTabs.sys.mjs",
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
|
||||
TabMetrics: "moz-src:///browser/components/tabbrowser/TabMetrics.sys.mjs",
|
||||
});
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "fxAccounts", () => {
|
||||
@@ -503,7 +504,10 @@ class OpenTabsInViewCard extends ViewPageContent {
|
||||
|
||||
closeTab(event) {
|
||||
const tab = event.originalTarget.tabElement;
|
||||
tab?.ownerGlobal.gBrowser.removeTab(tab);
|
||||
tab?.ownerGlobal.gBrowser.removeTab(
|
||||
tab,
|
||||
lazy.TabMetrics.userTriggeredContext()
|
||||
);
|
||||
|
||||
Glean.firefoxviewNext.closeOpenTabTabs.record();
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ const METRIC_REOPEN_TYPE = Object.freeze({
|
||||
* @returns {TabMetricsContext}
|
||||
*/
|
||||
function userTriggeredContext(telemetrySource) {
|
||||
telemetrySource = telemetrySource || METRIC_SOURCE.UNKNOWN;
|
||||
return {
|
||||
isUserTriggered: true,
|
||||
telemetrySource,
|
||||
|
||||
@@ -324,9 +324,12 @@ export class TabsPanel extends TabsListBase {
|
||||
break;
|
||||
}
|
||||
if (event.target.classList.contains("all-tabs-close-button")) {
|
||||
this.gBrowser.removeTab(event.target.tab, {
|
||||
telemetrySource: lazy.TabMetrics.METRIC_SOURCE.TAB_OVERFLOW_MENU,
|
||||
});
|
||||
this.gBrowser.removeTab(
|
||||
event.target.tab,
|
||||
lazy.TabMetrics.userTriggeredContext(
|
||||
lazy.TabMetrics.METRIC_SOURCE.TAB_OVERFLOW_MENU
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
if ("tabGroupId" in event.target.dataset) {
|
||||
|
||||
@@ -555,14 +555,18 @@
|
||||
|
||||
if (event.target.classList.contains("tab-close-button")) {
|
||||
if (this.multiselected) {
|
||||
gBrowser.removeMultiSelectedTabs({
|
||||
telemetrySource: lazy.TabMetrics.METRIC_SOURCE.TAB_STRIP,
|
||||
});
|
||||
gBrowser.removeMultiSelectedTabs(
|
||||
lazy.TabMetrics.userTriggeredContext(
|
||||
lazy.TabMetrics.METRIC_SOURCE.TAB_STRIP
|
||||
)
|
||||
);
|
||||
} else {
|
||||
gBrowser.removeTab(this, {
|
||||
animate: true,
|
||||
triggeringEvent: event,
|
||||
telemetrySource: lazy.TabMetrics.METRIC_SOURCE.TAB_STRIP,
|
||||
...lazy.TabMetrics.userTriggeredContext(
|
||||
lazy.TabMetrics.METRIC_SOURCE.TAB_STRIP
|
||||
),
|
||||
});
|
||||
}
|
||||
// This enables double-click protection for the tab container
|
||||
|
||||
@@ -4139,15 +4139,16 @@
|
||||
return duplicateTabs;
|
||||
}
|
||||
|
||||
removeDuplicateTabs(aTab) {
|
||||
removeDuplicateTabs(aTab, options) {
|
||||
this._removeDuplicateTabs(
|
||||
aTab,
|
||||
this.getDuplicateTabsToClose(aTab),
|
||||
this.closingTabsEnum.DUPLICATES
|
||||
this.closingTabsEnum.DUPLICATES,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
_removeDuplicateTabs(aConfirmationAnchor, tabs, aCloseTabs) {
|
||||
_removeDuplicateTabs(aConfirmationAnchor, tabs, aCloseTabs, options) {
|
||||
if (!tabs.length) {
|
||||
return;
|
||||
}
|
||||
@@ -4156,7 +4157,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
this.removeTabs(tabs);
|
||||
this.removeTabs(tabs, options);
|
||||
ConfirmationHint.show(
|
||||
aConfirmationAnchor,
|
||||
"confirmation-hint-duplicate-tabs-closed",
|
||||
@@ -4179,7 +4180,7 @@
|
||||
* In a multi-select context, the tabs (except pinned tabs) that are located to the
|
||||
* left of the leftmost selected tab will be removed.
|
||||
*/
|
||||
removeTabsToTheStartFrom(aTab) {
|
||||
removeTabsToTheStartFrom(aTab, options) {
|
||||
let tabs = this._getTabsToTheStartFrom(aTab);
|
||||
if (
|
||||
!this.warnAboutClosingTabs(tabs.length, this.closingTabsEnum.TO_START)
|
||||
@@ -4187,14 +4188,14 @@
|
||||
return;
|
||||
}
|
||||
|
||||
this.removeTabs(tabs);
|
||||
this.removeTabs(tabs, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* In a multi-select context, the tabs (except pinned tabs) that are located to the
|
||||
* right of the rightmost selected tab will be removed.
|
||||
*/
|
||||
removeTabsToTheEndFrom(aTab) {
|
||||
removeTabsToTheEndFrom(aTab, options) {
|
||||
let tabs = this._getTabsToTheEndFrom(aTab);
|
||||
if (
|
||||
!this.warnAboutClosingTabs(tabs.length, this.closingTabsEnum.TO_END)
|
||||
@@ -4202,7 +4203,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
this.removeTabs(tabs);
|
||||
this.removeTabs(tabs, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4258,7 +4259,7 @@
|
||||
this.removeTabs(tabsToRemove, aParams);
|
||||
}
|
||||
|
||||
removeMultiSelectedTabs({ telemetrySource } = {}) {
|
||||
removeMultiSelectedTabs({ isUserTriggered, telemetrySource } = {}) {
|
||||
let selectedTabs = this.selectedTabs;
|
||||
if (
|
||||
!this.warnAboutClosingTabs(
|
||||
@@ -4269,7 +4270,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
this.removeTabs(selectedTabs, { telemetrySource });
|
||||
this.removeTabs(selectedTabs, { isUserTriggered, telemetrySource });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4313,6 +4314,7 @@
|
||||
skipPermitUnload,
|
||||
skipRemoves,
|
||||
skipSessionStore,
|
||||
isUserTriggered,
|
||||
telemetrySource,
|
||||
}
|
||||
) {
|
||||
@@ -4396,6 +4398,7 @@
|
||||
prewarmed: true,
|
||||
skipPermitUnload,
|
||||
skipSessionStore,
|
||||
isUserTriggered,
|
||||
telemetrySource,
|
||||
});
|
||||
}
|
||||
@@ -4509,6 +4512,7 @@
|
||||
* @param {boolean} [options.skipGroupCheck]
|
||||
* Skip separate processing of whole tab groups from the set of tabs.
|
||||
* Used by removeTabGroup.
|
||||
* TODO add docs
|
||||
*/
|
||||
removeTabs(
|
||||
tabs,
|
||||
@@ -4518,6 +4522,7 @@
|
||||
skipPermitUnload = false,
|
||||
skipSessionStore = false,
|
||||
skipGroupCheck = false,
|
||||
isUserTriggered = false,
|
||||
telemetrySource,
|
||||
} = {}
|
||||
) {
|
||||
@@ -4553,6 +4558,8 @@
|
||||
animate,
|
||||
skipSessionStore,
|
||||
skipPermitUnload,
|
||||
isUserTriggered,
|
||||
telemetrySource,
|
||||
});
|
||||
});
|
||||
tabs = leftoverTabs;
|
||||
@@ -4565,6 +4572,7 @@
|
||||
skipPermitUnload,
|
||||
skipRemoves: false,
|
||||
skipSessionStore,
|
||||
isUserTriggered,
|
||||
telemetrySource,
|
||||
});
|
||||
|
||||
@@ -4589,6 +4597,8 @@
|
||||
prewarmed: true,
|
||||
skipPermitUnload,
|
||||
skipSessionStore,
|
||||
isUserTriggered,
|
||||
telemetrySource,
|
||||
};
|
||||
|
||||
// Now run again sequentially the beforeunload listeners that will result in a prompt.
|
||||
@@ -4626,6 +4636,7 @@
|
||||
closeWindowWithLastTab,
|
||||
prewarmed,
|
||||
skipSessionStore,
|
||||
isUserTriggered,
|
||||
telemetrySource,
|
||||
} = {}
|
||||
) {
|
||||
@@ -4664,6 +4675,7 @@
|
||||
closeWindowWithLastTab,
|
||||
prewarmed,
|
||||
skipSessionStore,
|
||||
isUserTriggered,
|
||||
telemetrySource,
|
||||
})
|
||||
) {
|
||||
@@ -4752,6 +4764,7 @@
|
||||
skipPermitUnload,
|
||||
prewarmed,
|
||||
skipSessionStore = false,
|
||||
isUserTriggered,
|
||||
telemetrySource,
|
||||
} = {}
|
||||
) {
|
||||
@@ -4904,7 +4917,12 @@
|
||||
// inspect the tab that's about to close.
|
||||
let evt = new CustomEvent("TabClose", {
|
||||
bubbles: true,
|
||||
detail: { adoptedBy: adoptedByTab, skipSessionStore, telemetrySource },
|
||||
detail: {
|
||||
adoptedBy: adoptedByTab,
|
||||
skipSessionStore,
|
||||
isUserTriggered,
|
||||
telemetrySource,
|
||||
},
|
||||
});
|
||||
aTab.dispatchEvent(evt);
|
||||
|
||||
@@ -9274,13 +9292,17 @@ var TabContextMenu = {
|
||||
|
||||
closeContextTabs() {
|
||||
if (this.contextTab.multiselected) {
|
||||
gBrowser.removeMultiSelectedTabs({
|
||||
telemetrySource: gBrowser.TabMetrics.METRIC_SOURCE.TAB_STRIP,
|
||||
});
|
||||
gBrowser.removeMultiSelectedTabs(
|
||||
gBrowser.TabMetrics.userTriggeredContext(
|
||||
gBrowser.TabMetrics.METRIC_SOURCE.TAB_STRIP
|
||||
)
|
||||
);
|
||||
} else {
|
||||
gBrowser.removeTab(this.contextTab, {
|
||||
animate: true,
|
||||
telemetrySource: gBrowser.TabMetrics.METRIC_SOURCE.TAB_STRIP,
|
||||
...gBrowser.TabMetrics.userTriggeredContext(
|
||||
gBrowser.TabMetrics.METRIC_SOURCE.TAB_STRIP
|
||||
),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -396,9 +396,11 @@ tabgroup:
|
||||
bugs:
|
||||
- https://bugzil.la/1938405
|
||||
- https://bugzil.la/1960360
|
||||
- https://bugzil.la/1961161
|
||||
data_reviews:
|
||||
- https://bugzil.la/1938405
|
||||
- https://bugzil.la/1960360
|
||||
- https://bugzil.la/1961161
|
||||
data_sensitivity:
|
||||
- interaction
|
||||
labels:
|
||||
@@ -408,6 +410,7 @@ tabgroup:
|
||||
- new
|
||||
- close_tabstrip
|
||||
- close_tabmenu
|
||||
- close_tab_other
|
||||
- reorder
|
||||
- remove_same_window
|
||||
- remove_other_window
|
||||
|
||||
@@ -514,6 +514,8 @@ tags = "vertical-tabs"
|
||||
["browser_tab_groups_keyboard_focus.js"]
|
||||
tags = "vertical-tabs"
|
||||
|
||||
["browser_tab_groups_tab_interactions_telemetry.js"]
|
||||
|
||||
["browser_tab_groups_telemetry.js"]
|
||||
|
||||
["browser_tab_label_during_reload.js"]
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
/* 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/. */
|
||||
|
||||
const {
|
||||
openFirefoxViewTab,
|
||||
closeFirefoxViewTab,
|
||||
init: FirefoxViewTestUtilsInit,
|
||||
} = ChromeUtils.importESModule(
|
||||
"resource://testing-common/FirefoxViewTestUtils.sys.mjs"
|
||||
);
|
||||
FirefoxViewTestUtilsInit(this);
|
||||
|
||||
const { TabStateFlusher } = ChromeUtils.importESModule(
|
||||
"resource:///modules/sessionstore/TabStateFlusher.sys.mjs"
|
||||
);
|
||||
|
||||
const { UrlbarTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/UrlbarTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
let resetTelemetry = async () => {
|
||||
await Services.fog.testFlushAllChildren();
|
||||
Services.fog.testResetFOG();
|
||||
};
|
||||
|
||||
let assertMetricEmpty = async metricName => {
|
||||
Assert.equal(
|
||||
Glean.tabgroup.tabInteractions[metricName].testGetValue(),
|
||||
null,
|
||||
`tab_interactions.${metricName} starts empty`
|
||||
);
|
||||
};
|
||||
|
||||
let assertMetricFoundFor = async (metricName, count = 1) => {
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
return Glean.tabgroup.tabInteractions[metricName].testGetValue() == count;
|
||||
}, `Wait for tab_interactions.${metricName} to be recorded`);
|
||||
Assert.equal(
|
||||
Glean.tabgroup.tabInteractions[metricName].testGetValue(),
|
||||
count,
|
||||
`tab_interactions.${metricName} was recorded`
|
||||
);
|
||||
};
|
||||
|
||||
let activateTabContextMenuItem = async (
|
||||
selectedTab,
|
||||
menuItemSelector,
|
||||
submenuItemSelector
|
||||
) => {
|
||||
let submenuItem;
|
||||
let submenuItemHiddenPromise;
|
||||
|
||||
const tabContextMenu = window.document.getElementById("tabContextMenu");
|
||||
Assert.equal(
|
||||
tabContextMenu.state,
|
||||
"closed",
|
||||
"context menu is initially closed"
|
||||
);
|
||||
const contextMenuShown = BrowserTestUtils.waitForEvent(
|
||||
tabContextMenu,
|
||||
"popupshown",
|
||||
false,
|
||||
ev => ev.target == tabContextMenu
|
||||
);
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
selectedTab,
|
||||
{ type: "contextmenu", button: 2 },
|
||||
window
|
||||
);
|
||||
await contextMenuShown;
|
||||
|
||||
if (submenuItemSelector) {
|
||||
submenuItem = tabContextMenu.querySelector(submenuItemSelector);
|
||||
|
||||
const submenuPopupPromise = BrowserTestUtils.waitForEvent(
|
||||
submenuItem.menupopup,
|
||||
"popupshown"
|
||||
);
|
||||
submenuItem.openMenu(true);
|
||||
await submenuPopupPromise;
|
||||
|
||||
submenuItemHiddenPromise = BrowserTestUtils.waitForEvent(
|
||||
submenuItem.menupopup,
|
||||
"popuphidden"
|
||||
);
|
||||
}
|
||||
|
||||
const contextMenuHidden = BrowserTestUtils.waitForEvent(
|
||||
tabContextMenu,
|
||||
"popuphidden",
|
||||
false,
|
||||
ev => ev.target == tabContextMenu
|
||||
);
|
||||
tabContextMenu.activateItem(tabContextMenu.querySelector(menuItemSelector));
|
||||
await contextMenuHidden;
|
||||
if (submenuItemSelector) {
|
||||
await submenuItemHiddenPromise;
|
||||
}
|
||||
|
||||
Assert.equal(tabContextMenu.state, "closed", "context menu is closed");
|
||||
};
|
||||
|
||||
add_setup(async () => {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.tabs.groups.enabled", true]],
|
||||
});
|
||||
window.gTabsPanel.init();
|
||||
registerCleanupFunction(async () => {
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_tabInteractionsBasic() {
|
||||
let initialTab = window.gBrowser.tabs[0];
|
||||
await resetTelemetry();
|
||||
|
||||
let tab = BrowserTestUtils.addTab(window.gBrowser, "https://example.com");
|
||||
let group = window.gBrowser.addTabGroup([tab]);
|
||||
|
||||
info(
|
||||
"Test that selecting a tab in a group records tab_interactions.activate"
|
||||
);
|
||||
await assertMetricEmpty("activate");
|
||||
const tabSelectEvent = BrowserTestUtils.waitForEvent(window, "TabSelect");
|
||||
window.gBrowser.selectTabAtIndex(1);
|
||||
await tabSelectEvent;
|
||||
await assertMetricFoundFor("activate");
|
||||
|
||||
info(
|
||||
"Test that moving an existing tab into a tab group records tab_interactions.add"
|
||||
);
|
||||
let tab1 = BrowserTestUtils.addTab(window.gBrowser, "https://example.com");
|
||||
await assertMetricEmpty("add");
|
||||
window.gBrowser.moveTabToGroup(tab1, group, { isUserTriggered: true });
|
||||
await assertMetricFoundFor("add");
|
||||
|
||||
info(
|
||||
"Test that adding a new tab to a tab group records tab_interactions.new"
|
||||
);
|
||||
await assertMetricEmpty("new");
|
||||
BrowserTestUtils.addTab(window.gBrowser, "https://example.com", {
|
||||
tabGroup: group,
|
||||
});
|
||||
await assertMetricFoundFor("new");
|
||||
|
||||
info("Test that moving a tab within a group calls tab_interactions.reorder");
|
||||
await assertMetricEmpty("reorder");
|
||||
window.gBrowser.moveTabTo(group.tabs[0], {
|
||||
tabIndex: 3,
|
||||
isUserTriggered: true,
|
||||
});
|
||||
await assertMetricFoundFor("reorder");
|
||||
|
||||
info(
|
||||
"Test that duplicating a tab within a group calls tab_interactions.duplicate"
|
||||
);
|
||||
await assertMetricEmpty("duplicate");
|
||||
window.gBrowser.duplicateTab(group.tabs[0], true, { tabIndex: 2 });
|
||||
await assertMetricFoundFor("duplicate");
|
||||
|
||||
window.gBrowser.removeAllTabsBut(initialTab);
|
||||
await resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_tabInteractionsClose() {
|
||||
let initialTab = window.gBrowser.tabs[0];
|
||||
await resetTelemetry();
|
||||
FirefoxViewTestUtilsInit(this, window);
|
||||
|
||||
let tabs = Array.from({ length: 5 }, () => {
|
||||
return BrowserTestUtils.addTab(window.gBrowser, "https://example.com", {
|
||||
skipAnimation: true,
|
||||
});
|
||||
});
|
||||
let group = window.gBrowser.addTabGroup(tabs);
|
||||
|
||||
info(
|
||||
"Test that closing a tab using the tab's close button calls tab_interactions.close_tabstrip"
|
||||
);
|
||||
await assertMetricEmpty("close_tabstrip");
|
||||
group.tabs.at(-1).querySelector(".tab-close-button").click();
|
||||
await assertMetricFoundFor("close_tabstrip");
|
||||
|
||||
info(
|
||||
"Test that closing a tab via the tab context menu calls tab_interactions.close_tabstrip"
|
||||
);
|
||||
await activateTabContextMenuItem(group.tabs[0], "#context_closeTab");
|
||||
await assertMetricFoundFor("close_tabstrip", 2);
|
||||
|
||||
info(
|
||||
"Test that closing a tab via the tab close keyboard shortcut calls tab_interactions.close_tab_other"
|
||||
);
|
||||
window.gBrowser.selectedTab = group.tabs.at(-1);
|
||||
await assertMetricEmpty("close_tab_other");
|
||||
EventUtils.synthesizeKey("w", { accelKey: true }, window);
|
||||
await assertMetricFoundFor("close_tab_other");
|
||||
|
||||
info(
|
||||
"Test that closing a tab via top menu calls tab_interactions.close_tab_other"
|
||||
);
|
||||
window.document.getElementById("cmd_close").doCommand();
|
||||
await assertMetricFoundFor("close_tab_other", 2);
|
||||
|
||||
info(
|
||||
"Test that closing a tab via firefox view calls tab_interactions.close_tab_other"
|
||||
);
|
||||
await openFirefoxViewTab(window).then(async viewTab => {
|
||||
const openTabs = viewTab.linkedBrowser.contentDocument
|
||||
.querySelector("named-deck > view-recentbrowsing view-opentabs")
|
||||
.shadowRoot.querySelector("view-opentabs-card").tabList.rowEls;
|
||||
const tabElement = Array.from(openTabs).find(t => t.__tabElement.group);
|
||||
tabElement.shadowRoot.querySelector("moz-button.dismiss-button").click();
|
||||
await assertMetricFoundFor("close_tab_other", 3);
|
||||
});
|
||||
await closeFirefoxViewTab(window);
|
||||
|
||||
window.gBrowser.removeAllTabsBut(initialTab);
|
||||
await resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_tabInteractionsCloseViaAnotherTabContext() {
|
||||
let initialTab = window.gBrowser.tabs[0];
|
||||
await resetTelemetry();
|
||||
|
||||
window.gBrowser.addTabGroup([
|
||||
BrowserTestUtils.addTab(window.gBrowser, "https://example.com", {
|
||||
skipAnimation: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
await assertMetricEmpty("close_tab_other");
|
||||
|
||||
info(
|
||||
"Test that closing a tab via the tab context menu 'close other tabs' command calls tab_interactions.close_tab_other"
|
||||
);
|
||||
await activateTabContextMenuItem(
|
||||
initialTab,
|
||||
"#context_closeOtherTabs",
|
||||
"#context_closeTabOptions"
|
||||
);
|
||||
await assertMetricFoundFor("close_tab_other");
|
||||
|
||||
info(
|
||||
"Test that closing a tab via the tab context menu 'close tabs to left' command calls tab_interactions.close_tab_other"
|
||||
);
|
||||
window.gBrowser.addTabGroup([
|
||||
BrowserTestUtils.addTab(window.gBrowser, "https://example.com", {
|
||||
skipAnimation: true,
|
||||
}),
|
||||
]);
|
||||
window.gBrowser.moveTabToEnd(initialTab);
|
||||
await activateTabContextMenuItem(
|
||||
initialTab,
|
||||
"#context_closeTabsToTheStart",
|
||||
"#context_closeTabOptions"
|
||||
);
|
||||
await assertMetricFoundFor("close_tab_other", 2);
|
||||
|
||||
info(
|
||||
"Test that closing a tab via the tab context menu 'close tabs to right' command calls tab_interactions.close_tab_other"
|
||||
);
|
||||
window.gBrowser.addTabGroup([
|
||||
BrowserTestUtils.addTab(window.gBrowser, "https://example.com", {
|
||||
skipAnimation: true,
|
||||
}),
|
||||
]);
|
||||
await activateTabContextMenuItem(
|
||||
initialTab,
|
||||
"#context_closeTabsToTheEnd",
|
||||
"#context_closeTabOptions"
|
||||
);
|
||||
await assertMetricFoundFor("close_tab_other", 3);
|
||||
|
||||
info(
|
||||
"Test that closing a tab via the tab context menu 'close duplicate tabs' command calls tab_interactions.close_tab_other"
|
||||
);
|
||||
let duplicateTabs = [
|
||||
BrowserTestUtils.addTab(window.gBrowser, "https://example.com", {
|
||||
skipAnimation: true,
|
||||
}),
|
||||
BrowserTestUtils.addTab(window.gBrowser, "https://example.com", {
|
||||
skipAnimation: true,
|
||||
}),
|
||||
];
|
||||
await Promise.all(
|
||||
duplicateTabs.map(t => BrowserTestUtils.browserLoaded(t.linkedBrowser))
|
||||
);
|
||||
window.gBrowser.addTabGroup([duplicateTabs[1]]);
|
||||
|
||||
await activateTabContextMenuItem(
|
||||
duplicateTabs[0],
|
||||
"#context_closeDuplicateTabs",
|
||||
"#context_closeTabOptions"
|
||||
);
|
||||
await assertMetricFoundFor("close_tab_other", 4);
|
||||
|
||||
window.gBrowser.removeAllTabsBut(initialTab);
|
||||
await resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_tabInteractionsCloseTabOverflowMenu() {
|
||||
let initialTab = window.gBrowser.tabs[0];
|
||||
await resetTelemetry();
|
||||
FirefoxViewTestUtilsInit(this, window);
|
||||
|
||||
let tab = BrowserTestUtils.addTab(window.gBrowser, "https://example.com", {
|
||||
skipAnimation: true,
|
||||
});
|
||||
window.gBrowser.addTabGroup([tab]);
|
||||
|
||||
info(
|
||||
"Test that closing a tab from the tab overflow menu calls tab_interactions.close_tabmenu"
|
||||
);
|
||||
let viewShown = BrowserTestUtils.waitForEvent(
|
||||
window.document.getElementById("allTabsMenu-allTabsView"),
|
||||
"ViewShown"
|
||||
);
|
||||
window.document.getElementById("alltabs-button").click();
|
||||
await viewShown;
|
||||
|
||||
await assertMetricEmpty("close_tabmenu");
|
||||
window.document
|
||||
.querySelector(".all-tabs-item.grouped .all-tabs-close-button")
|
||||
.click();
|
||||
await assertMetricFoundFor("close_tabmenu");
|
||||
|
||||
let panel = window.document
|
||||
.getElementById("allTabsMenu-allTabsView")
|
||||
.closest("panel");
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
let hidden = BrowserTestUtils.waitForPopupEvent(panel, "hidden");
|
||||
panel.hidePopup();
|
||||
await hidden;
|
||||
|
||||
window.gBrowser.removeAllTabsBut(initialTab);
|
||||
await resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_tabInteractionsRemoveFromGroup() {
|
||||
let initialTab = window.gBrowser.tabs[0];
|
||||
await resetTelemetry();
|
||||
|
||||
let tabs = Array.from({ length: 3 }, () => {
|
||||
return BrowserTestUtils.addTab(window.gBrowser, "https://example.com", {
|
||||
skipAnimation: true,
|
||||
});
|
||||
});
|
||||
let group = window.gBrowser.addTabGroup(tabs);
|
||||
|
||||
info(
|
||||
"Test that moving a tab out of a tab group calls tab_interactions.remove_same_window"
|
||||
);
|
||||
await assertMetricEmpty("remove_same_window");
|
||||
window.gBrowser.moveTabTo(group.tabs[0], {
|
||||
tabIndex: 0,
|
||||
isUserTriggered: true,
|
||||
});
|
||||
await assertMetricFoundFor("remove_same_window");
|
||||
|
||||
info(
|
||||
"Test that moving a tab out of a tab group and into a different (existing) window calls tab_interactions.remove_other_window"
|
||||
);
|
||||
await assertMetricEmpty("remove_other_window");
|
||||
let newWin = await BrowserTestUtils.openNewBrowserWindow();
|
||||
newWin.gBrowser.adoptTab(group.tabs[0]);
|
||||
await assertMetricFoundFor("remove_other_window");
|
||||
await BrowserTestUtils.closeWindow(newWin);
|
||||
|
||||
info(
|
||||
"Test that moving a tab out of a tab group and into a different (new) window calls tab_interactions.remove_new_window"
|
||||
);
|
||||
await assertMetricEmpty("remove_new_window");
|
||||
let newWindowPromise = BrowserTestUtils.waitForNewWindow();
|
||||
await EventUtils.synthesizePlainDragAndDrop({
|
||||
srcElement: group.tabs[0],
|
||||
srcWindow: window,
|
||||
destElement: null,
|
||||
// don't move horizontally because that could cause a tab move
|
||||
// animation, and there's code to prevent a tab detaching if
|
||||
// the dragged tab is released while the animation is running.
|
||||
stepX: 0,
|
||||
stepY: 100,
|
||||
});
|
||||
newWin = await newWindowPromise;
|
||||
await assertMetricFoundFor("remove_new_window");
|
||||
await BrowserTestUtils.closeWindow(newWin);
|
||||
|
||||
window.gBrowser.removeAllTabsBut(initialTab);
|
||||
await resetTelemetry();
|
||||
});
|
||||
@@ -715,128 +715,6 @@ add_task(async function test_tabContextMenu_addTabsToGroup() {
|
||||
await resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_tabInteractions() {
|
||||
let assertMetricEmpty = async metricName => {
|
||||
Assert.equal(
|
||||
Glean.tabgroup.tabInteractions[metricName].testGetValue(),
|
||||
null,
|
||||
`tab_interactions.${metricName} starts empty`
|
||||
);
|
||||
};
|
||||
|
||||
let assertOneMetricFoundFor = async metricName => {
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
return Glean.tabgroup.tabInteractions[metricName].testGetValue() !== null;
|
||||
}, `Wait for tab_interactions.${metricName} to be recorded`);
|
||||
Assert.equal(
|
||||
Glean.tabgroup.tabInteractions[metricName].testGetValue(),
|
||||
1,
|
||||
`tab_interactions.${metricName} was recorded`
|
||||
);
|
||||
};
|
||||
|
||||
let initialTab = win.gBrowser.tabs[0];
|
||||
|
||||
await resetTelemetry();
|
||||
let group = await makeTabGroup();
|
||||
|
||||
info(
|
||||
"Test that selecting a tab in a group records tab_interactions.activate"
|
||||
);
|
||||
await assertMetricEmpty("activate");
|
||||
const tabSelectEvent = BrowserTestUtils.waitForEvent(win, "TabSelect");
|
||||
win.gBrowser.selectTabAtIndex(1);
|
||||
await tabSelectEvent;
|
||||
await assertOneMetricFoundFor("activate");
|
||||
|
||||
info(
|
||||
"Test that moving an existing tab into a tab group records tab_interactions.add"
|
||||
);
|
||||
let tab1 = BrowserTestUtils.addTab(win.gBrowser, "https://example.com");
|
||||
await assertMetricEmpty("add");
|
||||
win.gBrowser.moveTabToGroup(tab1, group, { isUserTriggered: true });
|
||||
await assertOneMetricFoundFor("add");
|
||||
|
||||
info(
|
||||
"Test that adding a new tab to a tab group records tab_interactions.new"
|
||||
);
|
||||
await assertMetricEmpty("new");
|
||||
BrowserTestUtils.addTab(win.gBrowser, "https://example.com", {
|
||||
tabGroup: group,
|
||||
});
|
||||
await assertOneMetricFoundFor("new");
|
||||
|
||||
info("Test that moving a tab within a group calls tab_interactions.reorder");
|
||||
await assertMetricEmpty("reorder");
|
||||
win.gBrowser.moveTabTo(group.tabs[0], { tabIndex: 3, isUserTriggered: true });
|
||||
await assertOneMetricFoundFor("reorder");
|
||||
|
||||
info(
|
||||
"Test that duplicating a tab within a group calls tab_interactions.duplicate"
|
||||
);
|
||||
await assertMetricEmpty("duplicate");
|
||||
win.gBrowser.duplicateTab(group.tabs[0], true, { tabIndex: 2 });
|
||||
await assertOneMetricFoundFor("duplicate");
|
||||
|
||||
info(
|
||||
"Test that closing a tab using the tab's close button calls tab_interactions.close_tabstrip"
|
||||
);
|
||||
await assertMetricEmpty("close_tabstrip");
|
||||
group.tabs.at(-1).querySelector(".tab-close-button").click();
|
||||
await assertOneMetricFoundFor("close_tabstrip");
|
||||
|
||||
info(
|
||||
"Test that closing a tab from the tab overflow menu calls tab_interactions.close_tabmenu"
|
||||
);
|
||||
await openTabsMenu();
|
||||
await assertMetricEmpty("close_tabmenu");
|
||||
win.document
|
||||
.querySelector(".all-tabs-item.grouped .all-tabs-close-button")
|
||||
.click();
|
||||
await assertOneMetricFoundFor("close_tabmenu");
|
||||
await closeTabsMenu();
|
||||
|
||||
info(
|
||||
"Test that moving a tab out of a tab group calls tab_interactions.remove_same_window"
|
||||
);
|
||||
await assertMetricEmpty("remove_same_window");
|
||||
win.gBrowser.moveTabTo(group.tabs[0], { tabIndex: 0, isUserTriggered: true });
|
||||
await assertOneMetricFoundFor("remove_same_window");
|
||||
|
||||
info(
|
||||
"Test that moving a tab out of a tab group and into a different (existing) window calls tab_interactions.remove_other_window"
|
||||
);
|
||||
await assertMetricEmpty("remove_other_window");
|
||||
let tab2 = BrowserTestUtils.addTab(win.gBrowser, "https://example.com");
|
||||
win.gBrowser.moveTabToGroup(tab2, group, { isUserTriggered: true });
|
||||
let newWin = await BrowserTestUtils.openNewBrowserWindow();
|
||||
newWin.gBrowser.adoptTab(tab2);
|
||||
await assertOneMetricFoundFor("remove_other_window");
|
||||
await BrowserTestUtils.closeWindow(newWin);
|
||||
|
||||
info(
|
||||
"Test that moving a tab out of a tab group and into a different (new) window calls tab_interactions.remove_new_window"
|
||||
);
|
||||
await assertMetricEmpty("remove_new_window");
|
||||
let newWindowPromise = BrowserTestUtils.waitForNewWindow();
|
||||
await EventUtils.synthesizePlainDragAndDrop({
|
||||
srcElement: group.tabs[0],
|
||||
srcWindow: win,
|
||||
destElement: null,
|
||||
// don't move horizontally because that could cause a tab move
|
||||
// animation, and there's code to prevent a tab detaching if
|
||||
// the dragged tab is released while the animation is running.
|
||||
stepX: 0,
|
||||
stepY: 100,
|
||||
});
|
||||
newWin = await newWindowPromise;
|
||||
await assertOneMetricFoundFor("remove_new_window");
|
||||
await BrowserTestUtils.closeWindow(newWin);
|
||||
|
||||
win.gBrowser.removeAllTabsBut(initialTab);
|
||||
await resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(async function test_groupInteractions() {
|
||||
await resetTelemetry();
|
||||
let group = await makeTabGroup();
|
||||
|
||||
@@ -1269,13 +1269,16 @@ export let BrowserUsageTelemetry = {
|
||||
|
||||
_onTabClosed(event) {
|
||||
const group = event.target?.group;
|
||||
const isUserTriggered = event.detail?.isUserTriggered;
|
||||
const source = event.detail?.telemetrySource;
|
||||
|
||||
if (group) {
|
||||
if (group && isUserTriggered) {
|
||||
if (source == lazy.TabMetrics.METRIC_SOURCE.TAB_STRIP) {
|
||||
Glean.tabgroup.tabInteractions.close_tabstrip.add();
|
||||
} else if (source == lazy.TabMetrics.METRIC_SOURCE.TAB_OVERFLOW_MENU) {
|
||||
Glean.tabgroup.tabInteractions.close_tabmenu.add();
|
||||
} else {
|
||||
Glean.tabgroup.tabInteractions.close_tab_other.add();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user