diff --git 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 index d8fb8cfdc04c..39c1aa9dcf2a 100644 --- 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 @@ -1072,7 +1072,7 @@ class WebExtensionTest : BaseSessionTest() { : WebExtension.SessionTabDelegate { override fun onCloseTab(source: WebExtension?, session: GeckoSession): GeckoResult { - assertEquals(extension, source) + assertEquals(extension.id, source!!.id) assertEquals(expectedSession, session) result.complete(null) return GeckoResult.ALLOW diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java index d2602114e531..65ef7d75b384 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java @@ -15,6 +15,7 @@ import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.geckoview.BuildConfig; +import org.mozilla.geckoview.GeckoResult; import android.os.Handler; import androidx.annotation.AnyThread; @@ -221,6 +222,129 @@ public final class EventDispatcher extends JNIObject { dispatch(type, message, /* callback */ null); } + private abstract class CallbackResult extends GeckoResult + implements EventCallback { + @Override + public void sendError(final Object response) { + completeExceptionally(new QueryException(response)); + } + } + + public class QueryException extends Exception { + public final Object data; + + public QueryException(final Object data) { + this.data = data; + } + } + + /** + * Query event to any registered Bundle listeners (non-Gecko thread listeners). + * + * The returned GeckoResult completes when the event handler returns. + * + * @param type Event type + */ + public GeckoResult queryVoid(final String type) { + return queryVoid(type, null); + } + + /** + * Query event to any registered Bundle listeners (non-Gecko thread listeners). + * + * The returned GeckoResult completes when the event handler returns. + * + * @param type Event type + * @param message GeckoBundle message + */ + public GeckoResult queryVoid(final String type, final GeckoBundle message) { + return query(type, message); + } + + /** + * Query event to any registered Bundle listeners (non-Gecko thread listeners). + * + * The returned GeckoResult completes with the given boolean value returned by the handler. + * + * @param type Event type + */ + public GeckoResult queryBoolean(final String type) { + return queryBoolean(type, null); + } + + /** + * Query event to any registered Bundle listeners (non-Gecko thread listeners). + * + * The returned GeckoResult completes with the given boolean value returned by the handler. + * + * @param type Event type + * @param message GeckoBundle message + */ + public GeckoResult queryBoolean(final String type, final GeckoBundle message) { + return query(type, message); + } + + /** + * Query event to any registered Bundle listeners (non-Gecko thread listeners). + * + * The returned GeckoResult completes with the given String value returned by the handler. + * + * @param type Event type + */ + public GeckoResult queryString(final String type) { + return queryString(type, null); + } + + /** + * Query event to any registered Bundle listeners (non-Gecko thread listeners). + * + * The returned GeckoResult completes with the given String value returned by the handler. + * + * @param type Event type + * @param message GeckoBundle message + */ + public GeckoResult queryString(final String type, final GeckoBundle message) { + return query(type, message); + } + + /** + * Query event to any registered Bundle listeners (non-Gecko thread listeners). + * + * The returned GeckoResult completes with the given {@link GeckoBundle} value + * returned by the handler. + * + * @param type Event type + */ + public GeckoResult queryBundle(final String type) { + return queryBundle(type, null); + } + + /** + * Query event to any registered Bundle listeners (non-Gecko thread listeners). + * + * The returned GeckoResult completes with the given {@link GeckoBundle} value + * returned by the handler. + * + * @param type Event type + * @param message GeckoBundle message + */ + public GeckoResult queryBundle(final String type, final GeckoBundle message) { + return query(type, message); + } + + private GeckoResult query(final String type, final GeckoBundle message) { + final CallbackResult result = new CallbackResult() { + @Override + @SuppressWarnings("unchecked") // Not a lot we can do about this :( + public void sendSuccess(final Object response) { + complete((T) response); + } + }; + + dispatch(type, message, result); + return result; + } + /** * Flushes pending messages of given types. * @@ -259,8 +383,8 @@ public final class EventDispatcher extends JNIObject { * @param callback Optional object for callbacks from events. */ @AnyThread - public void dispatch(final String type, final GeckoBundle message, - final EventCallback callback) { + private void dispatch(final String type, final GeckoBundle message, + final EventCallback callback) { final boolean isGeckoReady; synchronized (this) { isGeckoReady = isReadyForDispatchingToGecko(); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ContentBlockingController.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ContentBlockingController.java index e1bd16faf1ad..3c02c458b931 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ContentBlockingController.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ContentBlockingController.java @@ -132,16 +132,27 @@ public class ContentBlockingController { */ @UiThread public @NonNull GeckoResult checkException(final @NonNull GeckoSession session) { - final CallbackResult result = new CallbackResult() { - @Override - public void sendSuccess(final Object value) { - complete((Boolean) value); - } - }; final GeckoBundle msg = new GeckoBundle(1); msg.putString("sessionId", session.getId()); - EventDispatcher.getInstance().dispatch("ContentBlocking:CheckException", msg, result); - return result; + return EventDispatcher.getInstance() + .queryBoolean("ContentBlocking:CheckException", msg); + } + + private List exceptionListFromBundle(final GeckoBundle value) { + final String[] principals = value.getStringArray("principals"); + final String[] uris = value.getStringArray("uris"); + + if (principals == null || uris == null) { + throw new RuntimeException("Received invalid content blocking exception list"); + } + + final ArrayList res = new ArrayList<>(principals.length); + + for (int i = 0; i < principals.length; i++) { + res.add(new ContentBlockingException(principals[i], uris[i])); + } + + return Collections.unmodifiableList(res); } /** @@ -152,28 +163,9 @@ public class ContentBlockingController { */ @UiThread public @NonNull GeckoResult> saveExceptionList() { - final CallbackResult> result = new CallbackResult>() { - @Override - public void sendSuccess(final Object value) { - final String[] principals = ((GeckoBundle) value).getStringArray("principals"); - final String[] uris = ((GeckoBundle) value).getStringArray("uris"); - - if (principals == null || uris == null) { - completeExceptionally(new RuntimeException("Received invalid content blocking exception list")); - return; - } - - final ArrayList res = new ArrayList(principals.length); - - for (int i = 0; i < principals.length; i++) { - res.add(new ContentBlockingException(principals[i], uris[i])); - } - - complete(Collections.unmodifiableList(res)); - } - }; - EventDispatcher.getInstance().dispatch("ContentBlocking:SaveList", null, result); - return result; + return EventDispatcher.getInstance() + .queryBundle("ContentBlocking:SaveList") + .map(this::exceptionListFromBundle); } /** @@ -401,6 +393,15 @@ public class ContentBlockingController { } } + private List logFromBundle(final GeckoBundle value) { + final GeckoBundle[] bundles = value.getBundleArray("log"); + final ArrayList logArray = new ArrayList<>(bundles.length); + for (GeckoBundle b : bundles) { + logArray.add(new LogEntry(b)); + } + return Collections.unmodifiableList(logArray); + } + /** * Get a log of all content blocking information for the site currently loaded by the * supplied {@link GeckoSession}. @@ -411,18 +412,8 @@ public class ContentBlockingController { */ @UiThread public @NonNull GeckoResult> getLog(final @NonNull GeckoSession session) { - final CallbackResult> result = new CallbackResult>() { - @Override - public void sendSuccess(final Object value) { - final GeckoBundle[] bundles = ((GeckoBundle) value).getBundleArray("log"); - final ArrayList logArray = new ArrayList(bundles.length); - for (GeckoBundle b : bundles) { - logArray.add(new LogEntry(b)); - } - complete(Collections.unmodifiableList(logArray)); - } - }; - session.getEventDispatcher().dispatch("ContentBlocking:RequestLog", null, result); - return result; + return session.getEventDispatcher() + .queryBundle("ContentBlocking:RequestLog") + .map(this::logFromBundle); } } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java index a4176954ebc9..0153b0edd4e9 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java @@ -953,14 +953,7 @@ public class GeckoSession { */ @AnyThread public @NonNull GeckoResult getUserAgent() { - final CallbackResult result = new CallbackResult() { - @Override - public void sendSuccess(final Object value) { - complete((String) value); - } - }; - mEventDispatcher.dispatch("GeckoView:GetUserAgent", null, result); - return result; + return mEventDispatcher.queryString("GeckoView:GetUserAgent"); } /** diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionFinder.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionFinder.java index 31e7f68c432b..52dc80f6eb61 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionFinder.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionFinder.java @@ -85,14 +85,8 @@ public final class SessionFinder { bundle.putString("searchString", searchString); addFlagsToBundle(flags, bundle); - final CallbackResult result = new CallbackResult() { - @Override - public void sendSuccess(final Object response) { - complete(new FinderResult((GeckoBundle) response)); - } - }; - mDispatcher.dispatch("GeckoView:FindInPage", bundle, result); - return result; + return mDispatcher.queryBundle("GeckoView:FindInPage", bundle) + .map(response -> new FinderResult(response)); } /** diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/StorageController.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/StorageController.java index 877c6bdde8c0..a9342a923f3d 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/StorageController.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/StorageController.java @@ -112,20 +112,11 @@ public final class StorageController { @AnyThread public @NonNull GeckoResult clearData( final @StorageControllerClearFlags long flags) { - final CallbackResult result = new CallbackResult() { - @Override - public void sendSuccess(final Object response) { - complete(null); - } - }; - final GeckoBundle bundle = new GeckoBundle(1); bundle.putLong("flags", flags); - EventDispatcher.getInstance().dispatch( - "GeckoView:ClearData", bundle, result); - - return result; + return EventDispatcher.getInstance() + .queryVoid("GeckoView:ClearData", bundle); } /** @@ -146,21 +137,12 @@ public final class StorageController { public @NonNull GeckoResult clearDataFromHost( final @NonNull String host, final @StorageControllerClearFlags long flags) { - final CallbackResult result = new CallbackResult() { - @Override - public void sendSuccess(final Object response) { - complete(null); - } - }; - final GeckoBundle bundle = new GeckoBundle(2); bundle.putString("host", host); bundle.putLong("flags", flags); - EventDispatcher.getInstance().dispatch( - "GeckoView:ClearHostData", bundle, result); - - return result; + return EventDispatcher.getInstance() + .queryVoid("GeckoView:ClearHostData", bundle); } /** diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtension.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtension.java index cfaecd222c1a..d62a3b17fc1c 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtension.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtension.java @@ -1216,6 +1216,13 @@ public class WebExtension { } } + /* package */ static WebExtension fromBundle(final GeckoBundle bundle) { + if (bundle == null) { + return null; + } + return new WebExtension(bundle.getBundle("extension")); + } + /** * Represents either a Browser Action or a Page Action from the * WebExtension API. @@ -1604,6 +1611,33 @@ public class WebExtension { protected ErrorCodes() {} } + /** These states should match gecko's AddonManager.STATE_* constants. */ + private static class StateCodes { + public static final int STATE_POSTPONED = 7; + public static final int STATE_CANCELED = 12; + } + + /* package */ static Throwable fromQueryException(final Throwable exception) { + final EventDispatcher.QueryException queryException = + (EventDispatcher.QueryException) exception; + final Object response = queryException.data; + if (response instanceof GeckoBundle + && ((GeckoBundle) response).containsKey("installError")) { + final GeckoBundle bundle = (GeckoBundle) response; + int errorCode = bundle.getInt("installError"); + final int installState = bundle.getInt("state"); + if (errorCode == 0 && installState == + StateCodes.STATE_CANCELED) { + errorCode = ErrorCodes.ERROR_USER_CANCELED; + } else if (errorCode == 0 && installState == StateCodes.STATE_POSTPONED) { + errorCode = ErrorCodes.ERROR_POSTPONED; + } + return new WebExtension.InstallException(errorCode); + } else { + return new Exception(response.toString()); + } + } + @Retention(RetentionPolicy.SOURCE) @IntDef(value = { ErrorCodes.ERROR_NETWORK_FAILURE, diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionController.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionController.java index c074047c32ac..72179dd0bdf9 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionController.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebExtensionController.java @@ -26,9 +26,6 @@ import java.util.Map; import java.util.Objects; import java.util.UUID; -import static org.mozilla.geckoview.WebExtension.InstallException.ErrorCodes.ERROR_POSTPONED; -import static org.mozilla.geckoview.WebExtension.InstallException.ErrorCodes.ERROR_USER_CANCELED; - public class WebExtensionController { private final static String LOGTAG = "WebExtension"; @@ -74,23 +71,23 @@ public class WebExtensionController { public GeckoResult get(final String id) { final WebExtension extension = mData.get(id); - if (extension == null) { - final WebExtensionResult result = new WebExtensionResult("extension"); - - final GeckoBundle bundle = new GeckoBundle(1); - bundle.putString("extensionId", id); - - EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:Get", - bundle, result); - - return result.then(ext -> { - mData.put(ext.id, ext); - mObserver.onNewExtension(ext); - return GeckoResult.fromValue(ext); - }); + if (extension != null) { + return GeckoResult.fromValue(extension); } - return GeckoResult.fromValue(extension); + final GeckoBundle bundle = new GeckoBundle(1); + bundle.putString("extensionId", id); + + final GeckoResult pending = EventDispatcher.getInstance() + .queryBundle("GeckoView:WebExtension:Get", bundle) + .map(WebExtension::fromBundle) + .map(ext -> { + mData.put(ext.id, ext); + mObserver.onNewExtension(ext); + return ext; + }); + + return pending; } public void setObserver(final Observer observer) { @@ -359,107 +356,21 @@ public class WebExtensionController { mDebuggerDelegate = delegate; } - private static class WebExtensionResult extends GeckoResult - implements EventCallback { - /** These states should match gecko's AddonManager.STATE_* constants. */ - private static class StateCodes { - public static final int STATE_POSTPONED = 7; - public static final int STATE_CANCELED = 12; - } - - private final String mFieldName; - - public WebExtensionResult(final String fieldName) { - mFieldName = fieldName; - } - - @Override - public void sendSuccess(final Object response) { - if (response == null) { - complete(null); - return; - } - final GeckoBundle bundle = (GeckoBundle) response; - complete(new WebExtension(bundle.getBundle(mFieldName))); - } - - @Override - public void sendError(final Object response) { - if (response instanceof GeckoBundle - && ((GeckoBundle) response).containsKey("installError")) { - final GeckoBundle bundle = (GeckoBundle) response; - int errorCode = bundle.getInt("installError"); - final int installState = bundle.getInt("state"); - if (errorCode == 0 && installState == StateCodes.STATE_CANCELED) { - errorCode = ERROR_USER_CANCELED; - } else if (errorCode == 0 && installState == StateCodes.STATE_POSTPONED) { - errorCode = ERROR_POSTPONED; - } - completeExceptionally(new WebExtension.InstallException(errorCode)); - } else { - completeExceptionally(new Exception(response.toString())); - } - } - } - - private static class WebExtensionInstallResult extends WebExtensionResult { - private static class InstallCanceller implements GeckoResult.CancellationDelegate { - private static class CancelResult extends GeckoResult - implements EventCallback { - @Override - public void sendSuccess(final Object response) { - final boolean result = ((GeckoBundle) response).getBoolean("cancelled"); - complete(result); - } - - @Override - public void sendError(final Object response) { - completeExceptionally(new Exception(response.toString())); - } - } - - private final String mInstallId; - private boolean mCancelled; - - public InstallCanceller(@NonNull final String aInstallId) { - mInstallId = aInstallId; - mCancelled = false; - } - - @Override - public GeckoResult cancel() { - CancelResult result = new CancelResult(); - - final GeckoBundle bundle = new GeckoBundle(1); - bundle.putString("installId", mInstallId); - - EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:CancelInstall", - bundle, result); - - return result.then(wasCancelled -> { - mCancelled = wasCancelled; - return GeckoResult.fromValue(wasCancelled); - }); - } - } - - /* package */ final @NonNull String installId; - - private final InstallCanceller mInstallCanceller; - - public WebExtensionInstallResult() { - super("extension"); + private static class InstallCanceller implements GeckoResult.CancellationDelegate { + public final String installId; + public InstallCanceller() { installId = UUID.randomUUID().toString(); - mInstallCanceller = new InstallCanceller(installId); - setCancellationDelegate(mInstallCanceller); } @Override - public void sendError(final Object response) { - if (!mInstallCanceller.mCancelled) { - super.sendError(response); - } + public GeckoResult cancel() { + final GeckoBundle bundle = new GeckoBundle(1); + bundle.putString("installId", installId); + + return EventDispatcher.getInstance() + .queryBundle("GeckoView:WebExtension:CancelInstall", bundle) + .map(response -> response.getBoolean("cancelled")); } } @@ -501,16 +412,18 @@ public class WebExtensionController { @NonNull @AnyThread public GeckoResult install(final @NonNull String uri) { - WebExtensionInstallResult result = new WebExtensionInstallResult(); + final InstallCanceller canceller = new InstallCanceller(); final GeckoBundle bundle = new GeckoBundle(2); bundle.putString("locationUri", uri); - bundle.putString("installId", result.installId); - EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:Install", - bundle, result); - return result.then(extension -> { - registerWebExtension(extension); - return GeckoResult.fromValue(extension); - }); + bundle.putString("installId", canceller.installId); + + final GeckoResult result = EventDispatcher.getInstance() + .queryBundle("GeckoView:WebExtension:Install", bundle) + .map(WebExtension::fromBundle, + WebExtension.InstallException::fromQueryException) + .map(this::registerWebExtension); + result.setCancellationDelegate(canceller); + return result; } /** @@ -526,20 +439,14 @@ public class WebExtensionController { public GeckoResult setAllowedInPrivateBrowsing( final @NonNull WebExtension extension, final boolean allowed) { - final WebExtensionController.WebExtensionResult result = - new WebExtensionController.WebExtensionResult("extension"); - final GeckoBundle bundle = new GeckoBundle(2); bundle.putString("extensionId", extension.id); bundle.putBoolean("allowed", allowed); - EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:SetPBAllowed", - bundle, result); - - return result.then(newExtension -> { - registerWebExtension(newExtension); - return GeckoResult.fromValue(newExtension); - }); + return EventDispatcher.getInstance() + .queryBundle("GeckoView:WebExtension:SetPBAllowed", bundle) + .map(WebExtension::fromBundle) + .map(this::registerWebExtension); } /** @@ -567,15 +474,14 @@ public class WebExtensionController { @NonNull @AnyThread public GeckoResult installBuiltIn(final @NonNull String uri) { - WebExtensionInstallResult result = new WebExtensionInstallResult(); final GeckoBundle bundle = new GeckoBundle(1); bundle.putString("locationUri", uri); - EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:InstallBuiltIn", - bundle, result); - return result.then(extension -> { - registerWebExtension(extension); - return GeckoResult.fromValue(extension); - }); + + return EventDispatcher.getInstance() + .queryBundle("GeckoView:WebExtension:InstallBuiltIn", bundle) + .map(WebExtension::fromBundle, + WebExtension.InstallException::fromQueryException) + .map(this::registerWebExtension); } /** @@ -604,16 +510,15 @@ public class WebExtensionController { @AnyThread public GeckoResult ensureBuiltIn(final @NonNull String uri, final @Nullable String id) { - WebExtensionInstallResult result = new WebExtensionInstallResult(); final GeckoBundle bundle = new GeckoBundle(2); bundle.putString("locationUri", uri); bundle.putString("webExtensionId", id); - EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:EnsureBuiltIn", - bundle, result); - return result.then(extension -> { - registerWebExtension(extension); - return GeckoResult.fromValue(extension); - }); + + return EventDispatcher.getInstance() + .queryBundle("GeckoView:WebExtension:EnsureBuiltIn", bundle) + .map(WebExtension::fromBundle, + WebExtension.InstallException::fromQueryException) + .map(this::registerWebExtension); } /** @@ -629,23 +534,12 @@ public class WebExtensionController { @NonNull @AnyThread public GeckoResult uninstall(final @NonNull WebExtension extension) { - final CallbackResult result = new CallbackResult() { - @Override - public void sendSuccess(final Object response) { - complete(null); - } - }; - final GeckoBundle bundle = new GeckoBundle(1); bundle.putString("webExtensionId", extension.id); - EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:Uninstall", - bundle, result); - - return result.then(success -> { - unregisterWebExtension(extension); - return GeckoResult.fromValue(success); - }); + return EventDispatcher.getInstance() + .queryBundle("GeckoView:WebExtension:Uninstall", bundle) + .accept(result -> unregisterWebExtension(extension)); } @Retention(RetentionPolicy.SOURCE) @@ -686,19 +580,14 @@ public class WebExtensionController { @NonNull public GeckoResult enable(final @NonNull WebExtension extension, final @EnableSources int source) { - final WebExtensionResult result = new WebExtensionResult("extension"); - final GeckoBundle bundle = new GeckoBundle(2); bundle.putString("webExtensionId", extension.id); bundle.putString("source", EnableSource.toString(source)); - EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:Enable", - bundle, result); - - return result.then(newExtension -> { - registerWebExtension(newExtension); - return GeckoResult.fromValue(newExtension); - }); + return EventDispatcher.getInstance() + .queryBundle("GeckoView:WebExtension:Enable", bundle) + .map(WebExtension::fromBundle) + .map(this::registerWebExtension); } /** @@ -714,19 +603,26 @@ public class WebExtensionController { @NonNull public GeckoResult disable(final @NonNull WebExtension extension, final @EnableSources int source) { - final WebExtensionResult result = new WebExtensionResult("extension"); - final GeckoBundle bundle = new GeckoBundle(2); bundle.putString("webExtensionId", extension.id); bundle.putString("source", EnableSource.toString(source)); - EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:Disable", - bundle, result); + return EventDispatcher.getInstance() + .queryBundle("GeckoView:WebExtension:Disable", bundle) + .map(WebExtension::fromBundle) + .map(this::registerWebExtension); + } - return result.then(newExtension -> { - registerWebExtension(newExtension); - return GeckoResult.fromValue(newExtension); - }); + private List listFromBundle(final GeckoBundle response) { + final GeckoBundle[] bundles = response.getBundleArray("extensions"); + final List list = new ArrayList<>(bundles.length); + + for (GeckoBundle bundle : bundles) { + final WebExtension extension = new WebExtension(bundle); + list.add(registerWebExtension(extension)); + } + + return list; } /** @@ -740,27 +636,9 @@ public class WebExtensionController { @AnyThread @NonNull public GeckoResult> list() { - final CallbackResult> result = new CallbackResult>() { - @Override - public void sendSuccess(final Object response) { - final GeckoBundle[] bundles = ((GeckoBundle) response) - .getBundleArray("extensions"); - final List list = new ArrayList<>(bundles.length); - - for (GeckoBundle bundle : bundles) { - final WebExtension extension = new WebExtension(bundle); - registerWebExtension(extension); - list.add(extension); - } - - complete(list); - } - }; - - EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:List", - null, result); - - return result; + return EventDispatcher.getInstance() + .queryBundle("GeckoView:WebExtension:List") + .map(this::listFromBundle); } /** @@ -788,20 +666,14 @@ public class WebExtensionController { @AnyThread @NonNull public GeckoResult update(final @NonNull WebExtension extension) { - final WebExtensionResult result = new WebExtensionResult("extension"); - final GeckoBundle bundle = new GeckoBundle(1); bundle.putString("webExtensionId", extension.id); - EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:Update", - bundle, result); - - return result.then(newExtension -> { - if (newExtension != null) { - registerWebExtension(newExtension); - } - return GeckoResult.fromValue(newExtension); - }); + return EventDispatcher.getInstance() + .queryBundle("GeckoView:WebExtension:Update", bundle) + .map(WebExtension::fromBundle, + WebExtension.InstallException::fromQueryException) + .map(this::registerWebExtension); } /* package */ WebExtensionController(final GeckoRuntime runtime) { @@ -814,9 +686,12 @@ public class WebExtensionController { mDownloads = new HashMap<>(); } - /* package */ void registerWebExtension(final WebExtension webExtension) { - webExtension.setDelegateController(new DelegateController(webExtension)); - mExtensions.update(webExtension.id, webExtension); + /* package */ WebExtension registerWebExtension(final WebExtension webExtension) { + if (webExtension != null) { + webExtension.setDelegateController(new DelegateController(webExtension)); + mExtensions.update(webExtension.id, webExtension); + } + return webExtension; } /* package */ void handleMessage(final String event, final GeckoBundle bundle,