@capacitor/android 9.0.0-alpha.6 → 9.0.0-alpha.8

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.
@@ -53,18 +53,6 @@ public class Plugin {
53
53
  // Reference to the PluginHandle wrapper for this Plugin
54
54
  protected PluginHandle handle;
55
55
 
56
- /**
57
- * A way for plugins to quickly save a call that they will need to reference
58
- * between activity/permissions starts/requests
59
- *
60
- * @deprecated store calls on the bridge using the methods
61
- * {@link com.getcapacitor.Bridge#saveCall(PluginCall)},
62
- * {@link com.getcapacitor.Bridge#getSavedCall(String)} and
63
- * {@link com.getcapacitor.Bridge#releaseCall(PluginCall)}
64
- */
65
- @Deprecated
66
- protected PluginCall savedLastCall;
67
-
68
56
  // Stored event listeners
69
57
  private final Map<String, List<PluginCall>> eventListeners;
70
58
 
@@ -253,39 +241,6 @@ public class Plugin {
253
241
  return getContext().getPackageName();
254
242
  }
255
243
 
256
- /**
257
- * Called to save a {@link PluginCall} in order to reference it
258
- * later, such as in an activity or permissions result handler
259
- * @deprecated use {@link Bridge#saveCall(PluginCall)}
260
- *
261
- * @param lastCall
262
- */
263
- @Deprecated
264
- public void saveCall(PluginCall lastCall) {
265
- this.savedLastCall = lastCall;
266
- }
267
-
268
- /**
269
- * Set the last saved call to null to free memory
270
- * @deprecated use {@link PluginCall#release(Bridge)}
271
- */
272
- @Deprecated
273
- public void freeSavedCall() {
274
- this.savedLastCall.release(bridge);
275
- this.savedLastCall = null;
276
- }
277
-
278
- /**
279
- * Get the last saved call, if any
280
- * @deprecated use {@link Bridge#getSavedCall(String)}
281
- *
282
- * @return
283
- */
284
- @Deprecated
285
- public PluginCall getSavedCall() {
286
- return this.savedLastCall;
287
- }
288
-
289
244
  /**
290
245
  * Get the config options for this plugin.
291
246
  *
@@ -296,41 +251,6 @@ public class Plugin {
296
251
  return bridge.getConfig().getPluginConfiguration(handle.getId());
297
252
  }
298
253
 
299
- /**
300
- * Get the value for a key on the config for this plugin.
301
- * @deprecated use {@link #getConfig()} and access config values using the methods available
302
- * depending on the type.
303
- *
304
- * @param key the key for the config value
305
- * @return some object containing the value from the config
306
- */
307
- @Deprecated
308
- public Object getConfigValue(String key) {
309
- try {
310
- PluginConfig pluginConfig = getConfig();
311
- return pluginConfig.getConfigJSON().get(key);
312
- } catch (JSONException ex) {
313
- return null;
314
- }
315
- }
316
-
317
- /**
318
- * Check whether any of the given permissions has been defined in the AndroidManifest.xml
319
- * @deprecated use {@link #isPermissionDeclared(String)}
320
- *
321
- * @param permissions
322
- * @return
323
- */
324
- @Deprecated
325
- public boolean hasDefinedPermissions(String[] permissions) {
326
- for (String permission : permissions) {
327
- if (!PermissionHelper.hasDefinedPermission(getContext(), permission)) {
328
- return false;
329
- }
330
- }
331
- return true;
332
- }
333
-
334
254
  /**
335
255
  * Check if all annotated permissions have been defined in the AndroidManifest.xml
336
256
  * @deprecated use {@link #isPermissionDeclared(String)}
@@ -341,19 +261,15 @@ public class Plugin {
341
261
  public boolean hasDefinedRequiredPermissions() {
342
262
  CapacitorPlugin annotation = handle.getPluginAnnotation();
343
263
  if (annotation == null) {
344
- // Check for legacy plugin annotation, @NativePlugin
345
- NativePlugin legacyAnnotation = handle.getLegacyPluginAnnotation();
346
- return hasDefinedPermissions(legacyAnnotation.permissions());
347
- } else {
348
- for (Permission perm : annotation.permissions()) {
349
- for (String permString : perm.strings()) {
350
- if (!PermissionHelper.hasDefinedPermission(getContext(), permString)) {
351
- return false;
352
- }
264
+ return true;
265
+ }
266
+ for (Permission perm : annotation.permissions()) {
267
+ for (String permString : perm.strings()) {
268
+ if (!PermissionHelper.hasDefinedPermission(getContext(), permString)) {
269
+ return false;
353
270
  }
354
271
  }
355
272
  }
356
-
357
273
  return true;
358
274
  }
359
275
 
@@ -381,21 +297,6 @@ public class Plugin {
381
297
  return false;
382
298
  }
383
299
 
384
- /**
385
- * Check whether the given permission has been granted by the user
386
- * @deprecated use {@link #getPermissionState(String)} and {@link #getPermissionStates()} to get
387
- * the states of permissions defined on the Plugin in conjunction with the @CapacitorPlugin
388
- * annotation. Use the Android API {@link ActivityCompat#checkSelfPermission(Context, String)}
389
- * methods to check permissions with Android permission strings
390
- *
391
- * @param permission
392
- * @return
393
- */
394
- @Deprecated
395
- public boolean hasPermission(String permission) {
396
- return ActivityCompat.checkSelfPermission(this.getContext(), permission) == PackageManager.PERMISSION_GRANTED;
397
- }
398
-
399
300
  /**
400
301
  * If the plugin annotation specified a set of permissions, this method checks if each is
401
302
  * granted
@@ -408,17 +309,8 @@ public class Plugin {
408
309
  public boolean hasRequiredPermissions() {
409
310
  CapacitorPlugin annotation = handle.getPluginAnnotation();
410
311
  if (annotation == null) {
411
- // Check for legacy plugin annotation, @NativePlugin
412
- NativePlugin legacyAnnotation = handle.getLegacyPluginAnnotation();
413
- for (String perm : legacyAnnotation.permissions()) {
414
- if (ActivityCompat.checkSelfPermission(this.getContext(), perm) != PackageManager.PERMISSION_GRANTED) {
415
- return false;
416
- }
417
- }
418
-
419
312
  return true;
420
313
  }
421
-
422
314
  for (Permission perm : annotation.permissions()) {
423
315
  for (String permString : perm.strings()) {
424
316
  if (ActivityCompat.checkSelfPermission(this.getContext(), permString) != PackageManager.PERMISSION_GRANTED) {
@@ -426,7 +318,6 @@ public class Plugin {
426
318
  }
427
319
  }
428
320
  }
429
-
430
321
  return true;
431
322
  }
432
323
 
@@ -502,10 +393,14 @@ public class Plugin {
502
393
  private String[] getPermissionStringsForAliases(@NonNull String[] aliases) {
503
394
  CapacitorPlugin annotation = handle.getPluginAnnotation();
504
395
  HashSet<String> perms = new HashSet<>();
505
- for (Permission perm : annotation.permissions()) {
506
- if (Arrays.asList(aliases).contains(perm.alias())) {
507
- perms.addAll(Arrays.asList(perm.strings()));
396
+ if (annotation != null) {
397
+ for (Permission perm : annotation.permissions()) {
398
+ if (Arrays.asList(aliases).contains(perm.alias())) {
399
+ perms.addAll(Arrays.asList(perm.strings()));
400
+ }
508
401
  }
402
+ } else {
403
+ Logger.warn(String.format("getPermissionStringsForAliases: missing @CapacitorPlugin annotation for plugin %s", handle.getId()));
509
404
  }
510
405
 
511
406
  return perms.toArray(new String[0]);
@@ -563,42 +458,6 @@ public class Plugin {
563
458
  return permissionLauncher;
564
459
  }
565
460
 
566
- /**
567
- * Request all of the specified permissions in the CapacitorPlugin annotation (if any)
568
- *
569
- * @deprecated use {@link #requestAllPermissions(PluginCall, String)} in conjunction with @CapacitorPlugin
570
- */
571
- @Deprecated
572
- public void pluginRequestAllPermissions() {
573
- NativePlugin legacyAnnotation = handle.getLegacyPluginAnnotation();
574
- ActivityCompat.requestPermissions(getActivity(), legacyAnnotation.permissions(), legacyAnnotation.permissionRequestCode());
575
- }
576
-
577
- /**
578
- * Helper for requesting a specific permission
579
- *
580
- * @param permission the permission to request
581
- * @param requestCode the requestCode to use to associate the result with the plugin
582
- * @deprecated use {@link #requestPermissionForAlias(String, PluginCall, String)} in conjunction with @CapacitorPlugin
583
- */
584
- @Deprecated
585
- public void pluginRequestPermission(String permission, int requestCode) {
586
- ActivityCompat.requestPermissions(getActivity(), new String[] { permission }, requestCode);
587
- }
588
-
589
- /**
590
- * Helper for requesting specific permissions
591
- * @deprecated use {@link #requestPermissionForAliases(String[], PluginCall, String)} in conjunction
592
- * with @CapacitorPlugin
593
- *
594
- * @param permissions the set of permissions to request
595
- * @param requestCode the requestCode to use to associate the result with the plugin
596
- */
597
- @Deprecated
598
- public void pluginRequestPermissions(String[] permissions, int requestCode) {
599
- ActivityCompat.requestPermissions(getActivity(), permissions, requestCode);
600
- }
601
-
602
461
  /**
603
462
  * Get the permission state for the provided permission alias.
604
463
  *
@@ -803,92 +662,72 @@ public class Plugin {
803
662
  @PluginMethod
804
663
  public void requestPermissions(PluginCall call) {
805
664
  CapacitorPlugin annotation = handle.getPluginAnnotation();
806
- if (annotation == null) {
807
- handleLegacyPermission(call);
808
- } else {
809
- // handle permission requests for plugins defined with @CapacitorPlugin (since 3.0.0)
810
- String[] permAliases = null;
811
- Set<String> autoGrantPerms = new HashSet<>();
812
-
813
- // If call was made with a list of specific permission aliases to request, save them
814
- // to be requested
815
- JSArray providedPerms = call.getArray("permissions");
816
- List<String> providedPermsList = null;
817
-
818
- if (providedPerms != null) {
819
- try {
820
- providedPermsList = providedPerms.toList();
821
- } catch (JSONException ignore) {
822
- // do nothing
823
- }
824
- }
665
+ String[] permAliases = null;
666
+ Set<String> autoGrantPerms = new HashSet<>();
825
667
 
826
- // If call was made without any custom permissions, request all from plugin annotation
827
- Set<String> aliasSet = new HashSet<>();
828
- if (providedPermsList == null || providedPermsList.isEmpty()) {
829
- for (Permission perm : annotation.permissions()) {
830
- // If a permission is defined with no permission strings, separate it for auto-granting.
831
- // Otherwise, the alias is added to the list to be requested.
832
- if (perm.strings().length == 0 || (perm.strings().length == 1 && perm.strings()[0].isEmpty())) {
833
- if (!perm.alias().isEmpty()) {
834
- autoGrantPerms.add(perm.alias());
835
- }
836
- } else {
837
- aliasSet.add(perm.alias());
838
- }
839
- }
668
+ // If call was made with a list of specific permission aliases to request, save them
669
+ // to be requested
670
+ JSArray providedPerms = call.getArray("permissions");
671
+ List<String> providedPermsList = null;
840
672
 
841
- permAliases = aliasSet.toArray(new String[0]);
842
- } else {
843
- for (Permission perm : annotation.permissions()) {
844
- if (providedPermsList.contains(perm.alias())) {
845
- aliasSet.add(perm.alias());
846
- }
847
- }
673
+ if (providedPerms != null) {
674
+ try {
675
+ providedPermsList = providedPerms.toList();
676
+ } catch (JSONException ignore) {
677
+ // do nothing
678
+ }
679
+ }
848
680
 
849
- if (aliasSet.isEmpty()) {
850
- call.reject("No valid permission alias was requested of this plugin.");
681
+ // If call was made without any custom permissions, request all from plugin annotation
682
+ Set<String> aliasSet = new HashSet<>();
683
+ if (providedPermsList == null || providedPermsList.isEmpty()) {
684
+ for (Permission perm : annotation.permissions()) {
685
+ // If a permission is defined with no permission strings, separate it for auto-granting.
686
+ // Otherwise, the alias is added to the list to be requested.
687
+ if (perm.strings().length == 0 || (perm.strings().length == 1 && perm.strings()[0].isEmpty())) {
688
+ if (!perm.alias().isEmpty()) {
689
+ autoGrantPerms.add(perm.alias());
690
+ }
851
691
  } else {
852
- permAliases = aliasSet.toArray(new String[0]);
692
+ aliasSet.add(perm.alias());
853
693
  }
854
694
  }
855
695
 
856
- if (permAliases != null && permAliases.length > 0) {
857
- // request permissions using provided aliases or all defined on the plugin
858
- requestPermissionForAliases(permAliases, call, "checkPermissions");
859
- } else if (!autoGrantPerms.isEmpty()) {
860
- // if the plugin only has auto-grant permissions, return all as GRANTED
861
- JSObject permissionsResults = new JSObject();
862
-
863
- for (String perm : autoGrantPerms) {
864
- permissionsResults.put(perm, PermissionState.GRANTED.toString());
696
+ permAliases = aliasSet.toArray(new String[0]);
697
+ } else {
698
+ for (Permission perm : annotation.permissions()) {
699
+ if (providedPermsList.contains(perm.alias())) {
700
+ aliasSet.add(perm.alias());
865
701
  }
702
+ }
866
703
 
867
- call.resolve(permissionsResults);
704
+ if (aliasSet.isEmpty()) {
705
+ call.reject("No valid permission alias was requested of this plugin.");
868
706
  } else {
869
- // no permissions are defined on the plugin, resolve undefined
870
- call.resolve();
707
+ permAliases = aliasSet.toArray(new String[0]);
871
708
  }
872
709
  }
873
- }
874
710
 
875
- @SuppressWarnings("deprecation")
876
- private void handleLegacyPermission(PluginCall call) {
877
- // handle permission requests for plugins defined with @NativePlugin (prior to 3.0.0)
878
- NativePlugin legacyAnnotation = this.handle.getLegacyPluginAnnotation();
879
- String[] perms = legacyAnnotation.permissions();
880
- if (perms.length > 0) {
881
- saveCall(call);
882
- pluginRequestPermissions(perms, legacyAnnotation.permissionRequestCode());
711
+ if (permAliases != null && permAliases.length > 0) {
712
+ // request permissions using provided aliases or all defined on the plugin
713
+ requestPermissionForAliases(permAliases, call, "checkPermissions");
714
+ } else if (!autoGrantPerms.isEmpty()) {
715
+ // if the plugin only has auto-grant permissions, return all as GRANTED
716
+ JSObject permissionsResults = new JSObject();
717
+
718
+ for (String perm : autoGrantPerms) {
719
+ permissionsResults.put(perm, PermissionState.GRANTED.toString());
720
+ }
721
+
722
+ call.resolve(permissionsResults);
883
723
  } else {
724
+ // no permissions are defined on the plugin, resolve undefined
884
725
  call.resolve();
885
726
  }
886
727
  }
887
728
 
888
729
  /**
889
- * Handle request permissions result. A plugin using the deprecated {@link NativePlugin}
890
- * should override this to handle the result, or this method will handle the result
891
- * for our convenient requestPermissions call.
730
+ * Handle request permissions result. Subclasses may override this to handle the result.
892
731
  * @deprecated in favor of using callbacks in conjunction with {@link CapacitorPlugin}
893
732
  *
894
733
  * @param requestCode
@@ -896,18 +735,7 @@ public class Plugin {
896
735
  * @param grantResults
897
736
  */
898
737
  @Deprecated
899
- protected void handleRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
900
- if (!hasDefinedPermissions(permissions)) {
901
- StringBuilder builder = new StringBuilder();
902
- builder.append("Missing the following permissions in AndroidManifest.xml:\n");
903
- String[] missing = PermissionHelper.getUndefinedPermissions(getContext(), permissions);
904
- for (String perm : missing) {
905
- builder.append(perm + "\n");
906
- }
907
- savedLastCall.reject(builder.toString());
908
- savedLastCall = null;
909
- }
910
- }
738
+ protected void handleRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {}
911
739
 
912
740
  /**
913
741
  * Called before the app is destroyed to give a plugin the chance to
@@ -1003,28 +831,14 @@ public class Plugin {
1003
831
  * Give the plugins a chance to take control when a URL is about to be loaded in the WebView.
1004
832
  * Returning true causes the WebView to abort loading the URL.
1005
833
  * Returning false causes the WebView to continue loading the URL.
1006
- * Returning null will defer to the default Capacitor policy
834
+ * Returning null will defer to the default Capacitor policy.
835
+ * Not called for Capacitor's internal HTTP proxy path, which is always blocked.
1007
836
  */
1008
837
  @SuppressWarnings("unused")
1009
838
  public Boolean shouldOverrideLoad(Uri url) {
1010
839
  return null;
1011
840
  }
1012
841
 
1013
- /**
1014
- * Start a new Activity.
1015
- *
1016
- * Note: This method must be used by all plugins instead of calling
1017
- * {@link Activity#startActivityForResult} as it associates the plugin with
1018
- * any resulting data from the new Activity even if this app
1019
- * is destroyed by the OS (to free up memory, for example).
1020
- * @param intent
1021
- * @param resultCode
1022
- */
1023
- @Deprecated
1024
- protected void startActivityForResult(PluginCall call, Intent intent, int resultCode) {
1025
- bridge.startActivityForPluginWithResult(call, intent, resultCode);
1026
- }
1027
-
1028
842
  /**
1029
843
  * Execute the given runnable on the Bridge's task handler
1030
844
  * @param runnable
@@ -27,12 +27,6 @@ public class PluginCall {
27
27
 
28
28
  private boolean keepAlive = false;
29
29
 
30
- /**
31
- * Indicates that this PluginCall was released, and should no longer be used
32
- */
33
- @Deprecated
34
- private boolean isReleased = false;
35
-
36
30
  public PluginCall(MessageHandler msgHandler, String pluginId, String callbackId, String methodName, JSObject data) {
37
31
  this.msgHandler = msgHandler;
38
32
  this.pluginId = pluginId;
@@ -324,28 +318,6 @@ public class PluginCall {
324
318
  return defaultValue;
325
319
  }
326
320
 
327
- /**
328
- * @param name of the option to check
329
- * @return boolean indicating if the plugin call has an option for the provided name.
330
- * @deprecated Presence of a key should not be considered significant.
331
- * Use typed accessors to check the value instead.
332
- */
333
- @Deprecated
334
- public boolean hasOption(String name) {
335
- return this.data.has(name);
336
- }
337
-
338
- /**
339
- * Indicate that the Bridge should cache this call in order to call
340
- * it again later. For example, the addListener system uses this to
341
- * continuously call the call's callback (😆).
342
- * @deprecated use {@link #setKeepAlive(Boolean)} instead
343
- */
344
- @Deprecated
345
- public void save() {
346
- setKeepAlive(true);
347
- }
348
-
349
321
  /**
350
322
  * Indicate that the Bridge should cache this call in order to call
351
323
  * it again later. For example, the addListener system uses this to
@@ -360,16 +332,6 @@ public class PluginCall {
360
332
  public void release(Bridge bridge) {
361
333
  this.keepAlive = false;
362
334
  bridge.releaseCall(this);
363
- this.isReleased = true;
364
- }
365
-
366
- /**
367
- * @deprecated use {@link #isKeptAlive()}
368
- * @return true if the plugin call is kept alive
369
- */
370
- @Deprecated
371
- public boolean isSaved() {
372
- return isKeptAlive();
373
335
  }
374
336
 
375
337
  /**
@@ -380,11 +342,6 @@ public class PluginCall {
380
342
  return keepAlive;
381
343
  }
382
344
 
383
- @Deprecated
384
- public boolean isReleased() {
385
- return isReleased;
386
- }
387
-
388
345
  class PluginCallDataTypeException extends Exception {
389
346
 
390
347
  PluginCallDataTypeException(String m) {
@@ -20,43 +20,27 @@ public class PluginHandle {
20
20
 
21
21
  private final String pluginId;
22
22
 
23
- @SuppressWarnings("deprecation")
24
- private NativePlugin legacyPluginAnnotation;
25
-
26
23
  private CapacitorPlugin pluginAnnotation;
27
24
 
28
25
  private Plugin instance;
29
26
 
30
- @SuppressWarnings("deprecation")
31
27
  private PluginHandle(Class<? extends Plugin> clazz, Bridge bridge) throws InvalidPluginException {
32
28
  this.bridge = bridge;
33
29
  this.pluginClass = clazz;
34
30
 
35
31
  CapacitorPlugin pluginAnnotation = pluginClass.getAnnotation(CapacitorPlugin.class);
36
32
  if (pluginAnnotation == null) {
37
- // Check for legacy plugin annotation, @NativePlugin
38
- NativePlugin legacyPluginAnnotation = pluginClass.getAnnotation(NativePlugin.class);
39
- if (legacyPluginAnnotation == null) {
40
- throw new InvalidPluginException("No @CapacitorPlugin annotation found for plugin " + pluginClass.getName());
41
- }
42
-
43
- if (!legacyPluginAnnotation.name().equals("")) {
44
- this.pluginId = legacyPluginAnnotation.name();
45
- } else {
46
- this.pluginId = pluginClass.getSimpleName();
47
- }
33
+ throw new InvalidPluginException("No @CapacitorPlugin annotation found for plugin " + pluginClass.getName());
34
+ }
48
35
 
49
- this.legacyPluginAnnotation = legacyPluginAnnotation;
36
+ if (!pluginAnnotation.name().equals("")) {
37
+ this.pluginId = pluginAnnotation.name();
50
38
  } else {
51
- if (!pluginAnnotation.name().equals("")) {
52
- this.pluginId = pluginAnnotation.name();
53
- } else {
54
- this.pluginId = pluginClass.getSimpleName();
55
- }
56
-
57
- this.pluginAnnotation = pluginAnnotation;
39
+ this.pluginId = pluginClass.getSimpleName();
58
40
  }
59
41
 
42
+ this.pluginAnnotation = pluginAnnotation;
43
+
60
44
  this.indexMethods(clazz);
61
45
  }
62
46
 
@@ -78,11 +62,6 @@ public class PluginHandle {
78
62
  return this.pluginId;
79
63
  }
80
64
 
81
- @SuppressWarnings("deprecation")
82
- public NativePlugin getLegacyPluginAnnotation() {
83
- return this.legacyPluginAnnotation;
84
- }
85
-
86
65
  public CapacitorPlugin getPluginAnnotation() {
87
66
  return this.pluginAnnotation;
88
67
  }
@@ -172,6 +172,11 @@ public class WebViewLocalServer {
172
172
  Uri loadingUrl = request.getUrl();
173
173
 
174
174
  if (null != loadingUrl.getPath() && loadingUrl.getPath().startsWith(Bridge.CAPACITOR_HTTP_INTERCEPTOR_START)) {
175
+ // Only fetch/XHR should reach the proxy; a document would run remote content at the app origin.
176
+ boolean httpEnabled = bridge.getConfig().getPluginConfiguration("CapacitorHttp").getBoolean("enabled", false);
177
+ if (!httpEnabled || isDocumentRequest(request)) {
178
+ return null;
179
+ }
175
180
  Logger.debug("Handling CapacitorHttp request: " + loadingUrl);
176
181
  try {
177
182
  return handleCapacitorHttpRequest(request);
@@ -197,6 +202,24 @@ public class WebViewLocalServer {
197
202
  }
198
203
  }
199
204
 
205
+ /** isForMainFrame() is false for an iframe and a fetch alike; only navigations send this header. */
206
+ private boolean isDocumentRequest(WebResourceRequest request) {
207
+ if (request.isForMainFrame()) {
208
+ return true;
209
+ }
210
+ Map<String, String> headers = request.getRequestHeaders();
211
+ if (headers == null) {
212
+ // The proxy needs the headers too, so the request fails there anyway.
213
+ return false;
214
+ }
215
+ for (String header : headers.keySet()) {
216
+ if ("Upgrade-Insecure-Requests".equalsIgnoreCase(header)) {
217
+ return true;
218
+ }
219
+ }
220
+ return false;
221
+ }
222
+
200
223
  private boolean isLocalFile(Uri uri) {
201
224
  String path = uri.getPath();
202
225
  return path.startsWith(capacitorContentStart) || path.startsWith(capacitorFileStart);
@@ -320,6 +343,9 @@ public class WebViewLocalServer {
320
343
  int responseCode = connection.getResponseCode();
321
344
  String reasonPhrase = getReasonPhraseFromResponseCode(responseCode);
322
345
 
346
+ // Nothing should render this. If anything does, sandbox keeps it inert and off the app origin.
347
+ responseHeaders.put("Content-Security-Policy", "sandbox; frame-ancestors 'none'");
348
+
323
349
  return new WebResourceResponse(mimeType, encoding, responseCode, reasonPhrase, responseHeaders, inputStream);
324
350
  }
325
351