Bug 1419195: Show items from WebExtensions in bookmarks sidebar context menu r=mixedpuppy
authorPeter Simonyi <pts@petersimonyi.ca>
Thu, 17 Jan 2019 09:49:36 +0000
changeset 514235 6b7a9a7afa56c0cc0f2399bf378cb6d12b73b86a
parent 514234 cbe224190304b678b8ca0c9647cd5989e3a93609
child 514236 8b35181c3ccc747106037b61a9b52adfbba5ed60
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy
bugs1419195
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1419195: Show items from WebExtensions in bookmarks sidebar context menu r=mixedpuppy Differential Revision: https://phabricator.services.mozilla.com/D16413
browser/components/extensions/parent/ext-menus.js
browser/components/extensions/test/browser/browser-common.ini
browser/components/extensions/test/browser/browser_ext_contextMenus.js
--- a/browser/components/extensions/parent/ext-menus.js
+++ b/browser/components/extensions/parent/ext-menus.js
@@ -874,38 +874,77 @@ const menuTracker = {
       this.onWindowOpen(window);
     }
     windowTracker.addOpenListener(this.onWindowOpen);
   },
 
   unregister() {
     Services.obs.removeObserver(this, "on-build-contextmenu");
     for (const window of windowTracker.browserWindows()) {
-      for (const id of this.menuIds) {
-        const menu = window.document.getElementById(id);
-        menu.removeEventListener("popupshowing", this);
-      }
+      this.cleanupWindow(window);
     }
     windowTracker.removeOpenListener(this.onWindowOpen);
   },
 
   observe(subject, topic, data) {
     subject = subject.wrappedJSObject;
     gMenuBuilder.build(subject);
   },
 
   onWindowOpen(window) {
     for (const id of menuTracker.menuIds) {
       const menu = window.document.getElementById(id);
       menu.addEventListener("popupshowing", menuTracker);
     }
+
+    const sidebarHeader = window.document.getElementById("sidebar-switcher-target");
+    sidebarHeader.addEventListener("SidebarShown", menuTracker.onSidebarShown);
+    if (window.SidebarUI.currentID === "viewBookmarksSidebar") {
+      menuTracker.onSidebarShown({currentTarget: sidebarHeader});
+    }
+  },
+
+  cleanupWindow(window) {
+    for (const id of this.menuIds) {
+      const menu = window.document.getElementById(id);
+      menu.removeEventListener("popupshowing", this);
+    }
+
+    const sidebarHeader = window.document.getElementById("sidebar-switcher-target");
+    sidebarHeader.removeEventListener("SidebarShown", this.onSidebarShown);
+
+    if (window.SidebarUI.currentID === "viewBookmarksSidebar") {
+      let sidebarBrowser = window.SidebarUI.browser;
+      sidebarBrowser.removeEventListener("load", this.onSidebarShown);
+      const menu = sidebarBrowser.contentDocument.getElementById("placesContext");
+      menu.removeEventListener("popupshowing", this.onBookmarksContextMenu);
+    }
+  },
+
+  onSidebarShown(event) {
+    // The event target is an element in a browser window, so |window| will be
+    // the browser window that contains the sidebar.
+    const window = event.currentTarget.ownerGlobal;
+    if (window.SidebarUI.currentID === "viewBookmarksSidebar") {
+      let sidebarBrowser = window.SidebarUI.browser;
+      if (sidebarBrowser.contentDocument.readyState !== "complete") {
+        // SidebarUI.currentID may be updated before the bookmark sidebar's
+        // document has finished loading. This sometimes happens when the
+        // sidebar is automatically shown when a new window is opened.
+        sidebarBrowser.addEventListener("load", menuTracker.onSidebarShown, {once: true});
+        return;
+      }
+      const menu = sidebarBrowser.contentDocument.getElementById("placesContext");
+      menu.addEventListener("popupshowing", menuTracker.onBookmarksContextMenu);
+    }
   },
 
   handleEvent(event) {
     const menu = event.target;
+
     if (menu.id === "placesContext") {
       const trigger = menu.triggerNode;
       if (!trigger._placesNode) {
         return;
       }
 
       gMenuBuilder.build({
         menu,
@@ -920,16 +959,29 @@ const menuTracker = {
     }
     if (menu.id === "tabContextMenu") {
       const trigger = menu.triggerNode;
       const tab = trigger.localName === "tab" ? trigger : tabTracker.activeTab;
       const pageUrl = tab.linkedBrowser.currentURI.spec;
       gMenuBuilder.build({menu, tab, pageUrl, onTab: true});
     }
   },
+
+  onBookmarksContextMenu(event) {
+    const menu = event.target;
+    const tree = menu.triggerNode.parentElement;
+    const cell = tree.getCellAt(event.x, event.y);
+    const node = tree.view.nodeForTreeIndex(cell.row);
+
+    gMenuBuilder.build({
+      menu,
+      bookmarkId: node.bookmarkGuid,
+      onBookmark: true,
+    });
+  },
 };
 
 this.menusInternal = class extends ExtensionAPI {
   constructor(extension) {
     super(extension);
 
     if (!gMenuMap.size) {
       menuTracker.register();
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -76,16 +76,17 @@ skip-if = (os == 'mac' || os == 'win' ||
 skip-if = (verify && (os == 'linux' || os == 'mac'))
 [browser_ext_commands_execute_sidebar_action.js]
 [browser_ext_commands_getAll.js]
 [browser_ext_commands_onCommand.js]
 [browser_ext_commands_update.js]
 [browser_ext_connect_and_move_tabs.js]
 [browser_ext_contentscript_connect.js]
 [browser_ext_contextMenus.js]
+support-files = !/browser/components/places/tests/browser/head.js
 [browser_ext_contextMenus_checkboxes.js]
 [browser_ext_contextMenus_commands.js]
 [browser_ext_contextMenus_icons.js]
 [browser_ext_contextMenus_onclick.js]
 [browser_ext_contextMenus_radioGroups.js]
 [browser_ext_contextMenus_targetUrlPatterns.js]
 [browser_ext_contextMenus_uninstall.js]
 [browser_ext_contextMenus_urlPatterns.js]
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
@@ -1,12 +1,17 @@
 /* -*- 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 */
+
 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({
@@ -509,23 +514,18 @@ add_task(async function testRemoveAllWit
   await confirmMenuItems("gamma");
   await closeContextMenu();
 
   await first.unload();
   await second.unload();
   BrowserTestUtils.removeTab(tab);
 });
 
-add_task(async function test_bookmark_contextmenu() {
-  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
-
-  const bookmarksToolbar = document.getElementById("PersonalToolbar");
-  setToolbarVisibility(bookmarksToolbar, true);
-
-  const extension = ExtensionTestUtils.loadExtension({
+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,
@@ -547,36 +547,70 @@ add_task(async function test_bookmark_co
         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");
+        browser.test.sendMessage("bookmark-created", newBookmark.id);
       });
     },
   });
+}
+
+add_task(async function test_bookmark_contextmenu() {
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+
+  const bookmarksToolbar = document.getElementById("PersonalToolbar");
+  setToolbarVisibility(bookmarksToolbar, 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();
   setToolbarVisibility(bookmarksToolbar, 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);
+  });
+});
+
 add_task(async function test_bookmark_context_requires_permission() {
   const bookmarksToolbar = document.getElementById("PersonalToolbar");
   setToolbarVisibility(bookmarksToolbar, true);
 
   const extension = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["contextMenus"],
     },