@capgo/capacitor-webview-crash 8.1.10 → 8.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -1,24 +1,48 @@
|
|
|
1
1
|
# @capgo/capacitor-webview-crash
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Keep your Capacitor app alive when the WebView crashes: detect recovered crashes, restart dead WebViews natively, and recycle long-running WebViews before they run out of memory.
|
|
4
|
+
|
|
5
|
+
<a href="https://capgo.app/?ref=plugin_webview_crash">
|
|
4
6
|
<img
|
|
5
|
-
src="https://
|
|
6
|
-
alt="Capgo -
|
|
7
|
+
src="https://capgo.app/readme-banner.svg?repo=Cap-go/capacitor-webview-crash"
|
|
8
|
+
alt="Capgo - Build Capacitor apps with AI, ship updates instantly"
|
|
7
9
|
/>
|
|
8
10
|
</a>
|
|
9
11
|
|
|
10
12
|
<div align="center">
|
|
13
|
+
<p>
|
|
14
|
+
<b>Capgo</b>: push fixes to your Capacitor users in minutes, build signed iOS and Android apps without a Mac, and
|
|
15
|
+
roll back in one click.
|
|
16
|
+
</p>
|
|
11
17
|
<h2>
|
|
12
|
-
<a href="https://capgo.app/?ref=plugin_webview_crash"
|
|
18
|
+
<a href="https://capgo.app/register/?ref=plugin_webview_crash">➡️ Get started for free</a>
|
|
13
19
|
</h2>
|
|
14
|
-
<
|
|
20
|
+
<p>14-day unlimited free trial. No credit card required</p>
|
|
21
|
+
<p>
|
|
15
22
|
<a href="https://capgo.app/consulting/?ref=plugin_webview_crash">
|
|
16
|
-
Missing a feature? We
|
|
23
|
+
Missing a feature? We'll build the plugin for you 💪
|
|
17
24
|
</a>
|
|
18
|
-
</
|
|
25
|
+
</p>
|
|
19
26
|
</div>
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
> 🤖 **Building with an AI coding agent?** Install [Capgo skills](https://capgo.app/skills/?ref=plugin_webview_crash) so Claude Code, Codex, Cursor, Copilot, or Gemini CLI know how to install and use `@capgo/capacitor-webview-crash` in your Capacitor app: `npx skills add Cap-go/capgo-skills`. Then ask your agent: _"Add @capgo/capacitor-webview-crash to my Capacitor app and wire it up."_ [Agent setup guides](https://capgo.app/agents/?ref=plugin_webview_crash) · [Hosted MCP](https://capgo.app/docs/ai/mcp/?ref=plugin_webview_crash)
|
|
29
|
+
|
|
30
|
+
<p align="center">
|
|
31
|
+
<img
|
|
32
|
+
src="https://raw.githubusercontent.com/Cap-go/capacitor-webview-crash/main/assets/github-social-preview.png"
|
|
33
|
+
alt="@capgo/capacitor-webview-crash for Capacitor apps"
|
|
34
|
+
width="300"
|
|
35
|
+
/>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
## Key features
|
|
39
|
+
|
|
40
|
+
- **Crash recovery**: `restartOnCrash` restarts the WebView when its renderer process dies.
|
|
41
|
+
- **Recovery info**: `getPendingCrashInfo()` and `clearPendingCrashInfo()`, plus the `webViewRestoredAfterCrash` event.
|
|
42
|
+
- **Scheduled recycling**: `restartIntervalMs` or `restartCron` replaces long-running WebViews.
|
|
43
|
+
- **Manual restart**: `restartWebView()` creates a fresh WebView.
|
|
44
|
+
- **Testing**: `simulateCrashRecovery()` exercises your recovery flow.
|
|
45
|
+
- **Platforms**: iOS and Android. Web only supports the testing helpers.
|
|
22
46
|
|
|
23
47
|
## What It Does
|
|
24
48
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import Capacitor
|
|
2
2
|
import Foundation
|
|
3
|
-
import ObjectiveC.runtime
|
|
4
3
|
import UIKit
|
|
5
4
|
import WebKit
|
|
6
5
|
|
|
@@ -292,31 +291,98 @@ enum WebViewCrashStore {
|
|
|
292
291
|
}
|
|
293
292
|
}
|
|
294
293
|
|
|
295
|
-
enum
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
294
|
+
enum WebViewCrashNavigationDelegateInstaller {
|
|
295
|
+
/// Retains the proxy because `WKWebView.navigationDelegate` is weak.
|
|
296
|
+
private static var retainedProxy: WebViewCrashNavigationDelegateProxy?
|
|
297
|
+
|
|
298
|
+
/// Installs a typed `WKNavigationDelegate` proxy on the bridge web view.
|
|
299
|
+
///
|
|
300
|
+
/// Ordering: call after Capacitor assigns `navigationDelegate` (plugin `load()` or after
|
|
301
|
+
/// `loadWebView()`). If another plugin replaces `navigationDelegate` later, crash handling
|
|
302
|
+
/// stops until this installer runs again. Manual or periodic restarts re-install via
|
|
303
|
+
/// `recreateBridgeWebView()`.
|
|
304
|
+
static func installIfNeeded(on webView: WKWebView?) {
|
|
305
|
+
guard let webView else {
|
|
300
306
|
return
|
|
301
307
|
}
|
|
302
308
|
|
|
303
|
-
|
|
304
|
-
|
|
309
|
+
if webView.navigationDelegate is WebViewCrashNavigationDelegateProxy {
|
|
310
|
+
return
|
|
311
|
+
}
|
|
305
312
|
|
|
306
|
-
guard
|
|
307
|
-
let originalMethod = class_getInstanceMethod(WebViewDelegationHandler.self, originalSelector),
|
|
308
|
-
let swizzledMethod = class_getInstanceMethod(WebViewDelegationHandler.self, swizzledSelector)
|
|
309
|
-
else {
|
|
313
|
+
guard let handler = webView.navigationDelegate as? WebViewDelegationHandler else {
|
|
310
314
|
return
|
|
311
315
|
}
|
|
312
316
|
|
|
313
|
-
|
|
314
|
-
|
|
317
|
+
let proxy = WebViewCrashNavigationDelegateProxy(handler: handler)
|
|
318
|
+
retainedProxy = proxy
|
|
319
|
+
webView.navigationDelegate = proxy
|
|
315
320
|
}
|
|
316
321
|
}
|
|
317
322
|
|
|
318
|
-
|
|
319
|
-
|
|
323
|
+
final class WebViewCrashNavigationDelegateProxy: NSObject, WKNavigationDelegate {
|
|
324
|
+
private weak var handler: WebViewDelegationHandler?
|
|
325
|
+
|
|
326
|
+
init(handler: WebViewDelegationHandler) {
|
|
327
|
+
self.handler = handler
|
|
328
|
+
super.init()
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
|
332
|
+
handler?.webView(webView, didStartProvisionalNavigation: navigation)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
func webView(
|
|
336
|
+
_ webView: WKWebView,
|
|
337
|
+
requestMediaCapturePermissionFor origin: WKSecurityOrigin,
|
|
338
|
+
initiatedByFrame frame: WKFrameInfo,
|
|
339
|
+
type: WKMediaCaptureType,
|
|
340
|
+
decisionHandler: @escaping (WKPermissionDecision) -> Void
|
|
341
|
+
) {
|
|
342
|
+
handler?.webView(
|
|
343
|
+
webView,
|
|
344
|
+
requestMediaCapturePermissionFor: origin,
|
|
345
|
+
initiatedByFrame: frame,
|
|
346
|
+
type: type,
|
|
347
|
+
decisionHandler: decisionHandler
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
func webView(
|
|
352
|
+
_ webView: WKWebView,
|
|
353
|
+
requestDeviceOrientationAndMotionPermissionFor origin: WKSecurityOrigin,
|
|
354
|
+
initiatedByFrame frame: WKFrameInfo,
|
|
355
|
+
decisionHandler: @escaping (WKPermissionDecision) -> Void
|
|
356
|
+
) {
|
|
357
|
+
handler?.webView(
|
|
358
|
+
webView,
|
|
359
|
+
requestDeviceOrientationAndMotionPermissionFor: origin,
|
|
360
|
+
initiatedByFrame: frame,
|
|
361
|
+
decisionHandler: decisionHandler
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
func webView(
|
|
366
|
+
_ webView: WKWebView,
|
|
367
|
+
decidePolicyFor navigationAction: WKNavigationAction,
|
|
368
|
+
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
|
|
369
|
+
) {
|
|
370
|
+
handler?.webView(webView, decidePolicyFor: navigationAction, decisionHandler: decisionHandler)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
374
|
+
handler?.webView(webView, didFinish: navigation)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
|
378
|
+
handler?.webView(webView, didFail: navigation, withError: error)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
|
382
|
+
handler?.webView(webView, didFailProvisionalNavigation: navigation, withError: error)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
|
|
320
386
|
let crashInfo = WebViewCrashStore.buildCrashInfo(
|
|
321
387
|
platform: "ios",
|
|
322
388
|
reason: "webContentProcessDidTerminate",
|
|
@@ -330,17 +396,28 @@ private extension WebViewDelegationHandler {
|
|
|
330
396
|
return
|
|
331
397
|
}
|
|
332
398
|
|
|
333
|
-
let
|
|
334
|
-
self
|
|
399
|
+
let forwardToCapacitor: () -> Void = { [weak self] in
|
|
400
|
+
guard let handler = self?.handler else {
|
|
401
|
+
return
|
|
402
|
+
}
|
|
403
|
+
handler.webViewWebContentProcessDidTerminate(webView)
|
|
335
404
|
}
|
|
336
405
|
|
|
337
406
|
let delay = WebViewCrashRuntime.restartAfterCrashDelaySeconds
|
|
338
407
|
if delay > 0 {
|
|
339
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute:
|
|
408
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: forwardToCapacitor)
|
|
340
409
|
} else {
|
|
341
|
-
|
|
410
|
+
forwardToCapacitor()
|
|
342
411
|
}
|
|
343
412
|
}
|
|
413
|
+
|
|
414
|
+
func webView(
|
|
415
|
+
_ webView: WKWebView,
|
|
416
|
+
didReceive challenge: URLAuthenticationChallenge,
|
|
417
|
+
completionHandler: @escaping @MainActor (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
|
|
418
|
+
) {
|
|
419
|
+
handler?.webView(webView, didReceive: challenge, completionHandler: completionHandler)
|
|
420
|
+
}
|
|
344
421
|
}
|
|
345
422
|
|
|
346
423
|
extension UIApplication.State {
|
|
@@ -20,7 +20,7 @@ public class WebViewCrashPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
20
20
|
override public func load() {
|
|
21
21
|
restartOptions = WebViewCrashRestartOptions(config: getConfig())
|
|
22
22
|
WebViewCrashRuntime.update(options: restartOptions)
|
|
23
|
-
|
|
23
|
+
WebViewCrashNavigationDelegateInstaller.installIfNeeded(on: bridge?.webView)
|
|
24
24
|
schedulePeriodicRestart()
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -147,6 +147,7 @@ public class WebViewCrashPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
147
147
|
viewController.webView?.uiDelegate = nil
|
|
148
148
|
viewController.loadView()
|
|
149
149
|
viewController.loadWebView()
|
|
150
|
+
WebViewCrashNavigationDelegateInstaller.installIfNeeded(on: viewController.webView)
|
|
150
151
|
return true
|
|
151
152
|
}
|
|
152
153
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capgo/capacitor-webview-crash",
|
|
3
|
-
"version": "8.1.
|
|
3
|
+
"version": "8.1.12",
|
|
4
4
|
"description": "Capacitor plugin for detecting WebView crash recovery and restarting long-running WebViews natively.",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|