Bug 1353073 handle contextmenu in sidebar when remote=true, r?kmag draft
authorShane Caraveo <scaraveo@mozilla.com>
Sat, 15 Apr 2017 10:30:44 -0700
changeset 563237 b004758c9e898d2f914b0508817c5fd4885c48c5
parent 562355 3243c8fc3ce7831dda843b60d6bb2d7e4acf1fd4
child 624425 0d00138c1f47a125c20084cfeff1a731224c8e5c
push id54246
push usermixedpuppy@gmail.com
push dateSat, 15 Apr 2017 17:31:04 +0000
reviewerskmag
bugs1353073
milestone55.0a1
Bug 1353073 handle contextmenu in sidebar when remote=true, r?kmag MozReview-Commit-ID: BSmPhaNYmYe
browser/base/content/nsContextMenu.js
browser/base/content/tabbrowser.xml
browser/base/content/webext-panels.js
browser/components/extensions/test/browser/browser_ext_sidebarAction.js
browser/components/extensions/test/browser/head.js
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -13,16 +13,54 @@ Components.utils.import("resource://gre/
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 
 XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
   "resource://gre/modules/LoginHelper.jsm");
 
 var gContextMenuContentData = null;
 
+function openContextMenu(aMessage) {
+  let data = aMessage.data;
+  let browser = aMessage.target;
+
+  let spellInfo = data.spellInfo;
+  if (spellInfo)
+    spellInfo.target = aMessage.target.messageManager;
+  let documentURIObject = makeURI(data.docLocation,
+                                  data.charSet,
+                                  makeURI(data.baseURI));
+  gContextMenuContentData = { isRemote: true,
+                              event: aMessage.objects.event,
+                              popupNode: aMessage.objects.popupNode,
+                              browser,
+                              editFlags: data.editFlags,
+                              spellInfo,
+                              principal: data.principal,
+                              customMenuItems: data.customMenuItems,
+                              addonInfo: data.addonInfo,
+                              documentURIObject,
+                              docLocation: data.docLocation,
+                              charSet: data.charSet,
+                              referrer: data.referrer,
+                              referrerPolicy: data.referrerPolicy,
+                              contentType: data.contentType,
+                              contentDisposition: data.contentDisposition,
+                              frameOuterWindowID: data.frameOuterWindowID,
+                              selectionInfo: data.selectionInfo,
+                              disableSetDesktopBackground: data.disableSetDesktopBg,
+                              loginFillInfo: data.loginFillInfo,
+                              parentAllowsMixedContent: data.parentAllowsMixedContent,
+                              userContextId: data.userContextId,
+                            };
+  let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
+  let event = gContextMenuContentData.event;
+  popup.openPopupAtScreen(event.screenX, event.screenY, true);
+}
+
 function nsContextMenu(aXulMenu, aIsShift) {
   this.shouldDisplay = true;
   this.initMenu(aXulMenu, aIsShift);
 }
 
 // Prototype for nsContextMenu "class."
 nsContextMenu.prototype = {
   initMenu: function CM_initMenu(aXulMenu, aIsShift) {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4950,48 +4950,17 @@
               if (tab) {
                 // Skip running PermitUnload since it already happened in
                 // the content process.
                 this.removeTab(tab, {skipPermitUnload: true});
               }
               break;
             }
             case "contextmenu": {
-              let spellInfo = data.spellInfo;
-              if (spellInfo)
-                spellInfo.target = aMessage.target.messageManager;
-              let documentURIObject = makeURI(data.docLocation,
-                                              data.charSet,
-                                              makeURI(data.baseURI));
-              gContextMenuContentData = { isRemote: true,
-                                          event: aMessage.objects.event,
-                                          popupNode: aMessage.objects.popupNode,
-                                          browser,
-                                          editFlags: data.editFlags,
-                                          spellInfo,
-                                          principal: data.principal,
-                                          customMenuItems: data.customMenuItems,
-                                          addonInfo: data.addonInfo,
-                                          documentURIObject,
-                                          docLocation: data.docLocation,
-                                          charSet: data.charSet,
-                                          referrer: data.referrer,
-                                          referrerPolicy: data.referrerPolicy,
-                                          contentType: data.contentType,
-                                          contentDisposition: data.contentDisposition,
-                                          frameOuterWindowID: data.frameOuterWindowID,
-                                          selectionInfo: data.selectionInfo,
-                                          disableSetDesktopBackground: data.disableSetDesktopBg,
-                                          loginFillInfo: data.loginFillInfo,
-                                          parentAllowsMixedContent: data.parentAllowsMixedContent,
-                                          userContextId: data.userContextId,
-                                        };
-              let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
-              let event = gContextMenuContentData.event;
-              popup.openPopupAtScreen(event.screenX, event.screenY, true);
+              openContextMenu(aMessage);
               break;
             }
             case "DOMWindowFocus": {
               let tab = this.getTabForBrowser(browser);
               if (!tab)
                 return undefined;
               this.selectedTab = tab;
               window.focus();
--- a/browser/base/content/webext-panels.js
+++ b/browser/base/content/webext-panels.js
@@ -1,26 +1,28 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Via webext-panels.xul
 /* import-globals-from browser.js */
+/* import-globals-from nsContextMenu.js */
 
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
                                   "resource://gre/modules/ExtensionParent.jsm");
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 
 var {
   promiseEvent,
 } = ExtensionUtils;
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
+
 function getBrowser(sidebar) {
   let browser = document.getElementById("webext-panels-browser");
   if (browser) {
     return Promise.resolve(browser);
   }
 
   browser = document.createElementNS(XUL_NS, "browser");
   browser.setAttribute("id", "webext-panels-browser");
@@ -34,16 +36,21 @@ function getBrowser(sidebar) {
 
   let readyPromise;
   if (sidebar.remote) {
     browser.setAttribute("remote", "true");
     browser.setAttribute("remoteType",
                          E10SUtils.getRemoteTypeForURI(sidebar.uri, true,
                                                        E10SUtils.EXTENSION_REMOTE_TYPE));
     readyPromise = promiseEvent(browser, "XULFrameLoaderCreated");
+
+    window.messageManager.addMessageListener("contextmenu", openContextMenu);
+    window.addEventListener("unload", () => {
+      window.messageManager.removeMessageListener("contextmenu", openContextMenu);
+    }, {once: true});
   } else {
     readyPromise = Promise.resolve();
   }
   document.documentElement.appendChild(browser);
 
   return readyPromise.then(() => {
     browser.messageManager.loadFrameScript("chrome://browser/content/content.js", false);
     ExtensionParent.apiManager.emit("extension-browser-inserted", browser);
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
@@ -1,14 +1,15 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 let extData = {
   manifest: {
+    "permissions": ["contextMenus"],
     "sidebar_action": {
       "default_panel": "sidebar.html",
     },
   },
   useAddonManager: "temporary",
 
   files: {
     "sidebar.html": `
@@ -25,16 +26,22 @@ let extData = {
     "sidebar.js": function() {
       window.onload = () => {
         browser.test.sendMessage("sidebar");
       };
     },
   },
 
   background: function() {
+    browser.contextMenus.create({
+      id: "clickme-page",
+      title: "Click me!",
+      contexts: ["all"],
+    });
+
     browser.test.onMessage.addListener(msg => {
       if (msg === "set-panel") {
         browser.sidebarAction.setPanel({panel: ""}).then(() => {
           browser.test.notifyFail("empty panel settable");
         }).catch(() => {
           browser.test.notifyPass("unable to set empty panel");
         });
       }
@@ -91,12 +98,26 @@ add_task(function* sidebar_empty_panel()
   // Test sidebar is opened on install
   yield extension.awaitMessage("sidebar");
   ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible in first window");
   extension.sendMessage("set-panel");
   yield extension.awaitFinish();
   yield extension.unload();
 });
 
+add_task(function* sidebar_contextmenu() {
+  let extension = ExtensionTestUtils.loadExtension(extData);
+  yield extension.startup();
+  // Test sidebar is opened on install
+  yield extension.awaitMessage("sidebar");
+
+  let contentAreaContextMenu = yield openContextMenuInSidebar();
+  let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
+  is(item.length, 1, "contextMenu item for page was found");
+  yield closeContextMenu(contentAreaContextMenu);
+
+  yield extension.unload();
+});
+
 add_task(function* cleanup() {
   // This is set on initial sidebar install.
   Services.prefs.clearUserPref("extensions.sidebar-button.shown");
 });
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -3,17 +3,17 @@
 "use strict";
 
 /* exported CustomizableUI makeWidgetId focusWindow forceGC
  *          getBrowserActionWidget
  *          clickBrowserAction clickPageAction
  *          getBrowserActionPopup getPageActionPopup
  *          closeBrowserAction closePageAction
  *          promisePopupShown promisePopupHidden
- *          openContextMenu closeContextMenu
+ *          openContextMenu closeContextMenu openContextMenuInSidebar
  *          openExtensionContextMenu closeExtensionContextMenu
  *          openActionContextMenu openSubmenu closeActionContextMenu
  *          openTabContextMenu closeTabContextMenu
  *          imageBuffer imageBufferFromDataURI
  *          getListStyleImage getPanelForNode
  *          awaitExtensionPanel awaitPopupResize
  *          promiseContentDimensions alterContent
  *          promisePrefChangeObserved openContextMenuInFrame
@@ -227,16 +227,26 @@ function closeBrowserAction(extension, w
   let group = getBrowserActionWidget(extension);
 
   let node = win.document.getElementById(group.viewId);
   CustomizableUI.hidePanelForNode(node);
 
   return Promise.resolve();
 }
 
+async function openContextMenuInSidebar(selector = "body") {
+  let contentAreaContextMenu = SidebarUI.browser.contentDocument.getElementById("contentAreaContextMenu");
+  let browser = SidebarUI.browser.contentDocument.getElementById("webext-panels-browser");
+  let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
+  await BrowserTestUtils.synthesizeMouseAtCenter(selector, {type: "mousedown", button: 2}, browser);
+  await BrowserTestUtils.synthesizeMouseAtCenter(selector, {type: "contextmenu"}, browser);
+  await popupShownPromise;
+  return contentAreaContextMenu;
+}
+
 async function openContextMenuInFrame(frameId) {
   let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
   let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
   let doc = gBrowser.selectedBrowser.contentDocument;
   let frame = doc.getElementById(frameId);
   EventUtils.synthesizeMouseAtCenter(frame.contentDocument.body, {type: "contextmenu"}, frame.contentWindow);
   await popupShownPromise;
   return contentAreaContextMenu;
@@ -246,18 +256,18 @@ async function openContextMenu(selector 
   let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
   let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
   await BrowserTestUtils.synthesizeMouseAtCenter(selector, {type: "mousedown", button: 2}, gBrowser.selectedBrowser);
   await BrowserTestUtils.synthesizeMouseAtCenter(selector, {type: "contextmenu"}, gBrowser.selectedBrowser);
   await popupShownPromise;
   return contentAreaContextMenu;
 }
 
-async function closeContextMenu() {
-  let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
+async function closeContextMenu(contextMenu) {
+  let contentAreaContextMenu = contextMenu || document.getElementById("contentAreaContextMenu");
   let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
   contentAreaContextMenu.hidePopup();
   await popupHiddenPromise;
 }
 
 function* openExtensionContextMenu(selector = "#img1") {
   let contextMenu = yield openContextMenu(selector);
   let topLevelMenu = contextMenu.getElementsByAttribute("ext-type", "top-level-menu");