@capgo/capacitor-inappbrowser 8.21.0 → 8.21.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.
@@ -3,7 +3,45 @@ ext {
3
3
  androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
4
  androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
5
  androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
- androidxBrowserVersion = project.hasProperty('androidxBrowserVersion') ? rootProject.ext.androidxBrowserVersion : '1.9.0'
6
+ androidxBrowserVersion = {
7
+ def requested = project.hasProperty('androidxBrowserVersion') ? rootProject.ext.androidxBrowserVersion : '1.9.0'
8
+ def minimum = '1.9.0'
9
+ def parseVersionParts = { String version ->
10
+ version.tokenize('.').collect { part ->
11
+ def match = (part =~ /^(\d+)/)
12
+ match.find() ? match.group(1).toInteger() : 0
13
+ }
14
+ }
15
+ def stripPrerelease = { String version ->
16
+ def dashIndex = version.indexOf('-')
17
+ dashIndex > 0 ? version.substring(0, dashIndex) : version
18
+ }
19
+ def isAtLeast = { String version, String baseline ->
20
+ def versionParts = parseVersionParts(version)
21
+ def baselineParts = parseVersionParts(baseline)
22
+ def length = Math.max(versionParts.size(), baselineParts.size())
23
+ for (int index = 0; index < length; index++) {
24
+ int versionPart = index < versionParts.size() ? versionParts[index] : 0
25
+ int baselinePart = index < baselineParts.size() ? baselineParts[index] : 0
26
+ if (versionPart != baselinePart) {
27
+ return versionPart >= baselinePart
28
+ }
29
+ }
30
+ return true
31
+ }
32
+ def requestedBase = stripPrerelease(requested)
33
+ if (!isAtLeast(requestedBase, minimum)) {
34
+ throw new GradleException(
35
+ "androidxBrowserVersion must be >= ${minimum} (required for Custom Tabs APIs such as setUrlBarHidingEnabled and setEphemeralBrowsingEnabled), got: ${requested}"
36
+ )
37
+ }
38
+ if (requested.contains('-') && isAtLeast(requestedBase, minimum) && isAtLeast(minimum, requestedBase)) {
39
+ throw new GradleException(
40
+ "androidxBrowserVersion must be >= ${minimum} stable (prerelease not accepted at the minimum version), got: ${requested}"
41
+ )
42
+ }
43
+ return requested
44
+ }()
7
45
  }
8
46
 
9
47
  buildscript {
@@ -9,6 +9,9 @@ import com.getcapacitor.Bridge;
9
9
  import java.io.IOException;
10
10
  import java.io.InputStream;
11
11
  import java.net.URI;
12
+ import java.net.URLDecoder;
13
+ import java.nio.charset.StandardCharsets;
14
+ import java.util.ArrayList;
12
15
  import java.util.Arrays;
13
16
  import java.util.Collections;
14
17
  import java.util.HashSet;
@@ -81,10 +84,123 @@ final class BundledAssetSupport {
81
84
  return resolve(url, localConfig);
82
85
  }
83
86
 
87
+ static boolean isFileUrl(String url) {
88
+ if (isBlank(url)) {
89
+ return false;
90
+ }
91
+
92
+ try {
93
+ return "file".equalsIgnoreCase(URI.create(url.trim()).getScheme());
94
+ } catch (IllegalArgumentException error) {
95
+ return url.trim().regionMatches(true, 0, "file:", 0, "file:".length());
96
+ }
97
+ }
98
+
99
+ static boolean isTrustedBundledFileUrl(String url) {
100
+ if (!isFileUrl(url)) {
101
+ return false;
102
+ }
103
+
104
+ String path = fileUrlPath(url.trim());
105
+ if (path == null || path.isEmpty()) {
106
+ return false;
107
+ }
108
+
109
+ String decoded = percentDecodePath(path);
110
+ if (decoded == null) {
111
+ return false;
112
+ }
113
+
114
+ String canonical = canonicalizeAbsolutePath(decoded);
115
+ if (canonical == null) {
116
+ return false;
117
+ }
118
+
119
+ return "/android_asset".equals(canonical) || canonical.startsWith("/android_asset/");
120
+ }
121
+
122
+ private static String percentDecodePath(String path) {
123
+ String decoded = path;
124
+ for (int iteration = 0; iteration < 3; iteration++) {
125
+ try {
126
+ String next = URLDecoder.decode(decoded, StandardCharsets.UTF_8);
127
+ if (next.equals(decoded)) {
128
+ break;
129
+ }
130
+ decoded = next;
131
+ } catch (IllegalArgumentException error) {
132
+ return null;
133
+ }
134
+ }
135
+ return decoded;
136
+ }
137
+
138
+ private static String canonicalizeAbsolutePath(String path) {
139
+ if (path == null || path.isEmpty()) {
140
+ return null;
141
+ }
142
+
143
+ String normalized = path.startsWith("/") ? path : "/" + path;
144
+ if (normalized.indexOf('\\') >= 0) {
145
+ return null;
146
+ }
147
+ String[] rawSegments = normalized.split("/", -1);
148
+ ArrayList<String> segments = new ArrayList<>();
149
+ for (String segment : rawSegments) {
150
+ if (segment.isEmpty() || ".".equals(segment)) {
151
+ continue;
152
+ }
153
+ if ("..".equals(segment)) {
154
+ if (segments.isEmpty()) {
155
+ return null;
156
+ }
157
+ segments.remove(segments.size() - 1);
158
+ continue;
159
+ }
160
+ segments.add(segment);
161
+ }
162
+
163
+ if (segments.isEmpty()) {
164
+ return "/";
165
+ }
166
+
167
+ StringBuilder builder = new StringBuilder();
168
+ for (String segment : segments) {
169
+ builder.append('/').append(segment);
170
+ }
171
+ return builder.toString();
172
+ }
173
+
174
+ private static String fileUrlPath(String url) {
175
+ try {
176
+ return URI.create(url).getPath();
177
+ } catch (IllegalArgumentException error) {
178
+ if (!url.regionMatches(true, 0, "file:", 0, "file:".length())) {
179
+ return null;
180
+ }
181
+
182
+ String remainder = url.substring("file:".length());
183
+ if (remainder.startsWith("//")) {
184
+ remainder = remainder.substring(2);
185
+ if (remainder.startsWith("/")) {
186
+ return remainder;
187
+ }
188
+ int slash = remainder.indexOf('/');
189
+ return slash >= 0 ? remainder.substring(slash) : "/" + remainder;
190
+ }
191
+
192
+ return remainder.startsWith("/") ? remainder : "/" + remainder;
193
+ }
194
+ }
195
+
84
196
  static Resolution resolve(String url, LocalConfig localConfig) {
85
197
  String trimmed = url == null ? "" : url.trim();
86
198
  String navigationScheme = assetLoaderScheme(localConfig);
87
199
 
200
+ if (isFileUrl(trimmed) && !isTrustedBundledFileUrl(trimmed)) {
201
+ return null;
202
+ }
203
+
88
204
  if (isRelativeBundledPath(trimmed)) {
89
205
  String path = normalizeBundledPath(trimmed);
90
206
  if (path == null) {
@@ -2152,7 +2152,7 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
2152
2152
  openSecureWindowRedirectUri = redirectUri;
2153
2153
 
2154
2154
  CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
2155
- builder.enableUrlBarHiding();
2155
+ builder.setUrlBarHidingEnabled(true);
2156
2156
  builder.setShareState(CustomTabsIntent.SHARE_STATE_OFF);
2157
2157
  builder.setEphemeralBrowsingEnabled(Boolean.TRUE.equals(call.getBoolean("prefersEphemeralWebBrowserSession", false)));
2158
2158
  CustomTabsIntent customTabsIntent = builder.build();
@@ -81,6 +81,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout;
81
81
  import androidx.core.content.FileProvider;
82
82
  import androidx.core.content.res.ResourcesCompat;
83
83
  import androidx.core.graphics.Insets;
84
+ import androidx.core.graphics.drawable.DrawableCompat;
84
85
  import androidx.core.view.ViewCompat;
85
86
  import androidx.core.view.WindowInsetsCompat;
86
87
  import androidx.core.view.WindowInsetsControllerCompat;
@@ -539,7 +540,10 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
539
540
  }
540
541
 
541
542
  @Override
542
- public void onLowMemory() {}
543
+ @SuppressWarnings("deprecation")
544
+ public void onLowMemory() {
545
+ onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
546
+ }
543
547
 
544
548
  @Override
545
549
  public void onTrimMemory(int level) {}
@@ -2451,11 +2455,10 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
2451
2455
  _webView.getSettings().setAllowFileAccess(true);
2452
2456
  _webView.getSettings().setLoadWithOverviewMode(true);
2453
2457
  _webView.getSettings().setUseWideViewPort(true);
2454
- _webView.getSettings().setAllowFileAccessFromFileURLs(true);
2455
- _webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
2458
+ applyTrustedBundledFileCrossOriginSettings(_options != null ? _options.getUrl() : null);
2456
2459
  if (!_options.getPersistWebViewData()) {
2457
- _webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
2458
2460
  _webView.getSettings().setDatabaseEnabled(false);
2461
+ _webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
2459
2462
  _webView.clearCache(true);
2460
2463
  _webView.clearHistory();
2461
2464
  _webView.clearFormData();
@@ -3378,8 +3381,19 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
3378
3381
  mediaFullscreenWindow.restore();
3379
3382
  mediaFullscreenWindow = null;
3380
3383
  }
3381
- if (nativeFullscreenWindow != null) nativeFullscreenWindow.enter();
3382
- else reapplyInsetsFromWindowRoot();
3384
+ if (nativeFullscreenWindow != null) {
3385
+ nativeFullscreenWindow.enter();
3386
+ } else {
3387
+ reapplyInsetsFromWindowRoot();
3388
+ if (SystemUiChromeSupport.requiresEdgeToEdgeChrome(Build.VERSION.SDK_INT)) {
3389
+ boolean isBlankToolbar = _options != null && TextUtils.equals(_options.getToolbarType(), "blank");
3390
+ if (isBlankToolbar) {
3391
+ configureBlankToolbarLayout();
3392
+ } else {
3393
+ refreshEdgeToEdgeChrome();
3394
+ }
3395
+ }
3396
+ }
3383
3397
 
3384
3398
  unregisterCustomFullscreenBackHandler();
3385
3399
 
@@ -4984,6 +4998,16 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
4984
4998
  }
4985
4999
  }
4986
5000
 
5001
+ private void applyTrustedBundledFileCrossOriginSettings(String url) {
5002
+ if (_webView == null) {
5003
+ return;
5004
+ }
5005
+
5006
+ boolean trustedBundledFile = BundledAssetSupport.isTrustedBundledFileUrl(url);
5007
+ _webView.getSettings().setAllowFileAccessFromFileURLs(trustedBundledFile);
5008
+ _webView.getSettings().setAllowUniversalAccessFromFileURLs(trustedBundledFile);
5009
+ }
5010
+
4987
5011
  private WebResourceResponse interceptBundledAssetRequest(WebResourceRequest request) {
4988
5012
  final WebViewAssetLoader loader;
4989
5013
  synchronized (bundledAssetLoaderLock) {
@@ -5012,6 +5036,13 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
5012
5036
  return;
5013
5037
  }
5014
5038
 
5039
+ if (BundledAssetSupport.isFileUrl(url) && !BundledAssetSupport.isTrustedBundledFileUrl(url)) {
5040
+ Log.e("InAppBrowser", "Rejected untrusted file URL");
5041
+ return;
5042
+ }
5043
+
5044
+ applyTrustedBundledFileCrossOriginSettings(url);
5045
+
5015
5046
  try {
5016
5047
  if (loadHtmlDataUrlIfNeeded(url)) {
5017
5048
  return;
@@ -5156,7 +5187,7 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
5156
5187
  return null;
5157
5188
  }
5158
5189
  drawable = drawable.mutate();
5159
- drawable.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN);
5190
+ DrawableCompat.setTint(drawable, iconColor);
5160
5191
  drawable.setBounds(0, 0, width, height);
5161
5192
  return drawable;
5162
5193
  } catch (Exception e) {
@@ -6586,6 +6617,7 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
6586
6617
  if (view == null || _webView == null) {
6587
6618
  return;
6588
6619
  }
6620
+ applyTrustedBundledFileCrossOriginSettings(url);
6589
6621
  if (!isReload) {
6590
6622
  _options.getCallbacks().urlChangeEvent(url);
6591
6623
  }
@@ -446,7 +446,7 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
446
446
  case aware = "AWARE"
447
447
  case fakeVisible = "FAKE_VISIBLE"
448
448
  }
449
- private let pluginVersion: String = "8.21.0"
449
+ private let pluginVersion: String = "8.21.1"
450
450
  public let identifier = "CapgoInAppBrowserPlugin"
451
451
  public let jsName = "CapgoInAppBrowser"
452
452
  public let pluginMethods: [CAPPluginMethod] = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-inappbrowser",
3
- "version": "8.21.0",
3
+ "version": "8.21.1",
4
4
  "description": "Capacitor plugin in app browser",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",