summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions/test/browser/browser_ext_contextMenus_onclick.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/extensions/test/browser/browser_ext_contextMenus_onclick.js')
-rw-r--r--browser/components/extensions/test/browser/browser_ext_contextMenus_onclick.js297
1 files changed, 297 insertions, 0 deletions
diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus_onclick.js b/browser/components/extensions/test/browser/browser_ext_contextMenus_onclick.js
new file mode 100644
index 0000000000..72c365f7d7
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_onclick.js
@@ -0,0 +1,297 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+const PAGE =
+ "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html";
+
+// Loaded both as a background script and a tab page.
+function testScript() {
+ let page = location.pathname.includes("tab.html") ? "tab" : "background";
+ let clickCounts = {
+ old: 0,
+ new: 0,
+ };
+ browser.contextMenus.onClicked.addListener(() => {
+ // Async to give other onclick handlers a chance to fire.
+ setTimeout(() => {
+ browser.test.sendMessage("onClicked-fired", page);
+ });
+ });
+ browser.test.onMessage.addListener((toPage, msg) => {
+ if (toPage !== page) {
+ return;
+ }
+ browser.test.log(`Received ${msg} for ${toPage}`);
+ if (msg == "get-click-counts") {
+ browser.test.sendMessage("click-counts", clickCounts);
+ } else if (msg == "clear-click-counts") {
+ clickCounts.old = clickCounts.new = 0;
+ browser.test.sendMessage("next");
+ } else if (msg == "create-with-onclick") {
+ browser.contextMenus.create(
+ {
+ id: "iden",
+ title: "tifier",
+ onclick() {
+ ++clickCounts.old;
+ browser.test.log(
+ `onclick fired for original onclick property in ${page}`
+ );
+ },
+ },
+ () => browser.test.sendMessage("next")
+ );
+ } else if (msg == "create-without-onclick") {
+ browser.contextMenus.create(
+ {
+ id: "iden",
+ title: "tifier",
+ },
+ () => browser.test.sendMessage("next")
+ );
+ } else if (msg == "update-without-onclick") {
+ browser.contextMenus.update(
+ "iden",
+ {
+ enabled: true, // Already enabled, so this does nothing.
+ },
+ () => browser.test.sendMessage("next")
+ );
+ } else if (msg == "update-with-onclick") {
+ browser.contextMenus.update(
+ "iden",
+ {
+ onclick() {
+ ++clickCounts.new;
+ browser.test.log(
+ `onclick fired for updated onclick property in ${page}`
+ );
+ },
+ },
+ () => browser.test.sendMessage("next")
+ );
+ } else if (msg == "remove") {
+ browser.contextMenus.remove("iden", () =>
+ browser.test.sendMessage("next")
+ );
+ } else if (msg == "removeAll") {
+ browser.contextMenus.removeAll(() => browser.test.sendMessage("next"));
+ }
+ });
+
+ if (page == "background") {
+ browser.test.log("Opening tab.html");
+ browser.tabs.create({
+ url: "tab.html",
+ active: false, // To not interfere with the context menu tests.
+ });
+ } else {
+ // Sanity check - the pages must be in the same process.
+ let pages = browser.extension.getViews();
+ browser.test.assertTrue(
+ pages.includes(window),
+ "Expected this tab to be an extension view"
+ );
+ pages = pages.filter(w => w !== window);
+ browser.test.assertEq(
+ pages[0],
+ browser.extension.getBackgroundPage(),
+ "Expected the other page to be a background page"
+ );
+ browser.test.sendMessage("tab.html ready");
+ }
+}
+
+add_task(async function () {
+ let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+
+ gBrowser.selectedTab = tab1;
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["contextMenus"],
+ },
+ background: testScript,
+ files: {
+ "tab.html": `<!DOCTYPE html><meta charset="utf-8"><script src="tab.js"></script>`,
+ "tab.js": testScript,
+ },
+ });
+ await extension.startup();
+ await extension.awaitMessage("tab.html ready");
+
+ async function clickContextMenu() {
+ // Using openContextMenu instead of openExtensionContextMenu because the
+ // test extension has only one context menu item.
+ let extensionMenuRoot = await openContextMenu();
+ let items = extensionMenuRoot.getElementsByAttribute("label", "tifier");
+ is(items.length, 1, "Expected one context menu item");
+ await closeExtensionContextMenu(items[0]);
+ // One of them is "tab", the other is "background".
+ info(`onClicked from: ${await extension.awaitMessage("onClicked-fired")}`);
+ info(`onClicked from: ${await extension.awaitMessage("onClicked-fired")}`);
+ }
+
+ function getCounts(page) {
+ extension.sendMessage(page, "get-click-counts");
+ return extension.awaitMessage("click-counts");
+ }
+ async function resetCounts() {
+ extension.sendMessage("tab", "clear-click-counts");
+ extension.sendMessage("background", "clear-click-counts");
+ await extension.awaitMessage("next");
+ await extension.awaitMessage("next");
+ }
+
+ // During this test, at most one "onclick" attribute is expected at any time.
+ for (let pageOne of ["background", "tab"]) {
+ for (let pageTwo of ["background", "tab"]) {
+ info(`Testing with menu created by ${pageOne} and updated by ${pageTwo}`);
+ extension.sendMessage(pageOne, "create-with-onclick");
+ await extension.awaitMessage("next");
+
+ // Test that update without onclick attribute does not clear the existing
+ // onclick handler.
+ extension.sendMessage(pageTwo, "update-without-onclick");
+ await extension.awaitMessage("next");
+ await clickContextMenu();
+ let clickCounts = await getCounts(pageOne);
+ is(
+ clickCounts.old,
+ 1,
+ `Original onclick should still be present in ${pageOne}`
+ );
+ is(clickCounts.new, 0, `Not expecting any new handlers in ${pageOne}`);
+ if (pageOne !== pageTwo) {
+ clickCounts = await getCounts(pageTwo);
+ is(clickCounts.old, 0, `Not expecting any handlers in ${pageTwo}`);
+ is(clickCounts.new, 0, `Not expecting any new handlers in ${pageTwo}`);
+ }
+ await resetCounts();
+
+ // Test that update with onclick handler in a different page clears the
+ // existing handler and activates the new onclick handler.
+ extension.sendMessage(pageTwo, "update-with-onclick");
+ await extension.awaitMessage("next");
+ await clickContextMenu();
+ clickCounts = await getCounts(pageOne);
+ is(clickCounts.old, 0, `Original onclick should be gone from ${pageOne}`);
+ if (pageOne !== pageTwo) {
+ is(
+ clickCounts.new,
+ 0,
+ `Still not expecting new handlers in ${pageOne}`
+ );
+ }
+ clickCounts = await getCounts(pageTwo);
+ if (pageOne !== pageTwo) {
+ is(clickCounts.old, 0, `Not expecting an old onclick in ${pageTwo}`);
+ }
+ is(clickCounts.new, 1, `New onclick should be triggered in ${pageTwo}`);
+ await resetCounts();
+
+ // Test that updating the handler (different again from the last `update`
+ // call, but the same as the `create` call) clears the existing handler
+ // and activates the new onclick handler.
+ extension.sendMessage(pageOne, "update-with-onclick");
+ await extension.awaitMessage("next");
+ await clickContextMenu();
+ clickCounts = await getCounts(pageOne);
+ is(clickCounts.new, 1, `onclick should be triggered in ${pageOne}`);
+ if (pageOne !== pageTwo) {
+ clickCounts = await getCounts(pageTwo);
+ is(clickCounts.new, 0, `onclick should be gone from ${pageTwo}`);
+ }
+ await resetCounts();
+
+ // Test that removing the context menu and recreating it with the same ID
+ // (in a different context) does not leave behind any onclick handlers.
+ extension.sendMessage(pageTwo, "remove");
+ await extension.awaitMessage("next");
+ extension.sendMessage(pageTwo, "create-without-onclick");
+ await extension.awaitMessage("next");
+ await clickContextMenu();
+ clickCounts = await getCounts(pageOne);
+ is(clickCounts.new, 0, `Did not expect any click handlers in ${pageOne}`);
+ if (pageOne !== pageTwo) {
+ clickCounts = await getCounts(pageTwo);
+ is(
+ clickCounts.new,
+ 0,
+ `Did not expect any click handlers in ${pageTwo}`
+ );
+ }
+ await resetCounts();
+
+ // Remove context menu for the next iteration of the test. And just to get
+ // more coverage, let's use removeAll instead of remove.
+ extension.sendMessage(pageOne, "removeAll");
+ await extension.awaitMessage("next");
+ }
+ }
+ await extension.unload();
+ BrowserTestUtils.removeTab(tab1);
+});
+
+add_task(async function test_onclick_modifiers() {
+ const manifest = {
+ permissions: ["contextMenus"],
+ };
+
+ function background() {
+ function onclick(info) {
+ browser.test.sendMessage("click", info);
+ }
+ browser.contextMenus.create(
+ { contexts: ["all"], title: "modify", onclick },
+ () => {
+ browser.test.sendMessage("ready");
+ }
+ );
+ }
+
+ const extension = ExtensionTestUtils.loadExtension({ manifest, background });
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+
+ await extension.startup();
+ await extension.awaitMessage("ready");
+
+ async function click(modifiers = {}) {
+ const menu = await openContextMenu();
+ const items = menu.getElementsByAttribute("label", "modify");
+ is(items.length, 1, "Got exactly one context menu item");
+ await closeExtensionContextMenu(items[0], modifiers);
+ return extension.awaitMessage("click");
+ }
+
+ const plain = await click();
+ is(plain.modifiers.length, 0, "modifiers array empty with a plain click");
+
+ const shift = await click({ shiftKey: true });
+ is(shift.modifiers.join(), "Shift", "Correct modifier: Shift");
+
+ const ctrl = await click({ ctrlKey: true });
+ if (AppConstants.platform !== "macosx") {
+ is(ctrl.modifiers.join(), "Ctrl", "Correct modifier: Ctrl");
+ } else {
+ is(
+ ctrl.modifiers.sort().join(),
+ "Ctrl,MacCtrl",
+ "Correct modifier: Ctrl (and MacCtrl)"
+ );
+
+ const meta = await click({ metaKey: true });
+ is(meta.modifiers.join(), "Command", "Correct modifier: Command");
+ }
+
+ const altShift = await click({ altKey: true, shiftKey: true });
+ is(
+ altShift.modifiers.sort().join(),
+ "Alt,Shift",
+ "Correct modifiers: Shift+Alt"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ await extension.unload();
+});