@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 +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,KAAK,CAAC;IA4E1C,CAAC;IA1EC,KAAK,CAAC,mBAAmB;QACvB,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,qBAAqB;QACzB,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC9B,IAAI,CAAC,uBAAuB,GAAG,KAAK,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,qBAAqB;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACpC,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,uBAAuB,GAAG,KAAK,CAAC;QACrC,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC9B,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,SAAiB,EAAE,YAAqC;QACxE,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAChE,IAAI,SAAS,KAAK,eAAe,CAAC,SAAS,EAAE,CAAC;YAC5C,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAChC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,sBAAsB;QAC5B,IAAI,IAAI,CAAC,uBAAuB,EAAE,CAAC;YACjC,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO;QACT,CAAC;QAED,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC;QACpC,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC;IAEO,cAAc;;QACpB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,SAAS;YACT,YAAY,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;YAC/C,MAAM,EAAE,WAAW;YACnB,GAAG,EAAE,MAAA,UAAU,CAAC,QAAQ,0CAAE,IAAI;YAC9B,QAAQ,EAAE,QAAQ;SACnB,CAAC;IACJ,CAAC;IAEO,oBAAoB;;QAC1B,MAAM,GAAG,GAAG,MAAA,UAAU,CAAC,YAAY,0CAAE,OAAO,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QACzE,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB,CAAC;QAC7C,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAEO,qBAAqB,CAAC,KAAuB;;QACnD,MAAA,UAAU,CAAC,YAAY,0CAAE,OAAO,CAAC,eAAe,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACtF,CAAC;IAEO,sBAAsB;;QAC5B,MAAA,UAAU,CAAC,YAAY,0CAAE,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;IAClE,CAAC;;AAEuB,yBAAS,GAAG,2BAA2B,AAA9B,CAA+B;AACxC,0BAAU,GAAG,6BAA6B,AAAhC,CAAiC","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 didDispatchPendingEvent = false;\n\n async getPendingCrashInfo(): Promise<PendingCrashInfoResult> {\n return { value: this.readPendingCrashInfo() };\n }\n\n async clearPendingCrashInfo(): Promise<void> {\n this.removePendingCrashInfo();\n this.didDispatchPendingEvent = false;\n }\n\n async simulateCrashRecovery(): Promise<PendingCrashInfoResult> {\n const value = this.buildCrashInfo();\n this.writePendingCrashInfo(value);\n this.didDispatchPendingEvent = false;\n this.flushPendingCrashEvent();\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.eventName) {\n this.flushPendingCrashEvent();\n }\n return handle;\n }\n\n private flushPendingCrashEvent(): void {\n if (this.didDispatchPendingEvent) {\n return;\n }\n\n const value = this.readPendingCrashInfo();\n if (!value) {\n return;\n }\n\n this.didDispatchPendingEvent = true;\n this.notifyListeners(WebViewCrashWeb.eventName, value);\n }\n\n private buildCrashInfo(): WebViewCrashInfo {\n const timestamp = Date.now();\n return {\n platform: 'web',\n timestamp,\n timestampISO: new Date(timestamp).toISOString(),\n reason: 'simulated',\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 eventName = 'webViewRestoredAfterCrash';\n private static readonly storageKey = 'capgo.webview-crash.pending';\n}\n"]}
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"]}
@@ -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.didDispatchPendingEvent = false;
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.didDispatchPendingEvent = false;
19
+ this.dispatchedPendingEvents.clear();
20
20
  }
21
21
  async simulateCrashRecovery() {
22
22
  const value = this.buildCrashInfo();
23
23
  this.writePendingCrashInfo(value);
24
- this.didDispatchPendingEvent = false;
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.didDispatchPendingEvent) {
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.didDispatchPendingEvent = true;
44
- this.notifyListeners(WebViewCrashWeb.eventName, value);
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: 'simulated',
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.eventName = 'webViewRestoredAfterCrash';
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({
@@ -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.didDispatchPendingEvent = false;\n }\n async getPendingCrashInfo() {\n return { value: this.readPendingCrashInfo() };\n }\n async clearPendingCrashInfo() {\n this.removePendingCrashInfo();\n this.didDispatchPendingEvent = false;\n }\n async simulateCrashRecovery() {\n const value = this.buildCrashInfo();\n this.writePendingCrashInfo(value);\n this.didDispatchPendingEvent = false;\n this.flushPendingCrashEvent();\n return { value };\n }\n async addListener(eventName, listenerFunc) {\n const handle = await super.addListener(eventName, listenerFunc);\n if (eventName === WebViewCrashWeb.eventName) {\n this.flushPendingCrashEvent();\n }\n return handle;\n }\n flushPendingCrashEvent() {\n if (this.didDispatchPendingEvent) {\n return;\n }\n const value = this.readPendingCrashInfo();\n if (!value) {\n return;\n }\n this.didDispatchPendingEvent = true;\n this.notifyListeners(WebViewCrashWeb.eventName, value);\n }\n buildCrashInfo() {\n var _a;\n const timestamp = Date.now();\n return {\n platform: 'web',\n timestamp,\n timestampISO: new Date(timestamp).toISOString(),\n reason: 'simulated',\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.eventName = 'webViewRestoredAfterCrash';\nWebViewCrashWeb.storageKey = 'capgo.webview-crash.pending';\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AACK,MAAC,YAAY,GAAGA,mBAAc,CAAC,cAAc,EAAE;AACpD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;AACnE,CAAC;;ACFM,MAAM,eAAe,SAASC,cAAS,CAAC;AAC/C,IAAI,WAAW,GAAG;AAClB,QAAQ,KAAK,CAAC,GAAG,SAAS,CAAC;AAC3B,QAAQ,IAAI,CAAC,uBAAuB,GAAG,KAAK;AAC5C,IAAI;AACJ,IAAI,MAAM,mBAAmB,GAAG;AAChC,QAAQ,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,oBAAoB,EAAE,EAAE;AACrD,IAAI;AACJ,IAAI,MAAM,qBAAqB,GAAG;AAClC,QAAQ,IAAI,CAAC,sBAAsB,EAAE;AACrC,QAAQ,IAAI,CAAC,uBAAuB,GAAG,KAAK;AAC5C,IAAI;AACJ,IAAI,MAAM,qBAAqB,GAAG;AAClC,QAAQ,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE;AAC3C,QAAQ,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC;AACzC,QAAQ,IAAI,CAAC,uBAAuB,GAAG,KAAK;AAC5C,QAAQ,IAAI,CAAC,sBAAsB,EAAE;AACrC,QAAQ,OAAO,EAAE,KAAK,EAAE;AACxB,IAAI;AACJ,IAAI,MAAM,WAAW,CAAC,SAAS,EAAE,YAAY,EAAE;AAC/C,QAAQ,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,YAAY,CAAC;AACvE,QAAQ,IAAI,SAAS,KAAK,eAAe,CAAC,SAAS,EAAE;AACrD,YAAY,IAAI,CAAC,sBAAsB,EAAE;AACzC,QAAQ;AACR,QAAQ,OAAO,MAAM;AACrB,IAAI;AACJ,IAAI,sBAAsB,GAAG;AAC7B,QAAQ,IAAI,IAAI,CAAC,uBAAuB,EAAE;AAC1C,YAAY;AACZ,QAAQ;AACR,QAAQ,MAAM,KAAK,GAAG,IAAI,CAAC,oBAAoB,EAAE;AACjD,QAAQ,IAAI,CAAC,KAAK,EAAE;AACpB,YAAY;AACZ,QAAQ;AACR,QAAQ,IAAI,CAAC,uBAAuB,GAAG,IAAI;AAC3C,QAAQ,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC;AAC9D,IAAI;AACJ,IAAI,cAAc,GAAG;AACrB,QAAQ,IAAI,EAAE;AACd,QAAQ,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE;AACpC,QAAQ,OAAO;AACf,YAAY,QAAQ,EAAE,KAAK;AAC3B,YAAY,SAAS;AACrB,YAAY,YAAY,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;AAC3D,YAAY,MAAM,EAAE,WAAW;AAC/B,YAAY,GAAG,EAAE,CAAC,EAAE,GAAG,UAAU,CAAC,QAAQ,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC,IAAI;AACxF,YAAY,QAAQ,EAAE,QAAQ;AAC9B,SAAS;AACT,IAAI;AACJ,IAAI,oBAAoB,GAAG;AAC3B,QAAQ,IAAI,EAAE;AACd,QAAQ,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,UAAU,CAAC,YAAY,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,UAAU,CAAC;AAC9H,QAAQ,IAAI,CAAC,GAAG,EAAE;AAClB,YAAY,OAAO,IAAI;AACvB,QAAQ;AACR,QAAQ,IAAI;AACZ,YAAY,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;AAClC,QAAQ;AACR,QAAQ,OAAO,EAAE,EAAE;AACnB,YAAY,OAAO,IAAI;AACvB,QAAQ;AACR,IAAI;AACJ,IAAI,qBAAqB,CAAC,KAAK,EAAE;AACjC,QAAQ,IAAI,EAAE;AACd,QAAQ,CAAC,EAAE,GAAG,UAAU,CAAC,YAAY,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AACzI,IAAI;AACJ,IAAI,sBAAsB,GAAG;AAC7B,QAAQ,IAAI,EAAE;AACd,QAAQ,CAAC,EAAE,GAAG,UAAU,CAAC,YAAY,MAAM,IAAI,IAAI,EAAE,KAAK,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC;AACrH,IAAI;AACJ;AACA,eAAe,CAAC,SAAS,GAAG,2BAA2B;AACvD,eAAe,CAAC,UAAU,GAAG,6BAA6B;;;;;;;;;"}
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.didDispatchPendingEvent = false;
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.didDispatchPendingEvent = false;
18
+ this.dispatchedPendingEvents.clear();
19
19
  }
20
20
  async simulateCrashRecovery() {
21
21
  const value = this.buildCrashInfo();
22
22
  this.writePendingCrashInfo(value);
23
- this.didDispatchPendingEvent = false;
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.didDispatchPendingEvent) {
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.didDispatchPendingEvent = true;
43
- this.notifyListeners(WebViewCrashWeb.eventName, value);
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: 'simulated',
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.eventName = 'webViewRestoredAfterCrash';
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({
@@ -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.didDispatchPendingEvent = false;\n }\n async getPendingCrashInfo() {\n return { value: this.readPendingCrashInfo() };\n }\n async clearPendingCrashInfo() {\n this.removePendingCrashInfo();\n this.didDispatchPendingEvent = false;\n }\n async simulateCrashRecovery() {\n const value = this.buildCrashInfo();\n this.writePendingCrashInfo(value);\n this.didDispatchPendingEvent = false;\n this.flushPendingCrashEvent();\n return { value };\n }\n async addListener(eventName, listenerFunc) {\n const handle = await super.addListener(eventName, listenerFunc);\n if (eventName === WebViewCrashWeb.eventName) {\n this.flushPendingCrashEvent();\n }\n return handle;\n }\n flushPendingCrashEvent() {\n if (this.didDispatchPendingEvent) {\n return;\n }\n const value = this.readPendingCrashInfo();\n if (!value) {\n return;\n }\n this.didDispatchPendingEvent = true;\n this.notifyListeners(WebViewCrashWeb.eventName, value);\n }\n buildCrashInfo() {\n var _a;\n const timestamp = Date.now();\n return {\n platform: 'web',\n timestamp,\n timestampISO: new Date(timestamp).toISOString(),\n reason: 'simulated',\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.eventName = 'webViewRestoredAfterCrash';\nWebViewCrashWeb.storageKey = 'capgo.webview-crash.pending';\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
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 eventName = "webViewRestoredAfterCrash"
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
- capgo_webViewCrash_webViewWebContentProcessDidTerminate(webView)
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