@capgo/capacitor-inappbrowser 8.14.3 → 8.15.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 +20 -2
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/BundledAssetSupport.java +397 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/CapgoInAppBrowserPlugin.java +42 -4
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/Options.java +39 -1
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +286 -220
- package/dist/docs.json +14 -2
- package/dist/esm/bundled-asset-support.d.ts +40 -0
- package/dist/esm/bundled-asset-support.js +157 -0
- package/dist/esm/bundled-asset-support.js.map +1 -0
- package/dist/esm/definitions.d.ts +17 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.js +43 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/plugin.cjs.js +199 -1
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +199 -1
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/InAppBrowserPlugin/BundledAssetSchemeHandler.swift +188 -0
- package/ios/Sources/InAppBrowserPlugin/BundledAssetSupport.swift +221 -0
- package/ios/Sources/InAppBrowserPlugin/InAppBrowserPlugin.swift +26 -4
- package/ios/Sources/InAppBrowserPlugin/WKWebViewController.swift +21 -0
- package/package.json +2 -2
|
@@ -81,6 +81,7 @@ import androidx.core.view.WindowInsetsControllerCompat;
|
|
|
81
81
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
|
82
82
|
import androidx.webkit.WebMessageCompat;
|
|
83
83
|
import androidx.webkit.WebSettingsCompat;
|
|
84
|
+
import androidx.webkit.WebViewAssetLoader;
|
|
84
85
|
import androidx.webkit.WebViewCompat;
|
|
85
86
|
import androidx.webkit.WebViewFeature;
|
|
86
87
|
import com.caverock.androidsvg.SVG;
|
|
@@ -344,6 +345,8 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
344
345
|
private String instanceId = "";
|
|
345
346
|
private final Map<String, ProxiedRequest> proxiedRequestsHashmap = new ConcurrentHashMap<>();
|
|
346
347
|
private ProxyBridge proxyBridge;
|
|
348
|
+
private volatile WebViewAssetLoader bundledAssetLoader;
|
|
349
|
+
private final Object bundledAssetLoaderLock = new Object();
|
|
347
350
|
private String proxyBridgeScript;
|
|
348
351
|
private String proxyAccessToken;
|
|
349
352
|
private final ExecutorService executorService = Executors.newCachedThreadPool();
|
|
@@ -2232,6 +2235,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
2232
2235
|
_webView.addJavascriptInterface(proxyBridge, "__capgoProxy");
|
|
2233
2236
|
proxyBridgeScript = loadProxyBridgeScript();
|
|
2234
2237
|
}
|
|
2238
|
+
ensureBundledAssetLoader();
|
|
2235
2239
|
_webView.getSettings().setJavaScriptEnabled(true);
|
|
2236
2240
|
_webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
|
|
2237
2241
|
_webView.getSettings().setDatabaseEnabled(true);
|
|
@@ -4364,6 +4368,60 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
4364
4368
|
return proxyHeaders;
|
|
4365
4369
|
}
|
|
4366
4370
|
|
|
4371
|
+
private void ensureBundledAssetLoader() {
|
|
4372
|
+
synchronized (bundledAssetLoaderLock) {
|
|
4373
|
+
ensureBundledAssetLoaderLocked();
|
|
4374
|
+
}
|
|
4375
|
+
}
|
|
4376
|
+
|
|
4377
|
+
private void ensureBundledAssetLoaderLocked() {
|
|
4378
|
+
if (bundledAssetLoader != null || _options == null || !_options.getServeBundledAssets()) {
|
|
4379
|
+
return;
|
|
4380
|
+
}
|
|
4381
|
+
|
|
4382
|
+
bundledAssetLoader = BundledAssetSupport.createAssetLoader(
|
|
4383
|
+
_context,
|
|
4384
|
+
_options.getBundledAssetHost(),
|
|
4385
|
+
_options.getBundledAssetScheme()
|
|
4386
|
+
);
|
|
4387
|
+
}
|
|
4388
|
+
|
|
4389
|
+
public void applyBundledAssetResolution(BundledAssetSupport.Resolution resolution, String localUrl) {
|
|
4390
|
+
if (_options == null || resolution == null) {
|
|
4391
|
+
return;
|
|
4392
|
+
}
|
|
4393
|
+
|
|
4394
|
+
synchronized (bundledAssetLoaderLock) {
|
|
4395
|
+
bundledAssetLoader = null;
|
|
4396
|
+
_options.setUrl(resolution.url);
|
|
4397
|
+
if (resolution.needsAssetLoader) {
|
|
4398
|
+
BundledAssetSupport.LocalConfig localConfig = BundledAssetSupport.parseLocalConfig(localUrl);
|
|
4399
|
+
_options.setBundledAssetHost(localConfig != null ? localConfig.host : "localhost");
|
|
4400
|
+
_options.setBundledAssetScheme(localConfig != null ? BundledAssetSupport.assetLoaderScheme(localConfig) : "https");
|
|
4401
|
+
_options.setServeBundledAssets(true);
|
|
4402
|
+
} else {
|
|
4403
|
+
_options.setServeBundledAssets(false);
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
|
|
4408
|
+
private WebResourceResponse interceptBundledAssetRequest(WebResourceRequest request) {
|
|
4409
|
+
final WebViewAssetLoader loader;
|
|
4410
|
+
synchronized (bundledAssetLoaderLock) {
|
|
4411
|
+
if (_options == null || !_options.getServeBundledAssets()) {
|
|
4412
|
+
return null;
|
|
4413
|
+
}
|
|
4414
|
+
|
|
4415
|
+
ensureBundledAssetLoaderLocked();
|
|
4416
|
+
loader = bundledAssetLoader;
|
|
4417
|
+
}
|
|
4418
|
+
|
|
4419
|
+
if (loader == null) {
|
|
4420
|
+
return null;
|
|
4421
|
+
}
|
|
4422
|
+
return loader.shouldInterceptRequest(request.getUrl());
|
|
4423
|
+
}
|
|
4424
|
+
|
|
4367
4425
|
public void setUrl(String url) {
|
|
4368
4426
|
if (_webView == null) {
|
|
4369
4427
|
Log.w("InAppBrowser", "Cannot set URL - WebView is null");
|
|
@@ -5586,186 +5644,254 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
5586
5644
|
if (view == null || _webView == null) {
|
|
5587
5645
|
return null;
|
|
5588
5646
|
}
|
|
5589
|
-
if (!shouldUseNativeProxy()) {
|
|
5590
|
-
return null;
|
|
5591
|
-
}
|
|
5592
5647
|
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5648
|
+
if (shouldUseNativeProxy()) {
|
|
5649
|
+
String requestUrl = request.getUrl().toString();
|
|
5650
|
+
boolean bridgeBackedRequest = ProxyRequestSupport.isBridgeMarkerRequestUrl(requestUrl);
|
|
5651
|
+
String originalUrl;
|
|
5652
|
+
String method;
|
|
5653
|
+
Map<String, String> requestHeaders = new HashMap<>();
|
|
5654
|
+
String base64Body = "";
|
|
5655
|
+
String credentialsMode = "same-origin";
|
|
5656
|
+
|
|
5657
|
+
if (bridgeBackedRequest) {
|
|
5658
|
+
Uri uri = request.getUrl();
|
|
5659
|
+
originalUrl = uri.getQueryParameter("u");
|
|
5660
|
+
String requestId = uri.getQueryParameter("rid");
|
|
5661
|
+
if (originalUrl == null || requestId == null) {
|
|
5662
|
+
return null;
|
|
5663
|
+
}
|
|
5608
5664
|
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5665
|
+
if (
|
|
5666
|
+
ProxyRequestSupport.usesLegacyJsProxyMode(_options) &&
|
|
5667
|
+
!ProxyRequestSupport.shouldDelegateLegacyJsProxyRequest(_options, originalUrl)
|
|
5668
|
+
) {
|
|
5669
|
+
if (proxyBridge != null) {
|
|
5670
|
+
proxyBridge.getAndRemove(requestId);
|
|
5671
|
+
}
|
|
5672
|
+
Log.w("InAppBrowserProxy", "Ignoring legacy regex miss for bridge-backed request: " + originalUrl);
|
|
5673
|
+
return createCanceledResponse();
|
|
5615
5674
|
}
|
|
5616
|
-
Log.w("InAppBrowserProxy", "Ignoring legacy regex miss for bridge-backed request: " + originalUrl);
|
|
5617
|
-
return createCanceledResponse();
|
|
5618
|
-
}
|
|
5619
5675
|
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
requestHeaders = ProxyRequestSupport.mergeRequestHeaders(null, stored.headersJson);
|
|
5631
|
-
requestHeaders = ProxyRequestSupport.mergeMissingHeaders(requestHeaders, safeMarkerHeaders);
|
|
5632
|
-
} catch (JSONException error) {
|
|
5633
|
-
Log.e("InAppBrowserProxy", "Failed to parse stored proxy headers", error);
|
|
5634
|
-
return createCanceledResponse();
|
|
5635
|
-
}
|
|
5636
|
-
String initiatorUrl = request.getRequestHeaders().get("Referer");
|
|
5637
|
-
if (initiatorUrl == null || initiatorUrl.isBlank()) {
|
|
5638
|
-
initiatorUrl = getWebViewUrlOrFallback(
|
|
5639
|
-
_options != null && _options.getUrl() != null ? _options.getUrl() : originalUrl
|
|
5676
|
+
ProxyBridge.StoredRequest stored = proxyBridge != null ? proxyBridge.getAndRemove(requestId) : null;
|
|
5677
|
+
if (stored == null) {
|
|
5678
|
+
Log.e("InAppBrowserProxy", "Missing stored proxy bridge payload for request id: " + requestId);
|
|
5679
|
+
return createCanceledResponse();
|
|
5680
|
+
}
|
|
5681
|
+
method = stored.method;
|
|
5682
|
+
base64Body = stored.base64Body;
|
|
5683
|
+
credentialsMode = stored.credentialsMode;
|
|
5684
|
+
Map<String, String> safeMarkerHeaders = ProxyRequestSupport.extractSafeMarkerHeaders(
|
|
5685
|
+
request.getRequestHeaders()
|
|
5640
5686
|
);
|
|
5687
|
+
try {
|
|
5688
|
+
requestHeaders = ProxyRequestSupport.mergeRequestHeaders(null, stored.headersJson);
|
|
5689
|
+
requestHeaders = ProxyRequestSupport.mergeMissingHeaders(requestHeaders, safeMarkerHeaders);
|
|
5690
|
+
} catch (JSONException error) {
|
|
5691
|
+
Log.e("InAppBrowserProxy", "Failed to parse stored proxy headers", error);
|
|
5692
|
+
return createCanceledResponse();
|
|
5693
|
+
}
|
|
5694
|
+
String initiatorUrl = request.getRequestHeaders().get("Referer");
|
|
5695
|
+
if (initiatorUrl == null || initiatorUrl.isBlank()) {
|
|
5696
|
+
initiatorUrl = getWebViewUrlOrFallback(
|
|
5697
|
+
_options != null && _options.getUrl() != null ? _options.getUrl() : originalUrl
|
|
5698
|
+
);
|
|
5699
|
+
}
|
|
5700
|
+
String targetCookies = CookieManager.getInstance().getCookie(originalUrl);
|
|
5701
|
+
if (
|
|
5702
|
+
targetCookies != null &&
|
|
5703
|
+
!targetCookies.isBlank() &&
|
|
5704
|
+
ProxyRequestSupport.shouldInjectCookies(credentialsMode, initiatorUrl, originalUrl, requestHeaders)
|
|
5705
|
+
) {
|
|
5706
|
+
requestHeaders.put("Cookie", targetCookies);
|
|
5707
|
+
}
|
|
5708
|
+
} else {
|
|
5709
|
+
if (!ProxyRequestSupport.shouldHandleNonBridgeRequest(_options, requestUrl)) {
|
|
5710
|
+
return interceptBundledAssetRequest(request);
|
|
5711
|
+
}
|
|
5712
|
+
|
|
5713
|
+
originalUrl = requestUrl;
|
|
5714
|
+
method = request.getMethod();
|
|
5715
|
+
if (request.getRequestHeaders() != null) {
|
|
5716
|
+
requestHeaders.putAll(request.getRequestHeaders());
|
|
5717
|
+
}
|
|
5641
5718
|
}
|
|
5642
|
-
|
|
5719
|
+
|
|
5720
|
+
NativeRequestContext requestContext = new NativeRequestContext(
|
|
5721
|
+
originalUrl,
|
|
5722
|
+
method,
|
|
5723
|
+
requestHeaders,
|
|
5724
|
+
base64Body,
|
|
5725
|
+
request.isForMainFrame(),
|
|
5726
|
+
credentialsMode
|
|
5727
|
+
);
|
|
5728
|
+
|
|
5643
5729
|
if (
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5730
|
+
ProxyRequestSupport.shouldLetWebViewHandleMissingBody(
|
|
5731
|
+
requestUrl,
|
|
5732
|
+
requestContext.method,
|
|
5733
|
+
requestContext.base64Body
|
|
5734
|
+
)
|
|
5647
5735
|
) {
|
|
5648
|
-
|
|
5649
|
-
}
|
|
5650
|
-
} else {
|
|
5651
|
-
if (!ProxyRequestSupport.shouldHandleNonBridgeRequest(_options, requestUrl)) {
|
|
5736
|
+
Log.w("InAppBrowserProxy", "Allowing WebView to handle request with uncaptured body: " + requestContext.url);
|
|
5652
5737
|
return null;
|
|
5653
5738
|
}
|
|
5654
5739
|
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5740
|
+
boolean legacyProxyMode = ProxyRequestSupport.usesLegacyJsProxyMode(_options);
|
|
5741
|
+
boolean shouldDelegateLegacyRequest = ProxyRequestSupport.shouldDelegateLegacyJsProxyRequest(
|
|
5742
|
+
_options,
|
|
5743
|
+
requestContext.url
|
|
5744
|
+
);
|
|
5745
|
+
NativeResponseData directResponseData = null;
|
|
5746
|
+
|
|
5747
|
+
NativeProxyRule outboundRule =
|
|
5748
|
+
legacyProxyMode && shouldDelegateLegacyRequest
|
|
5749
|
+
? new NativeProxyRule(
|
|
5750
|
+
null,
|
|
5751
|
+
null,
|
|
5752
|
+
null,
|
|
5753
|
+
null,
|
|
5754
|
+
null,
|
|
5755
|
+
null,
|
|
5756
|
+
null,
|
|
5757
|
+
null,
|
|
5758
|
+
false,
|
|
5759
|
+
NativeProxyRule.Action.DELEGATE_TO_JS
|
|
5760
|
+
)
|
|
5761
|
+
: findMatchingRule(_options.getOutboundProxyRules(), requestContext, null);
|
|
5762
|
+
|
|
5763
|
+
if (outboundRule != null && outboundRule.getAction() == NativeProxyRule.Action.CANCEL) {
|
|
5764
|
+
return createCanceledResponse();
|
|
5659
5765
|
}
|
|
5660
|
-
}
|
|
5661
|
-
|
|
5662
|
-
NativeRequestContext requestContext = new NativeRequestContext(
|
|
5663
|
-
originalUrl,
|
|
5664
|
-
method,
|
|
5665
|
-
requestHeaders,
|
|
5666
|
-
base64Body,
|
|
5667
|
-
request.isForMainFrame(),
|
|
5668
|
-
credentialsMode
|
|
5669
|
-
);
|
|
5670
|
-
|
|
5671
|
-
if (
|
|
5672
|
-
ProxyRequestSupport.shouldLetWebViewHandleMissingBody(requestUrl, requestContext.method, requestContext.base64Body)
|
|
5673
|
-
) {
|
|
5674
|
-
Log.w("InAppBrowserProxy", "Allowing WebView to handle request with uncaptured body: " + requestContext.url);
|
|
5675
|
-
return null;
|
|
5676
|
-
}
|
|
5677
5766
|
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
: findMatchingRule(_options.getOutboundProxyRules(), requestContext, null);
|
|
5700
|
-
|
|
5701
|
-
if (outboundRule != null && outboundRule.getAction() == NativeProxyRule.Action.CANCEL) {
|
|
5702
|
-
return createCanceledResponse();
|
|
5703
|
-
}
|
|
5704
|
-
|
|
5705
|
-
if (outboundRule != null && outboundRule.getAction() == NativeProxyRule.Action.DELEGATE_TO_JS) {
|
|
5706
|
-
String proxyId = UUID.randomUUID().toString();
|
|
5707
|
-
ProxiedRequest proxiedRequest = new ProxiedRequest();
|
|
5708
|
-
proxiedRequest.requestContext = requestContext;
|
|
5709
|
-
addProxiedRequest(proxyId, proxiedRequest);
|
|
5710
|
-
|
|
5711
|
-
String dialogId = instanceId != null ? instanceId : "";
|
|
5712
|
-
_options
|
|
5713
|
-
.getCallbacks()
|
|
5714
|
-
.proxyRequestEvent(
|
|
5715
|
-
proxyId,
|
|
5716
|
-
"outbound",
|
|
5717
|
-
requestContext.url,
|
|
5718
|
-
requestContext.method,
|
|
5719
|
-
serializeHeaders(requestContext.headers),
|
|
5720
|
-
requestContext.base64Body.isEmpty() ? null : requestContext.base64Body,
|
|
5721
|
-
null,
|
|
5722
|
-
null,
|
|
5723
|
-
null,
|
|
5724
|
-
dialogId
|
|
5725
|
-
);
|
|
5767
|
+
if (outboundRule != null && outboundRule.getAction() == NativeProxyRule.Action.DELEGATE_TO_JS) {
|
|
5768
|
+
String proxyId = UUID.randomUUID().toString();
|
|
5769
|
+
ProxiedRequest proxiedRequest = new ProxiedRequest();
|
|
5770
|
+
proxiedRequest.requestContext = requestContext;
|
|
5771
|
+
addProxiedRequest(proxyId, proxiedRequest);
|
|
5772
|
+
|
|
5773
|
+
String dialogId = instanceId != null ? instanceId : "";
|
|
5774
|
+
_options
|
|
5775
|
+
.getCallbacks()
|
|
5776
|
+
.proxyRequestEvent(
|
|
5777
|
+
proxyId,
|
|
5778
|
+
"outbound",
|
|
5779
|
+
requestContext.url,
|
|
5780
|
+
requestContext.method,
|
|
5781
|
+
serializeHeaders(requestContext.headers),
|
|
5782
|
+
requestContext.base64Body.isEmpty() ? null : requestContext.base64Body,
|
|
5783
|
+
null,
|
|
5784
|
+
null,
|
|
5785
|
+
null,
|
|
5786
|
+
dialogId
|
|
5787
|
+
);
|
|
5726
5788
|
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5789
|
+
try {
|
|
5790
|
+
if (proxiedRequest.semaphore.tryAcquire(1, 10, TimeUnit.SECONDS)) {
|
|
5791
|
+
if (proxiedRequest.canceled) {
|
|
5792
|
+
return createCanceledResponse();
|
|
5793
|
+
}
|
|
5794
|
+
if (proxiedRequest.response != null) {
|
|
5795
|
+
return proxiedRequest.response;
|
|
5796
|
+
}
|
|
5797
|
+
if (proxiedRequest.nativeResponse != null) {
|
|
5798
|
+
directResponseData = proxiedRequest.nativeResponse;
|
|
5799
|
+
} else {
|
|
5800
|
+
requestContext =
|
|
5801
|
+
proxiedRequest.requestContext != null ? proxiedRequest.requestContext : requestContext;
|
|
5802
|
+
}
|
|
5737
5803
|
} else {
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5804
|
+
synchronized (proxiedRequest) {
|
|
5805
|
+
proxiedRequest.timedOut = true;
|
|
5806
|
+
}
|
|
5807
|
+
removeProxiedRequest(proxyId);
|
|
5808
|
+
Log.w("InAppBrowserProxy", "Proxy timeout, falling back to native replay for: " + requestContext.url);
|
|
5743
5809
|
}
|
|
5810
|
+
} catch (InterruptedException error) {
|
|
5744
5811
|
removeProxiedRequest(proxyId);
|
|
5745
|
-
|
|
5812
|
+
Thread.currentThread().interrupt();
|
|
5813
|
+
Log.e("InAppBrowserProxy", "Semaphore wait error", error);
|
|
5814
|
+
return bridgeBackedRequest ? createCanceledResponse() : null;
|
|
5746
5815
|
}
|
|
5747
|
-
} catch (InterruptedException error) {
|
|
5748
|
-
removeProxiedRequest(proxyId);
|
|
5749
|
-
Thread.currentThread().interrupt();
|
|
5750
|
-
Log.e("InAppBrowserProxy", "Semaphore wait error", error);
|
|
5751
|
-
return bridgeBackedRequest ? createCanceledResponse() : null;
|
|
5752
5816
|
}
|
|
5753
|
-
}
|
|
5754
5817
|
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5818
|
+
NativeResponseData nativeResponse = directResponseData;
|
|
5819
|
+
if (nativeResponse == null) {
|
|
5820
|
+
try {
|
|
5821
|
+
nativeResponse = performNativeRequest(requestContext);
|
|
5822
|
+
} catch (IOException error) {
|
|
5823
|
+
Log.e("InAppBrowserProxy", "Native request failed for: " + requestContext.url, error);
|
|
5824
|
+
return createProxiedNativeFailureResponse(requestContext.url, bridgeBackedRequest, error);
|
|
5825
|
+
}
|
|
5762
5826
|
}
|
|
5763
|
-
}
|
|
5764
5827
|
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5828
|
+
int redirectsFollowed = 0;
|
|
5829
|
+
while (true) {
|
|
5830
|
+
NativeProxyRule inboundRule = findMatchingRule(_options.getInboundProxyRules(), requestContext, nativeResponse);
|
|
5831
|
+
if (inboundRule == null || inboundRule.getAction() == NativeProxyRule.Action.CONTINUE) {
|
|
5832
|
+
RedirectReplayResult redirectReplay;
|
|
5833
|
+
try {
|
|
5834
|
+
redirectReplay = followRedirectForWebView(requestContext, nativeResponse, redirectsFollowed);
|
|
5835
|
+
} catch (IOException error) {
|
|
5836
|
+
Log.e("InAppBrowserProxy", "Native redirect replay failed for: " + requestContext.url, error);
|
|
5837
|
+
return createProxiedNativeFailureResponse(requestContext.url, bridgeBackedRequest, error);
|
|
5838
|
+
}
|
|
5839
|
+
if (redirectReplay != null) {
|
|
5840
|
+
requestContext = redirectReplay.requestContext;
|
|
5841
|
+
nativeResponse = redirectReplay.responseData;
|
|
5842
|
+
redirectsFollowed++;
|
|
5843
|
+
continue;
|
|
5844
|
+
}
|
|
5845
|
+
return createWebResourceResponseOrFallback(nativeResponse, bridgeBackedRequest, requestContext.url);
|
|
5846
|
+
}
|
|
5847
|
+
if (inboundRule.getAction() == NativeProxyRule.Action.CANCEL) {
|
|
5848
|
+
return createCanceledResponse();
|
|
5849
|
+
}
|
|
5850
|
+
|
|
5851
|
+
String proxyId = UUID.randomUUID().toString();
|
|
5852
|
+
ProxiedRequest proxiedRequest = new ProxiedRequest();
|
|
5853
|
+
proxiedRequest.requestContext = requestContext;
|
|
5854
|
+
proxiedRequest.nativeResponse = nativeResponse;
|
|
5855
|
+
addProxiedRequest(proxyId, proxiedRequest);
|
|
5856
|
+
|
|
5857
|
+
String dialogId = instanceId != null ? instanceId : "";
|
|
5858
|
+
_options
|
|
5859
|
+
.getCallbacks()
|
|
5860
|
+
.proxyRequestEvent(
|
|
5861
|
+
proxyId,
|
|
5862
|
+
"inbound",
|
|
5863
|
+
requestContext.url,
|
|
5864
|
+
requestContext.method,
|
|
5865
|
+
serializeHeaders(requestContext.headers),
|
|
5866
|
+
requestContext.base64Body.isEmpty() ? null : requestContext.base64Body,
|
|
5867
|
+
nativeResponse.statusCode,
|
|
5868
|
+
serializeHeaders(nativeResponse.headers),
|
|
5869
|
+
Base64.encodeToString(nativeResponse.bodyBytes, Base64.NO_WRAP),
|
|
5870
|
+
dialogId
|
|
5871
|
+
);
|
|
5872
|
+
|
|
5873
|
+
try {
|
|
5874
|
+
if (proxiedRequest.semaphore.tryAcquire(1, 10, TimeUnit.SECONDS)) {
|
|
5875
|
+
if (proxiedRequest.canceled) {
|
|
5876
|
+
return createCanceledResponse();
|
|
5877
|
+
}
|
|
5878
|
+
if (proxiedRequest.response != null) {
|
|
5879
|
+
return proxiedRequest.response;
|
|
5880
|
+
}
|
|
5881
|
+
if (proxiedRequest.nativeResponse != null) {
|
|
5882
|
+
nativeResponse = proxiedRequest.nativeResponse;
|
|
5883
|
+
}
|
|
5884
|
+
} else {
|
|
5885
|
+
synchronized (proxiedRequest) {
|
|
5886
|
+
proxiedRequest.timedOut = true;
|
|
5887
|
+
}
|
|
5888
|
+
removeProxiedRequest(proxyId);
|
|
5889
|
+
}
|
|
5890
|
+
} catch (InterruptedException error) {
|
|
5891
|
+
Thread.currentThread().interrupt();
|
|
5892
|
+
Log.e("InAppBrowserProxy", "Semaphore wait error", error);
|
|
5893
|
+
}
|
|
5894
|
+
|
|
5769
5895
|
RedirectReplayResult redirectReplay;
|
|
5770
5896
|
try {
|
|
5771
5897
|
redirectReplay = followRedirectForWebView(requestContext, nativeResponse, redirectsFollowed);
|
|
@@ -5781,69 +5907,9 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
5781
5907
|
}
|
|
5782
5908
|
return createWebResourceResponseOrFallback(nativeResponse, bridgeBackedRequest, requestContext.url);
|
|
5783
5909
|
}
|
|
5784
|
-
if (inboundRule.getAction() == NativeProxyRule.Action.CANCEL) {
|
|
5785
|
-
return createCanceledResponse();
|
|
5786
|
-
}
|
|
5787
|
-
|
|
5788
|
-
String proxyId = UUID.randomUUID().toString();
|
|
5789
|
-
ProxiedRequest proxiedRequest = new ProxiedRequest();
|
|
5790
|
-
proxiedRequest.requestContext = requestContext;
|
|
5791
|
-
proxiedRequest.nativeResponse = nativeResponse;
|
|
5792
|
-
addProxiedRequest(proxyId, proxiedRequest);
|
|
5793
|
-
|
|
5794
|
-
String dialogId = instanceId != null ? instanceId : "";
|
|
5795
|
-
_options
|
|
5796
|
-
.getCallbacks()
|
|
5797
|
-
.proxyRequestEvent(
|
|
5798
|
-
proxyId,
|
|
5799
|
-
"inbound",
|
|
5800
|
-
requestContext.url,
|
|
5801
|
-
requestContext.method,
|
|
5802
|
-
serializeHeaders(requestContext.headers),
|
|
5803
|
-
requestContext.base64Body.isEmpty() ? null : requestContext.base64Body,
|
|
5804
|
-
nativeResponse.statusCode,
|
|
5805
|
-
serializeHeaders(nativeResponse.headers),
|
|
5806
|
-
Base64.encodeToString(nativeResponse.bodyBytes, Base64.NO_WRAP),
|
|
5807
|
-
dialogId
|
|
5808
|
-
);
|
|
5809
|
-
|
|
5810
|
-
try {
|
|
5811
|
-
if (proxiedRequest.semaphore.tryAcquire(1, 10, TimeUnit.SECONDS)) {
|
|
5812
|
-
if (proxiedRequest.canceled) {
|
|
5813
|
-
return createCanceledResponse();
|
|
5814
|
-
}
|
|
5815
|
-
if (proxiedRequest.response != null) {
|
|
5816
|
-
return proxiedRequest.response;
|
|
5817
|
-
}
|
|
5818
|
-
if (proxiedRequest.nativeResponse != null) {
|
|
5819
|
-
nativeResponse = proxiedRequest.nativeResponse;
|
|
5820
|
-
}
|
|
5821
|
-
} else {
|
|
5822
|
-
synchronized (proxiedRequest) {
|
|
5823
|
-
proxiedRequest.timedOut = true;
|
|
5824
|
-
}
|
|
5825
|
-
removeProxiedRequest(proxyId);
|
|
5826
|
-
}
|
|
5827
|
-
} catch (InterruptedException error) {
|
|
5828
|
-
Thread.currentThread().interrupt();
|
|
5829
|
-
Log.e("InAppBrowserProxy", "Semaphore wait error", error);
|
|
5830
|
-
}
|
|
5831
|
-
|
|
5832
|
-
RedirectReplayResult redirectReplay;
|
|
5833
|
-
try {
|
|
5834
|
-
redirectReplay = followRedirectForWebView(requestContext, nativeResponse, redirectsFollowed);
|
|
5835
|
-
} catch (IOException error) {
|
|
5836
|
-
Log.e("InAppBrowserProxy", "Native redirect replay failed for: " + requestContext.url, error);
|
|
5837
|
-
return createProxiedNativeFailureResponse(requestContext.url, bridgeBackedRequest, error);
|
|
5838
|
-
}
|
|
5839
|
-
if (redirectReplay != null) {
|
|
5840
|
-
requestContext = redirectReplay.requestContext;
|
|
5841
|
-
nativeResponse = redirectReplay.responseData;
|
|
5842
|
-
redirectsFollowed++;
|
|
5843
|
-
continue;
|
|
5844
|
-
}
|
|
5845
|
-
return createWebResourceResponseOrFallback(nativeResponse, bridgeBackedRequest, requestContext.url);
|
|
5846
5910
|
}
|
|
5911
|
+
|
|
5912
|
+
return interceptBundledAssetRequest(request);
|
|
5847
5913
|
}
|
|
5848
5914
|
|
|
5849
5915
|
@Override
|
package/dist/docs.json
CHANGED
|
@@ -999,9 +999,13 @@
|
|
|
999
999
|
{
|
|
1000
1000
|
"text": "0.1.0",
|
|
1001
1001
|
"name": "since"
|
|
1002
|
+
},
|
|
1003
|
+
{
|
|
1004
|
+
"text": "\"https://capgo.app\"",
|
|
1005
|
+
"name": "example"
|
|
1002
1006
|
}
|
|
1003
1007
|
],
|
|
1004
|
-
"docs": "Target URL to load.",
|
|
1008
|
+
"docs": "Target URL to load.\n\nRemote `http://` and `https://` URLs are loaded as-is. Relative bundled paths\nsuch as `/index.html` are not supported; use `openWebView()` instead.",
|
|
1005
1009
|
"complexTypes": [],
|
|
1006
1010
|
"type": "string"
|
|
1007
1011
|
},
|
|
@@ -1403,12 +1407,20 @@
|
|
|
1403
1407
|
"text": "0.1.0",
|
|
1404
1408
|
"name": "since"
|
|
1405
1409
|
},
|
|
1410
|
+
{
|
|
1411
|
+
"text": "8.15.0 Relative bundled paths (`/index.html`, `assets/page.html`) are supported.",
|
|
1412
|
+
"name": "since"
|
|
1413
|
+
},
|
|
1406
1414
|
{
|
|
1407
1415
|
"text": "\"https://capgo.app\"",
|
|
1408
1416
|
"name": "example"
|
|
1417
|
+
},
|
|
1418
|
+
{
|
|
1419
|
+
"text": "\"/index.html\"",
|
|
1420
|
+
"name": "example"
|
|
1409
1421
|
}
|
|
1410
1422
|
],
|
|
1411
|
-
"docs": "Target URL to load.",
|
|
1423
|
+
"docs": "Target URL to load.\n\nRemote `http://` and `https://` URLs are loaded as-is.\n\nTo open bundled web assets from the app bundle without running a local HTTP server,\npass a relative path such as `/index.html` or `assets/page.html`. The plugin resolves it to\nthe Capacitor local URL for the current platform (defaults: `capacitor://localhost/...` on iOS,\n`https://localhost/...` on Android; actual scheme and host follow the app's configured Capacitor\nlocal URL) and serves files from the packaged `public/` directory, or `www/` on iOS when `public/`\nis absent.",
|
|
1412
1424
|
"complexTypes": [],
|
|
1413
1425
|
"type": "string"
|
|
1414
1426
|
},
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type BundledAssetPlatform = 'ios' | 'android';
|
|
2
|
+
export interface BundledAssetLocalConfig {
|
|
3
|
+
scheme: string;
|
|
4
|
+
host: string;
|
|
5
|
+
}
|
|
6
|
+
export interface BundledAssetResolution {
|
|
7
|
+
url: string;
|
|
8
|
+
}
|
|
9
|
+
/** Default Capacitor local URL settings used when no runtime config is available. */
|
|
10
|
+
export declare const BUNDLED_ASSET_DEFAULTS: Record<BundledAssetPlatform, BundledAssetLocalConfig>;
|
|
11
|
+
/** Returns true when the input looks like an absolute URL (including protocol-relative URLs). */
|
|
12
|
+
export declare function isAbsoluteUrl(url: string): boolean;
|
|
13
|
+
/** Returns true when any path segment is `..`. */
|
|
14
|
+
export declare function containsPathTraversal(path: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Normalizes a relative bundled asset path to a leading-slash form.
|
|
17
|
+
* Returns null when the path contains traversal segments.
|
|
18
|
+
*/
|
|
19
|
+
export declare function normalizeBundledPath(path: string): string | null;
|
|
20
|
+
/** Parses a Capacitor local URL into scheme and host components. */
|
|
21
|
+
export declare function parseBundledLocalConfig(localUrl?: string | null): BundledAssetLocalConfig | null;
|
|
22
|
+
/** Returns the scheme WebView navigation should use for bundled assets on a platform. */
|
|
23
|
+
export declare function handlerSchemeForPlatform(platform: BundledAssetPlatform, localConfig: BundledAssetLocalConfig): string;
|
|
24
|
+
/**
|
|
25
|
+
* Returns true when the URL targets the configured Capacitor local host/scheme
|
|
26
|
+
* (including the platform handler scheme rewrite).
|
|
27
|
+
*/
|
|
28
|
+
export declare function isBundledLocalUrl(url: string, platform: BundledAssetPlatform, localConfig?: BundledAssetLocalConfig | null): boolean;
|
|
29
|
+
/** Returns true when the input is a non-empty relative bundled asset path. */
|
|
30
|
+
export declare function isRelativeBundledPath(url: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Resolves a bundled asset URL for the given platform.
|
|
33
|
+
* Relative paths become platform-local URLs; bundled local URLs may be rewritten to the handler scheme.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveBundledAssetUrl(url: string, platform: BundledAssetPlatform, localConfig?: BundledAssetLocalConfig | null): BundledAssetResolution | null;
|
|
36
|
+
/**
|
|
37
|
+
* Resolves relative bundled paths for legacy native plugins that do not perform native resolution.
|
|
38
|
+
* Throws when the path is invalid.
|
|
39
|
+
*/
|
|
40
|
+
export declare function resolveLegacyNativeWebViewUrl(url: string, platform: BundledAssetPlatform, localConfig?: BundledAssetLocalConfig | null): string;
|