From 086302552c065595c8512c485728b2f53fc9fa05 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Thu, 8 Aug 2013 20:16:47 -0400 Subject: [PATCH] Bug 897062 - Handle special clicks in e10s. r=felipe,smaug --- browser/base/content/browser.js | 9 ++- browser/base/content/content.js | 94 +++++++++++++++++++++++++++++ browser/components/nsBrowserGlue.js | 6 ++ browser/modules/ContentClick.jsm | 82 +++++++++++++++++++++++++ browser/modules/moz.build | 1 + dom/ipc/TabChild.h | 8 +++ 6 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 browser/modules/ContentClick.jsm diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index a5ca5419331f..6962b85e1a64 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -732,9 +732,12 @@ var gBrowserInit = { var mustLoadSidebar = false; - Cc["@mozilla.org/eventlistenerservice;1"] - .getService(Ci.nsIEventListenerService) - .addSystemEventListener(gBrowser, "click", contentAreaClick, true); + if (!gMultiProcessBrowser) { + // There is a Content:Click message manually sent from content. + Cc["@mozilla.org/eventlistenerservice;1"] + .getService(Ci.nsIEventListenerService) + .addSystemEventListener(gBrowser, "click", contentAreaClick, true); + } gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false); diff --git a/browser/base/content/content.js b/browser/base/content/content.js index ed8262cd56f7..e5bc1481a5eb 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -195,3 +195,97 @@ let AboutHomeListener = { } }; AboutHomeListener.init(); + +var global = this; + +let ClickEventHandler = { + init: function init() { + Cc["@mozilla.org/eventlistenerservice;1"] + .getService(Ci.nsIEventListenerService) + .addSystemEventListener(global, "click", this, true); + }, + + handleEvent: function(event) { + // Bug 903016: Most of this code is an unfortunate duplication from + // contentAreaClick in browser.js. + if (!event.isTrusted || event.defaultPrevented || event.button == 2) + return; + + let [href, node] = this._hrefAndLinkNodeForClickEvent(event); + + let json = { button: event.button, shiftKey: event.shiftKey, + ctrlKey: event.ctrlKey, metaKey: event.metaKey, + altKey: event.altKey, href: null, title: null, + bookmark: false }; + + if (href) { + json.href = href; + if (node) { + json.title = node.getAttribute("title"); + + if (event.button == 0 && !event.ctrlKey && !event.shiftKey && + !event.altKey && !event.metaKey) { + json.bookmark = node.getAttribute("rel") == "sidebar"; + if (json.bookmark) + event.preventDefault(); // Need to prevent the pageload. + } + } + + sendAsyncMessage("Content:Click", json); + return; + } + + // This might be middle mouse navigation. + if (event.button == 1) + sendAsyncMessage("Content:Click", json); + }, + + /** + * Extracts linkNode and href for the current click target. + * + * @param event + * The click event. + * @return [href, linkNode]. + * + * @note linkNode will be null if the click wasn't on an anchor + * element (or XLink). + */ + _hrefAndLinkNodeForClickEvent: function(event) { + function isHTMLLink(aNode) { + // Be consistent with what nsContextMenu.js does. + return ((aNode instanceof content.HTMLAnchorElement && aNode.href) || + (aNode instanceof content.HTMLAreaElement && aNode.href) || + aNode instanceof content.HTMLLinkElement); + } + + function makeURLAbsolute(aBase, aUrl) { + // Note: makeURI() will throw if aUri is not a valid URI + return makeURI(aUrl, null, makeURI(aBase)).spec; + } + + let node = event.target; + while (node && !isHTMLLink(node)) { + node = node.parentNode; + } + + if (node) + return [node.href, node]; + + // If there is no linkNode, try simple XLink. + let href, baseURI; + node = event.target; + while (node && !href) { + if (node.nodeType == content.Node.ELEMENT_NODE) { + href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href"); + if (href) + baseURI = node.baseURI; + } + node = node.parentNode; + } + + // In case of XLink, we don't return the node we got href from since + // callers expect -like elements. + return [href ? makeURLAbsolute(baseURI, href) : null, null]; + } +}; +ClickEventHandler.init(); diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 0977442d621b..02c99b54cc4c 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -20,6 +20,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "AboutHome", XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "ContentClick", + "resource:///modules/ContentClick.jsm"); + XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); @@ -467,6 +470,9 @@ BrowserGlue.prototype = { webrtcUI.init(); AboutHome.init(); + if (Services.prefs.getBoolPref("browser.tabs.remote")) + ContentClick.init(); + Services.obs.notifyObservers(null, "browser-ui-startup-complete", ""); }, diff --git a/browser/modules/ContentClick.jsm b/browser/modules/ContentClick.jsm new file mode 100644 index 000000000000..d09affd8f5b9 --- /dev/null +++ b/browser/modules/ContentClick.jsm @@ -0,0 +1,82 @@ +/* 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/. */ + +"use strict"; + +let Cc = Components.classes; +let Ci = Components.interfaces; +let Cu = Components.utils; + +this.EXPORTED_SYMBOLS = [ "ContentClick" ]; + +Cu.import("resource:///modules/PlacesUIUtils.jsm"); +Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +let ContentClick = { + init: function() { + let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager); + mm.addMessageListener("Content:Click", this); + }, + + receiveMessage: function (message) { + switch (message.name) { + case "Content:Click": + this.contentAreaClick(message.json, message.target) + break; + } + }, + + contentAreaClick: function (json, browser) { + // This is heavily based on contentAreaClick from browser.js (Bug 903016) + // The json is set up in a way to look like an Event. + let window = browser.ownerDocument.defaultView; + + if (!json.href) { + // Might be middle mouse navigation. + if (Services.prefs.getBoolPref("middlemouse.contentLoadURL") && + !Services.prefs.getBoolPref("general.autoScroll")) { + window.middleMousePaste(json); + } + return; + } + + if (json.bookmark) { + // This is the Opera convention for a special link that, when clicked, + // allows to add a sidebar panel. The link's title attribute contains + // the title that should be used for the sidebar panel. + PlacesUIUtils.showBookmarkDialog({ action: "add" + , type: "bookmark" + , uri: Services.io.newURI(json.href, null, null) + , title: json.title + , loadBookmarkInSidebar: true + , hiddenRows: [ "description" + , "location" + , "keyword" ] + }, window); + return; + } + + // Note: We don't need the sidebar code here. + + // This part is based on handleLinkClick. + var where = window.whereToOpenLink(json); + if (where == "current") + return false; + + // Todo(903022): code for where == save + + window.openLinkIn(json.href, where, { referrerURI: browser.documentURI, + charset: browser.characterSet }); + + // Mark the page as a user followed link. This is done so that history can + // distinguish automatic embed visits from user activated ones. For example + // pages loaded in frames are embed visits and lost with the session, while + // visits across frames should be preserved. + try { + if (!PrivateBrowsingUtils.isWindowPrivate(window)) + PlacesUIUtils.markPageAsFollowedLink(href); + } catch (ex) { /* Skip invalid URIs. */ } + } +}; diff --git a/browser/modules/moz.build b/browser/modules/moz.build index 3118f216bdfb..8b723537b243 100644 --- a/browser/modules/moz.build +++ b/browser/modules/moz.build @@ -8,6 +8,7 @@ TEST_DIRS += ['test'] EXTRA_JS_MODULES += [ 'BrowserNewTabPreloader.jsm', + 'ContentClick.jsm', 'NetworkPrioritizer.jsm', 'SharedFrame.jsm', 'SignInToWebsite.jsm', diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index e982da01546f..bd4f498187d8 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -16,6 +16,7 @@ #include "nsIWebNavigation.h" #include "nsCOMPtr.h" #include "nsAutoPtr.h" +#include "nsEventDispatcher.h" #include "nsIWebBrowserChrome2.h" #include "nsIEmbeddingSiteWindow.h" #include "nsIWebBrowserChromeFocus.h" @@ -119,6 +120,13 @@ public: optional_argc); } + nsresult + PreHandleEvent(nsEventChainPreVisitor& aVisitor) + { + aVisitor.mForceContentDispatch = true; + return NS_OK; + } + virtual JSContext* GetJSContextForEventHandlers() MOZ_OVERRIDE; virtual nsIPrincipal* GetPrincipal() MOZ_OVERRIDE;