@capgo/capacitor-inappbrowser 8.10.1 → 8.10.2
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.
|
@@ -2649,6 +2649,33 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
2649
2649
|
WebView.HitTestResult result = view.getHitTestResult();
|
|
2650
2650
|
String data = result != null ? result.getExtra() : null;
|
|
2651
2651
|
|
|
2652
|
+
// Authorized App Links with target=_blank should open the native app,
|
|
2653
|
+
// not spawn a managed popup (which previously left the UI in a broken state).
|
|
2654
|
+
if (
|
|
2655
|
+
data != null &&
|
|
2656
|
+
!_options.getPreventDeeplink() &&
|
|
2657
|
+
isHttpOrHttpsUrl(data) &&
|
|
2658
|
+
isAuthorizedAppLink(data, _options.getAuthorizedAppLinks())
|
|
2659
|
+
) {
|
|
2660
|
+
try {
|
|
2661
|
+
Log.d("InAppBrowser", "Opening authorized blank-target link externally: " + data);
|
|
2662
|
+
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(data));
|
|
2663
|
+
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
2664
|
+
view.getContext().startActivity(intent);
|
|
2665
|
+
return false;
|
|
2666
|
+
} catch (ActivityNotFoundException | SecurityException e) {
|
|
2667
|
+
Log.w("InAppBrowser", "No app for authorized blank-target link, loading in current WebView", e);
|
|
2668
|
+
final String fallbackUrl = data;
|
|
2669
|
+
view.post(() -> {
|
|
2670
|
+
if (isDismissing || _webView == null || _webView != view) {
|
|
2671
|
+
return;
|
|
2672
|
+
}
|
|
2673
|
+
_webView.loadUrl(fallbackUrl);
|
|
2674
|
+
});
|
|
2675
|
+
return false;
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2652
2679
|
if (shouldLoadBlankTargetInCurrentWebView(data)) {
|
|
2653
2680
|
Log.d("InAppBrowser", "Loading target=_blank link in current WebView: " + data);
|
|
2654
2681
|
view.post(() -> _webView.loadUrl(data));
|
|
@@ -13,6 +13,70 @@ enum ActiveWebViewSupport {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
enum BlankTargetNavigationSupport {
|
|
17
|
+
enum Action: Equatable {
|
|
18
|
+
case openExternalApp
|
|
19
|
+
case loadInCurrentWebView
|
|
20
|
+
case createPopup
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// Decides how a `target=_blank` / new-window request should be handled.
|
|
24
|
+
static func resolve(
|
|
25
|
+
urlIsHttpOrHttps: Bool,
|
|
26
|
+
openBlankTargetInWebView: Bool,
|
|
27
|
+
preventDeeplink: Bool,
|
|
28
|
+
isAuthorizedAppLink: Bool
|
|
29
|
+
) -> Action {
|
|
30
|
+
guard urlIsHttpOrHttps else {
|
|
31
|
+
return .createPopup
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if isAuthorizedAppLink && !preventDeeplink {
|
|
35
|
+
return .openExternalApp
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if openBlankTargetInWebView || preventDeeplink {
|
|
39
|
+
return .loadInCurrentWebView
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return .createPopup
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Prefer the parent content VC so we never present from a UINavigationController
|
|
46
|
+
/// whose root view was replaced (custom width/height PassThroughView).
|
|
47
|
+
static func popupPresenter(
|
|
48
|
+
parentController: UIViewController,
|
|
49
|
+
bridgeViewController: UIViewController?
|
|
50
|
+
) -> UIViewController {
|
|
51
|
+
if parentController.view.window != nil {
|
|
52
|
+
return parentController
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if let navigationController = parentController.navigationController,
|
|
56
|
+
navigationController.view.window != nil {
|
|
57
|
+
if let visible = navigationController.visibleViewController,
|
|
58
|
+
visible.view.window != nil {
|
|
59
|
+
return visible
|
|
60
|
+
}
|
|
61
|
+
return navigationController
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
var top = bridgeViewController
|
|
65
|
+
while let presented = top?.presentedViewController {
|
|
66
|
+
top = presented
|
|
67
|
+
}
|
|
68
|
+
return top ?? parentController
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static func topPresenter(from root: UIViewController) -> UIViewController {
|
|
72
|
+
var top = root
|
|
73
|
+
while let presented = top.presentedViewController {
|
|
74
|
+
top = presented
|
|
75
|
+
}
|
|
76
|
+
return top
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
16
80
|
protocol ProxyRequestLocating {
|
|
17
81
|
func hasPendingProxyRequest(_ requestId: String) -> Bool
|
|
18
82
|
}
|
|
@@ -98,7 +162,7 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
98
162
|
case aware = "AWARE"
|
|
99
163
|
case fakeVisible = "FAKE_VISIBLE"
|
|
100
164
|
}
|
|
101
|
-
private let pluginVersion: String = "8.10.
|
|
165
|
+
private let pluginVersion: String = "8.10.2"
|
|
102
166
|
public let identifier = "CapgoInAppBrowserPlugin"
|
|
103
167
|
public let jsName = "CapgoInAppBrowser"
|
|
104
168
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
@@ -314,8 +378,25 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
314
378
|
return nil
|
|
315
379
|
}
|
|
316
380
|
} else {
|
|
317
|
-
|
|
318
|
-
|
|
381
|
+
navigationController.modalPresentationStyle = .overFullScreen
|
|
382
|
+
navigationController.modalTransitionStyle = .crossDissolve
|
|
383
|
+
|
|
384
|
+
let presenter = BlankTargetNavigationSupport.popupPresenter(
|
|
385
|
+
parentController: parentController,
|
|
386
|
+
bridgeViewController: self.bridge?.viewController
|
|
387
|
+
)
|
|
388
|
+
presentNavigationControllerSafely(navigationController, from: presenter, animated: true) { [weak self] presented in
|
|
389
|
+
if !presented {
|
|
390
|
+
self?.unregisterWebView(id: popupId)
|
|
391
|
+
}
|
|
392
|
+
self?.notifyPopupWindowOpened(
|
|
393
|
+
id: popupId,
|
|
394
|
+
parentId: parentController.instanceId,
|
|
395
|
+
url: navigationAction.request.url?.absoluteString,
|
|
396
|
+
visible: presented
|
|
397
|
+
)
|
|
398
|
+
}
|
|
399
|
+
return popupWebView
|
|
319
400
|
}
|
|
320
401
|
notifyPopupWindowOpened(
|
|
321
402
|
id: popupId,
|
|
@@ -326,6 +407,43 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
326
407
|
return popupWebView
|
|
327
408
|
}
|
|
328
409
|
|
|
410
|
+
private func presentNavigationControllerSafely(
|
|
411
|
+
_ navigationController: UINavigationController,
|
|
412
|
+
from presenter: UIViewController?,
|
|
413
|
+
animated: Bool,
|
|
414
|
+
completion: ((Bool) -> Void)? = nil
|
|
415
|
+
) {
|
|
416
|
+
guard let presenter else {
|
|
417
|
+
completion?(false)
|
|
418
|
+
return
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
let presentBlock = {
|
|
422
|
+
if navigationController.presentingViewController != nil {
|
|
423
|
+
completion?(true)
|
|
424
|
+
return
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
let top = BlankTargetNavigationSupport.topPresenter(from: presenter)
|
|
428
|
+
guard top.presentedViewController == nil else {
|
|
429
|
+
print("[InAppBrowser] Skipping present; presenter already has a presented VC")
|
|
430
|
+
completion?(false)
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
top.present(navigationController, animated: animated) {
|
|
434
|
+
completion?(true)
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if Thread.isMainThread,
|
|
439
|
+
!presenter.isBeingPresented,
|
|
440
|
+
!presenter.isBeingDismissed {
|
|
441
|
+
presentBlock()
|
|
442
|
+
} else {
|
|
443
|
+
DispatchQueue.main.async(execute: presentBlock)
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
329
447
|
private func resolveWebViewController(for id: String?) -> WKWebViewController? {
|
|
330
448
|
if let id {
|
|
331
449
|
return webViewControllers[id]
|
|
@@ -421,9 +539,13 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
421
539
|
}
|
|
422
540
|
dismissActiveKeyboard()
|
|
423
541
|
let presenter = self.bridge?.viewController?.presentedViewController ?? self.bridge?.viewController
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
542
|
+
presentNavigationControllerSafely(navigationController, from: presenter, animated: isAnimated) { presented in
|
|
543
|
+
if presented {
|
|
544
|
+
self.currentPluginCall?.resolve()
|
|
545
|
+
} else {
|
|
546
|
+
self.currentPluginCall?.reject("Failed to present webview")
|
|
547
|
+
}
|
|
548
|
+
}
|
|
427
549
|
return true
|
|
428
550
|
}
|
|
429
551
|
|
|
@@ -1610,9 +1732,13 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1610
1732
|
|
|
1611
1733
|
if navController.presentingViewController == nil {
|
|
1612
1734
|
let presenter = self.bridge?.viewController?.presentedViewController ?? self.bridge?.viewController
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1735
|
+
self.presentNavigationControllerSafely(navController, from: presenter, animated: true) { presented in
|
|
1736
|
+
if presented {
|
|
1737
|
+
call?.resolve()
|
|
1738
|
+
} else {
|
|
1739
|
+
call?.reject("Failed to present webview")
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1616
1742
|
return
|
|
1617
1743
|
}
|
|
1618
1744
|
}
|
|
@@ -1664,7 +1790,6 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1664
1790
|
}
|
|
1665
1791
|
}
|
|
1666
1792
|
}
|
|
1667
|
-
|
|
1668
1793
|
@objc func bringToFront(_ call: CAPPluginCall) {
|
|
1669
1794
|
DispatchQueue.main.async {
|
|
1670
1795
|
let isAnimated = call.getBool("isAnimated", true)
|
|
@@ -1685,9 +1810,13 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1685
1810
|
self.setActiveWebView(id: resolvedId, webView: webViewController, navigationController: navigationController)
|
|
1686
1811
|
if navigationController.presentingViewController == nil {
|
|
1687
1812
|
let presenter = self.bridge?.viewController?.presentedViewController ?? self.bridge?.viewController
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1813
|
+
self.presentNavigationControllerSafely(navigationController, from: presenter, animated: isAnimated) { presented in
|
|
1814
|
+
if presented {
|
|
1815
|
+
call.resolve()
|
|
1816
|
+
} else {
|
|
1817
|
+
call.reject("Failed to present webview")
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1691
1820
|
return
|
|
1692
1821
|
}
|
|
1693
1822
|
call.resolve()
|
|
@@ -3211,6 +3211,31 @@ extension WKWebViewController: WKUIDelegate {
|
|
|
3211
3211
|
}
|
|
3212
3212
|
|
|
3213
3213
|
print("[InAppBrowser] Handling popup/new window request for URL: \(url.absoluteString)")
|
|
3214
|
+
|
|
3215
|
+
let isAuthorized = isUrlAuthorized(url, authorizedLinks: authorizedAppLinks)
|
|
3216
|
+
switch BlankTargetNavigationSupport.resolve(
|
|
3217
|
+
urlIsHttpOrHttps: isHttpOrHttps(url),
|
|
3218
|
+
openBlankTargetInWebView: openBlankTargetInWebView,
|
|
3219
|
+
preventDeeplink: preventDeeplink,
|
|
3220
|
+
isAuthorizedAppLink: isAuthorized
|
|
3221
|
+
) {
|
|
3222
|
+
case .openExternalApp:
|
|
3223
|
+
// Prefer the native app / Universal Link. If none handles it, keep browsing in-place.
|
|
3224
|
+
tryOpenUniversalLink(url) { [weak webView] opened in
|
|
3225
|
+
if !opened {
|
|
3226
|
+
DispatchQueue.main.async {
|
|
3227
|
+
webView?.load(navigationAction.request)
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
return nil
|
|
3232
|
+
case .loadInCurrentWebView:
|
|
3233
|
+
webView.load(navigationAction.request)
|
|
3234
|
+
return nil
|
|
3235
|
+
case .createPopup:
|
|
3236
|
+
break
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3214
3239
|
return capBrowserPlugin?.createManagedPopupWebView(
|
|
3215
3240
|
from: self,
|
|
3216
3241
|
configuration: configuration,
|