@capgo/inappbrowser 7.10.10 → 7.11.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.
@@ -24,6 +24,8 @@ import android.print.PrintAttributes;
24
24
  import android.print.PrintDocumentAdapter;
25
25
  import android.print.PrintManager;
26
26
  import android.provider.MediaStore;
27
+ import android.security.KeyChain;
28
+ import android.security.KeyChainAliasCallback;
27
29
  import android.text.TextUtils;
28
30
  import android.util.Base64;
29
31
  import android.util.Log;
@@ -65,6 +67,8 @@ import java.io.InputStream;
65
67
  import java.net.URI;
66
68
  import java.net.URISyntaxException;
67
69
  import java.nio.charset.StandardCharsets;
70
+ import java.security.PrivateKey;
71
+ import java.security.cert.X509Certificate;
68
72
  import java.util.Arrays;
69
73
  import java.util.HashMap;
70
74
  import java.util.Iterator;
@@ -186,19 +190,7 @@ public class WebViewDialog extends Dialog {
186
190
 
187
191
  activity.runOnUiThread(() -> {
188
192
  try {
189
- String currentUrl = "";
190
- if (_webView != null) {
191
- try {
192
- currentUrl = _webView.getUrl();
193
- if (currentUrl == null) {
194
- currentUrl = "";
195
- }
196
- } catch (Exception e) {
197
- Log.e("InAppBrowser", "Error getting URL: " + e.getMessage());
198
- currentUrl = "";
199
- }
200
- }
201
-
193
+ String currentUrl = getUrl();
202
194
  dismiss();
203
195
 
204
196
  if (_options != null && _options.getCallbacks() != null) {
@@ -444,11 +436,7 @@ public class WebViewDialog extends Dialog {
444
436
  // DEBUG: Log details about the file chooser request
445
437
  Log.d("InAppBrowser", "onShowFileChooser called");
446
438
  Log.d("InAppBrowser", "Accept type: " + acceptType);
447
- Log.d(
448
- "InAppBrowser",
449
- "Current URL: " +
450
- (webView.getUrl() != null ? webView.getUrl() : "null")
451
- );
439
+ Log.d("InAppBrowser", "Current URL: " + getUrl());
452
440
  Log.d(
453
441
  "InAppBrowser",
454
442
  "Original URL: " +
@@ -477,7 +465,7 @@ public class WebViewDialog extends Dialog {
477
465
  // Direct check for capture attribute in URL (fallback method)
478
466
  boolean isCaptureInUrl;
479
467
  String captureMode;
480
- String currentUrl = webView.getUrl();
468
+ String currentUrl = getUrl();
481
469
 
482
470
  // Look for capture in URL parameters - sometimes the attribute shows up in URL
483
471
  if (currentUrl != null && currentUrl.contains("capture=")) {
@@ -512,43 +500,48 @@ public class WebViewDialog extends Dialog {
512
500
 
513
501
  // Fixed JavaScript with proper error handling
514
502
  String js =
515
- "try {" +
516
- " (function() {" +
517
- " var captureAttr = null;" +
518
- " // Check active element first" +
519
- " if (document.activeElement && " +
520
- " document.activeElement.tagName === 'INPUT' && " +
521
- " document.activeElement.type === 'file') {" +
522
- " if (document.activeElement.hasAttribute('capture')) {" +
523
- " captureAttr = document.activeElement.getAttribute('capture') || 'environment';" +
524
- " return captureAttr;" +
525
- " }" +
526
- " }" +
527
- " // Try to find any input with capture attribute" +
528
- " var inputs = document.querySelectorAll('input[type=\"file\"][capture]');" +
529
- " if (inputs && inputs.length > 0) {" +
530
- " captureAttr = inputs[0].getAttribute('capture') || 'environment';" +
531
- " return captureAttr;" +
532
- " }" +
533
- " // Try to extract from HTML attributes" +
534
- " var allInputs = document.getElementsByTagName('input');" +
535
- " for (var i = 0; i < allInputs.length; i++) {" +
536
- " var input = allInputs[i];" +
537
- " if (input.type === 'file') {" +
538
- " if (input.hasAttribute('capture')) {" +
539
- " captureAttr = input.getAttribute('capture') || 'environment';" +
540
- " return captureAttr;" +
541
- " }" +
542
- " // Look for the accept attribute containing image/* as this might be a camera input" +
543
- " var acceptAttr = input.getAttribute('accept');" +
544
- " if (acceptAttr && acceptAttr.indexOf('image/*') >= 0) {" +
545
- " console.log('Found input with image/* accept');" +
546
- " }" +
547
- " }" +
548
- " }" +
549
- " return '';" +
550
- " })();" +
551
- "} catch(e) { console.error('Capture detection error:', e); return ''; }";
503
+ """
504
+ try {
505
+ (function() {
506
+ var captureAttr = null;
507
+ // Check active element first
508
+ if (document.activeElement &&
509
+ document.activeElement.tagName === 'INPUT' &&
510
+ document.activeElement.type === 'file') {
511
+ if (document.activeElement.hasAttribute('capture')) {
512
+ captureAttr = document.activeElement.getAttribute('capture') || 'environment';
513
+ return captureAttr;
514
+ }
515
+ }
516
+ // Try to find any input with capture attribute
517
+ var inputs = document.querySelectorAll('input[type="file"][capture]');
518
+ if (inputs && inputs.length > 0) {
519
+ captureAttr = inputs[0].getAttribute('capture') || 'environment';
520
+ return captureAttr;
521
+ }
522
+ // Try to extract from HTML attributes
523
+ var allInputs = document.getElementsByTagName('input');
524
+ for (var i = 0; i < allInputs.length; i++) {
525
+ var input = allInputs[i];
526
+ if (input.type === 'file') {
527
+ if (input.hasAttribute('capture')) {
528
+ captureAttr = input.getAttribute('capture') || 'environment';
529
+ return captureAttr;
530
+ }
531
+ // Look for the accept attribute containing image/* as this might be a camera input
532
+ var acceptAttr = input.getAttribute('accept');
533
+ if (acceptAttr && acceptAttr.indexOf('image/*') >= 0) {
534
+ console.log('Found input with image/* accept');
535
+ }
536
+ }
537
+ }
538
+ return '';
539
+ })();
540
+ } catch(e) {
541
+ console.error('Capture detection error:', e);
542
+ return '';
543
+ }
544
+ """;
552
545
 
553
546
  webView.evaluateJavascript(js, value -> {
554
547
  Log.d("InAppBrowser", "Capture attribute JS result: " + value);
@@ -1462,7 +1455,7 @@ public class WebViewDialog extends Dialog {
1462
1455
  _webView.stopLoading();
1463
1456
 
1464
1457
  // Check if there's a URL to reload
1465
- String currentUrl = _webView.getUrl();
1458
+ String currentUrl = getUrl();
1466
1459
  if (currentUrl != null && !currentUrl.equals("about:blank")) {
1467
1460
  // Reload the current page
1468
1461
  _webView.reload();
@@ -1486,7 +1479,16 @@ public class WebViewDialog extends Dialog {
1486
1479
  }
1487
1480
 
1488
1481
  public String getUrl() {
1489
- return _webView != null ? _webView.getUrl() : "";
1482
+ try {
1483
+ WebView webView = _webView;
1484
+ if (webView != null) {
1485
+ String url = webView.getUrl();
1486
+ return url != null ? url : "";
1487
+ }
1488
+ } catch (Exception e) {
1489
+ Log.w("InAppBrowser", "Error getting URL: " + e.getMessage());
1490
+ }
1491
+ return "";
1490
1492
  }
1491
1493
 
1492
1494
  public void executeScript(String script) {
@@ -1628,9 +1630,7 @@ public class WebViewDialog extends Dialog {
1628
1630
  new OnClickListener() {
1629
1631
  public void onClick(DialogInterface dialog, int which) {
1630
1632
  // Close button clicked, do something
1631
- String currentUrl = _webView != null
1632
- ? _webView.getUrl()
1633
- : "";
1633
+ String currentUrl = getUrl();
1634
1634
  dismiss();
1635
1635
  if (_options != null && _options.getCallbacks() != null) {
1636
1636
  _options.getCallbacks().closeEvent(currentUrl);
@@ -1641,7 +1641,7 @@ public class WebViewDialog extends Dialog {
1641
1641
  .setNegativeButton(_options.getCloseModalCancel(), null)
1642
1642
  .show();
1643
1643
  } else {
1644
- String currentUrl = _webView != null ? _webView.getUrl() : "";
1644
+ String currentUrl = getUrl();
1645
1645
  dismiss();
1646
1646
  if (_options != null && _options.getCallbacks() != null) {
1647
1647
  _options.getCallbacks().closeEvent(currentUrl);
@@ -1671,7 +1671,7 @@ public class WebViewDialog extends Dialog {
1671
1671
  _webView.stopLoading();
1672
1672
 
1673
1673
  // Check if there's a URL to reload
1674
- String currentUrl = _webView.getUrl();
1674
+ String currentUrl = getUrl();
1675
1675
  if (currentUrl != null) {
1676
1676
  // Reload the current page
1677
1677
  _webView.reload();
@@ -2153,6 +2153,109 @@ public class WebViewDialog extends Dialog {
2153
2153
  return false;
2154
2154
  }
2155
2155
 
2156
+ @Override
2157
+ public void onReceivedClientCertRequest(
2158
+ WebView view,
2159
+ android.webkit.ClientCertRequest request
2160
+ ) {
2161
+ Log.i("InAppBrowser", "onReceivedClientCertRequest CALLED");
2162
+
2163
+ if (request == null) {
2164
+ Log.e("InAppBrowser", "ClientCertRequest is null");
2165
+ return;
2166
+ }
2167
+
2168
+ if (activity == null) {
2169
+ Log.e("InAppBrowser", "Activity is null, canceling request");
2170
+ try {
2171
+ request.cancel();
2172
+ } catch (Exception e) {
2173
+ Log.e(
2174
+ "InAppBrowser",
2175
+ "Error canceling request: " + e.getMessage()
2176
+ );
2177
+ }
2178
+ return;
2179
+ }
2180
+
2181
+ try {
2182
+ Log.i("InAppBrowser", "Host: " + request.getHost());
2183
+ Log.i("InAppBrowser", "Port: " + request.getPort());
2184
+ Log.i(
2185
+ "InAppBrowser",
2186
+ "Principals: " +
2187
+ java.util.Arrays.toString(request.getPrincipals())
2188
+ );
2189
+ Log.i(
2190
+ "InAppBrowser",
2191
+ "KeyTypes: " + java.util.Arrays.toString(request.getKeyTypes())
2192
+ );
2193
+
2194
+ KeyChain.choosePrivateKeyAlias(
2195
+ activity,
2196
+ new KeyChainAliasCallback() {
2197
+ @Override
2198
+ public void alias(String alias) {
2199
+ if (alias != null) {
2200
+ try {
2201
+ PrivateKey privateKey = KeyChain.getPrivateKey(
2202
+ activity,
2203
+ alias
2204
+ );
2205
+ X509Certificate[] certChain =
2206
+ KeyChain.getCertificateChain(activity, alias);
2207
+ request.proceed(privateKey, certChain);
2208
+ Log.i("InAppBrowser", "Selected certificate: " + alias);
2209
+ } catch (Exception e) {
2210
+ try {
2211
+ request.cancel();
2212
+ } catch (Exception cancelEx) {
2213
+ Log.e(
2214
+ "InAppBrowser",
2215
+ "Error canceling request: " + cancelEx.getMessage()
2216
+ );
2217
+ }
2218
+ Log.e(
2219
+ "InAppBrowser",
2220
+ "Error selecting certificate: " + e.getMessage()
2221
+ );
2222
+ }
2223
+ } else {
2224
+ try {
2225
+ request.cancel();
2226
+ } catch (Exception e) {
2227
+ Log.e(
2228
+ "InAppBrowser",
2229
+ "Error canceling request: " + e.getMessage()
2230
+ );
2231
+ }
2232
+ Log.i("InAppBrowser", "No certificate found");
2233
+ }
2234
+ }
2235
+ },
2236
+ null, // keyTypes
2237
+ null, // issuers
2238
+ request.getHost(),
2239
+ request.getPort(),
2240
+ null // alias (null = system asks user to choose)
2241
+ );
2242
+ } catch (Exception e) {
2243
+ Log.e(
2244
+ "InAppBrowser",
2245
+ "Error in onReceivedClientCertRequest: " + e.getMessage()
2246
+ );
2247
+ try {
2248
+ request.cancel();
2249
+ } catch (Exception cancelEx) {
2250
+ Log.e(
2251
+ "InAppBrowser",
2252
+ "Error canceling request after exception: " +
2253
+ cancelEx.getMessage()
2254
+ );
2255
+ }
2256
+ }
2257
+ }
2258
+
2156
2259
  private String randomRequestId() {
2157
2260
  return UUID.randomUUID().toString();
2158
2261
  }
@@ -2567,7 +2670,7 @@ public class WebViewDialog extends Dialog {
2567
2670
  ) {
2568
2671
  _webView.goBack();
2569
2672
  } else if (!_options.getDisableGoBackOnNativeApplication()) {
2570
- String currentUrl = _webView != null ? _webView.getUrl() : "";
2673
+ String currentUrl = getUrl();
2571
2674
  _options.getCallbacks().closeEvent(currentUrl);
2572
2675
  if (_webView != null) {
2573
2676
  _webView.destroy();
@@ -443,11 +443,20 @@ open class WKWebViewController: UIViewController, WKScriptMessageHandler {
443
443
 
444
444
  // Method to send a message from Swift to JavaScript
445
445
  open func postMessageToJS(message: [String: Any]) {
446
- if let jsonData = try? JSONSerialization.data(withJSONObject: message, options: []),
447
- let jsonString = String(data: jsonData, encoding: .utf8) {
448
- let script = "window.dispatchEvent(new CustomEvent('messageFromNative', { detail: \(jsonString) }));"
449
- DispatchQueue.main.async {
450
- self.webView?.evaluateJavaScript(script, completionHandler: nil)
446
+ guard let jsonData = try? JSONSerialization.data(withJSONObject: message, options: []),
447
+ let jsonString = String(data: jsonData, encoding: .utf8) else {
448
+ print("[InAppBrowser] Failed to serialize message to JSON")
449
+ return
450
+ }
451
+
452
+ // Safely build the script to avoid any potential issues
453
+ let script = "window.dispatchEvent(new CustomEvent('messageFromNative', { detail: \(jsonString) }));"
454
+
455
+ DispatchQueue.main.async {
456
+ self.webView?.evaluateJavaScript(script) { result, error in
457
+ if let error = error {
458
+ print("[InAppBrowser] JavaScript evaluation error in postMessageToJS: \(error)")
459
+ }
451
460
  }
452
461
  }
453
462
  }
@@ -1339,20 +1348,25 @@ extension WKWebViewController: WKNavigationDelegate {
1339
1348
  return
1340
1349
  }
1341
1350
 
1342
- // TODO: implement interface
1343
- let script = """
1344
- async function preShowFunction() {
1345
- \(self.preShowScript ?? "")
1346
- };
1347
- preShowFunction().then(
1348
- () => window.webkit.messageHandlers.preShowScriptSuccess.postMessage({})
1349
- ).catch(
1350
- err => {
1351
- console.error('Preshow error', err);
1352
- window.webkit.messageHandlers.preShowScriptError.postMessage(JSON.stringify(err, Object.getOwnPropertyNames(err)));
1353
- }
1354
- )
1355
- """
1351
+ // Safely construct script template with proper escaping
1352
+ let userScript = self.preShowScript ?? ""
1353
+
1354
+ // Build script using safe concatenation to avoid multi-line string issues
1355
+ let scriptTemplate = [
1356
+ "async function preShowFunction() {",
1357
+ userScript,
1358
+ "}",
1359
+ "preShowFunction().then(",
1360
+ " () => window.webkit.messageHandlers.preShowScriptSuccess.postMessage({})",
1361
+ ").catch(",
1362
+ " err => {",
1363
+ " console.error('Preshow error', err);",
1364
+ " window.webkit.messageHandlers.preShowScriptError.postMessage(JSON.stringify(err, Object.getOwnPropertyNames(err)));",
1365
+ " }",
1366
+ ")"
1367
+ ]
1368
+
1369
+ let script = scriptTemplate.joined(separator: "\n")
1356
1370
  print("[InAppBrowser - InjectPreShowScript] PreShowScript script: \(script)")
1357
1371
 
1358
1372
  self.preShowSemaphore = DispatchSemaphore(value: 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/inappbrowser",
3
- "version": "7.10.10",
3
+ "version": "7.11.1",
4
4
  "description": "Capacitor plugin in app browser",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",