@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.
- package/android/build.gradle +39 -1
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/BundledAssetSupport.java +116 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/CapgoInAppBrowserPlugin.java +1 -1
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +39 -7
- package/ios/Sources/InAppBrowserPlugin/InAppBrowserPlugin.swift +1 -1
- package/package.json +1 -1
package/android/build.gradle
CHANGED
|
@@ -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 =
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
3382
|
-
|
|
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
|
-
|
|
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.
|
|
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] = [
|