Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: Create some mechanism to notify plugins when the webview crashes #7713

Open
2 of 3 tasks
laine-hallot opened this issue Oct 11, 2024 · 3 comments
Open
2 of 3 tasks
Labels

Comments

@laine-hallot
Copy link

laine-hallot commented Oct 11, 2024

Description

It would be nice to have a way for a plugin to tell Capacitor to run some code when the webview crashes

Platforms

  • iOS
  • Android
  • Web

Request or proposed solution

Before getting into things I just want to say I'm not sure whether i should create this as an issue or as a discussion so if the latter is more appropriate let me know and I'll post it there instead. Anyways

This is a subject that's been brought up a few times already but the discussions surrounding it never lead to any actual changes in Capacitor's code

Our team wants to use it for logging and looking at prior issues/discussions that use case seems like a pretty common desire (especially on iOS).

I was planning on writing the code for this and making a PR but wanted to make this issue to check if the maintainers are actually interested in adding this feature and to get some input on what the implementation should look like.

Alternatives

For stuff like logging crashes you could always just create a way to opt out of the auto-restart behavior on iOS and let the whole app crash which should let you see a log for that in the App analytics but it seems like Apples policies might be a concern there. #2379 (comment)

Also I think there is a case for not wanting to let plugin developers hook into the something that runs on app crash since putting too much code there could slow things down and lead to crashes being even more annoying to end users.

Additional Information

Android solution

It seems like there's already sort of a way to run code when the webview crashes on android, by having your plugin add a listener for onRenderProcessGone to the bridge with addWebViewListener().

@Override
public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
super.onRenderProcessGone(view, detail);
boolean result = false;
List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
if (webViewListeners != null) {
for (WebViewListener listener : bridge.getWebViewListeners()) {
result = listener.onRenderProcessGone(view, detail) || result;
}
}
return result;
}

public Builder addWebViewListener(WebViewListener webViewListener) {
webViewListeners.add(webViewListener);
return this;
}

I while I haven't tried that particular route to get code to run on webview crash it seems like all the pieces are already there for it on android. Between that and the lack of an analogous webview.reload() call when the webview crashes on Android I think the bulk of the discussion here should be focused on iOS.

iOS

I haven't tried all of these but it seems like there's a couple ways we could get plugin code run when the webview restarts (both of them involve calling code from webViewWebContentProcessDidTerminate())

1. Adding to the CAPPlugin interface

This approach involves extending the CAPPlugin interface to include a new method that webViewWebContentProcessDidTerminate() can call, similar to shouldOverrideLoad().

/**
* Give the plugins a chance to take control when a URL is about to be loaded in the WebView.
* Returning true causes the WebView to abort loading the URL.
* Returning false causes the WebView to continue loading the URL.
* Returning nil will defer to the default Capacitor policy
*/
- (NSNumber* _Nullable)shouldOverrideLoad:(WKNavigationAction* _Nonnull)navigationAction;

// first, give plugins the chance to handle the decision
for pluginObject in bridge.plugins {
let plugin = pluginObject.value
let selector = NSSelectorFromString("shouldOverrideLoad:")
if plugin.responds(to: selector) {
let shouldOverrideLoad = plugin.shouldOverrideLoad(navigationAction)
if shouldOverrideLoad != nil {
if shouldOverrideLoad == true {
decisionHandler(.cancel)
return
} else if shouldOverrideLoad == false {
decisionHandler(.allow)
return
}
}
}
}

This is the only method I've actually tried so far and while it seems like it could maybe work the fact that it involves messing with the defenition of the CAPPlugin interface makes me want to avoid it to not risk screwing something up there.
I also can't seem to figure our how to override the Objective-C version of the method from the Swift plugin code but I think that's just me being bad at Swift.

2. The NotificationCenter and Observer apis

This route involves defining a new notification name in CAPNotifications

extension Notification.Name {
public static let capacitorOpenURL = Notification.Name(rawValue: "CapacitorOpenURLNotification")
public static let capacitorOpenUniversalLink = Notification.Name(rawValue: "CapacitorOpenUniversalLinkNotification")
public static let capacitorContinueActivity = Notification.Name(rawValue: "CapacitorContinueActivityNotification")
public static let capacitorDidRegisterForRemoteNotifications =
Notification.Name(rawValue: "CapacitorDidRegisterForRemoteNotificationsNotification")
public static let capacitorDidFailToRegisterForRemoteNotifications =
Notification.Name(rawValue: "CapacitorDidFailToRegisterForRemoteNotificationsNotification")
public static let capacitorDecidePolicyForNavigationAction =
Notification.Name(rawValue: "CapacitorDecidePolicyForNavigationActionNotification")
public static let capacitorStatusBarTapped = Notification.Name(rawValue: "CapacitorStatusBarTappedNotification")
}

and having plugins create an Observer for it so that their code gets run when webViewWebContentProcessDidTerminate() triggers the notification.

The main issue I think this approach has is that it seems like there's no guarantee that all Observers will get run before webView.reload() gets called. While that would be annoying in the case where you want to be sure your JS side code knows about the crash as soon as it starts I could also see it being desirable if there's a concerns over the webview restart getting delayed due to slow plugin code.

TL:DR

I want to make a PR to add this feature but need a some questions answered before I can do that:

  1. Do the maintainers even want this feature?
  2. Should this be a method that's part of the CAPPlugin interface or should it be an some kind of event that plugins can listen for?
  3. Should webView.reload() only get called once all plugin handlers for this complete?
@peitschie
Copy link

Just for some more background that I'm aware of...

I believe this is already supported on the Android side as part of #6946

See for tips on how to add a custom WebViewListener to subscribe: #2645

There's an open request to add a similar mechanism for iOS here: #7260 which is perhaps all that is needed to support your use-case here?

@iVanPan
Copy link

iVanPan commented Nov 28, 2024

@peitschie Hi, I would like to know how to add a custom WebViewListener inside a plugin. I tried to achieve this by using the addWebViewListener method of the Bridge in the load method of the plugin. However, it gets overridden by the Bridge.Builder.

@iVanPan
Copy link

iVanPan commented Nov 28, 2024

@peitschie Hi, I would like to know how to add a custom WebViewListener inside a plugin. I tried to achieve this by using the addWebViewListener method of the Bridge in the load method of the plugin. However, it gets overridden by the Bridge.Builder.

The issue was resolved by adding the WebViewListener in HandleOnStart

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants