@capgo/capacitor-webview-crash 8.0.1
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 +257 -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 +58 -0
- package/android/src/main/java/app/capgo/webviewcrash/WebViewCrashPlugin.java +104 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +251 -0
- package/dist/esm/definitions.d.ts +84 -0
- package/dist/esm/definitions.js +2 -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 +17 -0
- package/dist/esm/web.js +75 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +89 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +92 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/WebViewCrashPlugin/WebViewCrash.swift +105 -0
- package/ios/Sources/WebViewCrashPlugin/WebViewCrashPlugin.swift +62 -0
- package/ios/Tests/WebViewCrashPluginTests/WebViewCrashPluginTests.swift +12 -0
- package/package.json +92 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
ext {
|
|
2
|
+
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
|
|
4
|
+
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
|
|
5
|
+
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
buildscript {
|
|
9
|
+
repositories {
|
|
10
|
+
google()
|
|
11
|
+
mavenCentral()
|
|
12
|
+
}
|
|
13
|
+
dependencies {
|
|
14
|
+
classpath 'com.android.tools.build:gradle:8.13.0'
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
apply plugin: 'com.android.library'
|
|
19
|
+
|
|
20
|
+
android {
|
|
21
|
+
namespace = "app.capgo.webviewcrash"
|
|
22
|
+
compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
|
|
23
|
+
defaultConfig {
|
|
24
|
+
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
|
|
25
|
+
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
|
|
26
|
+
versionCode 1
|
|
27
|
+
versionName "1.0"
|
|
28
|
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
29
|
+
}
|
|
30
|
+
buildTypes {
|
|
31
|
+
release {
|
|
32
|
+
minifyEnabled false
|
|
33
|
+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
lintOptions {
|
|
37
|
+
abortOnError = false
|
|
38
|
+
}
|
|
39
|
+
compileOptions {
|
|
40
|
+
sourceCompatibility JavaVersion.VERSION_21
|
|
41
|
+
targetCompatibility JavaVersion.VERSION_21
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
repositories {
|
|
46
|
+
google()
|
|
47
|
+
mavenCentral()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
dependencies {
|
|
52
|
+
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
53
|
+
implementation project(':capacitor-android')
|
|
54
|
+
annotationProcessor project(':capacitor-android')
|
|
55
|
+
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
56
|
+
testImplementation "junit:junit:$junitVersion"
|
|
57
|
+
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
58
|
+
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
59
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
package app.capgo.webviewcrash;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import com.getcapacitor.JSObject;
|
|
5
|
+
import java.time.Instant;
|
|
6
|
+
import org.json.JSONException;
|
|
7
|
+
|
|
8
|
+
final class WebViewCrash {
|
|
9
|
+
|
|
10
|
+
static final String EVENT_NAME = "webViewRestoredAfterCrash";
|
|
11
|
+
|
|
12
|
+
private static final String PREFERENCES_NAME = "CapgoWebViewCrash";
|
|
13
|
+
private static final String PENDING_CRASH_KEY = "pendingCrashInfo";
|
|
14
|
+
|
|
15
|
+
JSObject buildCrashInfo(String reason, String url, Boolean didCrash, Integer rendererPriorityAtExit) {
|
|
16
|
+
long timestamp = System.currentTimeMillis();
|
|
17
|
+
JSObject crashInfo = new JSObject();
|
|
18
|
+
crashInfo.put("platform", "android");
|
|
19
|
+
crashInfo.put("timestamp", timestamp);
|
|
20
|
+
crashInfo.put("timestampISO", Instant.ofEpochMilli(timestamp).toString());
|
|
21
|
+
crashInfo.put("reason", reason);
|
|
22
|
+
|
|
23
|
+
if (url != null && !url.isBlank()) {
|
|
24
|
+
crashInfo.put("url", url);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (didCrash != null) {
|
|
28
|
+
crashInfo.put("didCrash", didCrash);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (rendererPriorityAtExit != null) {
|
|
32
|
+
crashInfo.put("rendererPriorityAtExit", rendererPriorityAtExit);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return crashInfo;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
JSObject readPendingCrashInfo(Context context) {
|
|
39
|
+
String raw = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE).getString(PENDING_CRASH_KEY, null);
|
|
40
|
+
if (raw == null) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
return new JSObject(raw);
|
|
46
|
+
} catch (JSONException ignored) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
void writePendingCrashInfo(Context context, JSObject value) {
|
|
52
|
+
context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE).edit().putString(PENDING_CRASH_KEY, value.toString()).apply();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
void clearPendingCrashInfo(Context context) {
|
|
56
|
+
context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE).edit().remove(PENDING_CRASH_KEY).apply();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
package app.capgo.webviewcrash;
|
|
2
|
+
|
|
3
|
+
import android.webkit.RenderProcessGoneDetail;
|
|
4
|
+
import android.webkit.WebView;
|
|
5
|
+
import androidx.appcompat.app.AppCompatActivity;
|
|
6
|
+
import com.getcapacitor.JSObject;
|
|
7
|
+
import com.getcapacitor.Plugin;
|
|
8
|
+
import com.getcapacitor.PluginCall;
|
|
9
|
+
import com.getcapacitor.PluginMethod;
|
|
10
|
+
import com.getcapacitor.WebViewListener;
|
|
11
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
12
|
+
|
|
13
|
+
@CapacitorPlugin(name = "WebViewCrash")
|
|
14
|
+
public class WebViewCrashPlugin extends Plugin {
|
|
15
|
+
|
|
16
|
+
private final WebViewCrash implementation = new WebViewCrash();
|
|
17
|
+
private boolean didDispatchPendingEvent = false;
|
|
18
|
+
|
|
19
|
+
private final WebViewListener webViewListener = new WebViewListener() {
|
|
20
|
+
@Override
|
|
21
|
+
public boolean onRenderProcessGone(WebView webView, RenderProcessGoneDetail detail) {
|
|
22
|
+
JSObject crashInfo = implementation.buildCrashInfo(
|
|
23
|
+
"renderProcessGone",
|
|
24
|
+
webView.getUrl(),
|
|
25
|
+
detail.didCrash(),
|
|
26
|
+
detail.rendererPriorityAtExit()
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
implementation.writePendingCrashInfo(getContext(), crashInfo);
|
|
30
|
+
didDispatchPendingEvent = false;
|
|
31
|
+
|
|
32
|
+
AppCompatActivity currentActivity = getActivity();
|
|
33
|
+
if (currentActivity != null) {
|
|
34
|
+
currentActivity.runOnUiThread(() -> {
|
|
35
|
+
bridge.reset();
|
|
36
|
+
currentActivity.recreate();
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
@Override
|
|
45
|
+
public void load() {
|
|
46
|
+
bridge.addWebViewListener(webViewListener);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Override
|
|
50
|
+
protected void handleOnDestroy() {
|
|
51
|
+
bridge.removeWebViewListener(webViewListener);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Override
|
|
55
|
+
public void addListener(PluginCall call) {
|
|
56
|
+
super.addListener(call);
|
|
57
|
+
|
|
58
|
+
if (WebViewCrash.EVENT_NAME.equals(call.getString("eventName"))) {
|
|
59
|
+
dispatchPendingCrashIfNeeded();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@PluginMethod
|
|
64
|
+
public void getPendingCrashInfo(PluginCall call) {
|
|
65
|
+
JSObject result = new JSObject();
|
|
66
|
+
result.put("value", implementation.readPendingCrashInfo(getContext()));
|
|
67
|
+
call.resolve(result);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@PluginMethod
|
|
71
|
+
public void clearPendingCrashInfo(PluginCall call) {
|
|
72
|
+
implementation.clearPendingCrashInfo(getContext());
|
|
73
|
+
didDispatchPendingEvent = false;
|
|
74
|
+
call.resolve();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@PluginMethod
|
|
78
|
+
public void simulateCrashRecovery(PluginCall call) {
|
|
79
|
+
String url = bridge.getWebView() != null ? bridge.getWebView().getUrl() : null;
|
|
80
|
+
JSObject crashInfo = implementation.buildCrashInfo("simulated", url, null, null);
|
|
81
|
+
|
|
82
|
+
implementation.writePendingCrashInfo(getContext(), crashInfo);
|
|
83
|
+
didDispatchPendingEvent = false;
|
|
84
|
+
dispatchPendingCrashIfNeeded();
|
|
85
|
+
|
|
86
|
+
JSObject result = new JSObject();
|
|
87
|
+
result.put("value", crashInfo);
|
|
88
|
+
call.resolve(result);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private void dispatchPendingCrashIfNeeded() {
|
|
92
|
+
if (didDispatchPendingEvent) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
JSObject crashInfo = implementation.readPendingCrashInfo(getContext());
|
|
97
|
+
if (crashInfo == null) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
didDispatchPendingEvent = true;
|
|
102
|
+
notifyListeners(WebViewCrash.EVENT_NAME, crashInfo);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
File without changes
|
package/dist/docs.json
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
{
|
|
2
|
+
"api": {
|
|
3
|
+
"name": "WebViewCrashPlugin",
|
|
4
|
+
"slug": "webviewcrashplugin",
|
|
5
|
+
"docs": "Capacitor API for recovered WebView crash 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 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 crash 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": "addListener",
|
|
44
|
+
"signature": "(eventName: 'webViewRestoredAfterCrash', listenerFunc: (info: WebViewCrashInfo) => void) => Promise<PluginListenerHandle>",
|
|
45
|
+
"parameters": [
|
|
46
|
+
{
|
|
47
|
+
"name": "eventName",
|
|
48
|
+
"docs": "",
|
|
49
|
+
"type": "'webViewRestoredAfterCrash'"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "listenerFunc",
|
|
53
|
+
"docs": "",
|
|
54
|
+
"type": "(info: WebViewCrashInfo) => void"
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"returns": "Promise<PluginListenerHandle>",
|
|
58
|
+
"tags": [],
|
|
59
|
+
"docs": "Fires after a new JavaScript runtime attaches a listener and a crash marker is still pending.",
|
|
60
|
+
"complexTypes": [
|
|
61
|
+
"PluginListenerHandle",
|
|
62
|
+
"WebViewCrashInfo"
|
|
63
|
+
],
|
|
64
|
+
"slug": "addlistenerwebviewrestoredaftercrash-"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"name": "removeAllListeners",
|
|
68
|
+
"signature": "() => Promise<void>",
|
|
69
|
+
"parameters": [],
|
|
70
|
+
"returns": "Promise<void>",
|
|
71
|
+
"tags": [],
|
|
72
|
+
"docs": "Removes all plugin listeners.",
|
|
73
|
+
"complexTypes": [],
|
|
74
|
+
"slug": "removealllisteners"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"properties": []
|
|
78
|
+
},
|
|
79
|
+
"interfaces": [
|
|
80
|
+
{
|
|
81
|
+
"name": "PendingCrashInfoResult",
|
|
82
|
+
"slug": "pendingcrashinforesult",
|
|
83
|
+
"docs": "Pending crash marker returned to JavaScript.",
|
|
84
|
+
"tags": [],
|
|
85
|
+
"methods": [],
|
|
86
|
+
"properties": [
|
|
87
|
+
{
|
|
88
|
+
"name": "value",
|
|
89
|
+
"tags": [],
|
|
90
|
+
"docs": "Stored crash metadata, or `null` when no marker is pending.",
|
|
91
|
+
"complexTypes": [
|
|
92
|
+
"WebViewCrashInfo"
|
|
93
|
+
],
|
|
94
|
+
"type": "WebViewCrashInfo | null"
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "WebViewCrashInfo",
|
|
100
|
+
"slug": "webviewcrashinfo",
|
|
101
|
+
"docs": "Metadata captured natively after the previous WebView process died.",
|
|
102
|
+
"tags": [],
|
|
103
|
+
"methods": [],
|
|
104
|
+
"properties": [
|
|
105
|
+
{
|
|
106
|
+
"name": "platform",
|
|
107
|
+
"tags": [],
|
|
108
|
+
"docs": "Platform that detected and stored the crash marker.",
|
|
109
|
+
"complexTypes": [
|
|
110
|
+
"WebViewCrashPlatform"
|
|
111
|
+
],
|
|
112
|
+
"type": "WebViewCrashPlatform"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "timestamp",
|
|
116
|
+
"tags": [],
|
|
117
|
+
"docs": "Unix timestamp in milliseconds for when the crash marker was written.",
|
|
118
|
+
"complexTypes": [],
|
|
119
|
+
"type": "number"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"name": "timestampISO",
|
|
123
|
+
"tags": [],
|
|
124
|
+
"docs": "ISO-8601 version of `timestamp`.",
|
|
125
|
+
"complexTypes": [],
|
|
126
|
+
"type": "string"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"name": "reason",
|
|
130
|
+
"tags": [],
|
|
131
|
+
"docs": "Platform-specific reason for the crash marker.",
|
|
132
|
+
"complexTypes": [
|
|
133
|
+
"WebViewCrashReason"
|
|
134
|
+
],
|
|
135
|
+
"type": "WebViewCrashReason"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"name": "url",
|
|
139
|
+
"tags": [],
|
|
140
|
+
"docs": "Last known WebView URL when the crash marker was written.",
|
|
141
|
+
"complexTypes": [],
|
|
142
|
+
"type": "string | undefined"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"name": "didCrash",
|
|
146
|
+
"tags": [],
|
|
147
|
+
"docs": "Android-only hint from `RenderProcessGoneDetail.didCrash()`.",
|
|
148
|
+
"complexTypes": [],
|
|
149
|
+
"type": "boolean | undefined"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"name": "rendererPriorityAtExit",
|
|
153
|
+
"tags": [],
|
|
154
|
+
"docs": "Android-only renderer priority reported at exit.",
|
|
155
|
+
"complexTypes": [],
|
|
156
|
+
"type": "number | undefined"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "appState",
|
|
160
|
+
"tags": [],
|
|
161
|
+
"docs": "iOS-only application state captured when the crash marker was written.",
|
|
162
|
+
"complexTypes": [
|
|
163
|
+
"WebViewCrashAppState"
|
|
164
|
+
],
|
|
165
|
+
"type": "WebViewCrashAppState"
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"name": "PluginListenerHandle",
|
|
171
|
+
"slug": "pluginlistenerhandle",
|
|
172
|
+
"docs": "",
|
|
173
|
+
"tags": [],
|
|
174
|
+
"methods": [],
|
|
175
|
+
"properties": [
|
|
176
|
+
{
|
|
177
|
+
"name": "remove",
|
|
178
|
+
"tags": [],
|
|
179
|
+
"docs": "",
|
|
180
|
+
"complexTypes": [],
|
|
181
|
+
"type": "() => Promise<void>"
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
"enums": [],
|
|
187
|
+
"typeAliases": [
|
|
188
|
+
{
|
|
189
|
+
"name": "WebViewCrashPlatform",
|
|
190
|
+
"slug": "webviewcrashplatform",
|
|
191
|
+
"docs": "Platform that produced the stored crash marker.",
|
|
192
|
+
"types": [
|
|
193
|
+
{
|
|
194
|
+
"text": "'android'",
|
|
195
|
+
"complexTypes": []
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"text": "'ios'",
|
|
199
|
+
"complexTypes": []
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"text": "'web'",
|
|
203
|
+
"complexTypes": []
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"name": "WebViewCrashReason",
|
|
209
|
+
"slug": "webviewcrashreason",
|
|
210
|
+
"docs": "Native reason reported for the previous WebView failure.",
|
|
211
|
+
"types": [
|
|
212
|
+
{
|
|
213
|
+
"text": "'renderProcessGone'",
|
|
214
|
+
"complexTypes": []
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"text": "'webContentProcessDidTerminate'",
|
|
218
|
+
"complexTypes": []
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"text": "'simulated'",
|
|
222
|
+
"complexTypes": []
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"name": "WebViewCrashAppState",
|
|
228
|
+
"slug": "webviewcrashappstate",
|
|
229
|
+
"docs": "Best-effort application state captured on iOS when the WebView process died.",
|
|
230
|
+
"types": [
|
|
231
|
+
{
|
|
232
|
+
"text": "'active'",
|
|
233
|
+
"complexTypes": []
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"text": "'inactive'",
|
|
237
|
+
"complexTypes": []
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"text": "'background'",
|
|
241
|
+
"complexTypes": []
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"text": "'unknown'",
|
|
245
|
+
"complexTypes": []
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
],
|
|
250
|
+
"pluginConfigs": []
|
|
251
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { PluginListenerHandle } from '@capacitor/core';
|
|
2
|
+
/**
|
|
3
|
+
* Native reason reported for the previous WebView failure.
|
|
4
|
+
*/
|
|
5
|
+
export type WebViewCrashReason = 'renderProcessGone' | 'webContentProcessDidTerminate' | 'simulated';
|
|
6
|
+
/**
|
|
7
|
+
* Platform that produced the stored crash marker.
|
|
8
|
+
*/
|
|
9
|
+
export type WebViewCrashPlatform = 'android' | 'ios' | 'web';
|
|
10
|
+
/**
|
|
11
|
+
* Best-effort application state captured on iOS when the WebView process died.
|
|
12
|
+
*/
|
|
13
|
+
export type WebViewCrashAppState = 'active' | 'inactive' | 'background' | 'unknown';
|
|
14
|
+
/**
|
|
15
|
+
* Metadata captured natively after the previous WebView process died.
|
|
16
|
+
*/
|
|
17
|
+
export interface WebViewCrashInfo {
|
|
18
|
+
/**
|
|
19
|
+
* Platform that detected and stored the crash marker.
|
|
20
|
+
*/
|
|
21
|
+
platform: WebViewCrashPlatform;
|
|
22
|
+
/**
|
|
23
|
+
* Unix timestamp in milliseconds for when the crash marker was written.
|
|
24
|
+
*/
|
|
25
|
+
timestamp: number;
|
|
26
|
+
/**
|
|
27
|
+
* ISO-8601 version of `timestamp`.
|
|
28
|
+
*/
|
|
29
|
+
timestampISO: string;
|
|
30
|
+
/**
|
|
31
|
+
* Platform-specific reason for the crash marker.
|
|
32
|
+
*/
|
|
33
|
+
reason: WebViewCrashReason;
|
|
34
|
+
/**
|
|
35
|
+
* Last known WebView URL when the crash marker was written.
|
|
36
|
+
*/
|
|
37
|
+
url?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Android-only hint from `RenderProcessGoneDetail.didCrash()`.
|
|
40
|
+
*/
|
|
41
|
+
didCrash?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Android-only renderer priority reported at exit.
|
|
44
|
+
*/
|
|
45
|
+
rendererPriorityAtExit?: number;
|
|
46
|
+
/**
|
|
47
|
+
* iOS-only application state captured when the crash marker was written.
|
|
48
|
+
*/
|
|
49
|
+
appState?: WebViewCrashAppState;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Pending crash marker returned to JavaScript.
|
|
53
|
+
*/
|
|
54
|
+
export interface PendingCrashInfoResult {
|
|
55
|
+
/**
|
|
56
|
+
* Stored crash metadata, or `null` when no marker is pending.
|
|
57
|
+
*/
|
|
58
|
+
value: WebViewCrashInfo | null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Capacitor API for recovered WebView crash detection.
|
|
62
|
+
*/
|
|
63
|
+
export interface WebViewCrashPlugin {
|
|
64
|
+
/**
|
|
65
|
+
* Returns the pending native crash marker, if one exists.
|
|
66
|
+
*/
|
|
67
|
+
getPendingCrashInfo(): Promise<PendingCrashInfoResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Clears the stored crash marker after the app has handled recovery.
|
|
70
|
+
*/
|
|
71
|
+
clearPendingCrashInfo(): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Creates a fake crash marker so recovery flows can be tested locally.
|
|
74
|
+
*/
|
|
75
|
+
simulateCrashRecovery(): Promise<PendingCrashInfoResult>;
|
|
76
|
+
/**
|
|
77
|
+
* Fires after a new JavaScript runtime attaches a listener and a crash marker is still pending.
|
|
78
|
+
*/
|
|
79
|
+
addListener(eventName: 'webViewRestoredAfterCrash', listenerFunc: (info: WebViewCrashInfo) => void): Promise<PluginListenerHandle>;
|
|
80
|
+
/**
|
|
81
|
+
* Removes all plugin listeners.
|
|
82
|
+
*/
|
|
83
|
+
removeAllListeners(): Promise<void>;
|
|
84
|
+
}
|
|
@@ -0,0 +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"]}
|
|
@@ -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,17 @@
|
|
|
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 didDispatchPendingEvent;
|
|
6
|
+
getPendingCrashInfo(): Promise<PendingCrashInfoResult>;
|
|
7
|
+
clearPendingCrashInfo(): Promise<void>;
|
|
8
|
+
simulateCrashRecovery(): Promise<PendingCrashInfoResult>;
|
|
9
|
+
addListener(eventName: string, listenerFunc: (...args: any[]) => any): Promise<PluginListenerHandle>;
|
|
10
|
+
private flushPendingCrashEvent;
|
|
11
|
+
private buildCrashInfo;
|
|
12
|
+
private readPendingCrashInfo;
|
|
13
|
+
private writePendingCrashInfo;
|
|
14
|
+
private removePendingCrashInfo;
|
|
15
|
+
private static readonly eventName;
|
|
16
|
+
private static readonly storageKey;
|
|
17
|
+
}
|
package/dist/esm/web.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { WebPlugin } from '@capacitor/core';
|
|
2
|
+
export class WebViewCrashWeb extends WebPlugin {
|
|
3
|
+
constructor() {
|
|
4
|
+
super(...arguments);
|
|
5
|
+
this.didDispatchPendingEvent = false;
|
|
6
|
+
}
|
|
7
|
+
async getPendingCrashInfo() {
|
|
8
|
+
return { value: this.readPendingCrashInfo() };
|
|
9
|
+
}
|
|
10
|
+
async clearPendingCrashInfo() {
|
|
11
|
+
this.removePendingCrashInfo();
|
|
12
|
+
this.didDispatchPendingEvent = false;
|
|
13
|
+
}
|
|
14
|
+
async simulateCrashRecovery() {
|
|
15
|
+
const value = this.buildCrashInfo();
|
|
16
|
+
this.writePendingCrashInfo(value);
|
|
17
|
+
this.didDispatchPendingEvent = false;
|
|
18
|
+
this.flushPendingCrashEvent();
|
|
19
|
+
return { value };
|
|
20
|
+
}
|
|
21
|
+
async addListener(eventName, listenerFunc) {
|
|
22
|
+
const handle = await super.addListener(eventName, listenerFunc);
|
|
23
|
+
if (eventName === WebViewCrashWeb.eventName) {
|
|
24
|
+
this.flushPendingCrashEvent();
|
|
25
|
+
}
|
|
26
|
+
return handle;
|
|
27
|
+
}
|
|
28
|
+
flushPendingCrashEvent() {
|
|
29
|
+
if (this.didDispatchPendingEvent) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const value = this.readPendingCrashInfo();
|
|
33
|
+
if (!value) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
this.didDispatchPendingEvent = true;
|
|
37
|
+
this.notifyListeners(WebViewCrashWeb.eventName, value);
|
|
38
|
+
}
|
|
39
|
+
buildCrashInfo() {
|
|
40
|
+
var _a;
|
|
41
|
+
const timestamp = Date.now();
|
|
42
|
+
return {
|
|
43
|
+
platform: 'web',
|
|
44
|
+
timestamp,
|
|
45
|
+
timestampISO: new Date(timestamp).toISOString(),
|
|
46
|
+
reason: 'simulated',
|
|
47
|
+
url: (_a = globalThis.location) === null || _a === void 0 ? void 0 : _a.href,
|
|
48
|
+
appState: 'active',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
readPendingCrashInfo() {
|
|
52
|
+
var _a;
|
|
53
|
+
const raw = (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.getItem(WebViewCrashWeb.storageKey);
|
|
54
|
+
if (!raw) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
return JSON.parse(raw);
|
|
59
|
+
}
|
|
60
|
+
catch (_b) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
writePendingCrashInfo(value) {
|
|
65
|
+
var _a;
|
|
66
|
+
(_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.setItem(WebViewCrashWeb.storageKey, JSON.stringify(value));
|
|
67
|
+
}
|
|
68
|
+
removePendingCrashInfo() {
|
|
69
|
+
var _a;
|
|
70
|
+
(_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.removeItem(WebViewCrashWeb.storageKey);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
WebViewCrashWeb.eventName = 'webViewRestoredAfterCrash';
|
|
74
|
+
WebViewCrashWeb.storageKey = 'capgo.webview-crash.pending';
|
|
75
|
+
//# sourceMappingURL=web.js.map
|