Bug 1381983 - [1.1] Add context menu event handler to the content delegate. r=snorp
authorEugen Sawin <esawin@mozilla.com>
Tue, 18 Jul 2017 22:18:56 +0200
changeset 419577 60f7e2cb890c80d38cb254aa8e7a08caf079e46d
parent 419576 961e6b6a79a211e254102f9529741f23a36608d1
child 419578 21a44de78be3996c6a8599d086888751666afa75
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1381983
milestone56.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 1381983 - [1.1] Add context menu event handler to the content delegate. r=snorp
mobile/android/chrome/geckoview/GeckoViewContent.js
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
mobile/android/modules/geckoview/GeckoViewContent.jsm
--- a/mobile/android/chrome/geckoview/GeckoViewContent.js
+++ b/mobile/android/chrome/geckoview/GeckoViewContent.js
@@ -17,31 +17,33 @@ class GeckoViewContent extends GeckoView
   register() {
     debug("register");
 
     addEventListener("DOMTitleChanged", this, false);
     addEventListener("MozDOMFullscreen:Entered", this, false);
     addEventListener("MozDOMFullscreen:Exit", this, false);
     addEventListener("MozDOMFullscreen:Exited", this, false);
     addEventListener("MozDOMFullscreen:Request", this, false);
+    addEventListener("contextmenu", this, { capture: true, passive: false });
 
     this.messageManager.addMessageListener("GeckoView:DOMFullscreenEntered",
                                            this);
     this.messageManager.addMessageListener("GeckoView:DOMFullscreenExited",
                                            this);
   }
 
   unregister() {
     debug("unregister");
 
     removeEventListener("DOMTitleChanged", this);
     removeEventListener("MozDOMFullscreen:Entered", this);
     removeEventListener("MozDOMFullscreen:Exit", this);
     removeEventListener("MozDOMFullscreen:Exited", this);
     removeEventListener("MozDOMFullscreen:Request", this);
+    removeEventListener("contextmenu", this);
 
     this.messageManager.removeMessageListener("GeckoView:DOMFullscreenEntered",
                                               this);
     this.messageManager.removeMessageListener("GeckoView:DOMFullscreenExited",
                                               this);
   }
 
   receiveMessage(aMsg) {
@@ -61,23 +63,42 @@ class GeckoViewContent extends GeckoView
                  .getInterface(Ci.nsIDOMWindowUtils)
                  .exitFullscreen();
         }
         break;
     }
   }
 
   handleEvent(aEvent) {
-    if (aEvent.originalTarget.defaultView != content) {
-      return;
-    }
-
     debug("handleEvent " + aEvent.type);
 
     switch (aEvent.type) {
+      case "contextmenu":
+        function nearestParentHref(node) {
+          while (node && !node.href) {
+            node = node.parentNode;
+          }
+          return node && node.href;
+        }
+
+        let node = aEvent.target;
+        let hrefNode = nearestParentHref(node);
+        let isImageNode = node instanceof Ci.nsIDOMHTMLImageElement;
+        let msg = {
+          screenX: aEvent.screenX,
+          screenY: aEvent.screenY,
+          uri: hrefNode,
+          imageSrc: isImageNode ? node.src : null
+        };
+
+        if (hrefNode || isImageNode) {
+          sendAsyncMessage("GeckoView:ContextMenu", msg);
+          aEvent.preventDefault();
+        }
+        break;
       case "MozDOMFullscreen:Request":
         sendAsyncMessage("GeckoView:DOMFullscreenRequest");
         break;
       case "MozDOMFullscreen:Entered":
       case "MozDOMFullscreen:Exited":
         // Content may change fullscreen state by itself, and we should ensure
         // that the parent always exits fullscreen when content has left
         // full screen mode.
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -91,36 +91,42 @@ public class GeckoView extends LayerView
 
     private final EventDispatcher mEventDispatcher =
         new EventDispatcher(mNativeQueue);
 
     private final GeckoViewHandler<ContentListener> mContentHandler =
         new GeckoViewHandler<ContentListener>(
             "GeckoViewContent", this,
             new String[]{
+                "GeckoView:ContextMenu",
                 "GeckoView:DOMTitleChanged",
                 "GeckoView:FullScreenEnter",
                 "GeckoView:FullScreenExit"
             }
         ) {
             @Override
             public void handleMessage(final ContentListener listener,
                                       final String event,
                                       final GeckoBundle message,
                                       final EventCallback callback) {
 
-                if ("GeckoView:DOMTitleChanged".equals(event)) {
+                if ("GeckoView:ContextMenu".equals(event)) {
+                    listener.onContextMenu(GeckoView.this,
+                                           message.getInt("screenX"),
+                                           message.getInt("screenY"),
+                                           message.getString("uri"),
+                                           message.getString("imageSrc"));
+                } else if ("GeckoView:DOMTitleChanged".equals(event)) {
                     listener.onTitleChange(GeckoView.this,
                                            message.getString("title"));
                 } else if ("GeckoView:FullScreenEnter".equals(event)) {
                     listener.onFullScreen(GeckoView.this, true);
                 } else if ("GeckoView:FullScreenExit".equals(event)) {
                     listener.onFullScreen(GeckoView.this, false);
                 }
-
             }
         };
 
     private final GeckoViewHandler<NavigationListener> mNavigationHandler =
         new GeckoViewHandler<NavigationListener>(
             "GeckoViewNavigation", this,
             new String[]{ "GeckoView:LocationChange" }
         ) {
@@ -1283,16 +1289,33 @@ public class GeckoView extends LayerView
          * A page has entered or exited full screen mode. Typically, the implementation
          * would set the Activity containing the GeckoView to full screen when the page is
          * in full screen mode.
          *
          * @param view The GeckoView that initiated the callback.
          * @param fullScreen True if the page is in full screen mode.
          */
         void onFullScreen(GeckoView view, boolean fullScreen);
+
+
+        /**
+         * A user has initiated the context menu via long-press.
+         * This event is fired on links, images and image-links (images nested
+         * in link nodes).
+         *
+         * @param view The GeckoView that initiated the callback.
+         * @param screenX The screen coordinates of the press.
+         * @param screenY The screen coordinates of the press.
+         * @param uri The URI of the pressed link, set for links and
+         *            image-links.
+         * @param imageSrc The URI of the pressed image, set for images and
+         *                 image-links.
+         */
+        void onContextMenu(GeckoView view, int screenX, int screenY,
+                           String uri, String imageSrc);
     }
 
     public interface NavigationListener {
         /**
         * A view has started loading content from the network.
         * @param view The GeckoView that initiated the callback.
         * @param url The resource being loaded.
         */
--- a/mobile/android/modules/geckoview/GeckoViewContent.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewContent.jsm
@@ -33,16 +33,17 @@ class GeckoViewContent extends GeckoView
                                  /* capture */ true, /* untrusted */ false);
     this.window.addEventListener("MozDOMFullScreen:Exited", this,
                                  /* capture */ true, /* untrusted */ false);
 
     this.eventDispatcher.registerListener(this, "GeckoViewContent:ExitFullScreen");
     this.messageManager.addMessageListener("GeckoView:DOMFullscreenExit", this);
     this.messageManager.addMessageListener("GeckoView:DOMFullscreenRequest", this);
     this.messageManager.addMessageListener("GeckoView:DOMTitleChanged", this);
+    this.messageManager.addMessageListener("GeckoView:ContextMenu", this);
   }
 
   // Bundle event handler.
   onEvent(aEvent, aData, aCallback) {
     debug("onEvent: " + aEvent);
     switch (aEvent) {
       case "GeckoViewContent:ExitFullScreen":
         this.messageManager.sendAsyncMessage("GeckoView:DOMFullscreenExited");
@@ -54,16 +55,17 @@ class GeckoViewContent extends GeckoView
     this.window.removeEventListener("MozDOMFullScreen:Entered", this,
                                     /* capture */ true);
     this.window.removeEventListener("MozDOMFullScreen:Exited", this,
                                     /* capture */ true);
     this.eventDispatcher.unregisterListener(this, "GeckoViewContent:ExitFullScreen");
     this.messageManager.removeMessageListener("GeckoView:DOMFullscreenExit", this);
     this.messageManager.removeMessageListener("GeckoView:DOMFullscreenRequest", this);
     this.messageManager.removeMessageListener("GeckoView:DOMTitleChanged", this);
+    this.messageManager.removeMessageListener("GeckoView:ContextMenu", this);
   }
 
   // DOM event handler
   handleEvent(aEvent) {
     debug("handleEvent: aEvent.type=" + aEvent.type);
 
     switch (aEvent.type) {
       case "MozDOMFullscreen:Entered":
@@ -78,16 +80,25 @@ class GeckoViewContent extends GeckoView
     }
   }
 
   // Message manager event handler.
   receiveMessage(aMsg) {
     debug("receiveMessage " + aMsg.name);
 
     switch (aMsg.name) {
+      case "GeckoView:ContextMenu":
+        this.eventDispatcher.sendRequest({
+          type: aMsg.name,
+          screenX: aMsg.data.screenX,
+          screenY: aMsg.data.screenY,
+          imageSrc: aMsg.data.imageSrc,
+          uri: aMsg.data.uri
+        });
+        break;
       case "GeckoView:DOMFullscreenExit":
         this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindowUtils)
                    .remoteFrameFullscreenReverted();
         break;
       case "GeckoView:DOMFullscreenRequest":
         this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindowUtils)