/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; async function loadExtension(options) { let extension = ExtensionTestUtils.loadExtension({ useAddonManager: "temporary", manifest: Object.assign( { permissions: ["tabs"], }, options.manifest ), files: { "options.html": ` `, "options.js": function () { window.iAmOption = true; browser.runtime.sendMessage("options.html"); browser.runtime.onMessage.addListener((msg, sender, respond) => { if (msg == "ping") { respond("pong"); } else if (msg == "connect") { let port = browser.runtime.connect(); port.postMessage("ping-from-options-html"); port.onMessage.addListener(msg => { if (msg == "ping-from-bg") { browser.test.log("Got outbound options.html pong"); browser.test.sendMessage("options-html-outbound-pong"); } }); } }); browser.runtime.onConnect.addListener(port => { browser.test.log("Got inbound options.html port"); port.postMessage("ping-from-options-html"); port.onMessage.addListener(msg => { if (msg == "ping-from-bg") { browser.test.log("Got inbound options.html pong"); browser.test.sendMessage("options-html-inbound-pong"); } }); }); }, }, background: options.background, }); await extension.startup(); return extension; } add_task(async function run_test_inline_options() { let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "http://example.com/" ); let extension = await loadExtension({ manifest: { browser_specific_settings: { gecko: { id: "inline_options@tests.mozilla.org" }, }, options_ui: { page: "options.html", }, }, background: async function () { let _optionsPromise; let awaitOptions = () => { browser.test.assertFalse( _optionsPromise, "Should not be awaiting options already" ); return new Promise(resolve => { _optionsPromise = { resolve }; }); }; browser.runtime.onMessage.addListener((msg, sender) => { if (msg == "options.html") { if (_optionsPromise) { _optionsPromise.resolve(sender.tab); _optionsPromise = null; } else { browser.test.fail("Saw unexpected options page load"); } } }); try { let [firstTab] = await browser.tabs.query({ currentWindow: true, active: true, }); browser.test.log("Open options page. Expect fresh load."); let [, optionsTab] = await Promise.all([ browser.runtime.openOptionsPage(), awaitOptions(), ]); browser.test.assertEq( "about:addons", optionsTab.url, "Tab contains AddonManager" ); browser.test.assertTrue(optionsTab.active, "Tab is active"); browser.test.assertTrue( optionsTab.id != firstTab.id, "Tab is a new tab" ); browser.test.assertEq( 0, browser.extension.getViews({ type: "popup" }).length, "viewType is not popup" ); browser.test.assertEq( 1, browser.extension.getViews({ type: "tab" }).length, "viewType is tab" ); browser.test.assertEq( 1, browser.extension.getViews({ windowId: optionsTab.windowId }).length, "windowId matches" ); let views = browser.extension.getViews(); browser.test.assertEq( 2, views.length, "Expected the options page and the background page" ); browser.test.assertTrue( views.includes(window), "One of the views is the background page" ); browser.test.assertTrue( views.some(w => w.iAmOption), "One of the views is the options page" ); browser.test.log("Switch tabs."); await browser.tabs.update(firstTab.id, { active: true }); browser.test.log( "Open options page again. Expect tab re-selected, no new load." ); await browser.runtime.openOptionsPage(); let [tab] = await browser.tabs.query({ currentWindow: true, active: true, }); browser.test.assertEq( optionsTab.id, tab.id, "Tab is the same as the previous options tab" ); browser.test.assertEq( "about:addons", tab.url, "Tab contains AddonManager" ); browser.test.log("Ping options page."); let pong = await browser.runtime.sendMessage("ping"); browser.test.assertEq("pong", pong, "Got pong."); let done = new Promise(resolve => { browser.test.onMessage.addListener(msg => { if (msg == "ports-done") { resolve(); } }); }); browser.runtime.onConnect.addListener(port => { browser.test.log("Got inbound background port"); port.postMessage("ping-from-bg"); port.onMessage.addListener(msg => { if (msg == "ping-from-options-html") { browser.test.log("Got inbound background pong"); browser.test.sendMessage("bg-inbound-pong"); } }); }); browser.runtime.sendMessage("connect"); let port = browser.runtime.connect(); port.postMessage("ping-from-bg"); port.onMessage.addListener(msg => { if (msg == "ping-from-options-html") { browser.test.log("Got outbound background pong"); browser.test.sendMessage("bg-outbound-pong"); } }); await done; browser.test.log("Remove options tab."); await browser.tabs.remove(optionsTab.id); browser.test.log("Open options page again. Expect fresh load."); [, tab] = await Promise.all([ browser.runtime.openOptionsPage(), awaitOptions(), ]); browser.test.assertEq( "about:addons", tab.url, "Tab contains AddonManager" ); browser.test.assertTrue(tab.active, "Tab is active"); browser.test.assertTrue(tab.id != optionsTab.id, "Tab is a new tab"); await browser.tabs.remove(tab.id); browser.test.notifyPass("options-ui"); } catch (error) { browser.test.fail(`Error: ${error} :: ${error.stack}`); browser.test.notifyFail("options-ui"); } }, }); await Promise.all([ extension.awaitMessage("options-html-inbound-pong"), extension.awaitMessage("options-html-outbound-pong"), extension.awaitMessage("bg-inbound-pong"), extension.awaitMessage("bg-outbound-pong"), ]); extension.sendMessage("ports-done"); await extension.awaitFinish("options-ui"); await extension.unload(); BrowserTestUtils.removeTab(tab); }); add_task(async function test_tab_options() { info(`Test options opened in a tab`); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "http://example.com/" ); let extension = await loadExtension({ manifest: { browser_specific_settings: { gecko: { id: "tab_options@tests.mozilla.org" }, }, options_ui: { page: "options.html", open_in_tab: true, }, }, background: async function () { let _optionsPromise; let awaitOptions = () => { browser.test.assertFalse( _optionsPromise, "Should not be awaiting options already" ); return new Promise(resolve => { _optionsPromise = { resolve }; }); }; browser.runtime.onMessage.addListener((msg, sender) => { if (msg == "options.html") { if (_optionsPromise) { _optionsPromise.resolve(sender.tab); _optionsPromise = null; } else { browser.test.fail("Saw unexpected options page load"); } } }); let optionsURL = browser.runtime.getURL("options.html"); try { let [firstTab] = await browser.tabs.query({ currentWindow: true, active: true, }); browser.test.log("Open options page. Expect fresh load."); let [, optionsTab] = await Promise.all([ browser.runtime.openOptionsPage(), awaitOptions(), ]); browser.test.assertEq( optionsURL, optionsTab.url, "Tab contains options.html" ); browser.test.assertTrue(optionsTab.active, "Tab is active"); browser.test.assertTrue( optionsTab.id != firstTab.id, "Tab is a new tab" ); browser.test.assertEq( 0, browser.extension.getViews({ type: "popup" }).length, "viewType is not popup" ); browser.test.assertEq( 1, browser.extension.getViews({ type: "tab" }).length, "viewType is tab" ); browser.test.assertEq( 1, browser.extension.getViews({ windowId: optionsTab.windowId }).length, "windowId matches" ); let views = browser.extension.getViews(); browser.test.assertEq( 2, views.length, "Expected the options page and the background page" ); browser.test.assertTrue( views.includes(window), "One of the views is the background page" ); browser.test.assertTrue( views.some(w => w.iAmOption), "One of the views is the options page" ); browser.test.log("Switch tabs."); await browser.tabs.update(firstTab.id, { active: true }); browser.test.log( "Open options page again. Expect tab re-selected, no new load." ); await browser.runtime.openOptionsPage(); let [tab] = await browser.tabs.query({ currentWindow: true, active: true, }); browser.test.assertEq( optionsTab.id, tab.id, "Tab is the same as the previous options tab" ); browser.test.assertEq(optionsURL, tab.url, "Tab contains options.html"); // Unfortunately, we can't currently do this, since onMessage doesn't // currently support responses when there are multiple listeners. // // browser.test.log("Ping options page."); // return new Promise(resolve => browser.runtime.sendMessage("ping", resolve)); browser.test.log("Remove options tab."); await browser.tabs.remove(optionsTab.id); browser.test.log("Open options page again. Expect fresh load."); [, tab] = await Promise.all([ browser.runtime.openOptionsPage(), awaitOptions(), ]); browser.test.assertEq(optionsURL, tab.url, "Tab contains options.html"); browser.test.assertTrue(tab.active, "Tab is active"); browser.test.assertTrue(tab.id != optionsTab.id, "Tab is a new tab"); await browser.tabs.remove(tab.id); browser.test.notifyPass("options-ui-tab"); } catch (error) { browser.test.fail(`Error: ${error} :: ${error.stack}`); browser.test.notifyFail("options-ui-tab"); } }, }); await extension.awaitFinish("options-ui-tab"); await extension.unload(); BrowserTestUtils.removeTab(tab); }); add_task(async function test_options_no_manifest() { info(`Test with no manifest key`); let extension = await loadExtension({ manifest: { browser_specific_settings: { gecko: { id: "no_options@tests.mozilla.org" }, }, }, async background() { browser.test.log( "Try to open options page when not specified in the manifest." ); await browser.test.assertRejects( browser.runtime.openOptionsPage(), /No `options_ui` declared/, "Expected error from openOptionsPage()" ); browser.test.notifyPass("options-no-manifest"); }, }); await extension.awaitFinish("options-no-manifest"); await extension.unload(); });