@capgo/inappbrowser 8.2.0 → 8.3.0-alpha.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 CHANGED
@@ -1,9 +1,22 @@
1
1
  # @capgo/inappbrowser
2
- <a href="https://capgo.app/"><img src='https://raw.githubusercontent.com/Cap-go/capgo/main/assets/capgo_banner.png' alt='Capgo - Instant updates for capacitor'/></a>
2
+
3
+ <a href="https://capgo.app/">
4
+ <img
5
+ src="https://raw.githubusercontent.com/Cap-go/capgo/main/assets/capgo_banner.png"
6
+ alt="Capgo - Instant updates for capacitor"
7
+ />
8
+ </a>
3
9
 
4
10
  <div align="center">
5
- <h2><a href="https://capgo.app/?ref=plugin_inappbrowser"> ➡️ Get Instant updates for your App with Capgo</a></h2>
6
- <h2><a href="https://capgo.app/consulting/?ref=plugin_inappbrowser"> Missing a feature? We’ll build the plugin for you 💪</a></h2>
11
+ <h2>
12
+ <a href="https://capgo.app/?ref=plugin_inappbrowser"> ➡️ Get Instant updates for your App with Capgo</a>
13
+ </h2>
14
+ <h2>
15
+ <a href="https://capgo.app/consulting/?ref=plugin_inappbrowser">
16
+ {' '}
17
+ Missing a feature? We’ll build the plugin for you 💪
18
+ </a>
19
+ </h2>
7
20
  </div>
8
21
 
9
22
  Capacitor plugin in app browser with urlChangeEvent, two way communication, camera and microphone usage, etc.
@@ -30,10 +43,10 @@ The most complete doc is available here: https://capgo.app/docs/plugins/inappbro
30
43
 
31
44
  | Plugin version | Capacitor compatibility | Maintained |
32
45
  | -------------- | ----------------------- | ---------- |
33
- | v8.\*.\* | v8.\*.\* | ✅ |
34
- | v7.\*.\* | v7.\*.\* | On demand |
35
- | v6.\*.\* | v6.\*.\* | ❌ |
36
- | v5.\*.\* | v5.\*.\* | ❌ |
46
+ | v8.\*.\* | v8.\*.\* | ✅ |
47
+ | v7.\*.\* | v7.\*.\* | On demand |
48
+ | v6.\*.\* | v6.\*.\* | ❌ |
49
+ | v5.\*.\* | v5.\*.\* | ❌ |
37
50
 
38
51
  > **Note:** The major version of this plugin follows the major version of Capacitor. Use the version that matches your Capacitor installation (e.g., plugin v8 for Capacitor 8). Only the latest major version is actively maintained.
39
52
 
@@ -43,12 +56,13 @@ The most complete doc is available here: https://capgo.app/docs/plugins/inappbro
43
56
  npm install @capgo/inappbrowser
44
57
  npx cap sync
45
58
  ```
59
+
46
60
  ## Usage
47
61
 
48
62
  ```js
49
- import { InAppBrowser } from '@capgo/inappbrowser'
63
+ import { InAppBrowser } from '@capgo/inappbrowser';
50
64
 
51
- InAppBrowser.open({ url: "YOUR_URL" });
65
+ InAppBrowser.open({ url: 'YOUR_URL' });
52
66
  ```
53
67
 
54
68
  ### Customize Chrome Custom Tab Appearance (Android)
@@ -77,15 +91,15 @@ All CCT options are Android-only and safely ignored on iOS. See [`OpenOptions`](
77
91
  By default, the webview opens in fullscreen. You can set custom dimensions to control the size and position:
78
92
 
79
93
  ```js
80
- import { InAppBrowser } from '@capgo/inappbrowser'
94
+ import { InAppBrowser } from '@capgo/inappbrowser';
81
95
 
82
96
  // Open with custom dimensions (400x600 at position 50,100)
83
97
  const { id } = await InAppBrowser.openWebView({
84
- url: "YOUR_URL",
98
+ url: 'YOUR_URL',
85
99
  width: 400,
86
100
  height: 600,
87
101
  x: 50,
88
- y: 100
102
+ y: 100,
89
103
  });
90
104
 
91
105
  // Update dimensions at runtime
@@ -94,7 +108,7 @@ InAppBrowser.updateDimensions({
94
108
  width: 500,
95
109
  height: 700,
96
110
  x: 100,
97
- y: 150
111
+ y: 150,
98
112
  });
99
113
  ```
100
114
 
@@ -105,11 +119,11 @@ InAppBrowser.updateDimensions({
105
119
  To create a webView with a 20px bottom margin (safe margin area outside the browser):
106
120
 
107
121
  ```js
108
- import { InAppBrowser } from '@capgo/inappbrowser'
122
+ import { InAppBrowser } from '@capgo/inappbrowser';
109
123
 
110
124
  InAppBrowser.openWebView({
111
- url: "YOUR_URL",
112
- enabledSafeBottomMargin: true
125
+ url: 'YOUR_URL',
126
+ enabledSafeBottomMargin: true,
113
127
  });
114
128
  ```
115
129
 
@@ -120,15 +134,16 @@ Web platform is not supported. Use `window.open` instead.
120
134
  To open the webview in true full screen mode (content extends behind the status bar), set `enabledSafeTopMargin` to `false`:
121
135
 
122
136
  ```js
123
- import { InAppBrowser } from '@capgo/inappbrowser'
137
+ import { InAppBrowser } from '@capgo/inappbrowser';
124
138
 
125
139
  InAppBrowser.openWebView({
126
- url: "YOUR_URL",
127
- enabledSafeTopMargin: false // Disables safe area at top, allows full screen
140
+ url: 'YOUR_URL',
141
+ enabledSafeTopMargin: false, // Disables safe area at top, allows full screen
128
142
  });
129
143
  ```
130
144
 
131
145
  This option works independently of the toolbar type:
146
+
132
147
  - **iOS**: The webview extends behind the status bar, providing true edge-to-edge content
133
148
  - **Android**: The top margin is disabled, allowing content to fill the entire screen
134
149
 
@@ -214,16 +229,16 @@ With this plugin you can send events from the main app to the inappbrowser and v
214
229
  #### Main app to inappbrowser, detail object is mendatory
215
230
 
216
231
  ```js
217
- const { id } = await InAppBrowser.openWebView({ url: "YOUR_URL" });
218
- InAppBrowser.postMessage({ id, detail: { message: "myMessage" } });
232
+ const { id } = await InAppBrowser.openWebView({ url: 'YOUR_URL' });
233
+ InAppBrowser.postMessage({ id, detail: { message: 'myMessage' } });
219
234
  // Or broadcast to all open webviews
220
- InAppBrowser.postMessage({ detail: { message: "broadcast" } });
235
+ InAppBrowser.postMessage({ detail: { message: 'broadcast' } });
221
236
  ```
222
237
 
223
238
  #### Receive event from native in the inappbrowser
224
239
 
225
240
  ```js
226
- window.addEventListener("messageFromNative", (event) => {
241
+ window.addEventListener('messageFromNative', (event) => {
227
242
  console.log(event);
228
243
  });
229
244
  ```
@@ -231,13 +246,13 @@ window.addEventListener("messageFromNative", (event) => {
231
246
  #### Send event from inappbrowser to main app, detail object is mendatory
232
247
 
233
248
  ```js
234
- window.mobileApp.postMessage({ detail: { message: "myMessage" } });
249
+ window.mobileApp.postMessage({ detail: { message: 'myMessage' } });
235
250
  ```
236
251
 
237
252
  #### Receive event from inappbrowser in the main app
238
253
 
239
254
  ```js
240
- InAppBrowser.addListener("messageFromWebview", (event) => {
255
+ InAppBrowser.addListener('messageFromWebview', (event) => {
241
256
  console.log(event.id, event.detail);
242
257
  });
243
258
  ```
@@ -272,6 +287,8 @@ window.mobileApp.close();
272
287
  * [`addListener('messageFromWebview', ...)`](#addlistenermessagefromwebview-)
273
288
  * [`addListener('browserPageLoaded', ...)`](#addlistenerbrowserpageloaded-)
274
289
  * [`addListener('pageLoadError', ...)`](#addlistenerpageloaderror-)
290
+ * [`addListener('proxyRequest', ...)`](#addlistenerproxyrequest-)
291
+ * [`handleProxyRequest(...)`](#handleproxyrequest)
275
292
  * [`removeAllListeners()`](#removealllisteners)
276
293
  * [`reload(...)`](#reload)
277
294
  * [`updateDimensions(...)`](#updatedimensions)
@@ -656,6 +673,45 @@ Will be triggered when page load error
656
673
  --------------------
657
674
 
658
675
 
676
+ ### addListener('proxyRequest', ...)
677
+
678
+ ```typescript
679
+ addListener(eventName: 'proxyRequest', listenerFunc: (event: ProxyRequest) => void) => Promise<PluginListenerHandle>
680
+ ```
681
+
682
+ Listen for proxied requests from the in-app browser webview.
683
+ Use addProxyHandler() wrapper instead of calling this directly.
684
+
685
+ | Param | Type |
686
+ | ------------------ | ------------------------------------------------------------------------- |
687
+ | **`eventName`** | <code>'proxyRequest'</code> |
688
+ | **`listenerFunc`** | <code>(event: <a href="#proxyrequest">ProxyRequest</a>) =&gt; void</code> |
689
+
690
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
691
+
692
+ **Since:** 9.0.0
693
+
694
+ --------------------
695
+
696
+
697
+ ### handleProxyRequest(...)
698
+
699
+ ```typescript
700
+ handleProxyRequest(options: { requestId: string; response: ProxyResponse | null; webviewId?: string; }) => Promise<void>
701
+ ```
702
+
703
+ Internal method: sends a proxied response back to native.
704
+ Called by addProxyHandler() wrapper — not intended for direct use.
705
+
706
+ | Param | Type |
707
+ | ------------- | --------------------------------------------------------------------------------------------------------------------- |
708
+ | **`options`** | <code>{ requestId: string; response: <a href="#proxyresponse">ProxyResponse</a> \| null; webviewId?: string; }</code> |
709
+
710
+ **Since:** 9.0.0
711
+
712
+ --------------------
713
+
714
+
659
715
  ### removeAllListeners()
660
716
 
661
717
  ```typescript
@@ -848,7 +904,7 @@ And in the AndroidManifest.xml file:
848
904
  | **`ignoreUntrustedSSLError`** | <code>boolean</code> | ignoreUntrustedSSLError: if true, the webview will ignore untrusted SSL errors allowing the user to view the website. | <code>false</code> | 6.1.0 |
849
905
  | **`preShowScript`** | <code>string</code> | preShowScript: if isPresentAfterPageLoad is true and this variable is set the plugin will inject a script before showing the browser. This script will be run in an async context. The plugin will wait for the script to finish (max 10 seconds) | | 6.6.0 |
850
906
  | **`preShowScriptInjectionTime`** | <code>'documentStart' \| 'pageLoad'</code> | preShowScriptInjectionTime: controls when the preShowScript is injected. - "documentStart": injects before any page JavaScript runs (good for polyfills like Firebase) - "pageLoad": injects after page load (default, original behavior) | <code>"pageLoad"</code> | 7.26.0 |
851
- | **`proxyRequests`** | <code>string</code> | proxyRequests is a regex expression. Please see [this pr](https://github.com/Cap-go/capacitor-inappbrowser/pull/222) for more info. (Android only) | | 6.9.0 |
907
+ | **`proxyRequests`** | <code>boolean</code> | When true, all HTTP/HTTPS requests from the webview are sent to the proxy handler registered via addProxyHandler(). The handler can return a custom Response or null for pass-through. | | 9.0.0 |
852
908
  | **`buttonNearDone`** | <code>{ ios: { iconType: 'sf-symbol' \| 'asset'; icon: string; }; android: { iconType: 'asset' \| 'vector'; icon: string; width?: number; height?: number; }; }</code> | buttonNearDone allows for a creation of a custom button near the done/close button. The button is only shown when toolbarType is not "activity", "navigation", or "blank". For Android: - iconType must be "asset" - icon path should be in the public folder (e.g. "monkey.svg") - width and height are optional, defaults to 48dp - button is positioned at the end of toolbar with 8dp margin For iOS: - iconType can be "sf-symbol" or "asset" - for sf-symbol, icon should be the symbol name - for asset, icon should be the asset name | | 6.7.0 |
853
909
  | **`textZoom`** | <code>number</code> | textZoom: sets the text zoom of the page in percent. Allows users to increase or decrease the text size for better readability. | <code>100</code> | 7.6.0 |
854
910
  | **`preventDeeplink`** | <code>boolean</code> | preventDeeplink: if true, the deeplink will not be opened, if false the deeplink will be opened when clicked on the link. on IOS each schema need to be added to info.plist file under LSApplicationQueriesSchemes when false to make it work. | <code>false</code> | 0.1.0 |
@@ -911,6 +967,31 @@ And in the AndroidManifest.xml file:
911
967
  | **`url`** | <code>string</code> | Emit when a button is clicked. | 0.0.1 |
912
968
 
913
969
 
970
+ #### ProxyRequest
971
+
972
+ Represents an intercepted HTTP request from the in-app browser webview.
973
+
974
+ | Prop | Type | Description |
975
+ | --------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
976
+ | **`requestId`** | <code>string</code> | Unique identifier for this request, used to match responses |
977
+ | **`url`** | <code>string</code> | The full URL being requested |
978
+ | **`method`** | <code>string</code> | HTTP method (GET, POST, PUT, DELETE, etc.) |
979
+ | **`headers`** | <code><a href="#record">Record</a>&lt;string, string&gt;</code> | Request headers as key-value pairs |
980
+ | **`body`** | <code>string \| null</code> | Base64-encoded request body, or null if no body. On Android, requests from HTML elements (img, script, link, iframe) are intercepted natively and do not have access to the request body — this field will be empty for those requests. Requests from fetch() and XMLHttpRequest include the full body. |
981
+ | **`webviewId`** | <code>string</code> | ID of the webview that made this request |
982
+
983
+
984
+ #### ProxyResponse
985
+
986
+ Response to send back for a proxied request.
987
+
988
+ | Prop | Type | Description |
989
+ | ------------- | --------------------------------------------------------------- | ----------------------------------- |
990
+ | **`body`** | <code>string</code> | Base64-encoded response body |
991
+ | **`status`** | <code>number</code> | HTTP status code |
992
+ | **`headers`** | <code><a href="#record">Record</a>&lt;string, string&gt;</code> | Response headers as key-value pairs |
993
+
994
+
914
995
  #### DimensionOptions
915
996
 
916
997
  | Prop | Type | Description |
@@ -1024,5 +1105,6 @@ Construct a type with a set of properties K of type T
1024
1105
  </docgen-api>
1025
1106
 
1026
1107
  **Credits**
1027
- - [WKWebViewController](https://github.com/Meniny/WKWebViewController) - for iOS
1028
- - [CapBrowser](https://github.com/gadapa-rakesh/CapBrowser) - For the base in capacitor v2
1108
+
1109
+ - [WKWebViewController](https://github.com/Meniny/WKWebViewController) - for iOS
1110
+ - [CapBrowser](https://github.com/gadapa-rakesh/CapBrowser) - For the base in capacitor v2
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ (() => {
3
+ var __async = (__this, __arguments, generator) => {
4
+ return new Promise((resolve, reject) => {
5
+ var fulfilled = (value) => {
6
+ try {
7
+ step(generator.next(value));
8
+ } catch (e) {
9
+ reject(e);
10
+ }
11
+ };
12
+ var rejected = (value) => {
13
+ try {
14
+ step(generator.throw(value));
15
+ } catch (e) {
16
+ reject(e);
17
+ }
18
+ };
19
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
20
+ step((generator = generator.apply(__this, __arguments)).next());
21
+ });
22
+ };
23
+
24
+ // src/proxy-bridge.ts
25
+ (function() {
26
+ const proxyBridge = window.__capgoProxy;
27
+ if (!proxyBridge) return;
28
+ const accessToken = "___CAPGO_PROXY_TOKEN___";
29
+ let requestCounter = 0;
30
+ function generateRequestId() {
31
+ return "pr_" + Date.now() + "_" + requestCounter++;
32
+ }
33
+ function arrayBufferToBase64(buffer) {
34
+ const bytes = new Uint8Array(buffer);
35
+ let binary = "";
36
+ for (let i = 0; i < bytes.byteLength; i++) {
37
+ binary += String.fromCharCode(bytes[i]);
38
+ }
39
+ return btoa(binary);
40
+ }
41
+ function stringToBase64(str) {
42
+ return btoa(unescape(encodeURIComponent(str)));
43
+ }
44
+ function resolveUrl(url) {
45
+ if (url && !url.match(/^[a-zA-Z][a-zA-Z0-9+\-.]*:\/\//)) {
46
+ try {
47
+ return new URL(url, window.location.href).href;
48
+ } catch (_e) {
49
+ return url;
50
+ }
51
+ }
52
+ return url;
53
+ }
54
+ function bodyToBase64(body) {
55
+ return __async(this, null, function* () {
56
+ if (body === null || body === void 0) return null;
57
+ if (typeof body === "string") return stringToBase64(body);
58
+ if (body instanceof ArrayBuffer) return arrayBufferToBase64(body);
59
+ if (ArrayBuffer.isView(body))
60
+ return arrayBufferToBase64(body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength));
61
+ if (body instanceof Blob) {
62
+ const ab = yield body.arrayBuffer();
63
+ return arrayBufferToBase64(ab);
64
+ }
65
+ if (body instanceof FormData) {
66
+ const ab = yield new Response(body).arrayBuffer();
67
+ return arrayBufferToBase64(ab);
68
+ }
69
+ if (body instanceof URLSearchParams) {
70
+ return stringToBase64(body.toString());
71
+ }
72
+ return null;
73
+ });
74
+ }
75
+ const originalFetch = window.fetch;
76
+ window.fetch = function(input, init) {
77
+ return __async(this, null, function* () {
78
+ const requestId = generateRequestId();
79
+ let url;
80
+ let method = "GET";
81
+ const headers = {};
82
+ let body = null;
83
+ if (input instanceof Request) {
84
+ url = input.url;
85
+ method = input.method;
86
+ input.headers.forEach((v, k) => {
87
+ headers[k] = v;
88
+ });
89
+ try {
90
+ const cloned = input.clone();
91
+ const ab = yield cloned.arrayBuffer();
92
+ if (ab.byteLength > 0) {
93
+ body = ab;
94
+ }
95
+ } catch (_e) {
96
+ }
97
+ } else {
98
+ url = input instanceof URL ? input.toString() : input;
99
+ }
100
+ url = resolveUrl(url);
101
+ if (init) {
102
+ if (init.method) method = init.method;
103
+ if (init.headers) {
104
+ const h = new Headers(init.headers);
105
+ h.forEach((v, k) => {
106
+ headers[k] = v;
107
+ });
108
+ }
109
+ if (init.body !== void 0) body = init.body;
110
+ }
111
+ if (body instanceof FormData) {
112
+ const encoded = new Response(body);
113
+ const ct = encoded.headers.get("content-type");
114
+ if (ct) {
115
+ Object.keys(headers).forEach((k) => {
116
+ if (k.toLowerCase() === "content-type") delete headers[k];
117
+ });
118
+ headers["content-type"] = ct;
119
+ }
120
+ body = yield encoded.arrayBuffer();
121
+ }
122
+ const base64Body = yield bodyToBase64(body);
123
+ proxyBridge.storeRequest(accessToken, requestId, method, JSON.stringify(headers), base64Body || "");
124
+ const proxyUrl = "/_capgo_proxy_?u=" + encodeURIComponent(url) + "&rid=" + requestId;
125
+ return originalFetch.call(window, proxyUrl, { method: "GET" });
126
+ });
127
+ };
128
+ const XHROpen = XMLHttpRequest.prototype.open;
129
+ const XHRSend = XMLHttpRequest.prototype.send;
130
+ const XHRSetHeader = XMLHttpRequest.prototype.setRequestHeader;
131
+ XMLHttpRequest.prototype.open = function(method, url, ...rest) {
132
+ this.__proxyMethod = method;
133
+ this.__proxyUrl = resolveUrl(url instanceof URL ? url.toString() : url);
134
+ this.__proxyHeaders = {};
135
+ return XHROpen.apply(this, [method, url, ...rest]);
136
+ };
137
+ XMLHttpRequest.prototype.setRequestHeader = function(name, value) {
138
+ if (this.__proxyHeaders) {
139
+ this.__proxyHeaders[name] = value;
140
+ }
141
+ return XHRSetHeader.call(this, name, value);
142
+ };
143
+ XMLHttpRequest.prototype.send = function(body) {
144
+ const xhr = this;
145
+ const requestId = generateRequestId();
146
+ const method = xhr.__proxyMethod || "GET";
147
+ const url = xhr.__proxyUrl || "";
148
+ const headers = xhr.__proxyHeaders || {};
149
+ function completeSend(base64Body) {
150
+ proxyBridge.storeRequest(accessToken, requestId, method, JSON.stringify(headers), base64Body);
151
+ const proxyUrl = "/_capgo_proxy_?u=" + encodeURIComponent(url) + "&rid=" + requestId;
152
+ XHROpen.call(xhr, "GET", proxyUrl, true);
153
+ XHRSend.call(xhr, null);
154
+ }
155
+ if (body === null || body === void 0) {
156
+ completeSend("");
157
+ return;
158
+ }
159
+ if (typeof body === "string") {
160
+ completeSend(stringToBase64(body));
161
+ return;
162
+ }
163
+ if (body instanceof ArrayBuffer) {
164
+ completeSend(arrayBufferToBase64(body));
165
+ return;
166
+ }
167
+ if (ArrayBuffer.isView(body)) {
168
+ completeSend(arrayBufferToBase64(body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength)));
169
+ return;
170
+ }
171
+ if (body instanceof URLSearchParams) {
172
+ completeSend(stringToBase64(body.toString()));
173
+ return;
174
+ }
175
+ if (body instanceof Blob || body instanceof FormData) {
176
+ const encoded = new Response(body);
177
+ if (body instanceof FormData) {
178
+ const ct = encoded.headers.get("content-type");
179
+ if (ct) {
180
+ Object.keys(headers).forEach((k) => {
181
+ if (k.toLowerCase() === "content-type") delete headers[k];
182
+ });
183
+ headers["content-type"] = ct;
184
+ }
185
+ }
186
+ encoded.arrayBuffer().then((ab) => {
187
+ completeSend(arrayBufferToBase64(ab));
188
+ }).catch((_e) => {
189
+ console.error("[proxy-bridge] Failed to encode Blob/FormData body:", _e);
190
+ completeSend("");
191
+ });
192
+ return;
193
+ }
194
+ completeSend("");
195
+ };
196
+ })();
197
+ })();
@@ -46,8 +46,6 @@ import java.util.Iterator;
46
46
  import java.util.List;
47
47
  import java.util.Map;
48
48
  import java.util.UUID;
49
- import java.util.regex.Pattern;
50
- import java.util.regex.PatternSyntaxException;
51
49
  import org.json.JSONException;
52
50
  import org.json.JSONObject;
53
51
 
@@ -62,7 +60,7 @@ import org.json.JSONObject;
62
60
  )
63
61
  public class InAppBrowserPlugin extends Plugin implements WebViewDialog.PermissionHandler {
64
62
 
65
- private final String pluginVersion = "8.2.0";
63
+ private final String pluginVersion = "8.3.0-alpha.0";
66
64
 
67
65
  public static final String CUSTOM_TAB_PACKAGE_NAME = "com.android.chrome"; // Change when in stable
68
66
  private CustomTabsClient customTabsClient;
@@ -577,14 +575,7 @@ public class InAppBrowserPlugin extends Plugin implements WebViewDialog.Permissi
577
575
  options.setTextZoom(textZoom);
578
576
  }
579
577
 
580
- String proxyRequestsStr = call.getString("proxyRequests");
581
- if (proxyRequestsStr != null) {
582
- try {
583
- options.setProxyRequestsPattern(Pattern.compile(proxyRequestsStr));
584
- } catch (PatternSyntaxException e) {
585
- Log.e("WebViewDialog", String.format("Pattern '%s' is not a valid pattern", proxyRequestsStr));
586
- }
587
- }
578
+ options.setProxyRequests(Boolean.TRUE.equals(call.getBoolean("proxyRequests", false)));
588
579
 
589
580
  try {
590
581
  // Try to set buttonNearDone if present, with better error handling
@@ -733,6 +724,22 @@ public class InAppBrowserPlugin extends Plugin implements WebViewDialog.Permissi
733
724
  notifyListeners("confirmBtnClicked", new JSObject().put("id", webViewId).put("url", url));
734
725
  }
735
726
 
727
+ @Override
728
+ public void proxyRequestEvent(String requestId, String url, String method, String headersJson, String body, String wvId) {
729
+ JSObject data = new JSObject();
730
+ data.put("requestId", requestId);
731
+ data.put("url", url);
732
+ data.put("method", method);
733
+ try {
734
+ data.put("headers", new JSObject(headersJson));
735
+ } catch (Exception e) {
736
+ data.put("headers", new JSObject());
737
+ }
738
+ data.put("body", body);
739
+ data.put("webviewId", wvId);
740
+ notifyListeners("proxyRequest", data);
741
+ }
742
+
736
743
  @Override
737
744
  public void javascriptCallback(String message) {
738
745
  // Handle the message received from JavaScript
@@ -1070,24 +1077,20 @@ public class InAppBrowserPlugin extends Plugin implements WebViewDialog.Permissi
1070
1077
  }
1071
1078
 
1072
1079
  @PluginMethod
1073
- public void lsuakdchgbbaHandleProxiedRequest(PluginCall call) {
1080
+ public void handleProxyRequest(PluginCall call) {
1081
+ String requestId = call.getString("requestId");
1082
+ if (requestId == null) {
1083
+ call.reject("requestId is required");
1084
+ return;
1085
+ }
1074
1086
  String webviewId = call.getString("webviewId");
1075
1087
  WebViewDialog webViewDialog = webviewId != null ? webViewDialogs.get(webviewId) : resolveDialog(null);
1076
- if (webViewDialog != null) {
1077
- Boolean ok = call.getBoolean("ok", false);
1078
- String id = call.getString("id");
1079
- if (id == null) {
1080
- Log.e("InAppBrowserProxy", "CRITICAL ERROR, proxy id = null");
1081
- return;
1082
- }
1083
- if (Boolean.FALSE.equals(ok)) {
1084
- String result = call.getString("result", "");
1085
- webViewDialog.handleProxyResultError(result, id);
1086
- } else {
1087
- JSONObject object = call.getObject("result");
1088
- webViewDialog.handleProxyResultOk(object, id);
1089
- }
1088
+ if (webViewDialog == null) {
1089
+ call.reject("Target WebView not found for proxy request");
1090
+ return;
1090
1091
  }
1092
+ JSObject response = call.getObject("response");
1093
+ webViewDialog.handleProxyResponse(requestId, response);
1091
1094
  call.resolve();
1092
1095
  }
1093
1096
 
@@ -9,8 +9,6 @@ import java.io.InputStream;
9
9
  import java.util.ArrayList;
10
10
  import java.util.List;
11
11
  import java.util.Objects;
12
- import java.util.Objects;
13
- import java.util.regex.Pattern;
14
12
 
15
13
  public class Options {
16
14
 
@@ -170,7 +168,7 @@ public class Options {
170
168
  private boolean ignoreUntrustedSSLError;
171
169
  private String preShowScript;
172
170
  private String toolbarTextColor;
173
- private Pattern proxyRequestsPattern = null;
171
+ private boolean proxyRequests = false;
174
172
  private boolean materialPicker = false;
175
173
  private int textZoom = 100; // Default text zoom is 100%
176
174
  private boolean preventDeeplink = false;
@@ -262,12 +260,12 @@ public class Options {
262
260
  this.useTopInset = useTopInset;
263
261
  }
264
262
 
265
- public Pattern getProxyRequestsPattern() {
266
- return proxyRequestsPattern;
263
+ public boolean getProxyRequests() {
264
+ return proxyRequests;
267
265
  }
268
266
 
269
- public void setProxyRequestsPattern(Pattern proxyRequestsPattern) {
270
- this.proxyRequestsPattern = proxyRequestsPattern;
267
+ public void setProxyRequests(boolean proxyRequests) {
268
+ this.proxyRequests = proxyRequests;
271
269
  }
272
270
 
273
271
  public PluginCall getPluginCall() {
@@ -0,0 +1,60 @@
1
+ package ee.forgr.capacitor_inappbrowser;
2
+
3
+ import android.webkit.JavascriptInterface;
4
+ import java.util.concurrent.ConcurrentHashMap;
5
+
6
+ public class ProxyBridge {
7
+
8
+ private static final int MAX_STORED_REQUESTS = 256;
9
+
10
+ public static class StoredRequest {
11
+
12
+ public final String method;
13
+ public final String headersJson;
14
+ public final String base64Body;
15
+
16
+ public StoredRequest(String method, String headersJson, String base64Body) {
17
+ this.method = method != null ? method : "";
18
+ this.headersJson = headersJson != null ? headersJson : "{}";
19
+ this.base64Body = base64Body != null ? base64Body : "";
20
+ }
21
+ }
22
+
23
+ private final ConcurrentHashMap<String, StoredRequest> storedRequests = new ConcurrentHashMap<>();
24
+ private final String accessToken;
25
+
26
+ /**
27
+ * @param accessToken Random token generated per webview instance.
28
+ * The injected proxy-bridge script receives this token and must
29
+ * pass it on every storeRequest call. Page JS that doesn't know
30
+ * the token cannot abuse the interface.
31
+ */
32
+ public ProxyBridge(String accessToken) {
33
+ this.accessToken = accessToken;
34
+ }
35
+
36
+ @JavascriptInterface
37
+ public void storeRequest(String token, String requestId, String method, String headersJson, String base64Body) {
38
+ if (token == null || !token.equals(accessToken)) {
39
+ return;
40
+ }
41
+ if (requestId == null || requestId.isEmpty()) {
42
+ return;
43
+ }
44
+ if (storedRequests.size() >= MAX_STORED_REQUESTS) {
45
+ // Remove an arbitrary entry to prevent unbounded growth
46
+ var it = storedRequests.keys().asIterator();
47
+ if (it.hasNext()) {
48
+ storedRequests.remove(it.next());
49
+ }
50
+ }
51
+ storedRequests.put(requestId, new StoredRequest(method, headersJson, base64Body));
52
+ }
53
+
54
+ public StoredRequest getAndRemove(String requestId) {
55
+ if (requestId == null) {
56
+ return null;
57
+ }
58
+ return storedRequests.remove(requestId);
59
+ }
60
+ }
@@ -14,4 +14,6 @@ public interface WebViewCallbacks {
14
14
  public void buttonNearDoneClicked();
15
15
 
16
16
  public void confirmBtnClicked(String url);
17
+
18
+ public void proxyRequestEvent(String requestId, String url, String method, String headersJson, String body, String webviewId);
17
19
  }