@capgo/capacitor-webview-crash 8.0.2 → 8.1.0

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.
@@ -1,5 +1,7 @@
1
1
  package app.capgo.webviewcrash;
2
2
 
3
+ import android.os.Handler;
4
+ import android.os.Looper;
3
5
  import android.webkit.RenderProcessGoneDetail;
4
6
  import android.webkit.WebView;
5
7
  import androidx.appcompat.app.AppCompatActivity;
@@ -9,12 +11,26 @@ import com.getcapacitor.PluginCall;
9
11
  import com.getcapacitor.PluginMethod;
10
12
  import com.getcapacitor.WebViewListener;
11
13
  import com.getcapacitor.annotation.CapacitorPlugin;
14
+ import java.util.HashSet;
15
+ import java.util.Set;
12
16
 
13
17
  @CapacitorPlugin(name = "WebViewCrash")
14
18
  public class WebViewCrashPlugin extends Plugin {
15
19
 
16
20
  private final WebViewCrash implementation = new WebViewCrash();
17
- private boolean didDispatchPendingEvent = false;
21
+ private final Set<String> dispatchedPendingEvents = new HashSet<>();
22
+ private WebViewCrash.RestartOptions restartOptions = new WebViewCrash.RestartOptions(true, 0, null, 0);
23
+ private Handler mainHandler;
24
+
25
+ private final Runnable periodicRestartRunnable = () -> {
26
+ WebView webView = bridge != null ? bridge.getWebView() : null;
27
+ String url = webView != null ? webView.getUrl() : null;
28
+ JSObject restartInfo = implementation.buildCrashInfo(WebViewCrash.PERIODIC_RESTART_REASON, url, null, null);
29
+
30
+ implementation.writePendingCrashInfo(getContext(), restartInfo);
31
+ dispatchedPendingEvents.clear();
32
+ restartWebView(0);
33
+ };
18
34
 
19
35
  private final WebViewListener webViewListener = new WebViewListener() {
20
36
  @Override
@@ -27,36 +43,37 @@ public class WebViewCrashPlugin extends Plugin {
27
43
  );
28
44
 
29
45
  implementation.writePendingCrashInfo(getContext(), crashInfo);
30
- didDispatchPendingEvent = false;
46
+ dispatchedPendingEvents.clear();
31
47
 
32
- AppCompatActivity currentActivity = getActivity();
33
- if (currentActivity != null) {
34
- currentActivity.runOnUiThread(() -> {
35
- bridge.reset();
36
- currentActivity.recreate();
37
- });
48
+ if (!restartOptions.restartOnCrash) {
49
+ return false;
38
50
  }
39
51
 
52
+ restartWebView(restartOptions.restartAfterCrashDelayMs);
40
53
  return true;
41
54
  }
42
55
  };
43
56
 
44
57
  @Override
45
58
  public void load() {
59
+ restartOptions = implementation.readRestartOptions(getConfig());
46
60
  bridge.addWebViewListener(webViewListener);
61
+ schedulePeriodicRestart();
47
62
  }
48
63
 
49
64
  @Override
50
65
  protected void handleOnDestroy() {
51
66
  bridge.removeWebViewListener(webViewListener);
67
+ cancelPeriodicRestart();
52
68
  }
53
69
 
54
70
  @Override
55
71
  public void addListener(PluginCall call) {
56
72
  super.addListener(call);
57
73
 
58
- if (WebViewCrash.EVENT_NAME.equals(call.getString("eventName"))) {
59
- dispatchPendingCrashIfNeeded();
74
+ String eventName = call.getString("eventName");
75
+ if (WebViewCrash.CRASH_EVENT_NAME.equals(eventName) || WebViewCrash.RESTART_EVENT_NAME.equals(eventName)) {
76
+ dispatchPendingCrashIfNeeded(eventName);
60
77
  }
61
78
  }
62
79
 
@@ -70,7 +87,7 @@ public class WebViewCrashPlugin extends Plugin {
70
87
  @PluginMethod
71
88
  public void clearPendingCrashInfo(PluginCall call) {
72
89
  implementation.clearPendingCrashInfo(getContext());
73
- didDispatchPendingEvent = false;
90
+ dispatchedPendingEvents.clear();
74
91
  call.resolve();
75
92
  }
76
93
 
@@ -80,25 +97,89 @@ public class WebViewCrashPlugin extends Plugin {
80
97
  JSObject crashInfo = implementation.buildCrashInfo("simulated", url, null, null);
81
98
 
82
99
  implementation.writePendingCrashInfo(getContext(), crashInfo);
83
- didDispatchPendingEvent = false;
84
- dispatchPendingCrashIfNeeded();
100
+ dispatchedPendingEvents.clear();
101
+ dispatchPendingCrashIfNeeded(WebViewCrash.CRASH_EVENT_NAME);
102
+ dispatchPendingCrashIfNeeded(WebViewCrash.RESTART_EVENT_NAME);
85
103
 
86
104
  JSObject result = new JSObject();
87
105
  result.put("value", crashInfo);
88
106
  call.resolve(result);
89
107
  }
90
108
 
91
- private void dispatchPendingCrashIfNeeded() {
92
- if (didDispatchPendingEvent) {
109
+ @PluginMethod
110
+ public void restartWebView(PluginCall call) {
111
+ WebView webView = bridge != null ? bridge.getWebView() : null;
112
+ String url = webView != null ? webView.getUrl() : null;
113
+ JSObject restartInfo = implementation.buildCrashInfo(WebViewCrash.MANUAL_RESTART_REASON, url, null, null);
114
+
115
+ implementation.writePendingCrashInfo(getContext(), restartInfo);
116
+ dispatchedPendingEvents.clear();
117
+
118
+ JSObject result = new JSObject();
119
+ result.put("value", restartInfo);
120
+ call.resolve(result);
121
+
122
+ restartWebView(0);
123
+ }
124
+
125
+ private void dispatchPendingCrashIfNeeded(String eventName) {
126
+ if (dispatchedPendingEvents.contains(eventName)) {
93
127
  return;
94
128
  }
95
129
 
96
130
  JSObject crashInfo = implementation.readPendingCrashInfo(getContext());
97
- if (crashInfo == null) {
131
+ if (crashInfo == null || !implementation.shouldDispatchEvent(eventName, crashInfo)) {
132
+ return;
133
+ }
134
+
135
+ dispatchedPendingEvents.add(eventName);
136
+ notifyListeners(eventName, crashInfo);
137
+ }
138
+
139
+ private void schedulePeriodicRestart() {
140
+ cancelPeriodicRestart();
141
+ Long delayMs = restartOptions.nextRestartDelayMs();
142
+ if (delayMs == null || delayMs <= 0) {
98
143
  return;
99
144
  }
100
145
 
101
- didDispatchPendingEvent = true;
102
- notifyListeners(WebViewCrash.EVENT_NAME, crashInfo);
146
+ getMainHandler().postDelayed(periodicRestartRunnable, delayMs);
147
+ }
148
+
149
+ private void cancelPeriodicRestart() {
150
+ if (mainHandler != null) {
151
+ mainHandler.removeCallbacks(periodicRestartRunnable);
152
+ }
153
+ }
154
+
155
+ private void restartWebView(int delayMs) {
156
+ Runnable restart = () -> {
157
+ AppCompatActivity currentActivity = getActivity();
158
+ if (currentActivity == null) {
159
+ schedulePeriodicRestart();
160
+ return;
161
+ }
162
+
163
+ currentActivity.runOnUiThread(() -> {
164
+ if (bridge != null) {
165
+ bridge.reset();
166
+ }
167
+ currentActivity.recreate();
168
+ });
169
+ };
170
+
171
+ if (delayMs > 0) {
172
+ getMainHandler().postDelayed(restart, delayMs);
173
+ } else {
174
+ getMainHandler().post(restart);
175
+ }
176
+ }
177
+
178
+ private Handler getMainHandler() {
179
+ if (mainHandler == null) {
180
+ mainHandler = new Handler(Looper.getMainLooper());
181
+ }
182
+
183
+ return mainHandler;
103
184
  }
104
185
  }
package/dist/docs.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "api": {
3
3
  "name": "WebViewCrashPlugin",
4
4
  "slug": "webviewcrashplugin",
5
- "docs": "Capacitor API for recovered WebView crash detection.",
5
+ "docs": "Capacitor API for recovered WebView crash and restart detection.",
6
6
  "tags": [],
7
7
  "methods": [
8
8
  {
@@ -11,7 +11,7 @@
11
11
  "parameters": [],
12
12
  "returns": "Promise<PendingCrashInfoResult>",
13
13
  "tags": [],
14
- "docs": "Returns the pending native crash marker, if one exists.",
14
+ "docs": "Returns the pending native crash or restart marker, if one exists.",
15
15
  "complexTypes": [
16
16
  "PendingCrashInfoResult"
17
17
  ],
@@ -23,7 +23,7 @@
23
23
  "parameters": [],
24
24
  "returns": "Promise<void>",
25
25
  "tags": [],
26
- "docs": "Clears the stored crash marker after the app has handled recovery.",
26
+ "docs": "Clears the stored marker after the app has handled recovery.",
27
27
  "complexTypes": [],
28
28
  "slug": "clearpendingcrashinfo"
29
29
  },
@@ -39,14 +39,26 @@
39
39
  ],
40
40
  "slug": "simulatecrashrecovery"
41
41
  },
42
+ {
43
+ "name": "restartWebView",
44
+ "signature": "() => Promise<PendingCrashInfoResult>",
45
+ "parameters": [],
46
+ "returns": "Promise<PendingCrashInfoResult>",
47
+ "tags": [],
48
+ "docs": "Stores a manual restart marker and asks native code to create a fresh WebView.\n\nOn Android this recreates the host Activity. On iOS this rebuilds the Capacitor bridge view so a new `WKWebView`\ninstance is created instead of reloading the current page.",
49
+ "complexTypes": [
50
+ "PendingCrashInfoResult"
51
+ ],
52
+ "slug": "restartwebview"
53
+ },
42
54
  {
43
55
  "name": "addListener",
44
- "signature": "(eventName: 'webViewRestoredAfterCrash', listenerFunc: (info: WebViewCrashInfo) => void) => Promise<PluginListenerHandle>",
56
+ "signature": "(eventName: 'webViewRestoredAfterCrash' | 'webViewRestoredAfterRestart', listenerFunc: (info: WebViewCrashInfo) => void) => Promise<PluginListenerHandle>",
45
57
  "parameters": [
46
58
  {
47
59
  "name": "eventName",
48
60
  "docs": "",
49
- "type": "'webViewRestoredAfterCrash'"
61
+ "type": "'webViewRestoredAfterCrash' | 'webViewRestoredAfterRestart'"
50
62
  },
51
63
  {
52
64
  "name": "listenerFunc",
@@ -56,12 +68,12 @@
56
68
  ],
57
69
  "returns": "Promise<PluginListenerHandle>",
58
70
  "tags": [],
59
- "docs": "Fires after a new JavaScript runtime attaches a listener and a crash marker is still pending.",
71
+ "docs": "Fires after a new JavaScript runtime attaches a listener and a matching marker is still pending.",
60
72
  "complexTypes": [
61
73
  "PluginListenerHandle",
62
74
  "WebViewCrashInfo"
63
75
  ],
64
- "slug": "addlistenerwebviewrestoredaftercrash-"
76
+ "slug": "addlistenerwebviewrestoredaftercrash--webviewrestoredafterrestart-"
65
77
  },
66
78
  {
67
79
  "name": "removeAllListeners",
@@ -80,14 +92,14 @@
80
92
  {
81
93
  "name": "PendingCrashInfoResult",
82
94
  "slug": "pendingcrashinforesult",
83
- "docs": "Pending crash marker returned to JavaScript.",
95
+ "docs": "Pending crash or restart marker returned to JavaScript.",
84
96
  "tags": [],
85
97
  "methods": [],
86
98
  "properties": [
87
99
  {
88
100
  "name": "value",
89
101
  "tags": [],
90
- "docs": "Stored crash metadata, or `null` when no marker is pending.",
102
+ "docs": "Stored crash or restart metadata, or `null` when no marker is pending.",
91
103
  "complexTypes": [
92
104
  "WebViewCrashInfo"
93
105
  ],
@@ -98,14 +110,14 @@
98
110
  {
99
111
  "name": "WebViewCrashInfo",
100
112
  "slug": "webviewcrashinfo",
101
- "docs": "Metadata captured natively after the previous WebView process died.",
113
+ "docs": "Metadata captured natively after the previous WebView process died or was restarted.",
102
114
  "tags": [],
103
115
  "methods": [],
104
116
  "properties": [
105
117
  {
106
118
  "name": "platform",
107
119
  "tags": [],
108
- "docs": "Platform that detected and stored the crash marker.",
120
+ "docs": "Platform that detected and stored the marker.",
109
121
  "complexTypes": [
110
122
  "WebViewCrashPlatform"
111
123
  ],
@@ -114,7 +126,7 @@
114
126
  {
115
127
  "name": "timestamp",
116
128
  "tags": [],
117
- "docs": "Unix timestamp in milliseconds for when the crash marker was written.",
129
+ "docs": "Unix timestamp in milliseconds for when the marker was written.",
118
130
  "complexTypes": [],
119
131
  "type": "number"
120
132
  },
@@ -128,7 +140,7 @@
128
140
  {
129
141
  "name": "reason",
130
142
  "tags": [],
131
- "docs": "Platform-specific reason for the crash marker.",
143
+ "docs": "Platform-specific reason for the crash or restart marker.",
132
144
  "complexTypes": [
133
145
  "WebViewCrashReason"
134
146
  ],
@@ -137,7 +149,7 @@
137
149
  {
138
150
  "name": "url",
139
151
  "tags": [],
140
- "docs": "Last known WebView URL when the crash marker was written.",
152
+ "docs": "Last known WebView URL when the marker was written.",
141
153
  "complexTypes": [],
142
154
  "type": "string | undefined"
143
155
  },
@@ -158,7 +170,7 @@
158
170
  {
159
171
  "name": "appState",
160
172
  "tags": [],
161
- "docs": "iOS-only application state captured when the crash marker was written.",
173
+ "docs": "iOS-only application state captured when the marker was written.",
162
174
  "complexTypes": [
163
175
  "WebViewCrashAppState"
164
176
  ],
@@ -188,7 +200,7 @@
188
200
  {
189
201
  "name": "WebViewCrashPlatform",
190
202
  "slug": "webviewcrashplatform",
191
- "docs": "Platform that produced the stored crash marker.",
203
+ "docs": "Platform that produced the stored marker.",
192
204
  "types": [
193
205
  {
194
206
  "text": "'android'",
@@ -207,7 +219,7 @@
207
219
  {
208
220
  "name": "WebViewCrashReason",
209
221
  "slug": "webviewcrashreason",
210
- "docs": "Native reason reported for the previous WebView failure.",
222
+ "docs": "Native reason reported for the previous WebView failure or restart.",
211
223
  "types": [
212
224
  {
213
225
  "text": "'renderProcessGone'",
@@ -217,6 +229,14 @@
217
229
  "text": "'webContentProcessDidTerminate'",
218
230
  "complexTypes": []
219
231
  },
232
+ {
233
+ "text": "'periodicRestart'",
234
+ "complexTypes": []
235
+ },
236
+ {
237
+ "text": "'manualRestart'",
238
+ "complexTypes": []
239
+ },
220
240
  {
221
241
  "text": "'simulated'",
222
242
  "complexTypes": []
@@ -1,10 +1,58 @@
1
1
  import type { PluginListenerHandle } from '@capacitor/core';
2
+ declare module '@capacitor/cli' {
3
+ interface PluginsConfig {
4
+ /**
5
+ * Configure native WebView restart behavior for `@capgo/capacitor-webview-crash`.
6
+ */
7
+ WebViewCrash?: WebViewCrashPluginConfig;
8
+ }
9
+ }
10
+ /**
11
+ * Native WebView restart behavior configured in `capacitor.config.ts`.
12
+ */
13
+ export interface WebViewCrashPluginConfig {
14
+ /**
15
+ * Restart the WebView from native code when the renderer process dies.
16
+ *
17
+ * @default true
18
+ */
19
+ restartOnCrash?: boolean;
20
+ /**
21
+ * Fixed native interval, in milliseconds, for proactively replacing long-running WebViews.
22
+ *
23
+ * Set to `0` to disable interval restarts. Do not combine an active interval
24
+ * with `restartCron`; native initialization fails fast when both schedules are configured.
25
+ *
26
+ * @default 0
27
+ */
28
+ restartIntervalMs?: number;
29
+ /**
30
+ * Cron schedule for proactively replacing long-running WebViews.
31
+ *
32
+ * Uses standard 5-field cron syntax in the device local timezone:
33
+ * `minute hour day-of-month month day-of-week`.
34
+ *
35
+ * Examples:
36
+ * - `0 3 * * *` restarts every day at 03:00.
37
+ * - `0,30 * * * *` restarts every 30 minutes.
38
+ *
39
+ * Do not combine this with an active `restartIntervalMs`; native initialization
40
+ * fails fast when both schedules are configured.
41
+ */
42
+ restartCron?: string;
43
+ /**
44
+ * Delay, in milliseconds, before restarting after a crash.
45
+ *
46
+ * @default 0
47
+ */
48
+ restartAfterCrashDelayMs?: number;
49
+ }
2
50
  /**
3
- * Native reason reported for the previous WebView failure.
51
+ * Native reason reported for the previous WebView failure or restart.
4
52
  */
5
- export type WebViewCrashReason = 'renderProcessGone' | 'webContentProcessDidTerminate' | 'simulated';
53
+ export type WebViewCrashReason = 'renderProcessGone' | 'webContentProcessDidTerminate' | 'periodicRestart' | 'manualRestart' | 'simulated';
6
54
  /**
7
- * Platform that produced the stored crash marker.
55
+ * Platform that produced the stored marker.
8
56
  */
9
57
  export type WebViewCrashPlatform = 'android' | 'ios' | 'web';
10
58
  /**
@@ -12,15 +60,15 @@ export type WebViewCrashPlatform = 'android' | 'ios' | 'web';
12
60
  */
13
61
  export type WebViewCrashAppState = 'active' | 'inactive' | 'background' | 'unknown';
14
62
  /**
15
- * Metadata captured natively after the previous WebView process died.
63
+ * Metadata captured natively after the previous WebView process died or was restarted.
16
64
  */
17
65
  export interface WebViewCrashInfo {
18
66
  /**
19
- * Platform that detected and stored the crash marker.
67
+ * Platform that detected and stored the marker.
20
68
  */
21
69
  platform: WebViewCrashPlatform;
22
70
  /**
23
- * Unix timestamp in milliseconds for when the crash marker was written.
71
+ * Unix timestamp in milliseconds for when the marker was written.
24
72
  */
25
73
  timestamp: number;
26
74
  /**
@@ -28,11 +76,11 @@ export interface WebViewCrashInfo {
28
76
  */
29
77
  timestampISO: string;
30
78
  /**
31
- * Platform-specific reason for the crash marker.
79
+ * Platform-specific reason for the crash or restart marker.
32
80
  */
33
81
  reason: WebViewCrashReason;
34
82
  /**
35
- * Last known WebView URL when the crash marker was written.
83
+ * Last known WebView URL when the marker was written.
36
84
  */
37
85
  url?: string;
38
86
  /**
@@ -44,29 +92,29 @@ export interface WebViewCrashInfo {
44
92
  */
45
93
  rendererPriorityAtExit?: number;
46
94
  /**
47
- * iOS-only application state captured when the crash marker was written.
95
+ * iOS-only application state captured when the marker was written.
48
96
  */
49
97
  appState?: WebViewCrashAppState;
50
98
  }
51
99
  /**
52
- * Pending crash marker returned to JavaScript.
100
+ * Pending crash or restart marker returned to JavaScript.
53
101
  */
54
102
  export interface PendingCrashInfoResult {
55
103
  /**
56
- * Stored crash metadata, or `null` when no marker is pending.
104
+ * Stored crash or restart metadata, or `null` when no marker is pending.
57
105
  */
58
106
  value: WebViewCrashInfo | null;
59
107
  }
60
108
  /**
61
- * Capacitor API for recovered WebView crash detection.
109
+ * Capacitor API for recovered WebView crash and restart detection.
62
110
  */
63
111
  export interface WebViewCrashPlugin {
64
112
  /**
65
- * Returns the pending native crash marker, if one exists.
113
+ * Returns the pending native crash or restart marker, if one exists.
66
114
  */
67
115
  getPendingCrashInfo(): Promise<PendingCrashInfoResult>;
68
116
  /**
69
- * Clears the stored crash marker after the app has handled recovery.
117
+ * Clears the stored marker after the app has handled recovery.
70
118
  */
71
119
  clearPendingCrashInfo(): Promise<void>;
72
120
  /**
@@ -74,9 +122,16 @@ export interface WebViewCrashPlugin {
74
122
  */
75
123
  simulateCrashRecovery(): Promise<PendingCrashInfoResult>;
76
124
  /**
77
- * Fires after a new JavaScript runtime attaches a listener and a crash marker is still pending.
125
+ * Stores a manual restart marker and asks native code to create a fresh WebView.
126
+ *
127
+ * On Android this recreates the host Activity. On iOS this rebuilds the Capacitor bridge view so a new `WKWebView`
128
+ * instance is created instead of reloading the current page.
129
+ */
130
+ restartWebView(): Promise<PendingCrashInfoResult>;
131
+ /**
132
+ * Fires after a new JavaScript runtime attaches a listener and a matching marker is still pending.
78
133
  */
79
- addListener(eventName: 'webViewRestoredAfterCrash', listenerFunc: (info: WebViewCrashInfo) => void): Promise<PluginListenerHandle>;
134
+ addListener(eventName: 'webViewRestoredAfterCrash' | 'webViewRestoredAfterRestart', listenerFunc: (info: WebViewCrashInfo) => void): Promise<PluginListenerHandle>;
80
135
  /**
81
136
  * Removes all plugin listeners.
82
137
  */
@@ -1,2 +1,3 @@
1
+ /// <reference types="@capacitor/cli" />
1
2
  export {};
2
3
  //# sourceMappingURL=definitions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * Native reason reported for the previous WebView failure.\n */\nexport type WebViewCrashReason = 'renderProcessGone' | 'webContentProcessDidTerminate' | 'simulated';\n\n/**\n * Platform that produced the stored crash marker.\n */\nexport type WebViewCrashPlatform = 'android' | 'ios' | 'web';\n\n/**\n * Best-effort application state captured on iOS when the WebView process died.\n */\nexport type WebViewCrashAppState = 'active' | 'inactive' | 'background' | 'unknown';\n\n/**\n * Metadata captured natively after the previous WebView process died.\n */\nexport interface WebViewCrashInfo {\n /**\n * Platform that detected and stored the crash marker.\n */\n platform: WebViewCrashPlatform;\n /**\n * Unix timestamp in milliseconds for when the crash marker was written.\n */\n timestamp: number;\n /**\n * ISO-8601 version of `timestamp`.\n */\n timestampISO: string;\n /**\n * Platform-specific reason for the crash marker.\n */\n reason: WebViewCrashReason;\n /**\n * Last known WebView URL when the crash marker was written.\n */\n url?: string;\n /**\n * Android-only hint from `RenderProcessGoneDetail.didCrash()`.\n */\n didCrash?: boolean;\n /**\n * Android-only renderer priority reported at exit.\n */\n rendererPriorityAtExit?: number;\n /**\n * iOS-only application state captured when the crash marker was written.\n */\n appState?: WebViewCrashAppState;\n}\n\n/**\n * Pending crash marker returned to JavaScript.\n */\nexport interface PendingCrashInfoResult {\n /**\n * Stored crash metadata, or `null` when no marker is pending.\n */\n value: WebViewCrashInfo | null;\n}\n\n/**\n * Capacitor API for recovered WebView crash detection.\n */\nexport interface WebViewCrashPlugin {\n /**\n * Returns the pending native crash marker, if one exists.\n */\n getPendingCrashInfo(): Promise<PendingCrashInfoResult>;\n\n /**\n * Clears the stored crash marker after the app has handled recovery.\n */\n clearPendingCrashInfo(): Promise<void>;\n\n /**\n * Creates a fake crash marker so recovery flows can be tested locally.\n */\n simulateCrashRecovery(): Promise<PendingCrashInfoResult>;\n\n /**\n * Fires after a new JavaScript runtime attaches a listener and a crash marker is still pending.\n */\n addListener(\n eventName: 'webViewRestoredAfterCrash',\n listenerFunc: (info: WebViewCrashInfo) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Removes all plugin listeners.\n */\n removeAllListeners(): Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,wCAAwC","sourcesContent":["/// <reference types=\"@capacitor/cli\" />\n\nimport type { PluginListenerHandle } from '@capacitor/core';\n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n /**\n * Configure native WebView restart behavior for `@capgo/capacitor-webview-crash`.\n */\n WebViewCrash?: WebViewCrashPluginConfig;\n }\n}\n\n/**\n * Native WebView restart behavior configured in `capacitor.config.ts`.\n */\nexport interface WebViewCrashPluginConfig {\n /**\n * Restart the WebView from native code when the renderer process dies.\n *\n * @default true\n */\n restartOnCrash?: boolean;\n\n /**\n * Fixed native interval, in milliseconds, for proactively replacing long-running WebViews.\n *\n * Set to `0` to disable interval restarts. Do not combine an active interval\n * with `restartCron`; native initialization fails fast when both schedules are configured.\n *\n * @default 0\n */\n restartIntervalMs?: number;\n\n /**\n * Cron schedule for proactively replacing long-running WebViews.\n *\n * Uses standard 5-field cron syntax in the device local timezone:\n * `minute hour day-of-month month day-of-week`.\n *\n * Examples:\n * - `0 3 * * *` restarts every day at 03:00.\n * - `0,30 * * * *` restarts every 30 minutes.\n *\n * Do not combine this with an active `restartIntervalMs`; native initialization\n * fails fast when both schedules are configured.\n */\n restartCron?: string;\n\n /**\n * Delay, in milliseconds, before restarting after a crash.\n *\n * @default 0\n */\n restartAfterCrashDelayMs?: number;\n}\n\n/**\n * Native reason reported for the previous WebView failure or restart.\n */\nexport type WebViewCrashReason =\n | 'renderProcessGone'\n | 'webContentProcessDidTerminate'\n | 'periodicRestart'\n | 'manualRestart'\n | 'simulated';\n\n/**\n * Platform that produced the stored marker.\n */\nexport type WebViewCrashPlatform = 'android' | 'ios' | 'web';\n\n/**\n * Best-effort application state captured on iOS when the WebView process died.\n */\nexport type WebViewCrashAppState = 'active' | 'inactive' | 'background' | 'unknown';\n\n/**\n * Metadata captured natively after the previous WebView process died or was restarted.\n */\nexport interface WebViewCrashInfo {\n /**\n * Platform that detected and stored the marker.\n */\n platform: WebViewCrashPlatform;\n /**\n * Unix timestamp in milliseconds for when the marker was written.\n */\n timestamp: number;\n /**\n * ISO-8601 version of `timestamp`.\n */\n timestampISO: string;\n /**\n * Platform-specific reason for the crash or restart marker.\n */\n reason: WebViewCrashReason;\n /**\n * Last known WebView URL when the marker was written.\n */\n url?: string;\n /**\n * Android-only hint from `RenderProcessGoneDetail.didCrash()`.\n */\n didCrash?: boolean;\n /**\n * Android-only renderer priority reported at exit.\n */\n rendererPriorityAtExit?: number;\n /**\n * iOS-only application state captured when the marker was written.\n */\n appState?: WebViewCrashAppState;\n}\n\n/**\n * Pending crash or restart marker returned to JavaScript.\n */\nexport interface PendingCrashInfoResult {\n /**\n * Stored crash or restart metadata, or `null` when no marker is pending.\n */\n value: WebViewCrashInfo | null;\n}\n\n/**\n * Capacitor API for recovered WebView crash and restart detection.\n */\nexport interface WebViewCrashPlugin {\n /**\n * Returns the pending native crash or restart marker, if one exists.\n */\n getPendingCrashInfo(): Promise<PendingCrashInfoResult>;\n\n /**\n * Clears the stored marker after the app has handled recovery.\n */\n clearPendingCrashInfo(): Promise<void>;\n\n /**\n * Creates a fake crash marker so recovery flows can be tested locally.\n */\n simulateCrashRecovery(): Promise<PendingCrashInfoResult>;\n\n /**\n * Stores a manual restart marker and asks native code to create a fresh WebView.\n *\n * On Android this recreates the host Activity. On iOS this rebuilds the Capacitor bridge view so a new `WKWebView`\n * instance is created instead of reloading the current page.\n */\n restartWebView(): Promise<PendingCrashInfoResult>;\n\n /**\n * Fires after a new JavaScript runtime attaches a listener and a matching marker is still pending.\n */\n addListener(\n eventName: 'webViewRestoredAfterCrash' | 'webViewRestoredAfterRestart',\n listenerFunc: (info: WebViewCrashInfo) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Removes all plugin listeners.\n */\n removeAllListeners(): Promise<void>;\n}\n"]}
package/dist/esm/web.d.ts CHANGED
@@ -2,16 +2,19 @@ import type { PluginListenerHandle } from '@capacitor/core';
2
2
  import { WebPlugin } from '@capacitor/core';
3
3
  import type { PendingCrashInfoResult, WebViewCrashPlugin } from './definitions';
4
4
  export declare class WebViewCrashWeb extends WebPlugin implements WebViewCrashPlugin {
5
- private didDispatchPendingEvent;
5
+ private dispatchedPendingEvents;
6
6
  getPendingCrashInfo(): Promise<PendingCrashInfoResult>;
7
7
  clearPendingCrashInfo(): Promise<void>;
8
8
  simulateCrashRecovery(): Promise<PendingCrashInfoResult>;
9
+ restartWebView(): Promise<PendingCrashInfoResult>;
9
10
  addListener(eventName: string, listenerFunc: (...args: any[]) => any): Promise<PluginListenerHandle>;
10
11
  private flushPendingCrashEvent;
12
+ private shouldDispatchEvent;
11
13
  private buildCrashInfo;
12
14
  private readPendingCrashInfo;
13
15
  private writePendingCrashInfo;
14
16
  private removePendingCrashInfo;
15
- private static readonly eventName;
17
+ private static readonly crashEventName;
18
+ private static readonly restartEventName;
16
19
  private static readonly storageKey;
17
20
  }
package/dist/esm/web.js CHANGED
@@ -2,48 +2,62 @@ import { WebPlugin } from '@capacitor/core';
2
2
  export class WebViewCrashWeb extends WebPlugin {
3
3
  constructor() {
4
4
  super(...arguments);
5
- this.didDispatchPendingEvent = false;
5
+ this.dispatchedPendingEvents = new Set();
6
6
  }
7
7
  async getPendingCrashInfo() {
8
8
  return { value: this.readPendingCrashInfo() };
9
9
  }
10
10
  async clearPendingCrashInfo() {
11
11
  this.removePendingCrashInfo();
12
- this.didDispatchPendingEvent = false;
12
+ this.dispatchedPendingEvents.clear();
13
13
  }
14
14
  async simulateCrashRecovery() {
15
15
  const value = this.buildCrashInfo();
16
16
  this.writePendingCrashInfo(value);
17
- this.didDispatchPendingEvent = false;
17
+ this.dispatchedPendingEvents.clear();
18
18
  this.flushPendingCrashEvent();
19
+ this.flushPendingCrashEvent(WebViewCrashWeb.restartEventName);
20
+ return { value };
21
+ }
22
+ async restartWebView() {
23
+ const value = this.buildCrashInfo('manualRestart');
24
+ this.writePendingCrashInfo(value);
25
+ this.dispatchedPendingEvents.clear();
26
+ this.flushPendingCrashEvent(WebViewCrashWeb.restartEventName);
19
27
  return { value };
20
28
  }
21
29
  async addListener(eventName, listenerFunc) {
22
30
  const handle = await super.addListener(eventName, listenerFunc);
23
- if (eventName === WebViewCrashWeb.eventName) {
24
- this.flushPendingCrashEvent();
31
+ if (eventName === WebViewCrashWeb.crashEventName || eventName === WebViewCrashWeb.restartEventName) {
32
+ this.flushPendingCrashEvent(eventName);
25
33
  }
26
34
  return handle;
27
35
  }
28
- flushPendingCrashEvent() {
29
- if (this.didDispatchPendingEvent) {
36
+ flushPendingCrashEvent(eventName = WebViewCrashWeb.crashEventName) {
37
+ if (this.dispatchedPendingEvents.has(eventName)) {
30
38
  return;
31
39
  }
32
40
  const value = this.readPendingCrashInfo();
33
- if (!value) {
41
+ if (!value || !this.shouldDispatchEvent(eventName, value)) {
34
42
  return;
35
43
  }
36
- this.didDispatchPendingEvent = true;
37
- this.notifyListeners(WebViewCrashWeb.eventName, value);
44
+ this.dispatchedPendingEvents.add(eventName);
45
+ this.notifyListeners(eventName, value);
46
+ }
47
+ shouldDispatchEvent(eventName, value) {
48
+ if (eventName === WebViewCrashWeb.crashEventName) {
49
+ return value.reason !== 'periodicRestart' && value.reason !== 'manualRestart';
50
+ }
51
+ return eventName === WebViewCrashWeb.restartEventName;
38
52
  }
39
- buildCrashInfo() {
53
+ buildCrashInfo(reason = 'simulated') {
40
54
  var _a;
41
55
  const timestamp = Date.now();
42
56
  return {
43
57
  platform: 'web',
44
58
  timestamp,
45
59
  timestampISO: new Date(timestamp).toISOString(),
46
- reason: 'simulated',
60
+ reason,
47
61
  url: (_a = globalThis.location) === null || _a === void 0 ? void 0 : _a.href,
48
62
  appState: 'active',
49
63
  };
@@ -70,6 +84,7 @@ export class WebViewCrashWeb extends WebPlugin {
70
84
  (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.removeItem(WebViewCrashWeb.storageKey);
71
85
  }
72
86
  }
73
- WebViewCrashWeb.eventName = 'webViewRestoredAfterCrash';
87
+ WebViewCrashWeb.crashEventName = 'webViewRestoredAfterCrash';
88
+ WebViewCrashWeb.restartEventName = 'webViewRestoredAfterRestart';
74
89
  WebViewCrashWeb.storageKey = 'capgo.webview-crash.pending';
75
90
  //# sourceMappingURL=web.js.map