@capgo/capacitor-inappbrowser 8.6.35 → 8.7.0-beta.pr596.340.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/README.md +127 -1
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/CapgoInAppBrowserPlugin.java +194 -3
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/Options.java +30 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +388 -8
- package/dist/docs.json +303 -4
- package/dist/esm/definitions.d.ts +127 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +10 -1
- package/dist/esm/web.js +16 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +16 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +16 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/InAppBrowserPlugin/InAppBrowserPlugin.swift +438 -35
- package/ios/Sources/InAppBrowserPlugin/ProxySchemeHandler.swift +0 -1
- package/ios/Sources/InAppBrowserPlugin/WKWebViewController.swift +10 -3
- package/package.json +1 -1
|
@@ -98,7 +98,7 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
98
98
|
case aware = "AWARE"
|
|
99
99
|
case fakeVisible = "FAKE_VISIBLE"
|
|
100
100
|
}
|
|
101
|
-
private let pluginVersion: String = "8.
|
|
101
|
+
private let pluginVersion: String = "8.7.0"
|
|
102
102
|
public let identifier = "CapgoInAppBrowserPlugin"
|
|
103
103
|
public let jsName = "CapgoInAppBrowser"
|
|
104
104
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
@@ -109,10 +109,14 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
109
109
|
CAPPluginMethod(name: "getCookies", returnType: CAPPluginReturnPromise),
|
|
110
110
|
CAPPluginMethod(name: "clearAllCookies", returnType: CAPPluginReturnPromise),
|
|
111
111
|
CAPPluginMethod(name: "clearCache", returnType: CAPPluginReturnPromise),
|
|
112
|
+
CAPPluginMethod(name: "clearAllBrowsingData", returnType: CAPPluginReturnPromise),
|
|
112
113
|
CAPPluginMethod(name: "reload", returnType: CAPPluginReturnPromise),
|
|
113
114
|
CAPPluginMethod(name: "setUrl", returnType: CAPPluginReturnPromise),
|
|
114
115
|
CAPPluginMethod(name: "show", returnType: CAPPluginReturnPromise),
|
|
115
116
|
CAPPluginMethod(name: "hide", returnType: CAPPluginReturnPromise),
|
|
117
|
+
CAPPluginMethod(name: "sendToBack", returnType: CAPPluginReturnPromise),
|
|
118
|
+
CAPPluginMethod(name: "bringToFront", returnType: CAPPluginReturnPromise),
|
|
119
|
+
CAPPluginMethod(name: "dispatchInputEvent", returnType: CAPPluginReturnPromise),
|
|
116
120
|
CAPPluginMethod(name: "close", returnType: CAPPluginReturnPromise),
|
|
117
121
|
CAPPluginMethod(name: "executeScript", returnType: CAPPluginReturnPromise),
|
|
118
122
|
CAPPluginMethod(name: "postMessage", returnType: CAPPluginReturnPromise),
|
|
@@ -140,6 +144,14 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
140
144
|
private var activeWebViewId: String?
|
|
141
145
|
private var hiddenWebViewContainers: [ObjectIdentifier: UIView] = [:]
|
|
142
146
|
private var closeModalTitle: String?
|
|
147
|
+
private var layeredWebViewIds = Set<String>()
|
|
148
|
+
private var transparentHostWebViewIds = Set<String>()
|
|
149
|
+
private var hasStoredHostWebViewAppearance = false
|
|
150
|
+
private var originalHostWebViewBackgroundColor: UIColor?
|
|
151
|
+
private var originalHostWebViewIsOpaque = true
|
|
152
|
+
private var originalHostSubviewBackgrounds: [(UIView, UIColor?, Bool)] = []
|
|
153
|
+
private var originalHostSuperviewBackgroundColor: UIColor?
|
|
154
|
+
private var originalHostSuperviewIsOpaque = true
|
|
143
155
|
private var closeModalDescription: String?
|
|
144
156
|
private var closeModalOk: String?
|
|
145
157
|
private var closeModalCancel: String?
|
|
@@ -178,14 +190,20 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
178
190
|
}
|
|
179
191
|
}
|
|
180
192
|
|
|
181
|
-
private func dismissNavigationControllerIfPresented(_ navigationController: UINavigationController?) {
|
|
182
|
-
guard let navigationController
|
|
193
|
+
private func dismissNavigationControllerIfPresented(_ navigationController: UINavigationController?, completion: (() -> Void)? = nil) {
|
|
194
|
+
guard let navigationController else {
|
|
195
|
+
completion?()
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
guard navigationController.presentingViewController != nil else {
|
|
199
|
+
completion?()
|
|
183
200
|
return
|
|
184
201
|
}
|
|
185
|
-
navigationController.dismiss(animated: false)
|
|
202
|
+
navigationController.dismiss(animated: false, completion: completion)
|
|
186
203
|
}
|
|
187
204
|
|
|
188
205
|
private func unregisterWebView(id: String) {
|
|
206
|
+
detachLayeredWebView(id: id)
|
|
189
207
|
if let webView = webViewControllers[id]?.capableWebView {
|
|
190
208
|
cleanupHiddenWebViewContainer(for: webView)
|
|
191
209
|
}
|
|
@@ -346,6 +364,26 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
346
364
|
return stores
|
|
347
365
|
}
|
|
348
366
|
|
|
367
|
+
private func allBrowsingDataStores() -> [WKWebsiteDataStore] {
|
|
368
|
+
var seen = Set<ObjectIdentifier>()
|
|
369
|
+
var stores: [WKWebsiteDataStore] = []
|
|
370
|
+
|
|
371
|
+
func append(_ store: WKWebsiteDataStore) {
|
|
372
|
+
let identifier = ObjectIdentifier(store)
|
|
373
|
+
if seen.insert(identifier).inserted {
|
|
374
|
+
stores.append(store)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
append(WKWebsiteDataStore.default())
|
|
379
|
+
for controller in webViewControllers.values {
|
|
380
|
+
if let store = controller.websiteDataStore() {
|
|
381
|
+
append(store)
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return stores
|
|
385
|
+
}
|
|
386
|
+
|
|
349
387
|
private func parseProxyRules(_ rawRules: [Any]) throws -> [NativeProxyRule] {
|
|
350
388
|
try rawRules.enumerated().map { index, item in
|
|
351
389
|
guard let dictionary = item as? [String: Any] else {
|
|
@@ -359,19 +397,154 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
359
397
|
}
|
|
360
398
|
}
|
|
361
399
|
|
|
362
|
-
|
|
400
|
+
@discardableResult
|
|
401
|
+
func presentView(webViewId: String? = nil, isAnimated: Bool = true) -> Bool {
|
|
363
402
|
let resolvedId = webViewId ?? activeWebViewId
|
|
364
403
|
let navigationController = resolvedId.flatMap { navigationControllers[$0] } ?? self.navigationWebViewController
|
|
365
404
|
guard let navigationController else {
|
|
366
405
|
self.currentPluginCall?.reject("Navigation controller is not initialized")
|
|
367
|
-
return
|
|
406
|
+
return false
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if let resolvedId,
|
|
410
|
+
let webViewController = webViewControllers[resolvedId],
|
|
411
|
+
webViewController.isLayeredBehind {
|
|
412
|
+
if !sendNavigationControllerToBack(id: resolvedId, transparentBackground: webViewController.transparentHostBackground) {
|
|
413
|
+
self.currentPluginCall?.reject("Failed to send webview to back")
|
|
414
|
+
return false
|
|
415
|
+
}
|
|
416
|
+
return true
|
|
368
417
|
}
|
|
369
418
|
|
|
419
|
+
if let resolvedId {
|
|
420
|
+
detachLayeredWebView(id: resolvedId)
|
|
421
|
+
}
|
|
370
422
|
dismissActiveKeyboard()
|
|
371
423
|
let presenter = self.bridge?.viewController?.presentedViewController ?? self.bridge?.viewController
|
|
372
424
|
presenter?.present(navigationController, animated: isAnimated, completion: {
|
|
373
425
|
self.currentPluginCall?.resolve()
|
|
374
426
|
})
|
|
427
|
+
return true
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private func targetFrame(for webViewController: WKWebViewController, in hostWebView: UIView) -> CGRect {
|
|
431
|
+
return CustomWebViewFrameSupport.resolvedFrame(
|
|
432
|
+
width: webViewController.customWidth,
|
|
433
|
+
height: webViewController.customHeight,
|
|
434
|
+
x: webViewController.customX,
|
|
435
|
+
y: webViewController.customY,
|
|
436
|
+
fallbackSize: hostWebView.bounds.size
|
|
437
|
+
) ?? hostWebView.bounds
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
@discardableResult
|
|
441
|
+
private func sendNavigationControllerToBack(id: String, transparentBackground: Bool) -> Bool {
|
|
442
|
+
guard let navigationController = navigationControllers[id],
|
|
443
|
+
let webViewController = webViewControllers[id],
|
|
444
|
+
let hostWebView = self.bridge?.webView else {
|
|
445
|
+
return false
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if let webView = webViewController.capableWebView, webView.superview !== webViewController.view {
|
|
449
|
+
cleanupHiddenWebViewContainer(for: webView)
|
|
450
|
+
attachWebViewToController(webViewController, webView: webView)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
dismissActiveKeyboard()
|
|
454
|
+
navigationController.view.removeFromSuperview()
|
|
455
|
+
if transparentBackground {
|
|
456
|
+
makeHostWebViewTransparent(for: id)
|
|
457
|
+
} else {
|
|
458
|
+
restoreHostWebViewBackgroundIfNeeded(for: id)
|
|
459
|
+
}
|
|
460
|
+
navigationController.view.frame = targetFrame(for: webViewController, in: hostWebView)
|
|
461
|
+
navigationController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
462
|
+
navigationController.view.isUserInteractionEnabled = false
|
|
463
|
+
hostWebView.addSubview(navigationController.view)
|
|
464
|
+
hostWebView.sendSubviewToBack(navigationController.view)
|
|
465
|
+
navigationController.view.setNeedsLayout()
|
|
466
|
+
navigationController.view.layoutIfNeeded()
|
|
467
|
+
|
|
468
|
+
layeredWebViewIds.insert(id)
|
|
469
|
+
webViewController.isLayeredBehind = true
|
|
470
|
+
webViewController.transparentHostBackground = transparentBackground
|
|
471
|
+
return true
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
private func detachLayeredWebView(id: String) {
|
|
475
|
+
if let navigationController = navigationControllers[id] {
|
|
476
|
+
if navigationController.view.superview === self.bridge?.webView {
|
|
477
|
+
navigationController.view.removeFromSuperview()
|
|
478
|
+
}
|
|
479
|
+
navigationController.view.isUserInteractionEnabled = true
|
|
480
|
+
}
|
|
481
|
+
layeredWebViewIds.remove(id)
|
|
482
|
+
restoreHostWebViewBackgroundIfNeeded(for: id)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
private func storeSubviewBackgrounds(from view: UIView) {
|
|
486
|
+
for subview in view.subviews {
|
|
487
|
+
originalHostSubviewBackgrounds.append((subview, subview.backgroundColor, subview.isOpaque))
|
|
488
|
+
storeSubviewBackgrounds(from: subview)
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
private func clearBackgrounds(in view: UIView) {
|
|
493
|
+
view.isOpaque = false
|
|
494
|
+
view.backgroundColor = .clear
|
|
495
|
+
view.subviews.forEach { clearBackgrounds(in: $0) }
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
private func makeHostWebViewTransparent(for id: String) {
|
|
499
|
+
guard let hostWebView = self.bridge?.webView else {
|
|
500
|
+
return
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
transparentHostWebViewIds.insert(id)
|
|
504
|
+
if !hasStoredHostWebViewAppearance {
|
|
505
|
+
hasStoredHostWebViewAppearance = true
|
|
506
|
+
originalHostWebViewBackgroundColor = hostWebView.backgroundColor
|
|
507
|
+
originalHostWebViewIsOpaque = hostWebView.isOpaque
|
|
508
|
+
originalHostSuperviewBackgroundColor = hostWebView.superview?.backgroundColor
|
|
509
|
+
originalHostSuperviewIsOpaque = hostWebView.superview?.isOpaque ?? true
|
|
510
|
+
originalHostSubviewBackgrounds.removeAll()
|
|
511
|
+
storeSubviewBackgrounds(from: hostWebView)
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
clearBackgrounds(in: hostWebView)
|
|
515
|
+
hostWebView.superview?.isOpaque = false
|
|
516
|
+
hostWebView.superview?.backgroundColor = .clear
|
|
517
|
+
hostWebView.setNeedsLayout()
|
|
518
|
+
hostWebView.layoutIfNeeded()
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
private func restoreHostWebViewBackgroundIfNeeded(for id: String? = nil) {
|
|
522
|
+
if let id {
|
|
523
|
+
transparentHostWebViewIds.remove(id)
|
|
524
|
+
} else {
|
|
525
|
+
transparentHostWebViewIds.removeAll()
|
|
526
|
+
}
|
|
527
|
+
guard transparentHostWebViewIds.isEmpty, hasStoredHostWebViewAppearance else {
|
|
528
|
+
return
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if let hostWebView = self.bridge?.webView {
|
|
532
|
+
hostWebView.backgroundColor = originalHostWebViewBackgroundColor
|
|
533
|
+
hostWebView.isOpaque = originalHostWebViewIsOpaque
|
|
534
|
+
hostWebView.superview?.backgroundColor = originalHostSuperviewBackgroundColor
|
|
535
|
+
hostWebView.superview?.isOpaque = originalHostSuperviewIsOpaque
|
|
536
|
+
for (view, backgroundColor, isOpaque) in originalHostSubviewBackgrounds {
|
|
537
|
+
view.backgroundColor = backgroundColor
|
|
538
|
+
view.isOpaque = isOpaque
|
|
539
|
+
}
|
|
540
|
+
hostWebView.setNeedsLayout()
|
|
541
|
+
hostWebView.layoutIfNeeded()
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
hasStoredHostWebViewAppearance = false
|
|
545
|
+
originalHostWebViewBackgroundColor = nil
|
|
546
|
+
originalHostSuperviewBackgroundColor = nil
|
|
547
|
+
originalHostSubviewBackgrounds.removeAll()
|
|
375
548
|
}
|
|
376
549
|
|
|
377
550
|
private func dismissActiveKeyboard() {
|
|
@@ -572,6 +745,24 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
572
745
|
}
|
|
573
746
|
}
|
|
574
747
|
|
|
748
|
+
@objc func clearAllBrowsingData(_ call: CAPPluginCall) {
|
|
749
|
+
DispatchQueue.main.async {
|
|
750
|
+
let dataStores = self.allBrowsingDataStores()
|
|
751
|
+
let dataTypes = WKWebsiteDataStore.allWebsiteDataTypes()
|
|
752
|
+
let group = DispatchGroup()
|
|
753
|
+
for dataStore in dataStores {
|
|
754
|
+
group.enter()
|
|
755
|
+
dataStore.removeData(ofTypes: dataTypes,
|
|
756
|
+
modifiedSince: Date(timeIntervalSince1970: 0)) {
|
|
757
|
+
group.leave()
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
group.notify(queue: .main) {
|
|
761
|
+
call.resolve()
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
575
766
|
@objc func clearCookies(_ call: CAPPluginCall) {
|
|
576
767
|
guard let url = call.getString("url"),
|
|
577
768
|
let host = URL(string: url)?.host else {
|
|
@@ -771,12 +962,15 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
771
962
|
let enabledSafeBottomMargin = call.getBool("enabledSafeBottomMargin", false)
|
|
772
963
|
let enabledSafeTopMargin = call.getBool("enabledSafeTopMargin", true)
|
|
773
964
|
let hidden = call.getBool("hidden", false)
|
|
965
|
+
let toBack = call.getBool("toBack", false)
|
|
966
|
+
let transparentBackground = call.getBool("transparentBackground", true)
|
|
774
967
|
self.isHidden = hidden
|
|
775
968
|
let hiddenPopupWindow = call.getBool("hiddenPopupWindow", false)
|
|
776
969
|
let allowWebViewJsVisibilityControl = self.getConfig().getBoolean("allowWebViewJsVisibilityControl", false)
|
|
777
970
|
let allowScreenshotsFromWebPage = call.getBool("allowScreenshotsFromWebPage", false)
|
|
778
971
|
let captureConsoleLogs = call.getBool("captureConsoleLogs", false)
|
|
779
972
|
let handleDownloads = call.getBool("handleDownloads", false)
|
|
973
|
+
let persistWebViewData = call.getBool("persistWebViewData", true)
|
|
780
974
|
let invisibilityModeRaw = call.getString("invisibilityMode", "AWARE")
|
|
781
975
|
self.invisibilityMode = InvisibilityMode(rawValue: invisibilityModeRaw.uppercased()) ?? .aware
|
|
782
976
|
|
|
@@ -918,38 +1112,36 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
918
1112
|
self.proxyBridges[webViewId] = proxyBridge
|
|
919
1113
|
}
|
|
920
1114
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1115
|
+
let webViewController = WKWebViewController()
|
|
1116
|
+
webViewController.blankNavigationTab = toolbarType == "blank"
|
|
1117
|
+
webViewController.enabledSafeBottomMargin = enabledSafeBottomMargin
|
|
1118
|
+
webViewController.enabledSafeTopMargin = enabledSafeTopMargin
|
|
1119
|
+
webViewController.source = .remote(url)
|
|
1120
|
+
webViewController.setCredentials(credentials: credentials)
|
|
1121
|
+
webViewController.allowWebViewJsVisibilityControl = allowWebViewJsVisibilityControl
|
|
1122
|
+
webViewController.allowScreenshotsFromWebPage = allowScreenshotsFromWebPage
|
|
1123
|
+
webViewController.captureConsoleLogs = captureConsoleLogs
|
|
1124
|
+
webViewController.proxyRequests = legacyProxyRequests.isEnabled
|
|
1125
|
+
webViewController.proxySchemeHandler = proxyHandler
|
|
1126
|
+
webViewController.proxyBridge = proxyBridge
|
|
1127
|
+
webViewController.proxyBridgeAccessToken = proxyBridgeAccessToken
|
|
1128
|
+
webViewController.legacyProxyRequestURLRegexPattern = legacyProxyRequests.urlRegex?.pattern
|
|
1129
|
+
webViewController.openBlankTargetInWebView = openBlankTargetInWebView
|
|
1130
|
+
webViewController.persistWebViewData = persistWebViewData
|
|
1131
|
+
webViewController.setHeaders(headers: headers)
|
|
1132
|
+
webViewController.setPreventDeeplink(preventDeeplink: preventDeeplink)
|
|
1133
|
+
webViewController.setBlockedHosts(blockedHosts: blockedHosts)
|
|
1134
|
+
webViewController.setAuthorizedAppLinks(authorizedAppLinks: authorizedAppLinks)
|
|
1135
|
+
webViewController.documentStartUserScripts = self.documentStartUserScripts(
|
|
931
1136
|
authorizedAppLinks: authorizedAppLinks,
|
|
932
|
-
allowWebViewJsVisibilityControl: allowWebViewJsVisibilityControl,
|
|
933
|
-
allowScreenshotsFromWebPage: allowScreenshotsFromWebPage,
|
|
934
|
-
captureConsoleLogs: captureConsoleLogs,
|
|
935
|
-
proxyRequests: legacyProxyRequests.isEnabled,
|
|
936
|
-
proxySchemeHandler: proxyHandler,
|
|
937
|
-
proxyBridge: proxyBridge,
|
|
938
|
-
proxyBridgeAccessToken: proxyBridgeAccessToken,
|
|
939
|
-
legacyProxyRequestURLRegexPattern: legacyProxyRequests.urlRegex?.pattern,
|
|
940
|
-
documentStartUserScripts: self.documentStartUserScripts(
|
|
941
|
-
authorizedAppLinks: authorizedAppLinks,
|
|
942
|
-
openBlankTargetInWebView: openBlankTargetInWebView
|
|
943
|
-
),
|
|
944
1137
|
openBlankTargetInWebView: openBlankTargetInWebView
|
|
945
1138
|
)
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
call.reject("Failed to initialize WebViewController")
|
|
949
|
-
return
|
|
950
|
-
}
|
|
1139
|
+
webViewController.initWebview(isInspectable: isInspectable)
|
|
1140
|
+
self.webViewController = webViewController
|
|
951
1141
|
|
|
952
1142
|
webViewController.instanceId = webViewId
|
|
1143
|
+
webViewController.isLayeredBehind = toBack
|
|
1144
|
+
webViewController.transparentHostBackground = transparentBackground
|
|
953
1145
|
|
|
954
1146
|
// Set HTTP method and body if provided
|
|
955
1147
|
if let method = httpMethod {
|
|
@@ -1182,7 +1374,7 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1182
1374
|
}
|
|
1183
1375
|
|
|
1184
1376
|
// Configure modal presentation for touch passthrough if custom dimensions are set
|
|
1185
|
-
if width != nil || height != nil {
|
|
1377
|
+
if (width != nil || height != nil) && !toBack {
|
|
1186
1378
|
self.navigationWebViewController?.modalPresentationStyle = .overFullScreen
|
|
1187
1379
|
|
|
1188
1380
|
// Create a pass-through container
|
|
@@ -1272,7 +1464,9 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1272
1464
|
return
|
|
1273
1465
|
}
|
|
1274
1466
|
} else if !self.isPresentAfterPageLoad {
|
|
1275
|
-
self.presentView(webViewId: webViewId, isAnimated: isAnimated)
|
|
1467
|
+
guard self.presentView(webViewId: webViewId, isAnimated: isAnimated) else {
|
|
1468
|
+
return
|
|
1469
|
+
}
|
|
1276
1470
|
}
|
|
1277
1471
|
call.resolve(["id": webViewId])
|
|
1278
1472
|
}
|
|
@@ -1338,6 +1532,9 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1338
1532
|
self.isHidden = hidden
|
|
1339
1533
|
|
|
1340
1534
|
if hidden {
|
|
1535
|
+
if let resolvedId {
|
|
1536
|
+
self.detachLayeredWebView(id: resolvedId)
|
|
1537
|
+
}
|
|
1341
1538
|
if !self.attachWebViewToWindow(webView) {
|
|
1342
1539
|
call?.reject("Failed to get active window for hidden webview")
|
|
1343
1540
|
return
|
|
@@ -1348,6 +1545,18 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1348
1545
|
self.attachWebViewToController(webViewController, webView: webView)
|
|
1349
1546
|
}
|
|
1350
1547
|
|
|
1548
|
+
if webViewController.isLayeredBehind, let resolvedId {
|
|
1549
|
+
if self.sendNavigationControllerToBack(id: resolvedId, transparentBackground: webViewController.transparentHostBackground) {
|
|
1550
|
+
if let navController = navigationController {
|
|
1551
|
+
self.setActiveWebView(id: resolvedId, webView: webViewController, navigationController: navController)
|
|
1552
|
+
}
|
|
1553
|
+
call?.resolve()
|
|
1554
|
+
} else {
|
|
1555
|
+
call?.reject("Failed to send webview to back")
|
|
1556
|
+
}
|
|
1557
|
+
return
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1351
1560
|
if let navController = navigationController {
|
|
1352
1561
|
if let resolvedId {
|
|
1353
1562
|
self.setActiveWebView(id: resolvedId, webView: webViewController, navigationController: navController)
|
|
@@ -1386,6 +1595,200 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1386
1595
|
self.setHiddenState(false, targetId: call.getString("id"), call: call)
|
|
1387
1596
|
}
|
|
1388
1597
|
|
|
1598
|
+
@objc func sendToBack(_ call: CAPPluginCall) {
|
|
1599
|
+
DispatchQueue.main.async {
|
|
1600
|
+
let targetId = call.getString("id") ?? self.activeWebViewId
|
|
1601
|
+
guard let resolvedId = targetId,
|
|
1602
|
+
let webViewController = self.resolveWebViewController(for: resolvedId),
|
|
1603
|
+
let navigationController = self.resolveNavigationController(for: resolvedId) else {
|
|
1604
|
+
call.reject("WebView is not initialized")
|
|
1605
|
+
return
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
let transparentBackground = call.getBool("transparentBackground", true)
|
|
1609
|
+
self.dismissNavigationControllerIfPresented(navigationController) {
|
|
1610
|
+
webViewController.isLayeredBehind = true
|
|
1611
|
+
webViewController.transparentHostBackground = transparentBackground
|
|
1612
|
+
guard self.sendNavigationControllerToBack(id: resolvedId, transparentBackground: transparentBackground) else {
|
|
1613
|
+
call.reject("Failed to send webview to back")
|
|
1614
|
+
return
|
|
1615
|
+
}
|
|
1616
|
+
self.setActiveWebView(id: resolvedId, webView: webViewController, navigationController: navigationController)
|
|
1617
|
+
call.resolve()
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
@objc func bringToFront(_ call: CAPPluginCall) {
|
|
1623
|
+
DispatchQueue.main.async {
|
|
1624
|
+
let targetId = call.getString("id") ?? self.activeWebViewId
|
|
1625
|
+
guard let resolvedId = targetId,
|
|
1626
|
+
let webViewController = self.resolveWebViewController(for: resolvedId),
|
|
1627
|
+
let navigationController = self.resolveNavigationController(for: resolvedId) else {
|
|
1628
|
+
call.reject("WebView is not initialized")
|
|
1629
|
+
return
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
webViewController.isLayeredBehind = false
|
|
1633
|
+
webViewController.transparentHostBackground = true
|
|
1634
|
+
self.detachLayeredWebView(id: resolvedId)
|
|
1635
|
+
if let webView = webViewController.capableWebView, webView.superview !== webViewController.view {
|
|
1636
|
+
self.attachWebViewToController(webViewController, webView: webView)
|
|
1637
|
+
}
|
|
1638
|
+
self.setActiveWebView(id: resolvedId, webView: webViewController, navigationController: navigationController)
|
|
1639
|
+
if navigationController.presentingViewController == nil {
|
|
1640
|
+
let presenter = self.bridge?.viewController?.presentedViewController ?? self.bridge?.viewController
|
|
1641
|
+
presenter?.present(navigationController, animated: true, completion: {
|
|
1642
|
+
call.resolve()
|
|
1643
|
+
})
|
|
1644
|
+
return
|
|
1645
|
+
}
|
|
1646
|
+
call.resolve()
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
private func jsNumber(_ value: Float?, fallback: String = "0") -> String {
|
|
1651
|
+
guard let value, value.isFinite else {
|
|
1652
|
+
return fallback
|
|
1653
|
+
}
|
|
1654
|
+
return String(Double(value))
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
private func dispatchInputScript(type: String, x: Float?, y: Float?, deltaX: Float?, deltaY: Float?) -> String? {
|
|
1658
|
+
if type == "scroll" {
|
|
1659
|
+
guard let x, let y, let deltaX, let deltaY,
|
|
1660
|
+
x.isFinite, y.isFinite, deltaX.isFinite, deltaY.isFinite else {
|
|
1661
|
+
return nil
|
|
1662
|
+
}
|
|
1663
|
+
let xLiteral = jsNumber(x)
|
|
1664
|
+
let yLiteral = jsNumber(y)
|
|
1665
|
+
let deltaXLiteral = jsNumber(deltaX)
|
|
1666
|
+
let deltaYLiteral = jsNumber(deltaY)
|
|
1667
|
+
return """
|
|
1668
|
+
(function() {
|
|
1669
|
+
const x = \(xLiteral);
|
|
1670
|
+
const y = \(yLiteral);
|
|
1671
|
+
const dx = \(deltaXLiteral);
|
|
1672
|
+
const dy = \(deltaYLiteral);
|
|
1673
|
+
let target = Number.isFinite(x) && Number.isFinite(y) ? document.elementFromPoint(x, y) : null;
|
|
1674
|
+
while (target && target !== document.body && target !== document.documentElement) {
|
|
1675
|
+
const style = window.getComputedStyle(target);
|
|
1676
|
+
const canScroll = /(auto|scroll)/.test(style.overflow + style.overflowX + style.overflowY) &&
|
|
1677
|
+
(target.scrollHeight > target.clientHeight || target.scrollWidth > target.clientWidth);
|
|
1678
|
+
if (canScroll && typeof target.scrollBy === 'function') {
|
|
1679
|
+
target.scrollBy(dx, dy);
|
|
1680
|
+
return true;
|
|
1681
|
+
}
|
|
1682
|
+
target = target.parentElement;
|
|
1683
|
+
}
|
|
1684
|
+
window.scrollBy(dx, dy);
|
|
1685
|
+
return true;
|
|
1686
|
+
})();
|
|
1687
|
+
"""
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
let pointerEvents: [String]
|
|
1691
|
+
let mouseEvents: [String]
|
|
1692
|
+
let touchEvents: [String]
|
|
1693
|
+
switch type {
|
|
1694
|
+
case "click":
|
|
1695
|
+
pointerEvents = ["pointerdown", "pointerup"]
|
|
1696
|
+
mouseEvents = ["mousedown", "mouseup", "click"]
|
|
1697
|
+
touchEvents = ["touchstart", "touchend"]
|
|
1698
|
+
case "touchstart":
|
|
1699
|
+
pointerEvents = ["pointerdown"]
|
|
1700
|
+
mouseEvents = ["mousedown"]
|
|
1701
|
+
touchEvents = ["touchstart"]
|
|
1702
|
+
case "touchmove":
|
|
1703
|
+
pointerEvents = ["pointermove"]
|
|
1704
|
+
mouseEvents = ["mousemove"]
|
|
1705
|
+
touchEvents = ["touchmove"]
|
|
1706
|
+
case "touchend":
|
|
1707
|
+
pointerEvents = ["pointerup"]
|
|
1708
|
+
mouseEvents = ["mouseup"]
|
|
1709
|
+
touchEvents = ["touchend"]
|
|
1710
|
+
case "touchcancel":
|
|
1711
|
+
pointerEvents = ["pointercancel"]
|
|
1712
|
+
mouseEvents = []
|
|
1713
|
+
touchEvents = ["touchcancel"]
|
|
1714
|
+
default:
|
|
1715
|
+
return nil
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
guard let x, let y, x.isFinite, y.isFinite else {
|
|
1719
|
+
return nil
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
let pointerEventsData = try? JSONSerialization.data(withJSONObject: pointerEvents)
|
|
1723
|
+
let mouseEventsData = try? JSONSerialization.data(withJSONObject: mouseEvents)
|
|
1724
|
+
let touchEventsData = try? JSONSerialization.data(withJSONObject: touchEvents)
|
|
1725
|
+
let pointerEventsJSON = pointerEventsData.flatMap { String(data: $0, encoding: .utf8) } ?? "[]"
|
|
1726
|
+
let mouseEventsJSON = mouseEventsData.flatMap { String(data: $0, encoding: .utf8) } ?? "[]"
|
|
1727
|
+
let touchEventsJSON = touchEventsData.flatMap { String(data: $0, encoding: .utf8) } ?? "[]"
|
|
1728
|
+
|
|
1729
|
+
return """
|
|
1730
|
+
(function() {
|
|
1731
|
+
const x = \(jsNumber(x));
|
|
1732
|
+
const y = \(jsNumber(y));
|
|
1733
|
+
const target = document.elementFromPoint(x, y);
|
|
1734
|
+
if (!target) return false;
|
|
1735
|
+
const base = { bubbles: true, cancelable: true, composed: true, clientX: x, clientY: y, screenX: x, screenY: y };
|
|
1736
|
+
for (const name of \(pointerEventsJSON)) {
|
|
1737
|
+
if (typeof PointerEvent === 'function') {
|
|
1738
|
+
target.dispatchEvent(new PointerEvent(name, Object.assign({}, base, { pointerId: 1, pointerType: 'touch', isPrimary: true })));
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
for (const name of \(mouseEventsJSON)) {
|
|
1742
|
+
target.dispatchEvent(new MouseEvent(name, base));
|
|
1743
|
+
}
|
|
1744
|
+
if (typeof TouchEvent === 'function' && typeof Touch === 'function') {
|
|
1745
|
+
for (const name of \(touchEventsJSON)) {
|
|
1746
|
+
try {
|
|
1747
|
+
const touch = new Touch({ identifier: 1, target: target, clientX: x, clientY: y, screenX: x, screenY: y });
|
|
1748
|
+
const activeTouches = name === 'touchend' || name === 'touchcancel' ? [] : [touch];
|
|
1749
|
+
target.dispatchEvent(new TouchEvent(name, {
|
|
1750
|
+
bubbles: true,
|
|
1751
|
+
cancelable: true,
|
|
1752
|
+
composed: true,
|
|
1753
|
+
touches: activeTouches,
|
|
1754
|
+
targetTouches: activeTouches,
|
|
1755
|
+
changedTouches: [touch]
|
|
1756
|
+
}));
|
|
1757
|
+
} catch (_) {}
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
return true;
|
|
1761
|
+
})();
|
|
1762
|
+
"""
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
@objc func dispatchInputEvent(_ call: CAPPluginCall) {
|
|
1766
|
+
guard let type = call.getString("type") else {
|
|
1767
|
+
call.reject("Input event type is required")
|
|
1768
|
+
return
|
|
1769
|
+
}
|
|
1770
|
+
guard let script = dispatchInputScript(
|
|
1771
|
+
type: type,
|
|
1772
|
+
x: call.getFloat("x"),
|
|
1773
|
+
y: call.getFloat("y"),
|
|
1774
|
+
deltaX: call.getFloat("deltaX"),
|
|
1775
|
+
deltaY: call.getFloat("deltaY")
|
|
1776
|
+
) else {
|
|
1777
|
+
call.reject("Unsupported input event type or invalid input payload: \(type)")
|
|
1778
|
+
return
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
DispatchQueue.main.async {
|
|
1782
|
+
let targetId = call.getString("id") ?? self.activeWebViewId
|
|
1783
|
+
guard let webViewController = self.resolveWebViewController(for: targetId) else {
|
|
1784
|
+
call.reject("WebView is not initialized")
|
|
1785
|
+
return
|
|
1786
|
+
}
|
|
1787
|
+
webViewController.executeScript(script: script)
|
|
1788
|
+
call.resolve()
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1389
1792
|
@objc func executeScript(_ call: CAPPluginCall) {
|
|
1390
1793
|
guard let script = call.getString("code") else {
|
|
1391
1794
|
call.reject("Cannot get script to execute")
|
|
@@ -1169,7 +1169,6 @@ public class ProxySchemeHandler: NSObject, WKURLSchemeHandler, URLSessionTaskDel
|
|
|
1169
1169
|
}
|
|
1170
1170
|
}
|
|
1171
1171
|
|
|
1172
|
-
|
|
1173
1172
|
private func makeInitialRequestContext(from urlSchemeTask: WKURLSchemeTask, requestURL: URL) -> NativeRequestContext? {
|
|
1174
1173
|
let requestURLString = requestURL.absoluteString
|
|
1175
1174
|
|
|
@@ -387,6 +387,7 @@ open class WKWebViewController: UIViewController, WKScriptMessageHandler {
|
|
|
387
387
|
open var allowWebViewJsVisibilityControl = false
|
|
388
388
|
open var allowScreenshotsFromWebPage = false
|
|
389
389
|
open var captureConsoleLogs = false
|
|
390
|
+
open var persistWebViewData = true
|
|
390
391
|
open var handleDownloads = false
|
|
391
392
|
open var delegate: WKWebViewControllerDelegate?
|
|
392
393
|
open var bypassedSSLHosts: [String]?
|
|
@@ -432,6 +433,8 @@ open class WKWebViewController: UIViewController, WKScriptMessageHandler {
|
|
|
432
433
|
var waitsForPopupNavigation = false
|
|
433
434
|
var hiddenPopupWindow = false
|
|
434
435
|
var opensHidden = false
|
|
436
|
+
var isLayeredBehind = false
|
|
437
|
+
var transparentHostBackground = true
|
|
435
438
|
|
|
436
439
|
// Dimension properties
|
|
437
440
|
var customWidth: CGFloat?
|
|
@@ -748,8 +751,8 @@ open class WKWebViewController: UIViewController, WKScriptMessageHandler {
|
|
|
748
751
|
.compactMap { $0 as? UIWindowScene }
|
|
749
752
|
.first { $0.activationState == .foregroundActive }
|
|
750
753
|
?? UIApplication.shared.connectedScenes
|
|
751
|
-
|
|
752
|
-
|
|
754
|
+
.compactMap { $0 as? UIWindowScene }
|
|
755
|
+
.first
|
|
753
756
|
return windowScene?.statusBarManager?.statusBarFrame.height ?? 0
|
|
754
757
|
}
|
|
755
758
|
|
|
@@ -1513,6 +1516,9 @@ open class WKWebViewController: UIViewController, WKScriptMessageHandler {
|
|
|
1513
1516
|
self.edgesForExtendedLayout = [.bottom]
|
|
1514
1517
|
|
|
1515
1518
|
let webConfiguration = initialWebConfiguration ?? WKWebViewConfiguration()
|
|
1519
|
+
if !persistWebViewData {
|
|
1520
|
+
webConfiguration.websiteDataStore = .nonPersistent()
|
|
1521
|
+
}
|
|
1516
1522
|
let userContentController = webConfiguration.userContentController
|
|
1517
1523
|
userContentController.removeAllUserScripts()
|
|
1518
1524
|
userContentController.removeScriptMessageHandler(forName: "messageHandler")
|
|
@@ -1763,6 +1769,8 @@ open class WKWebViewController: UIViewController, WKScriptMessageHandler {
|
|
|
1763
1769
|
self.blockedHosts = parent.blockedHosts
|
|
1764
1770
|
self.authorizedAppLinks = parent.authorizedAppLinks
|
|
1765
1771
|
self.activeNativeNavigationForWebview = parent.activeNativeNavigationForWebview
|
|
1772
|
+
self.initialWebConfiguration = configuration
|
|
1773
|
+
self.persistWebViewData = parent.persistWebViewData
|
|
1766
1774
|
self.disableOverscroll = parent.disableOverscroll
|
|
1767
1775
|
self.proxyRequests = parent.proxyRequests
|
|
1768
1776
|
self.proxySchemeHandler = proxySchemeHandler
|
|
@@ -1935,7 +1943,6 @@ public extension WKWebViewController {
|
|
|
1935
1943
|
return false
|
|
1936
1944
|
}
|
|
1937
1945
|
|
|
1938
|
-
|
|
1939
1946
|
func load(source sourceValue: WKWebSource) {
|
|
1940
1947
|
switch sourceValue {
|
|
1941
1948
|
case .remote(let url):
|