Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
const PAGE =
// 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();
});