@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
- - **Cookie and cache management** for session control
29
- - **Custom sizes** for extra control of the display position
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 any scenario requiring deep integration with web content.
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 (inset > 0 || !applyFallbackWhenZero || fallbackBottomInset <= 0) {
29
+ if (!applyFallbackWhenZero || fallbackBottomInset <= 0) {
30
30
  return inset;
31
31
  }
32
32
 
33
- if (hasSideNavigationBarInsets(systemBarsLeft, systemBarsRight, navigationBarsLeft, navigationBarsRight)) {
34
- return 0;
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(int systemBarsLeft, int systemBarsRight, int navigationBarsLeft, int navigationBarsRight) {
41
- return systemBarsLeft > 0 || systemBarsRight > 0 || navigationBarsLeft > 0 || navigationBarsRight > 0;
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.0"
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, navigationController.presentingViewController != nil else {
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
- 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
+ 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
- // Make status bar background with colored view underneath
760
- open func setupStatusBarBackground(color: UIColor) {
761
- // Remove any existing status bar view
762
- statusBarBackgroundView?.removeFromSuperview()
759
+ private func applyNavigationBarBackground(color: UIColor) {
760
+ guard let navigationBar = navigationController?.navigationBar else {
761
+ return
762
+ }
763
763
 
764
- // Create a new view to cover both status bar and navigation bar
765
- statusBarBackgroundView = UIView()
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 let navView = navigationController?.view {
768
- // Add to back of view hierarchy
769
- navView.insertSubview(statusBarBackgroundView!, at: 0)
770
- statusBarBackgroundView?.translatesAutoresizingMaskIntoConstraints = false
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
- // Calculate total height - status bar + navigation bar
773
- let navBarHeight = navigationController?.navigationBar.frame.height ?? 44
774
- let totalHeight = (navigationController?.view.safeAreaInsets.top ?? CGFloat(0)) + navBarHeight
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
- // Position from top of screen to bottom of navigation bar
777
- NSLayoutConstraint.activate([
778
- statusBarBackgroundView!.topAnchor.constraint(equalTo: navView.topAnchor),
779
- statusBarBackgroundView!.leadingAnchor.constraint(equalTo: navView.leadingAnchor),
780
- statusBarBackgroundView!.trailingAnchor.constraint(equalTo: navView.trailingAnchor),
781
- statusBarBackgroundView!.heightAnchor.constraint(equalToConstant: totalHeight)
782
- ])
786
+ navigationBar.standardAppearance = appearance
787
+ navigationBar.compactAppearance = appearance
788
+ navigationBar.scrollEdgeAppearance = appearance
789
+ navigationBar.compactScrollEdgeAppearance = appearance
790
+ }
791
+ }
783
792
 
784
- // Set background color
785
- statusBarBackgroundView?.backgroundColor = color
793
+ // Make status bar background with colored view underneath
794
+ open func setupStatusBarBackground(color: UIColor) {
795
+ statusBarBackgroundView?.removeFromSuperview()
796
+ statusBarBackgroundView = UIView()
786
797
 
787
- // Make navigation bar transparent to show our view underneath
788
- navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
789
- navigationController?.navigationBar.shadowImage = UIImage()
790
- navigationController?.navigationBar.isTranslucent = true
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
- // If we have a target frame and the touch is outside it, pass through
3260
- if let frame = targetFrame {
3261
- if !frame.contains(point) {
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-inappbrowser",
3
- "version": "8.7.0",
3
+ "version": "8.7.2-beta.pr599.357.1",
4
4
  "description": "Capacitor plugin in app browser",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",