/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; PromiseTestUtils.allowMatchingRejectionsGlobally(/packaging errors/); function getExtension(page_action) { return ExtensionTestUtils.loadExtension({ manifest: { page_action, }, background: function () { browser.test.onMessage.addListener( async ({ method, param, expect, msg }) => { let result = await browser.pageAction[method](param); if (expect !== undefined) { browser.test.assertEq(expect, result, msg); } browser.test.sendMessage("done"); } ); }, }); } async function sendMessage(ext, method, param, expect, msg) { ext.sendMessage({ method, param, expect, msg }); await ext.awaitMessage("done"); } let tests = [ { name: "Test shown for all_urls", page_action: { show_matches: [""], }, shown: [true, true, false], }, { name: "Test hide_matches overrides all_urls.", page_action: { show_matches: [""], hide_matches: ["*://mochi.test/*"], }, shown: [true, false, false], }, { name: "Test shown only for show_matches.", page_action: { show_matches: ["*://mochi.test/*"], }, shown: [false, true, false], }, ]; // For some reason about:rights and about:about used to behave differently (maybe // because only the latter is privileged?) so both should be tested. about:about // is used in the test as the base tab. let urls = ["http://example.com/", "http://mochi.test:8888/", "about:rights"]; function getId(tab) { const { Management: { global: { tabTracker }, }, } = ChromeUtils.importESModule("resource://gre/modules/Extension.sys.mjs"); getId = tabTracker.getId.bind(tabTracker); // eslint-disable-line no-func-assign return getId(tab); } async function check(extension, tab, expected, msg) { await promiseAnimationFrame(); let widgetId = makeWidgetId(extension.id); let pageActionId = BrowserPageActions.urlbarButtonNodeIDForActionID(widgetId); is( gBrowser.selectedTab, tab, `tab ${tab.linkedBrowser.currentURI.spec} is selected` ); let button = document.getElementById(pageActionId); // Sometimes we're hidden, sometimes a parent is hidden via css (e.g. about pages) let hidden = button === null || button.hidden || window.getComputedStyle(button).display == "none"; is(!hidden, expected, msg + " (computed)"); await sendMessage( extension, "isShown", { tabId: getId(tab) }, expected, msg + " (isShown)" ); } add_task(async function test_pageAction_default_show_tabs() { info( "Check show_matches and hide_matches are respected when opening a new tab or switching to an existing tab." ); let switchTab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:about", true, true ); for (let [i, test] of tests.entries()) { info(`test ${i}: ${test.name}`); let extension = getExtension(test.page_action); await extension.startup(); for (let [j, url] of urls.entries()) { let expected = test.shown[j]; let msg = `test ${i} url ${j}: page action is ${ expected ? "shown" : "hidden" } for ${url}`; info("Check new tab."); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, url, true, true ); await check(extension, tab, expected, msg + " (new)"); info("Check switched tab."); await BrowserTestUtils.switchTab(gBrowser, switchTab); await check(extension, switchTab, false, msg + " (about:about)"); await BrowserTestUtils.switchTab(gBrowser, tab); await check(extension, tab, expected, msg + " (switched)"); BrowserTestUtils.removeTab(tab); } await extension.unload(); } BrowserTestUtils.removeTab(switchTab); }); add_task(async function test_pageAction_default_show_install() { info( "Check show_matches and hide_matches are respected when installing the extension" ); for (let [i, test] of tests.entries()) { info(`test ${i}: ${test.name}`); for (let expected of [true, false]) { let j = test.shown.indexOf(expected); if (j === -1) { continue; } let initialTab = await BrowserTestUtils.openNewForegroundTab( gBrowser, urls[j], true, true ); let installTab = await BrowserTestUtils.openNewForegroundTab( gBrowser, urls[j], true, true ); let extension = getExtension(test.page_action); await extension.startup(); let msg = `test ${i} url ${j}: page action is ${ expected ? "shown" : "hidden" } for ${urls[j]}`; await check(extension, installTab, expected, msg + " (active)"); // initialTab has not been activated after installation, so we have not evaluated whether the page // action should be shown in it. Check that pageAction.isShown works anyways. await sendMessage( extension, "isShown", { tabId: getId(initialTab) }, expected, msg + " (inactive)" ); BrowserTestUtils.removeTab(initialTab); BrowserTestUtils.removeTab(installTab); await extension.unload(); } } }); add_task(async function test_pageAction_history() { info( "Check match patterns are reevaluated when using history.pushState or navigating" ); let url1 = "http://example.com/"; let url2 = url1 + "path/"; let extension = getExtension({ show_matches: [url1], hide_matches: [url2], }); await extension.startup(); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, url1, true, true ); await check(extension, tab, true, "page action is shown for " + url1); info("Use history.pushState to change the URL without navigating"); await historyPushState(tab, url2); await check(extension, tab, false, "page action is hidden for " + url2); info("Use hide()"); await sendMessage(extension, "hide", getId(tab)); await check(extension, tab, false, "page action is still hidden"); info("Use history.pushState to revert to first url"); await historyPushState(tab, url1); await check( extension, tab, false, "hide() has more precedence than pattern matching" ); info("Select another tab"); let tab2 = await BrowserTestUtils.openNewForegroundTab( gBrowser, url1, true, true ); info("Perform navigation in the old tab"); await navigateTab(tab, url1); await sendMessage( extension, "isShown", { tabId: getId(tab) }, true, "Navigating undoes hide(), even when the tab is not selected." ); BrowserTestUtils.removeTab(tab); BrowserTestUtils.removeTab(tab2); await extension.unload(); }); add_task(async function test_pageAction_all_urls() { info("Check is not allowed in hide_matches"); let extension = getExtension({ show_matches: ["*://mochi.test/*"], hide_matches: [""], }); let rejects = await extension.startup().then( () => false, () => true ); is(rejects, true, "startup failed"); }); add_task(async function test_pageAction_restrictScheme_false() { info( "Check restricted origins are allowed in show_matches for privileged extensions" ); let extension = ExtensionTestUtils.loadExtension({ isPrivileged: true, manifest: { permissions: ["mozillaAddons", "tabs"], page_action: { show_matches: ["about:reader*"], hide_matches: ["*://*/*"], }, }, background: function () { browser.tabs.onUpdated.addListener(async (tabId, changeInfo) => { if (changeInfo.url && changeInfo.url.startsWith("about:reader")) { browser.test.sendMessage("readerModeEntered"); } }); browser.test.onMessage.addListener(async msg => { if (msg !== "enterReaderMode") { browser.test.fail(`Received unexpected test message: ${msg}`); return; } browser.tabs.toggleReaderMode(); }); }, }); async function expectPageAction(extension, tab, isShown) { await promiseAnimationFrame(); let widgetId = makeWidgetId(extension.id); let pageActionId = BrowserPageActions.urlbarButtonNodeIDForActionID(widgetId); let iconEl = document.getElementById(pageActionId); if (isShown) { ok(iconEl && !iconEl.hasAttribute("disabled"), "pageAction is shown"); } else { ok( iconEl == null || iconEl.getAttribute("disabled") == "true", "pageAction is hidden" ); } } const baseUrl = getRootDirectory(gTestPath).replace( "chrome://mochitests/content", "http://example.com" ); const url = `${baseUrl}/readerModeArticle.html`; let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, url, true, true ); await extension.startup(); await expectPageAction(extension, tab, false); extension.sendMessage("enterReaderMode"); await extension.awaitMessage("readerModeEntered"); await expectPageAction(extension, tab, true); BrowserTestUtils.removeTab(tab); await extension.unload(); });