Skip to content

Commit 1dc479b

Browse files
[webview_flutter] Add setMixedContentMode for Android (#9586)
Adds an Android-level method to control the mixed content mode. Fixes flutter/flutter#43595 ## Pre-Review Checklist [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 2b0ecd2 commit 1dc479b

File tree

12 files changed

+271
-1
lines changed

12 files changed

+271
-1
lines changed

packages/webview_flutter/webview_flutter_android/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 4.8.0
2+
3+
* Adds `AndroidWebViewController.setMixedContentMode` to control how
4+
mixed-content pages load.
5+
16
## 4.7.0
27

38
* Adds support to respond to recoverable SSL certificate errors. See `AndroidNavigationDelegate.setOnSSlAuthError`.

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/AndroidWebkitLibrary.g.kt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,7 @@ private class AndroidWebkitLibraryPigeonProxyApiBaseCodec(
660660
value is ConsoleMessageLevel ||
661661
value is OverScrollMode ||
662662
value is SslErrorType ||
663+
value is MixedContentMode ||
663664
value == null) {
664665
super.writeValue(stream, value)
665666
return
@@ -886,6 +887,32 @@ enum class SslErrorType(val raw: Int) {
886887
}
887888
}
888889

890+
/**
891+
* Options for mixed content mode support.
892+
*
893+
* See https://developer.android.com/reference/android/webkit/WebSettings#MIXED_CONTENT_ALWAYS_ALLOW
894+
*/
895+
enum class MixedContentMode(val raw: Int) {
896+
/**
897+
* The WebView will allow a secure origin to load content from any other origin, even if that
898+
* origin is insecure.
899+
*/
900+
ALWAYS_ALLOW(0),
901+
/**
902+
* The WebView will attempt to be compatible with the approach of a modern web browser with regard
903+
* to mixed content.
904+
*/
905+
COMPATIBILITY_MODE(1),
906+
/** The WebView will not allow a secure origin to load content from an insecure origin. */
907+
NEVER_ALLOW(2);
908+
909+
companion object {
910+
fun ofRaw(raw: Int): MixedContentMode? {
911+
return values().firstOrNull { it.raw == raw }
912+
}
913+
}
914+
}
915+
889916
private open class AndroidWebkitLibraryPigeonCodec : StandardMessageCodec() {
890917
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
891918
return when (type) {
@@ -901,6 +928,9 @@ private open class AndroidWebkitLibraryPigeonCodec : StandardMessageCodec() {
901928
132.toByte() -> {
902929
return (readValue(buffer) as Long?)?.let { SslErrorType.ofRaw(it.toInt()) }
903930
}
931+
133.toByte() -> {
932+
return (readValue(buffer) as Long?)?.let { MixedContentMode.ofRaw(it.toInt()) }
933+
}
904934
else -> super.readValueOfType(type, buffer)
905935
}
906936
}
@@ -923,6 +953,10 @@ private open class AndroidWebkitLibraryPigeonCodec : StandardMessageCodec() {
923953
stream.write(132)
924954
writeValue(stream, value.raw)
925955
}
956+
is MixedContentMode -> {
957+
stream.write(133)
958+
writeValue(stream, value.raw)
959+
}
926960
else -> super.writeValue(stream, value)
927961
}
928962
}
@@ -2285,6 +2319,12 @@ abstract class PigeonApiWebSettings(
22852319
/** Gets the WebView's user-agent string. */
22862320
abstract fun getUserAgentString(pigeon_instance: android.webkit.WebSettings): String
22872321

2322+
/** Configures the WebView's behavior when handling mixed content. */
2323+
abstract fun setMixedContentMode(
2324+
pigeon_instance: android.webkit.WebSettings,
2325+
mode: MixedContentMode
2326+
)
2327+
22882328
companion object {
22892329
@Suppress("LocalVariableName")
22902330
fun setUpMessageHandlers(binaryMessenger: BinaryMessenger, api: PigeonApiWebSettings?) {
@@ -2671,6 +2711,30 @@ abstract class PigeonApiWebSettings(
26712711
channel.setMessageHandler(null)
26722712
}
26732713
}
2714+
run {
2715+
val channel =
2716+
BasicMessageChannel<Any?>(
2717+
binaryMessenger,
2718+
"dev.flutter.pigeon.webview_flutter_android.WebSettings.setMixedContentMode",
2719+
codec)
2720+
if (api != null) {
2721+
channel.setMessageHandler { message, reply ->
2722+
val args = message as List<Any?>
2723+
val pigeon_instanceArg = args[0] as android.webkit.WebSettings
2724+
val modeArg = args[1] as MixedContentMode
2725+
val wrapped: List<Any?> =
2726+
try {
2727+
api.setMixedContentMode(pigeon_instanceArg, modeArg)
2728+
listOf(null)
2729+
} catch (exception: Throwable) {
2730+
AndroidWebkitLibraryPigeonUtils.wrapError(exception)
2731+
}
2732+
reply.reply(wrapped)
2733+
}
2734+
} else {
2735+
channel.setMessageHandler(null)
2736+
}
2737+
}
26742738
}
26752739
}
26762740

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsProxyApi.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,20 @@ public void setTextZoom(@NonNull WebSettings pigeon_instance, long textZoom) {
101101
public String getUserAgentString(@NonNull WebSettings pigeon_instance) {
102102
return pigeon_instance.getUserAgentString();
103103
}
104+
105+
@Override
106+
public void setMixedContentMode(
107+
@NonNull WebSettings pigeon_instance, @NonNull MixedContentMode mode) {
108+
switch (mode) {
109+
case ALWAYS_ALLOW:
110+
pigeon_instance.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
111+
break;
112+
case COMPATIBILITY_MODE:
113+
pigeon_instance.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
114+
break;
115+
case NEVER_ALLOW:
116+
pigeon_instance.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
117+
break;
118+
}
119+
}
104120
}

packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebSettingsTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,14 @@ public void getUserAgentString() {
166166

167167
assertEquals(value, api.getUserAgentString(instance));
168168
}
169+
170+
@Test
171+
public void setMixedContentMode() {
172+
final PigeonApiWebSettings api = new TestProxyApiRegistrar().getPigeonApiWebSettings();
173+
174+
final WebSettings instance = mock(WebSettings.class);
175+
api.setMixedContentMode(instance, MixedContentMode.COMPATIBILITY_MODE);
176+
177+
verify(instance).setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
178+
}
169179
}

packages/webview_flutter/webview_flutter_android/lib/src/android_webkit.g.dart

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,23 @@ enum SslErrorType {
570570
unknown,
571571
}
572572

573+
/// Options for mixed content mode support.
574+
///
575+
/// See https://developer.android.com/reference/android/webkit/WebSettings#MIXED_CONTENT_ALWAYS_ALLOW
576+
enum MixedContentMode {
577+
/// The WebView will allow a secure origin to load content from any other
578+
/// origin, even if that origin is insecure.
579+
alwaysAllow,
580+
581+
/// The WebView will attempt to be compatible with the approach of a modern
582+
/// web browser with regard to mixed content.
583+
compatibilityMode,
584+
585+
/// The WebView will not allow a secure origin to load content from an
586+
/// insecure origin.
587+
neverAllow,
588+
}
589+
573590
class _PigeonCodec extends StandardMessageCodec {
574591
const _PigeonCodec();
575592
@override
@@ -589,6 +606,9 @@ class _PigeonCodec extends StandardMessageCodec {
589606
} else if (value is SslErrorType) {
590607
buffer.putUint8(132);
591608
writeValue(buffer, value.index);
609+
} else if (value is MixedContentMode) {
610+
buffer.putUint8(133);
611+
writeValue(buffer, value.index);
592612
} else {
593613
super.writeValue(buffer, value);
594614
}
@@ -609,6 +629,9 @@ class _PigeonCodec extends StandardMessageCodec {
609629
case 132:
610630
final int? value = readValue(buffer) as int?;
611631
return value == null ? null : SslErrorType.values[value];
632+
case 133:
633+
final int? value = readValue(buffer) as int?;
634+
return value == null ? null : MixedContentMode.values[value];
612635
default:
613636
return super.readValueOfType(type, buffer);
614637
}
@@ -2895,6 +2918,36 @@ class WebSettings extends PigeonInternalProxyApiBaseClass {
28952918
}
28962919
}
28972920

2921+
/// Configures the WebView's behavior when handling mixed content.
2922+
Future<void> setMixedContentMode(MixedContentMode mode) async {
2923+
final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec =
2924+
_pigeonVar_codecWebSettings;
2925+
final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger;
2926+
const String pigeonVar_channelName =
2927+
'dev.flutter.pigeon.webview_flutter_android.WebSettings.setMixedContentMode';
2928+
final BasicMessageChannel<Object?> pigeonVar_channel =
2929+
BasicMessageChannel<Object?>(
2930+
pigeonVar_channelName,
2931+
pigeonChannelCodec,
2932+
binaryMessenger: pigeonVar_binaryMessenger,
2933+
);
2934+
final Future<Object?> pigeonVar_sendFuture =
2935+
pigeonVar_channel.send(<Object?>[this, mode]);
2936+
final List<Object?>? pigeonVar_replyList =
2937+
await pigeonVar_sendFuture as List<Object?>?;
2938+
if (pigeonVar_replyList == null) {
2939+
throw _createConnectionError(pigeonVar_channelName);
2940+
} else if (pigeonVar_replyList.length > 1) {
2941+
throw PlatformException(
2942+
code: pigeonVar_replyList[0]! as String,
2943+
message: pigeonVar_replyList[1] as String?,
2944+
details: pigeonVar_replyList[2],
2945+
);
2946+
} else {
2947+
return;
2948+
}
2949+
}
2950+
28982951
@override
28992952
WebSettings pigeon_copy() {
29002953
return WebSettings.pigeon_detached(

packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,19 @@ class AndroidWebViewController extends PlatformWebViewController {
768768
_ => throw UnsupportedError('Android does not support $mode.'),
769769
};
770770
}
771+
772+
/// Configures the WebView's behavior when handling mixed content.
773+
Future<void> setMixedContentMode(MixedContentMode mode) {
774+
final android_webview.MixedContentMode androidMode = switch (mode) {
775+
MixedContentMode.alwaysAllow =>
776+
android_webview.MixedContentMode.alwaysAllow,
777+
MixedContentMode.compatibilityMode =>
778+
android_webview.MixedContentMode.compatibilityMode,
779+
MixedContentMode.neverAllow =>
780+
android_webview.MixedContentMode.neverAllow,
781+
};
782+
return _webView.settings.setMixedContentMode(androidMode);
783+
}
771784
}
772785

773786
/// Android implementation of [PlatformWebViewPermissionRequest].
@@ -866,6 +879,36 @@ enum FileSelectorMode {
866879
save,
867880
}
868881

882+
/// Mode for controlling mixed content handling.
883+
884+
/// See [AndroidWebViewController.setMixedContentMode].
885+
enum MixedContentMode {
886+
/// The WebView will allow a secure origin to load content from any other
887+
/// origin, even if that origin is insecure.
888+
///
889+
/// This is the least secure mode of operation, and where possible apps should
890+
/// not set this mode.
891+
alwaysAllow,
892+
893+
/// The WebView will attempt to be compatible with the approach of a modern
894+
/// web browser with regard to mixed content.
895+
///
896+
/// The types of content are allowed or blocked may change release to release
897+
/// of the underlying Android WebView, and are not explicitly defined. This
898+
/// mode is intended to be used by apps that are not in control of the content
899+
/// that they render but desire to operate in a reasonably secure environment.
900+
compatibilityMode,
901+
902+
/// The WebView will not allow a secure origin to load content from an
903+
/// insecure origin.
904+
///
905+
/// This is the preferred and most secure mode of operation, and apps are
906+
/// strongly advised to use this mode.
907+
///
908+
/// This is the default mode.
909+
neverAllow,
910+
}
911+
869912
/// Parameters received when the `WebView` should show a file selector.
870913
@immutable
871914
class FileSelectorParams {

packages/webview_flutter/webview_flutter_android/pigeons/android_webkit.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,23 @@ enum SslErrorType {
125125
unknown,
126126
}
127127

128+
/// Options for mixed content mode support.
129+
///
130+
/// See https://developer.android.com/reference/android/webkit/WebSettings#MIXED_CONTENT_ALWAYS_ALLOW
131+
enum MixedContentMode {
132+
/// The WebView will allow a secure origin to load content from any other
133+
/// origin, even if that origin is insecure.
134+
alwaysAllow,
135+
136+
/// The WebView will attempt to be compatible with the approach of a modern
137+
/// web browser with regard to mixed content.
138+
compatibilityMode,
139+
140+
/// The WebView will not allow a secure origin to load content from an
141+
/// insecure origin.
142+
neverAllow,
143+
}
144+
128145
/// Encompasses parameters to the `WebViewClient.shouldInterceptRequest` method.
129146
///
130147
/// See https://developer.android.com/reference/android/webkit/WebResourceRequest.
@@ -410,6 +427,9 @@ abstract class WebSettings {
410427

411428
/// Gets the WebView's user-agent string.
412429
String getUserAgentString();
430+
431+
/// Configures the WebView's behavior when handling mixed content.
432+
void setMixedContentMode(MixedContentMode mode);
413433
}
414434

415435
/// A JavaScript interface for exposing Javascript callbacks to Dart.

packages/webview_flutter/webview_flutter_android/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: webview_flutter_android
22
description: A Flutter plugin that provides a WebView widget on Android.
33
repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_android
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
5-
version: 4.7.0
5+
version: 4.8.0
66

77
environment:
88
sdk: ^3.6.0

packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,21 @@ void main() {
16621662
verify(mockSettings.setTextZoom(100)).called(1);
16631663
});
16641664

1665+
test('setMixedContentMode', () async {
1666+
final MockWebView mockWebView = MockWebView();
1667+
final MockWebSettings mockSettings = MockWebSettings();
1668+
final AndroidWebViewController controller = createControllerWithMocks(
1669+
mockWebView: mockWebView,
1670+
mockSettings: mockSettings,
1671+
);
1672+
1673+
await controller.setMixedContentMode(MixedContentMode.compatibilityMode);
1674+
1675+
verify(mockSettings.setMixedContentMode(
1676+
android_webview.MixedContentMode.compatibilityMode,
1677+
)).called(1);
1678+
});
1679+
16651680
test('setOverScrollMode', () async {
16661681
final MockWebView mockWebView = MockWebView();
16671682
final AndroidWebViewController controller = createControllerWithMocks(

packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,17 @@ class MockAndroidWebViewController extends _i1.Mock
10071007
returnValue: _i8.Future<void>.value(),
10081008
returnValueForMissingStub: _i8.Future<void>.value(),
10091009
) as _i8.Future<void>);
1010+
1011+
@override
1012+
_i8.Future<void> setMixedContentMode(_i7.MixedContentMode? mode) =>
1013+
(super.noSuchMethod(
1014+
Invocation.method(
1015+
#setMixedContentMode,
1016+
[mode],
1017+
),
1018+
returnValue: _i8.Future<void>.value(),
1019+
returnValueForMissingStub: _i8.Future<void>.value(),
1020+
) as _i8.Future<void>);
10101021
}
10111022

10121023
/// A class which mocks [AndroidWebViewProxy].
@@ -3001,6 +3012,17 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings {
30013012
)),
30023013
) as _i8.Future<String>);
30033014

3015+
@override
3016+
_i8.Future<void> setMixedContentMode(_i2.MixedContentMode? mode) =>
3017+
(super.noSuchMethod(
3018+
Invocation.method(
3019+
#setMixedContentMode,
3020+
[mode],
3021+
),
3022+
returnValue: _i8.Future<void>.value(),
3023+
returnValueForMissingStub: _i8.Future<void>.value(),
3024+
) as _i8.Future<void>);
3025+
30043026
@override
30053027
_i2.WebSettings pigeon_copy() => (super.noSuchMethod(
30063028
Invocation.method(

0 commit comments

Comments
 (0)