capacitor icon indicating copy to clipboard operation
capacitor copied to clipboard

feat: Notify JS-layer when webview crashed

Open sandstrom opened this issue 4 years ago • 16 comments

Feature Request

It would be great if the JS-layer (Capacitor app) could know that the underlying web view crashed and subsequently restarted. Right now there is nothing to discern these from a regular app startup and users won't be aware that data was lost.

More concretely, an issue that occur frequently is that we take a photograph via an <input capture> element and do on-the-fly resize using JS and canvas, and the web view crashes, causing the page to reload. But for the user this isn't obvious, it's just a short flash when the web view reloads, and they don't know that the photograph failed and they've just lost data.

If we could listen to these events we could show a warning to the user.

This is the code on iOS today:

https://github.com/ionic-team/capacitor/blob/0a25cce21f935c0d18cdeb9b0e04a9b6145f2072/ios/Capacitor/Capacitor/CAPBridgeViewController.swift#L279-L281

Platform Support Requested

  • Android
  • iOS

Describe Preferred Solution

There are multiple solutions, but one could be a listener similar to appRestoredResult , that would let us know that the app restored after a crash.

We'd then have the ability to show a warning to the user.

Additional Context

This related issue about the web view crashing when taking photos: https://github.com/ionic-team/capacitor/issues/2265

sandstrom avatar Jan 28 '20 13:01 sandstrom

Since appRestoredResult is Android only, we could just try to implement it for iOS too, but that won't work for your user case as it passes info from the last plugin call and you are not using a plugin.

jcesarmobile avatar Jan 28 '20 14:01 jcesarmobile

@jcesarmobile Yeah, I looked at that event too, but seems like it has slightly different semantics/meaning. It's more of a switching thing.

Maybe a separate event for crashes would make more sense, e.g. restartedAfterCrash or similar.

sandstrom avatar Jan 29 '20 09:01 sandstrom

Would it be so crazy to crash the app if webViewWebContentProcessDidTerminate was called? I think I would prefer the app to quit immediately and obviously, rather than data disappearing silently. Perhaps it could be a configuration option.

diachedelic avatar Aug 20 '20 03:08 diachedelic

Yeah, it’s crazy to crash an iOS app, apple will reject apps if they crash

jcesarmobile avatar Aug 20 '20 09:08 jcesarmobile

In that case I support @sandstrom 's idea of something like a webViewWebContentProcessDidTerminate event. In my case the webview was crashing intermittently due to a memory leak, which was fairly trivial to fix, but painful to diagnose due to the absence of any diagnostic.

diachedelic avatar Aug 20 '20 10:08 diachedelic

Just another though – silently restarting the webview can leave plugins in an inconsistent state. For example, if you have called Geolocation.watchPosition, and the webview crashes and reloads, there is no way to halt location updates other than manually killing and restarting the app.

I have been running Capacitor in development with the below modification for months now, and I would love to see this behaviour behind a configuration option.

diff --git a/CAPBridgeViewController.swift b/CAPBridgeViewController.swift
index 431c46ae..fb1c8bdd 100644
--- a/CAPBridgeViewController.swift
+++ b/CAPBridgeViewController.swift
@@ -344,7 +344,7 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr
   }
 
   public func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
-    webView.reload()
+    fatalError("webViewWebContentProcessDidTerminate")
   }

diachedelic avatar Nov 23 '20 00:11 diachedelic

Does this work?

public func webViewWebContentProcessDidTerminate(_ webView: WKWebView) { 
   webView.reload() 
 } 

dodomui avatar Jan 20 '22 14:01 dodomui

I believe that is the default behaviour...

diachedelic avatar Jan 20 '22 22:01 diachedelic

@diachedelic cannot find this function at Capacitor 3.3.4 @capacitor/ios/Capacitor/Capacitor/CapacitorBridge.swift

dodomui avatar Jan 21 '22 02:01 dodomui

Oh wow. So what happens now when the webview crashes on iOS?

diachedelic avatar Jan 25 '22 11:01 diachedelic

Not sure what happens now, but it appears the reload was just added back in https://github.com/ionic-team/capacitor/pull/5391

I'm glad it was added back as we've had issues where the OS kills the WebView process and we need to recover (JetSam or what-have-you).

We plan to very soon add logging to Sentry when it occurs so we can learn more about it happening in production and make better informed decisions on our technical approaches. That's why I'm interested in this issue. Not sure exactly what we'll do at the moment to capture the automatic reload event but I have some not-amazing ideas to try out. 🙂

KevinKelchen avatar Jan 28 '22 21:01 KevinKelchen

We have found the iOS WebView to be very sensitive to memory usage. It crashes quite often on startup, possibly due to this bug: https://bugs.webkit.org/show_bug.cgi?id=212790. If you have any memory leaks in the WebView, it will eventually be terminated. WebKit unfortunately has some nasty memory leaks associated with the <video> and <canvas> elements.

In general, an automatic reload is necessary in production. But I would prefer a crash in development. And ideally, we should be able to override the default behaviour, so we can maintain the integrity of the app.

diachedelic avatar Jan 28 '22 23:01 diachedelic

Yep, it can be rough.

Using more resource/memory-intensive technologies like WebGL (used in modern map control JS libraries) can exacerbate it as well. We'd like to embrace that technology but are held back.

KevinKelchen avatar Jan 29 '22 21:01 KevinKelchen

Yes! We need a hook in as well to know when this happens. Maybe having a event that I can hook into so I can "embrace the crash" and re-hydrate my application to where they left off? iOS is not getting any better with new devices. It seems the govenator is set with 5 year old specs in mind :(

briandpeterson avatar Mar 04 '22 13:03 briandpeterson

I posted a Q&A Discussion item related to detecting the involuntary reload for anyone who's curious.

It also summarizes our approach to informing the JS layer about the reload (albeit not with an event).

KevinKelchen avatar Mar 08 '22 22:03 KevinKelchen

We're also running into the webview crash when switching back to our backgrounded app after using an intensive app (such as a game). We suspect it's due to low RAM.

We've found that for our Angular applicationwebView.reload() causes it to run in an inconsistent state. A normal reload is fine, but after a webView.reload() there are a number of strange JavaScript related issues including issues with zone.js, routing and Angular lifecycle events not firing.

Interestingly, webView.load(currentUrl) seems to return the application in a more stable state, but we're still left with abandoned listeners etc as mentioned by https://github.com/ionic-team/capacitor/issues/2379#issuecomment-731876985

rossholdway avatar Mar 25 '22 14:03 rossholdway

@rossholdway what you are describing in the last comment here is exactly what we are currently experiencing. We have built an Ionic app that crashes when coming back from the background mode after a heavy load in a separate app like the camera or a game. So far we are not able to correctly restart our application to be in a working state again.

Therefore it would be very interesting to know, if you have been able to solve this issue?

Eric-Savvi avatar Jun 16 '23 13:06 Eric-Savvi

Just as a point of interest, I have a patch up for Android at least that works to expose this crash event through the plugin interfaces. Hypothetically, it should be possible to build a plugin that catches and restarts the app when this case is detected: https://github.com/ionic-team/capacitor/pull/6416

peitschie avatar Jun 17 '23 00:06 peitschie