Bug 1505909 - Merge remote and non-remote context menu handlers. r=NeilDeakin
☠☠ backed out by d83d3b37c2b8 ☠ ☠
authorMike Conley <mconley@mozilla.com>
Fri, 07 Jun 2019 14:28:33 +0000
changeset 477838 3e31f9726798475cb120da8b03cad64d97c5d822
parent 477837 000091da2b92ddcb030cfc39f6c7271be6d50af7
child 477839 40f0a56ed3af2a2b94ce6cfe17cb4920d1702e05
push id36125
push userapavel@mozilla.com
push dateFri, 07 Jun 2019 22:00:07 +0000
treeherdermozilla-central@d820bbb356aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersNeilDeakin
bugs1505909
milestone69.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 1505909 - Merge remote and non-remote context menu handlers. r=NeilDeakin Differential Revision: https://phabricator.services.mozilla.com/D32757
browser/actors/ContextMenuChild.jsm
browser/base/content/browser.js
browser/base/content/nsContextMenu.js
browser/base/content/tabbrowser.js
toolkit/modules/PageMenu.jsm
--- a/browser/actors/ContextMenuChild.jsm
+++ b/browser/actors/ContextMenuChild.jsm
@@ -481,25 +481,26 @@ class ContextMenuChild extends ActorChil
     } = doc;
     docLocation = docLocation && docLocation.spec;
     let frameOuterWindowID = WebNavigationFrames.getFrameId(doc.defaultView);
     let loginFillInfo = LoginManagerContent.getFieldContext(aEvent.composedTarget);
 
     // The same-origin check will be done in nsContextMenu.openLinkInTab.
     let parentAllowsMixedContent = !!this.docShell.mixedContentChannel;
 
-    let disableSetDesktopBg = null;
+    let disableSetDesktopBackground = null;
 
     // Media related cache info parent needs for saving
     let contentType = null;
     let contentDisposition = null;
     if (aEvent.composedTarget.nodeType == aEvent.composedTarget.ELEMENT_NODE &&
         aEvent.composedTarget instanceof Ci.nsIImageLoadingContent &&
         aEvent.composedTarget.currentURI) {
-      disableSetDesktopBg = this._disableSetDesktopBackground(aEvent.composedTarget);
+      disableSetDesktopBackground =
+        this._disableSetDesktopBackground(aEvent.composedTarget);
 
       try {
         let imageCache = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
                                                          .getImgCacheForDocument(doc);
         // The image cache's notion of where this image is located is
         // the currentURI of the image loading content.
         let props = imageCache.findEntryProperties(aEvent.composedTarget.currentURI, doc);
 
@@ -524,90 +525,79 @@ class ContextMenuChild extends ActorChil
 
     let spellInfo = null;
     let editFlags = null;
     let principal = null;
     let customMenuItems = null;
 
     let referrerInfo = Cc["@mozilla.org/referrer-info;1"].createInstance(Ci.nsIReferrerInfo);
     referrerInfo.initWithNode(context.onLink ? context.link : aEvent.composedTarget);
+    referrerInfo = E10SUtils.serializeReferrerInfo(referrerInfo);
 
     let targetAsCPOW = context.target;
     if (targetAsCPOW) {
       this._cleanContext();
     }
 
-    let isRemote = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
+    editFlags = SpellCheckHelper.isEditable(aEvent.composedTarget, this.content);
 
-    if (isRemote) {
-      editFlags = SpellCheckHelper.isEditable(aEvent.composedTarget, this.content);
-
-      if (editFlags & SpellCheckHelper.SPELLCHECKABLE) {
-        spellInfo = InlineSpellCheckerContent.initContextMenu(aEvent, editFlags, this.mm);
-      }
+    if (editFlags & SpellCheckHelper.SPELLCHECKABLE) {
+      spellInfo = InlineSpellCheckerContent.initContextMenu(aEvent, editFlags, this.mm);
+    }
 
-      // Set the event target first as the copy image command needs it to
-      // determine what was context-clicked on. Then, update the state of the
-      // commands on the context menu.
-      this.docShell.contentViewer.QueryInterface(Ci.nsIContentViewerEdit)
-                   .setCommandNode(aEvent.composedTarget);
-      aEvent.composedTarget.ownerGlobal.updateCommands("contentcontextmenu");
+    // Set the event target first as the copy image command needs it to
+    // determine what was context-clicked on. Then, update the state of the
+    // commands on the context menu.
+    this.docShell.contentViewer.QueryInterface(Ci.nsIContentViewerEdit)
+                 .setCommandNode(aEvent.composedTarget);
+    aEvent.composedTarget.ownerGlobal.updateCommands("contentcontextmenu");
 
-      customMenuItems = PageMenuChild.build(aEvent.composedTarget);
-      principal = doc.nodePrincipal;
-    }
+    principal = doc.nodePrincipal;
 
     let data = {
       context,
       charSet,
       baseURI,
-      isRemote,
       referrerInfo,
       editFlags,
       principal,
       spellInfo,
       contentType,
       docLocation,
       loginFillInfo,
       selectionInfo,
       userContextId,
       customMenuItems,
       contentDisposition,
       frameOuterWindowID,
       popupNodeSelectors,
-      disableSetDesktopBg,
+      disableSetDesktopBackground,
       parentAllowsMixedContent,
     };
 
     if (context.inFrame && !context.inSrcdocFrame) {
       data.frameReferrerInfo = doc.referrerInfo;
     }
 
+    if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+      data.customMenuItems = PageMenuChild.build(aEvent.composedTarget);
+    }
+
     Services.obs.notifyObservers({wrappedJSObject: data}, "on-prepare-contextmenu");
 
-    if (isRemote) {
-      data.referrerInfo = E10SUtils.serializeReferrerInfo(data.referrerInfo);
-      if (data.frameReferrerInfo) {
-        data.frameReferrerInfo = E10SUtils.serializeReferrerInfo(data.frameReferrerInfo);
-      }
+    // In the event that the content is running in the parent process, we don't
+    // actually want the contextmenu events to reach the parent - we'll dispatch
+    // a new contextmenu event after the async message has reached the parent
+    // instead.
+    aEvent.preventDefault();
+    aEvent.stopPropagation();
 
-      this.mm.sendAsyncMessage("contextmenu", data, {
-        targetAsCPOW,
-      });
-    } else {
-      let browser = this.docShell.chromeEventHandler;
-      let mainWin = browser.ownerGlobal;
-
-      data.documentURIObject = doc.documentURIObject;
-      data.disableSetDesktopBackground = data.disableSetDesktopBg;
-      delete data.disableSetDesktopBg;
-
-      data.context.targetAsCPOW = targetAsCPOW;
-      mainWin.setContextMenuContentData(data);
-    }
+    this.mm.sendAsyncMessage("contextmenu", data, {
+      targetAsCPOW,
+    });
   }
 
   /**
    * Some things are not serializable, so we either have to only send
    * their needed data or regenerate them in nsContextMenu.js
    * - target and target.ownerDocument
    * - link
    * - linkURI
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -119,18 +119,17 @@ XPCOMUtils.defineLazyScriptGetter(this, 
 XPCOMUtils.defineLazyScriptGetter(this, ["gGestureSupport", "gHistorySwipeAnimation"],
                                   "chrome://browser/content/browser-gestureSupport.js");
 XPCOMUtils.defineLazyScriptGetter(this, "gSafeBrowsing",
                                   "chrome://browser/content/browser-safebrowsing.js");
 XPCOMUtils.defineLazyScriptGetter(this, "gSync",
                                   "chrome://browser/content/browser-sync.js");
 XPCOMUtils.defineLazyScriptGetter(this, "gBrowserThumbnails",
                                   "chrome://browser/content/browser-thumbnails.js");
-XPCOMUtils.defineLazyScriptGetter(this, ["setContextMenuContentData",
-                                         "openContextMenu", "nsContextMenu"],
+XPCOMUtils.defineLazyScriptGetter(this, ["openContextMenu", "nsContextMenu"],
                                   "chrome://browser/content/nsContextMenu.js");
 XPCOMUtils.defineLazyScriptGetter(this, ["DownloadsPanel",
                                          "DownloadsOverlayLoader",
                                          "DownloadsSubview",
                                          "DownloadsView", "DownloadsViewUI",
                                          "DownloadsViewController",
                                          "DownloadsSummary", "DownloadsFooter",
                                          "DownloadsBlockedSubview"],
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -22,20 +22,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
 
 XPCOMUtils.defineLazyGetter(this, "ReferrerInfo", () =>
   Components.Constructor("@mozilla.org/referrer-info;1",
                          "nsIReferrerInfo",
                          "init"));
 
 var gContextMenuContentData = null;
 
-function setContextMenuContentData(data) {
-  gContextMenuContentData = data;
-}
-
 function openContextMenu(aMessage) {
   let data = aMessage.data;
   let browser = aMessage.target;
   let spellInfo = data.spellInfo;
   let frameReferrerInfo = data.frameReferrerInfo;
 
   // ContextMenu.jsm sends us the target as a CPOW only so that
   // we can send that CPOW back down to the content process and
@@ -53,49 +49,47 @@ function openContextMenu(aMessage) {
                                   makeURI(data.baseURI));
 
   if (frameReferrerInfo) {
     frameReferrerInfo =
       E10SUtils.deserializeReferrerInfo(frameReferrerInfo);
   }
 
   gContextMenuContentData = { context: data.context,
-                              isRemote: data.isRemote,
                               popupNodeSelectors: data.popupNodeSelectors,
                               browser,
                               editFlags: data.editFlags,
                               spellInfo,
                               principal: data.principal,
                               customMenuItems: data.customMenuItems,
                               documentURIObject,
                               docLocation: data.docLocation,
                               charSet: data.charSet,
                               referrerInfo: E10SUtils.deserializeReferrerInfo(data.referrerInfo),
                               frameReferrerInfo,
                               contentType: data.contentType,
                               contentDisposition: data.contentDisposition,
                               frameOuterWindowID: data.frameOuterWindowID,
                               selectionInfo: data.selectionInfo,
-                              disableSetDesktopBackground: data.disableSetDesktopBg,
+                              disableSetDesktopBackground: data.disableSetDesktopBackground,
                               loginFillInfo: data.loginFillInfo,
                               parentAllowsMixedContent: data.parentAllowsMixedContent,
                               userContextId: data.userContextId,
                               webExtContextData: data.webExtContextData,
                             };
 
   let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
   let context = gContextMenuContentData.context;
 
   // The event is a CPOW that can't be passed into the native openPopupAtScreen
   // function. Therefore we synthesize a new MouseEvent to propagate the
   // inputSource to the subsequently triggered popupshowing event.
   var newEvent = document.createEvent("MouseEvent");
   newEvent.initNSMouseEvent("contextmenu", true, true, null, 0, context.screenX, context.screenY,
                             0, 0, false, false, false, false, 0, null, 0, context.mozInputSource);
-
   popup.openPopupAtScreen(newEvent.screenX, newEvent.screenY, true, newEvent);
 }
 
 function nsContextMenu(aXulMenu, aIsShift) {
   this.shouldDisplay = true;
   this.initMenu(aXulMenu, aIsShift);
 }
 
@@ -106,23 +100,19 @@ nsContextMenu.prototype = {
     this.setContext();
 
     if (!this.shouldDisplay)
       return;
 
     this.hasPageMenu = false;
     this.isContentSelected = !this.selectionInfo.docSelectionIsCollapsed;
     if (!aIsShift) {
-      if (this.isRemote) {
-        this.hasPageMenu =
-          PageMenuParent.addToPopup(gContextMenuContentData.customMenuItems,
-                                    this.browser, aXulMenu);
-      } else {
-        this.hasPageMenu = PageMenuParent.buildAndAddToPopup(this.target, aXulMenu);
-      }
+      this.hasPageMenu =
+        PageMenuParent.addToPopup(gContextMenuContentData.customMenuItems,
+                                  this.browser, aXulMenu);
 
       let tab = gBrowser && gBrowser.getTabForBrowser ?
         gBrowser.getTabForBrowser(this.browser) : undefined;
 
       let subject = {
         menu: aXulMenu,
         tab,
         timeStamp: this.timeStamp,
@@ -170,22 +160,20 @@ nsContextMenu.prototype = {
       BookmarkingUI.onCurrentPageContextPopupShowing();
 
     // Initialize (disable/remove) menu items.
     this.initItems();
   },
 
   setContext() {
     let context = Object.create(null);
-    this.isRemote = false;
 
     if (gContextMenuContentData) {
       context = gContextMenuContentData.context;
       gContextMenuContentData.context = null;
-      this.isRemote = gContextMenuContentData.isRemote;
     }
 
     this.shouldDisplay = context.shouldDisplay;
     this.timeStamp = context.timeStamp;
 
     // Assign what's _possibly_ needed from `context` sent by ContextMenuChild.jsm
     // Keep this consistent with the similar code in ContextMenu's _setContext
     this.bgImageURL          = context.bgImageURL;
@@ -226,17 +214,17 @@ nsContextMenu.prototype = {
     this.onMozExtLink        = context.onMozExtLink;
     this.onNumeric           = context.onNumeric;
     this.onPassword          = context.onPassword;
     this.onSaveableLink      = context.onSaveableLink;
     this.onSpellcheckable    = context.onSpellcheckable;
     this.onTextInput         = context.onTextInput;
     this.onVideo             = context.onVideo;
 
-    this.target = this.isRemote ? context.target : document.popupNode;
+    this.target = context.target;
     this.targetAsCPOW = context.targetAsCPOW;
 
     this.principal = context.principal;
     this.frameOuterWindowID = context.frameOuterWindowID;
 
     this.inSyntheticDoc = context.inSyntheticDoc;
     this.inAboutDevtoolsToolbox = context.inAboutDevtoolsToolbox;
 
@@ -247,55 +235,34 @@ nsContextMenu.prototype = {
     this.csp = E10SUtils.deserializeCSP(context.csp);
 
     // Remember the CSS selectors corresponding to clicked node. gContextMenuContentData
     // can be null if the menu was triggered by tests in which case use an empty array.
     this.targetSelectors = gContextMenuContentData
                            ? gContextMenuContentData.popupNodeSelectors
                            : [];
 
-    if (this.isRemote) {
-      this.browser = gContextMenuContentData.browser;
-      this.selectionInfo = gContextMenuContentData.selectionInfo;
-    } else {
-      this.browser = this.ownerDoc.defaultView.docShell.chromeEventHandler;
-      this.selectionInfo = BrowserUtils.getSelectionDetails(window);
-    }
+    this.browser = gContextMenuContentData.browser;
+    this.selectionInfo = gContextMenuContentData.selectionInfo;
 
     const {gBrowser} = this.browser.ownerGlobal;
 
     this.textSelected      = this.selectionInfo.text;
     this.isTextSelected    = this.textSelected.length != 0;
     this.webExtBrowserType = this.browser.getAttribute("webextension-view-type");
     this.inWebExtBrowser   = !!this.webExtBrowserType;
     this.inTabBrowser      = gBrowser && gBrowser.getTabForBrowser ?
       !!gBrowser.getTabForBrowser(this.browser) : false;
 
     if (context.shouldInitInlineSpellCheckerUINoChildren) {
-      if (this.isRemote) {
-        InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
-      } else {
-        InlineSpellCheckerUI.init(this.target.editor);
-        InlineSpellCheckerUI.initFromEvent(document.popupRangeParent,
-                                           document.popupRangeOffset);
-      }
+      InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
     }
 
     if (context.shouldInitInlineSpellCheckerUIWithChildren) {
-      if (this.isRemote) {
-        InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
-      } else {
-        var targetWin = this.ownerDoc.defaultView;
-        var {editingSession} = targetWin.docShell;
-
-        InlineSpellCheckerUI.init(editingSession.getEditorForWindow(targetWin));
-        InlineSpellCheckerUI.initFromEvent(document.popupRangeParent,
-                                           document.popupRangeOffset);
-      }
-
+      InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
       let canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
       this.showItem("spell-check-enabled", canSpell);
       this.showItem("spell-separator", canSpell);
     }
   },  // setContext
 
   hiding: function CM_hiding() {
     if (this.browser && this.browser.messageManager) {
@@ -799,22 +766,16 @@ nsContextMenu.prototype = {
                    originPrincipal: this.principal,
                    triggeringPrincipal: this.principal,
                    csp: this.csp,
                    frameOuterWindowID: gContextMenuContentData.frameOuterWindowID};
     for (let p in extra) {
       params[p] = extra[p];
     }
 
-    if (!this.isRemote) {
-      // Propagate the frameOuterWindowID value saved when
-      // the context menu has been opened.
-      params.frameOuterWindowID = this.frameOuterWindowID;
-    }
-
     let referrerInfo = gContextMenuContentData.referrerInfo;
     // If we want to change userContextId, we must be sure that we don't
     // propagate the referrer.
     if (("userContextId" in params &&
         params.userContextId != gContextMenuContentData.userContextId) ||
       this.onPlainTextLink) {
       referrerInfo = new ReferrerInfo(referrerInfo.referrerPolicy, false,
         referrerInfo.originalReferrer);
@@ -1261,34 +1222,34 @@ nsContextMenu.prototype = {
                            timer.TYPE_ONE_SHOT);
 
     // kick off the channel with our proxy object as the listener
     channel.asyncOpen(new saveAsListener(this.principal));
   },
 
   // Save URL of clicked-on link.
   saveLink() {
-    let isContentWindowPrivate = this.isRemote ? this.ownerDoc.isPrivate : undefined;
+    let isContentWindowPrivate = this.ownerDoc.isPrivate;
     this.saveHelper(this.linkURL, this.linkTextStr, null, true, this.ownerDoc,
                     gContextMenuContentData.documentURIObject,
                     this.frameOuterWindowID,
                     this.linkDownload,
                     isContentWindowPrivate);
   },
 
   // Backwards-compatibility wrapper
   saveImage() {
     if (this.onCanvas || this.onImage)
         this.saveMedia();
   },
 
   // Save URL of the clicked upon image, video, or audio.
   saveMedia() {
     let doc = this.ownerDoc;
-    let isContentWindowPrivate = this.isRemote ? this.ownerDoc.isPrivate : undefined;
+    let isContentWindowPrivate = this.ownerDoc.isPrivate;
     let referrerURI = gContextMenuContentData.documentURIObject;
     let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(this.browser);
     if (this.onCanvas) {
       // Bypass cache, since it's a data: URL.
       this._canvasToBlobURL(this.target).then(function(blobURL) {
         saveImageURL(blobURL, "canvas.png", "SaveImageTitle",
                      true, false, referrerURI, null, null, null,
                      isPrivate,
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -37,19 +37,21 @@ window._gBrowser = {
     if (Services.prefs.getBoolPref("browser.display.use_system_colors")) {
       this.tabpanels.style.backgroundColor = "-moz-default-background-color";
     } else if (Services.prefs.getIntPref("browser.display.document_color_use") == 2) {
       this.tabpanels.style.backgroundColor =
         Services.prefs.getCharPref("browser.display.background_color");
     }
 
     let messageManager = window.getGroupMessageManager("browsers");
+    window.messageManager.addMessageListener("contextmenu", this);
+
     if (gMultiProcessBrowser) {
       messageManager.addMessageListener("DOMTitleChanged", this);
-      window.messageManager.addMessageListener("contextmenu", this);
+      messageManager.addMessageListener("DOMWindowClose", this);
       messageManager.addMessageListener("Browser:Init", this);
     } else {
       this._outerWindowIDBrowserMap.set(this.selectedBrowser.outerWindowID,
         this.selectedBrowser);
     }
     messageManager.addMessageListener("RefreshBlocker:Blocked", this);
     messageManager.addMessageListener("Browser:WindowCreated", this);
 
--- a/toolkit/modules/PageMenu.jsm
+++ b/toolkit/modules/PageMenu.jsm
@@ -239,36 +239,18 @@ PageMenu.prototype = {
 };
 
 // This object is expected to be used from a parent process.
 function PageMenuParent() {
 }
 
 PageMenuParent.prototype = {
   __proto__: PageMenu.prototype,
-
   /*
-   * Given a target node and popup, add the context menu to the popup. This is
-   * intended to be called when a single process is used. This is equivalent to
-   * calling PageMenuChild.build and PageMenuParent.addToPopup in sequence.
-   *
-   * Returns true if custom menu items were present.
-   */
-  buildAndAddToPopup(aTarget, aPopup) {
-    let menuObject = this.maybeBuild(aTarget);
-    if (!menuObject) {
-      return false;
-    }
-
-    return this.buildAndAttachMenuWithObject(menuObject, null, aPopup);
-  },
-
-  /*
-   * Given a JSON menu object and popup, add the context menu to the popup. This
-   * is intended to be called when the child page is in a different process.
+   * Given a JSON menu object and popup, add the context menu to the popup.
    * aBrowser should be the browser containing the page the context menu is
    * displayed for, which may be null.
    *
    * Returns true if custom menu items were present.
    */
   addToPopup(aMenu, aBrowser, aPopup) {
     return this.buildAndAttachMenuWithObject(aMenu, aBrowser, aPopup);
   },