@capgo/capacitor-webview-crash 7.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.
- package/CapgoCapacitorWebViewCrash.podspec +17 -0
- package/LICENSE +373 -0
- package/Package.swift +28 -0
- package/README.md +293 -0
- package/android/build.gradle +59 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/app/capgo/webviewcrash/WebViewCrash.java +288 -0
- package/android/src/main/java/app/capgo/webviewcrash/WebViewCrashPlugin.java +185 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +271 -0
- package/dist/esm/definitions.d.ts +139 -0
- package/dist/esm/definitions.js +3 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +20 -0
- package/dist/esm/web.js +90 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +104 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +107 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/WebViewCrashPlugin/WebViewCrash.swift +359 -0
- package/ios/Sources/WebViewCrashPlugin/WebViewCrashPlugin.swift +152 -0
- package/ios/Tests/WebViewCrashPluginTests/WebViewCrashPluginTests.swift +12 -0
- package/package.json +94 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
package app.capgo.webviewcrash;
|
|
2
|
+
|
|
3
|
+
import android.os.Handler;
|
|
4
|
+
import android.os.Looper;
|
|
5
|
+
import android.webkit.RenderProcessGoneDetail;
|
|
6
|
+
import android.webkit.WebView;
|
|
7
|
+
import androidx.appcompat.app.AppCompatActivity;
|
|
8
|
+
import com.getcapacitor.JSObject;
|
|
9
|
+
import com.getcapacitor.Plugin;
|
|
10
|
+
import com.getcapacitor.PluginCall;
|
|
11
|
+
import com.getcapacitor.PluginMethod;
|
|
12
|
+
import com.getcapacitor.WebViewListener;
|
|
13
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
14
|
+
import java.util.HashSet;
|
|
15
|
+
import java.util.Set;
|
|
16
|
+
|
|
17
|
+
@CapacitorPlugin(name = "WebViewCrash")
|
|
18
|
+
public class WebViewCrashPlugin extends Plugin {
|
|
19
|
+
|
|
20
|
+
private final WebViewCrash implementation = new WebViewCrash();
|
|
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
|
+
};
|
|
34
|
+
|
|
35
|
+
private final WebViewListener webViewListener = new WebViewListener() {
|
|
36
|
+
@Override
|
|
37
|
+
public boolean onRenderProcessGone(WebView webView, RenderProcessGoneDetail detail) {
|
|
38
|
+
JSObject crashInfo = implementation.buildCrashInfo(
|
|
39
|
+
"renderProcessGone",
|
|
40
|
+
webView.getUrl(),
|
|
41
|
+
detail.didCrash(),
|
|
42
|
+
detail.rendererPriorityAtExit()
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
implementation.writePendingCrashInfo(getContext(), crashInfo);
|
|
46
|
+
dispatchedPendingEvents.clear();
|
|
47
|
+
|
|
48
|
+
if (!restartOptions.restartOnCrash) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
restartWebView(restartOptions.restartAfterCrashDelayMs);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
@Override
|
|
58
|
+
public void load() {
|
|
59
|
+
restartOptions = implementation.readRestartOptions(getConfig());
|
|
60
|
+
bridge.addWebViewListener(webViewListener);
|
|
61
|
+
schedulePeriodicRestart();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@Override
|
|
65
|
+
protected void handleOnDestroy() {
|
|
66
|
+
bridge.removeWebViewListener(webViewListener);
|
|
67
|
+
cancelPeriodicRestart();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@Override
|
|
71
|
+
public void addListener(PluginCall call) {
|
|
72
|
+
super.addListener(call);
|
|
73
|
+
|
|
74
|
+
String eventName = call.getString("eventName");
|
|
75
|
+
if (WebViewCrash.CRASH_EVENT_NAME.equals(eventName) || WebViewCrash.RESTART_EVENT_NAME.equals(eventName)) {
|
|
76
|
+
dispatchPendingCrashIfNeeded(eventName);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@PluginMethod
|
|
81
|
+
public void getPendingCrashInfo(PluginCall call) {
|
|
82
|
+
JSObject result = new JSObject();
|
|
83
|
+
result.put("value", implementation.readPendingCrashInfo(getContext()));
|
|
84
|
+
call.resolve(result);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@PluginMethod
|
|
88
|
+
public void clearPendingCrashInfo(PluginCall call) {
|
|
89
|
+
implementation.clearPendingCrashInfo(getContext());
|
|
90
|
+
dispatchedPendingEvents.clear();
|
|
91
|
+
call.resolve();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@PluginMethod
|
|
95
|
+
public void simulateCrashRecovery(PluginCall call) {
|
|
96
|
+
String url = bridge.getWebView() != null ? bridge.getWebView().getUrl() : null;
|
|
97
|
+
JSObject crashInfo = implementation.buildCrashInfo("simulated", url, null, null);
|
|
98
|
+
|
|
99
|
+
implementation.writePendingCrashInfo(getContext(), crashInfo);
|
|
100
|
+
dispatchedPendingEvents.clear();
|
|
101
|
+
dispatchPendingCrashIfNeeded(WebViewCrash.CRASH_EVENT_NAME);
|
|
102
|
+
dispatchPendingCrashIfNeeded(WebViewCrash.RESTART_EVENT_NAME);
|
|
103
|
+
|
|
104
|
+
JSObject result = new JSObject();
|
|
105
|
+
result.put("value", crashInfo);
|
|
106
|
+
call.resolve(result);
|
|
107
|
+
}
|
|
108
|
+
|
|
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)) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
JSObject crashInfo = implementation.readPendingCrashInfo(getContext());
|
|
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) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
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;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
File without changes
|
package/dist/docs.json
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
{
|
|
2
|
+
"api": {
|
|
3
|
+
"name": "WebViewCrashPlugin",
|
|
4
|
+
"slug": "webviewcrashplugin",
|
|
5
|
+
"docs": "Capacitor API for recovered WebView crash and restart detection.",
|
|
6
|
+
"tags": [],
|
|
7
|
+
"methods": [
|
|
8
|
+
{
|
|
9
|
+
"name": "getPendingCrashInfo",
|
|
10
|
+
"signature": "() => Promise<PendingCrashInfoResult>",
|
|
11
|
+
"parameters": [],
|
|
12
|
+
"returns": "Promise<PendingCrashInfoResult>",
|
|
13
|
+
"tags": [],
|
|
14
|
+
"docs": "Returns the pending native crash or restart marker, if one exists.",
|
|
15
|
+
"complexTypes": [
|
|
16
|
+
"PendingCrashInfoResult"
|
|
17
|
+
],
|
|
18
|
+
"slug": "getpendingcrashinfo"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "clearPendingCrashInfo",
|
|
22
|
+
"signature": "() => Promise<void>",
|
|
23
|
+
"parameters": [],
|
|
24
|
+
"returns": "Promise<void>",
|
|
25
|
+
"tags": [],
|
|
26
|
+
"docs": "Clears the stored marker after the app has handled recovery.",
|
|
27
|
+
"complexTypes": [],
|
|
28
|
+
"slug": "clearpendingcrashinfo"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "simulateCrashRecovery",
|
|
32
|
+
"signature": "() => Promise<PendingCrashInfoResult>",
|
|
33
|
+
"parameters": [],
|
|
34
|
+
"returns": "Promise<PendingCrashInfoResult>",
|
|
35
|
+
"tags": [],
|
|
36
|
+
"docs": "Creates a fake crash marker so recovery flows can be tested locally.",
|
|
37
|
+
"complexTypes": [
|
|
38
|
+
"PendingCrashInfoResult"
|
|
39
|
+
],
|
|
40
|
+
"slug": "simulatecrashrecovery"
|
|
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
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "addListener",
|
|
56
|
+
"signature": "(eventName: 'webViewRestoredAfterCrash' | 'webViewRestoredAfterRestart', listenerFunc: (info: WebViewCrashInfo) => void) => Promise<PluginListenerHandle>",
|
|
57
|
+
"parameters": [
|
|
58
|
+
{
|
|
59
|
+
"name": "eventName",
|
|
60
|
+
"docs": "",
|
|
61
|
+
"type": "'webViewRestoredAfterCrash' | 'webViewRestoredAfterRestart'"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "listenerFunc",
|
|
65
|
+
"docs": "",
|
|
66
|
+
"type": "(info: WebViewCrashInfo) => void"
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"returns": "Promise<PluginListenerHandle>",
|
|
70
|
+
"tags": [],
|
|
71
|
+
"docs": "Fires after a new JavaScript runtime attaches a listener and a matching marker is still pending.",
|
|
72
|
+
"complexTypes": [
|
|
73
|
+
"PluginListenerHandle",
|
|
74
|
+
"WebViewCrashInfo"
|
|
75
|
+
],
|
|
76
|
+
"slug": "addlistenerwebviewrestoredaftercrash--webviewrestoredafterrestart-"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"name": "removeAllListeners",
|
|
80
|
+
"signature": "() => Promise<void>",
|
|
81
|
+
"parameters": [],
|
|
82
|
+
"returns": "Promise<void>",
|
|
83
|
+
"tags": [],
|
|
84
|
+
"docs": "Removes all plugin listeners.",
|
|
85
|
+
"complexTypes": [],
|
|
86
|
+
"slug": "removealllisteners"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"properties": []
|
|
90
|
+
},
|
|
91
|
+
"interfaces": [
|
|
92
|
+
{
|
|
93
|
+
"name": "PendingCrashInfoResult",
|
|
94
|
+
"slug": "pendingcrashinforesult",
|
|
95
|
+
"docs": "Pending crash or restart marker returned to JavaScript.",
|
|
96
|
+
"tags": [],
|
|
97
|
+
"methods": [],
|
|
98
|
+
"properties": [
|
|
99
|
+
{
|
|
100
|
+
"name": "value",
|
|
101
|
+
"tags": [],
|
|
102
|
+
"docs": "Stored crash or restart metadata, or `null` when no marker is pending.",
|
|
103
|
+
"complexTypes": [
|
|
104
|
+
"WebViewCrashInfo"
|
|
105
|
+
],
|
|
106
|
+
"type": "WebViewCrashInfo | null"
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "WebViewCrashInfo",
|
|
112
|
+
"slug": "webviewcrashinfo",
|
|
113
|
+
"docs": "Metadata captured natively after the previous WebView process died or was restarted.",
|
|
114
|
+
"tags": [],
|
|
115
|
+
"methods": [],
|
|
116
|
+
"properties": [
|
|
117
|
+
{
|
|
118
|
+
"name": "platform",
|
|
119
|
+
"tags": [],
|
|
120
|
+
"docs": "Platform that detected and stored the marker.",
|
|
121
|
+
"complexTypes": [
|
|
122
|
+
"WebViewCrashPlatform"
|
|
123
|
+
],
|
|
124
|
+
"type": "WebViewCrashPlatform"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"name": "timestamp",
|
|
128
|
+
"tags": [],
|
|
129
|
+
"docs": "Unix timestamp in milliseconds for when the marker was written.",
|
|
130
|
+
"complexTypes": [],
|
|
131
|
+
"type": "number"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"name": "timestampISO",
|
|
135
|
+
"tags": [],
|
|
136
|
+
"docs": "ISO-8601 version of `timestamp`.",
|
|
137
|
+
"complexTypes": [],
|
|
138
|
+
"type": "string"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "reason",
|
|
142
|
+
"tags": [],
|
|
143
|
+
"docs": "Platform-specific reason for the crash or restart marker.",
|
|
144
|
+
"complexTypes": [
|
|
145
|
+
"WebViewCrashReason"
|
|
146
|
+
],
|
|
147
|
+
"type": "WebViewCrashReason"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"name": "url",
|
|
151
|
+
"tags": [],
|
|
152
|
+
"docs": "Last known WebView URL when the marker was written.",
|
|
153
|
+
"complexTypes": [],
|
|
154
|
+
"type": "string | undefined"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"name": "didCrash",
|
|
158
|
+
"tags": [],
|
|
159
|
+
"docs": "Android-only hint from `RenderProcessGoneDetail.didCrash()`.",
|
|
160
|
+
"complexTypes": [],
|
|
161
|
+
"type": "boolean | undefined"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"name": "rendererPriorityAtExit",
|
|
165
|
+
"tags": [],
|
|
166
|
+
"docs": "Android-only renderer priority reported at exit.",
|
|
167
|
+
"complexTypes": [],
|
|
168
|
+
"type": "number | undefined"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "appState",
|
|
172
|
+
"tags": [],
|
|
173
|
+
"docs": "iOS-only application state captured when the marker was written.",
|
|
174
|
+
"complexTypes": [
|
|
175
|
+
"WebViewCrashAppState"
|
|
176
|
+
],
|
|
177
|
+
"type": "WebViewCrashAppState"
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"name": "PluginListenerHandle",
|
|
183
|
+
"slug": "pluginlistenerhandle",
|
|
184
|
+
"docs": "",
|
|
185
|
+
"tags": [],
|
|
186
|
+
"methods": [],
|
|
187
|
+
"properties": [
|
|
188
|
+
{
|
|
189
|
+
"name": "remove",
|
|
190
|
+
"tags": [],
|
|
191
|
+
"docs": "",
|
|
192
|
+
"complexTypes": [],
|
|
193
|
+
"type": "() => Promise<void>"
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
],
|
|
198
|
+
"enums": [],
|
|
199
|
+
"typeAliases": [
|
|
200
|
+
{
|
|
201
|
+
"name": "WebViewCrashPlatform",
|
|
202
|
+
"slug": "webviewcrashplatform",
|
|
203
|
+
"docs": "Platform that produced the stored marker.",
|
|
204
|
+
"types": [
|
|
205
|
+
{
|
|
206
|
+
"text": "'android'",
|
|
207
|
+
"complexTypes": []
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"text": "'ios'",
|
|
211
|
+
"complexTypes": []
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"text": "'web'",
|
|
215
|
+
"complexTypes": []
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"name": "WebViewCrashReason",
|
|
221
|
+
"slug": "webviewcrashreason",
|
|
222
|
+
"docs": "Native reason reported for the previous WebView failure or restart.",
|
|
223
|
+
"types": [
|
|
224
|
+
{
|
|
225
|
+
"text": "'renderProcessGone'",
|
|
226
|
+
"complexTypes": []
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"text": "'webContentProcessDidTerminate'",
|
|
230
|
+
"complexTypes": []
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"text": "'periodicRestart'",
|
|
234
|
+
"complexTypes": []
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"text": "'manualRestart'",
|
|
238
|
+
"complexTypes": []
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"text": "'simulated'",
|
|
242
|
+
"complexTypes": []
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"name": "WebViewCrashAppState",
|
|
248
|
+
"slug": "webviewcrashappstate",
|
|
249
|
+
"docs": "Best-effort application state captured on iOS when the WebView process died.",
|
|
250
|
+
"types": [
|
|
251
|
+
{
|
|
252
|
+
"text": "'active'",
|
|
253
|
+
"complexTypes": []
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"text": "'inactive'",
|
|
257
|
+
"complexTypes": []
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"text": "'background'",
|
|
261
|
+
"complexTypes": []
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"text": "'unknown'",
|
|
265
|
+
"complexTypes": []
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
}
|
|
269
|
+
],
|
|
270
|
+
"pluginConfigs": []
|
|
271
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
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
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Native reason reported for the previous WebView failure or restart.
|
|
52
|
+
*/
|
|
53
|
+
export type WebViewCrashReason = 'renderProcessGone' | 'webContentProcessDidTerminate' | 'periodicRestart' | 'manualRestart' | 'simulated';
|
|
54
|
+
/**
|
|
55
|
+
* Platform that produced the stored marker.
|
|
56
|
+
*/
|
|
57
|
+
export type WebViewCrashPlatform = 'android' | 'ios' | 'web';
|
|
58
|
+
/**
|
|
59
|
+
* Best-effort application state captured on iOS when the WebView process died.
|
|
60
|
+
*/
|
|
61
|
+
export type WebViewCrashAppState = 'active' | 'inactive' | 'background' | 'unknown';
|
|
62
|
+
/**
|
|
63
|
+
* Metadata captured natively after the previous WebView process died or was restarted.
|
|
64
|
+
*/
|
|
65
|
+
export interface WebViewCrashInfo {
|
|
66
|
+
/**
|
|
67
|
+
* Platform that detected and stored the marker.
|
|
68
|
+
*/
|
|
69
|
+
platform: WebViewCrashPlatform;
|
|
70
|
+
/**
|
|
71
|
+
* Unix timestamp in milliseconds for when the marker was written.
|
|
72
|
+
*/
|
|
73
|
+
timestamp: number;
|
|
74
|
+
/**
|
|
75
|
+
* ISO-8601 version of `timestamp`.
|
|
76
|
+
*/
|
|
77
|
+
timestampISO: string;
|
|
78
|
+
/**
|
|
79
|
+
* Platform-specific reason for the crash or restart marker.
|
|
80
|
+
*/
|
|
81
|
+
reason: WebViewCrashReason;
|
|
82
|
+
/**
|
|
83
|
+
* Last known WebView URL when the marker was written.
|
|
84
|
+
*/
|
|
85
|
+
url?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Android-only hint from `RenderProcessGoneDetail.didCrash()`.
|
|
88
|
+
*/
|
|
89
|
+
didCrash?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Android-only renderer priority reported at exit.
|
|
92
|
+
*/
|
|
93
|
+
rendererPriorityAtExit?: number;
|
|
94
|
+
/**
|
|
95
|
+
* iOS-only application state captured when the marker was written.
|
|
96
|
+
*/
|
|
97
|
+
appState?: WebViewCrashAppState;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Pending crash or restart marker returned to JavaScript.
|
|
101
|
+
*/
|
|
102
|
+
export interface PendingCrashInfoResult {
|
|
103
|
+
/**
|
|
104
|
+
* Stored crash or restart metadata, or `null` when no marker is pending.
|
|
105
|
+
*/
|
|
106
|
+
value: WebViewCrashInfo | null;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Capacitor API for recovered WebView crash and restart detection.
|
|
110
|
+
*/
|
|
111
|
+
export interface WebViewCrashPlugin {
|
|
112
|
+
/**
|
|
113
|
+
* Returns the pending native crash or restart marker, if one exists.
|
|
114
|
+
*/
|
|
115
|
+
getPendingCrashInfo(): Promise<PendingCrashInfoResult>;
|
|
116
|
+
/**
|
|
117
|
+
* Clears the stored marker after the app has handled recovery.
|
|
118
|
+
*/
|
|
119
|
+
clearPendingCrashInfo(): Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* Creates a fake crash marker so recovery flows can be tested locally.
|
|
122
|
+
*/
|
|
123
|
+
simulateCrashRecovery(): Promise<PendingCrashInfoResult>;
|
|
124
|
+
/**
|
|
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.
|
|
133
|
+
*/
|
|
134
|
+
addListener(eventName: 'webViewRestoredAfterCrash' | 'webViewRestoredAfterRestart', listenerFunc: (info: WebViewCrashInfo) => void): Promise<PluginListenerHandle>;
|
|
135
|
+
/**
|
|
136
|
+
* Removes all plugin listeners.
|
|
137
|
+
*/
|
|
138
|
+
removeAllListeners(): Promise<void>;
|
|
139
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { registerPlugin } from '@capacitor/core';
|
|
2
|
+
const WebViewCrash = registerPlugin('WebViewCrash', {
|
|
3
|
+
web: () => import('./web').then((m) => new m.WebViewCrashWeb()),
|
|
4
|
+
});
|
|
5
|
+
export * from './definitions';
|
|
6
|
+
export { WebViewCrash };
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,YAAY,GAAG,cAAc,CAAqB,cAAc,EAAE;IACtE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;CAChE,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { WebViewCrashPlugin } from './definitions';\n\nconst WebViewCrash = registerPlugin<WebViewCrashPlugin>('WebViewCrash', {\n web: () => import('./web').then((m) => new m.WebViewCrashWeb()),\n});\n\nexport * from './definitions';\nexport { WebViewCrash };\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PluginListenerHandle } from '@capacitor/core';
|
|
2
|
+
import { WebPlugin } from '@capacitor/core';
|
|
3
|
+
import type { PendingCrashInfoResult, WebViewCrashPlugin } from './definitions';
|
|
4
|
+
export declare class WebViewCrashWeb extends WebPlugin implements WebViewCrashPlugin {
|
|
5
|
+
private dispatchedPendingEvents;
|
|
6
|
+
getPendingCrashInfo(): Promise<PendingCrashInfoResult>;
|
|
7
|
+
clearPendingCrashInfo(): Promise<void>;
|
|
8
|
+
simulateCrashRecovery(): Promise<PendingCrashInfoResult>;
|
|
9
|
+
restartWebView(): Promise<PendingCrashInfoResult>;
|
|
10
|
+
addListener(eventName: string, listenerFunc: (...args: any[]) => any): Promise<PluginListenerHandle>;
|
|
11
|
+
private flushPendingCrashEvent;
|
|
12
|
+
private shouldDispatchEvent;
|
|
13
|
+
private buildCrashInfo;
|
|
14
|
+
private readPendingCrashInfo;
|
|
15
|
+
private writePendingCrashInfo;
|
|
16
|
+
private removePendingCrashInfo;
|
|
17
|
+
private static readonly crashEventName;
|
|
18
|
+
private static readonly restartEventName;
|
|
19
|
+
private static readonly storageKey;
|
|
20
|
+
}
|