browser_multiselect_tabs_unload_telemetry.js (9226B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 /** 5 * Opens the context menu on a tab and waits for it to be shown. 6 * 7 * @param tab The tab to open a context menu on 8 * @returns {Promise} Promise object with the menu. 9 */ 10 async function openTabMenuFor(tab) { 11 let tabMenu = tab.ownerDocument.getElementById("tabContextMenu"); 12 13 let tabMenuShown = BrowserTestUtils.waitForPopupEvent(tabMenu, "shown"); 14 EventUtils.synthesizeMouseAtCenter( 15 tab, 16 { type: "contextmenu" }, 17 tab.ownerGlobal 18 ); 19 await tabMenuShown; 20 21 return tabMenu; 22 } 23 24 /** 25 * Adds new tabs to `gBrowser`. 26 * 27 * @param {number} numberOfTabs The number of new tabs to add. 28 * @returns {Promise<Array>} Promise object containing the new tabs that were added. 29 */ 30 async function addBrowserTabs(numberOfTabs) { 31 let tabs = []; 32 for (let i = 0; i < numberOfTabs; i++) { 33 tabs.push(await addTab(`http://mochi.test:8888/#${i}`)); 34 } 35 return tabs; 36 } 37 38 add_setup(async function () { 39 // This is helpful to avoid some weird race conditions in the test, specifically 40 // the assertion that !this.blankTab in AsyncTabSwitcher when adding a new tab. 41 await BrowserTestUtils.loadURIString({ 42 browser: gBrowser.selectedTab.linkedBrowser, 43 uriString: "http://mochi.test:8888/#originalTab", 44 }); 45 let originalTab = gBrowser.selectedTab; 46 // switch to Firefox View tab to initialize it 47 FirefoxViewHandler.openTab(); 48 // switch back to the original tab since tests expect this 49 await BrowserTestUtils.switchTab(gBrowser, originalTab); 50 51 await SpecialPowers.pushPrefEnv({ 52 set: [ 53 ["test.wait300msAfterTabSwitch", true], 54 ["browser.tabs.unloadTabInContextMenu", true], 55 ], 56 }); 57 }); 58 59 /** 60 * Checks various common properties of a tabExplicitUnload event. 61 * 62 * @param e The telemetry event to check 63 */ 64 function checkEventCommon(e) { 65 Assert.equal(e.category, "browser.engagement", "correct category"); 66 Assert.equal(e.name, "tab_explicit_unload", "correct name"); 67 // Since we're unloading small pages here, the memory may not change much or might even 68 // go up a smidge. 69 let memoryBefore = parseInt(e.extra.memory_before, 10); 70 let memoryAfter = parseInt(e.extra.memory_after, 10); 71 Assert.less( 72 memoryAfter - memoryBefore, 73 100000, 74 `Memory should go down after unload (before: ${memoryBefore}, after ${memoryAfter})` 75 ); 76 Assert.greaterOrEqual( 77 parseInt(e.extra.time_to_unload_in_ms, 10), 78 0, 79 "time_to_unload should be >= 0" 80 ); 81 Assert.less( 82 parseInt(e.extra.time_to_unload_in_ms, 10), 83 10000, 84 "time_to_unload should be within reason" 85 ); 86 } 87 88 /** 89 * Gets a promise that will be fulfilled when the tab is unloaded. 90 * 91 * @param tab The tab that will be unloaded 92 * @returns {Promise} A promise that will be fulfilled when 93 * the tab is unloaded. 94 */ 95 function getWaitForUnloadedPromise(tab) { 96 return BrowserTestUtils.waitForEvent(tab, "TabBrowserDiscarded"); 97 } 98 99 /** 100 * Waits for a tabExplicitUnload telemetry event to occur. 101 * 102 * @returns {Promise} A promise that will be fulfilled when the 103 * telemetry event occurs. 104 */ 105 async function waitForTelemetryEvent() { 106 await TestUtils.waitForCondition(() => { 107 let events = Glean.browserEngagement.tabExplicitUnload.testGetValue(); 108 if (!events) { 109 return false; 110 } 111 return !!events.length; 112 }); 113 } 114 115 /** 116 * Unload the currently selected tab and one other and ensure 117 * the telemetry is correct. 118 */ 119 add_task(async function test_unload_selected_and_one_other_tab() { 120 let [tab1, tab2, tab3] = await addBrowserTabs(3); 121 122 let menuItemUnload = document.getElementById("context_unloadTab"); 123 124 await BrowserTestUtils.switchTab(gBrowser, tab1); 125 await triggerClickOn(tab2, { ctrlKey: true }); 126 127 Services.fog.testResetFOG(); 128 updateTabContextMenu(tab1); 129 let tab1UnloadedPromise = getWaitForUnloadedPromise(tab1); 130 let tab2UnloadedPromise = getWaitForUnloadedPromise(tab2); 131 { 132 let menu = await openTabMenuFor(tab1); 133 let menuHiddenPromise = BrowserTestUtils.waitForPopupEvent(menu, "hidden"); 134 menu.activateItem(menuItemUnload); 135 await menuHiddenPromise; 136 } 137 138 await Promise.all([tab1UnloadedPromise, tab2UnloadedPromise]); 139 140 await waitForTelemetryEvent(); 141 let unloadTelemetry = 142 Glean.browserEngagement.tabExplicitUnload.testGetValue(); 143 Assert.equal( 144 unloadTelemetry.length, 145 1, 146 "should get exactly one telemetry event" 147 ); 148 let e = unloadTelemetry[0]; 149 checkEventCommon(e); 150 Assert.equal(e.extra.unload_selected_tab, "true", "did unload selected tab"); 151 Assert.equal(e.extra.all_tabs_unloaded, "false", "did not unload everything"); 152 Assert.equal(e.extra.tabs_unloaded, "2", "correct number of tabs unloaded"); 153 154 Services.fog.testResetFOG(); 155 await BrowserTestUtils.removeTab(tab3); 156 await BrowserTestUtils.removeTab(tab2); 157 await BrowserTestUtils.removeTab(tab1); 158 }); 159 160 /** 161 * Unload one unselected tab and ensure the telemetry is correct. 162 */ 163 add_task(async function test_unload_one_unselected_tab() { 164 let [tab1, tab2, tab3] = await addBrowserTabs(3); 165 166 let menuItemUnload = document.getElementById("context_unloadTab"); 167 await BrowserTestUtils.switchTab(gBrowser, tab1); 168 updateTabContextMenu(tab2); 169 Services.fog.testResetFOG(); 170 let tab2UnloadedPromise = getWaitForUnloadedPromise(tab2); 171 { 172 let menu = await openTabMenuFor(tab2); 173 let menuHiddenPromise = BrowserTestUtils.waitForPopupEvent(menu, "hidden"); 174 menu.activateItem(menuItemUnload); 175 await menuHiddenPromise; 176 } 177 await tab2UnloadedPromise; 178 179 await waitForTelemetryEvent(); 180 let unloadTelemetry = 181 Glean.browserEngagement.tabExplicitUnload.testGetValue(); 182 Assert.equal( 183 unloadTelemetry.length, 184 1, 185 "should get exactly one telemetry event" 186 ); 187 let e = unloadTelemetry[0]; 188 checkEventCommon(e); 189 Assert.equal( 190 e.extra.unload_selected_tab, 191 "false", 192 "did not unload selected tab" 193 ); 194 Assert.equal(e.extra.all_tabs_unloaded, "false", "did not unload everything"); 195 Assert.equal(e.extra.tabs_unloaded, "1", "correct number of tabs unloaded"); 196 197 Services.fog.testResetFOG(); 198 await BrowserTestUtils.removeTab(tab3); 199 await BrowserTestUtils.removeTab(tab2); 200 await BrowserTestUtils.removeTab(tab1); 201 }); 202 203 /** 204 * Unload just the selected tab and ensure the telemetry is correct. 205 */ 206 add_task(async function test_unload_selected_tab() { 207 let [tab1, tab2, tab3] = await addBrowserTabs(3); 208 209 let menuItemUnload = document.getElementById("context_unloadTab"); 210 await BrowserTestUtils.switchTab(gBrowser, tab1); 211 Services.fog.testResetFOG(); 212 let tab1UnloadedPromise = getWaitForUnloadedPromise(tab1); 213 { 214 let menu = await openTabMenuFor(tab1); 215 let menuHiddenPromise = BrowserTestUtils.waitForPopupEvent(menu, "hidden"); 216 menu.activateItem(menuItemUnload); 217 await menuHiddenPromise; 218 } 219 await tab1UnloadedPromise; 220 221 await waitForTelemetryEvent(); 222 let unloadTelemetry = 223 Glean.browserEngagement.tabExplicitUnload.testGetValue(); 224 Assert.equal( 225 unloadTelemetry.length, 226 1, 227 "should get exactly one telemetry event" 228 ); 229 let e = unloadTelemetry[0]; 230 checkEventCommon(e); 231 Assert.equal(e.extra.unload_selected_tab, "true", "did unload selected tab"); 232 Assert.equal(e.extra.all_tabs_unloaded, "false", "did not unload everything"); 233 Assert.equal(e.extra.tabs_unloaded, "1", "correct number of tabs unloaded"); 234 235 Services.fog.testResetFOG(); 236 await BrowserTestUtils.removeTab(tab3); 237 await BrowserTestUtils.removeTab(tab2); 238 await BrowserTestUtils.removeTab(tab1); 239 }); 240 241 /** 242 * Unload all tabs in the window and ensure 243 * the telemetry is correct. 244 */ 245 add_task(async function test_unload_all_tabs() { 246 let originalTab = gBrowser.selectedTab; 247 let [tab1, tab2, tab3] = await addBrowserTabs(3); 248 249 let menuItemUnload = document.getElementById("context_unloadTab"); 250 await triggerClickOn(tab1, { ctrlKey: true }); 251 await triggerClickOn(tab2, { ctrlKey: true }); 252 await triggerClickOn(tab3, { ctrlKey: true }); 253 updateTabContextMenu(originalTab); 254 Services.fog.testResetFOG(); 255 let allTabsUnloadedPromises = [originalTab, tab1, tab2, tab3].map( 256 getWaitForUnloadedPromise 257 ); 258 { 259 let menu = await openTabMenuFor(originalTab); 260 let menuHiddenPromise = BrowserTestUtils.waitForPopupEvent(menu, "hidden"); 261 menu.activateItem(menuItemUnload); 262 await menuHiddenPromise; 263 } 264 265 await Promise.all(allTabsUnloadedPromises); 266 267 await waitForTelemetryEvent(); 268 let unloadTelemetry = 269 Glean.browserEngagement.tabExplicitUnload.testGetValue(); 270 Assert.equal( 271 unloadTelemetry.length, 272 1, 273 "should get exactly one telemetry event" 274 ); 275 let e = unloadTelemetry[0]; 276 checkEventCommon(e); 277 Assert.equal(e.extra.unload_selected_tab, "true", "did unload selected tab"); 278 Assert.equal(e.extra.all_tabs_unloaded, "true", "did unload everything"); 279 Assert.equal(e.extra.tabs_unloaded, "4", "correct number of tabs unloaded"); 280 281 Services.fog.testResetFOG(); 282 await BrowserTestUtils.removeTab(tab3); 283 await BrowserTestUtils.removeTab(tab2); 284 await BrowserTestUtils.removeTab(tab1); 285 }); 286 287 add_task(async function test_cleanup() { 288 await BrowserTestUtils.removeTab(FirefoxViewHandler.tab); 289 });