mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1819737 - Listen for extension process crashes and pass to new delegate methods r=geckoview-reviewers,willdurand,owlish,amejiamarmol
Differential Revision: https://phabricator.services.mozilla.com/D184071
This commit is contained in:
parent
86b2ace5b5
commit
14cca9efa4
@ -192,6 +192,7 @@ export class GeckoViewStartup {
|
||||
"GeckoView:WebExtension:SetPBAllowed",
|
||||
"GeckoView:WebExtension:Uninstall",
|
||||
"GeckoView:WebExtension:Update",
|
||||
"GeckoView:WebExtension:EnableProcessSpawning",
|
||||
],
|
||||
observers: [
|
||||
"devtools-installed-addon",
|
||||
|
@ -2491,6 +2491,7 @@ package org.mozilla.geckoview {
|
||||
method @Nullable @UiThread public WebExtension.Download createDownload(int);
|
||||
method @AnyThread @NonNull public GeckoResult<WebExtension> disable(@NonNull WebExtension, int);
|
||||
method @AnyThread @NonNull public GeckoResult<WebExtension> enable(@NonNull WebExtension, int);
|
||||
method @AnyThread public void enableExtensionProcessSpawning();
|
||||
method @AnyThread @NonNull public GeckoResult<WebExtension> ensureBuiltIn(@NonNull String, @Nullable String);
|
||||
method @Nullable @UiThread public WebExtensionController.PromptDelegate getPromptDelegate();
|
||||
method @AnyThread @NonNull public GeckoResult<WebExtension> install(@NonNull String);
|
||||
@ -2499,6 +2500,7 @@ package org.mozilla.geckoview {
|
||||
method @UiThread public void setAddonManagerDelegate(@Nullable WebExtensionController.AddonManagerDelegate);
|
||||
method @AnyThread @NonNull public GeckoResult<WebExtension> setAllowedInPrivateBrowsing(@NonNull WebExtension, boolean);
|
||||
method @UiThread public void setDebuggerDelegate(@NonNull WebExtensionController.DebuggerDelegate);
|
||||
method @UiThread public void setExtensionProcessDelegate(@Nullable WebExtensionController.ExtensionProcessDelegate);
|
||||
method @UiThread public void setPromptDelegate(@Nullable WebExtensionController.PromptDelegate);
|
||||
method @AnyThread public void setTabActive(@NonNull GeckoSession, boolean);
|
||||
method @AnyThread @NonNull public GeckoResult<Void> uninstall(@NonNull WebExtension);
|
||||
@ -2529,6 +2531,10 @@ package org.mozilla.geckoview {
|
||||
@Retention(value=RetentionPolicy.SOURCE) public static interface WebExtensionController.EnableSources {
|
||||
}
|
||||
|
||||
public static interface WebExtensionController.ExtensionProcessDelegate {
|
||||
method @UiThread default public void onDisabledProcessSpawning();
|
||||
}
|
||||
|
||||
@UiThread public static interface WebExtensionController.PromptDelegate {
|
||||
method @Nullable default public GeckoResult<AllowOrDeny> onInstallPrompt(@NonNull WebExtension);
|
||||
method @Nullable default public GeckoResult<AllowOrDeny> onOptionalPrompt(@NonNull WebExtension, @NonNull String[], @NonNull String[]);
|
||||
|
@ -3126,4 +3126,42 @@ class WebExtensionTest : BaseSessionTest() {
|
||||
equalTo(true),
|
||||
)
|
||||
}
|
||||
|
||||
fun extensionProcessCrash() {
|
||||
sessionRule.setPrefsUntilTestEnd(
|
||||
mapOf(
|
||||
"extensions.webextensions.remote" to true,
|
||||
"dom.ipc.keepProcessesAlive.extension" to 1,
|
||||
"xpinstall.signatures.required" to false,
|
||||
),
|
||||
)
|
||||
|
||||
sessionRule.delegateDuringNextWait(object : WebExtensionController.PromptDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun onInstallPrompt(extension: WebExtension): GeckoResult<AllowOrDeny>? {
|
||||
return GeckoResult.allow()
|
||||
}
|
||||
})
|
||||
|
||||
sessionRule.addExternalDelegateUntilTestEnd(
|
||||
WebExtensionController.ExtensionProcessDelegate::class,
|
||||
{ delegate -> controller.setExtensionProcessDelegate(delegate) },
|
||||
{ controller.setExtensionProcessDelegate(null) },
|
||||
object : WebExtensionController.ExtensionProcessDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun onDisabledProcessSpawning() {}
|
||||
},
|
||||
)
|
||||
|
||||
val borderify = sessionRule.waitForResult(
|
||||
controller.install("resource://android/assets/web_extensions/borderify.xpi"),
|
||||
)
|
||||
|
||||
val list = extensionsMap(sessionRule.waitForResult(controller.list()))
|
||||
assertTrue(list.containsKey(borderify.id))
|
||||
|
||||
mainSession.loadUri("about:crashextensions")
|
||||
|
||||
sessionRule.waitForResult(controller.uninstall(borderify))
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ public class WebExtensionController {
|
||||
private static final String LOGTAG = "WebExtension";
|
||||
|
||||
private AddonManagerDelegate mAddonManagerDelegate;
|
||||
private ExtensionProcessDelegate mExtensionProcessDelegate;
|
||||
private DebuggerDelegate mDebuggerDelegate;
|
||||
private PromptDelegate mPromptDelegate;
|
||||
private final WebExtension.Listener<WebExtension.TabDelegate> mListener;
|
||||
@ -393,6 +394,13 @@ public class WebExtensionController {
|
||||
default void onInstalled(final @NonNull WebExtension extension) {}
|
||||
}
|
||||
|
||||
/** This delegate is used to notify of extension process state changes. */
|
||||
public interface ExtensionProcessDelegate {
|
||||
/** Called when extension process spawning has been disabled. */
|
||||
@UiThread
|
||||
default void onDisabledProcessSpawning() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current {@link PromptDelegate} instance.
|
||||
* @see PromptDelegate
|
||||
@ -488,6 +496,42 @@ public class WebExtensionController {
|
||||
mAddonManagerDelegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ExtensionProcessDelegate} for this instance. This delegate will be used to
|
||||
* notify when the state of the extension process has changed.
|
||||
*
|
||||
* @param delegate the extension process delegate
|
||||
* @see ExtensionProcessDelegate
|
||||
*/
|
||||
@UiThread
|
||||
public void setExtensionProcessDelegate(final @Nullable ExtensionProcessDelegate delegate) {
|
||||
if (delegate == null && mExtensionProcessDelegate != null) {
|
||||
EventDispatcher.getInstance()
|
||||
.unregisterUiThreadListener(
|
||||
mInternals, "GeckoView:WebExtension:OnDisabledProcessSpawning");
|
||||
} else if (delegate != null && mExtensionProcessDelegate == null) {
|
||||
EventDispatcher.getInstance()
|
||||
.registerUiThreadListener(mInternals, "GeckoView:WebExtension:OnDisabledProcessSpawning");
|
||||
}
|
||||
|
||||
mExtensionProcessDelegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable extension process spawning.
|
||||
*
|
||||
* <p>Extension process spawning can be disabled when the extension process has been killed or
|
||||
* crashed beyond the threshold set for Gecko. This method can be called to reset the threshold
|
||||
* count and allow the spawning again. If the threshold is reached again, {@link
|
||||
* ExtensionProcessDelegate#onDisabledProcessSpawning()} will still be called.
|
||||
*
|
||||
* @see ExtensionProcessDelegate#onDisabledProcessSpawning()
|
||||
*/
|
||||
@AnyThread
|
||||
public void enableExtensionProcessSpawning() {
|
||||
EventDispatcher.getInstance().dispatch("GeckoView:WebExtension:EnableProcessSpawning", null);
|
||||
}
|
||||
|
||||
private static class InstallCanceller implements GeckoResult.CancellationDelegate {
|
||||
public final String installId;
|
||||
|
||||
@ -860,6 +904,9 @@ public class WebExtensionController {
|
||||
} else if ("GeckoView:WebExtension:OnInstalled".equals(event)) {
|
||||
onInstalled(bundle);
|
||||
return;
|
||||
} else if ("GeckoView:WebExtension:OnDisabledProcessSpawning".equals(event)) {
|
||||
onDisabledProcessSpawning();
|
||||
return;
|
||||
}
|
||||
|
||||
extensionFromBundle(bundle)
|
||||
@ -1133,6 +1180,15 @@ public class WebExtensionController {
|
||||
mAddonManagerDelegate.onInstalled(extension);
|
||||
}
|
||||
|
||||
private void onDisabledProcessSpawning() {
|
||||
if (mExtensionProcessDelegate == null) {
|
||||
Log.e(LOGTAG, "no extension process delegate registered");
|
||||
return;
|
||||
}
|
||||
|
||||
mExtensionProcessDelegate.onDisabledProcessSpawning();
|
||||
}
|
||||
|
||||
@SuppressLint("WrongThread") // for .toGeckoBundle
|
||||
private void getSettings(final Message message, final WebExtension extension) {
|
||||
final WebExtension.BrowsingDataDelegate delegate = mListener.getBrowsingDataDelegate(extension);
|
||||
|
@ -25,6 +25,8 @@ exclude: true
|
||||
- Added [`getExperimentDelegate`][118.9] and [`setExperimentDelegate`][118.10] to the GeckoSession allow GeckoView to get and set the experiment delegate for the session. Default is to use the runtime delegate.
|
||||
- ⚠️ Deprecated [`onGetNimbusFeature`][115.5] by 122, please use `ExperimentDelegate.onGetExperimentFeature` instead.
|
||||
- Added [`GeckoRuntimeSettings.Builder.extensionsProcessEnabled`][118.11] for setting whether extensions process is enabled. ([bug 1843926]({{bugzilla}}1843926))
|
||||
- Added [`ExtensionProcessDelegate`][118.12] to allow GeckoView to notify disabling of the extension process spawning due to excessive crash/kill. ([bug 1819737]({{bugzilla}}1819737))
|
||||
- Added [`enableExtensionProcessSpawning`][118.13] for enabling the extension process spawning
|
||||
|
||||
[118.1]: {{javadoc_uri}}/ExperimentDelegate.html
|
||||
[118.2]: {{javadoc_uri}}/WebExtension.InstallException.ErrorCodes.html#ERROR_BLOCKLISTED
|
||||
@ -37,6 +39,8 @@ exclude: true
|
||||
[118.9]: {{javadoc_uri}}/GeckoSession.html#getExperimentDelegate()
|
||||
[118.10]: {{javadoc_uri}}/GeckoSession.html#setExperimentDelegate(org.mozilla.geckoview.ExperimentDelegate)
|
||||
[118.11]: {{javadoc_uri}}/GeckoRuntimeSettings.Builder.html#extensionsProcessEnabled(Boolean)
|
||||
[118.12]: {{javadoc_uri}}/WebExtensionController.ExtensionProcessDelegate.html
|
||||
[118.13]: {{javadoc_uri}}/WebExtensionController.html#enableExtensionProcessSpawning
|
||||
|
||||
## v116
|
||||
- Added [`GeckoSession.didPrintPageContent`][116.1] to included extra print status for a standard print and new `GeckoPrintException.ERROR_NO_PRINT_DELEGATE`
|
||||
@ -1417,4 +1421,4 @@ to allow adding gecko profiler markers.
|
||||
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport(android.content.Context,android.os.Bundle,java.lang.String)
|
||||
[65.25]: {{javadoc_uri}}/GeckoResult.html
|
||||
|
||||
[api-version]: 5743799f9da8509cfb6546e154f0e2d7989edc72
|
||||
[api-version]: 1b85a4f582547bb6ca01fad7bc36344a3179b28f
|
||||
|
@ -651,6 +651,45 @@ class AddonManagerListener {
|
||||
|
||||
new AddonManagerListener();
|
||||
|
||||
class ExtensionProcessListener {
|
||||
constructor() {
|
||||
this.onExtensionProcessCrash = this.onExtensionProcessCrash.bind(this);
|
||||
lazy.Management.on("extension-process-crash", this.onExtensionProcessCrash);
|
||||
|
||||
lazy.EventDispatcher.instance.registerListener(this, [
|
||||
"GeckoView:WebExtension:EnableProcessSpawning",
|
||||
]);
|
||||
}
|
||||
|
||||
async onEvent(aEvent, aData, aCallback) {
|
||||
debug`onEvent ${aEvent} ${aData}`;
|
||||
|
||||
switch (aEvent) {
|
||||
case "GeckoView:WebExtension:EnableProcessSpawning": {
|
||||
//TODO: Bug 1848426 - Add ability to reset threshold counters and allow for process spawning
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onExtensionProcessCrash(name, { childID, disabledProcessSpawning }) {
|
||||
debug`Extension process crash -> childID=${childID} disabledProcessSpawning=${disabledProcessSpawning}`;
|
||||
|
||||
// When an extension process has crashed too many times, Gecko will set `disabledProcessSpawning`
|
||||
// and no longer allow the extension process spawning. We only want to send a request
|
||||
// to the embedder when we are disabling the process spawning.
|
||||
// If process spawning is still enabled then we short circuit and don't notify the embedder.
|
||||
if (!disabledProcessSpawning) {
|
||||
return;
|
||||
}
|
||||
|
||||
lazy.EventDispatcher.instance.sendRequest({
|
||||
type: "GeckoView:WebExtension:OnDisabledProcessSpawning",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
new ExtensionProcessListener();
|
||||
|
||||
class MobileWindowTracker extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
Loading…
Reference in New Issue
Block a user