@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.
- package/Package.swift +2 -2
- package/README.md +107 -30
- package/android/src/main/java/app/capgo/webviewcrash/WebViewCrash.java +231 -1
- package/android/src/main/java/app/capgo/webviewcrash/WebViewCrashPlugin.java +99 -18
- package/dist/docs.json +37 -17
- package/dist/esm/definitions.d.ts +71 -16
- package/dist/esm/definitions.js +1 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +5 -2
- package/dist/esm/web.js +28 -13
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +28 -13
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +28 -13
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/WebViewCrashPlugin/WebViewCrash.swift +256 -2
- package/ios/Sources/WebViewCrashPlugin/WebViewCrashPlugin.swift +101 -11
- package/package.json +5 -3
package/dist/esm/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,MAAM,OAAO,eAAgB,SAAQ,SAAS;IAA9C;;QACU,4BAAuB,GAAG,
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\nimport { WebPlugin } from '@capacitor/core';\n\nimport type { PendingCrashInfoResult, WebViewCrashInfo, WebViewCrashPlugin } from './definitions';\n\nexport class WebViewCrashWeb extends WebPlugin implements WebViewCrashPlugin {\n private dispatchedPendingEvents = new Set<string>();\n\n async getPendingCrashInfo(): Promise<PendingCrashInfoResult> {\n return { value: this.readPendingCrashInfo() };\n }\n\n async clearPendingCrashInfo(): Promise<void> {\n this.removePendingCrashInfo();\n this.dispatchedPendingEvents.clear();\n }\n\n async simulateCrashRecovery(): Promise<PendingCrashInfoResult> {\n const value = this.buildCrashInfo();\n this.writePendingCrashInfo(value);\n this.dispatchedPendingEvents.clear();\n this.flushPendingCrashEvent();\n this.flushPendingCrashEvent(WebViewCrashWeb.restartEventName);\n return { value };\n }\n\n async restartWebView(): Promise<PendingCrashInfoResult> {\n const value = this.buildCrashInfo('manualRestart');\n this.writePendingCrashInfo(value);\n this.dispatchedPendingEvents.clear();\n this.flushPendingCrashEvent(WebViewCrashWeb.restartEventName);\n return { value };\n }\n\n async addListener(eventName: string, listenerFunc: (...args: any[]) => any): Promise<PluginListenerHandle> {\n const handle = await super.addListener(eventName, listenerFunc);\n if (eventName === WebViewCrashWeb.crashEventName || eventName === WebViewCrashWeb.restartEventName) {\n this.flushPendingCrashEvent(eventName);\n }\n return handle;\n }\n\n private flushPendingCrashEvent(eventName = WebViewCrashWeb.crashEventName): void {\n if (this.dispatchedPendingEvents.has(eventName)) {\n return;\n }\n\n const value = this.readPendingCrashInfo();\n if (!value || !this.shouldDispatchEvent(eventName, value)) {\n return;\n }\n\n this.dispatchedPendingEvents.add(eventName);\n this.notifyListeners(eventName, value);\n }\n\n private shouldDispatchEvent(eventName: string, value: WebViewCrashInfo): boolean {\n if (eventName === WebViewCrashWeb.crashEventName) {\n return value.reason !== 'periodicRestart' && value.reason !== 'manualRestart';\n }\n\n return eventName === WebViewCrashWeb.restartEventName;\n }\n\n private buildCrashInfo(reason: WebViewCrashInfo['reason'] = 'simulated'): WebViewCrashInfo {\n const timestamp = Date.now();\n return {\n platform: 'web',\n timestamp,\n timestampISO: new Date(timestamp).toISOString(),\n reason,\n url: globalThis.location?.href,\n appState: 'active',\n };\n }\n\n private readPendingCrashInfo(): WebViewCrashInfo | null {\n const raw = globalThis.localStorage?.getItem(WebViewCrashWeb.storageKey);\n if (!raw) {\n return null;\n }\n\n try {\n return JSON.parse(raw) as WebViewCrashInfo;\n } catch {\n return null;\n }\n }\n\n private writePendingCrashInfo(value: WebViewCrashInfo): void {\n globalThis.localStorage?.setItem(WebViewCrashWeb.storageKey, JSON.stringify(value));\n }\n\n private removePendingCrashInfo(): void {\n globalThis.localStorage?.removeItem(WebViewCrashWeb.storageKey);\n }\n\n private static readonly crashEventName = 'webViewRestoredAfterCrash';\n private static readonly restartEventName = 'webViewRestoredAfterRestart';\n private static readonly storageKey = 'capgo.webview-crash.pending';\n}\n"]}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -9,48 +9,62 @@ const WebViewCrash = core.registerPlugin('WebViewCrash', {
|
|
|
9
9
|
class WebViewCrashWeb extends core.WebPlugin {
|
|
10
10
|
constructor() {
|
|
11
11
|
super(...arguments);
|
|
12
|
-
this.
|
|
12
|
+
this.dispatchedPendingEvents = new Set();
|
|
13
13
|
}
|
|
14
14
|
async getPendingCrashInfo() {
|
|
15
15
|
return { value: this.readPendingCrashInfo() };
|
|
16
16
|
}
|
|
17
17
|
async clearPendingCrashInfo() {
|
|
18
18
|
this.removePendingCrashInfo();
|
|
19
|
-
this.
|
|
19
|
+
this.dispatchedPendingEvents.clear();
|
|
20
20
|
}
|
|
21
21
|
async simulateCrashRecovery() {
|
|
22
22
|
const value = this.buildCrashInfo();
|
|
23
23
|
this.writePendingCrashInfo(value);
|
|
24
|
-
this.
|
|
24
|
+
this.dispatchedPendingEvents.clear();
|
|
25
25
|
this.flushPendingCrashEvent();
|
|
26
|
+
this.flushPendingCrashEvent(WebViewCrashWeb.restartEventName);
|
|
27
|
+
return { value };
|
|
28
|
+
}
|
|
29
|
+
async restartWebView() {
|
|
30
|
+
const value = this.buildCrashInfo('manualRestart');
|
|
31
|
+
this.writePendingCrashInfo(value);
|
|
32
|
+
this.dispatchedPendingEvents.clear();
|
|
33
|
+
this.flushPendingCrashEvent(WebViewCrashWeb.restartEventName);
|
|
26
34
|
return { value };
|
|
27
35
|
}
|
|
28
36
|
async addListener(eventName, listenerFunc) {
|
|
29
37
|
const handle = await super.addListener(eventName, listenerFunc);
|
|
30
|
-
if (eventName === WebViewCrashWeb.eventName) {
|
|
31
|
-
this.flushPendingCrashEvent();
|
|
38
|
+
if (eventName === WebViewCrashWeb.crashEventName || eventName === WebViewCrashWeb.restartEventName) {
|
|
39
|
+
this.flushPendingCrashEvent(eventName);
|
|
32
40
|
}
|
|
33
41
|
return handle;
|
|
34
42
|
}
|
|
35
|
-
flushPendingCrashEvent() {
|
|
36
|
-
if (this.
|
|
43
|
+
flushPendingCrashEvent(eventName = WebViewCrashWeb.crashEventName) {
|
|
44
|
+
if (this.dispatchedPendingEvents.has(eventName)) {
|
|
37
45
|
return;
|
|
38
46
|
}
|
|
39
47
|
const value = this.readPendingCrashInfo();
|
|
40
|
-
if (!value) {
|
|
48
|
+
if (!value || !this.shouldDispatchEvent(eventName, value)) {
|
|
41
49
|
return;
|
|
42
50
|
}
|
|
43
|
-
this.
|
|
44
|
-
this.notifyListeners(
|
|
51
|
+
this.dispatchedPendingEvents.add(eventName);
|
|
52
|
+
this.notifyListeners(eventName, value);
|
|
53
|
+
}
|
|
54
|
+
shouldDispatchEvent(eventName, value) {
|
|
55
|
+
if (eventName === WebViewCrashWeb.crashEventName) {
|
|
56
|
+
return value.reason !== 'periodicRestart' && value.reason !== 'manualRestart';
|
|
57
|
+
}
|
|
58
|
+
return eventName === WebViewCrashWeb.restartEventName;
|
|
45
59
|
}
|
|
46
|
-
buildCrashInfo() {
|
|
60
|
+
buildCrashInfo(reason = 'simulated') {
|
|
47
61
|
var _a;
|
|
48
62
|
const timestamp = Date.now();
|
|
49
63
|
return {
|
|
50
64
|
platform: 'web',
|
|
51
65
|
timestamp,
|
|
52
66
|
timestampISO: new Date(timestamp).toISOString(),
|
|
53
|
-
reason
|
|
67
|
+
reason,
|
|
54
68
|
url: (_a = globalThis.location) === null || _a === void 0 ? void 0 : _a.href,
|
|
55
69
|
appState: 'active',
|
|
56
70
|
};
|
|
@@ -77,7 +91,8 @@ class WebViewCrashWeb extends core.WebPlugin {
|
|
|
77
91
|
(_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.removeItem(WebViewCrashWeb.storageKey);
|
|
78
92
|
}
|
|
79
93
|
}
|
|
80
|
-
WebViewCrashWeb.
|
|
94
|
+
WebViewCrashWeb.crashEventName = 'webViewRestoredAfterCrash';
|
|
95
|
+
WebViewCrashWeb.restartEventName = 'webViewRestoredAfterRestart';
|
|
81
96
|
WebViewCrashWeb.storageKey = 'capgo.webview-crash.pending';
|
|
82
97
|
|
|
83
98
|
var web = /*#__PURE__*/Object.freeze({
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst WebViewCrash = registerPlugin('WebViewCrash', {\n web: () => import('./web').then((m) => new m.WebViewCrashWeb()),\n});\nexport * from './definitions';\nexport { WebViewCrash };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class WebViewCrashWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst WebViewCrash = registerPlugin('WebViewCrash', {\n web: () => import('./web').then((m) => new m.WebViewCrashWeb()),\n});\nexport * from './definitions';\nexport { WebViewCrash };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class WebViewCrashWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.dispatchedPendingEvents = new Set();\n }\n async getPendingCrashInfo() {\n return { value: this.readPendingCrashInfo() };\n }\n async clearPendingCrashInfo() {\n this.removePendingCrashInfo();\n this.dispatchedPendingEvents.clear();\n }\n async simulateCrashRecovery() {\n const value = this.buildCrashInfo();\n this.writePendingCrashInfo(value);\n this.dispatchedPendingEvents.clear();\n this.flushPendingCrashEvent();\n this.flushPendingCrashEvent(WebViewCrashWeb.restartEventName);\n return { value };\n }\n async restartWebView() {\n const value = this.buildCrashInfo('manualRestart');\n this.writePendingCrashInfo(value);\n this.dispatchedPendingEvents.clear();\n this.flushPendingCrashEvent(WebViewCrashWeb.restartEventName);\n return { value };\n }\n async addListener(eventName, listenerFunc) {\n const handle = await super.addListener(eventName, listenerFunc);\n if (eventName === WebViewCrashWeb.crashEventName || eventName === WebViewCrashWeb.restartEventName) {\n this.flushPendingCrashEvent(eventName);\n }\n return handle;\n }\n flushPendingCrashEvent(eventName = WebViewCrashWeb.crashEventName) {\n if (this.dispatchedPendingEvents.has(eventName)) {\n return;\n }\n const value = this.readPendingCrashInfo();\n if (!value || !this.shouldDispatchEvent(eventName, value)) {\n return;\n }\n this.dispatchedPendingEvents.add(eventName);\n this.notifyListeners(eventName, value);\n }\n shouldDispatchEvent(eventName, value) {\n if (eventName === WebViewCrashWeb.crashEventName) {\n return value.reason !== 'periodicRestart' && value.reason !== 'manualRestart';\n }\n return eventName === WebViewCrashWeb.restartEventName;\n }\n buildCrashInfo(reason = 'simulated') {\n var _a;\n const timestamp = Date.now();\n return {\n platform: 'web',\n timestamp,\n timestampISO: new Date(timestamp).toISOString(),\n reason,\n url: (_a = globalThis.location) === null || _a === void 0 ? void 0 : _a.href,\n appState: 'active',\n };\n }\n readPendingCrashInfo() {\n var _a;\n const raw = (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.getItem(WebViewCrashWeb.storageKey);\n if (!raw) {\n return null;\n }\n try {\n return JSON.parse(raw);\n }\n catch (_b) {\n return null;\n }\n }\n writePendingCrashInfo(value) {\n var _a;\n (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.setItem(WebViewCrashWeb.storageKey, JSON.stringify(value));\n }\n removePendingCrashInfo() {\n var _a;\n (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.removeItem(WebViewCrashWeb.storageKey);\n }\n}\nWebViewCrashWeb.crashEventName = 'webViewRestoredAfterCrash';\nWebViewCrashWeb.restartEventName = 'webViewRestoredAfterRestart';\nWebViewCrashWeb.storageKey = 'capgo.webview-crash.pending';\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
|
package/dist/plugin.js
CHANGED
|
@@ -8,48 +8,62 @@ var capacitorWebViewCrash = (function (exports, core) {
|
|
|
8
8
|
class WebViewCrashWeb extends core.WebPlugin {
|
|
9
9
|
constructor() {
|
|
10
10
|
super(...arguments);
|
|
11
|
-
this.
|
|
11
|
+
this.dispatchedPendingEvents = new Set();
|
|
12
12
|
}
|
|
13
13
|
async getPendingCrashInfo() {
|
|
14
14
|
return { value: this.readPendingCrashInfo() };
|
|
15
15
|
}
|
|
16
16
|
async clearPendingCrashInfo() {
|
|
17
17
|
this.removePendingCrashInfo();
|
|
18
|
-
this.
|
|
18
|
+
this.dispatchedPendingEvents.clear();
|
|
19
19
|
}
|
|
20
20
|
async simulateCrashRecovery() {
|
|
21
21
|
const value = this.buildCrashInfo();
|
|
22
22
|
this.writePendingCrashInfo(value);
|
|
23
|
-
this.
|
|
23
|
+
this.dispatchedPendingEvents.clear();
|
|
24
24
|
this.flushPendingCrashEvent();
|
|
25
|
+
this.flushPendingCrashEvent(WebViewCrashWeb.restartEventName);
|
|
26
|
+
return { value };
|
|
27
|
+
}
|
|
28
|
+
async restartWebView() {
|
|
29
|
+
const value = this.buildCrashInfo('manualRestart');
|
|
30
|
+
this.writePendingCrashInfo(value);
|
|
31
|
+
this.dispatchedPendingEvents.clear();
|
|
32
|
+
this.flushPendingCrashEvent(WebViewCrashWeb.restartEventName);
|
|
25
33
|
return { value };
|
|
26
34
|
}
|
|
27
35
|
async addListener(eventName, listenerFunc) {
|
|
28
36
|
const handle = await super.addListener(eventName, listenerFunc);
|
|
29
|
-
if (eventName === WebViewCrashWeb.eventName) {
|
|
30
|
-
this.flushPendingCrashEvent();
|
|
37
|
+
if (eventName === WebViewCrashWeb.crashEventName || eventName === WebViewCrashWeb.restartEventName) {
|
|
38
|
+
this.flushPendingCrashEvent(eventName);
|
|
31
39
|
}
|
|
32
40
|
return handle;
|
|
33
41
|
}
|
|
34
|
-
flushPendingCrashEvent() {
|
|
35
|
-
if (this.
|
|
42
|
+
flushPendingCrashEvent(eventName = WebViewCrashWeb.crashEventName) {
|
|
43
|
+
if (this.dispatchedPendingEvents.has(eventName)) {
|
|
36
44
|
return;
|
|
37
45
|
}
|
|
38
46
|
const value = this.readPendingCrashInfo();
|
|
39
|
-
if (!value) {
|
|
47
|
+
if (!value || !this.shouldDispatchEvent(eventName, value)) {
|
|
40
48
|
return;
|
|
41
49
|
}
|
|
42
|
-
this.
|
|
43
|
-
this.notifyListeners(
|
|
50
|
+
this.dispatchedPendingEvents.add(eventName);
|
|
51
|
+
this.notifyListeners(eventName, value);
|
|
52
|
+
}
|
|
53
|
+
shouldDispatchEvent(eventName, value) {
|
|
54
|
+
if (eventName === WebViewCrashWeb.crashEventName) {
|
|
55
|
+
return value.reason !== 'periodicRestart' && value.reason !== 'manualRestart';
|
|
56
|
+
}
|
|
57
|
+
return eventName === WebViewCrashWeb.restartEventName;
|
|
44
58
|
}
|
|
45
|
-
buildCrashInfo() {
|
|
59
|
+
buildCrashInfo(reason = 'simulated') {
|
|
46
60
|
var _a;
|
|
47
61
|
const timestamp = Date.now();
|
|
48
62
|
return {
|
|
49
63
|
platform: 'web',
|
|
50
64
|
timestamp,
|
|
51
65
|
timestampISO: new Date(timestamp).toISOString(),
|
|
52
|
-
reason
|
|
66
|
+
reason,
|
|
53
67
|
url: (_a = globalThis.location) === null || _a === void 0 ? void 0 : _a.href,
|
|
54
68
|
appState: 'active',
|
|
55
69
|
};
|
|
@@ -76,7 +90,8 @@ var capacitorWebViewCrash = (function (exports, core) {
|
|
|
76
90
|
(_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.removeItem(WebViewCrashWeb.storageKey);
|
|
77
91
|
}
|
|
78
92
|
}
|
|
79
|
-
WebViewCrashWeb.
|
|
93
|
+
WebViewCrashWeb.crashEventName = 'webViewRestoredAfterCrash';
|
|
94
|
+
WebViewCrashWeb.restartEventName = 'webViewRestoredAfterRestart';
|
|
80
95
|
WebViewCrashWeb.storageKey = 'capgo.webview-crash.pending';
|
|
81
96
|
|
|
82
97
|
var web = /*#__PURE__*/Object.freeze({
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst WebViewCrash = registerPlugin('WebViewCrash', {\n web: () => import('./web').then((m) => new m.WebViewCrashWeb()),\n});\nexport * from './definitions';\nexport { WebViewCrash };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class WebViewCrashWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst WebViewCrash = registerPlugin('WebViewCrash', {\n web: () => import('./web').then((m) => new m.WebViewCrashWeb()),\n});\nexport * from './definitions';\nexport { WebViewCrash };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class WebViewCrashWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.dispatchedPendingEvents = new Set();\n }\n async getPendingCrashInfo() {\n return { value: this.readPendingCrashInfo() };\n }\n async clearPendingCrashInfo() {\n this.removePendingCrashInfo();\n this.dispatchedPendingEvents.clear();\n }\n async simulateCrashRecovery() {\n const value = this.buildCrashInfo();\n this.writePendingCrashInfo(value);\n this.dispatchedPendingEvents.clear();\n this.flushPendingCrashEvent();\n this.flushPendingCrashEvent(WebViewCrashWeb.restartEventName);\n return { value };\n }\n async restartWebView() {\n const value = this.buildCrashInfo('manualRestart');\n this.writePendingCrashInfo(value);\n this.dispatchedPendingEvents.clear();\n this.flushPendingCrashEvent(WebViewCrashWeb.restartEventName);\n return { value };\n }\n async addListener(eventName, listenerFunc) {\n const handle = await super.addListener(eventName, listenerFunc);\n if (eventName === WebViewCrashWeb.crashEventName || eventName === WebViewCrashWeb.restartEventName) {\n this.flushPendingCrashEvent(eventName);\n }\n return handle;\n }\n flushPendingCrashEvent(eventName = WebViewCrashWeb.crashEventName) {\n if (this.dispatchedPendingEvents.has(eventName)) {\n return;\n }\n const value = this.readPendingCrashInfo();\n if (!value || !this.shouldDispatchEvent(eventName, value)) {\n return;\n }\n this.dispatchedPendingEvents.add(eventName);\n this.notifyListeners(eventName, value);\n }\n shouldDispatchEvent(eventName, value) {\n if (eventName === WebViewCrashWeb.crashEventName) {\n return value.reason !== 'periodicRestart' && value.reason !== 'manualRestart';\n }\n return eventName === WebViewCrashWeb.restartEventName;\n }\n buildCrashInfo(reason = 'simulated') {\n var _a;\n const timestamp = Date.now();\n return {\n platform: 'web',\n timestamp,\n timestampISO: new Date(timestamp).toISOString(),\n reason,\n url: (_a = globalThis.location) === null || _a === void 0 ? void 0 : _a.href,\n appState: 'active',\n };\n }\n readPendingCrashInfo() {\n var _a;\n const raw = (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.getItem(WebViewCrashWeb.storageKey);\n if (!raw) {\n return null;\n }\n try {\n return JSON.parse(raw);\n }\n catch (_b) {\n return null;\n }\n }\n writePendingCrashInfo(value) {\n var _a;\n (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.setItem(WebViewCrashWeb.storageKey, JSON.stringify(value));\n }\n removePendingCrashInfo() {\n var _a;\n (_a = globalThis.localStorage) === null || _a === void 0 ? void 0 : _a.removeItem(WebViewCrashWeb.storageKey);\n }\n}\nWebViewCrashWeb.crashEventName = 'webViewRestoredAfterCrash';\nWebViewCrashWeb.restartEventName = 'webViewRestoredAfterRestart';\nWebViewCrashWeb.storageKey = 'capgo.webview-crash.pending';\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
|
|
@@ -5,7 +5,10 @@ import UIKit
|
|
|
5
5
|
import WebKit
|
|
6
6
|
|
|
7
7
|
enum WebViewCrashBridge {
|
|
8
|
-
static let
|
|
8
|
+
static let crashEventName = "webViewRestoredAfterCrash"
|
|
9
|
+
static let restartEventName = "webViewRestoredAfterRestart"
|
|
10
|
+
static let periodicRestartReason = "periodicRestart"
|
|
11
|
+
static let manualRestartReason = "manualRestart"
|
|
9
12
|
|
|
10
13
|
static func pendingResult(_ value: [String: Any]?) -> [String: Any] {
|
|
11
14
|
[
|
|
@@ -14,6 +17,230 @@ enum WebViewCrashBridge {
|
|
|
14
17
|
}
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
struct WebViewCrashRestartOptions {
|
|
21
|
+
let restartOnCrash: Bool
|
|
22
|
+
let restartIntervalMs: Int
|
|
23
|
+
let restartCron: WebViewCrashCronSchedule?
|
|
24
|
+
let restartAfterCrashDelayMs: Int
|
|
25
|
+
|
|
26
|
+
init(config: PluginConfig? = nil) {
|
|
27
|
+
let intervalMs = max(0, config?.getInt("restartIntervalMs", 0) ?? 0)
|
|
28
|
+
let cronExpression = config?.getString("restartCron", "") ?? ""
|
|
29
|
+
|
|
30
|
+
if intervalMs > 0 && !cronExpression.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
31
|
+
fatalError("Invalid WebViewCrash config: set either restartIntervalMs or restartCron, not both.")
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
restartOnCrash = config?.getBoolean("restartOnCrash", true) ?? true
|
|
35
|
+
restartIntervalMs = intervalMs
|
|
36
|
+
restartCron = WebViewCrashCronSchedule(cronExpression)
|
|
37
|
+
restartAfterCrashDelayMs = max(0, config?.getInt("restartAfterCrashDelayMs", 0) ?? 0)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
var restartIntervalSeconds: TimeInterval {
|
|
41
|
+
TimeInterval(restartIntervalMs) / 1_000
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
var nextRestartDelaySeconds: TimeInterval? {
|
|
45
|
+
if let restartCron {
|
|
46
|
+
return restartCron.nextDelaySeconds()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return restartIntervalMs > 0 ? restartIntervalSeconds : nil
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
var restartAfterCrashDelaySeconds: TimeInterval {
|
|
53
|
+
TimeInterval(restartAfterCrashDelayMs) / 1_000
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
struct WebViewCrashCronSchedule {
|
|
58
|
+
private static let searchLimitMinutes = 366 * 24 * 60 * 5
|
|
59
|
+
|
|
60
|
+
private let minutes: CronField
|
|
61
|
+
private let hours: CronField
|
|
62
|
+
private let daysOfMonth: CronField
|
|
63
|
+
private let months: CronField
|
|
64
|
+
private let daysOfWeek: CronField
|
|
65
|
+
|
|
66
|
+
init?(_ expression: String?) {
|
|
67
|
+
guard let expression else {
|
|
68
|
+
return nil
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let parts = expression
|
|
72
|
+
.split(whereSeparator: { $0 == " " || $0 == "\t" || $0 == "\n" })
|
|
73
|
+
.map(String.init)
|
|
74
|
+
|
|
75
|
+
guard parts.count == 5,
|
|
76
|
+
let minutes = CronField(parts[0], min: 0, max: 59),
|
|
77
|
+
let hours = CronField(parts[1], min: 0, max: 23),
|
|
78
|
+
let daysOfMonth = CronField(parts[2], min: 1, max: 31),
|
|
79
|
+
let months = CronField(parts[3], min: 1, max: 12),
|
|
80
|
+
let daysOfWeek = CronField(parts[4], min: 0, max: 7, normalizeSunday: true) else {
|
|
81
|
+
return nil
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
self.minutes = minutes
|
|
85
|
+
self.hours = hours
|
|
86
|
+
self.daysOfMonth = daysOfMonth
|
|
87
|
+
self.months = months
|
|
88
|
+
self.daysOfWeek = daysOfWeek
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
func nextDelaySeconds(from date: Date = Date(), calendar sourceCalendar: Calendar = .current) -> TimeInterval? {
|
|
92
|
+
var calendar = sourceCalendar
|
|
93
|
+
calendar.timeZone = .current
|
|
94
|
+
|
|
95
|
+
guard var candidate = calendar.date(byAdding: .minute, value: 1, to: date) else {
|
|
96
|
+
return nil
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
var components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: candidate)
|
|
100
|
+
components.second = 0
|
|
101
|
+
components.nanosecond = 0
|
|
102
|
+
guard let roundedCandidate = calendar.date(from: components) else {
|
|
103
|
+
return nil
|
|
104
|
+
}
|
|
105
|
+
candidate = roundedCandidate
|
|
106
|
+
|
|
107
|
+
for _ in 0..<Self.searchLimitMinutes {
|
|
108
|
+
if matches(candidate, calendar: calendar) {
|
|
109
|
+
return max(0, candidate.timeIntervalSince(date))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
guard let nextCandidate = calendar.date(byAdding: .minute, value: 1, to: candidate) else {
|
|
113
|
+
return nil
|
|
114
|
+
}
|
|
115
|
+
candidate = nextCandidate
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return nil
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private func matches(_ date: Date, calendar: Calendar) -> Bool {
|
|
122
|
+
let components = calendar.dateComponents([.minute, .hour, .day, .month, .weekday], from: date)
|
|
123
|
+
|
|
124
|
+
guard let minute = components.minute,
|
|
125
|
+
let hour = components.hour,
|
|
126
|
+
let day = components.day,
|
|
127
|
+
let month = components.month,
|
|
128
|
+
let weekday = components.weekday else {
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
guard minutes.matches(minute), hours.matches(hour), months.matches(month) else {
|
|
133
|
+
return false
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let cronWeekday = (weekday - 1) % 7
|
|
137
|
+
let dayOfMonthMatches = daysOfMonth.matches(day)
|
|
138
|
+
let dayOfWeekMatches = daysOfWeek.matches(cronWeekday)
|
|
139
|
+
|
|
140
|
+
if daysOfMonth.restricted && daysOfWeek.restricted {
|
|
141
|
+
return dayOfMonthMatches || dayOfWeekMatches
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return dayOfMonthMatches && dayOfWeekMatches
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private struct CronField {
|
|
148
|
+
let values: Set<Int>
|
|
149
|
+
let restricted: Bool
|
|
150
|
+
|
|
151
|
+
init?(_ expression: String, min: Int, max: Int, normalizeSunday: Bool = false) {
|
|
152
|
+
var values = Set<Int>()
|
|
153
|
+
|
|
154
|
+
for part in expression.split(separator: ",", omittingEmptySubsequences: false) {
|
|
155
|
+
guard Self.apply(String(part), to: &values, min: min, max: max, normalizeSunday: normalizeSunday) else {
|
|
156
|
+
return nil
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
guard !values.isEmpty else {
|
|
161
|
+
return nil
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let allValueCount = normalizeSunday ? 7 : max - min + 1
|
|
165
|
+
self.values = values
|
|
166
|
+
self.restricted = values.count != allValueCount
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
func matches(_ value: Int) -> Bool {
|
|
170
|
+
values.contains(value)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private static func apply(_ part: String, to values: inout Set<Int>, min: Int, max: Int, normalizeSunday: Bool) -> Bool {
|
|
174
|
+
let stepParts = part.split(separator: "/", omittingEmptySubsequences: false)
|
|
175
|
+
guard stepParts.count <= 2 else {
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
var step = 1
|
|
180
|
+
if stepParts.count == 2 {
|
|
181
|
+
guard let parsedStep = Int(stepParts[1]), parsedStep > 0 else {
|
|
182
|
+
return false
|
|
183
|
+
}
|
|
184
|
+
step = parsedStep
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let rangePart = String(stepParts[0])
|
|
188
|
+
let start: Int
|
|
189
|
+
let end: Int
|
|
190
|
+
|
|
191
|
+
if rangePart == "*" {
|
|
192
|
+
start = min
|
|
193
|
+
end = max
|
|
194
|
+
} else if rangePart.contains("-") {
|
|
195
|
+
let range = rangePart.split(separator: "-", omittingEmptySubsequences: false)
|
|
196
|
+
guard range.count == 2, let rangeStart = Int(range[0]), let rangeEnd = Int(range[1]) else {
|
|
197
|
+
return false
|
|
198
|
+
}
|
|
199
|
+
start = rangeStart
|
|
200
|
+
end = rangeEnd
|
|
201
|
+
} else {
|
|
202
|
+
guard let value = Int(rangePart) else {
|
|
203
|
+
return false
|
|
204
|
+
}
|
|
205
|
+
start = value
|
|
206
|
+
end = value
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
guard start >= min, end <= max, start <= end else {
|
|
210
|
+
return false
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
var value = start
|
|
214
|
+
while value <= end {
|
|
215
|
+
values.insert(normalize(value, normalizeSunday: normalizeSunday))
|
|
216
|
+
value += step
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return true
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private static func normalize(_ value: Int, normalizeSunday: Bool) -> Int {
|
|
223
|
+
normalizeSunday && value == 7 ? 0 : value
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
enum WebViewCrashRuntime {
|
|
229
|
+
private static var options = WebViewCrashRestartOptions()
|
|
230
|
+
|
|
231
|
+
static func update(options newOptions: WebViewCrashRestartOptions) {
|
|
232
|
+
options = newOptions
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
static var restartOnCrash: Bool {
|
|
236
|
+
options.restartOnCrash
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
static var restartAfterCrashDelaySeconds: TimeInterval {
|
|
240
|
+
options.restartAfterCrashDelaySeconds
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
17
244
|
enum WebViewCrashStore {
|
|
18
245
|
private static let pendingCrashKey = "CapgoWebViewCrash.pendingInfo"
|
|
19
246
|
private static let timestampFormatter = ISO8601DateFormatter()
|
|
@@ -50,6 +277,19 @@ enum WebViewCrashStore {
|
|
|
50
277
|
|
|
51
278
|
return value
|
|
52
279
|
}
|
|
280
|
+
|
|
281
|
+
static func shouldDispatch(eventName: String, crashInfo: [String: Any]) -> Bool {
|
|
282
|
+
if eventName == WebViewCrashBridge.restartEventName {
|
|
283
|
+
return true
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
guard eventName == WebViewCrashBridge.crashEventName else {
|
|
287
|
+
return false
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
let reason = crashInfo["reason"] as? String
|
|
291
|
+
return reason != WebViewCrashBridge.periodicRestartReason && reason != WebViewCrashBridge.manualRestartReason
|
|
292
|
+
}
|
|
53
293
|
}
|
|
54
294
|
|
|
55
295
|
enum WebViewCrashSwizzler {
|
|
@@ -85,7 +325,21 @@ private extension WebViewDelegationHandler {
|
|
|
85
325
|
)
|
|
86
326
|
|
|
87
327
|
WebViewCrashStore.write(crashInfo)
|
|
88
|
-
|
|
328
|
+
|
|
329
|
+
guard WebViewCrashRuntime.restartOnCrash else {
|
|
330
|
+
return
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
let restart = {
|
|
334
|
+
self.capgo_webViewCrash_webViewWebContentProcessDidTerminate(webView)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
let delay = WebViewCrashRuntime.restartAfterCrashDelaySeconds
|
|
338
|
+
if delay > 0 {
|
|
339
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: restart)
|
|
340
|
+
} else {
|
|
341
|
+
restart()
|
|
342
|
+
}
|
|
89
343
|
}
|
|
90
344
|
}
|
|
91
345
|
|