Bug 1614295 - Provide a way for apps to know when an extension is installed. r=snorp,ochameau,esawin

This patch adds a `onExtensionListUpdated` method to `DebuggerDelegate` which
is called whenever devtools install a new extension.

This method provides an opportunity for apps to refresh the list of installed
extensions and sets appropriate delegates so that the new extension is
correctly recognized.

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Agi Sferro 2020-02-20 19:25:31 +00:00
Родитель 6360b24e80
Коммит b1c5704faa
6 изменённых файлов: 97 добавлений и 14 удалений

Просмотреть файл

@ -8,6 +8,7 @@ const { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
const protocol = require("devtools/shared/protocol");
const { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
const { addonsSpec } = require("devtools/shared/specs/addon/addons");
const { Services } = require("resource://gre/modules/Services.jsm");
// This actor is not used by DevTools, but is relied on externally by
// webext-run and the Firefox VS-Code plugin. see bug #1578108
@ -26,6 +27,8 @@ const AddonsActor = protocol.ActorClassWithSpec(addonsSpec, {
throw new Error(`Could not install add-on at '${addonPath}': ${error}`);
}
Services.obs.notifyObservers(null, "devtools-installed-addon", addon.id);
// TODO: once the add-on actor has been refactored to use
// protocol.js, we could return it directly.
// return new AddonTargetActor(this.conn, addon);

Просмотреть файл

@ -1598,6 +1598,7 @@ package org.mozilla.geckoview {
method @UiThread @Nullable public WebExtensionController.TabDelegate getTabDelegate();
method @NonNull @AnyThread public GeckoResult<WebExtension> install(@NonNull String);
method @AnyThread @NonNull public GeckoResult<List<WebExtension>> list();
method @UiThread public void setDebuggerDelegate(@NonNull WebExtensionController.DebuggerDelegate);
method @UiThread public void setPromptDelegate(@Nullable WebExtensionController.PromptDelegate);
method @AnyThread public void setTabActive(@NonNull GeckoSession, boolean);
method @UiThread public void setTabDelegate(@Nullable WebExtensionController.TabDelegate);
@ -1605,6 +1606,10 @@ package org.mozilla.geckoview {
method @AnyThread @NonNull public GeckoResult<WebExtension> update(@NonNull WebExtension);
}
public static interface WebExtensionController.DebuggerDelegate {
method @UiThread default public void onExtensionListUpdated();
}
public static class WebExtensionController.EnableSource {
ctor public EnableSource();
field public static final int APP = 2;

Просмотреть файл

@ -27,6 +27,7 @@ import static org.mozilla.geckoview.WebExtension.InstallException.ErrorCodes.ERR
public class WebExtensionController {
private final static String LOGTAG = "WebExtension";
private DebuggerDelegate mDebuggerDelegate;
private PromptDelegate mPromptDelegate;
private final WebExtension.Listener mListener;
@ -291,6 +292,23 @@ public class WebExtensionController {
} */
}
public interface DebuggerDelegate {
/**
* Called whenever the list of installed extensions has been modified using the debugger
* with tools like web-ext.
*
* This is intended as an opportunity to refresh the list of installed extensions using
* {@link WebExtensionController#list} and to set delegates on the new {@link WebExtension}
* objects, e.g. using {@link WebExtension#setActionDelegate} and
* {@link WebExtension#setMessageDelegate}.
*
* @see <a href="https://extensionworkshop.com/documentation/develop/getting-started-with-web-ext">
* Getting started with web-ext</a>
*/
@UiThread
default void onExtensionListUpdated() {}
}
/**
* @return the current {@link PromptDelegate} instance.
* @see PromptDelegate
@ -328,6 +346,29 @@ public class WebExtensionController {
mPromptDelegate = delegate;
}
/**
* Set the {@link DebuggerDelegate} for this instance. This delegate will receive updates
* about extension changes using developer tools.
*
* @param delegate the Delegate instance
*/
@UiThread
public void setDebuggerDelegate(final @NonNull DebuggerDelegate delegate) {
if (delegate == null && mDebuggerDelegate != null) {
EventDispatcher.getInstance().unregisterUiThreadListener(
mInternals,
"GeckoView:WebExtension:DebuggerListUpdated"
);
} else if (delegate != null && mDebuggerDelegate == null) {
EventDispatcher.getInstance().registerUiThreadListener(
mInternals,
"GeckoView:WebExtension:DebuggerListUpdated"
);
}
mDebuggerDelegate = delegate;
}
private static class WebExtensionResult extends GeckoResult<WebExtension>
implements EventCallback {
/** These states should match gecko's AddonManager.STATE_* constants. */
@ -663,6 +704,11 @@ public class WebExtensionController {
} else if ("GeckoView:WebExtension:UpdatePrompt".equals(event)) {
updatePrompt(bundle, callback);
return;
} else if ("GeckoView:WebExtension:DebuggerListUpdated".equals(event)) {
if (mDebuggerDelegate != null) {
mDebuggerDelegate.onExtensionListUpdated();
}
return;
}
final String nativeApp = bundle.getString("nativeApp");

Просмотреть файл

@ -20,8 +20,12 @@ exclude: true
- ⚠️ Move [`GeckoSessionSettings.Builder#useMultiprocess`] to
[`GeckoRuntimeSettings.Builder#useMultiprocess`][75.1]. Multiprocess state is
no longer determined per session.
- Added [`DebuggerDelegate#onExtensionListUpdated`][75.2] to notify that a temporary
extension has been installed by the debugger.
([bug 1614295]({{bugzilla}}1614295))
[75.1]: {{javadoc_uri}}/GeckoRuntimeSettings.Builder.html#useMultiprocess-boolean-
[75.2]: {{javadoc_uri}}/WebExtensionController.DebuggerDelegate.html#onExtensionListUpdated--
## v74
- Added [`WebExtensionController.enable`][74.1] and [`disable`][74.2] to

Просмотреть файл

@ -94,12 +94,14 @@ interface BrowserActionDelegate {
class WebExtensionManager implements WebExtension.ActionDelegate,
WebExtensionController.PromptDelegate,
WebExtensionController.DebuggerDelegate,
TabSessionManager.TabObserver {
public WebExtension extension;
private LruCache<WebExtension.Icon, Bitmap> mBitmapCache = new LruCache<>(5);
private GeckoRuntime mRuntime;
private WebExtension.Action mDefaultAction;
private TabSessionManager mTabManager;
private WeakReference<BrowserActionDelegate> mActionDelegate;
@ -109,6 +111,11 @@ class WebExtensionManager implements WebExtension.ActionDelegate,
return GeckoResult.fromValue(AllowOrDeny.ALLOW);
}
@Override
public void onExtensionListUpdated() {
refreshExtensionList();
}
// We only support either one browserAction or one pageAction
private void onAction(final WebExtension extension, final GeckoSession session,
final WebExtension.Action action) {
@ -244,12 +251,12 @@ class WebExtensionManager implements WebExtension.ActionDelegate,
}
}
public GeckoResult<Void> unregisterExtension(TabSessionManager tabManager) {
public GeckoResult<Void> unregisterExtension() {
if (extension == null) {
return GeckoResult.fromValue(null);
}
tabManager.unregisterWebExtension();
mTabManager.unregisterWebExtension();
return mRuntime.getWebExtensionController().uninstall(extension).accept((unused) -> {
extension = null;
@ -258,22 +265,24 @@ class WebExtensionManager implements WebExtension.ActionDelegate,
});
}
public void registerExtension(WebExtension extension,
TabSessionManager tabManager) {
public void registerExtension(WebExtension extension) {
extension.setActionDelegate(this);
tabManager.setWebExtensionActionDelegate(extension, this);
mTabManager.setWebExtensionActionDelegate(extension, this);
this.extension = extension;
}
private void refreshExtensionList() {
mRuntime.getWebExtensionController()
.list().accept(extensions -> {
for (final WebExtension extension : extensions) {
registerExtension(extension);
}
});
}
public WebExtensionManager(GeckoRuntime runtime,
TabSessionManager tabManager) {
runtime.getWebExtensionController()
.list().accept(extensions -> {
for (final WebExtension extension : extensions) {
registerExtension(extension, tabManager);
}
});
mTabManager = tabManager;
mRuntime = runtime;
}
}
@ -415,6 +424,8 @@ public class GeckoViewActivity
sExtensionManager = new WebExtensionManager(sGeckoRuntime, mTabSessionManager);
mTabSessionManager.setTabObserver(sExtensionManager);
sGeckoRuntime.getWebExtensionController().setDebuggerDelegate(sExtensionManager);
// `getSystemService` call requires API level 23
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
sGeckoRuntime.setWebNotificationDelegate(new WebNotificationDelegate() {
@ -797,12 +808,12 @@ public class GeckoViewActivity
setPopupVisibility(false);
mPopupView = null;
mPopupSession = null;
sExtensionManager.unregisterExtension(mTabSessionManager).then(unused -> {
sExtensionManager.unregisterExtension().then(unused -> {
final WebExtensionController controller = sGeckoRuntime.getWebExtensionController();
controller.setPromptDelegate(sExtensionManager);
return controller.install(uri);
}).accept(extension ->
sExtensionManager.registerExtension(extension, mTabSessionManager));
sExtensionManager.registerExtension(extension));
});
builder.setNegativeButton(R.string.cancel, (dialog, which) -> {
// Nothing to do

Просмотреть файл

@ -438,6 +438,19 @@ async function updatePromptHandler(aInfo) {
}
var GeckoViewWebExtension = {
observe(aSubject, aTopic, aData) {
debug`observe ${aTopic}`;
switch (aTopic) {
case "devtools-installed-addon": {
EventDispatcher.instance.sendRequest({
type: "GeckoView:WebExtension:DebuggerListUpdated",
});
break;
}
}
},
async registerWebExtension(aId, aUri, allowContentMessaging, aCallback) {
const params = {
id: aId,
@ -836,3 +849,4 @@ GeckoViewWebExtension.extensionScopes = new Map();
GeckoViewWebExtension.browserActions = new WeakMap();
// WeakMap[Extension -> PageAction]
GeckoViewWebExtension.pageActions = new WeakMap();
Services.obs.addObserver(GeckoViewWebExtension, "devtools-installed-addon");