@capgo/capacitor-inappbrowser 8.6.34 → 8.7.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/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 +436 -57
- 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),
|
|
@@ -138,11 +142,16 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
138
142
|
private var proxyBridges: [String: ProxyBridge] = [:]
|
|
139
143
|
private var webViewStack: [String] = []
|
|
140
144
|
private var activeWebViewId: String?
|
|
141
|
-
private weak var presentationContainerView: UIView?
|
|
142
|
-
private var presentationContainerWasInteractive = true
|
|
143
|
-
private var presentationContainerPreviousAlpha: CGFloat = 1
|
|
144
145
|
private var hiddenWebViewContainers: [ObjectIdentifier: UIView] = [:]
|
|
145
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
|
|
146
155
|
private var closeModalDescription: String?
|
|
147
156
|
private var closeModalOk: String?
|
|
148
157
|
private var closeModalCancel: String?
|
|
@@ -181,7 +190,15 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
181
190
|
}
|
|
182
191
|
}
|
|
183
192
|
|
|
193
|
+
private func dismissNavigationControllerIfPresented(_ navigationController: UINavigationController?) {
|
|
194
|
+
guard let navigationController, navigationController.presentingViewController != nil else {
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
navigationController.dismiss(animated: false)
|
|
198
|
+
}
|
|
199
|
+
|
|
184
200
|
private func unregisterWebView(id: String) {
|
|
201
|
+
detachLayeredWebView(id: id)
|
|
185
202
|
if let webView = webViewControllers[id]?.capableWebView {
|
|
186
203
|
cleanupHiddenWebViewContainer(for: webView)
|
|
187
204
|
}
|
|
@@ -342,6 +359,26 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
342
359
|
return stores
|
|
343
360
|
}
|
|
344
361
|
|
|
362
|
+
private func allBrowsingDataStores() -> [WKWebsiteDataStore] {
|
|
363
|
+
var seen = Set<ObjectIdentifier>()
|
|
364
|
+
var stores: [WKWebsiteDataStore] = []
|
|
365
|
+
|
|
366
|
+
func append(_ store: WKWebsiteDataStore) {
|
|
367
|
+
let identifier = ObjectIdentifier(store)
|
|
368
|
+
if seen.insert(identifier).inserted {
|
|
369
|
+
stores.append(store)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
append(WKWebsiteDataStore.default())
|
|
374
|
+
for controller in webViewControllers.values {
|
|
375
|
+
if let store = controller.websiteDataStore() {
|
|
376
|
+
append(store)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return stores
|
|
380
|
+
}
|
|
381
|
+
|
|
345
382
|
private func parseProxyRules(_ rawRules: [Any]) throws -> [NativeProxyRule] {
|
|
346
383
|
try rawRules.enumerated().map { index, item in
|
|
347
384
|
guard let dictionary = item as? [String: Any] else {
|
|
@@ -355,19 +392,155 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
355
392
|
}
|
|
356
393
|
}
|
|
357
394
|
|
|
358
|
-
|
|
395
|
+
@discardableResult
|
|
396
|
+
func presentView(webViewId: String? = nil, isAnimated: Bool = true) -> Bool {
|
|
359
397
|
let resolvedId = webViewId ?? activeWebViewId
|
|
360
398
|
let navigationController = resolvedId.flatMap { navigationControllers[$0] } ?? self.navigationWebViewController
|
|
361
399
|
guard let navigationController else {
|
|
362
400
|
self.currentPluginCall?.reject("Navigation controller is not initialized")
|
|
363
|
-
return
|
|
401
|
+
return false
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if let resolvedId,
|
|
405
|
+
let webViewController = webViewControllers[resolvedId],
|
|
406
|
+
webViewController.isLayeredBehind {
|
|
407
|
+
if !sendNavigationControllerToBack(id: resolvedId, transparentBackground: webViewController.transparentHostBackground) {
|
|
408
|
+
self.currentPluginCall?.reject("Failed to send webview to back")
|
|
409
|
+
return false
|
|
410
|
+
}
|
|
411
|
+
return true
|
|
364
412
|
}
|
|
365
413
|
|
|
414
|
+
if let resolvedId {
|
|
415
|
+
detachLayeredWebView(id: resolvedId)
|
|
416
|
+
}
|
|
366
417
|
dismissActiveKeyboard()
|
|
367
418
|
let presenter = self.bridge?.viewController?.presentedViewController ?? self.bridge?.viewController
|
|
368
419
|
presenter?.present(navigationController, animated: isAnimated, completion: {
|
|
369
420
|
self.currentPluginCall?.resolve()
|
|
370
421
|
})
|
|
422
|
+
return true
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private func targetFrame(for webViewController: WKWebViewController, in hostWebView: UIView) -> CGRect {
|
|
426
|
+
return CustomWebViewFrameSupport.resolvedFrame(
|
|
427
|
+
width: webViewController.customWidth,
|
|
428
|
+
height: webViewController.customHeight,
|
|
429
|
+
x: webViewController.customX,
|
|
430
|
+
y: webViewController.customY,
|
|
431
|
+
fallbackSize: hostWebView.bounds.size
|
|
432
|
+
) ?? hostWebView.bounds
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
@discardableResult
|
|
436
|
+
private func sendNavigationControllerToBack(id: String, transparentBackground: Bool) -> Bool {
|
|
437
|
+
guard let navigationController = navigationControllers[id],
|
|
438
|
+
let webViewController = webViewControllers[id],
|
|
439
|
+
let hostWebView = self.bridge?.webView else {
|
|
440
|
+
return false
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if let webView = webViewController.capableWebView, webView.superview !== webViewController.view {
|
|
444
|
+
cleanupHiddenWebViewContainer(for: webView)
|
|
445
|
+
attachWebViewToController(webViewController, webView: webView)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
dismissActiveKeyboard()
|
|
449
|
+
dismissNavigationControllerIfPresented(navigationController)
|
|
450
|
+
navigationController.view.removeFromSuperview()
|
|
451
|
+
if transparentBackground {
|
|
452
|
+
makeHostWebViewTransparent(for: id)
|
|
453
|
+
} else {
|
|
454
|
+
restoreHostWebViewBackgroundIfNeeded(for: id)
|
|
455
|
+
}
|
|
456
|
+
navigationController.view.frame = targetFrame(for: webViewController, in: hostWebView)
|
|
457
|
+
navigationController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
458
|
+
navigationController.view.isUserInteractionEnabled = false
|
|
459
|
+
hostWebView.addSubview(navigationController.view)
|
|
460
|
+
hostWebView.sendSubviewToBack(navigationController.view)
|
|
461
|
+
navigationController.view.setNeedsLayout()
|
|
462
|
+
navigationController.view.layoutIfNeeded()
|
|
463
|
+
|
|
464
|
+
layeredWebViewIds.insert(id)
|
|
465
|
+
webViewController.isLayeredBehind = true
|
|
466
|
+
webViewController.transparentHostBackground = transparentBackground
|
|
467
|
+
return true
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
private func detachLayeredWebView(id: String) {
|
|
471
|
+
if let navigationController = navigationControllers[id] {
|
|
472
|
+
if navigationController.view.superview === self.bridge?.webView {
|
|
473
|
+
navigationController.view.removeFromSuperview()
|
|
474
|
+
}
|
|
475
|
+
navigationController.view.isUserInteractionEnabled = true
|
|
476
|
+
}
|
|
477
|
+
layeredWebViewIds.remove(id)
|
|
478
|
+
restoreHostWebViewBackgroundIfNeeded(for: id)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
private func storeSubviewBackgrounds(from view: UIView) {
|
|
482
|
+
for subview in view.subviews {
|
|
483
|
+
originalHostSubviewBackgrounds.append((subview, subview.backgroundColor, subview.isOpaque))
|
|
484
|
+
storeSubviewBackgrounds(from: subview)
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
private func clearBackgrounds(in view: UIView) {
|
|
489
|
+
view.isOpaque = false
|
|
490
|
+
view.backgroundColor = .clear
|
|
491
|
+
view.subviews.forEach { clearBackgrounds(in: $0) }
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
private func makeHostWebViewTransparent(for id: String) {
|
|
495
|
+
guard let hostWebView = self.bridge?.webView else {
|
|
496
|
+
return
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
transparentHostWebViewIds.insert(id)
|
|
500
|
+
if !hasStoredHostWebViewAppearance {
|
|
501
|
+
hasStoredHostWebViewAppearance = true
|
|
502
|
+
originalHostWebViewBackgroundColor = hostWebView.backgroundColor
|
|
503
|
+
originalHostWebViewIsOpaque = hostWebView.isOpaque
|
|
504
|
+
originalHostSuperviewBackgroundColor = hostWebView.superview?.backgroundColor
|
|
505
|
+
originalHostSuperviewIsOpaque = hostWebView.superview?.isOpaque ?? true
|
|
506
|
+
originalHostSubviewBackgrounds.removeAll()
|
|
507
|
+
storeSubviewBackgrounds(from: hostWebView)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
clearBackgrounds(in: hostWebView)
|
|
511
|
+
hostWebView.superview?.isOpaque = false
|
|
512
|
+
hostWebView.superview?.backgroundColor = .clear
|
|
513
|
+
hostWebView.setNeedsLayout()
|
|
514
|
+
hostWebView.layoutIfNeeded()
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
private func restoreHostWebViewBackgroundIfNeeded(for id: String? = nil) {
|
|
518
|
+
if let id {
|
|
519
|
+
transparentHostWebViewIds.remove(id)
|
|
520
|
+
} else {
|
|
521
|
+
transparentHostWebViewIds.removeAll()
|
|
522
|
+
}
|
|
523
|
+
guard transparentHostWebViewIds.isEmpty, hasStoredHostWebViewAppearance else {
|
|
524
|
+
return
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if let hostWebView = self.bridge?.webView {
|
|
528
|
+
hostWebView.backgroundColor = originalHostWebViewBackgroundColor
|
|
529
|
+
hostWebView.isOpaque = originalHostWebViewIsOpaque
|
|
530
|
+
hostWebView.superview?.backgroundColor = originalHostSuperviewBackgroundColor
|
|
531
|
+
hostWebView.superview?.isOpaque = originalHostSuperviewIsOpaque
|
|
532
|
+
for (view, backgroundColor, isOpaque) in originalHostSubviewBackgrounds {
|
|
533
|
+
view.backgroundColor = backgroundColor
|
|
534
|
+
view.isOpaque = isOpaque
|
|
535
|
+
}
|
|
536
|
+
hostWebView.setNeedsLayout()
|
|
537
|
+
hostWebView.layoutIfNeeded()
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
hasStoredHostWebViewAppearance = false
|
|
541
|
+
originalHostWebViewBackgroundColor = nil
|
|
542
|
+
originalHostSuperviewBackgroundColor = nil
|
|
543
|
+
originalHostSubviewBackgrounds.removeAll()
|
|
371
544
|
}
|
|
372
545
|
|
|
373
546
|
private func dismissActiveKeyboard() {
|
|
@@ -568,6 +741,24 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
568
741
|
}
|
|
569
742
|
}
|
|
570
743
|
|
|
744
|
+
@objc func clearAllBrowsingData(_ call: CAPPluginCall) {
|
|
745
|
+
DispatchQueue.main.async {
|
|
746
|
+
let dataStores = self.allBrowsingDataStores()
|
|
747
|
+
let dataTypes = WKWebsiteDataStore.allWebsiteDataTypes()
|
|
748
|
+
let group = DispatchGroup()
|
|
749
|
+
for dataStore in dataStores {
|
|
750
|
+
group.enter()
|
|
751
|
+
dataStore.removeData(ofTypes: dataTypes,
|
|
752
|
+
modifiedSince: Date(timeIntervalSince1970: 0)) {
|
|
753
|
+
group.leave()
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
group.notify(queue: .main) {
|
|
757
|
+
call.resolve()
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
571
762
|
@objc func clearCookies(_ call: CAPPluginCall) {
|
|
572
763
|
guard let url = call.getString("url"),
|
|
573
764
|
let host = URL(string: url)?.host else {
|
|
@@ -767,12 +958,15 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
767
958
|
let enabledSafeBottomMargin = call.getBool("enabledSafeBottomMargin", false)
|
|
768
959
|
let enabledSafeTopMargin = call.getBool("enabledSafeTopMargin", true)
|
|
769
960
|
let hidden = call.getBool("hidden", false)
|
|
961
|
+
let toBack = call.getBool("toBack", false)
|
|
962
|
+
let transparentBackground = call.getBool("transparentBackground", true)
|
|
770
963
|
self.isHidden = hidden
|
|
771
964
|
let hiddenPopupWindow = call.getBool("hiddenPopupWindow", false)
|
|
772
965
|
let allowWebViewJsVisibilityControl = self.getConfig().getBoolean("allowWebViewJsVisibilityControl", false)
|
|
773
966
|
let allowScreenshotsFromWebPage = call.getBool("allowScreenshotsFromWebPage", false)
|
|
774
967
|
let captureConsoleLogs = call.getBool("captureConsoleLogs", false)
|
|
775
968
|
let handleDownloads = call.getBool("handleDownloads", false)
|
|
969
|
+
let persistWebViewData = call.getBool("persistWebViewData", true)
|
|
776
970
|
let invisibilityModeRaw = call.getString("invisibilityMode", "AWARE")
|
|
777
971
|
self.invisibilityMode = InvisibilityMode(rawValue: invisibilityModeRaw.uppercased()) ?? .aware
|
|
778
972
|
|
|
@@ -914,38 +1108,36 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
914
1108
|
self.proxyBridges[webViewId] = proxyBridge
|
|
915
1109
|
}
|
|
916
1110
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1111
|
+
let webViewController = WKWebViewController()
|
|
1112
|
+
webViewController.blankNavigationTab = toolbarType == "blank"
|
|
1113
|
+
webViewController.enabledSafeBottomMargin = enabledSafeBottomMargin
|
|
1114
|
+
webViewController.enabledSafeTopMargin = enabledSafeTopMargin
|
|
1115
|
+
webViewController.source = .remote(url)
|
|
1116
|
+
webViewController.setCredentials(credentials: credentials)
|
|
1117
|
+
webViewController.allowWebViewJsVisibilityControl = allowWebViewJsVisibilityControl
|
|
1118
|
+
webViewController.allowScreenshotsFromWebPage = allowScreenshotsFromWebPage
|
|
1119
|
+
webViewController.captureConsoleLogs = captureConsoleLogs
|
|
1120
|
+
webViewController.proxyRequests = legacyProxyRequests.isEnabled
|
|
1121
|
+
webViewController.proxySchemeHandler = proxyHandler
|
|
1122
|
+
webViewController.proxyBridge = proxyBridge
|
|
1123
|
+
webViewController.proxyBridgeAccessToken = proxyBridgeAccessToken
|
|
1124
|
+
webViewController.legacyProxyRequestURLRegexPattern = legacyProxyRequests.urlRegex?.pattern
|
|
1125
|
+
webViewController.openBlankTargetInWebView = openBlankTargetInWebView
|
|
1126
|
+
webViewController.persistWebViewData = persistWebViewData
|
|
1127
|
+
webViewController.setHeaders(headers: headers)
|
|
1128
|
+
webViewController.setPreventDeeplink(preventDeeplink: preventDeeplink)
|
|
1129
|
+
webViewController.setBlockedHosts(blockedHosts: blockedHosts)
|
|
1130
|
+
webViewController.setAuthorizedAppLinks(authorizedAppLinks: authorizedAppLinks)
|
|
1131
|
+
webViewController.documentStartUserScripts = self.documentStartUserScripts(
|
|
927
1132
|
authorizedAppLinks: authorizedAppLinks,
|
|
928
|
-
allowWebViewJsVisibilityControl: allowWebViewJsVisibilityControl,
|
|
929
|
-
allowScreenshotsFromWebPage: allowScreenshotsFromWebPage,
|
|
930
|
-
captureConsoleLogs: captureConsoleLogs,
|
|
931
|
-
proxyRequests: legacyProxyRequests.isEnabled,
|
|
932
|
-
proxySchemeHandler: proxyHandler,
|
|
933
|
-
proxyBridge: proxyBridge,
|
|
934
|
-
proxyBridgeAccessToken: proxyBridgeAccessToken,
|
|
935
|
-
legacyProxyRequestURLRegexPattern: legacyProxyRequests.urlRegex?.pattern,
|
|
936
|
-
documentStartUserScripts: self.documentStartUserScripts(
|
|
937
|
-
authorizedAppLinks: authorizedAppLinks,
|
|
938
|
-
openBlankTargetInWebView: openBlankTargetInWebView
|
|
939
|
-
),
|
|
940
1133
|
openBlankTargetInWebView: openBlankTargetInWebView
|
|
941
1134
|
)
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
call.reject("Failed to initialize WebViewController")
|
|
945
|
-
return
|
|
946
|
-
}
|
|
1135
|
+
webViewController.initWebview(isInspectable: isInspectable)
|
|
1136
|
+
self.webViewController = webViewController
|
|
947
1137
|
|
|
948
1138
|
webViewController.instanceId = webViewId
|
|
1139
|
+
webViewController.isLayeredBehind = toBack
|
|
1140
|
+
webViewController.transparentHostBackground = transparentBackground
|
|
949
1141
|
|
|
950
1142
|
// Set HTTP method and body if provided
|
|
951
1143
|
if let method = httpMethod {
|
|
@@ -1178,7 +1370,7 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1178
1370
|
}
|
|
1179
1371
|
|
|
1180
1372
|
// Configure modal presentation for touch passthrough if custom dimensions are set
|
|
1181
|
-
if width != nil || height != nil {
|
|
1373
|
+
if (width != nil || height != nil) && !toBack {
|
|
1182
1374
|
self.navigationWebViewController?.modalPresentationStyle = .overFullScreen
|
|
1183
1375
|
|
|
1184
1376
|
// Create a pass-through container
|
|
@@ -1268,7 +1460,9 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1268
1460
|
return
|
|
1269
1461
|
}
|
|
1270
1462
|
} else if !self.isPresentAfterPageLoad {
|
|
1271
|
-
self.presentView(webViewId: webViewId, isAnimated: isAnimated)
|
|
1463
|
+
guard self.presentView(webViewId: webViewId, isAnimated: isAnimated) else {
|
|
1464
|
+
return
|
|
1465
|
+
}
|
|
1272
1466
|
}
|
|
1273
1467
|
call.resolve(["id": webViewId])
|
|
1274
1468
|
}
|
|
@@ -1334,42 +1528,35 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1334
1528
|
self.isHidden = hidden
|
|
1335
1529
|
|
|
1336
1530
|
if hidden {
|
|
1337
|
-
if let
|
|
1338
|
-
|
|
1339
|
-
navController.view.isUserInteractionEnabled = false
|
|
1340
|
-
if let containerView = navController.view.superview {
|
|
1341
|
-
if self.presentationContainerView == nil || self.presentationContainerView !== containerView {
|
|
1342
|
-
self.presentationContainerView = containerView
|
|
1343
|
-
self.presentationContainerWasInteractive = containerView.isUserInteractionEnabled
|
|
1344
|
-
self.presentationContainerPreviousAlpha = containerView.alpha
|
|
1345
|
-
}
|
|
1346
|
-
containerView.isUserInteractionEnabled = false
|
|
1347
|
-
containerView.alpha = 0
|
|
1348
|
-
}
|
|
1531
|
+
if let resolvedId {
|
|
1532
|
+
self.detachLayeredWebView(id: resolvedId)
|
|
1349
1533
|
}
|
|
1350
|
-
|
|
1351
1534
|
if !self.attachWebViewToWindow(webView) {
|
|
1352
1535
|
call?.reject("Failed to get active window for hidden webview")
|
|
1353
1536
|
return
|
|
1354
1537
|
}
|
|
1538
|
+
self.dismissNavigationControllerIfPresented(navigationController)
|
|
1355
1539
|
} else {
|
|
1356
1540
|
if webView.superview !== webViewController.view {
|
|
1357
1541
|
self.attachWebViewToController(webViewController, webView: webView)
|
|
1358
1542
|
}
|
|
1359
1543
|
|
|
1544
|
+
if webViewController.isLayeredBehind, let resolvedId {
|
|
1545
|
+
if self.sendNavigationControllerToBack(id: resolvedId, transparentBackground: webViewController.transparentHostBackground) {
|
|
1546
|
+
if let navController = navigationController {
|
|
1547
|
+
self.setActiveWebView(id: resolvedId, webView: webViewController, navigationController: navController)
|
|
1548
|
+
}
|
|
1549
|
+
call?.resolve()
|
|
1550
|
+
} else {
|
|
1551
|
+
call?.reject("Failed to send webview to back")
|
|
1552
|
+
}
|
|
1553
|
+
return
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1360
1556
|
if let navController = navigationController {
|
|
1361
1557
|
if let resolvedId {
|
|
1362
1558
|
self.setActiveWebView(id: resolvedId, webView: webViewController, navigationController: navController)
|
|
1363
1559
|
}
|
|
1364
|
-
navController.view.isHidden = false
|
|
1365
|
-
navController.view.isUserInteractionEnabled = true
|
|
1366
|
-
if let containerView = self.presentationContainerView {
|
|
1367
|
-
containerView.isUserInteractionEnabled = self.presentationContainerWasInteractive
|
|
1368
|
-
containerView.alpha = self.presentationContainerPreviousAlpha
|
|
1369
|
-
self.presentationContainerView = nil
|
|
1370
|
-
self.presentationContainerWasInteractive = true
|
|
1371
|
-
self.presentationContainerPreviousAlpha = 1
|
|
1372
|
-
}
|
|
1373
1560
|
|
|
1374
1561
|
if navController.presentingViewController == nil {
|
|
1375
1562
|
let presenter = self.bridge?.viewController?.presentedViewController ?? self.bridge?.viewController
|
|
@@ -1404,6 +1591,198 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1404
1591
|
self.setHiddenState(false, targetId: call.getString("id"), call: call)
|
|
1405
1592
|
}
|
|
1406
1593
|
|
|
1594
|
+
@objc func sendToBack(_ call: CAPPluginCall) {
|
|
1595
|
+
DispatchQueue.main.async {
|
|
1596
|
+
let targetId = call.getString("id") ?? self.activeWebViewId
|
|
1597
|
+
guard let resolvedId = targetId,
|
|
1598
|
+
let webViewController = self.resolveWebViewController(for: resolvedId),
|
|
1599
|
+
let navigationController = self.resolveNavigationController(for: resolvedId) else {
|
|
1600
|
+
call.reject("WebView is not initialized")
|
|
1601
|
+
return
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
let transparentBackground = call.getBool("transparentBackground", true)
|
|
1605
|
+
webViewController.isLayeredBehind = true
|
|
1606
|
+
webViewController.transparentHostBackground = transparentBackground
|
|
1607
|
+
guard self.sendNavigationControllerToBack(id: resolvedId, transparentBackground: transparentBackground) else {
|
|
1608
|
+
call.reject("Failed to send webview to back")
|
|
1609
|
+
return
|
|
1610
|
+
}
|
|
1611
|
+
self.setActiveWebView(id: resolvedId, webView: webViewController, navigationController: navigationController)
|
|
1612
|
+
call.resolve()
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
@objc func bringToFront(_ call: CAPPluginCall) {
|
|
1617
|
+
DispatchQueue.main.async {
|
|
1618
|
+
let targetId = call.getString("id") ?? self.activeWebViewId
|
|
1619
|
+
guard let resolvedId = targetId,
|
|
1620
|
+
let webViewController = self.resolveWebViewController(for: resolvedId),
|
|
1621
|
+
let navigationController = self.resolveNavigationController(for: resolvedId) else {
|
|
1622
|
+
call.reject("WebView is not initialized")
|
|
1623
|
+
return
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
webViewController.isLayeredBehind = false
|
|
1627
|
+
webViewController.transparentHostBackground = true
|
|
1628
|
+
self.detachLayeredWebView(id: resolvedId)
|
|
1629
|
+
if let webView = webViewController.capableWebView, webView.superview !== webViewController.view {
|
|
1630
|
+
self.attachWebViewToController(webViewController, webView: webView)
|
|
1631
|
+
}
|
|
1632
|
+
self.setActiveWebView(id: resolvedId, webView: webViewController, navigationController: navigationController)
|
|
1633
|
+
if navigationController.presentingViewController == nil {
|
|
1634
|
+
let presenter = self.bridge?.viewController?.presentedViewController ?? self.bridge?.viewController
|
|
1635
|
+
presenter?.present(navigationController, animated: true, completion: {
|
|
1636
|
+
call.resolve()
|
|
1637
|
+
})
|
|
1638
|
+
return
|
|
1639
|
+
}
|
|
1640
|
+
call.resolve()
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
private func jsNumber(_ value: Float?, fallback: String = "0") -> String {
|
|
1645
|
+
guard let value, value.isFinite else {
|
|
1646
|
+
return fallback
|
|
1647
|
+
}
|
|
1648
|
+
return String(Double(value))
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
private func dispatchInputScript(type: String, x: Float?, y: Float?, deltaX: Float?, deltaY: Float?) -> String? {
|
|
1652
|
+
if type == "scroll" {
|
|
1653
|
+
guard let x, let y, let deltaX, let deltaY,
|
|
1654
|
+
x.isFinite, y.isFinite, deltaX.isFinite, deltaY.isFinite else {
|
|
1655
|
+
return nil
|
|
1656
|
+
}
|
|
1657
|
+
let xLiteral = jsNumber(x)
|
|
1658
|
+
let yLiteral = jsNumber(y)
|
|
1659
|
+
let deltaXLiteral = jsNumber(deltaX)
|
|
1660
|
+
let deltaYLiteral = jsNumber(deltaY)
|
|
1661
|
+
return """
|
|
1662
|
+
(function() {
|
|
1663
|
+
const x = \(xLiteral);
|
|
1664
|
+
const y = \(yLiteral);
|
|
1665
|
+
const dx = \(deltaXLiteral);
|
|
1666
|
+
const dy = \(deltaYLiteral);
|
|
1667
|
+
let target = Number.isFinite(x) && Number.isFinite(y) ? document.elementFromPoint(x, y) : null;
|
|
1668
|
+
while (target && target !== document.body && target !== document.documentElement) {
|
|
1669
|
+
const style = window.getComputedStyle(target);
|
|
1670
|
+
const canScroll = /(auto|scroll)/.test(style.overflow + style.overflowX + style.overflowY) &&
|
|
1671
|
+
(target.scrollHeight > target.clientHeight || target.scrollWidth > target.clientWidth);
|
|
1672
|
+
if (canScroll && typeof target.scrollBy === 'function') {
|
|
1673
|
+
target.scrollBy(dx, dy);
|
|
1674
|
+
return true;
|
|
1675
|
+
}
|
|
1676
|
+
target = target.parentElement;
|
|
1677
|
+
}
|
|
1678
|
+
window.scrollBy(dx, dy);
|
|
1679
|
+
return true;
|
|
1680
|
+
})();
|
|
1681
|
+
"""
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
let pointerEvents: [String]
|
|
1685
|
+
let mouseEvents: [String]
|
|
1686
|
+
let touchEvents: [String]
|
|
1687
|
+
switch type {
|
|
1688
|
+
case "click":
|
|
1689
|
+
pointerEvents = ["pointerdown", "pointerup"]
|
|
1690
|
+
mouseEvents = ["mousedown", "mouseup", "click"]
|
|
1691
|
+
touchEvents = ["touchstart", "touchend"]
|
|
1692
|
+
case "touchstart":
|
|
1693
|
+
pointerEvents = ["pointerdown"]
|
|
1694
|
+
mouseEvents = ["mousedown"]
|
|
1695
|
+
touchEvents = ["touchstart"]
|
|
1696
|
+
case "touchmove":
|
|
1697
|
+
pointerEvents = ["pointermove"]
|
|
1698
|
+
mouseEvents = ["mousemove"]
|
|
1699
|
+
touchEvents = ["touchmove"]
|
|
1700
|
+
case "touchend":
|
|
1701
|
+
pointerEvents = ["pointerup"]
|
|
1702
|
+
mouseEvents = ["mouseup"]
|
|
1703
|
+
touchEvents = ["touchend"]
|
|
1704
|
+
case "touchcancel":
|
|
1705
|
+
pointerEvents = ["pointercancel"]
|
|
1706
|
+
mouseEvents = []
|
|
1707
|
+
touchEvents = ["touchcancel"]
|
|
1708
|
+
default:
|
|
1709
|
+
return nil
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
guard let x, let y, x.isFinite, y.isFinite else {
|
|
1713
|
+
return nil
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
let pointerEventsData = try? JSONSerialization.data(withJSONObject: pointerEvents)
|
|
1717
|
+
let mouseEventsData = try? JSONSerialization.data(withJSONObject: mouseEvents)
|
|
1718
|
+
let touchEventsData = try? JSONSerialization.data(withJSONObject: touchEvents)
|
|
1719
|
+
let pointerEventsJSON = pointerEventsData.flatMap { String(data: $0, encoding: .utf8) } ?? "[]"
|
|
1720
|
+
let mouseEventsJSON = mouseEventsData.flatMap { String(data: $0, encoding: .utf8) } ?? "[]"
|
|
1721
|
+
let touchEventsJSON = touchEventsData.flatMap { String(data: $0, encoding: .utf8) } ?? "[]"
|
|
1722
|
+
|
|
1723
|
+
return """
|
|
1724
|
+
(function() {
|
|
1725
|
+
const x = \(jsNumber(x));
|
|
1726
|
+
const y = \(jsNumber(y));
|
|
1727
|
+
const target = document.elementFromPoint(x, y);
|
|
1728
|
+
if (!target) return false;
|
|
1729
|
+
const base = { bubbles: true, cancelable: true, composed: true, clientX: x, clientY: y, screenX: x, screenY: y };
|
|
1730
|
+
for (const name of \(pointerEventsJSON)) {
|
|
1731
|
+
if (typeof PointerEvent === 'function') {
|
|
1732
|
+
target.dispatchEvent(new PointerEvent(name, Object.assign({}, base, { pointerId: 1, pointerType: 'touch', isPrimary: true })));
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
for (const name of \(mouseEventsJSON)) {
|
|
1736
|
+
target.dispatchEvent(new MouseEvent(name, base));
|
|
1737
|
+
}
|
|
1738
|
+
if (typeof TouchEvent === 'function' && typeof Touch === 'function') {
|
|
1739
|
+
for (const name of \(touchEventsJSON)) {
|
|
1740
|
+
try {
|
|
1741
|
+
const touch = new Touch({ identifier: 1, target: target, clientX: x, clientY: y, screenX: x, screenY: y });
|
|
1742
|
+
const activeTouches = name === 'touchend' || name === 'touchcancel' ? [] : [touch];
|
|
1743
|
+
target.dispatchEvent(new TouchEvent(name, {
|
|
1744
|
+
bubbles: true,
|
|
1745
|
+
cancelable: true,
|
|
1746
|
+
composed: true,
|
|
1747
|
+
touches: activeTouches,
|
|
1748
|
+
targetTouches: activeTouches,
|
|
1749
|
+
changedTouches: [touch]
|
|
1750
|
+
}));
|
|
1751
|
+
} catch (_) {}
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
return true;
|
|
1755
|
+
})();
|
|
1756
|
+
"""
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
@objc func dispatchInputEvent(_ call: CAPPluginCall) {
|
|
1760
|
+
guard let type = call.getString("type") else {
|
|
1761
|
+
call.reject("Input event type is required")
|
|
1762
|
+
return
|
|
1763
|
+
}
|
|
1764
|
+
guard let script = dispatchInputScript(
|
|
1765
|
+
type: type,
|
|
1766
|
+
x: call.getFloat("x"),
|
|
1767
|
+
y: call.getFloat("y"),
|
|
1768
|
+
deltaX: call.getFloat("deltaX"),
|
|
1769
|
+
deltaY: call.getFloat("deltaY")
|
|
1770
|
+
) else {
|
|
1771
|
+
call.reject("Unsupported input event type or invalid input payload: \(type)")
|
|
1772
|
+
return
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
DispatchQueue.main.async {
|
|
1776
|
+
let targetId = call.getString("id") ?? self.activeWebViewId
|
|
1777
|
+
guard let webViewController = self.resolveWebViewController(for: targetId) else {
|
|
1778
|
+
call.reject("WebView is not initialized")
|
|
1779
|
+
return
|
|
1780
|
+
}
|
|
1781
|
+
webViewController.executeScript(script: script)
|
|
1782
|
+
call.resolve()
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1407
1786
|
@objc func executeScript(_ call: CAPPluginCall) {
|
|
1408
1787
|
guard let script = call.getString("code") else {
|
|
1409
1788
|
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
|
|