Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit f456a47

Browse files
committed
[webview_flutter] configure mixedContentMode for Android WebView
1 parent 3222a3e commit f456a47

File tree

11 files changed

+143
-3
lines changed

11 files changed

+143
-3
lines changed

packages/webview_flutter/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.0.3
2+
3+
* Add `mixedContentMode` option for Android WebView.
4+
15
## 2.0.2
26

37
* Fixes bug where text fields are hidden behind the keyboard

packages/webview_flutter/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,7 @@ android {
7171

7272
To use Material Components when the user interacts with input elements in the WebView,
7373
follow the steps described in the [Enabling Material Components instructions](https://flutter.dev/docs/deployment/android#enabling-material-components).
74+
75+
### Android `mixedContentMode`
76+
77+
1. Android `mixedContentMode` requires `usesCleartextTraffic` in `AndroidManifest.xml`;

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import android.view.View;
1414
import android.webkit.WebChromeClient;
1515
import android.webkit.WebResourceRequest;
16+
import android.webkit.WebSettings;
1617
import android.webkit.WebStorage;
1718
import android.webkit.WebView;
1819
import android.webkit.WebViewClient;
@@ -371,6 +372,14 @@ private void applySettings(Map<String, Object> settings) {
371372
Integer mode = (Integer) settings.get(key);
372373
if (mode != null) updateJsMode(mode);
373374
break;
375+
case "mixedContentMode":
376+
{
377+
Object value = settings.get(key);
378+
if (value != null) {
379+
updateMixedContentMode((Integer) value);
380+
}
381+
break;
382+
}
374383
case "hasNavigationDelegate":
375384
final boolean hasNavigationDelegate = (boolean) settings.get(key);
376385

@@ -416,6 +425,24 @@ private void updateJsMode(int mode) {
416425
}
417426
}
418427

428+
private void updateMixedContentMode(int mode) {
429+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
430+
switch (mode) {
431+
case 0:
432+
webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
433+
break;
434+
case 1:
435+
webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
436+
break;
437+
case 2:
438+
webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
439+
break;
440+
default:
441+
throw new IllegalArgumentException("Trying to set unknown mixed content mode: " + mode);
442+
}
443+
}
444+
}
445+
419446
private void updateAutoMediaPlaybackPolicy(int mode) {
420447
// This is the index of the AutoMediaPlaybackPolicy enum, index 1 is always_allow, for all
421448
// other values we require a user gesture.

packages/webview_flutter/example/android/app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
<application
1010
android:icon="@mipmap/ic_launcher"
1111
android:label="webview_flutter_example"
12-
android:name="io.flutter.app.FlutterApplication">
12+
android:name="io.flutter.app.FlutterApplication"
13+
android:usesCleartextTraffic="true">
1314
<meta-data
1415
android:name="flutterEmbedding"
1516
android:value="2" />

packages/webview_flutter/example/integration_test/webview_flutter_test.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,48 @@ void main() {
542542
}, skip: true /* https://github.com/flutter/flutter/issues/72572 */);
543543
});
544544

545+
// NOTE: Requires `usesCleartextTraffic` in `AndroidManifest.xml`
546+
testWidgets('set mixedContentMode', (WidgetTester tester) async {
547+
// Only for Android
548+
if (!Platform.isAndroid) return;
549+
550+
void test(bool allow) async {
551+
final Completer<WebViewController> controllerCompleter =
552+
Completer<WebViewController>();
553+
final Completer<void> loadCompleter = Completer<void>();
554+
await tester.pumpWidget(
555+
Directionality(
556+
textDirection: TextDirection.ltr,
557+
child: WebView(
558+
key: GlobalKey(),
559+
initialUrl:
560+
'https://www.mixedcontentexamples.com/Test/NonSecureImage',
561+
javascriptMode: JavascriptMode.unrestricted,
562+
mixedContentMode: allow
563+
? MixedContentMode.compatibilityMode
564+
: MixedContentMode.neverAllow,
565+
onWebViewCreated: (WebViewController controller) {
566+
controllerCompleter.complete(controller);
567+
},
568+
onPageFinished: (_) => loadCompleter.complete(null),
569+
),
570+
),
571+
);
572+
final WebViewController controller = await controllerCompleter.future;
573+
await loadCompleter.future;
574+
final height = int.tryParse(await controller.evaluateJavascript(
575+
'document.getElementsByTagName("img")[0].height'));
576+
if (allow) {
577+
expect(height, 398);
578+
} else {
579+
expect(height != 398, true);
580+
}
581+
}
582+
583+
await test(false);
584+
await test(true);
585+
});
586+
545587
group('Audio playback policy', () {
546588
String audioTestBase64;
547589
setUpAll(() async {

packages/webview_flutter/example/lib/main.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class _WebViewExampleState extends State<WebViewExample> {
5959
return WebView(
6060
initialUrl: 'https://flutter.dev',
6161
javascriptMode: JavascriptMode.unrestricted,
62+
mixedContentMode: MixedContentMode.compatibilityMode,
6263
onWebViewCreated: (WebViewController webViewController) {
6364
_controller.complete(webViewController);
6465
},

packages/webview_flutter/lib/platform_interface.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ class WebSettings {
394394
this.hasNavigationDelegate,
395395
this.hasProgressTracking,
396396
this.debuggingEnabled,
397+
this.mixedContentMode,
397398
this.gestureNavigationEnabled,
398399
this.allowsInlineMediaPlayback,
399400
required this.userAgent,
@@ -429,14 +430,19 @@ class WebSettings {
429430
/// See also [WebView.userAgent].
430431
final WebSetting<String?> userAgent;
431432

433+
/// The mixed content mode to be used by the webview.
434+
///
435+
/// See also: [WebView.mixedContentMode]
436+
final MixedContentMode? mixedContentMode;
437+
432438
/// Whether to allow swipe based navigation in iOS.
433439
///
434440
/// See also: [WebView.gestureNavigationEnabled]
435441
final bool? gestureNavigationEnabled;
436442

437443
@override
438444
String toString() {
439-
return 'WebSettings(javascriptMode: $javascriptMode, hasNavigationDelegate: $hasNavigationDelegate, hasProgressTracking: $hasProgressTracking, debuggingEnabled: $debuggingEnabled, gestureNavigationEnabled: $gestureNavigationEnabled, userAgent: $userAgent, allowsInlineMediaPlayback: $allowsInlineMediaPlayback)';
445+
return 'WebSettings(javascriptMode: $javascriptMode, hasNavigationDelegate: $hasNavigationDelegate, hasProgressTracking: $hasProgressTracking, debuggingEnabled: $debuggingEnabled, gestureNavigationEnabled: $gestureNavigationEnabled, userAgent: $userAgent, allowsInlineMediaPlayback: $allowsInlineMediaPlayback, mixedContentMode: $mixedContentMode)';
440446
}
441447
}
442448

packages/webview_flutter/lib/src/webview_method_channel.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController {
188188
_addIfNonNull('hasNavigationDelegate', settings.hasNavigationDelegate);
189189
_addIfNonNull('hasProgressTracking', settings.hasProgressTracking);
190190
_addIfNonNull('debuggingEnabled', settings.debuggingEnabled);
191+
_addIfNonNull('mixedContentMode', settings.mixedContentMode?.index);
191192
_addIfNonNull(
192193
'gestureNavigationEnabled', settings.gestureNavigationEnabled);
193194
_addIfNonNull(

packages/webview_flutter/lib/webview_flutter.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,23 @@ class SurfaceAndroidWebView extends AndroidWebView {
130130
}
131131
}
132132

133+
/// Describes the state of mixed content(e.g. http content in https pages) support in a given web view.
134+
///
135+
/// Only works in Android.
136+
///
137+
/// See also https://developer.android.com/reference/android/webkit/WebSettings.html#MIXED_CONTENT_ALWAYS_ALLOW
138+
/// for these 3 options.
139+
enum MixedContentMode {
140+
/// See also Android `MIXED_CONTENT_ALWAYS_ALLOW` option
141+
alwaysAllow,
142+
143+
/// See also Android `MIXED_CONTENT_NEVER_ALLOW` option
144+
neverAllow,
145+
146+
/// See also Android `MIXED_CONTENT_COMPATIBILITY_MODE` option
147+
compatibilityMode,
148+
}
149+
133150
/// Decides how to handle a specific navigation request.
134151
///
135152
/// The returned [NavigationDecision] determines how the navigation described by
@@ -228,6 +245,7 @@ class WebView extends StatefulWidget {
228245
this.debuggingEnabled = false,
229246
this.gestureNavigationEnabled = false,
230247
this.userAgent,
248+
this.mixedContentMode,
231249
this.initialMediaPlaybackPolicy =
232250
AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
233251
this.allowsInlineMediaPlayback = false,
@@ -407,6 +425,14 @@ class WebView extends StatefulWidget {
407425
/// By default `userAgent` is null.
408426
final String? userAgent;
409427

428+
/// Controls whether mixed content is allowed to load.
429+
///
430+
/// This only works on Android.
431+
/// For iOS, you can change the settings in info.plist.
432+
///
433+
/// By default `mixedContentMode` is null, and Android would use its own default.
434+
final MixedContentMode? mixedContentMode;
435+
410436
/// Which restrictions apply on automatic media playback.
411437
///
412438
/// This initial value is applied to the platform's webview upon creation. Any following
@@ -488,6 +514,7 @@ WebSettings _webSettingsFromWidget(WebView widget) {
488514
hasNavigationDelegate: widget.navigationDelegate != null,
489515
hasProgressTracking: widget.onProgress != null,
490516
debuggingEnabled: widget.debuggingEnabled,
517+
mixedContentMode: widget.mixedContentMode,
491518
gestureNavigationEnabled: widget.gestureNavigationEnabled,
492519
allowsInlineMediaPlayback: widget.allowsInlineMediaPlayback,
493520
userAgent: WebSetting<String?>.of(widget.userAgent),

packages/webview_flutter/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: webview_flutter
22
description: A Flutter plugin that provides a WebView widget on Android and iOS.
33
homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter
4-
version: 2.0.2
4+
version: 2.0.3
55

66
environment:
77
sdk: ">=2.12.0-259.9.beta <3.0.0"

packages/webview_flutter/test/webview_flutter_test.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,31 @@ void main() {
844844
});
845845
});
846846

847+
group('mixedContentMode', () {
848+
testWidgets('set mixedContentMode', (WidgetTester tester) async {
849+
await tester.pumpWidget(const WebView(
850+
mixedContentMode: MixedContentMode.alwaysAllow,
851+
));
852+
853+
final FakePlatformWebView? platformWebView =
854+
fakePlatformViewsController.lastCreatedView;
855+
856+
expect(
857+
platformWebView?.mixedContentMode, MixedContentMode.alwaysAllow.index);
858+
});
859+
860+
testWidgets('defaults to null', (WidgetTester tester) async {
861+
await tester.pumpWidget(const WebView());
862+
863+
final FakePlatformWebView? platformWebView =
864+
fakePlatformViewsController.lastCreatedView;
865+
866+
expect(platformWebView?.mixedContentMode, null);
867+
});
868+
869+
// no need to change
870+
});
871+
847872
group('Custom platform implementation', () {
848873
setUpAll(() {
849874
WebView.platform = MyWebViewPlatform();
@@ -939,6 +964,7 @@ class FakePlatformWebView {
939964
hasNavigationDelegate =
940965
params['settings']['hasNavigationDelegate'] ?? false;
941966
debuggingEnabled = params['settings']['debuggingEnabled'];
967+
mixedContentMode = params['settings']['mixedContentMode'];
942968
userAgent = params['settings']['userAgent'];
943969
channel = MethodChannel(
944970
'plugins.flutter.io/webview_$id', const StandardMethodCodec());
@@ -958,6 +984,7 @@ class FakePlatformWebView {
958984

959985
bool? hasNavigationDelegate;
960986
bool? debuggingEnabled;
987+
int? mixedContentMode;
961988
String? userAgent;
962989

963990
Future<dynamic> onMethodCall(MethodCall call) {

0 commit comments

Comments
 (0)