summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions/test/browser/browser_ext_tabs_hide.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/extensions/test/browser/browser_ext_tabs_hide.js')
-rw-r--r--browser/components/extensions/test/browser/browser_ext_tabs_hide.js375
1 files changed, 375 insertions, 0 deletions
diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_hide.js b/browser/components/extensions/test/browser/browser_ext_tabs_hide.js
new file mode 100644
index 0000000000..89c50db692
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_hide.js
@@ -0,0 +1,375 @@
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ ExtensionControlledPopup:
+ "resource:///modules/ExtensionControlledPopup.sys.mjs",
+ SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
+ TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.sys.mjs",
+});
+
+const triggeringPrincipal_base64 = E10SUtils.SERIALIZED_SYSTEMPRINCIPAL;
+
+async function doorhangerTest(testFn) {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["tabs", "tabHide"],
+ icons: {
+ 48: "addon-icon.png",
+ },
+ },
+ background() {
+ browser.test.onMessage.addListener(async (msg, data) => {
+ let tabs = await browser.tabs.query(data);
+ await browser.tabs[msg](tabs.map(t => t.id));
+ browser.test.sendMessage("done");
+ });
+ },
+ useAddonManager: "temporary",
+ });
+
+ await extension.startup();
+
+ // Open some tabs so we can hide them.
+ let firstTab = gBrowser.selectedTab;
+ let tabs = [
+ await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://example.com/?one",
+ true,
+ true
+ ),
+ await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://example.com/?two",
+ true,
+ true
+ ),
+ ];
+ gBrowser.selectedTab = firstTab;
+
+ await testFn(extension);
+
+ BrowserTestUtils.removeTab(tabs[0]);
+ BrowserTestUtils.removeTab(tabs[1]);
+
+ await extension.unload();
+}
+
+add_task(function test_doorhanger_keep() {
+ return doorhangerTest(async function (extension) {
+ is(gBrowser.visibleTabs.length, 3, "There are 3 visible tabs");
+
+ // Hide the first tab, expect the doorhanger.
+ let panel = ExtensionControlledPopup._getAndMaybeCreatePanel(document);
+ let popupShown = promisePopupShown(panel);
+ extension.sendMessage("hide", { url: "*://*/?one" });
+ await extension.awaitMessage("done");
+ await popupShown;
+
+ is(gBrowser.visibleTabs.length, 2, "There are 2 visible tabs now");
+ is(
+ panel.anchorNode.closest("toolbarbutton").id,
+ "alltabs-button",
+ "The doorhanger is anchored to the all tabs button"
+ );
+
+ // Click the Keep Tabs Hidden button.
+ let popupnotification = document.getElementById(
+ "extension-tab-hide-notification"
+ );
+ let popupHidden = promisePopupHidden(panel);
+ popupnotification.button.click();
+ await popupHidden;
+
+ // Hide another tab and ensure the popup didn't open.
+ extension.sendMessage("hide", { url: "*://*/?two" });
+ await extension.awaitMessage("done");
+ is(panel.state, "closed", "The popup is still closed");
+ is(gBrowser.visibleTabs.length, 1, "There's one visible tab now");
+
+ extension.sendMessage("show", {});
+ await extension.awaitMessage("done");
+ });
+});
+
+add_task(function test_doorhanger_disable() {
+ return doorhangerTest(async function (extension) {
+ is(gBrowser.visibleTabs.length, 3, "There are 3 visible tabs");
+
+ // Hide the first tab, expect the doorhanger.
+ let panel = ExtensionControlledPopup._getAndMaybeCreatePanel(document);
+ let popupShown = promisePopupShown(panel);
+ extension.sendMessage("hide", { url: "*://*/?one" });
+ await extension.awaitMessage("done");
+ await popupShown;
+
+ is(gBrowser.visibleTabs.length, 2, "There are 2 visible tabs now");
+ is(
+ panel.anchorNode.closest("toolbarbutton").id,
+ "alltabs-button",
+ "The doorhanger is anchored to the all tabs button"
+ );
+
+ // verify the contents of the description.
+ let popupnotification = document.getElementById(
+ "extension-tab-hide-notification"
+ );
+ let description = popupnotification.querySelector(
+ "#extension-tab-hide-notification-description"
+ );
+ let addon = await AddonManager.getAddonByID(extension.id);
+ ok(
+ description.textContent.includes(addon.name),
+ "The extension name is in the description"
+ );
+ let images = Array.from(description.querySelectorAll("image"));
+ is(images.length, 2, "There are two images");
+ ok(
+ images.some(img => img.src.includes("addon-icon.png")),
+ "There's an icon for the extension"
+ );
+ ok(
+ images.some(img =>
+ getComputedStyle(img).backgroundImage.includes("arrow-down.svg")
+ ),
+ "There's an icon for the all tabs menu"
+ );
+
+ // Click the Disable Extension button.
+ let popupHidden = promisePopupHidden(panel);
+ popupnotification.secondaryButton.click();
+ await popupHidden;
+ await new Promise(executeSoon);
+
+ is(gBrowser.visibleTabs.length, 3, "There are 3 visible tabs again");
+ is(addon.userDisabled, true, "The extension is now disabled");
+ });
+});
+
+add_task(async function test_tabs_showhide() {
+ async function background() {
+ browser.test.onMessage.addListener(async (msg, data) => {
+ switch (msg) {
+ case "hideall": {
+ let tabs = await browser.tabs.query({ hidden: false });
+ browser.test.assertEq(tabs.length, 5, "got 5 tabs");
+ let ids = tabs.map(tab => tab.id);
+ browser.test.log(`working with ids ${JSON.stringify(ids)}`);
+
+ let hidden = await browser.tabs.hide(ids);
+ browser.test.assertEq(hidden.length, 3, "hid 3 tabs");
+ tabs = await browser.tabs.query({ hidden: true });
+ ids = tabs.map(tab => tab.id);
+ browser.test.assertEq(
+ JSON.stringify(hidden.sort()),
+ JSON.stringify(ids.sort()),
+ "hidden tabIds match"
+ );
+
+ browser.test.sendMessage("hidden", { hidden });
+ break;
+ }
+ case "showall": {
+ let tabs = await browser.tabs.query({ hidden: true });
+ for (let tab of tabs) {
+ browser.test.assertTrue(tab.hidden, "tab is hidden");
+ }
+ let ids = tabs.map(tab => tab.id);
+ browser.tabs.show(ids);
+ browser.test.sendMessage("shown");
+ break;
+ }
+ }
+ });
+ }
+
+ let extdata = {
+ manifest: { permissions: ["tabs", "tabHide"] },
+ background,
+ useAddonManager: "temporary", // So the doorhanger can find the addon.
+ };
+ let extension = ExtensionTestUtils.loadExtension(extdata);
+ await extension.startup();
+
+ let sessData = {
+ windows: [
+ {
+ tabs: [
+ { entries: [{ url: "about:blank", triggeringPrincipal_base64 }] },
+ {
+ entries: [
+ { url: "https://example.com/", triggeringPrincipal_base64 },
+ ],
+ },
+ {
+ entries: [
+ { url: "https://mochi.test:8888/", triggeringPrincipal_base64 },
+ ],
+ },
+ ],
+ },
+ {
+ tabs: [
+ { entries: [{ url: "about:blank", triggeringPrincipal_base64 }] },
+ {
+ entries: [
+ { url: "http://test1.example.com/", triggeringPrincipal_base64 },
+ ],
+ },
+ ],
+ },
+ ],
+ };
+
+ // Set up a test session with 2 windows and 5 tabs.
+ let oldState = SessionStore.getBrowserState();
+ let restored = TestUtils.topicObserved("sessionstore-browser-state-restored");
+ SessionStore.setBrowserState(JSON.stringify(sessData));
+ await restored;
+
+ if (!Services.prefs.getBoolPref("browser.tabs.tabmanager.enabled")) {
+ for (let win of BrowserWindowIterator()) {
+ let allTabsButton = win.document.getElementById("alltabs-button");
+ is(
+ getComputedStyle(allTabsButton).display,
+ "none",
+ "The all tabs button is hidden"
+ );
+ }
+ }
+
+ // Attempt to hide all the tabs, however the active tab in each window cannot
+ // be hidden, so the result will be 3 hidden tabs.
+ extension.sendMessage("hideall");
+ await extension.awaitMessage("hidden");
+
+ // We have 2 windows in this session. Otherwin is the non-current window.
+ // In each window, the first tab will be the selected tab and should not be
+ // hidden. The rest of the tabs should be hidden at this point. Hidden
+ // status was already validated inside the extension, this double checks
+ // from chrome code.
+ let otherwin;
+ for (let win of BrowserWindowIterator()) {
+ if (win != window) {
+ otherwin = win;
+ }
+ let tabs = Array.from(win.gBrowser.tabs);
+ ok(!tabs[0].hidden, "first tab not hidden");
+ for (let i = 1; i < tabs.length; i++) {
+ ok(tabs[i].hidden, "tab hidden value is correct");
+ let id = SessionStore.getCustomTabValue(tabs[i], "hiddenBy");
+ is(id, extension.id, "tab hiddenBy value is correct");
+ await TabStateFlusher.flush(tabs[i].linkedBrowser);
+ }
+
+ let allTabsButton = win.document.getElementById("alltabs-button");
+ isnot(
+ getComputedStyle(allTabsButton).display,
+ "none",
+ "The all tabs button is visible"
+ );
+ }
+
+ // Close the other window then restore it to test that the tabs are
+ // restored with proper hidden state, and the correct extension id.
+ await BrowserTestUtils.closeWindow(otherwin);
+
+ otherwin = SessionStore.undoCloseWindow(0);
+ await BrowserTestUtils.waitForEvent(otherwin, "load");
+ let tabs = Array.from(otherwin.gBrowser.tabs);
+ ok(!tabs[0].hidden, "first tab not hidden");
+ for (let i = 1; i < tabs.length; i++) {
+ ok(tabs[i].hidden, "tab hidden value is correct");
+ let id = SessionStore.getCustomTabValue(tabs[i], "hiddenBy");
+ is(id, extension.id, "tab hiddenBy value is correct");
+ }
+
+ // Test closing the last visible tab, the next tab which is hidden should become
+ // the selectedTab and will be visible.
+ ok(!otherwin.gBrowser.selectedTab.hidden, "selected tab is not hidden");
+ BrowserTestUtils.removeTab(otherwin.gBrowser.selectedTab);
+ ok(!otherwin.gBrowser.selectedTab.hidden, "tab was unhidden");
+
+ // Showall will unhide any remaining hidden tabs.
+ extension.sendMessage("showall");
+ await extension.awaitMessage("shown");
+
+ // Check from chrome code that all tabs are visible again.
+ for (let win of BrowserWindowIterator()) {
+ let tabs = Array.from(win.gBrowser.tabs);
+ for (let i = 0; i < tabs.length; i++) {
+ ok(!tabs[i].hidden, "tab hidden value is correct");
+ }
+ }
+
+ // Close second window.
+ await BrowserTestUtils.closeWindow(otherwin);
+
+ await extension.unload();
+
+ // Restore pre-test state.
+ restored = TestUtils.topicObserved("sessionstore-browser-state-restored");
+ SessionStore.setBrowserState(oldState);
+ await restored;
+});
+
+// Test our shutdown handling. Currently this means any hidden tabs will be
+// shown when a tabHide extension is shutdown. We additionally test the
+// tabs.onUpdated listener gets called with hidden state changes.
+add_task(async function test_tabs_shutdown() {
+ let tabs = [
+ await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://example.com/",
+ true,
+ true
+ ),
+ await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://mochi.test:8888/",
+ true,
+ true
+ ),
+ ];
+
+ async function background() {
+ let tabs = await browser.tabs.query({ url: "http://example.com/" });
+ let testTab = tabs[0];
+
+ browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
+ if ("hidden" in changeInfo) {
+ browser.test.assertEq(tabId, testTab.id, "correct tab was hidden");
+ browser.test.assertTrue(changeInfo.hidden, "tab is hidden");
+ browser.test.assertEq(tab.url, testTab.url, "tab has correct URL");
+ browser.test.sendMessage("changeInfo");
+ }
+ });
+
+ let hidden = await browser.tabs.hide(testTab.id);
+ browser.test.assertEq(hidden[0], testTab.id, "tab was hidden");
+ tabs = await browser.tabs.query({ hidden: true });
+ browser.test.assertEq(tabs[0].id, testTab.id, "tab was hidden");
+ browser.test.sendMessage("ready");
+ }
+
+ let extdata = {
+ manifest: { permissions: ["tabs", "tabHide"] },
+ useAddonManager: "temporary", // For testing onShutdown.
+ background,
+ };
+ let extension = ExtensionTestUtils.loadExtension(extdata);
+ await extension.startup();
+
+ // test onUpdated
+ await Promise.all([
+ extension.awaitMessage("ready"),
+ extension.awaitMessage("changeInfo"),
+ ]);
+ Assert.ok(tabs[0].hidden, "Tab is hidden by extension");
+
+ await extension.unload();
+
+ Assert.ok(!tabs[0].hidden, "Tab is not hidden after unloading extension");
+ BrowserTestUtils.removeTab(tabs[0]);
+ BrowserTestUtils.removeTab(tabs[1]);
+});