/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; async function test_clickData({ manifest_version, persistent }) { const action = manifest_version < 3 ? "browser_action" : "action"; const background = { scripts: ["background.js"] }; if (persistent != null) { background.persistent = persistent; } let extension = ExtensionTestUtils.loadExtension({ manifest: { manifest_version, [action]: {}, background, }, files: { "background.js": function backgroundScript() { function onClicked(tab, info) { let button = info.button; let modifiers = info.modifiers; browser.test.sendMessage("onClick", { button, modifiers }); } const apiNS = browser.runtime.getManifest().manifest_version >= 3 ? "action" : "browserAction"; browser[apiNS].onClicked.addListener(onClicked); browser.test.sendMessage("ready"); }, }, }); const map = { shiftKey: "Shift", altKey: "Alt", metaKey: "Command", ctrlKey: "Ctrl", }; function assertSingleModifier(info, modifier, area) { if (modifier === "ctrlKey" && AppConstants.platform === "macosx") { is( info.modifiers.length, 2, `MacCtrl modifier with control click on Mac` ); is( info.modifiers[1], "MacCtrl", `MacCtrl modifier with control click on Mac` ); } else { is( info.modifiers.length, 1, `No unnecessary modifiers for exactly one key on event` ); } is(info.modifiers[0], map[modifier], `Correct modifier on ${area} click`); } await extension.startup(); await extension.awaitMessage("ready"); if (manifest_version >= 3 || persistent === false) { // NOTE: even in MV3 where the API namespace is technically "action", // the event listeners will be persisted into the startup data // with "browserAction" as the module, because that is the name // of the module shared by both MV2 browserAction and MV3 action APIs. assertPersistentListeners(extension, "browserAction", "onClicked", { primed: false, }); await extension.terminateBackground(); assertPersistentListeners(extension, "browserAction", "onClicked", { primed: true, }); } for (let area of [CustomizableUI.AREA_NAVBAR, getCustomizableUIPanelID()]) { let widget = getBrowserActionWidget(extension); CustomizableUI.addWidgetToArea(widget.id, area); for (let modifier of Object.keys(map)) { for (let i = 0; i < 2; i++) { info(`test click with button ${i} modifier ${modifier}`); let clickEventData = { button: i }; clickEventData[modifier] = true; await clickBrowserAction(extension, window, clickEventData); let details = await extension.awaitMessage("onClick"); is(details.button, i, `Correct button in ${area} click`); assertSingleModifier(details, modifier, area); } info(`test keypress with modifier ${modifier}`); let keypressEventData = {}; keypressEventData[modifier] = true; await triggerBrowserActionWithKeyboard(extension, " ", keypressEventData); let details = await extension.awaitMessage("onClick"); is(details.button, 0, `Key command emulates left click`); assertSingleModifier(details, modifier, area); } } if (manifest_version >= 3 || persistent === false) { // The background event page is expected to have been // spawned again to handle the action onClicked event. await extension.awaitMessage("ready"); } await extension.unload(); } async function test_clickData_reset({ manifest_version }) { const action = manifest_version < 3 ? "browser_action" : "action"; const browser_action_command = manifest_version < 3 ? "_execute_browser_action" : "_execute_action"; const browser_action_key = "j"; let extension = ExtensionTestUtils.loadExtension({ manifest: { manifest_version, [action]: { default_area: "navbar", }, page_action: {}, commands: { [browser_action_command]: { suggested_key: { default: "Alt+Shift+J", }, }, }, }, async background() { function onBrowserActionClicked(tab, info) { browser.test.sendMessage("onClick", info); } function onPageActionClicked(tab, info) { browser.test.sendMessage("open-popup"); } const { manifest_version } = browser.runtime.getManifest(); const apiNS = manifest_version >= 3 ? "action" : "browserAction"; browser[apiNS].onClicked.addListener(onBrowserActionClicked); // pageAction should only be available in MV2 extensions. if (manifest_version < 3) { browser.pageAction.onClicked.addListener(onPageActionClicked); let [tab] = await browser.tabs.query({ active: true, currentWindow: true, }); await browser.pageAction.show(tab.id); } browser.test.sendMessage("ready"); }, }); // Pollute the state of the browserAction's lastClickInfo async function clickBrowserActionWithModifiers() { await clickBrowserAction(extension, window, { button: 1, shiftKey: true }); let info = await extension.awaitMessage("onClick"); is(info.button, 1, "Got expected ClickData button details"); is(info.modifiers[0], "Shift", "Got expected ClickData modifiers details"); } function assertInfoReset(info) { is(info.button, 0, `ClickData button reset properly`); is(info.modifiers.length, 0, `ClickData modifiers reset properly`); } await extension.startup(); await extension.awaitMessage("ready"); if (manifest_version >= 3) { // NOTE: even in MV3 where the API namespace is technically "action", // the event listeners will be persisted into the startup data // with "browserAction" as the module, because that is the name // of the module shared by both MV2 browserAction and MV3 action APIs. assertPersistentListeners(extension, "browserAction", "onClicked", { primed: false, }); await extension.terminateBackground(); assertPersistentListeners(extension, "browserAction", "onClicked", { primed: true, }); } await clickBrowserActionWithModifiers(); if (manifest_version >= 3) { // The background event page is expected to have been // spawned again to handle the action onClicked event. await extension.awaitMessage("ready"); } else { extension.onMessage("open-popup", () => { EventUtils.synthesizeKey(browser_action_key, { altKey: true, shiftKey: true, }); }); // pageAction should only be available in MV2 extensions. await clickPageAction(extension); // NOTE: the pageAction event listener then triggers browserAction.onClicked assertInfoReset(await extension.awaitMessage("onClick")); } await clickBrowserActionWithModifiers(); await triggerBrowserActionWithKeyboard(extension, " "); assertInfoReset(await extension.awaitMessage("onClick")); await clickBrowserActionWithModifiers(); await triggerBrowserActionWithKeyboard(extension, " "); assertInfoReset(await extension.awaitMessage("onClick")); await extension.unload(); } add_task(function test_clickData_MV2() { return test_clickData({ manifest_version: 2 }); }); add_task(async function test_clickData_MV2_eventpage() { await SpecialPowers.pushPrefEnv({ set: [["extensions.eventPages.enabled", true]], }); await test_clickData({ manifest_version: 2, persistent: false, }); await SpecialPowers.popPrefEnv(); }); add_task(async function test_clickData_MV3() { await SpecialPowers.pushPrefEnv({ set: [["extensions.manifestV3.enabled", true]], }); await test_clickData({ manifest_version: 3 }); await SpecialPowers.popPrefEnv(); }); add_task(function test_clickData_reset_MV2() { return test_clickData_reset({ manifest_version: 2 }); }); add_task(async function test_clickData_reset_MV3() { await SpecialPowers.pushPrefEnv({ set: [["extensions.manifestV3.enabled", true]], }); await test_clickData_reset({ manifest_version: 3 }); await SpecialPowers.popPrefEnv(); });