mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 15:55:36 +00:00
Bug 1612097 - Add ability to cancel the GeckoResult returned by WebExtensionControll.install(BuiltIn); r=snorp,agi
Make the GeckoResult<WebExtension> returned by WebExtensionControll.install(BuiltIn) cancellable Differential Revision: https://phabricator.services.mozilla.com/D64953 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
25b913bc5c
commit
c5824ee72d
@ -88,6 +88,7 @@ GeckoViewStartup.prototype = {
|
||||
"GeckoView:WebExtension:Get",
|
||||
"GeckoView:WebExtension:Disable",
|
||||
"GeckoView:WebExtension:Enable",
|
||||
"GeckoView:WebExtension:CancelInstall",
|
||||
"GeckoView:WebExtension:Install",
|
||||
"GeckoView:WebExtension:InstallBuiltIn",
|
||||
"GeckoView:WebExtension:List",
|
||||
|
@ -6,6 +6,7 @@ package org.mozilla.geckoview.test
|
||||
|
||||
import androidx.test.filters.MediumTest
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import java.util.concurrent.CancellationException;
|
||||
import org.hamcrest.core.StringEndsWith.endsWith
|
||||
import org.hamcrest.core.IsEqual.equalTo
|
||||
import org.json.JSONObject
|
||||
@ -1338,6 +1339,33 @@ class WebExtensionTest : BaseSessionTest() {
|
||||
assertBodyBorderEqualTo("")
|
||||
}
|
||||
|
||||
@Test(expected = CancellationException::class)
|
||||
fun cancelInstall() {
|
||||
val install = controller.install("$TEST_ENDPOINT/stall/test.xpi")
|
||||
val cancel = sessionRule.waitForResult(install.cancel())
|
||||
assertTrue(cancel)
|
||||
|
||||
sessionRule.waitForResult(install)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cancelInstallFailsAfterInstalled() {
|
||||
sessionRule.delegateDuringNextWait(object : WebExtensionController.PromptDelegate {
|
||||
@AssertCalled
|
||||
override fun onInstallPrompt(extension: WebExtension): GeckoResult<AllowOrDeny> {
|
||||
return GeckoResult.fromValue(AllowOrDeny.ALLOW)
|
||||
}
|
||||
})
|
||||
|
||||
var install = controller.install("resource://android/assets/web_extensions/borderify.xpi");
|
||||
val borderify = sessionRule.waitForResult(install)
|
||||
|
||||
val cancel = sessionRule.waitForResult(install.cancel())
|
||||
assertFalse(cancel)
|
||||
|
||||
sessionRule.waitForResult(controller.uninstall(borderify))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updatePostpone() {
|
||||
sessionRule.setPrefsUntilTestEnd(mapOf(
|
||||
|
@ -18,6 +18,7 @@ import java.util.*
|
||||
class TestServer {
|
||||
private val server = AsyncHttpServer()
|
||||
private val assets: AssetManager
|
||||
private val stallingResponses = Vector<AsyncHttpServerResponse>()
|
||||
|
||||
constructor(context: Context) {
|
||||
assets = context.resources.assets
|
||||
@ -132,6 +133,25 @@ class TestServer {
|
||||
|
||||
response.end()
|
||||
}
|
||||
|
||||
server.get("/stall/.*") { _, response ->
|
||||
// keep trickling data for a long time (until we are stopped)
|
||||
stallingResponses.add(response)
|
||||
|
||||
val count = 100
|
||||
response.setContentType("InstallException")
|
||||
response.headers.set("Content-Length", "${count}")
|
||||
response.writeHead()
|
||||
|
||||
val payload = byteArrayOf(1)
|
||||
for (i in 1..count - 1) {
|
||||
response.write(ByteBufferList(payload))
|
||||
SystemClock.sleep(250)
|
||||
}
|
||||
|
||||
stallingResponses.remove(response)
|
||||
response.end()
|
||||
}
|
||||
}
|
||||
|
||||
fun start(port: Int) {
|
||||
@ -139,6 +159,9 @@ class TestServer {
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
for (response in stallingResponses) {
|
||||
response.end()
|
||||
}
|
||||
server.stop()
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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;
|
||||
@ -440,6 +441,67 @@ public class WebExtensionController {
|
||||
}
|
||||
}
|
||||
|
||||
private static class WebExtensionInstallResult extends WebExtensionResult {
|
||||
private static class InstallCanceller implements GeckoResult.CancellationDelegate {
|
||||
private static class CancelResult extends GeckoResult<Boolean>
|
||||
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<Boolean> 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");
|
||||
|
||||
installId = UUID.randomUUID().toString();
|
||||
mInstallCanceller = new InstallCanceller(installId);
|
||||
setCancellationDelegate(mInstallCanceller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendError(final Object response) {
|
||||
if (!mInstallCanceller.mCancelled) {
|
||||
super.sendError(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install an extension.
|
||||
*
|
||||
@ -478,14 +540,12 @@ public class WebExtensionController {
|
||||
@NonNull
|
||||
@AnyThread
|
||||
public GeckoResult<WebExtension> install(final @NonNull String uri) {
|
||||
final WebExtensionResult result = new WebExtensionResult("extension");
|
||||
|
||||
final GeckoBundle bundle = new GeckoBundle(1);
|
||||
WebExtensionInstallResult result = new WebExtensionInstallResult();
|
||||
final GeckoBundle bundle = new GeckoBundle(2);
|
||||
bundle.putString("locationUri", uri);
|
||||
|
||||
bundle.putString("installId", result.installId);
|
||||
EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:Install",
|
||||
bundle, result);
|
||||
|
||||
bundle, result);
|
||||
return result.then(extension -> {
|
||||
registerWebExtension(extension);
|
||||
return GeckoResult.fromValue(extension);
|
||||
@ -523,14 +583,12 @@ public class WebExtensionController {
|
||||
|
||||
// TODO: Bug 1601067 make public
|
||||
GeckoResult<WebExtension> installBuiltIn(final String uri) {
|
||||
final WebExtensionResult result = new WebExtensionResult("extension");
|
||||
|
||||
final GeckoBundle bundle = new GeckoBundle(1);
|
||||
WebExtensionInstallResult result = new WebExtensionInstallResult();
|
||||
final GeckoBundle bundle = new GeckoBundle(2);
|
||||
bundle.putString("locationUri", uri);
|
||||
|
||||
bundle.putString("installId", result.installId);
|
||||
EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:InstallBuiltIn",
|
||||
bundle, result);
|
||||
|
||||
bundle, result);
|
||||
return result.then(extension -> {
|
||||
registerWebExtension(extension);
|
||||
return GeckoResult.fromValue(extension);
|
||||
|
@ -308,8 +308,40 @@ function exportExtension(aAddon, aPermissions, aSourceURI) {
|
||||
}
|
||||
|
||||
class ExtensionInstallListener {
|
||||
constructor(aResolve) {
|
||||
this.resolve = aResolve;
|
||||
constructor(aResolve, aInstall, aInstallId) {
|
||||
this.install = aInstall;
|
||||
this.installId = aInstallId;
|
||||
this.resolve = result => {
|
||||
aResolve(result);
|
||||
EventDispatcher.instance.unregisterListener(this, [
|
||||
"GeckoView:WebExtension:CancelInstall",
|
||||
]);
|
||||
};
|
||||
EventDispatcher.instance.registerListener(this, [
|
||||
"GeckoView:WebExtension:CancelInstall",
|
||||
]);
|
||||
}
|
||||
|
||||
async onEvent(aEvent, aData, aCallback) {
|
||||
debug`onEvent ${aEvent} ${aData}`;
|
||||
|
||||
switch (aEvent) {
|
||||
case "GeckoView:WebExtension:CancelInstall": {
|
||||
const { installId } = aData;
|
||||
if (this.installId !== installId) {
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
try {
|
||||
this.install.cancel();
|
||||
cancelled = true;
|
||||
} catch (_) {
|
||||
// install may have already failed or been cancelled
|
||||
}
|
||||
aCallback.onSuccess({ cancelled });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDownloadCancelled(aInstall) {
|
||||
@ -534,14 +566,16 @@ var GeckoViewWebExtension = {
|
||||
return scope.extension;
|
||||
},
|
||||
|
||||
async installWebExtension(aUri) {
|
||||
async installWebExtension(aInstallId, aUri) {
|
||||
const install = await AddonManager.getInstallForURL(aUri.spec, {
|
||||
telemetryInfo: {
|
||||
source: "geckoview-app",
|
||||
},
|
||||
});
|
||||
const promise = new Promise(resolve => {
|
||||
install.addListener(new ExtensionInstallListener(resolve));
|
||||
install.addListener(
|
||||
new ExtensionInstallListener(resolve, install, aInstallId)
|
||||
);
|
||||
});
|
||||
|
||||
const systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
@ -794,14 +828,15 @@ var GeckoViewWebExtension = {
|
||||
}
|
||||
|
||||
case "GeckoView:WebExtension:Install": {
|
||||
const uri = Services.io.newURI(aData.locationUri);
|
||||
const { locationUri, installId } = aData;
|
||||
const uri = Services.io.newURI(locationUri);
|
||||
if (uri == null) {
|
||||
aCallback.onError(`Could not parse uri: ${uri}`);
|
||||
aCallback.onError(`Could not parse uri: ${locationUri}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.installWebExtension(uri);
|
||||
const result = await this.installWebExtension(installId, uri);
|
||||
if (result.extension) {
|
||||
aCallback.onSuccess(result);
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user