From 9f7b825989270305f487a972b19adf98ac65ca58 Mon Sep 17 00:00:00 2001 From: David Keeler Date: Tue, 6 Aug 2013 15:00:07 -0700 Subject: [PATCH] Bug 516753 - Forward content context menu events to the parent. r=felipc --- browser/base/content/content.js | 6 +- browser/base/content/nsContextMenu.js | 74 ++++++++++++++----- browser/base/content/tabbrowser.xml | 18 ++++- content/events/src/nsEventStateManager.cpp | 1 + .../xul/content/src/nsXULPopupListener.cpp | 11 ++- 5 files changed, 87 insertions(+), 23 deletions(-) diff --git a/browser/base/content/content.js b/browser/base/content/content.js index c95ff003099..ed8262cd56f 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -38,7 +38,11 @@ addMessageListener("Browser:HideSessionRestoreButton", function (message) { } }); -if (!Services.prefs.getBoolPref("browser.tabs.remote")) { +if (Services.prefs.getBoolPref("browser.tabs.remote")) { + addEventListener("contextmenu", function (event) { + sendAsyncMessage("contextmenu", {}, { event: event }); + }, false); +} else { addEventListener("DOMContentLoaded", function(event) { LoginManagerContent.onContentLoaded(event); }); diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 9838b011c30..2a332cd51bf 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -4,6 +4,8 @@ Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); +var gContextMenuContentData = null; + function nsContextMenu(aXulMenu, aIsShift) { this.shouldDisplay = true; this.initMenu(aXulMenu, aIsShift); @@ -39,6 +41,7 @@ nsContextMenu.prototype = { }, hiding: function CM_hiding() { + gContextMenuContentData = null; InlineSpellCheckerUI.clearSuggestionsFromMenu(); InlineSpellCheckerUI.clearDictionaryListFromMenu(); InlineSpellCheckerUI.uninit(); @@ -500,6 +503,15 @@ nsContextMenu.prototype = { // Set various context menu attributes based on the state of the world. setTarget: function (aNode, aRangeParent, aRangeOffset) { + // If gContextMenuContentData is not null, this event was forwarded from a + // child process, so use that information instead. + if (gContextMenuContentData) { + this.isRemote = true; + aNode = gContextMenuContentData.event.target; + } else { + this.isRemote = false; + } + const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; if (aNode.namespaceURI == xulNS || aNode.nodeType == Node.DOCUMENT_NODE || @@ -539,11 +551,17 @@ nsContextMenu.prototype = { // Remember the node that was clicked. this.target = aNode; - this.browser = this.target.ownerDocument.defaultView - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .chromeEventHandler; + // If this is a remote context menu event, use the information from + // gContextMenuContentData instead. + if (this.isRemote) { + this.browser = gContextMenuContentData.browser; + } else { + this.browser = this.target.ownerDocument.defaultView + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell) + .chromeEventHandler; + } this.onSocial = !!this.browser.getAttribute("origin"); // Check if we are in a synthetic document (stand alone image, video, etc.). @@ -772,10 +790,22 @@ nsContextMenu.prototype = { this.linkProtocol == "snews" ); }, + _unremotePrincipal: function(aRemotePrincipal) { + if (this.isRemote) { + return Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager) + .getAppCodebasePrincipal(aRemotePrincipal.URI, + aRemotePrincipal.appId, + aRemotePrincipal.isInBrowserElement); + } + + return aRemotePrincipal; + }, + // Open linked-to URL in a new window. openLink : function () { var doc = this.target.ownerDocument; - urlSecurityCheck(this.linkURL, doc.nodePrincipal); + urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal)); openLinkIn(this.linkURL, "window", { charset: doc.characterSet, referrerURI: doc.documentURIObject }); @@ -784,7 +814,7 @@ nsContextMenu.prototype = { // Open linked-to URL in a new private window. openLinkInPrivateWindow : function () { var doc = this.target.ownerDocument; - urlSecurityCheck(this.linkURL, doc.nodePrincipal); + urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal)); openLinkIn(this.linkURL, "window", { charset: doc.characterSet, referrerURI: doc.documentURIObject, @@ -794,7 +824,7 @@ nsContextMenu.prototype = { // Open linked-to URL in a new tab. openLinkInTab: function() { var doc = this.target.ownerDocument; - urlSecurityCheck(this.linkURL, doc.nodePrincipal); + urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal)); openLinkIn(this.linkURL, "tab", { charset: doc.characterSet, referrerURI: doc.documentURIObject }); @@ -803,7 +833,7 @@ nsContextMenu.prototype = { // open URL in current tab openLinkInCurrent: function() { var doc = this.target.ownerDocument; - urlSecurityCheck(this.linkURL, doc.nodePrincipal); + urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal)); openLinkIn(this.linkURL, "current", { charset: doc.characterSet, referrerURI: doc.documentURIObject }); @@ -839,7 +869,8 @@ nsContextMenu.prototype = { var doc = this.target.ownerDocument; var frameURL = doc.location.href; - urlSecurityCheck(frameURL, this.browser.contentPrincipal, + urlSecurityCheck(frameURL, + this._unremotePrincipal(this.browser.contentPrincipal), Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT); var referrer = doc.referrer; openUILinkIn(frameURL, "current", { disallowInheritPrincipal: true, @@ -902,7 +933,8 @@ nsContextMenu.prototype = { viewImageDesc: function(e) { var doc = this.target.ownerDocument; - urlSecurityCheck(this.imageDescURL, this.browser.contentPrincipal, + urlSecurityCheck(this.imageDescURL, + this._unremotePrincipal(this.browser.contentPrincipal), Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT); openUILink(this.imageDescURL, e, { disallowInheritPrincipal: true, referrerURI: doc.documentURIObject }); @@ -914,7 +946,7 @@ nsContextMenu.prototype = { reloadImage: function(e) { urlSecurityCheck(this.mediaURL, - this.browser.contentPrincipal, + this._unremotePrincipal(this.browser.contentPrincipal), Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT); if (this.target instanceof Ci.nsIImageLoadingContent) @@ -930,7 +962,7 @@ nsContextMenu.prototype = { else { viewURL = this.mediaURL; urlSecurityCheck(viewURL, - this.browser.contentPrincipal, + this._unremotePrincipal(this.browser.contentPrincipal), Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT); } @@ -940,7 +972,8 @@ nsContextMenu.prototype = { }, saveVideoFrameAsImage: function () { - urlSecurityCheck(this.mediaURL, this.browser.contentPrincipal, + urlSecurityCheck(this.mediaURL, + this._unremotePrincipal(this.browser.contentPrincipal), Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT); let name = ""; try { @@ -973,7 +1006,7 @@ nsContextMenu.prototype = { // Change current window to the URL of the background image. viewBGImage: function(e) { urlSecurityCheck(this.bgImageURL, - this.browser.contentPrincipal, + this._unremotePrincipal(this.browser.contentPrincipal), Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT); var doc = this.target.ownerDocument; openUILink(this.bgImageURL, e, { disallowInheritPrincipal: true, @@ -1007,8 +1040,9 @@ nsContextMenu.prototype = { if (this.disableSetDesktopBackground()) return; + var doc = this.target.ownerDocument; urlSecurityCheck(this.target.currentURI.spec, - this.target.ownerDocument.nodePrincipal); + this._unremotePrincipal(doc.nodePrincipal)); // Confirm since it's annoying if you hit this accidentally. const kDesktopBackgroundURL = @@ -1185,7 +1219,7 @@ nsContextMenu.prototype = { linkText = document.commandDispatcher.focusedWindow.getSelection().toString().trim(); else linkText = this.linkText(); - urlSecurityCheck(this.linkURL, doc.nodePrincipal); + urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal)); this.saveHelper(this.linkURL, linkText, null, true, doc); }, @@ -1205,12 +1239,14 @@ nsContextMenu.prototype = { true, false, doc.documentURIObject, doc); } else if (this.onImage) { - urlSecurityCheck(this.mediaURL, doc.nodePrincipal); + urlSecurityCheck(this.mediaURL, + this._unremotePrincipal(doc.nodePrincipal)); saveImageURL(this.mediaURL, null, "SaveImageTitle", false, false, doc.documentURIObject, doc); } else if (this.onVideo || this.onAudio) { - urlSecurityCheck(this.mediaURL, doc.nodePrincipal); + urlSecurityCheck(this.mediaURL, + this._unremotePrincipal(doc.nodePrincipal)); var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle"; this.saveHelper(this.mediaURL, null, dialogTitle, false, doc); } diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index abe689cc58b..624b1d70b3e 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -2908,6 +2908,16 @@ let titleChanged = this.setTabTitle(tab); if (titleChanged && !tab.selected && !tab.hasAttribute("busy")) tab.setAttribute("titlechanged", "true"); + break; + case "contextmenu": + gContextMenuContentData = { event: aMessage.objects.event, + browser: browser }; + let popup = browser.ownerDocument.getElementById("contentAreaContextMenu"); + popup.openPopup(browser, "overlap", + gContextMenuContentData.event.clientX, + gContextMenuContentData.event.clientY, + true, false, null); + break; } ]]> @@ -2957,8 +2967,10 @@ "-moz-default-background-color" : Services.prefs.getCharPref("browser.display.background_color"); - if (Services.prefs.getBoolPref("browser.tabs.remote")) + if (Services.prefs.getBoolPref("browser.tabs.remote")) { messageManager.addMessageListener("DOMTitleChanged", this); + messageManager.addMessageListener("contextmenu", this); + } ]]> @@ -2993,8 +3005,10 @@ document.removeEventListener("keypress", this, false); window.removeEventListener("sizemodechange", this, false); - if (Services.prefs.getBoolPref("browser.tabs.remote")) + if (Services.prefs.getBoolPref("browser.tabs.remote")) { messageManager.removeMessageListener("DOMTitleChanged", this); + messageManager.removeMessageListener("contextmenu", this); + } ]]> diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index e4cf4fce269..174a7532621 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -1585,6 +1585,7 @@ CrossProcessSafeEvent(const nsEvent& aEvent) case NS_MOUSE_BUTTON_DOWN: case NS_MOUSE_BUTTON_UP: case NS_MOUSE_MOVE: + case NS_CONTEXTMENU: return true; default: return false; diff --git a/content/xul/content/src/nsXULPopupListener.cpp b/content/xul/content/src/nsXULPopupListener.cpp index 5368d5ed765..cbe0e1f053b 100644 --- a/content/xul/content/src/nsXULPopupListener.cpp +++ b/content/xul/content/src/nsXULPopupListener.cpp @@ -136,6 +136,16 @@ nsXULPopupListener::HandleEvent(nsIDOMEvent* aEvent) } } + nsCOMPtr targetContent = do_QueryInterface(target); + if (!targetContent) { + return NS_OK; + } + if (targetContent->Tag() == nsGkAtoms::browser && + targetContent->IsXUL() && + nsEventStateManager::IsRemoteTarget(targetContent)) { + return NS_OK; + } + bool preventDefault; mouseEvent->GetDefaultPrevented(&preventDefault); if (preventDefault && targetNode && mIsContext) { @@ -180,7 +190,6 @@ nsXULPopupListener::HandleEvent(nsIDOMEvent* aEvent) // If a menu item child was clicked on that leads to a popup needing // to show, we know (guaranteed) that we're dealing with a menu or // submenu of an already-showing popup. We don't need to do anything at all. - nsCOMPtr targetContent = do_QueryInterface(target); if (!mIsContext) { nsIAtom *tag = targetContent ? targetContent->Tag() : nullptr; if (tag == nsGkAtoms::menu || tag == nsGkAtoms::menuitem)