Bug 1260102 - Pass isHandlingUserInput through process boundary for content menu command. r=mrbkap
MozReview-Commit-ID: FMQOFpeO6yn
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -11,16 +11,18 @@ var {classes: Cc, interfaces: Ci, utils:
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/ContentWebRTC.jsm");
Cu.import("resource:///modules/ContentObservers.jsm");
Cu.import("resource://gre/modules/InlineSpellChecker.jsm");
Cu.import("resource://gre/modules/InlineSpellCheckerContent.jsm");
Cu.import("resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
+ "resource:///modules/E10SUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
"resource://gre/modules/BrowserUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContentLinkHandler",
"resource:///modules/ContentLinkHandler.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
"resource://gre/modules/LoginManagerContent.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "InsecurePasswordUtils",
"resource://gre/modules/InsecurePasswordUtils.jsm");
@@ -44,17 +46,19 @@ XPCOMUtils.defineLazyModuleGetter(this,
// TabChildGlobal
var global = this;
// Load the form validation popup handler
var formSubmitObserver = new FormSubmitObserver(content, this);
addMessageListener("ContextMenu:DoCustomCommand", function(message) {
- PageMenuChild.executeMenu(message.data);
+ E10SUtils.wrapHandlingUserInput(
+ content, message.data.handlingUserInput,
+ () => PageMenuChild.executeMenu(message.data.generatedItemId));
});
addMessageListener("RemoteLogins:fillForm", function(message) {
LoginManagerContent.receiveMessage(message, content);
});
addEventListener("DOMFormHasPassword", function(event) {
LoginManagerContent.onDOMFormHasPassword(event, content);
InsecurePasswordUtils.checkForInsecurePasswords(event.target);
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -63,25 +63,20 @@ addMessageListener("Browser:Reload", fun
try {
let sh = webNav.sessionHistory;
if (sh)
webNav = sh.QueryInterface(Ci.nsIWebNavigation);
} catch (e) {
}
let reloadFlags = message.data.flags;
- let handlingUserInput;
try {
- handlingUserInput = content.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils)
- .setHandlingUserInput(message.data.handlingUserInput);
- webNav.reload(reloadFlags);
+ E10SUtils.wrapHandlingUserInput(content, message.data.handlingUserInput,
+ () => webNav.reload(reloadFlags));
} catch (e) {
- } finally {
- handlingUserInput.destruct();
}
});
addMessageListener("MixedContent:ReenableProtection", function() {
docShell.mixedContentChannel = null;
});
addMessageListener("SecondScreen:tab-mirror", function(message) {
--- a/browser/modules/E10SUtils.jsm
+++ b/browser/modules/E10SUtils.jsm
@@ -97,9 +97,21 @@ this.E10SUtils = {
uri: aURI.spec,
flags: Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
referrer: aReferrer ? aReferrer.spec : null,
},
historyIndex: sessionHistory.requestedIndex,
});
return false;
},
+
+ wrapHandlingUserInput: function(aWindow, aIsHandling, aCallback) {
+ var handlingUserInput;
+ try {
+ handlingUserInput = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .setHandlingUserInput(aIsHandling);
+ aCallback();
+ } finally {
+ handlingUserInput.destruct();
+ }
+ },
};
--- a/dom/html/test/browser.ini
+++ b/dom/html/test/browser.ini
@@ -1,23 +1,25 @@
[DEFAULT]
support-files =
bug592641_img.jpg
dummy_page.html
file_bug649778.html
file_bug649778.html^headers^
file_fullscreen-api-keys.html
+ file_content_contextmenu.html
head.js
[browser_bug592641.js]
[browser_bug649778.js]
skip-if = e10s # Bug ?????? - leaked until shutdown [nsGlobalWindow #16 about:blank]
[browser_bug1081537.js]
[browser_bug1108547.js]
support-files =
file_bug1108547-1.html
file_bug1108547-2.html
file_bug1108547-3.html
[browser_DOMDocElementInserted.js]
[browser_fullscreen-api-keys.js]
tags = fullscreen
[browser_fullscreen-contextmenu-esc.js]
tags = fullscreen
+[browser_content_contextmenu_userinput.js]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/browser_content_contextmenu_userinput.js
@@ -0,0 +1,61 @@
+"use strict";
+
+function frameScript() {
+ let Ci = Components.interfaces;
+ let windowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ let menuitem = content.document.getElementById("menuitem");
+ menuitem.addEventListener("click", function() {
+ sendAsyncMessage("Test:ContextMenuClick", windowUtils.isHandlingUserInput);
+ });
+}
+
+var gMessageManager;
+
+function listenOneMessage(aMsg, aListener) {
+ function listener({ data }) {
+ gMessageManager.removeMessageListener(aMsg, listener);
+ aListener(data);
+ }
+ gMessageManager.addMessageListener(aMsg, listener);
+}
+
+function promiseOneMessage(aMsg) {
+ return new Promise(resolve => listenOneMessage(aMsg, resolve));
+}
+
+const kPage = "http://example.org/browser/" +
+ "dom/html/test/file_content_contextmenu.html";
+
+add_task(function* () {
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: kPage
+ }, function*(aBrowser) {
+ gMessageManager = aBrowser.messageManager;
+ ContentTask.spawn(aBrowser, null, frameScript);
+
+ let contextMenu = document.getElementById("contentAreaContextMenu");
+ ok(contextMenu, "Got context menu");
+
+ info("Open context menu");
+ is(contextMenu.state, "closed", "Should not have opened context menu");
+ let popupShownPromise = promiseWaitForEvent(window, "popupshown");
+ EventUtils.synthesizeMouse(aBrowser, window.innerWidth / 3,
+ window.innerHeight / 3,
+ {type: "contextmenu", button: 2}, window);
+ yield popupShownPromise;
+ is(contextMenu.state, "open", "Should have opened context menu");
+
+ let pageMenuSep = document.getElementById("page-menu-separator");
+ ok(pageMenuSep && !pageMenuSep.hidden,
+ "Page menu separator should be shown");
+ let testMenuItem = pageMenuSep.previousSibling;
+ is(testMenuItem.label, "Test Context Menu Click", "Got context menu item");
+
+ let promiseContextMenuClick = promiseOneMessage("Test:ContextMenuClick");
+ EventUtils.synthesizeMouseAtCenter(testMenuItem, {}, window);
+ let isUserInput = yield promiseContextMenuClick;
+ ok(isUserInput, "Content menu click should be a user input");
+ });
+});
new file mode 100644
--- /dev/null
+++ b/dom/html/test/file_content_contextmenu.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title></title>
+ <style>
+ body {
+ margin: 0;
+ width: 100vw;
+ height: 100vh;
+ }
+ </style>
+</head>
+<body contextmenu="testmenu">
+ <menu type="context" id="testmenu">
+ <menuitem label="Test Context Menu Click" id="menuitem">
+ </menu>
+</body>
+</html>
--- a/toolkit/modules/PageMenu.jsm
+++ b/toolkit/modules/PageMenu.jsm
@@ -1,14 +1,16 @@
/* 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/. */
this.EXPORTED_SYMBOLS = ["PageMenuParent", "PageMenuChild"];
+var {interfaces: Ci} = Components;
+
this.PageMenu = function PageMenu() {
}
PageMenu.prototype = {
PAGEMENU_ATTR: "pagemenu",
GENERATEDITEMID_ATTR: "generateditemid",
_popup: null,
@@ -157,18 +159,23 @@ PageMenu.prototype = {
if (type == "command" && target.hasAttribute(this.GENERATEDITEMID_ATTR)) {
// If a builder is assigned, call click on it directly. Otherwise, this is
// likely a menu with data from another process, so send a message to the
// browser to execute the menuitem.
if (this._builder) {
this._builder.click(target.getAttribute(this.GENERATEDITEMID_ATTR));
}
else if (this._browser) {
- this._browser.messageManager.sendAsyncMessage("ContextMenu:DoCustomCommand",
- target.getAttribute(this.GENERATEDITEMID_ATTR));
+ let win = target.ownerDocument.defaultView;
+ let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ this._browser.messageManager.sendAsyncMessage("ContextMenu:DoCustomCommand", {
+ generatedItemId: target.getAttribute(this.GENERATEDITEMID_ATTR),
+ handlingUserInput: windowUtils.isHandlingUserInput
+ });
}
} else if (type == "popuphidden" && this._popup == target) {
this.removeGeneratedContent(this._popup);
this._popup.removeEventListener("popuphidden", this);
this._popup.removeEventListener("command", this);
this._popup = null;