summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/general/browser_ctrlTab.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/general/browser_ctrlTab.js')
-rw-r--r--browser/base/content/test/general/browser_ctrlTab.js464
1 files changed, 464 insertions, 0 deletions
diff --git a/browser/base/content/test/general/browser_ctrlTab.js b/browser/base/content/test/general/browser_ctrlTab.js
new file mode 100644
index 0000000000..7c4a7b6c23
--- /dev/null
+++ b/browser/base/content/test/general/browser_ctrlTab.js
@@ -0,0 +1,464 @@
+/* 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/. */
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.ctrlTab.sortByRecentlyUsed", true]],
+ });
+
+ BrowserTestUtils.addTab(gBrowser);
+ BrowserTestUtils.addTab(gBrowser);
+ BrowserTestUtils.addTab(gBrowser);
+
+ // While doing this test, we should make sure the selected tab in the tab
+ // preview is not changed by mouse events. That may happen after closing
+ // the selected tab with ctrl+W. Disable all mouse events to prevent it.
+ for (let node of ctrlTab.previews) {
+ node.style.pointerEvents = "none";
+ }
+ registerCleanupFunction(function () {
+ for (let node of ctrlTab.previews) {
+ try {
+ node.style.removeProperty("pointer-events");
+ } catch (e) {}
+ }
+ });
+
+ checkTabs(4);
+
+ await ctrlTabTest([2], 1, 0);
+ await ctrlTabTest([2, 3, 1], 2, 2);
+ await ctrlTabTest([], 4, 2);
+
+ {
+ let selectedIndex = gBrowser.tabContainer.selectedIndex;
+ await pressCtrlTab();
+ await pressCtrlTab(true);
+ await releaseCtrl();
+ is(
+ gBrowser.tabContainer.selectedIndex,
+ selectedIndex,
+ "Ctrl+Tab -> Ctrl+Shift+Tab keeps the selected tab"
+ );
+ }
+
+ {
+ info("test for bug 445369");
+ let tabs = gBrowser.tabs.length;
+ await pressCtrlTab();
+ await synthesizeCtrlW();
+ is(gBrowser.tabs.length, tabs - 1, "Ctrl+Tab -> Ctrl+W removes one tab");
+ await releaseCtrl();
+ }
+
+ {
+ info("test for bug 667314");
+ let tabs = gBrowser.tabs.length;
+ await pressCtrlTab();
+ await pressCtrlTab(true);
+ await synthesizeCtrlW();
+ is(
+ gBrowser.tabs.length,
+ tabs - 1,
+ "Ctrl+Tab -> Ctrl+W removes the selected tab"
+ );
+ await releaseCtrl();
+ }
+
+ BrowserTestUtils.addTab(gBrowser);
+ checkTabs(3);
+ await ctrlTabTest([2, 1, 0], 7, 1);
+
+ {
+ info("test for bug 1292049");
+ let tabToClose = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:buildconfig"
+ );
+ checkTabs(4);
+ selectTabs([0, 1, 2, 3]);
+
+ let promise = BrowserTestUtils.waitForSessionStoreUpdate(tabToClose);
+ BrowserTestUtils.removeTab(tabToClose);
+ await promise;
+ checkTabs(3);
+ undoCloseTab();
+ checkTabs(4);
+ is(
+ gBrowser.tabContainer.selectedIndex,
+ 3,
+ "tab is selected after closing and restoring it"
+ );
+
+ await ctrlTabTest([], 1, 2);
+ }
+
+ {
+ info("test for bug 445369");
+ checkTabs(4);
+ selectTabs([1, 2, 0]);
+
+ let selectedTab = gBrowser.selectedTab;
+ let tabToRemove = gBrowser.tabs[1];
+
+ await pressCtrlTab();
+ await pressCtrlTab();
+ await synthesizeCtrlW();
+ ok(
+ !tabToRemove.parentNode,
+ "Ctrl+Tab*2 -> Ctrl+W removes the second most recently selected tab"
+ );
+
+ await pressCtrlTab(true);
+ await pressCtrlTab(true);
+ await releaseCtrl();
+ ok(
+ selectedTab.selected,
+ "Ctrl+Tab*2 -> Ctrl+W -> Ctrl+Shift+Tab*2 keeps the selected tab"
+ );
+ }
+ gBrowser.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
+ checkTabs(2);
+
+ await ctrlTabTest([1], 1, 0);
+
+ gBrowser.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
+ checkTabs(1);
+
+ {
+ info("test for bug 445768");
+ let focusedWindow = document.commandDispatcher.focusedWindow;
+ let eventConsumed = true;
+ let detectKeyEvent = function (event) {
+ eventConsumed = event.defaultPrevented;
+ };
+ document.addEventListener("keypress", detectKeyEvent);
+ await pressCtrlTab();
+ document.removeEventListener("keypress", detectKeyEvent);
+ ok(
+ eventConsumed,
+ "Ctrl+Tab consumed by the tabbed browser if one tab is open"
+ );
+ is(
+ focusedWindow,
+ document.commandDispatcher.focusedWindow,
+ "Ctrl+Tab doesn't change focus if one tab is open"
+ );
+ }
+
+ // eslint-disable-next-line no-lone-blocks
+ {
+ info("Bug 1731050: test hidden tabs");
+ checkTabs(1);
+ await BrowserTestUtils.addTab(gBrowser);
+ await BrowserTestUtils.addTab(gBrowser);
+ await BrowserTestUtils.addTab(gBrowser);
+ await BrowserTestUtils.addTab(gBrowser);
+ FirefoxViewHandler.tab = await BrowserTestUtils.addTab(gBrowser);
+
+ gBrowser.hideTab(FirefoxViewHandler.tab);
+ FirefoxViewHandler.openTab();
+ selectTabs([1, 2, 3, 4, 3]);
+ gBrowser.hideTab(gBrowser.tabs[4]);
+ selectTabs([2]);
+ gBrowser.hideTab(gBrowser.tabs[3]);
+
+ is(gBrowser.tabs[5].hidden, true, "Tab at index 5 is hidden");
+ is(gBrowser.tabs[4].hidden, true, "Tab at index 4 is hidden");
+ is(gBrowser.tabs[3].hidden, true, "Tab at index 3 is hidden");
+ is(gBrowser.tabs[2].hidden, false, "Tab at index 2 is still shown");
+ is(gBrowser.tabs[1].hidden, false, "Tab at index 1 is still shown");
+ is(gBrowser.tabs[0].hidden, false, "Tab at index 0 is still shown");
+
+ await ctrlTabTest([], 1, 1);
+ await ctrlTabTest([], 2, 0);
+ gBrowser.showTab(gBrowser.tabs[4]);
+ await ctrlTabTest([2], 3, 4);
+ await ctrlTabTest([], 4, 4);
+ gBrowser.showTab(gBrowser.tabs[3]);
+ await ctrlTabTest([], 4, 3);
+ await ctrlTabTest([], 6, 4);
+ FirefoxViewHandler.openTab();
+ // Fx View tab should be visible in the panel while selected.
+ await ctrlTabTest([], 5, 1);
+ // Fx View tab should no longer be visible.
+ await ctrlTabTest([], 1, 4);
+
+ for (let i = 5; i > 0; i--) {
+ await BrowserTestUtils.removeTab(gBrowser.tabs[i]);
+ }
+ FirefoxViewHandler.tab = null;
+ info("End hidden tabs test");
+ }
+
+ {
+ info("Bug 1293692: Test asynchronous tab previews");
+
+ checkTabs(1);
+
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.pagethumbnails.capturing_disabled", false]],
+ });
+
+ await BrowserTestUtils.addTab(gBrowser);
+ await BrowserTestUtils.addTab(gBrowser);
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ `${getRootDirectory(gTestPath)}dummy_page.html`
+ );
+
+ info("Pressing Ctrl+Tab to open the panel");
+ ok(canOpen(), "Ctrl+Tab can open the preview panel");
+ let panelShown = BrowserTestUtils.waitForEvent(ctrlTab.panel, "popupshown");
+ EventUtils.synthesizeKey("VK_TAB", { ctrlKey: true });
+ await TestUtils.waitForTick();
+
+ let observedPreview = ctrlTab.previews[0];
+ is(observedPreview._tab, tab, "The observed preview is for the new tab");
+ ok(
+ !observedPreview._canvas.firstElementChild,
+ "The preview <canvas> does not exist yet"
+ );
+
+ let emptyCanvas = PageThumbs.createCanvas(window);
+ let emptyImageData = emptyCanvas
+ .getContext("2d")
+ .getImageData(0, 0, ctrlTab.canvasWidth, ctrlTab.canvasHeight)
+ .data.slice(0, 15)
+ .toString();
+
+ info("Waiting for the preview <canvas> to be loaded");
+ await BrowserTestUtils.waitForMutationCondition(
+ observedPreview._canvas,
+ {
+ childList: true,
+ attributes: true,
+ subtree: true,
+ },
+ () =>
+ HTMLCanvasElement.isInstance(observedPreview._canvas.firstElementChild)
+ );
+
+ // Ensure the image is not blank (see bug 1293692). The canvas shouldn't be
+ // rendered at all until it has image data, but this will allow us to catch
+ // any regressions in the future.
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ emptyImageData !==
+ observedPreview._canvas.firstElementChild
+ .getContext("2d")
+ .getImageData(0, 0, ctrlTab.canvasWidth, ctrlTab.canvasHeight)
+ .data.slice(0, 15)
+ .toString(),
+ "The preview <canvas> should be filled with a thumbnail"
+ );
+
+ // Wait for the panel to be shown.
+ await panelShown;
+ ok(isOpen(), "The preview panel is open");
+
+ // Keep the same tab selected.
+ await pressCtrlTab(true);
+ await releaseCtrl();
+
+ // The next time the panel is open, our preview should now be an <img>, the
+ // thumbnail that was previously drawn in a <canvas> having been cached and
+ // now being immediately available for reuse.
+ info("Pressing Ctrl+Tab to open the panel again");
+ let imgExists = BrowserTestUtils.waitForMutationCondition(
+ observedPreview._canvas,
+ {
+ childList: true,
+ attributes: true,
+ subtree: true,
+ },
+ () => {
+ let img = observedPreview._canvas.firstElementChild;
+ return (
+ img &&
+ HTMLImageElement.isInstance(img) &&
+ img.src &&
+ img.complete &&
+ img.naturalWidth
+ );
+ }
+ );
+ let panelShownAgain = BrowserTestUtils.waitForEvent(
+ ctrlTab.panel,
+ "popupshown"
+ );
+ EventUtils.synthesizeKey("VK_TAB", { ctrlKey: true });
+
+ info("Waiting for the preview <img> to be loaded");
+ await imgExists;
+ ok(
+ true,
+ `The preview image is an <img> with src="${observedPreview._canvas.firstElementChild.src}"`
+ );
+ await panelShownAgain;
+ await releaseCtrl();
+
+ for (let i = gBrowser.tabs.length - 1; i > 0; i--) {
+ await BrowserTestUtils.removeTab(gBrowser.tabs[i]);
+ }
+ checkTabs(1);
+ }
+
+ /* private utility functions */
+
+ /**
+ * @return the number of times (Shift+)Ctrl+Tab was pressed
+ */
+ async function pressCtrlTab(aShiftKey = false) {
+ let promise;
+ if (!isOpen() && canOpen()) {
+ ok(
+ !aShiftKey,
+ "Shouldn't attempt to open the panel by pressing Shift+Ctrl+Tab"
+ );
+ info("Pressing Ctrl+Tab to open the panel");
+ promise = BrowserTestUtils.waitForEvent(ctrlTab.panel, "popupshown");
+ } else {
+ info(
+ `Pressing ${aShiftKey ? "Shift+" : ""}Ctrl+Tab while the panel is open`
+ );
+ promise = BrowserTestUtils.waitForEvent(document, "keyup");
+ }
+ EventUtils.synthesizeKey("VK_TAB", {
+ ctrlKey: true,
+ shiftKey: !!aShiftKey,
+ });
+ await promise;
+ if (document.activeElement == ctrlTab.showAllButton) {
+ info("Repeating keypress to skip over the 'List all tabs' button");
+ return 1 + (await pressCtrlTab(aShiftKey));
+ }
+ return 1;
+ }
+
+ async function releaseCtrl() {
+ let promise;
+ if (isOpen()) {
+ promise = BrowserTestUtils.waitForEvent(ctrlTab.panel, "popuphidden");
+ } else {
+ promise = BrowserTestUtils.waitForEvent(document, "keyup");
+ }
+ EventUtils.synthesizeKey("VK_CONTROL", { type: "keyup" });
+ await promise;
+ }
+
+ async function synthesizeCtrlW() {
+ let promise = BrowserTestUtils.waitForEvent(
+ gBrowser.tabContainer,
+ "TabClose"
+ );
+ EventUtils.synthesizeKey("w", { ctrlKey: true });
+ await promise;
+ }
+
+ function isOpen() {
+ return ctrlTab.isOpen;
+ }
+
+ function canOpen() {
+ return (
+ Services.prefs.getBoolPref("browser.ctrlTab.sortByRecentlyUsed") &&
+ gBrowser.tabs.length > 2
+ );
+ }
+
+ function checkTabs(aTabs) {
+ is(gBrowser.tabs.length, aTabs, "number of open tabs should be " + aTabs);
+ }
+
+ function selectTabs(tabs) {
+ tabs.forEach(function (index) {
+ gBrowser.selectedTab = gBrowser.tabs[index];
+ });
+ }
+
+ async function ctrlTabTest(tabsToSelect, tabTimes, expectedIndex) {
+ selectTabs(tabsToSelect);
+
+ var indexStart = gBrowser.tabContainer.selectedIndex;
+ var tabCount = gBrowser.visibleTabs.length;
+ var normalized = tabTimes % tabCount;
+ var where =
+ normalized == 1
+ ? "back to the previously selected tab"
+ : normalized + " tabs back in most-recently-selected order";
+
+ // Add keyup listener to all content documents.
+ await Promise.all(
+ gBrowser.tabs.map(tab =>
+ SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ if (!content.windowGlobalChild?.isInProcess) {
+ content.window.addEventListener("keyup", () => {
+ content.window._ctrlTabTestKeyupHappend = true;
+ });
+ }
+ })
+ )
+ );
+
+ let numTimesPressed = 0;
+ for (let i = 0; i < tabTimes; i++) {
+ numTimesPressed += await pressCtrlTab();
+
+ if (tabCount > 2) {
+ is(
+ gBrowser.tabContainer.selectedIndex,
+ indexStart,
+ "Selected tab doesn't change while tabbing"
+ );
+ }
+ }
+
+ if (tabCount > 2) {
+ ok(
+ isOpen(),
+ "With " + tabCount + " visible tabs, Ctrl+Tab opens the preview panel"
+ );
+
+ await releaseCtrl();
+
+ ok(!isOpen(), "Releasing Ctrl closes the preview panel");
+ } else {
+ ok(
+ !isOpen(),
+ "With " +
+ tabCount +
+ " visible tabs, Ctrl+Tab doesn't open the preview panel"
+ );
+ }
+
+ is(
+ gBrowser.tabContainer.selectedIndex,
+ expectedIndex,
+ "With " +
+ tabCount +
+ " visible tabs and tab " +
+ indexStart +
+ " selected, Ctrl+Tab*" +
+ numTimesPressed +
+ " goes " +
+ where
+ );
+
+ const keyupEvents = await Promise.all(
+ gBrowser.tabs.map(tab =>
+ SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ () => !!content.window._ctrlTabTestKeyupHappend
+ )
+ )
+ );
+ ok(
+ keyupEvents.every(isKeyupHappned => !isKeyupHappned),
+ "Content document doesn't capture Keyup event during cycling tabs"
+ );
+ }
+});