summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions/test/browser/browser_ext_contextMenus.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/extensions/test/browser/browser_ext_contextMenus.js')
-rw-r--r--browser/components/extensions/test/browser/browser_ext_contextMenus.js854
1 files changed, 854 insertions, 0 deletions
diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus.js b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
new file mode 100644
index 0000000000..c816c89f82
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
@@ -0,0 +1,854 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/browser/components/places/tests/browser/head.js",
+ this
+);
+/* globals withSidebarTree, synthesizeClickOnSelectedTreeCell, promiseLibrary, promiseLibraryClosed
+ */
+
+const PAGE =
+ "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html";
+
+add_task(async function () {
+ let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+
+ gBrowser.selectedTab = tab1;
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["contextMenus"],
+ },
+
+ background: function () {
+ browser.test.assertEq(
+ browser.contextMenus.ContextType.TAB,
+ "tab",
+ "ContextType is available"
+ );
+ browser.contextMenus.create({
+ id: "clickme-image",
+ title: "Click me!",
+ contexts: ["image"],
+ });
+ browser.contextMenus.create(
+ {
+ id: "clickme-page",
+ title: "Click me!",
+ contexts: ["page"],
+ },
+ () => {
+ browser.test.sendMessage("ready");
+ }
+ );
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("ready");
+
+ let contentAreaContextMenu = await openContextMenu("#img1");
+ let item = contentAreaContextMenu.getElementsByAttribute(
+ "label",
+ "Click me!"
+ );
+ is(item.length, 1, "contextMenu item for image was found");
+ await closeContextMenu();
+
+ contentAreaContextMenu = await openContextMenu("body");
+ item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
+ is(item.length, 1, "contextMenu item for page was found");
+ await closeContextMenu();
+
+ await extension.unload();
+
+ BrowserTestUtils.removeTab(tab1);
+});
+
+add_task(async function () {
+ let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+
+ gBrowser.selectedTab = tab1;
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["contextMenus"],
+ },
+
+ background: async function () {
+ browser.test.onMessage.addListener(msg => {
+ if (msg == "removeall") {
+ browser.contextMenus.removeAll();
+ browser.test.sendMessage("removed");
+ }
+ });
+
+ // A generic onclick callback function.
+ function genericOnClick(info, tab) {
+ browser.test.sendMessage("onclick", { info, tab });
+ }
+
+ browser.contextMenus.onClicked.addListener((info, tab) => {
+ browser.test.sendMessage("browser.contextMenus.onClicked", {
+ info,
+ tab,
+ });
+ });
+
+ browser.contextMenus.create({
+ contexts: ["all"],
+ type: "separator",
+ });
+
+ let contexts = [
+ "page",
+ "link",
+ "selection",
+ "image",
+ "editable",
+ "password",
+ ];
+ for (let i = 0; i < contexts.length; i++) {
+ let context = contexts[i];
+ let title = context;
+ browser.contextMenus.create({
+ title: title,
+ contexts: [context],
+ id: "ext-" + context,
+ onclick: genericOnClick,
+ });
+ if (context == "selection") {
+ browser.contextMenus.update("ext-selection", {
+ title: "selection is: '%s'",
+ onclick: genericOnClick,
+ });
+ }
+ }
+
+ let parent = browser.contextMenus.create({
+ title: "parent",
+ });
+ browser.contextMenus.create({
+ title: "child1",
+ parentId: parent,
+ onclick: genericOnClick,
+ });
+ let child2 = browser.contextMenus.create({
+ title: "child2",
+ parentId: parent,
+ onclick: genericOnClick,
+ });
+
+ let parentToDel = browser.contextMenus.create({
+ title: "parentToDel",
+ });
+ browser.contextMenus.create({
+ title: "child1",
+ parentId: parentToDel,
+ onclick: genericOnClick,
+ });
+ browser.contextMenus.create({
+ title: "child2",
+ parentId: parentToDel,
+ onclick: genericOnClick,
+ });
+ browser.contextMenus.remove(parentToDel);
+
+ browser.contextMenus.create({
+ title: "Without onclick property",
+ id: "ext-without-onclick",
+ });
+
+ await browser.test.assertRejects(
+ browser.contextMenus.update(parent, { parentId: child2 }),
+ /cannot be an ancestor/,
+ "Should not be able to reparent an item as descendent of itself"
+ );
+
+ browser.test.sendMessage("contextmenus");
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("contextmenus");
+
+ let expectedClickInfo = {
+ menuItemId: "ext-image",
+ mediaType: "image",
+ srcUrl:
+ "http://mochi.test:8888/browser/browser/components/extensions/test/browser/ctxmenu-image.png",
+ pageUrl: PAGE,
+ editable: false,
+ };
+
+ function checkClickInfo(result) {
+ for (let i of Object.keys(expectedClickInfo)) {
+ is(
+ result.info[i],
+ expectedClickInfo[i],
+ "click info " +
+ i +
+ " expected to be: " +
+ expectedClickInfo[i] +
+ " but was: " +
+ result.info[i]
+ );
+ }
+ is(
+ expectedClickInfo.pageSrc,
+ result.tab.url,
+ "click info page source is the right tab"
+ );
+ }
+
+ let extensionMenuRoot = await openExtensionContextMenu();
+
+ // Check some menu items
+ let items = extensionMenuRoot.getElementsByAttribute("label", "image");
+ is(items.length, 1, "contextMenu item for image was found (context=image)");
+ let image = items[0];
+
+ items = extensionMenuRoot.getElementsByAttribute("label", "selection-edited");
+ is(
+ items.length,
+ 0,
+ "contextMenu item for selection was not found (context=image)"
+ );
+
+ items = extensionMenuRoot.getElementsByAttribute("label", "parentToDel");
+ is(
+ items.length,
+ 0,
+ "contextMenu item for removed parent was not found (context=image)"
+ );
+
+ items = extensionMenuRoot.getElementsByAttribute("label", "parent");
+ is(items.length, 1, "contextMenu item for parent was found (context=image)");
+
+ is(
+ items[0].menupopup.children.length,
+ 2,
+ "child items for parent were found (context=image)"
+ );
+
+ // Click on ext-image item and check the click results
+ await closeExtensionContextMenu(image);
+
+ let result = await extension.awaitMessage("onclick");
+ checkClickInfo(result);
+ result = await extension.awaitMessage("browser.contextMenus.onClicked");
+ checkClickInfo(result);
+
+ // Test "link" context and OnClick data property.
+ extensionMenuRoot = await openExtensionContextMenu("[href=some-link]");
+
+ // Click on ext-link and check the click results
+ items = extensionMenuRoot.getElementsByAttribute("label", "link");
+ is(items.length, 1, "contextMenu item for parent was found (context=link)");
+ let link = items[0];
+
+ expectedClickInfo = {
+ menuItemId: "ext-link",
+ linkUrl:
+ "http://mochi.test:8888/browser/browser/components/extensions/test/browser/some-link",
+ linkText: "Some link",
+ pageUrl: PAGE,
+ editable: false,
+ };
+
+ await closeExtensionContextMenu(link);
+
+ result = await extension.awaitMessage("onclick");
+ checkClickInfo(result);
+ result = await extension.awaitMessage("browser.contextMenus.onClicked");
+ checkClickInfo(result);
+
+ // Test "editable" context and OnClick data property.
+ extensionMenuRoot = await openExtensionContextMenu("#edit-me");
+
+ // Check some menu items.
+ items = extensionMenuRoot.getElementsByAttribute("label", "editable");
+ is(
+ items.length,
+ 1,
+ "contextMenu item for text input element was found (context=editable)"
+ );
+ let editable = items[0];
+
+ // Click on ext-editable item and check the click results.
+ await closeExtensionContextMenu(editable);
+
+ expectedClickInfo = {
+ menuItemId: "ext-editable",
+ pageUrl: PAGE,
+ editable: true,
+ };
+
+ result = await extension.awaitMessage("onclick");
+ checkClickInfo(result);
+ result = await extension.awaitMessage("browser.contextMenus.onClicked");
+ checkClickInfo(result);
+
+ extensionMenuRoot = await openExtensionContextMenu("#readonly-text");
+
+ // Check some menu items.
+ items = extensionMenuRoot.getElementsByAttribute("label", "editable");
+ is(
+ items.length,
+ 0,
+ "contextMenu item for text input element was not found (context=editable fails for readonly items)"
+ );
+
+ // Hide the popup "manually" because there's nothing to click.
+ await closeContextMenu();
+
+ // Test "editable" context on type=tel and type=number items, and OnClick data property.
+ extensionMenuRoot = await openExtensionContextMenu("#call-me-maybe");
+
+ // Check some menu items.
+ items = extensionMenuRoot.getElementsByAttribute("label", "editable");
+ is(
+ items.length,
+ 1,
+ "contextMenu item for text input element was found (context=editable)"
+ );
+ editable = items[0];
+
+ // Click on ext-editable item and check the click results.
+ await closeExtensionContextMenu(editable);
+
+ expectedClickInfo = {
+ menuItemId: "ext-editable",
+ pageUrl: PAGE,
+ editable: true,
+ };
+
+ result = await extension.awaitMessage("onclick");
+ checkClickInfo(result);
+ result = await extension.awaitMessage("browser.contextMenus.onClicked");
+ checkClickInfo(result);
+
+ extensionMenuRoot = await openExtensionContextMenu("#number-input");
+
+ // Check some menu items.
+ items = extensionMenuRoot.getElementsByAttribute("label", "editable");
+ is(
+ items.length,
+ 1,
+ "contextMenu item for text input element was found (context=editable)"
+ );
+ editable = items[0];
+
+ // Click on ext-editable item and check the click results.
+ await closeExtensionContextMenu(editable);
+
+ expectedClickInfo = {
+ menuItemId: "ext-editable",
+ pageUrl: PAGE,
+ editable: true,
+ };
+
+ result = await extension.awaitMessage("onclick");
+ checkClickInfo(result);
+ result = await extension.awaitMessage("browser.contextMenus.onClicked");
+ checkClickInfo(result);
+
+ extensionMenuRoot = await openExtensionContextMenu("#password");
+ items = extensionMenuRoot.getElementsByAttribute("label", "password");
+ is(
+ items.length,
+ 1,
+ "contextMenu item for password input element was found (context=password)"
+ );
+ let password = items[0];
+ await closeExtensionContextMenu(password);
+ expectedClickInfo = {
+ menuItemId: "ext-password",
+ pageUrl: PAGE,
+ editable: true,
+ };
+
+ result = await extension.awaitMessage("onclick");
+ checkClickInfo(result);
+ result = await extension.awaitMessage("browser.contextMenus.onClicked");
+ checkClickInfo(result);
+
+ extensionMenuRoot = await openExtensionContextMenu("#noneditablepassword");
+ items = extensionMenuRoot.getElementsByAttribute("label", "password");
+ is(
+ items.length,
+ 1,
+ "contextMenu item for password input element was found (context=password)"
+ );
+ password = items[0];
+ await closeExtensionContextMenu(password);
+ expectedClickInfo.editable = false;
+ result = await extension.awaitMessage("onclick");
+ checkClickInfo(result);
+ result = await extension.awaitMessage("browser.contextMenus.onClicked");
+ checkClickInfo(result);
+
+ // Select some text
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function (arg) {
+ let doc = content.document;
+ let range = doc.createRange();
+ let selection = content.getSelection();
+ selection.removeAllRanges();
+ let textNode = doc.getElementById("img1").previousSibling;
+ range.setStart(textNode, 0);
+ range.setEnd(textNode, 100);
+ selection.addRange(range);
+ });
+
+ // Bring up context menu again
+ extensionMenuRoot = await openExtensionContextMenu();
+
+ // Check some menu items
+ items = extensionMenuRoot.getElementsByAttribute(
+ "label",
+ "Without onclick property"
+ );
+ is(items.length, 1, "contextMenu item was found (context=page)");
+
+ await closeExtensionContextMenu(items[0]);
+
+ expectedClickInfo = {
+ menuItemId: "ext-without-onclick",
+ pageUrl: PAGE,
+ };
+
+ result = await extension.awaitMessage("browser.contextMenus.onClicked");
+ checkClickInfo(result);
+
+ // Bring up context menu again
+ extensionMenuRoot = await openExtensionContextMenu();
+
+ // Check some menu items
+ items = extensionMenuRoot.getElementsByAttribute(
+ "label",
+ "selection is: 'just some text 12345678901234567890123456789012\u2026'"
+ );
+ is(
+ items.length,
+ 1,
+ "contextMenu item for selection was found (context=selection)"
+ );
+ let selectionItem = items[0];
+
+ items = extensionMenuRoot.getElementsByAttribute("label", "selection");
+ is(
+ items.length,
+ 0,
+ "contextMenu item label update worked (context=selection)"
+ );
+
+ await closeExtensionContextMenu(selectionItem);
+
+ expectedClickInfo = {
+ menuItemId: "ext-selection",
+ pageUrl: PAGE,
+ selectionText:
+ " just some text 1234567890123456789012345678901234567890123456789012345678901234567890123456789012",
+ };
+
+ result = await extension.awaitMessage("onclick");
+ checkClickInfo(result);
+ result = await extension.awaitMessage("browser.contextMenus.onClicked");
+ checkClickInfo(result);
+
+ // Select a lot of text
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function (arg) {
+ let doc = content.document;
+ let range = doc.createRange();
+ let selection = content.getSelection();
+ selection.removeAllRanges();
+ let textNode = doc.getElementById("longtext").firstChild;
+ range.setStart(textNode, 0);
+ range.setEnd(textNode, textNode.length);
+ selection.addRange(range);
+ });
+
+ // Bring up context menu again
+ extensionMenuRoot = await openExtensionContextMenu("#longtext");
+
+ // Check some menu items
+ items = extensionMenuRoot.getElementsByAttribute(
+ "label",
+ "selection is: 'Sed ut perspiciatis unde omnis iste natus error\u2026'"
+ );
+ is(
+ items.length,
+ 1,
+ `contextMenu item for longtext selection was found (context=selection)`
+ );
+ await closeExtensionContextMenu(items[0]);
+
+ expectedClickInfo = {
+ menuItemId: "ext-selection",
+ pageUrl: PAGE,
+ };
+
+ result = await extension.awaitMessage("onclick");
+ checkClickInfo(result);
+ result = await extension.awaitMessage("browser.contextMenus.onClicked");
+ checkClickInfo(result);
+ ok(
+ result.info.selectionText.endsWith("quo voluptas nulla pariatur?"),
+ "long text selection worked"
+ );
+
+ // Select a lot of text, excercise the editable element code path in
+ // the Browser:GetSelection handler.
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function (arg) {
+ let doc = content.document;
+ let node = doc.getElementById("editabletext");
+ // content.js handleContentContextMenu fails intermittently without focus.
+ node.focus();
+ node.selectionStart = 0;
+ node.selectionEnd = 844;
+ });
+
+ // Bring up context menu again
+ extensionMenuRoot = await openExtensionContextMenu("#editabletext");
+
+ // Check some menu items
+ items = extensionMenuRoot.getElementsByAttribute("label", "editable");
+ is(
+ items.length,
+ 1,
+ "contextMenu item for text input element was found (context=editable)"
+ );
+ await closeExtensionContextMenu(items[0]);
+
+ expectedClickInfo = {
+ menuItemId: "ext-editable",
+ editable: true,
+ pageUrl: PAGE,
+ };
+
+ result = await extension.awaitMessage("onclick");
+ checkClickInfo(result);
+ result = await extension.awaitMessage("browser.contextMenus.onClicked");
+ checkClickInfo(result);
+ ok(
+ result.info.selectionText.endsWith(
+ "perferendis doloribus asperiores repellat."
+ ),
+ "long text selection worked"
+ );
+
+ extension.sendMessage("removeall");
+ await extension.awaitMessage("removed");
+
+ let contentAreaContextMenu = await openContextMenu("#img1");
+ items = contentAreaContextMenu.getElementsByAttribute(
+ "ext-type",
+ "top-level-menu"
+ );
+ is(items.length, 0, "top level item was not found (after removeAll()");
+ await closeContextMenu();
+
+ await extension.unload();
+ BrowserTestUtils.removeTab(tab1);
+});
+
+add_task(async function testRemoveAllWithTwoExtensions() {
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+ const manifest = { permissions: ["contextMenus"] };
+
+ const first = ExtensionTestUtils.loadExtension({
+ manifest,
+ background() {
+ browser.contextMenus.create({ title: "alpha", contexts: ["all"] });
+
+ browser.contextMenus.onClicked.addListener(() => {
+ browser.contextMenus.removeAll();
+ });
+ browser.test.onMessage.addListener(msg => {
+ if (msg == "ping") {
+ browser.test.sendMessage("pong-alpha");
+ return;
+ }
+ browser.contextMenus.create({ title: "gamma", contexts: ["all"] });
+ });
+ },
+ });
+
+ const second = ExtensionTestUtils.loadExtension({
+ manifest,
+ background() {
+ browser.contextMenus.create({ title: "beta", contexts: ["all"] });
+
+ browser.contextMenus.onClicked.addListener(() => {
+ browser.contextMenus.removeAll();
+ });
+
+ browser.test.onMessage.addListener(() => {
+ browser.test.sendMessage("pong-beta");
+ });
+ },
+ });
+
+ await first.startup();
+ await second.startup();
+
+ async function confirmMenuItems(...items) {
+ // Round-trip to extension to make sure that the context menu state has been
+ // updated by the async contextMenus.create / contextMenus.removeAll calls.
+ first.sendMessage("ping");
+ second.sendMessage("ping");
+ await first.awaitMessage("pong-alpha");
+ await second.awaitMessage("pong-beta");
+
+ const menu = await openContextMenu();
+ for (const id of ["alpha", "beta", "gamma"]) {
+ const expected = items.includes(id);
+ const found = menu.getElementsByAttribute("label", id);
+ is(
+ !!found.length,
+ expected,
+ `menu item ${id} ${expected ? "" : "not "}found`
+ );
+ }
+ // Return the first menu item, we need to click it.
+ return menu.getElementsByAttribute("label", items[0])[0];
+ }
+
+ // Confirm alpha, beta exist; click alpha to remove it.
+ const alpha = await confirmMenuItems("alpha", "beta");
+ await closeExtensionContextMenu(alpha);
+
+ // Confirm only beta exists.
+ await confirmMenuItems("beta");
+ await closeContextMenu();
+
+ // Create gamma, confirm, click.
+ first.sendMessage("create");
+ const beta = await confirmMenuItems("beta", "gamma");
+ await closeExtensionContextMenu(beta);
+
+ // Confirm only gamma is left.
+ await confirmMenuItems("gamma");
+ await closeContextMenu();
+
+ await first.unload();
+ await second.unload();
+ BrowserTestUtils.removeTab(tab);
+});
+
+function bookmarkContextMenuExtension() {
+ return ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["contextMenus", "bookmarks", "activeTab"],
+ },
+ async background() {
+ const url = "https://example.com/";
+ const title = "Example";
+ let newBookmark = await browser.bookmarks.create({
+ url,
+ title,
+ parentId: "toolbar_____",
+ });
+ browser.contextMenus.onClicked.addListener(async (info, tab) => {
+ browser.test.assertEq(
+ undefined,
+ tab,
+ "click event in bookmarks menu is not associated with any tab"
+ );
+ browser.test.assertEq(
+ newBookmark.id,
+ info.bookmarkId,
+ "Bookmark ID matches"
+ );
+
+ await browser.test.assertRejects(
+ browser.tabs.executeScript({ code: "'some code';" }),
+ /Missing host permission for the tab/,
+ "Content script should not run, activeTab should not be granted to bookmark menu events"
+ );
+
+ let [bookmark] = await browser.bookmarks.get(info.bookmarkId);
+ browser.test.assertEq(title, bookmark.title, "Bookmark title matches");
+ browser.test.assertEq(url, bookmark.url, "Bookmark url matches");
+ browser.test.assertFalse(
+ info.hasOwnProperty("pageUrl"),
+ "Context menu does not expose pageUrl"
+ );
+ await browser.bookmarks.remove(info.bookmarkId);
+ browser.test.sendMessage("test-finish");
+ });
+ browser.contextMenus.create(
+ {
+ title: "Get bookmark",
+ contexts: ["bookmark"],
+ },
+ () => {
+ browser.test.sendMessage("bookmark-created", newBookmark.id);
+ }
+ );
+ },
+ });
+}
+
+add_task(async function test_bookmark_contextmenu() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+
+ await toggleBookmarksToolbar(true);
+
+ const extension = bookmarkContextMenuExtension();
+
+ await extension.startup();
+ await extension.awaitMessage("bookmark-created");
+ let menu = await openChromeContextMenu(
+ "placesContext",
+ "#PersonalToolbar .bookmark-item:last-child"
+ );
+
+ let menuItem = menu.getElementsByAttribute("label", "Get bookmark")[0];
+ closeChromeContextMenu("placesContext", menuItem);
+
+ await extension.awaitMessage("test-finish");
+ await extension.unload();
+ await toggleBookmarksToolbar(false);
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_bookmark_sidebar_contextmenu() {
+ await withSidebarTree("bookmarks", async tree => {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+
+ let extension = bookmarkContextMenuExtension();
+ await extension.startup();
+ let bookmarkGuid = await extension.awaitMessage("bookmark-created");
+
+ let sidebar = window.SidebarUI.browser;
+ let menu = sidebar.contentDocument.getElementById("placesContext");
+ tree.selectItems([bookmarkGuid]);
+ let shown = BrowserTestUtils.waitForEvent(menu, "popupshown");
+ synthesizeClickOnSelectedTreeCell(tree, { type: "contextmenu" });
+ await shown;
+
+ let menuItem = menu.getElementsByAttribute("label", "Get bookmark")[0];
+ closeChromeContextMenu("placesContext", menuItem, sidebar.contentWindow);
+ await extension.awaitMessage("test-finish");
+ await extension.unload();
+
+ BrowserTestUtils.removeTab(tab);
+ });
+});
+
+function bookmarkFolderContextMenuExtension() {
+ return ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["contextMenus", "bookmarks"],
+ },
+ async background() {
+ const title = "Example";
+ let newBookmark = await browser.bookmarks.create({
+ title,
+ parentId: "toolbar_____",
+ });
+ await new Promise(resolve =>
+ browser.contextMenus.create(
+ {
+ title: "Get bookmark",
+ contexts: ["bookmark"],
+ },
+ resolve
+ )
+ );
+ browser.contextMenus.onClicked.addListener(async info => {
+ browser.test.assertEq(
+ newBookmark.id,
+ info.bookmarkId,
+ "Bookmark ID matches"
+ );
+
+ let [bookmark] = await browser.bookmarks.get(info.bookmarkId);
+ browser.test.assertEq(title, bookmark.title, "Bookmark title matches");
+ browser.test.assertFalse(
+ info.hasOwnProperty("pageUrl"),
+ "Context menu does not expose pageUrl"
+ );
+ await browser.bookmarks.remove(info.bookmarkId);
+ browser.test.sendMessage("test-finish");
+ });
+ browser.test.sendMessage("bookmark-created", newBookmark.id);
+ },
+ });
+}
+
+add_task(async function test_organizer_contextmenu() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+ let library = await promiseLibrary("BookmarksToolbar");
+
+ let menu = library.document.getElementById("placesContext");
+ let mainTree = library.document.getElementById("placeContent");
+ let leftTree = library.document.getElementById("placesList");
+
+ let tests = [
+ [mainTree, bookmarkContextMenuExtension],
+ [mainTree, bookmarkFolderContextMenuExtension],
+ [leftTree, bookmarkFolderContextMenuExtension],
+ ];
+
+ for (let [tree, makeExtension] of tests) {
+ let extension = makeExtension();
+ await extension.startup();
+ let bookmarkGuid = await extension.awaitMessage("bookmark-created");
+
+ tree.selectItems([bookmarkGuid]);
+ let shown = BrowserTestUtils.waitForEvent(menu, "popupshown");
+ synthesizeClickOnSelectedTreeCell(tree, { type: "contextmenu" });
+ await shown;
+
+ let menuItem = menu.getElementsByAttribute("label", "Get bookmark")[0];
+ closeChromeContextMenu("placesContext", menuItem, library);
+ await extension.awaitMessage("test-finish");
+ await extension.unload();
+ }
+
+ await promiseLibraryClosed(library);
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_bookmark_context_requires_permission() {
+ await toggleBookmarksToolbar(true);
+
+ const extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["contextMenus"],
+ },
+ background() {
+ browser.contextMenus.create(
+ {
+ title: "Get bookmark",
+ contexts: ["bookmark"],
+ },
+ () => {
+ browser.test.sendMessage("bookmark-created");
+ }
+ );
+ },
+ });
+ await extension.startup();
+ await extension.awaitMessage("bookmark-created");
+ let menu = await openChromeContextMenu(
+ "placesContext",
+ "#PersonalToolbar .bookmark-item:last-child"
+ );
+
+ Assert.equal(
+ menu.getElementsByAttribute("label", "Get bookmark").length,
+ 0,
+ "bookmark context menu not created with `bookmarks` permission."
+ );
+
+ closeChromeContextMenu("placesContext");
+
+ await extension.unload();
+ await toggleBookmarksToolbar(false);
+});