@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 CHANGED
@@ -591,6 +591,24 @@ And in `AndroidManifest.xml`:
591
591
  </activity>
592
592
  ```
593
593
 
594
+ ### Bundled local assets
595
+
596
+ `openWebView()` can load HTML, JavaScript, CSS, and other files from the app `public/` folder without starting a local HTTP server. Pass a relative path and the plugin resolves it to the Capacitor local URL for the current platform.
597
+
598
+ ```typescript
599
+ import { InAppBrowser } from '@capgo/capacitor-inappbrowser';
600
+
601
+ await InAppBrowser.openWebView({
602
+ url: '/index.html',
603
+ });
604
+ ```
605
+
606
+ - iOS resolves to `capacitor://localhost/...` and serves files from the packaged `public/` directory.
607
+ - Android resolves to `https://localhost/...` and intercepts requests with `WebViewAssetLoader`, matching Capacitor's main WebView behavior.
608
+ - Remote `http://` and `https://` URLs are unchanged.
609
+
610
+ `open()` uses the system browser (Safari / Custom Tabs) and still requires a real remote URL.
611
+
594
612
  ## API
595
613
 
596
614
  <docgen-index>
@@ -1469,7 +1487,7 @@ See the README section "openSecureWindow (OAuth)" for full setup examples.
1469
1487
 
1470
1488
  | Prop | Type | Description | Default | Since |
1471
1489
  | ---------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
1472
- | **`url`** | <code>string</code> | Target URL to load. | | 0.1.0 |
1490
+ | **`url`** | <code>string</code> | Target URL to load. Remote `http://` and `https://` URLs are loaded as-is. Relative bundled paths such as `/index.html` are not supported; use `openWebView()` instead. | | 0.1.0 |
1473
1491
  | **`isPresentAfterPageLoad`** | <code>boolean</code> | if true, the browser will be presented after the page is loaded, if false, the browser will be presented immediately. | | 0.1.0 |
1474
1492
  | **`preventDeeplink`** | <code>boolean</code> | if true the deeplink will not be opened, if false the deeplink will be opened when clicked on the link | | 0.1.0 |
1475
1493
  | **`toolbarColor`** | <code>string</code> | Toolbar background color in hex format (e.g., "#1A1A2E"). Applied to both light and dark color schemes. Also sets the navigation bar color to match. **Android only** — ignored on iOS. | | 8.2.0 |
@@ -1556,7 +1574,7 @@ See the README section "openSecureWindow (OAuth)" for full setup examples.
1556
1574
 
1557
1575
  | Prop | Type | Description | Default | Since |
1558
1576
  | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ------ |
1559
- | **`url`** | <code>string</code> | Target URL to load. | | 0.1.0 |
1577
+ | **`url`** | <code>string</code> | Target URL to load. Remote `http://` and `https://` URLs are loaded as-is. To open bundled web assets from the app bundle without running a local HTTP server, pass a relative path such as `/index.html` or `assets/page.html`. The plugin resolves it to the Capacitor local URL for the current platform (defaults: `capacitor://localhost/...` on iOS, `https://localhost/...` on Android; actual scheme and host follow the app's configured Capacitor local URL) and serves files from the packaged `public/` directory, or `www/` on iOS when `public/` is absent. | | 0.1.0 |
1560
1578
  | **`headers`** | <code><a href="#headers">Headers</a></code> | <a href="#headers">Headers</a> to send with the request. | | 0.1.0 |
1561
1579
  | **`customUserAgent`** | <code>string</code> | Custom User-Agent string for the webview. When set, replaces the system default webview User-Agent on iOS and Android. Takes precedence over a `User-Agent` entry in `headers`. | | 8.13.0 |
1562
1580
  | **`credentials`** | <code><a href="#credentials">Credentials</a></code> | <a href="#credentials">Credentials</a> to send with the request and all subsequent requests for the same host. | | 6.1.0 |
@@ -0,0 +1,397 @@
1
+ package ee.forgr.capacitor_inappbrowser;
2
+
3
+ import android.content.Context;
4
+ import android.content.res.AssetManager;
5
+ import android.webkit.MimeTypeMap;
6
+ import android.webkit.WebResourceResponse;
7
+ import androidx.webkit.WebViewAssetLoader;
8
+ import com.getcapacitor.Bridge;
9
+ import java.io.IOException;
10
+ import java.io.InputStream;
11
+ import java.net.URI;
12
+ import java.util.Arrays;
13
+ import java.util.Collections;
14
+ import java.util.HashSet;
15
+ import java.util.Locale;
16
+ import java.util.Set;
17
+
18
+ final class BundledAssetSupport {
19
+
20
+ static final class LocalConfig {
21
+
22
+ final String scheme;
23
+ final String host;
24
+
25
+ LocalConfig(String scheme, String host) {
26
+ this.scheme = scheme;
27
+ this.host = host;
28
+ }
29
+ }
30
+
31
+ static final class Resolution {
32
+
33
+ final String url;
34
+ final boolean needsAssetLoader;
35
+
36
+ Resolution(String url) {
37
+ this(url, false);
38
+ }
39
+
40
+ Resolution(String url, boolean needsAssetLoader) {
41
+ this.url = url;
42
+ this.needsAssetLoader = needsAssetLoader;
43
+ }
44
+ }
45
+
46
+ private static final String[] RESERVED_WEB_SCHEMES = { "http", "https" };
47
+ private static final String PROXY_BRIDGE_MARKER_PATH = "/_capgo_proxy_";
48
+
49
+ private BundledAssetSupport() {}
50
+
51
+ static LocalConfig parseLocalConfig(String localUrl) {
52
+ if (isBlank(localUrl)) {
53
+ return null;
54
+ }
55
+
56
+ try {
57
+ URI uri = URI.create(localUrl.endsWith("/") ? localUrl : localUrl + "/");
58
+ String scheme = uri.getScheme();
59
+ String host = uri.getHost();
60
+ if (scheme == null || host == null) {
61
+ return null;
62
+ }
63
+ return new LocalConfig(scheme.toLowerCase(Locale.ROOT), host.toLowerCase(Locale.ROOT));
64
+ } catch (IllegalArgumentException error) {
65
+ return null;
66
+ }
67
+ }
68
+
69
+ static String assetLoaderScheme(LocalConfig localConfig) {
70
+ if ("http".equals(localConfig.scheme) || "https".equals(localConfig.scheme)) {
71
+ return localConfig.scheme;
72
+ }
73
+ return "https";
74
+ }
75
+
76
+ static Resolution resolve(String url, Bridge bridge) {
77
+ LocalConfig localConfig = parseLocalConfig(bridge != null ? bridge.getLocalUrl() : null);
78
+ if (localConfig == null) {
79
+ localConfig = new LocalConfig("https", "localhost");
80
+ }
81
+ return resolve(url, localConfig);
82
+ }
83
+
84
+ static Resolution resolve(String url, LocalConfig localConfig) {
85
+ String trimmed = url == null ? "" : url.trim();
86
+ String navigationScheme = assetLoaderScheme(localConfig);
87
+
88
+ if (isRelativeBundledPath(trimmed)) {
89
+ String path = normalizeBundledPath(trimmed);
90
+ if (path == null) {
91
+ return null;
92
+ }
93
+ return new Resolution(navigationScheme + "://" + localConfig.host + path, true);
94
+ }
95
+
96
+ if (isBundledLocalUrl(trimmed, localConfig)) {
97
+ String rewritten = rewriteBundledLocalUrl(trimmed, localConfig, navigationScheme);
98
+ return new Resolution(rewritten != null ? rewritten : trimmed, true);
99
+ }
100
+
101
+ return new Resolution(trimmed, false);
102
+ }
103
+
104
+ static boolean isBundledLocalUrl(String url, LocalConfig localConfig) {
105
+ if (!isAbsoluteUrl(url)) {
106
+ return false;
107
+ }
108
+
109
+ try {
110
+ URI uri = URI.create(url);
111
+ String scheme = uri.getScheme();
112
+ String host = uri.getHost();
113
+ if (scheme == null || host == null) {
114
+ return false;
115
+ }
116
+
117
+ if (!host.equalsIgnoreCase(localConfig.host)) {
118
+ return false;
119
+ }
120
+
121
+ if (uri.getPort() != -1) {
122
+ return false;
123
+ }
124
+
125
+ String normalizedScheme = scheme.toLowerCase(Locale.ROOT);
126
+ if (normalizedScheme.equalsIgnoreCase(localConfig.scheme) || normalizedScheme.equals(assetLoaderScheme(localConfig))) {
127
+ return true;
128
+ }
129
+
130
+ return isReservedWebScheme(normalizedScheme) && isReservedWebScheme(localConfig.scheme);
131
+ } catch (IllegalArgumentException error) {
132
+ return false;
133
+ }
134
+ }
135
+
136
+ static boolean isRelativeBundledPath(String url) {
137
+ if (isBlank(url)) {
138
+ return false;
139
+ }
140
+ return !isAbsoluteUrl(url);
141
+ }
142
+
143
+ private static final Set<String> BUNDLED_ASSET_EXTENSIONS = Collections.unmodifiableSet(
144
+ new HashSet<>(
145
+ Arrays.asList(
146
+ "html",
147
+ "htm",
148
+ "js",
149
+ "css",
150
+ "json",
151
+ "xml",
152
+ "svg",
153
+ "png",
154
+ "jpg",
155
+ "jpeg",
156
+ "gif",
157
+ "webp",
158
+ "woff",
159
+ "woff2",
160
+ "ttf",
161
+ "map"
162
+ )
163
+ )
164
+ );
165
+
166
+ static boolean isLikelyBundledRelativePath(String url) {
167
+ if (!isRelativeBundledPath(url)) {
168
+ return false;
169
+ }
170
+
171
+ String trimmed = stripPathQueryAndFragment(url.trim());
172
+ if (finalPathComponentStartsWithDot(trimmed)) {
173
+ return false;
174
+ }
175
+
176
+ if (trimmed.startsWith("/") || trimmed.contains("/")) {
177
+ return true;
178
+ }
179
+
180
+ int dot = trimmed.lastIndexOf('.');
181
+ if (dot < 0) {
182
+ return true;
183
+ }
184
+ if (dot == 0) {
185
+ return false;
186
+ }
187
+
188
+ String extension = trimmed.substring(dot + 1).toLowerCase(Locale.ROOT);
189
+ return BUNDLED_ASSET_EXTENSIONS.contains(extension);
190
+ }
191
+
192
+ private static boolean isBlank(String value) {
193
+ if (value == null || value.isEmpty()) {
194
+ return true;
195
+ }
196
+
197
+ for (int index = 0; index < value.length(); index++) {
198
+ if (!Character.isWhitespace(value.charAt(index))) {
199
+ return false;
200
+ }
201
+ }
202
+
203
+ return true;
204
+ }
205
+
206
+ private static boolean finalPathComponentStartsWithDot(String path) {
207
+ int lastSlash = path.lastIndexOf('/');
208
+ String finalComponent = lastSlash >= 0 ? path.substring(lastSlash + 1) : path;
209
+ return !finalComponent.isEmpty() && finalComponent.charAt(0) == '.';
210
+ }
211
+
212
+ private static String stripPathQueryAndFragment(String path) {
213
+ int query = path.indexOf('?');
214
+ if (query >= 0) {
215
+ path = path.substring(0, query);
216
+ }
217
+ int fragment = path.indexOf('#');
218
+ if (fragment >= 0) {
219
+ path = path.substring(0, fragment);
220
+ }
221
+ return path;
222
+ }
223
+
224
+ static String normalizeBundledPath(String path) {
225
+ String trimmed = path == null ? "" : path.trim();
226
+ if (trimmed.isEmpty() || "/".equals(trimmed)) {
227
+ return "/";
228
+ }
229
+
230
+ if (containsPathTraversal(trimmed)) {
231
+ return null;
232
+ }
233
+
234
+ return trimmed.startsWith("/") ? trimmed : "/" + trimmed;
235
+ }
236
+
237
+ static boolean isProxyBridgeMarkerPath(String path) {
238
+ return PROXY_BRIDGE_MARKER_PATH.equals(path);
239
+ }
240
+
241
+ static WebViewAssetLoader createAssetLoader(Context context, String hostname, String scheme) {
242
+ WebViewAssetLoader.Builder builder = new WebViewAssetLoader.Builder()
243
+ .setDomain(hostname)
244
+ .addPathHandler("/", new PublicAssetsPathHandler(context.getAssets()));
245
+ if ("http".equalsIgnoreCase(scheme)) {
246
+ builder.setHttpAllowed(true);
247
+ }
248
+ return builder.build();
249
+ }
250
+
251
+ static String mimeTypeForPath(String path) {
252
+ String extension = MimeTypeMap.getFileExtensionFromUrl(path);
253
+ if (extension != null && !extension.isEmpty()) {
254
+ String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase(Locale.ROOT));
255
+ if (mimeType != null) {
256
+ return mimeType;
257
+ }
258
+ }
259
+ return extension == null || extension.isEmpty() ? "text/html" : "application/octet-stream";
260
+ }
261
+
262
+ static String encodingForMimeType(String mimeType) {
263
+ if (mimeType == null || mimeType.isEmpty()) {
264
+ return null;
265
+ }
266
+
267
+ String normalized = mimeType.toLowerCase(Locale.ROOT);
268
+ if (normalized.startsWith("text/")) {
269
+ return "utf-8";
270
+ }
271
+ if (
272
+ normalized.contains("javascript") ||
273
+ normalized.contains("json") ||
274
+ normalized.contains("xml") ||
275
+ normalized.endsWith("+json") ||
276
+ normalized.endsWith("+xml")
277
+ ) {
278
+ return "utf-8";
279
+ }
280
+ return null;
281
+ }
282
+
283
+ private static WebResourceResponse notFoundResponse(String path) {
284
+ return new WebResourceResponse("text/plain", "utf-8", 404, "Not Found", null, new java.io.ByteArrayInputStream(new byte[0]));
285
+ }
286
+
287
+ private static boolean isReservedWebScheme(String scheme) {
288
+ for (String reservedScheme : RESERVED_WEB_SCHEMES) {
289
+ if (reservedScheme.equals(scheme)) {
290
+ return true;
291
+ }
292
+ }
293
+ return false;
294
+ }
295
+
296
+ private static boolean isAbsoluteUrl(String url) {
297
+ if (url.startsWith("//")) {
298
+ return true;
299
+ }
300
+
301
+ int separatorIndex = url.indexOf(':');
302
+ if (separatorIndex <= 0) {
303
+ return false;
304
+ }
305
+
306
+ for (int index = 0; index < separatorIndex; index++) {
307
+ char character = url.charAt(index);
308
+ if (!Character.isLetterOrDigit(character) && character != '+' && character != '-' && character != '.') {
309
+ return false;
310
+ }
311
+ }
312
+
313
+ return Character.isLetter(url.charAt(0));
314
+ }
315
+
316
+ private static boolean containsPathTraversal(String path) {
317
+ for (String segment : path.split("/")) {
318
+ if ("..".equals(segment)) {
319
+ return true;
320
+ }
321
+ }
322
+ return false;
323
+ }
324
+
325
+ private static String rewriteBundledLocalUrl(String url, LocalConfig localConfig, String navigationScheme) {
326
+ try {
327
+ URI uri = URI.create(url);
328
+ String scheme = uri.getScheme();
329
+ String host = uri.getHost();
330
+ if (scheme == null || host == null || !host.equalsIgnoreCase(localConfig.host)) {
331
+ return null;
332
+ }
333
+ if (scheme.equalsIgnoreCase(navigationScheme)) {
334
+ return null;
335
+ }
336
+ return new URI(
337
+ navigationScheme,
338
+ uri.getUserInfo(),
339
+ host,
340
+ uri.getPort(),
341
+ uri.getPath(),
342
+ uri.getQuery(),
343
+ uri.getFragment()
344
+ ).toString();
345
+ } catch (Exception error) {
346
+ return null;
347
+ }
348
+ }
349
+
350
+ private static String mapToAssetPath(String path) {
351
+ if (containsPathTraversal(path)) {
352
+ return null;
353
+ }
354
+
355
+ String normalizedPath = path == null || path.isEmpty() ? "/" : path;
356
+ if (!normalizedPath.startsWith("/")) {
357
+ normalizedPath = "/" + normalizedPath;
358
+ }
359
+
360
+ int lastSlash = normalizedPath.lastIndexOf('/');
361
+ String fileName = lastSlash >= 0 ? normalizedPath.substring(lastSlash + 1) : normalizedPath;
362
+ if (!fileName.contains(".")) {
363
+ return "public/index.html";
364
+ }
365
+
366
+ return "public" + normalizedPath;
367
+ }
368
+
369
+ private static final class PublicAssetsPathHandler implements WebViewAssetLoader.PathHandler {
370
+
371
+ private final AssetManager assetManager;
372
+
373
+ PublicAssetsPathHandler(AssetManager assetManager) {
374
+ this.assetManager = assetManager;
375
+ }
376
+
377
+ @Override
378
+ public WebResourceResponse handle(String path) {
379
+ if (isProxyBridgeMarkerPath(path)) {
380
+ return null;
381
+ }
382
+
383
+ String assetPath = mapToAssetPath(path);
384
+ if (assetPath == null) {
385
+ return null;
386
+ }
387
+
388
+ try {
389
+ InputStream stream = assetManager.open(assetPath);
390
+ String mimeType = mimeTypeForPath(path);
391
+ return new WebResourceResponse(mimeType, encodingForMimeType(mimeType), stream);
392
+ } catch (IOException error) {
393
+ return notFoundResponse(path);
394
+ }
395
+ }
396
+ }
397
+ }
@@ -32,6 +32,7 @@ import androidx.browser.customtabs.CustomTabsIntent;
32
32
  import androidx.browser.customtabs.CustomTabsServiceConnection;
33
33
  import androidx.browser.customtabs.CustomTabsSession;
34
34
  import androidx.core.content.ContextCompat;
35
+ import com.getcapacitor.Bridge;
35
36
  import com.getcapacitor.JSArray;
36
37
  import com.getcapacitor.JSObject;
37
38
  import com.getcapacitor.Plugin;
@@ -358,6 +359,26 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
358
359
  return dialog;
359
360
  }
360
361
 
362
+ private boolean applyBundledAssetOptions(Options options, String url) {
363
+ Bridge bridge = getBridge();
364
+ BundledAssetSupport.Resolution resolution = BundledAssetSupport.resolve(url, bridge);
365
+ if (resolution == null) {
366
+ return false;
367
+ }
368
+ options.setUrl(resolution.url);
369
+ if (resolution.needsAssetLoader) {
370
+ BundledAssetSupport.LocalConfig localConfig = BundledAssetSupport.parseLocalConfig(
371
+ bridge != null ? bridge.getLocalUrl() : null
372
+ );
373
+ options.setBundledAssetHost(localConfig != null ? localConfig.host : "localhost");
374
+ options.setBundledAssetScheme(localConfig != null ? BundledAssetSupport.assetLoaderScheme(localConfig) : "https");
375
+ options.setServeBundledAssets(true);
376
+ } else {
377
+ options.setServeBundledAssets(false);
378
+ }
379
+ return true;
380
+ }
381
+
361
382
  private void notifyPopupWindowOpened(String popupId, String parentId, String popupUrl, boolean visible) {
362
383
  JSObject event = new JSObject();
363
384
  event.put("id", popupId);
@@ -752,13 +773,21 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
752
773
  return;
753
774
  }
754
775
 
755
- currentUrl = url;
776
+ BundledAssetSupport.Resolution resolution = BundledAssetSupport.resolve(url, getBridge());
777
+ if (resolution == null) {
778
+ call.reject("Invalid bundled asset path");
779
+ return;
780
+ }
781
+ final String resolvedUrl = resolution.url;
782
+ currentUrl = resolvedUrl;
783
+ final String localUrl = getBridge() != null ? getBridge().getLocalUrl() : null;
756
784
  this.getActivity().runOnUiThread(
757
785
  new Runnable() {
758
786
  @Override
759
787
  public void run() {
760
788
  try {
761
- webViewDialog.setUrl(url);
789
+ webViewDialog.applyBundledAssetResolution(resolution, localUrl);
790
+ webViewDialog.setUrl(resolvedUrl);
762
791
  call.resolve();
763
792
  } catch (Exception e) {
764
793
  Log.e("InAppBrowser", "Error setting URL: " + e.getMessage());
@@ -785,6 +814,11 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
785
814
  call.reject("Invalid URL");
786
815
  return;
787
816
  }
817
+
818
+ if (BundledAssetSupport.isLikelyBundledRelativePath(url)) {
819
+ call.reject("Bundled assets require openWebView()");
820
+ return;
821
+ }
788
822
  currentUrl = url;
789
823
  awaitCustomTabsClientIfNeeded();
790
824
  CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(getCustomTabsSession());
@@ -1054,11 +1088,15 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
1054
1088
  String url = call.getString("url");
1055
1089
  if (url == null || TextUtils.isEmpty(url)) {
1056
1090
  call.reject("Invalid URL");
1091
+ return;
1057
1092
  }
1058
- currentUrl = url;
1059
1093
  final String webViewId = UUID.randomUUID().toString();
1060
1094
  final Options options = new Options();
1061
- options.setUrl(url);
1095
+ if (!applyBundledAssetOptions(options, url)) {
1096
+ call.reject("Invalid bundled asset path");
1097
+ return;
1098
+ }
1099
+ currentUrl = options.getUrl();
1062
1100
  options.setHeaders(call.getObject("headers"));
1063
1101
  options.setCustomUserAgent(call.getString("customUserAgent"));
1064
1102
  options.setCredentials(call.getObject("credentials"));
@@ -8,7 +8,7 @@ import java.io.IOException;
8
8
  import java.io.InputStream;
9
9
  import java.util.ArrayList;
10
10
  import java.util.List;
11
- import java.util.Objects;
11
+ import java.util.Locale;
12
12
  import java.util.Objects;
13
13
  import java.util.regex.Pattern;
14
14
 
@@ -151,6 +151,9 @@ public class Options {
151
151
  private String CloseModalOk;
152
152
  private Pattern closeModalURLPattern;
153
153
  private String url;
154
+ private volatile String bundledAssetHost = "localhost";
155
+ private volatile String bundledAssetScheme = "https";
156
+ private volatile boolean serveBundledAssets = false;
154
157
  private JSObject headers;
155
158
  private String customUserAgent;
156
159
  private JSObject credentials;
@@ -493,6 +496,38 @@ public class Options {
493
496
  this.url = url;
494
497
  }
495
498
 
499
+ public String getBundledAssetHost() {
500
+ return bundledAssetHost;
501
+ }
502
+
503
+ public void setBundledAssetHost(String bundledAssetHost) {
504
+ if (bundledAssetHost == null || bundledAssetHost.isBlank()) {
505
+ this.bundledAssetHost = "localhost";
506
+ return;
507
+ }
508
+ this.bundledAssetHost = bundledAssetHost;
509
+ }
510
+
511
+ public String getBundledAssetScheme() {
512
+ return bundledAssetScheme;
513
+ }
514
+
515
+ public void setBundledAssetScheme(String bundledAssetScheme) {
516
+ if (bundledAssetScheme == null || bundledAssetScheme.isBlank()) {
517
+ this.bundledAssetScheme = "https";
518
+ return;
519
+ }
520
+ this.bundledAssetScheme = bundledAssetScheme.toLowerCase(Locale.ROOT);
521
+ }
522
+
523
+ public boolean getServeBundledAssets() {
524
+ return serveBundledAssets;
525
+ }
526
+
527
+ public void setServeBundledAssets(boolean serveBundledAssets) {
528
+ this.serveBundledAssets = serveBundledAssets;
529
+ }
530
+
496
531
  public JSObject getHeaders() {
497
532
  return headers;
498
533
  }
@@ -784,6 +819,9 @@ public class Options {
784
819
  copy.setTitleFontFamily(titleFontFamily);
785
820
  copy.setTitleIcon(titleIcon);
786
821
  copy.setUrl("about:blank");
822
+ copy.setBundledAssetHost(bundledAssetHost);
823
+ copy.setBundledAssetScheme(bundledAssetScheme);
824
+ copy.setServeBundledAssets(serveBundledAssets);
787
825
  copy.setHeaders(headers);
788
826
  copy.setCustomUserAgent(customUserAgent);
789
827
  copy.setCredentials(credentials);