@capgo/capacitor-inappbrowser 8.14.2 → 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 +23 -5
- 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/SafeAreaInsetsSupport.java +34 -48
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +462 -335
- package/dist/docs.json +17 -5
- 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 +26 -4
- 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();
|
|
@@ -358,6 +361,10 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
358
361
|
private boolean cachedTitleIconResolved;
|
|
359
362
|
private boolean isHiddenModeActive = false;
|
|
360
363
|
private boolean toolbarHideInProgress = false;
|
|
364
|
+
private int injectedSafeAreaTop = Integer.MIN_VALUE;
|
|
365
|
+
private int injectedSafeAreaBottom = Integer.MIN_VALUE;
|
|
366
|
+
private int injectedSafeAreaLeft = Integer.MIN_VALUE;
|
|
367
|
+
private int injectedSafeAreaRight = Integer.MIN_VALUE;
|
|
361
368
|
private boolean configurationCallbacksRegistered = false;
|
|
362
369
|
private Configuration lastConfiguration;
|
|
363
370
|
private final ComponentCallbacks2 configurationCallbacks = new ComponentCallbacks2() {
|
|
@@ -675,6 +682,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
675
682
|
if (isShowing()) {
|
|
676
683
|
super.hide();
|
|
677
684
|
}
|
|
685
|
+
refreshInsetsForHostingLayer();
|
|
678
686
|
return true;
|
|
679
687
|
}
|
|
680
688
|
|
|
@@ -688,6 +696,18 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
688
696
|
show();
|
|
689
697
|
}
|
|
690
698
|
applyDimensions();
|
|
699
|
+
refreshInsetsForHostingLayer();
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* The container padding is computed for the window hosting the content, and the two windows inset
|
|
704
|
+
* differently: the dialog is edge-to-edge on Android 15+, while the host activity fits its own
|
|
705
|
+
* system windows. Recompute after a layer change so padding from the previous host is not left
|
|
706
|
+
* behind (which would stack on top of the host offset).
|
|
707
|
+
*/
|
|
708
|
+
private void refreshInsetsForHostingLayer() {
|
|
709
|
+
reapplyInsetsFromWindowRoot();
|
|
710
|
+
mainHandler.post(this::reapplyInsetsFromWindowRoot);
|
|
691
711
|
}
|
|
692
712
|
|
|
693
713
|
private boolean showAccordingToLayerMode() {
|
|
@@ -2215,6 +2235,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
2215
2235
|
_webView.addJavascriptInterface(proxyBridge, "__capgoProxy");
|
|
2216
2236
|
proxyBridgeScript = loadProxyBridgeScript();
|
|
2217
2237
|
}
|
|
2238
|
+
ensureBundledAssetLoader();
|
|
2218
2239
|
_webView.getSettings().setJavaScriptEnabled(true);
|
|
2219
2240
|
_webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
|
|
2220
2241
|
_webView.getSettings().setDatabaseEnabled(true);
|
|
@@ -3087,8 +3108,8 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3087
3108
|
|
|
3088
3109
|
requestSafeAreaInsets();
|
|
3089
3110
|
|
|
3090
|
-
mainHandler.postDelayed(this::
|
|
3091
|
-
mainHandler.postDelayed(this::
|
|
3111
|
+
mainHandler.postDelayed(this::reapplyInsetsFromWindowRoot, 300);
|
|
3112
|
+
mainHandler.postDelayed(this::reapplyInsetsFromWindowRoot, 1200);
|
|
3092
3113
|
}
|
|
3093
3114
|
|
|
3094
3115
|
private void registerConfigurationCallbacks() {
|
|
@@ -3312,15 +3333,49 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3312
3333
|
statusBarColorView.setVisibility(View.VISIBLE);
|
|
3313
3334
|
}
|
|
3314
3335
|
|
|
3315
|
-
|
|
3316
|
-
if (appBarParams instanceof ViewGroup.MarginLayoutParams marginParams) {
|
|
3317
|
-
marginParams.topMargin = statusBarHeight;
|
|
3318
|
-
appBarLayout.setLayoutParams(marginParams);
|
|
3319
|
-
}
|
|
3336
|
+
applyAppBarTopInset(appBarLayout, appBarHandlesTopInset(toolbarView) ? statusBarHeight : 0);
|
|
3320
3337
|
appBarLayout.setBackgroundColor(finalBgColor);
|
|
3321
3338
|
});
|
|
3322
3339
|
}
|
|
3323
3340
|
|
|
3341
|
+
/**
|
|
3342
|
+
* Whether a visible AppBarLayout consumes the top inset itself. It is the only alternative to
|
|
3343
|
+
* padding the WebView container for the status bar, so both mechanisms share this condition and
|
|
3344
|
+
* can never inset the top twice.
|
|
3345
|
+
*/
|
|
3346
|
+
private boolean appBarHandlesTopInset(View toolbarView) {
|
|
3347
|
+
return (
|
|
3348
|
+
Build.VERSION.SDK_INT >= 35 &&
|
|
3349
|
+
_options != null &&
|
|
3350
|
+
!TextUtils.equals(_options.getToolbarType(), "blank") &&
|
|
3351
|
+
toolbarView != null &&
|
|
3352
|
+
toolbarView.getVisibility() == View.VISIBLE &&
|
|
3353
|
+
toolbarView.getParent() instanceof com.google.android.material.appbar.AppBarLayout
|
|
3354
|
+
);
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
/**
|
|
3358
|
+
* The appbar must sit below the status bar on edge-to-edge windows. A top margin does that
|
|
3359
|
+
* visually, but CoordinatorLayout's scrolling-view behavior sizes the content container from the
|
|
3360
|
+
* appbar height only and positions it below the appbar margin, so a margin pushes the container
|
|
3361
|
+
* bottom off-screen by the status-bar height (#641). Padding grows the appbar height instead,
|
|
3362
|
+
* which the behavior does account for, keeping the container inside the window.
|
|
3363
|
+
*/
|
|
3364
|
+
private void applyAppBarTopInset(com.google.android.material.appbar.AppBarLayout appBarLayout, int statusBarTop) {
|
|
3365
|
+
ViewGroup.LayoutParams appBarParams = appBarLayout.getLayoutParams();
|
|
3366
|
+
if (appBarParams instanceof ViewGroup.MarginLayoutParams marginParams && marginParams.topMargin != 0) {
|
|
3367
|
+
marginParams.topMargin = 0;
|
|
3368
|
+
appBarLayout.setLayoutParams(marginParams);
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3371
|
+
appBarLayout.setPadding(
|
|
3372
|
+
appBarLayout.getPaddingLeft(),
|
|
3373
|
+
Math.max(0, statusBarTop),
|
|
3374
|
+
appBarLayout.getPaddingRight(),
|
|
3375
|
+
appBarLayout.getPaddingBottom()
|
|
3376
|
+
);
|
|
3377
|
+
}
|
|
3378
|
+
|
|
3324
3379
|
private void applyWindowInsetsToWebView(WindowInsetsCompat windowInsets, boolean isAndroid15Plus, View toolbarView) {
|
|
3325
3380
|
if (windowInsets == null || _options == null) {
|
|
3326
3381
|
return;
|
|
@@ -3333,53 +3388,73 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3333
3388
|
Insets ime = windowInsets.getInsets(WindowInsetsCompat.Type.ime());
|
|
3334
3389
|
boolean keyboardVisible = windowInsets.isVisible(WindowInsetsCompat.Type.ime());
|
|
3335
3390
|
|
|
3336
|
-
|
|
3337
|
-
isAndroid15Plus &&
|
|
3338
|
-
!TextUtils.equals(_options.getToolbarType(), "blank") &&
|
|
3339
|
-
toolbarView != null &&
|
|
3340
|
-
toolbarView.getVisibility() == View.VISIBLE &&
|
|
3341
|
-
toolbarView.getParent() instanceof com.google.android.material.appbar.AppBarLayout;
|
|
3342
|
-
|
|
3343
|
-
applySafeAreaMargins(
|
|
3391
|
+
applySafeAreaInsets(
|
|
3344
3392
|
bars,
|
|
3345
3393
|
navigationBars,
|
|
3346
3394
|
systemGestures,
|
|
3347
3395
|
mandatoryGestures,
|
|
3348
3396
|
ime,
|
|
3349
3397
|
keyboardVisible,
|
|
3350
|
-
appBarHandlesTopInset,
|
|
3398
|
+
appBarHandlesTopInset(toolbarView),
|
|
3351
3399
|
isAndroid15Plus
|
|
3352
3400
|
);
|
|
3353
3401
|
}
|
|
3354
3402
|
|
|
3355
3403
|
/**
|
|
3356
|
-
* Re-read root window insets and apply
|
|
3404
|
+
* Re-read root window insets and apply the container padding.
|
|
3357
3405
|
* Dialog windows may not re-dispatch inset listeners after rotation, leaving stale
|
|
3358
|
-
* portrait
|
|
3406
|
+
* portrait insets that break vertical scrolling in landscape.
|
|
3359
3407
|
*/
|
|
3360
3408
|
private void reapplyInsetsFromWindowRoot() {
|
|
3361
3409
|
if (_webView == null || _options == null) {
|
|
3362
3410
|
return;
|
|
3363
3411
|
}
|
|
3364
3412
|
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
if (decorView == null) {
|
|
3413
|
+
View insetsSourceView = resolveHostedInsetsSourceView();
|
|
3414
|
+
if (insetsSourceView == null) {
|
|
3368
3415
|
return;
|
|
3369
3416
|
}
|
|
3370
3417
|
|
|
3371
|
-
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(
|
|
3418
|
+
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(insetsSourceView);
|
|
3372
3419
|
if (windowInsets == null) {
|
|
3373
|
-
// Root insets not ready yet; do not mutate
|
|
3374
|
-
//
|
|
3420
|
+
// Root insets not ready yet; do not mutate the container padding. Delayed retries and
|
|
3421
|
+
// requestApplyInsets will retry.
|
|
3375
3422
|
return;
|
|
3376
3423
|
}
|
|
3377
3424
|
|
|
3378
3425
|
boolean isAndroid15Plus = Build.VERSION.SDK_INT >= 35;
|
|
3379
|
-
View toolbarView =
|
|
3426
|
+
View toolbarView = findBrowserContentDescendant(R.id.tool_bar);
|
|
3380
3427
|
applyWindowInsetsToWebView(windowInsets, isAndroid15Plus, toolbarView);
|
|
3381
3428
|
}
|
|
3382
3429
|
|
|
3430
|
+
/**
|
|
3431
|
+
* Insets must come from the window that currently hosts the content. In back-layer mode the
|
|
3432
|
+
* content lives in the host activity's window and this dialog's window is hidden, so its decor
|
|
3433
|
+
* would report stale insets.
|
|
3434
|
+
*/
|
|
3435
|
+
private View resolveHostedInsetsSourceView() {
|
|
3436
|
+
if (backLayerActive) {
|
|
3437
|
+
View contentView = getBrowserContentView();
|
|
3438
|
+
if (contentView != null && contentView.isAttachedToWindow()) {
|
|
3439
|
+
return contentView;
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
Window window = getWindow();
|
|
3444
|
+
return window != null ? window.getDecorView() : null;
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3447
|
+
/**
|
|
3448
|
+
* Back-layer mode reparents the browser content out of the dialog window, so {@link
|
|
3449
|
+
* #findViewById(int)} no longer reaches it. Resolve through the content view, which owns these
|
|
3450
|
+
* views in both layers.
|
|
3451
|
+
*/
|
|
3452
|
+
private View findBrowserContentDescendant(int id) {
|
|
3453
|
+
View contentView = getBrowserContentView();
|
|
3454
|
+
View view = contentView != null ? contentView.findViewById(id) : null;
|
|
3455
|
+
return view != null ? view : findViewById(id);
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3383
3458
|
private void requestWebViewContentRelayout() {
|
|
3384
3459
|
View container = findViewById(R.id.content_browser_layout);
|
|
3385
3460
|
if (container != null) {
|
|
@@ -3395,7 +3470,12 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3395
3470
|
_webView.invalidate();
|
|
3396
3471
|
}
|
|
3397
3472
|
|
|
3398
|
-
|
|
3473
|
+
/**
|
|
3474
|
+
* The WebView is the content child of a SwipeRefreshLayout, which lays that child out inside its
|
|
3475
|
+
* own padding and ignores child margins entirely. Safe-area insets are therefore applied as
|
|
3476
|
+
* padding on the container instead of margins on the WebView (#641).
|
|
3477
|
+
*/
|
|
3478
|
+
private void applySafeAreaInsets(
|
|
3399
3479
|
Insets bars,
|
|
3400
3480
|
Insets navigationBars,
|
|
3401
3481
|
Insets systemGestures,
|
|
@@ -3403,88 +3483,25 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3403
3483
|
Insets ime,
|
|
3404
3484
|
boolean keyboardVisible,
|
|
3405
3485
|
boolean appBarHandlesTopInset,
|
|
3406
|
-
boolean
|
|
3486
|
+
boolean isEdgeToEdge
|
|
3407
3487
|
) {
|
|
3408
3488
|
if (_webView == null || _options == null) {
|
|
3409
3489
|
return;
|
|
3410
3490
|
}
|
|
3411
3491
|
|
|
3412
|
-
|
|
3413
|
-
if (
|
|
3492
|
+
View container = findBrowserContentDescendant(R.id.content_browser_layout);
|
|
3493
|
+
if (container == null) {
|
|
3414
3494
|
return;
|
|
3415
3495
|
}
|
|
3416
3496
|
|
|
3417
|
-
|
|
3418
|
-
int
|
|
3497
|
+
boolean applyBottomInset = SafeAreaInsetsSupport.shouldInsetBottomForContainer(_options.getEnabledSafeMargin(), isEdgeToEdge);
|
|
3498
|
+
int statusBarTop = SafeAreaInsetsSupport.resolveStatusBarTop(
|
|
3499
|
+
bars.top,
|
|
3419
3500
|
bars.bottom,
|
|
3420
|
-
navigationBars.bottom,
|
|
3421
|
-
systemGestures.bottom,
|
|
3422
|
-
mandatoryGestures.bottom,
|
|
3423
3501
|
bars.left,
|
|
3424
3502
|
bars.right,
|
|
3425
|
-
|
|
3426
|
-
navigationBars.right,
|
|
3427
|
-
fallbackBottomInset,
|
|
3428
|
-
_options.getEnabledSafeMargin()
|
|
3429
|
-
);
|
|
3430
|
-
// Android 15+ uses edge-to-edge (decorFitsSystemWindows=false) and needs IME as margin.
|
|
3431
|
-
// Older dialogs still resize for the keyboard; re-applying decor IME creates a black gap (#622).
|
|
3432
|
-
int imeBottom = SafeAreaInsetsSupport.resolveImeBottomInset(keyboardVisible, ime.bottom, applyImeAsLayoutMargin);
|
|
3433
|
-
boolean applyTopFallback = _options.getEnabledSafeTopMargin() && _options.getUseTopInset();
|
|
3434
|
-
int fallbackTopInset = applyTopFallback ? getSystemStatusBarHeight() : 0;
|
|
3435
|
-
int navTop = SafeAreaInsetsSupport.resolveTopMarginWithFallback(
|
|
3436
|
-
_options.getEnabledSafeTopMargin(),
|
|
3437
|
-
_options.getUseTopInset(),
|
|
3438
|
-
bars.top,
|
|
3439
|
-
appBarHandlesTopInset,
|
|
3440
|
-
fallbackTopInset,
|
|
3441
|
-
applyTopFallback
|
|
3503
|
+
getSystemStatusBarHeight()
|
|
3442
3504
|
);
|
|
3443
|
-
int bottomMargin = SafeAreaInsetsSupport.resolveBottomMargin(_options.getEnabledSafeMargin(), safeBottomInset, imeBottom);
|
|
3444
|
-
|
|
3445
|
-
mlp.topMargin = navTop;
|
|
3446
|
-
mlp.bottomMargin = bottomMargin;
|
|
3447
|
-
mlp.leftMargin = bars.left;
|
|
3448
|
-
mlp.rightMargin = bars.right;
|
|
3449
|
-
_webView.setLayoutParams(mlp);
|
|
3450
|
-
injectSafeAreaCssVariables(navTop, bottomMargin, bars.left, bars.right);
|
|
3451
|
-
applyContainerInsetsSnapshot();
|
|
3452
|
-
}
|
|
3453
|
-
|
|
3454
|
-
private void applyContainerInsetsSnapshot() {
|
|
3455
|
-
if (_options == null || _webView == null) {
|
|
3456
|
-
return;
|
|
3457
|
-
}
|
|
3458
|
-
|
|
3459
|
-
View container = findViewById(R.id.content_browser_layout);
|
|
3460
|
-
if (container == null) {
|
|
3461
|
-
return;
|
|
3462
|
-
}
|
|
3463
|
-
|
|
3464
|
-
Window window = getWindow();
|
|
3465
|
-
View decorView = window != null ? window.getDecorView() : null;
|
|
3466
|
-
WindowInsetsCompat windowInsets = decorView != null ? ViewCompat.getRootWindowInsets(decorView) : null;
|
|
3467
|
-
|
|
3468
|
-
Insets bars = windowInsets != null ? windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) : Insets.NONE;
|
|
3469
|
-
Insets navigationBars = windowInsets != null ? windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()) : Insets.NONE;
|
|
3470
|
-
Insets systemGestures = windowInsets != null ? windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures()) : Insets.NONE;
|
|
3471
|
-
Insets mandatoryGestures =
|
|
3472
|
-
windowInsets != null ? windowInsets.getInsets(WindowInsetsCompat.Type.mandatorySystemGestures()) : Insets.NONE;
|
|
3473
|
-
Insets ime = windowInsets != null ? windowInsets.getInsets(WindowInsetsCompat.Type.ime()) : Insets.NONE;
|
|
3474
|
-
boolean keyboardVisible = windowInsets != null && windowInsets.isVisible(WindowInsetsCompat.Type.ime());
|
|
3475
|
-
|
|
3476
|
-
boolean isAndroid15Plus = Build.VERSION.SDK_INT >= 35;
|
|
3477
|
-
View toolbarView = findViewById(R.id.tool_bar);
|
|
3478
|
-
boolean appBarHandlesTopInset =
|
|
3479
|
-
isAndroid15Plus &&
|
|
3480
|
-
!TextUtils.equals(_options.getToolbarType(), "blank") &&
|
|
3481
|
-
toolbarView != null &&
|
|
3482
|
-
toolbarView.getVisibility() == View.VISIBLE &&
|
|
3483
|
-
toolbarView.getParent() instanceof com.google.android.material.appbar.AppBarLayout;
|
|
3484
|
-
|
|
3485
|
-
boolean applyBottomInset = SafeAreaInsetsSupport.shouldInsetBottomForContainer(_options.getEnabledSafeMargin(), isAndroid15Plus);
|
|
3486
|
-
|
|
3487
|
-
int statusBarTop = bars.top > 0 ? bars.top : getSystemStatusBarHeight();
|
|
3488
3505
|
int fallbackBottomInset = applyBottomInset ? getSystemNavigationBarHeight() : 0;
|
|
3489
3506
|
int safeBottomInset = SafeAreaInsetsSupport.resolveSafeBottomInsetWithFallback(
|
|
3490
3507
|
bars.bottom,
|
|
@@ -3498,32 +3515,41 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3498
3515
|
fallbackBottomInset,
|
|
3499
3516
|
applyBottomInset
|
|
3500
3517
|
);
|
|
3518
|
+
// Android 15+ uses edge-to-edge (decorFitsSystemWindows=false) and needs the IME inset applied.
|
|
3519
|
+
// Older dialogs still resize for the keyboard; re-applying decor IME creates a black gap (#622).
|
|
3520
|
+
int imeBottom = SafeAreaInsetsSupport.resolveImeBottomInset(keyboardVisible, ime.bottom, isEdgeToEdge);
|
|
3501
3521
|
|
|
3522
|
+
// In back-layer mode the content is reparented into the host activity's window, which this
|
|
3523
|
+
// plugin never puts in edge-to-edge, so the host owns the top inset and the useTopInset opt-in
|
|
3524
|
+
// still decides there.
|
|
3502
3525
|
int padTop = SafeAreaInsetsSupport.resolveContainerTopPadding(
|
|
3503
3526
|
_options.getEnabledSafeTopMargin(),
|
|
3504
3527
|
_options.getUseTopInset(),
|
|
3505
3528
|
statusBarTop,
|
|
3506
|
-
appBarHandlesTopInset
|
|
3507
|
-
);
|
|
3508
|
-
int imeBottom = SafeAreaInsetsSupport.resolveImeBottomInset(keyboardVisible, ime.bottom, isAndroid15Plus);
|
|
3509
|
-
int padBottom = SafeAreaInsetsSupport.resolveContainerBottomPadding(
|
|
3510
|
-
applyBottomInset,
|
|
3511
|
-
safeBottomInset,
|
|
3512
|
-
imeBottom,
|
|
3513
3529
|
appBarHandlesTopInset,
|
|
3514
|
-
|
|
3530
|
+
isEdgeToEdge && !backLayerActive
|
|
3515
3531
|
);
|
|
3532
|
+
int padBottom = SafeAreaInsetsSupport.resolveContainerBottomPadding(applyBottomInset, safeBottomInset, imeBottom);
|
|
3516
3533
|
|
|
3517
|
-
if (
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3534
|
+
if (appBarHandlesTopInset) {
|
|
3535
|
+
// Keep the appbar inset in sync with the reported inset (cutouts, rotation, multi-window)
|
|
3536
|
+
// instead of the status_bar_height resource used for the initial layout.
|
|
3537
|
+
View toolbarView = findBrowserContentDescendant(R.id.tool_bar);
|
|
3538
|
+
if (toolbarView != null && toolbarView.getParent() instanceof com.google.android.material.appbar.AppBarLayout appBarLayout) {
|
|
3539
|
+
applyAppBarTopInset(appBarLayout, statusBarTop);
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
|
|
3543
|
+
boolean paddingChanged =
|
|
3544
|
+
container.getPaddingLeft() != bars.left ||
|
|
3545
|
+
container.getPaddingTop() != padTop ||
|
|
3546
|
+
container.getPaddingRight() != bars.right ||
|
|
3547
|
+
container.getPaddingBottom() != padBottom;
|
|
3548
|
+
if (paddingChanged) {
|
|
3549
|
+
container.setPadding(bars.left, padTop, bars.right, padBottom);
|
|
3524
3550
|
}
|
|
3525
3551
|
|
|
3526
|
-
|
|
3552
|
+
injectSafeAreaCssVariables(padTop, padBottom, bars.left, bars.right);
|
|
3527
3553
|
}
|
|
3528
3554
|
|
|
3529
3555
|
private void configureBlankToolbarLayout() {
|
|
@@ -3554,27 +3580,62 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3554
3580
|
return getContext().getResources().getDimensionPixelSize(resourceId);
|
|
3555
3581
|
}
|
|
3556
3582
|
|
|
3583
|
+
private void resetInjectedSafeAreaCssVariables() {
|
|
3584
|
+
injectedSafeAreaTop = Integer.MIN_VALUE;
|
|
3585
|
+
injectedSafeAreaBottom = Integer.MIN_VALUE;
|
|
3586
|
+
injectedSafeAreaLeft = Integer.MIN_VALUE;
|
|
3587
|
+
injectedSafeAreaRight = Integer.MIN_VALUE;
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3557
3590
|
private void injectSafeAreaCssVariables(int top, int bottom, int left, int right) {
|
|
3558
3591
|
if (_webView == null) {
|
|
3559
3592
|
return;
|
|
3560
3593
|
}
|
|
3561
3594
|
|
|
3595
|
+
// Insets are dispatched on every layout pass; only touch the document when they differ from
|
|
3596
|
+
// what it already holds. Cleared on page load so a new document is served again.
|
|
3597
|
+
if (
|
|
3598
|
+
injectedSafeAreaTop == top && injectedSafeAreaBottom == bottom && injectedSafeAreaLeft == left && injectedSafeAreaRight == right
|
|
3599
|
+
) {
|
|
3600
|
+
return;
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
// Recorded before posting so passes that repeat within the same frame are suppressed too.
|
|
3604
|
+
injectedSafeAreaTop = top;
|
|
3605
|
+
injectedSafeAreaBottom = bottom;
|
|
3606
|
+
injectedSafeAreaLeft = left;
|
|
3607
|
+
injectedSafeAreaRight = right;
|
|
3608
|
+
|
|
3562
3609
|
String script = String.format(
|
|
3563
3610
|
Locale.US,
|
|
3564
3611
|
"(function(){var root=document.documentElement;" +
|
|
3565
3612
|
"root.style.setProperty('--safe-area-inset-top','%dpx');" +
|
|
3566
3613
|
"root.style.setProperty('--safe-area-inset-bottom','%dpx');" +
|
|
3567
3614
|
"root.style.setProperty('--safe-area-inset-left','%dpx');" +
|
|
3568
|
-
"root.style.setProperty('--safe-area-inset-right','%dpx');
|
|
3615
|
+
"root.style.setProperty('--safe-area-inset-right','%dpx');" +
|
|
3616
|
+
"return root.style.getPropertyValue('--safe-area-inset-top')?1:0;})();",
|
|
3569
3617
|
top,
|
|
3570
3618
|
bottom,
|
|
3571
3619
|
left,
|
|
3572
3620
|
right
|
|
3573
3621
|
);
|
|
3574
3622
|
_webView.post(() -> {
|
|
3575
|
-
if (_webView
|
|
3576
|
-
|
|
3623
|
+
if (_webView == null) {
|
|
3624
|
+
resetInjectedSafeAreaCssVariables();
|
|
3625
|
+
return;
|
|
3577
3626
|
}
|
|
3627
|
+
_webView.evaluateJavascript(script, (value) -> {
|
|
3628
|
+
boolean cacheStillHoldsTheseValues =
|
|
3629
|
+
injectedSafeAreaTop == top &&
|
|
3630
|
+
injectedSafeAreaBottom == bottom &&
|
|
3631
|
+
injectedSafeAreaLeft == left &&
|
|
3632
|
+
injectedSafeAreaRight == right;
|
|
3633
|
+
// The document did not take the variables, so let a later pass inject them again.
|
|
3634
|
+
// A newer injection may already have replaced them, in which case it owns the cache.
|
|
3635
|
+
if (!"1".equals(value) && cacheStillHoldsTheseValues) {
|
|
3636
|
+
resetInjectedSafeAreaCssVariables();
|
|
3637
|
+
}
|
|
3638
|
+
});
|
|
3578
3639
|
});
|
|
3579
3640
|
}
|
|
3580
3641
|
|
|
@@ -4307,6 +4368,60 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
4307
4368
|
return proxyHeaders;
|
|
4308
4369
|
}
|
|
4309
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
|
+
|
|
4310
4425
|
public void setUrl(String url) {
|
|
4311
4426
|
if (_webView == null) {
|
|
4312
4427
|
Log.w("InAppBrowser", "Cannot set URL - WebView is null");
|
|
@@ -5529,186 +5644,254 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
5529
5644
|
if (view == null || _webView == null) {
|
|
5530
5645
|
return null;
|
|
5531
5646
|
}
|
|
5532
|
-
if (!shouldUseNativeProxy()) {
|
|
5533
|
-
return null;
|
|
5534
|
-
}
|
|
5535
5647
|
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
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
|
+
}
|
|
5551
5664
|
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
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();
|
|
5558
5674
|
}
|
|
5559
|
-
Log.w("InAppBrowserProxy", "Ignoring legacy regex miss for bridge-backed request: " + originalUrl);
|
|
5560
|
-
return createCanceledResponse();
|
|
5561
|
-
}
|
|
5562
5675
|
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
requestHeaders = ProxyRequestSupport.mergeRequestHeaders(null, stored.headersJson);
|
|
5574
|
-
requestHeaders = ProxyRequestSupport.mergeMissingHeaders(requestHeaders, safeMarkerHeaders);
|
|
5575
|
-
} catch (JSONException error) {
|
|
5576
|
-
Log.e("InAppBrowserProxy", "Failed to parse stored proxy headers", error);
|
|
5577
|
-
return createCanceledResponse();
|
|
5578
|
-
}
|
|
5579
|
-
String initiatorUrl = request.getRequestHeaders().get("Referer");
|
|
5580
|
-
if (initiatorUrl == null || initiatorUrl.isBlank()) {
|
|
5581
|
-
initiatorUrl = getWebViewUrlOrFallback(
|
|
5582
|
-
_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()
|
|
5583
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
|
+
}
|
|
5584
5718
|
}
|
|
5585
|
-
|
|
5719
|
+
|
|
5720
|
+
NativeRequestContext requestContext = new NativeRequestContext(
|
|
5721
|
+
originalUrl,
|
|
5722
|
+
method,
|
|
5723
|
+
requestHeaders,
|
|
5724
|
+
base64Body,
|
|
5725
|
+
request.isForMainFrame(),
|
|
5726
|
+
credentialsMode
|
|
5727
|
+
);
|
|
5728
|
+
|
|
5586
5729
|
if (
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5730
|
+
ProxyRequestSupport.shouldLetWebViewHandleMissingBody(
|
|
5731
|
+
requestUrl,
|
|
5732
|
+
requestContext.method,
|
|
5733
|
+
requestContext.base64Body
|
|
5734
|
+
)
|
|
5590
5735
|
) {
|
|
5591
|
-
|
|
5592
|
-
}
|
|
5593
|
-
} else {
|
|
5594
|
-
if (!ProxyRequestSupport.shouldHandleNonBridgeRequest(_options, requestUrl)) {
|
|
5736
|
+
Log.w("InAppBrowserProxy", "Allowing WebView to handle request with uncaptured body: " + requestContext.url);
|
|
5595
5737
|
return null;
|
|
5596
5738
|
}
|
|
5597
5739
|
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
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();
|
|
5602
5765
|
}
|
|
5603
|
-
}
|
|
5604
|
-
|
|
5605
|
-
NativeRequestContext requestContext = new NativeRequestContext(
|
|
5606
|
-
originalUrl,
|
|
5607
|
-
method,
|
|
5608
|
-
requestHeaders,
|
|
5609
|
-
base64Body,
|
|
5610
|
-
request.isForMainFrame(),
|
|
5611
|
-
credentialsMode
|
|
5612
|
-
);
|
|
5613
|
-
|
|
5614
|
-
if (
|
|
5615
|
-
ProxyRequestSupport.shouldLetWebViewHandleMissingBody(requestUrl, requestContext.method, requestContext.base64Body)
|
|
5616
|
-
) {
|
|
5617
|
-
Log.w("InAppBrowserProxy", "Allowing WebView to handle request with uncaptured body: " + requestContext.url);
|
|
5618
|
-
return null;
|
|
5619
|
-
}
|
|
5620
5766
|
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
: findMatchingRule(_options.getOutboundProxyRules(), requestContext, null);
|
|
5643
|
-
|
|
5644
|
-
if (outboundRule != null && outboundRule.getAction() == NativeProxyRule.Action.CANCEL) {
|
|
5645
|
-
return createCanceledResponse();
|
|
5646
|
-
}
|
|
5647
|
-
|
|
5648
|
-
if (outboundRule != null && outboundRule.getAction() == NativeProxyRule.Action.DELEGATE_TO_JS) {
|
|
5649
|
-
String proxyId = UUID.randomUUID().toString();
|
|
5650
|
-
ProxiedRequest proxiedRequest = new ProxiedRequest();
|
|
5651
|
-
proxiedRequest.requestContext = requestContext;
|
|
5652
|
-
addProxiedRequest(proxyId, proxiedRequest);
|
|
5653
|
-
|
|
5654
|
-
String dialogId = instanceId != null ? instanceId : "";
|
|
5655
|
-
_options
|
|
5656
|
-
.getCallbacks()
|
|
5657
|
-
.proxyRequestEvent(
|
|
5658
|
-
proxyId,
|
|
5659
|
-
"outbound",
|
|
5660
|
-
requestContext.url,
|
|
5661
|
-
requestContext.method,
|
|
5662
|
-
serializeHeaders(requestContext.headers),
|
|
5663
|
-
requestContext.base64Body.isEmpty() ? null : requestContext.base64Body,
|
|
5664
|
-
null,
|
|
5665
|
-
null,
|
|
5666
|
-
null,
|
|
5667
|
-
dialogId
|
|
5668
|
-
);
|
|
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
|
+
);
|
|
5669
5788
|
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
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
|
+
}
|
|
5680
5803
|
} else {
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
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);
|
|
5686
5809
|
}
|
|
5810
|
+
} catch (InterruptedException error) {
|
|
5687
5811
|
removeProxiedRequest(proxyId);
|
|
5688
|
-
|
|
5812
|
+
Thread.currentThread().interrupt();
|
|
5813
|
+
Log.e("InAppBrowserProxy", "Semaphore wait error", error);
|
|
5814
|
+
return bridgeBackedRequest ? createCanceledResponse() : null;
|
|
5689
5815
|
}
|
|
5690
|
-
} catch (InterruptedException error) {
|
|
5691
|
-
removeProxiedRequest(proxyId);
|
|
5692
|
-
Thread.currentThread().interrupt();
|
|
5693
|
-
Log.e("InAppBrowserProxy", "Semaphore wait error", error);
|
|
5694
|
-
return bridgeBackedRequest ? createCanceledResponse() : null;
|
|
5695
5816
|
}
|
|
5696
|
-
}
|
|
5697
5817
|
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
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
|
+
}
|
|
5705
5826
|
}
|
|
5706
|
-
}
|
|
5707
5827
|
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
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
|
+
|
|
5712
5895
|
RedirectReplayResult redirectReplay;
|
|
5713
5896
|
try {
|
|
5714
5897
|
redirectReplay = followRedirectForWebView(requestContext, nativeResponse, redirectsFollowed);
|
|
@@ -5724,69 +5907,9 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
5724
5907
|
}
|
|
5725
5908
|
return createWebResourceResponseOrFallback(nativeResponse, bridgeBackedRequest, requestContext.url);
|
|
5726
5909
|
}
|
|
5727
|
-
if (inboundRule.getAction() == NativeProxyRule.Action.CANCEL) {
|
|
5728
|
-
return createCanceledResponse();
|
|
5729
|
-
}
|
|
5730
|
-
|
|
5731
|
-
String proxyId = UUID.randomUUID().toString();
|
|
5732
|
-
ProxiedRequest proxiedRequest = new ProxiedRequest();
|
|
5733
|
-
proxiedRequest.requestContext = requestContext;
|
|
5734
|
-
proxiedRequest.nativeResponse = nativeResponse;
|
|
5735
|
-
addProxiedRequest(proxyId, proxiedRequest);
|
|
5736
|
-
|
|
5737
|
-
String dialogId = instanceId != null ? instanceId : "";
|
|
5738
|
-
_options
|
|
5739
|
-
.getCallbacks()
|
|
5740
|
-
.proxyRequestEvent(
|
|
5741
|
-
proxyId,
|
|
5742
|
-
"inbound",
|
|
5743
|
-
requestContext.url,
|
|
5744
|
-
requestContext.method,
|
|
5745
|
-
serializeHeaders(requestContext.headers),
|
|
5746
|
-
requestContext.base64Body.isEmpty() ? null : requestContext.base64Body,
|
|
5747
|
-
nativeResponse.statusCode,
|
|
5748
|
-
serializeHeaders(nativeResponse.headers),
|
|
5749
|
-
Base64.encodeToString(nativeResponse.bodyBytes, Base64.NO_WRAP),
|
|
5750
|
-
dialogId
|
|
5751
|
-
);
|
|
5752
|
-
|
|
5753
|
-
try {
|
|
5754
|
-
if (proxiedRequest.semaphore.tryAcquire(1, 10, TimeUnit.SECONDS)) {
|
|
5755
|
-
if (proxiedRequest.canceled) {
|
|
5756
|
-
return createCanceledResponse();
|
|
5757
|
-
}
|
|
5758
|
-
if (proxiedRequest.response != null) {
|
|
5759
|
-
return proxiedRequest.response;
|
|
5760
|
-
}
|
|
5761
|
-
if (proxiedRequest.nativeResponse != null) {
|
|
5762
|
-
nativeResponse = proxiedRequest.nativeResponse;
|
|
5763
|
-
}
|
|
5764
|
-
} else {
|
|
5765
|
-
synchronized (proxiedRequest) {
|
|
5766
|
-
proxiedRequest.timedOut = true;
|
|
5767
|
-
}
|
|
5768
|
-
removeProxiedRequest(proxyId);
|
|
5769
|
-
}
|
|
5770
|
-
} catch (InterruptedException error) {
|
|
5771
|
-
Thread.currentThread().interrupt();
|
|
5772
|
-
Log.e("InAppBrowserProxy", "Semaphore wait error", error);
|
|
5773
|
-
}
|
|
5774
|
-
|
|
5775
|
-
RedirectReplayResult redirectReplay;
|
|
5776
|
-
try {
|
|
5777
|
-
redirectReplay = followRedirectForWebView(requestContext, nativeResponse, redirectsFollowed);
|
|
5778
|
-
} catch (IOException error) {
|
|
5779
|
-
Log.e("InAppBrowserProxy", "Native redirect replay failed for: " + requestContext.url, error);
|
|
5780
|
-
return createProxiedNativeFailureResponse(requestContext.url, bridgeBackedRequest, error);
|
|
5781
|
-
}
|
|
5782
|
-
if (redirectReplay != null) {
|
|
5783
|
-
requestContext = redirectReplay.requestContext;
|
|
5784
|
-
nativeResponse = redirectReplay.responseData;
|
|
5785
|
-
redirectsFollowed++;
|
|
5786
|
-
continue;
|
|
5787
|
-
}
|
|
5788
|
-
return createWebResourceResponseOrFallback(nativeResponse, bridgeBackedRequest, requestContext.url);
|
|
5789
5910
|
}
|
|
5911
|
+
|
|
5912
|
+
return interceptBundledAssetRequest(request);
|
|
5790
5913
|
}
|
|
5791
5914
|
|
|
5792
5915
|
@Override
|
|
@@ -5953,6 +6076,10 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
5953
6076
|
_options.getCallbacks().pageLoaded();
|
|
5954
6077
|
stopReloadGesture();
|
|
5955
6078
|
injectJavaScriptInterface();
|
|
6079
|
+
// The freshly parsed document carries no safe-area variables, whatever was
|
|
6080
|
+
// injected into the previous one, so drop the cache and serve them again.
|
|
6081
|
+
resetInjectedSafeAreaCssVariables();
|
|
6082
|
+
reapplyInsetsFromWindowRoot();
|
|
5956
6083
|
|
|
5957
6084
|
// Inject Google Pay polyfills if enabled
|
|
5958
6085
|
if (_options.getEnableGooglePaySupport()) {
|