@capgo/capacitor-inappbrowser 8.7.0 → 8.7.2-beta.pr599.357.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
CHANGED
|
@@ -25,10 +25,12 @@ The official Capacitor Browser plugin has strict security limitations that preve
|
|
|
25
25
|
- **Camera and microphone access** within the browser context
|
|
26
26
|
- **URL change monitoring** for navigation tracking
|
|
27
27
|
- **Custom toolbars and UI** for branded experiences
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
28
|
+
- **Native browser layering** behind Ionic or Capacitor UI with input forwarding
|
|
29
|
+
- **Partial-screen WebViews** with click-through areas outside the browser frame
|
|
30
|
+
- **Cookie, cache, and full browsing-data controls** for private or isolated sessions
|
|
31
|
+
- **Native proxy, download, and popup handling** for advanced embedded web apps
|
|
30
32
|
|
|
31
|
-
Perfect for OAuth flows, embedded web apps, video calls, and
|
|
33
|
+
Perfect for OAuth flows, embedded web apps, checkout screens, browser-backed payment flows, video calls, support portals, and apps that need native UI around live web content.
|
|
32
34
|
|
|
33
35
|
## Documentation
|
|
34
36
|
|
|
@@ -74,6 +76,82 @@ import { InAppBrowser } from '@capgo/capacitor-inappbrowser';
|
|
|
74
76
|
InAppBrowser.open({ url: 'YOUR_URL' });
|
|
75
77
|
```
|
|
76
78
|
|
|
79
|
+
### Common use cases
|
|
80
|
+
|
|
81
|
+
#### Show Ionic UI over a live browser page
|
|
82
|
+
|
|
83
|
+
Use `toBack` when the native browser should stay visible behind your Ionic or Capacitor UI. This is useful for checkout overlays, payment confirmation panels, guided browser flows, or any flow where your app owns the controls while the web page remains loaded behind it.
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
import { InAppBrowser } from '@capgo/capacitor-inappbrowser';
|
|
87
|
+
|
|
88
|
+
const { id } = await InAppBrowser.openWebView({
|
|
89
|
+
url: 'https://example.com/checkout',
|
|
90
|
+
toBack: true,
|
|
91
|
+
transparentBackground: true,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// If your overlay needs to interact with the page behind it, forward the gesture.
|
|
95
|
+
await InAppBrowser.dispatchInputEvent({
|
|
96
|
+
id,
|
|
97
|
+
type: 'click',
|
|
98
|
+
x: 160,
|
|
99
|
+
y: 420,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Bring the browser back above the app when the native overlay is done.
|
|
103
|
+
await InAppBrowser.bringToFront({ id });
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Make the app background transparent wherever the browser should be visible. `dispatchInputEvent()` supports click, touch, and scroll forwarding; coordinates are relative to the browser viewport.
|
|
107
|
+
|
|
108
|
+
#### Build a partial-screen browser or bottom sheet
|
|
109
|
+
|
|
110
|
+
Use `height`, `width`, `x`, and `y` for picture-in-picture views, browser sheets, or layouts where the user should keep interacting with the Ionic app around the browser. When the WebView does not cover the full screen, touches outside the browser frame pass through to the underlying Capacitor WebView on Android and iOS.
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
const bottomGap = 200;
|
|
114
|
+
|
|
115
|
+
const { id } = await InAppBrowser.openWebView({
|
|
116
|
+
url: 'https://example.com/offers',
|
|
117
|
+
height: window.innerHeight - bottomGap,
|
|
118
|
+
y: 0,
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Use `updateDimensions()` to resize the browser while keeping the same page state, for example when expanding a mini browser into a larger sheet.
|
|
123
|
+
|
|
124
|
+
#### Keep checkout, auth, or support sessions private
|
|
125
|
+
|
|
126
|
+
Use `persistWebViewData: false` when a WebView must avoid persistent cookies, cache, local storage, IndexedDB, and other website data where the platform supports it. Use `clearAllBrowsingData()` when the app needs to wipe the default store and all opened managed WebViews.
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
const { id } = await InAppBrowser.openWebView({
|
|
130
|
+
url: 'https://example.com/login',
|
|
131
|
+
persistWebViewData: false,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Later, after logout or account switching:
|
|
135
|
+
await InAppBrowser.clearAllBrowsingData();
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### Keep multiple browser instances ready
|
|
139
|
+
|
|
140
|
+
Use `hidden`, `hide()`, and `show()` when you need to preload or preserve several WebViews without keeping them on screen. This keeps browser state available while the native modal is dismissed so it does not block touches in the app.
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
const first = await InAppBrowser.openWebView({ url: 'https://example.com/a', hidden: true });
|
|
144
|
+
const second = await InAppBrowser.openWebView({ url: 'https://example.com/b', hidden: true });
|
|
145
|
+
|
|
146
|
+
await InAppBrowser.show({ id: first.id });
|
|
147
|
+
await InAppBrowser.hide({ id: first.id });
|
|
148
|
+
await InAppBrowser.show({ id: second.id });
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Embed advanced web app flows
|
|
152
|
+
|
|
153
|
+
Use proxy rules and `handleDownloads` for web apps that need request control, file uploads, downloads, or controlled popup behavior inside the managed browser. Typical examples include document portals, support desks, payment pages, Google Pay flows on Android, and apps that need to keep `_blank` links inside the same managed WebView.
|
|
154
|
+
|
|
77
155
|
### Customize Chrome Custom Tab Appearance (Android)
|
|
78
156
|
|
|
79
157
|
The `open()` method launches a Chrome Custom Tab on Android. You can customize its appearance to blend with your app:
|
|
@@ -26,19 +26,32 @@ final class SafeAreaInsetsSupport {
|
|
|
26
26
|
boolean applyFallbackWhenZero
|
|
27
27
|
) {
|
|
28
28
|
int inset = resolveSafeBottomInset(systemBarsBottom, navigationBarsBottom, systemGesturesBottom, mandatoryGesturesBottom);
|
|
29
|
-
if (
|
|
29
|
+
if (!applyFallbackWhenZero || fallbackBottomInset <= 0) {
|
|
30
30
|
return inset;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
if (hasSideNavigationBarInsets(systemBarsLeft, systemBarsRight, navigationBarsLeft, navigationBarsRight)) {
|
|
34
|
-
return
|
|
33
|
+
if (hasSideNavigationBarInsets(systemBarsLeft, systemBarsRight, navigationBarsLeft, navigationBarsRight, fallbackBottomInset)) {
|
|
34
|
+
return inset;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
return fallbackBottomInset;
|
|
37
|
+
return Math.max(inset, fallbackBottomInset);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
static boolean hasSideNavigationBarInsets(
|
|
41
|
-
|
|
40
|
+
static boolean hasSideNavigationBarInsets(
|
|
41
|
+
int systemBarsLeft,
|
|
42
|
+
int systemBarsRight,
|
|
43
|
+
int navigationBarsLeft,
|
|
44
|
+
int navigationBarsRight,
|
|
45
|
+
int minSideNavBarInset
|
|
46
|
+
) {
|
|
47
|
+
if (minSideNavBarInset <= 0) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
Math.max(systemBarsLeft, navigationBarsLeft) >= minSideNavBarInset ||
|
|
53
|
+
Math.max(systemBarsRight, navigationBarsRight) >= minSideNavBarInset
|
|
54
|
+
);
|
|
42
55
|
}
|
|
43
56
|
|
|
44
57
|
static int resolveBottomMargin(boolean enabledSafeBottomMargin, int safeBottomInset, int imeBottom) {
|
|
@@ -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.7.
|
|
101
|
+
private let pluginVersion: String = "8.7.2"
|
|
102
102
|
public let identifier = "CapgoInAppBrowserPlugin"
|
|
103
103
|
public let jsName = "CapgoInAppBrowser"
|
|
104
104
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
@@ -190,11 +190,16 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
private func dismissNavigationControllerIfPresented(_ navigationController: UINavigationController?) {
|
|
194
|
-
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?()
|
|
195
200
|
return
|
|
196
201
|
}
|
|
197
|
-
navigationController.dismiss(animated: false)
|
|
202
|
+
navigationController.dismiss(animated: false, completion: completion)
|
|
198
203
|
}
|
|
199
204
|
|
|
200
205
|
private func unregisterWebView(id: String) {
|
|
@@ -446,7 +451,6 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
446
451
|
}
|
|
447
452
|
|
|
448
453
|
dismissActiveKeyboard()
|
|
449
|
-
dismissNavigationControllerIfPresented(navigationController)
|
|
450
454
|
navigationController.view.removeFromSuperview()
|
|
451
455
|
if transparentBackground {
|
|
452
456
|
makeHostWebViewTransparent(for: id)
|
|
@@ -1378,6 +1382,7 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1378
1382
|
containerView.backgroundColor = .clear
|
|
1379
1383
|
containerView.frame = UIScreen.main.bounds
|
|
1380
1384
|
containerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
1385
|
+
containerView.passthroughView = self.bridge?.webView
|
|
1381
1386
|
|
|
1382
1387
|
let targetFrame = CustomWebViewFrameSupport.resolvedFrame(
|
|
1383
1388
|
width: width.map { CGFloat($0) },
|
|
@@ -1602,14 +1607,16 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1602
1607
|
}
|
|
1603
1608
|
|
|
1604
1609
|
let transparentBackground = call.getBool("transparentBackground", true)
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
+
self.dismissNavigationControllerIfPresented(navigationController) {
|
|
1611
|
+
webViewController.isLayeredBehind = true
|
|
1612
|
+
webViewController.transparentHostBackground = transparentBackground
|
|
1613
|
+
guard self.sendNavigationControllerToBack(id: resolvedId, transparentBackground: transparentBackground) else {
|
|
1614
|
+
call.reject("Failed to send webview to back")
|
|
1615
|
+
return
|
|
1616
|
+
}
|
|
1617
|
+
self.setActiveWebView(id: resolvedId, webView: webViewController, navigationController: navigationController)
|
|
1618
|
+
call.resolve()
|
|
1610
1619
|
}
|
|
1611
|
-
self.setActiveWebView(id: resolvedId, webView: webViewController, navigationController: navigationController)
|
|
1612
|
-
call.resolve()
|
|
1613
1620
|
}
|
|
1614
1621
|
}
|
|
1615
1622
|
|
|
@@ -756,40 +756,62 @@ open class WKWebViewController: UIViewController, WKScriptMessageHandler {
|
|
|
756
756
|
return windowScene?.statusBarManager?.statusBarFrame.height ?? 0
|
|
757
757
|
}
|
|
758
758
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
759
|
+
private func applyNavigationBarBackground(color: UIColor) {
|
|
760
|
+
guard let navigationBar = navigationController?.navigationBar else {
|
|
761
|
+
return
|
|
762
|
+
}
|
|
763
763
|
|
|
764
|
-
|
|
765
|
-
|
|
764
|
+
navigationBar.backgroundColor = color
|
|
765
|
+
navigationBar.barTintColor = color
|
|
766
|
+
navigationBar.isTranslucent = false
|
|
767
|
+
navigationBar.setBackgroundImage(UIImage(), for: .default)
|
|
768
|
+
navigationBar.shadowImage = UIImage()
|
|
769
|
+
navigationBar.setValue(true, forKey: "hidesShadow")
|
|
766
770
|
|
|
767
|
-
if
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
+
if #available(iOS 13.0, *) {
|
|
772
|
+
let appearance = UINavigationBarAppearance()
|
|
773
|
+
appearance.configureWithOpaqueBackground()
|
|
774
|
+
appearance.backgroundColor = color
|
|
775
|
+
appearance.backgroundEffect = nil
|
|
776
|
+
appearance.shadowColor = .clear
|
|
777
|
+
appearance.shadowImage = UIImage()
|
|
771
778
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
779
|
+
if let titleTextAttributes = navigationBar.titleTextAttributes {
|
|
780
|
+
appearance.titleTextAttributes = titleTextAttributes
|
|
781
|
+
}
|
|
782
|
+
if let largeTitleTextAttributes = navigationBar.largeTitleTextAttributes {
|
|
783
|
+
appearance.largeTitleTextAttributes = largeTitleTextAttributes
|
|
784
|
+
}
|
|
775
785
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
])
|
|
786
|
+
navigationBar.standardAppearance = appearance
|
|
787
|
+
navigationBar.compactAppearance = appearance
|
|
788
|
+
navigationBar.scrollEdgeAppearance = appearance
|
|
789
|
+
navigationBar.compactScrollEdgeAppearance = appearance
|
|
790
|
+
}
|
|
791
|
+
}
|
|
783
792
|
|
|
784
|
-
|
|
785
|
-
|
|
793
|
+
// Make status bar background with colored view underneath
|
|
794
|
+
open func setupStatusBarBackground(color: UIColor) {
|
|
795
|
+
statusBarBackgroundView?.removeFromSuperview()
|
|
796
|
+
statusBarBackgroundView = UIView()
|
|
786
797
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
navigationController?.navigationBar.isTranslucent = true
|
|
798
|
+
guard let navController = navigationController,
|
|
799
|
+
let navView = navController.view,
|
|
800
|
+
let statusBarBackgroundView else {
|
|
801
|
+
return
|
|
792
802
|
}
|
|
803
|
+
navView.insertSubview(statusBarBackgroundView, at: 0)
|
|
804
|
+
statusBarBackgroundView.translatesAutoresizingMaskIntoConstraints = false
|
|
805
|
+
statusBarBackgroundView.backgroundColor = color
|
|
806
|
+
|
|
807
|
+
NSLayoutConstraint.activate([
|
|
808
|
+
statusBarBackgroundView.topAnchor.constraint(equalTo: navView.topAnchor),
|
|
809
|
+
statusBarBackgroundView.leadingAnchor.constraint(equalTo: navView.leadingAnchor),
|
|
810
|
+
statusBarBackgroundView.trailingAnchor.constraint(equalTo: navView.trailingAnchor),
|
|
811
|
+
statusBarBackgroundView.bottomAnchor.constraint(equalTo: navController.navigationBar.bottomAnchor, constant: 1)
|
|
812
|
+
])
|
|
813
|
+
|
|
814
|
+
applyNavigationBarBackground(color: color)
|
|
793
815
|
}
|
|
794
816
|
|
|
795
817
|
// Override to use our custom status bar style
|
|
@@ -3244,6 +3266,7 @@ class PassThroughView: UIView {
|
|
|
3244
3266
|
}
|
|
3245
3267
|
}
|
|
3246
3268
|
weak var framedContentView: UIView?
|
|
3269
|
+
weak var passthroughView: UIView?
|
|
3247
3270
|
|
|
3248
3271
|
override func layoutSubviews() {
|
|
3249
3272
|
super.layoutSubviews()
|
|
@@ -3256,14 +3279,14 @@ class PassThroughView: UIView {
|
|
|
3256
3279
|
}
|
|
3257
3280
|
|
|
3258
3281
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
return nil // Pass through to underlying views
|
|
3282
|
+
if let frame = targetFrame, !frame.contains(point) {
|
|
3283
|
+
guard let passthroughView else {
|
|
3284
|
+
return nil
|
|
3263
3285
|
}
|
|
3286
|
+
let convertedPoint = convert(point, to: passthroughView)
|
|
3287
|
+
return passthroughView.hitTest(convertedPoint, with: event)
|
|
3264
3288
|
}
|
|
3265
3289
|
|
|
3266
|
-
// Otherwise, handle normally
|
|
3267
3290
|
return super.hitTest(point, with: event)
|
|
3268
3291
|
}
|
|
3269
3292
|
}
|