Bug 1545106 - Add GeckoSession-specific message delegate. r=snorp,esawin
authorAgi Sferro <agi@sferro.dev>
Thu, 18 Apr 2019 20:59:55 +0000
changeset 470148 401cb8e91e83eb79579e108910d871850792f96e
parent 470147 cebb49d20b00f9c076ca49862653dae263074d11
child 470149 c842ea1f7a5660f4c8d7822a286beb818b1f72aa
push id112843
push useraiakab@mozilla.com
push dateFri, 19 Apr 2019 09:50:22 +0000
treeherdermozilla-inbound@c06f27cbfe40 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp, esawin
bugs1545106
milestone68.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 1545106 - Add GeckoSession-specific message delegate. r=snorp,esawin Differential Revision: https://phabricator.services.mozilla.com/D27955
mobile/android/geckoview/api.txt
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExtensionTest.kt
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtension.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionEventDispatcher.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
--- a/mobile/android/geckoview/api.txt
+++ b/mobile/android/geckoview/api.txt
@@ -378,16 +378,17 @@ package org.mozilla.geckoview {
     method @UiThread @NonNull public CompositorController getCompositorController();
     method @AnyThread @Nullable public ContentBlocking.Delegate getContentBlockingDelegate();
     method @UiThread @Nullable public GeckoSession.ContentDelegate getContentDelegate();
     method @AnyThread @NonNull public static String getDefaultUserAgent();
     method @UiThread @NonNull public DynamicToolbarAnimator getDynamicToolbarAnimator();
     method @AnyThread @NonNull public SessionFinder getFinder();
     method @AnyThread @Nullable public GeckoSession.HistoryDelegate getHistoryDelegate();
     method @AnyThread @Nullable public GeckoSession.MediaDelegate getMediaDelegate();
+    method @AnyThread @Nullable public WebExtension.MessageDelegate getMessageDelegate(@NonNull String);
     method @UiThread @Nullable public GeckoSession.NavigationDelegate getNavigationDelegate();
     method @UiThread @NonNull public OverscrollEdgeEffect getOverscrollEdgeEffect();
     method @UiThread public void getPageToScreenMatrix(@NonNull Matrix);
     method @UiThread public void getPageToSurfaceMatrix(@NonNull Matrix);
     method @UiThread @NonNull public PanZoomController getPanZoomController();
     method @UiThread @Nullable public GeckoSession.PermissionDelegate getPermissionDelegate();
     method @UiThread @Nullable public GeckoSession.ProgressDelegate getProgressDelegate();
     method @AnyThread @Nullable public GeckoSession.PromptDelegate getPromptDelegate();
@@ -415,16 +416,17 @@ package org.mozilla.geckoview {
     method @AnyThread public void reload();
     method @AnyThread public void restoreState(@NonNull GeckoSession.SessionState);
     method @AnyThread public void setActive(boolean);
     method @AnyThread public void setContentBlockingDelegate(@Nullable ContentBlocking.Delegate);
     method @UiThread public void setContentDelegate(@Nullable GeckoSession.ContentDelegate);
     method @AnyThread public void setFocused(boolean);
     method @AnyThread public void setHistoryDelegate(@Nullable GeckoSession.HistoryDelegate);
     method @AnyThread public void setMediaDelegate(@Nullable GeckoSession.MediaDelegate);
+    method @AnyThread public void setMessageDelegate(@Nullable WebExtension.MessageDelegate, @NonNull String);
     method @UiThread public void setNavigationDelegate(@Nullable GeckoSession.NavigationDelegate);
     method @UiThread public void setPermissionDelegate(@Nullable GeckoSession.PermissionDelegate);
     method @UiThread public void setProgressDelegate(@Nullable GeckoSession.ProgressDelegate);
     method @AnyThread public void setPromptDelegate(@Nullable GeckoSession.PromptDelegate);
     method @UiThread public void setScrollDelegate(@Nullable GeckoSession.ScrollDelegate);
     method @UiThread public void setSelectionActionDelegate(@Nullable GeckoSession.SelectionActionDelegate);
     method @AnyThread public void stop();
     method @UiThread protected void setShouldPinOnScreen(boolean);
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExtensionTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExtensionTest.kt
@@ -107,17 +107,21 @@ class WebExtensionTest : BaseSessionTest
                     completed = true
                 }
                 return null
             }
         }
 
         val messaging = WebExtension(if (background) MESSAGING_BACKGROUND else MESSAGING_CONTENT,
                 "{${UUID.randomUUID()}}", !background)
-        messaging.setMessageDelegate(messageDelegate, "browser")
+        if (background) {
+            messaging.setMessageDelegate(messageDelegate, "browser")
+        } else {
+            sessionRule.session.setMessageDelegate(messageDelegate, "browser");
+        }
 
         sessionRule.waitForResult(sessionRule.runtime.registerWebExtension(messaging))
         sessionRule.waitForResult(messageResult)
 
         sessionRule.waitForResult(sessionRule.runtime.unregisterWebExtension(messaging))
     }
 
     @Test
@@ -185,17 +189,21 @@ class WebExtensionTest : BaseSessionTest
                                    sender: WebExtension.MessageSender): GeckoResult<Any>? {
                 // Ignored for this test
                 return null
             }
         }
 
         val messaging = WebExtension(if (background) MESSAGING_BACKGROUND else MESSAGING_CONTENT,
                 "{${UUID.randomUUID()}}", !background)
-        messaging.setMessageDelegate(messageDelegate, "browser")
+        if (background) {
+            messaging.setMessageDelegate(messageDelegate, "browser")
+        } else {
+            sessionRule.session.setMessageDelegate(messageDelegate, "browser");
+        }
 
         sessionRule.waitForResult(sessionRule.runtime.registerWebExtension(messaging))
         sessionRule.waitForResult(result)
         sessionRule.waitForResult(sessionRule.runtime.unregisterWebExtension(messaging))
     }
 
     @Test
     @WithDevToolsAPI
@@ -269,17 +277,21 @@ class WebExtensionTest : BaseSessionTest
 
                 // Ignored for this test
                 return null
             }
         }
 
         messaging = WebExtension(if (background) MESSAGING_BACKGROUND else MESSAGING_CONTENT,
                 "{${UUID.randomUUID()}}", !background)
-        messaging.setMessageDelegate(messageDelegate, "browser")
+        if (background) {
+            messaging.setMessageDelegate(messageDelegate, "browser")
+        } else {
+            sessionRule.session.setMessageDelegate(messageDelegate, "browser");
+        }
 
         sessionRule.waitForResult(sessionRule.runtime.registerWebExtension(messaging))
         sessionRule.waitForResult(result)
         sessionRule.waitForResult(sessionRule.runtime.unregisterWebExtension(messaging))
     }
 
     @Test
     @WithDevToolsAPI
@@ -351,17 +363,21 @@ class WebExtensionTest : BaseSessionTest
                 }
 
                 return null
             }
         }
 
         messaging = WebExtension(if (background) MESSAGING_BACKGROUND else MESSAGING_CONTENT,
                 "{${UUID.randomUUID()}}", !background)
-        messaging.setMessageDelegate(messageDelegate, "browser")
+        if (background) {
+            messaging.setMessageDelegate(messageDelegate, "browser")
+        } else {
+            sessionRule.session.setMessageDelegate(messageDelegate, "browser");
+        }
 
         sessionRule.waitForResult(sessionRule.runtime.registerWebExtension(messaging))
         sessionRule.waitForResult(result)
         sessionRule.waitForResult(sessionRule.runtime.unregisterWebExtension(messaging))
     }
 
     @Test
     @WithDevToolsAPI
@@ -421,17 +437,17 @@ class WebExtensionTest : BaseSessionTest
                 }
 
                 return null
             }
         }
 
         messaging = WebExtension("resource://android/assets/web_extensions/messaging-iframe/",
                 "{${UUID.randomUUID()}}", true)
-        messaging.setMessageDelegate(messageDelegate, "browser")
+        sessionRule.session.setMessageDelegate(messageDelegate, "browser");
 
         sessionRule.waitForResult(sessionRule.runtime.registerWebExtension(messaging))
         sessionRule.waitForResult(portTopLevel)
         sessionRule.waitForResult(portIframe)
         sessionRule.waitForResult(messageTopLevel)
         sessionRule.waitForResult(messageIframe)
         sessionRule.waitForResult(sessionRule.runtime.unregisterWebExtension(messaging))
     }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.geckoview;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.AbstractSequentialList;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.NoSuchElementException;
 import java.util.UUID;
 
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -329,24 +330,39 @@ public class GeckoSession implements Par
                             return null;
                         }
                     });
                 }
             }
         };
 
     private final class WebExtensionListener implements BundleEventListener {
+        final private HashMap<String, WebExtension.MessageDelegate> mMessageDelegates;
+
+        public WebExtensionListener() {
+            mMessageDelegates = new HashMap<>();
+        }
+
         /* package */ void registerListeners() {
             getEventDispatcher().registerUiThreadListener(this,
                     "GeckoView:WebExtension:Message",
                     "GeckoView:WebExtension:PortMessage",
                     "GeckoView:WebExtension:Connect",
                     null);
         }
 
+        public void setDelegate(final WebExtension.MessageDelegate delegate,
+                                final String nativeApp) {
+            mMessageDelegates.put(nativeApp, delegate);
+        }
+
+        public WebExtension.MessageDelegate getDelegate(final String nativeApp) {
+            return mMessageDelegates.get(nativeApp);
+        }
+
         @Override
         public void handleMessage(final String event, final GeckoBundle message,
                                   final EventCallback callback) {
             if (mWindow == null || mWindow.runtime.getWebExtensionDispatcher() == null) {
                 return;
             }
 
             if ("GeckoView:WebExtension:Message".equals(event)
@@ -355,16 +371,55 @@ public class GeckoSession implements Par
                 mWindow.runtime.getWebExtensionDispatcher()
                         .handleMessage(event, message, callback, GeckoSession.this);
             }
         }
     }
 
     private final WebExtensionListener mWebExtensionListener;
 
+    /**
+     * Get the message delegate for <code>nativeApp</code>.
+     *
+     * @param nativeApp identifier for the native app
+     * @return The {@link WebExtension.MessageDelegate} attached to the
+     *         <code>nativeApp</code>.  <code>null</code> if no delegate is
+     *         present.
+     */
+    @AnyThread
+    public @Nullable WebExtension.MessageDelegate getMessageDelegate(
+            final @NonNull String nativeApp) {
+        return mWebExtensionListener.getDelegate(nativeApp);
+    }
+
+    /**
+     * Defines a message delegate for a Native App.
+     *
+     * If a delegate is already present, this delegate will replace the
+     * existing one.
+     *
+     * This message delegate will be responsible for handling messaging between
+     * a WebExtension content script running on the {@link GeckoSession}.
+     *
+     * Note: To receive messages from content scripts, the WebExtension needs
+     * to explicitely allow it in {@link WebExtension#WebExtension} by setting
+     * {@link WebExtension#allowContentMessaging} to <code>true</code>.
+     *
+     * @param delegate {@link WebExtension.MessageDelegate} that will receive
+     *                 messages from this session.
+     * @param nativeApp which native app id this message delegate will handle
+     *                  messaging for.
+     * @see WebExtension#setMessageDelegate
+     */
+    @AnyThread
+    public void setMessageDelegate(final @Nullable WebExtension.MessageDelegate delegate,
+                                   final @NonNull String nativeApp) {
+        mWebExtensionListener.setDelegate(delegate, nativeApp);
+    }
+
     private final GeckoSessionHandler<ContentDelegate> mContentHandler =
         new GeckoSessionHandler<ContentDelegate>(
             "GeckoViewContent", this,
             new String[]{
                 "GeckoView:ContentCrash",
                 "GeckoView:ContextMenu",
                 "GeckoView:DOMTitleChanged",
                 "GeckoView:DOMWindowFocus",
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtension.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtension.java
@@ -119,20 +119,23 @@ public class WebExtension {
      *                 <code>resource:</code> URI to a folder inside the APK or
      *                 a <code>file:</code> URL to a <code>.xpi</code> file.
      */
     public WebExtension(final @NonNull String location) {
         this(location, "{" + UUID.randomUUID().toString() + "}", false);
     }
 
     /**
-     * Defines a message delegate for a Native App.
+     * Defines the message delegate for a Native App.
      *
-     * This message delegate will handle messaging for the native app specified in
-     * <code>nativeApp</code>.
+     * This message delegate will receive messages from the background script
+     * for the native app specified in <code>nativeApp</code>.
+     *
+     * For messages from content scripts, set a session-specific message
+     * delegate using {@link GeckoSession#setMessageDelegate}.
      *
      * See also
      *  <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging">
      *     WebExtensions/Native_messaging
      *  </a>
      *
      * @param messageDelegate handles messaging between the WebExtension and
      *      the app. To send a message from the WebExtension use the
@@ -160,16 +163,18 @@ public class WebExtension {
      *      You can unset the message delegate by setting a <code>null</code>
      *      messageDelegate.
      *
      * @param nativeApp which native app id this message delegate will handle
      *                  messaging for. Needs to match the
      *                  <code>application</code> parameter of
      *                  <code>runtime.sendNativeMessage</code> and
      *                  <code>runtime.connectNative</code>.
+     *
+     * @see GeckoSession#setMessageDelegate
      */
     @UiThread
     public void setMessageDelegate(final @Nullable MessageDelegate messageDelegate,
                                    final @NonNull String nativeApp) {
         if (messageDelegate == null) {
             messageDelegates.remove(nativeApp);
             return;
         }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionEventDispatcher.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionEventDispatcher.java
@@ -139,30 +139,35 @@ import java.util.Map;
         if (callback != null) {
             callback.sendSuccess(true);
         }
     }
 
     private WebExtension.MessageDelegate getDelegate(
             final String nativeApp, final WebExtension.MessageSender sender,
             final EventCallback callback) {
-        final WebExtension.MessageDelegate delegate =
-                sender.webExtension.messageDelegates.get(nativeApp);
+        if (!sender.webExtension.allowContentMessaging &&
+                sender.environmentType == WebExtension.MessageSender.ENV_TYPE_CONTENT_SCRIPT) {
+            callback.sendError("This NativeApp can't receive messages from Content Scripts.");
+            return null;
+        }
+
+        WebExtension.MessageDelegate delegate = null;
+
+        if (sender.session != null) {
+            delegate = sender.session.getMessageDelegate(nativeApp);
+        } else if (sender.environmentType == WebExtension.MessageSender.ENV_TYPE_EXTENSION) {
+            delegate = sender.webExtension.messageDelegates.get(nativeApp);
+        }
 
         if (delegate == null) {
             callback.sendError("Native app not found or this WebExtension does not have permissions.");
             return null;
         }
 
-        if (!sender.webExtension.allowContentMessaging &&
-                sender.environmentType == WebExtension.MessageSender.ENV_TYPE_CONTENT_SCRIPT) {
-            callback.sendError("This NativeApp can't receive messages from Content Scripts.");
-            return null;
-        }
-
         return delegate;
     }
 
     private void connect(final String nativeApp, final long portId, final EventCallback callback,
                          final WebExtension.MessageSender sender) {
         if (portId == -1) {
             callback.sendError("Missing portId.");
             return;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
@@ -299,9 +299,9 @@ exclude: true
 [65.23]: ../GeckoSession.FinderResult.html
 
 - Update [`CrashReporter#sendCrashReport`][65.24] to return the crash ID as a
   [`GeckoResult<String>`][65.25].
 
 [65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
 [65.25]: ../GeckoResult.html
 
-[api-version]: f602ba872a5af8da8af416364529297cd70c2791
+[api-version]: 2186cb94bf0e2282ec4dca649f09ecdff0ecf05b