Bug 1599578 - Merge WebExtensionController and EventDispatcher. r=snorp

Differential Revision: https://phabricator.services.mozilla.com/D54810

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Agi Sferro 2019-11-26 23:01:16 +00:00
parent 7d30f98797
commit 543a4cfa26
6 changed files with 367 additions and 390 deletions

View File

@ -83,7 +83,6 @@ import org.mozilla.geckoview.SlowScriptResponse;
import org.mozilla.geckoview.StorageController;
import org.mozilla.geckoview.WebExtension;
import org.mozilla.geckoview.WebExtensionController;
import org.mozilla.geckoview.WebExtensionEventDispatcher;
import org.mozilla.geckoview.WebMessage;
import org.mozilla.geckoview.WebNotification;
import org.mozilla.geckoview.WebNotificationDelegate;
@ -1458,7 +1457,6 @@ package org.mozilla.geckoview {
}
public class WebExtensionController {
ctor protected WebExtensionController(GeckoRuntime, WebExtensionEventDispatcher);
method @UiThread @Nullable public WebExtensionController.TabDelegate getTabDelegate();
method @UiThread public void setTabDelegate(@Nullable WebExtensionController.TabDelegate);
}

View File

@ -164,15 +164,13 @@ public final class GeckoRuntime implements Parcelable {
private ServiceWorkerDelegate mServiceWorkerDelegate;
private WebNotificationDelegate mNotificationDelegate;
private RuntimeTelemetry mTelemetry;
private final WebExtensionEventDispatcher mWebExtensionDispatcher;
private StorageController mStorageController;
private final WebExtensionController mWebExtensionController;
private WebPushController mPushController;
private final ContentBlockingController mContentBlockingController;
private GeckoRuntime() {
mWebExtensionDispatcher = new WebExtensionEventDispatcher();
mWebExtensionController = new WebExtensionController(this, mWebExtensionDispatcher);
mWebExtensionController = new WebExtensionController(this);
mContentBlockingController = new ContentBlockingController();
if (sRuntime != null) {
throw new IllegalStateException("Only one GeckoRuntime instance is allowed");
@ -455,7 +453,7 @@ public final class GeckoRuntime implements Parcelable {
bundle.putBoolean("allowContentMessaging",
(webExtension.flags & WebExtension.Flags.ALLOW_CONTENT_MESSAGING) > 0);
mWebExtensionDispatcher.registerWebExtension(webExtension);
mWebExtensionController.registerWebExtension(webExtension);
EventDispatcher.getInstance().dispatch("GeckoView:RegisterWebExtension",
bundle, result);
@ -485,17 +483,13 @@ public final class GeckoRuntime implements Parcelable {
final GeckoBundle bundle = new GeckoBundle(1);
bundle.putString("id", webExtension.id);
mWebExtensionDispatcher.unregisterWebExtension(webExtension);
mWebExtensionController.unregisterWebExtension(webExtension);
EventDispatcher.getInstance().dispatch("GeckoView:UnregisterWebExtension", bundle, result);
return result;
}
/* protected */ @NonNull WebExtensionEventDispatcher getWebExtensionDispatcher() {
return mWebExtensionDispatcher;
}
/**
* Create a new runtime with the given settings and attach it to the given
* context.

View File

@ -491,7 +491,7 @@ public class WebExtension {
|| "GeckoView:PageAction:OpenPopup".equals(event)
|| "GeckoView:BrowserAction:Update".equals(event)
|| "GeckoView:BrowserAction:OpenPopup".equals(event)) {
runtime.getWebExtensionDispatcher()
runtime.getWebExtensionController()
.handleMessage(event, message, callback, mSession);
return;
} else if ("GeckoView:WebExtension:CloseTab".equals(event)) {

View File

@ -3,13 +3,49 @@ package org.mozilla.geckoview;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.util.Log;
import org.json.JSONException;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class WebExtensionController {
private final static String LOGTAG = "WebExtension";
private GeckoRuntime mRuntime;
private boolean mHandlerRegistered = false;
private TabDelegate mTabDelegate;
private Map<String, WebExtension> mExtensions = new HashMap<>();
private Map<Long, WebExtension.Port> mPorts = new HashMap<>();
private Internals mInternals = new Internals();
// Avoids exposing listeners to the API
private class Internals implements BundleEventListener,
WebExtension.Port.DisconnectDelegate {
@Override
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
WebExtensionController.this.handleMessage(event, message, callback, null);
}
@Override
public void onDisconnectFromApp(final WebExtension.Port port) {
// If the port has been disconnected from the app side, we don't need to notify anyone and
// we just need to remove it from our list of ports.
mPorts.remove(port.id);
}
}
public interface TabDelegate {
/**
* Called when tabs.create is invoked, this method returns a *newly-created* session
@ -47,26 +83,9 @@ public class WebExtensionController {
}
}
private GeckoRuntime mRuntime;
private WebExtensionEventDispatcher mDispatcher;
private TabDelegate mTabDelegate;
private final EventListener mEventListener;
protected WebExtensionController(final GeckoRuntime runtime, final WebExtensionEventDispatcher dispatcher) {
mRuntime = runtime;
mDispatcher = dispatcher;
mEventListener = new EventListener();
}
private class EventListener implements BundleEventListener {
@Override
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
if ("GeckoView:WebExtension:NewTab".equals(event)) {
newTab(message, callback);
return;
}
}
@UiThread
public @Nullable TabDelegate getTabDelegate() {
return mTabDelegate;
}
@UiThread
@ -74,14 +93,14 @@ public class WebExtensionController {
if (delegate == null) {
if (mTabDelegate != null) {
EventDispatcher.getInstance().unregisterUiThreadListener(
mEventListener,
mInternals,
"GeckoView:WebExtension:NewTab"
);
}
} else {
if (mTabDelegate == null) {
EventDispatcher.getInstance().registerUiThreadListener(
mEventListener,
mInternals,
"GeckoView:WebExtension:NewTab"
);
}
@ -89,9 +108,78 @@ public class WebExtensionController {
mTabDelegate = delegate;
}
@UiThread
public @Nullable TabDelegate getTabDelegate() {
return mTabDelegate;
/* package */ WebExtensionController(final GeckoRuntime runtime) {
mRuntime = runtime;
}
/* package */ void registerWebExtension(final WebExtension webExtension) {
if (!mHandlerRegistered) {
EventDispatcher.getInstance().registerUiThreadListener(
mInternals,
"GeckoView:WebExtension:Message",
"GeckoView:WebExtension:PortMessage",
"GeckoView:WebExtension:Connect",
"GeckoView:WebExtension:Disconnect",
// {Browser,Page}Actions
"GeckoView:BrowserAction:Update",
"GeckoView:BrowserAction:OpenPopup",
"GeckoView:PageAction:Update",
"GeckoView:PageAction:OpenPopup"
);
mHandlerRegistered = true;
}
mExtensions.put(webExtension.id, webExtension);
}
/* package */ void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback, final GeckoSession session) {
if ("GeckoView:WebExtension:NewTab".equals(event)) {
newTab(message, callback);
return;
} else if ("GeckoView:WebExtension:Disconnect".equals(event)) {
disconnect(message.getLong("portId", -1), callback);
return;
} else if ("GeckoView:WebExtension:PortMessage".equals(event)) {
portMessage(message, callback);
return;
} else if ("GeckoView:BrowserAction:Update".equals(event)) {
actionUpdate(message, session, WebExtension.Action.TYPE_BROWSER_ACTION);
return;
} else if ("GeckoView:PageAction:Update".equals(event)) {
actionUpdate(message, session, WebExtension.Action.TYPE_PAGE_ACTION);
return;
} else if ("GeckoView:BrowserAction:OpenPopup".equals(event)) {
openPopup(message, session, WebExtension.Action.TYPE_BROWSER_ACTION);
return;
} else if ("GeckoView:PageAction:OpenPopup".equals(event)) {
openPopup(message, session, WebExtension.Action.TYPE_PAGE_ACTION);
return;
}
final String nativeApp = message.getString("nativeApp");
if (nativeApp == null) {
if (BuildConfig.DEBUG) {
throw new RuntimeException("Missing required nativeApp message parameter.");
}
callback.sendError("Missing nativeApp parameter.");
return;
}
final WebExtension.MessageSender sender = fromBundle(message.getBundle("sender"), session);
if (sender == null) {
if (callback != null) {
callback.sendError("Could not find recipient for " + message.getBundle("sender"));
}
return;
}
if ("GeckoView:WebExtension:Connect".equals(event)) {
connect(nativeApp, message.getLong("portId", -1), callback, sender);
} else if ("GeckoView:WebExtension:Message".equals(event)) {
message(nativeApp, message, callback, sender);
}
}
private void newTab(final GeckoBundle message, final EventCallback callback) {
@ -100,7 +188,7 @@ public class WebExtensionController {
return;
}
WebExtension extension = mDispatcher.getWebExtension(message.getString("extensionId"));
WebExtension extension = mExtensions.get(message.getString("extensionId"));
final GeckoResult<GeckoSession> result = mTabDelegate.onNewTab(extension, message.getString("uri"));
@ -131,7 +219,7 @@ public class WebExtensionController {
return;
}
WebExtension extension = mRuntime.getWebExtensionDispatcher().getWebExtension(message.getString("extensionId"));
WebExtension extension = mExtensions.get(message.getString("extensionId"));
GeckoResult<AllowOrDeny> result = mTabDelegate.onCloseTab(extension, session);
@ -143,4 +231,251 @@ public class WebExtensionController {
}
});
}
/* package */ void unregisterWebExtension(final WebExtension webExtension) {
mExtensions.remove(webExtension.id);
// Some ports may still be open so we need to go through the list and close all of the
// ports tied to this web extension
Iterator<Map.Entry<Long, WebExtension.Port>> it = mPorts.entrySet().iterator();
while (it.hasNext()) {
WebExtension.Port port = it.next().getValue();
if (port.sender.webExtension.equals(webExtension)) {
it.remove();
}
}
}
/* package */ @Nullable WebExtension getWebExtension(final String id) {
return mExtensions.get(id);
}
private WebExtension.MessageSender fromBundle(final GeckoBundle sender,
final GeckoSession session) {
final String extensionId = sender.getString("extensionId");
WebExtension extension = mExtensions.get(extensionId);
if (extension == null) {
// All senders should have an extension
return null;
}
final String envType = sender.getString("envType");
@WebExtension.MessageSender.EnvType int environmentType;
if ("content_child".equals(envType)) {
environmentType = WebExtension.MessageSender.ENV_TYPE_CONTENT_SCRIPT;
} else if ("addon_child".equals(envType)) {
// TODO Bug 1554277: check that this message is coming from the right process
environmentType = WebExtension.MessageSender.ENV_TYPE_EXTENSION;
} else {
environmentType = WebExtension.MessageSender.ENV_TYPE_UNKNOWN;
}
if (environmentType == WebExtension.MessageSender.ENV_TYPE_UNKNOWN) {
if (BuildConfig.DEBUG) {
throw new RuntimeException("Missing or unknown envType.");
}
return null;
}
final String url = sender.getString("url");
boolean isTopLevel;
if (session == null) {
// This message is coming from the background page
isTopLevel = true;
} else {
// If session is present we are either receiving this message from a content script or
// an extension page, let's make sure we have the proper identification so that
// embedders can check the origin of this message.
if (!sender.containsKey("frameId") || !sender.containsKey("url") ||
// -1 is an invalid frame id
sender.getInt("frameId", -1) == -1) {
if (BuildConfig.DEBUG) {
throw new RuntimeException("Missing sender information.");
}
// This message does not have the proper identification and may be compromised,
// let's ignore it.
return null;
}
isTopLevel = sender.getInt("frameId", -1) == 0;
}
return new WebExtension.MessageSender(extension, session, url, environmentType, isTopLevel);
}
private void disconnect(final long portId, final EventCallback callback) {
final WebExtension.Port port = mPorts.get(portId);
if (port == null) {
Log.d(LOGTAG, "Could not find recipient for port " + portId);
return;
}
port.delegate.onDisconnect(port);
mPorts.remove(portId);
if (callback != null) {
callback.sendSuccess(true);
}
}
private WebExtension.MessageDelegate getDelegate(
final String nativeApp, final WebExtension.MessageSender sender,
final EventCallback callback) {
if ((sender.webExtension.flags & WebExtension.Flags.ALLOW_CONTENT_MESSAGING) == 0 &&
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(sender.webExtension, 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;
}
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;
}
final WebExtension.Port port = new WebExtension.Port(nativeApp, portId, sender, mInternals);
mPorts.put(port.id, port);
final WebExtension.MessageDelegate delegate = getDelegate(nativeApp, sender, callback);
if (delegate == null) {
return;
}
delegate.onConnect(port);
callback.sendSuccess(true);
}
private void portMessage(final GeckoBundle message, final EventCallback callback) {
final long portId = message.getLong("portId", -1);
final WebExtension.Port port = mPorts.get(portId);
if (port == null) {
callback.sendError("Could not find recipient for port " + portId);
return;
}
final Object content;
try {
content = message.toJSONObject().get("data");
} catch (JSONException ex) {
callback.sendError(ex);
return;
}
port.delegate.onPortMessage(content, port);
callback.sendSuccess(null);
}
private void message(final String nativeApp, final GeckoBundle message,
final EventCallback callback, final WebExtension.MessageSender sender) {
final Object content;
try {
content = message.toJSONObject().get("data");
} catch (JSONException ex) {
callback.sendError(ex);
return;
}
final WebExtension.MessageDelegate delegate = getDelegate(nativeApp, sender, callback);
if (delegate == null) {
return;
}
final GeckoResult<Object> response = delegate.onMessage(nativeApp, content, sender);
if (response == null) {
callback.sendSuccess(null);
return;
}
response.accept(
value -> callback.sendSuccess(value),
exception -> callback.sendError(exception));
}
private WebExtension extensionFromBundle(final GeckoBundle message) {
final String extensionId = message.getString("extensionId");
final WebExtension extension = mExtensions.get(extensionId);
if (extension == null) {
if (BuildConfig.DEBUG) {
// TODO: Bug 1582185 Some gecko tests install WebExtensions that we
// don't know about and cause this to trigger.
// throw new RuntimeException("Could not find extension: " + extensionId);
}
Log.e(LOGTAG, "Could not find extension: " + extensionId);
}
return extension;
}
private void openPopup(final GeckoBundle message, final GeckoSession session,
final @WebExtension.Action.ActionType int actionType) {
final WebExtension extension = extensionFromBundle(message);
if (extension == null) {
return;
}
final WebExtension.Action action = new WebExtension.Action(
actionType, message.getBundle("action"), extension);
final WebExtension.ActionDelegate delegate = actionDelegateFor(extension, session);
if (delegate == null) {
return;
}
final GeckoResult<GeckoSession> popup = delegate.onOpenPopup(extension, action);
action.openPopup(popup);
}
private WebExtension.ActionDelegate actionDelegateFor(final WebExtension extension,
final GeckoSession session) {
if (session == null) {
return extension.actionDelegate;
}
return session.getWebExtensionActionDelegate(extension);
}
private void actionUpdate(final GeckoBundle message, final GeckoSession session,
final @WebExtension.Action.ActionType int actionType) {
final WebExtension extension = extensionFromBundle(message);
if (extension == null) {
return;
}
final WebExtension.ActionDelegate delegate = actionDelegateFor(extension, session);
if (delegate == null) {
return;
}
final WebExtension.Action action = new WebExtension.Action(
actionType, message.getBundle("action"), extension);
if (actionType == WebExtension.Action.TYPE_BROWSER_ACTION) {
delegate.onBrowserAction(extension, session, action);
} else if (actionType == WebExtension.Action.TYPE_PAGE_ACTION) {
delegate.onPageAction(extension, session, action);
}
}
}

View File

@ -1,350 +0,0 @@
package org.mozilla.geckoview;
import android.support.annotation.Nullable;
import android.util.Log;
import org.json.JSONException;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/* protected */ class WebExtensionEventDispatcher implements BundleEventListener,
WebExtension.Port.DisconnectDelegate {
private final static String LOGTAG = "WebExtension";
private boolean mHandlerRegistered = false;
private Map<String, WebExtension> mExtensions = new HashMap<>();
private Map<Long, WebExtension.Port> mPorts = new HashMap<>();
public void registerWebExtension(final WebExtension webExtension) {
if (!mHandlerRegistered) {
EventDispatcher.getInstance().registerUiThreadListener(
this,
"GeckoView:WebExtension:Message",
"GeckoView:WebExtension:PortMessage",
"GeckoView:WebExtension:Connect",
"GeckoView:WebExtension:Disconnect",
// {Browser,Page}Actions
"GeckoView:BrowserAction:Update",
"GeckoView:BrowserAction:OpenPopup",
"GeckoView:PageAction:Update",
"GeckoView:PageAction:OpenPopup"
);
mHandlerRegistered = true;
}
mExtensions.put(webExtension.id, webExtension);
}
public void unregisterWebExtension(final WebExtension webExtension) {
mExtensions.remove(webExtension.id);
// Some ports may still be open so we need to go through the list and close all of the
// ports tied to this web extension
Iterator<Map.Entry<Long, WebExtension.Port>> it = mPorts.entrySet().iterator();
while (it.hasNext()) {
WebExtension.Port port = it.next().getValue();
if (port.sender.webExtension.equals(webExtension)) {
it.remove();
}
}
}
public @Nullable WebExtension getWebExtension(final String id) {
return mExtensions.get(id);
}
@Override
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
handleMessage(event, message, callback, null);
}
private WebExtension.MessageSender fromBundle(final GeckoBundle sender,
final GeckoSession session) {
final String extensionId = sender.getString("extensionId");
WebExtension extension = mExtensions.get(extensionId);
if (extension == null) {
// All senders should have an extension
return null;
}
final String envType = sender.getString("envType");
@WebExtension.MessageSender.EnvType int environmentType;
if ("content_child".equals(envType)) {
environmentType = WebExtension.MessageSender.ENV_TYPE_CONTENT_SCRIPT;
} else if ("addon_child".equals(envType)) {
// TODO Bug 1554277: check that this message is coming from the right process
environmentType = WebExtension.MessageSender.ENV_TYPE_EXTENSION;
} else {
environmentType = WebExtension.MessageSender.ENV_TYPE_UNKNOWN;
}
if (environmentType == WebExtension.MessageSender.ENV_TYPE_UNKNOWN) {
if (BuildConfig.DEBUG) {
throw new RuntimeException("Missing or unknown envType.");
}
return null;
}
final String url = sender.getString("url");
boolean isTopLevel;
if (session == null) {
// This message is coming from the background page
isTopLevel = true;
} else {
// If session is present we are either receiving this message from a content script or
// an extension page, let's make sure we have the proper identification so that
// embedders can check the origin of this message.
if (!sender.containsKey("frameId") || !sender.containsKey("url") ||
// -1 is an invalid frame id
sender.getInt("frameId", -1) == -1) {
if (BuildConfig.DEBUG) {
throw new RuntimeException("Missing sender information.");
}
// This message does not have the proper identification and may be compromised,
// let's ignore it.
return null;
}
isTopLevel = sender.getInt("frameId", -1) == 0;
}
return new WebExtension.MessageSender(extension, session, url, environmentType, isTopLevel);
}
@Override
public void onDisconnectFromApp(final WebExtension.Port port) {
// If the port has been disconnected from the app side, we don't need to notify anyone and
// we just need to remove it from our list of ports.
mPorts.remove(port.id);
}
private void disconnect(final long portId, final EventCallback callback) {
final WebExtension.Port port = mPorts.get(portId);
if (port == null) {
Log.d(LOGTAG, "Could not find recipient for port " + portId);
return;
}
port.delegate.onDisconnect(port);
mPorts.remove(portId);
if (callback != null) {
callback.sendSuccess(true);
}
}
private WebExtension.MessageDelegate getDelegate(
final String nativeApp, final WebExtension.MessageSender sender,
final EventCallback callback) {
if ((sender.webExtension.flags & WebExtension.Flags.ALLOW_CONTENT_MESSAGING) == 0 &&
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(sender.webExtension, 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;
}
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;
}
final WebExtension.Port port = new WebExtension.Port(nativeApp, portId, sender, this);
mPorts.put(port.id, port);
final WebExtension.MessageDelegate delegate = getDelegate(nativeApp, sender, callback);
if (delegate == null) {
return;
}
delegate.onConnect(port);
callback.sendSuccess(true);
}
private void portMessage(final GeckoBundle message, final EventCallback callback) {
final long portId = message.getLong("portId", -1);
final WebExtension.Port port = mPorts.get(portId);
if (port == null) {
callback.sendError("Could not find recipient for port " + portId);
return;
}
final Object content;
try {
content = message.toJSONObject().get("data");
} catch (JSONException ex) {
callback.sendError(ex);
return;
}
port.delegate.onPortMessage(content, port);
callback.sendSuccess(null);
}
private void message(final String nativeApp, final GeckoBundle message,
final EventCallback callback, final WebExtension.MessageSender sender) {
final Object content;
try {
content = message.toJSONObject().get("data");
} catch (JSONException ex) {
callback.sendError(ex);
return;
}
final WebExtension.MessageDelegate delegate = getDelegate(nativeApp, sender, callback);
if (delegate == null) {
return;
}
final GeckoResult<Object> response = delegate.onMessage(nativeApp, content, sender);
if (response == null) {
callback.sendSuccess(null);
return;
}
response.accept(
value -> callback.sendSuccess(value),
exception -> callback.sendError(exception));
}
private WebExtension extensionFromBundle(final GeckoBundle message) {
final String extensionId = message.getString("extensionId");
final WebExtension extension = mExtensions.get(extensionId);
if (extension == null) {
if (BuildConfig.DEBUG) {
// TODO: Bug 1582185 Some gecko tests install WebExtensions that we
// don't know about and cause this to trigger.
// throw new RuntimeException("Could not find extension: " + extensionId);
}
Log.e(LOGTAG, "Could not find extension: " + extensionId);
}
return extension;
}
private void openPopup(final GeckoBundle message, final GeckoSession session,
final @WebExtension.Action.ActionType int actionType) {
final WebExtension extension = extensionFromBundle(message);
if (extension == null) {
return;
}
final WebExtension.Action action = new WebExtension.Action(
actionType, message.getBundle("action"), extension);
final WebExtension.ActionDelegate delegate = actionDelegateFor(extension, session);
if (delegate == null) {
return;
}
final GeckoResult<GeckoSession> popup = delegate.onOpenPopup(extension, action);
action.openPopup(popup);
}
private WebExtension.ActionDelegate actionDelegateFor(final WebExtension extension,
final GeckoSession session) {
if (session == null) {
return extension.actionDelegate;
}
return session.getWebExtensionActionDelegate(extension);
}
private void actionUpdate(final GeckoBundle message, final GeckoSession session,
final @WebExtension.Action.ActionType int actionType) {
final WebExtension extension = extensionFromBundle(message);
if (extension == null) {
return;
}
final WebExtension.ActionDelegate delegate = actionDelegateFor(extension, session);
if (delegate == null) {
return;
}
final WebExtension.Action action = new WebExtension.Action(
actionType, message.getBundle("action"), extension);
if (actionType == WebExtension.Action.TYPE_BROWSER_ACTION) {
delegate.onBrowserAction(extension, session, action);
} else if (actionType == WebExtension.Action.TYPE_PAGE_ACTION) {
delegate.onPageAction(extension, session, action);
}
}
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback, final GeckoSession session) {
if ("GeckoView:WebExtension:Disconnect".equals(event)) {
disconnect(message.getLong("portId", -1), callback);
return;
} else if ("GeckoView:WebExtension:PortMessage".equals(event)) {
portMessage(message, callback);
return;
} else if ("GeckoView:BrowserAction:Update".equals(event)) {
actionUpdate(message, session, WebExtension.Action.TYPE_BROWSER_ACTION);
return;
} else if ("GeckoView:PageAction:Update".equals(event)) {
actionUpdate(message, session, WebExtension.Action.TYPE_PAGE_ACTION);
return;
} else if ("GeckoView:BrowserAction:OpenPopup".equals(event)) {
openPopup(message, session, WebExtension.Action.TYPE_BROWSER_ACTION);
return;
} else if ("GeckoView:PageAction:OpenPopup".equals(event)) {
openPopup(message, session, WebExtension.Action.TYPE_PAGE_ACTION);
return;
}
final String nativeApp = message.getString("nativeApp");
if (nativeApp == null) {
if (BuildConfig.DEBUG) {
throw new RuntimeException("Missing required nativeApp message parameter.");
}
callback.sendError("Missing nativeApp parameter.");
return;
}
final WebExtension.MessageSender sender = fromBundle(message.getBundle("sender"), session);
if (sender == null) {
if (callback != null) {
callback.sendError("Could not find recipient for " + message.getBundle("sender"));
}
return;
}
if ("GeckoView:WebExtension:Connect".equals(event)) {
connect(nativeApp, message.getLong("portId", -1), callback, sender);
} else if ("GeckoView:WebExtension:Message".equals(event)) {
message(nativeApp, message, callback, sender);
}
}
}

View File

@ -465,4 +465,4 @@ exclude: true
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
[65.25]: {{javadoc_uri}}/GeckoResult.html
[api-version]: 4c9f04038d8478206efac05b518920819faeacea
[api-version]: f7f3675072cd2a0e4d65010942c8f02b4e4266f1