Bug 917681 - [e10s] Make context menu work for selected text (r=felipe)
authorBill McCloskey <wmccloskey@mozilla.com>
Fri, 14 Mar 2014 11:47:46 -0700
changeset 190888 7d192442dfca1bdb9cdb07f5f6380c1710ef4360
parent 190887 cc298e4b0f470d1ed70cd92e874b761c58b60fe8
child 190889 f9790cf27463c72f73eb359b84aaeb5c2f902c09
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfelipe
bugs917681
milestone30.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 917681 - [e10s] Make context menu work for selected text (r=felipe)
browser/base/content/browser.js
browser/base/content/nsContextMenu.js
toolkit/content/widgets/findbar.xml
toolkit/modules/BrowserUtils.jsm
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6,16 +6,18 @@
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/NotificationDB.jsm");
 Cu.import("resource:///modules/RecentWindow.jsm");
 Cu.import("resource://gre/modules/WindowsPrefSync.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
+                                  "resource://gre/modules/BrowserUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
                                   "resource://gre/modules/CharsetMenu.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
                                   "resource://gre/modules/ShortcutUtils.jsm");
 
 const nsIWebNavigation = Ci.nsIWebNavigation;
@@ -4835,23 +4837,21 @@ function UpdateDynamicShortcutTooltipTex
  *
  * @param aCharLen
  *        The maximum number of characters to return.
  */
 function getBrowserSelection(aCharLen) {
   // selections of more than 150 characters aren't useful
   const kMaxSelectionLen = 150;
   const charLen = Math.min(aCharLen || kMaxSelectionLen, kMaxSelectionLen);
-  let commandDispatcher = document.commandDispatcher;
-
-  var focusedWindow = commandDispatcher.focusedWindow;
+
+  let [element, focusedWindow] = BrowserUtils.getFocusSync(document);
   var selection = focusedWindow.getSelection().toString();
   // try getting a selected text in text input.
   if (!selection) {
-    let element = commandDispatcher.focusedElement;
     var isOnTextInput = function isOnTextInput(elem) {
       // we avoid to return a value if a selection is in password field.
       // ref. bug 565717
       return elem instanceof HTMLTextAreaElement ||
              (elem instanceof HTMLInputElement && elem.mozIsTextField(true));
     };
 
     if (isOnTextInput(element)) {
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -75,18 +75,17 @@ nsContextMenu.prototype = {
                           mailtoHandler.preferredAction == Ci.nsIHandlerInfo.useHelperApp &&
                           (mailtoHandler.preferredApplicationHandler instanceof Ci.nsIWebHandlerApp));
     }
 
     // Time to do some bad things and see if we've highlighted a URL that
     // isn't actually linked.
     if (this.isTextSelected && !this.onLink) {
       // Ok, we have some text, let's figure out if it looks like a URL.
-      let selection =  document.commandDispatcher.focusedWindow
-                               .getSelection();
+      let selection =  this.focusedWindow.getSelection();
       let linkText = selection.toString().trim();
       let uri;
       if (/^(?:https?|ftp):/i.test(linkText)) {
         try {
           uri = makeURI(linkText);
         } catch (ex) {}
       }
       // Check if this could be a valid url, just missing the protocol.
@@ -544,16 +543,20 @@ nsContextMenu.prototype = {
     this.onCTPPlugin       = false;
     this.canSpellCheck     = false;
     this.textSelected      = getBrowserSelection();
     this.isTextSelected    = this.textSelected.length != 0;
 
     // Remember the node that was clicked.
     this.target = aNode;
 
+    let [elt, win] = BrowserUtils.getFocusSync(document);
+    this.focusedWindow = win;
+    this.focusedElement = elt;
+
     // 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)
@@ -1247,17 +1250,17 @@ nsContextMenu.prototype = {
   },
 
   // Save URL of clicked-on link.
   saveLink: function() {
     var doc =  this.target.ownerDocument;
     var linkText;
     // If selected text is found to match valid URL pattern.
     if (this.onPlainTextLink)
-      linkText = document.commandDispatcher.focusedWindow.getSelection().toString().trim();
+      linkText = this.focusedWindow.getSelection().toString().trim();
     else
       linkText = this.linkText();
     urlSecurityCheck(this.linkURL, this._unremotePrincipal(doc.nodePrincipal));
 
     this.saveHelper(this.linkURL, linkText, null, true, doc);
   },
 
   // Backwards-compatibility wrapper
@@ -1444,17 +1447,17 @@ nsContextMenu.prototype = {
       }
     }
 
     return text;
   },
 
   // Returns true if anything is selected.
   isContentSelection: function() {
-    return !document.commandDispatcher.focusedWindow.getSelection().isCollapsed;
+    return !this.focusedWindow.getSelection().isCollapsed;
   },
 
   toString: function () {
     return "contextMenu.target     = " + this.target + "\n" +
            "contextMenu.onImage    = " + this.onImage + "\n" +
            "contextMenu.onLink     = " + this.onLink + "\n" +
            "contextMenu.link       = " + this.link + "\n" +
            "contextMenu.inFrame    = " + this.inFrame + "\n" +
@@ -1546,17 +1549,17 @@ nsContextMenu.prototype = {
   bookmarkThisPage: function CM_bookmarkThisPage() {
     window.top.PlacesCommandHook.bookmarkPage(this.browser, PlacesUtils.bookmarksMenuFolderId, true);
   },
 
   bookmarkLink: function CM_bookmarkLink() {
     var linkText;
     // If selected text is found to match valid URL pattern.
     if (this.onPlainTextLink)
-      linkText = document.commandDispatcher.focusedWindow.getSelection().toString().trim();
+      linkText = this.focusedWindow.getSelection().toString().trim();
     else
       linkText = this.linkText();
     window.top.PlacesCommandHook.bookmarkLink(PlacesUtils.bookmarksMenuFolderId, this.linkURL,
                                               linkText);
   },
 
   addBookmarkForFrame: function CM_addBookmarkForFrame() {
     var doc = this.target.ownerDocument;
--- a/toolkit/content/widgets/findbar.xml
+++ b/toolkit/content/widgets/findbar.xml
@@ -671,27 +671,18 @@
         -->
       <method name="_shouldFastFind">
         <parameter name="aEvent"/>
         <body><![CDATA[
           if (aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey ||
               aEvent.defaultPrevented)
             return false;
 
-          let elt = document.commandDispatcher.focusedElement;
-          let win = document.commandDispatcher.focusedWindow;
-
-          // Temporary fix for e10s.
-          const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-          if (elt instanceof XULElement &&
-              elt.localName == "browser" &&
-              elt.namespaceURI == XUL_NS &&
-              elt.getAttribute("remote")) {
-            [elt, win] = elt.syncHandler.getFocusedElementAndWindow();
-          }
+          let {BrowserUtils} = Components.utils.import("resource://gre/modules/BrowserUtils.jsm", {});
+          let [elt, win] = BrowserUtils.getFocusSync(document);
 
           if (elt) {
             if (elt instanceof HTMLInputElement && elt.mozIsTextField(false))
               return false;
 
             if (elt instanceof HTMLTextAreaElement ||
                 elt instanceof HTMLSelectElement ||
                 elt instanceof HTMLObjectElement ||
--- a/toolkit/modules/BrowserUtils.jsm
+++ b/toolkit/modules/BrowserUtils.jsm
@@ -1,17 +1,17 @@
 /* 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";
 
 this.EXPORTED_SYMBOLS = [ "BrowserUtils" ];
 
-const {interfaces: Ci, utils: Cu} = Components;
+const {interfaces: Ci, utils: Cu, classes: Cc} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 this.BrowserUtils = {
 
   /**
    * urlSecurityCheck: JavaScript wrapper for checkLoadURIWithPrincipal
    * and checkLoadURIStrWithPrincipal.
@@ -60,16 +60,41 @@ this.BrowserUtils = {
     return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
   },
 
   makeFileURI: function(aFile) {
     return Services.io.newFileURI(aFile);
   },
 
   /**
+   * Return the current focus element and window. If the current focus
+   * is in a content process, then this function returns CPOWs
+   * (cross-process object wrappers) that refer to the focused
+   * items. Note that calling this function synchronously contacts the
+   * content process, which may block for a long time.
+   *
+   * @param document The document in question.
+   * @return [focusedElement, focusedWindow]
+   */
+  getFocusSync: function(document) {
+    let elt = document.commandDispatcher.focusedElement;
+    var window = document.commandDispatcher.focusedWindow;
+
+    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+    if (elt instanceof window.XULElement &&
+        elt.localName == "browser" &&
+        elt.namespaceURI == XUL_NS &&
+        elt.getAttribute("remote")) {
+      [elt, window] = elt.syncHandler.getFocusedElementAndWindow();
+    }
+
+    return [elt, window];
+  },
+
+  /**
    * For a given DOM element, returns its position in "screen"
    * coordinates. In a content process, the coordinates returned will
    * be relative to the left/top of the tab. In the chrome process,
    * the coordinates are relative to the user's screen.
    */
   getElementBoundingScreenRect: function(aElement) {
     let rect = aElement.getBoundingClientRect();
     let window = aElement.ownerDocument.defaultView;