diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java index ebc7c31987f4..e6db97d3b009 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java @@ -14,6 +14,7 @@ import android.webkit.WebChromeClient; import android.webkit.WebResourceRequest; import android.webkit.WebStorage; +import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import androidx.annotation.NonNull; @@ -371,6 +372,10 @@ private void applySettings(Map settings) { Integer mode = (Integer) settings.get(key); if (mode != null) updateJsMode(mode); break; + case "mixedContentMode": + Integer mode = (Integer) settings.get(key); + if (mode != null) updateMixedContentMode(mode); + break; case "hasNavigationDelegate": final boolean hasNavigationDelegate = (boolean) settings.get(key); @@ -416,6 +421,24 @@ private void updateJsMode(int mode) { } } + private void updateMixedContentMode(int mode) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + switch (mode) { + case 0: + webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + break; + case 1: + webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW); + break; + case 2: + webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); + break; + default: + throw new IllegalArgumentException("Trying to set unknown MixedContent mode: " + mode); + } + } + } + private void updateAutoMediaPlaybackPolicy(int mode) { // This is the index of the AutoMediaPlaybackPolicy enum, index 1 is always_allow, for all // other values we require a user gesture. diff --git a/packages/webview_flutter/lib/platform_interface.dart b/packages/webview_flutter/lib/platform_interface.dart index 92aa87b7480f..d46dd7891a4e 100644 --- a/packages/webview_flutter/lib/platform_interface.dart +++ b/packages/webview_flutter/lib/platform_interface.dart @@ -391,6 +391,7 @@ class WebSettings { /// The `userAgent` parameter must not be null. WebSettings({ this.javascriptMode, + this.mixedContentMode, this.hasNavigationDelegate, this.hasProgressTracking, this.debuggingEnabled, @@ -402,6 +403,9 @@ class WebSettings { /// The JavaScript execution mode to be used by the webview. final JavascriptMode? javascriptMode; + ///mixedContentMode in WebSettings + final MixedContentMode? mixedContentMode; + /// Whether the [WebView] has a [NavigationDelegate] set. final bool? hasNavigationDelegate; diff --git a/packages/webview_flutter/lib/src/webview_method_channel.dart b/packages/webview_flutter/lib/src/webview_method_channel.dart index 05831a9d8794..68f55253913f 100644 --- a/packages/webview_flutter/lib/src/webview_method_channel.dart +++ b/packages/webview_flutter/lib/src/webview_method_channel.dart @@ -185,6 +185,7 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController { } _addIfNonNull('jsMode', settings!.javascriptMode?.index); + _addIfNonNull('mixedContentMode', settings!.mixedContentMode?.index); _addIfNonNull('hasNavigationDelegate', settings.hasNavigationDelegate); _addIfNonNull('hasProgressTracking', settings.hasProgressTracking); _addIfNonNull('debuggingEnabled', settings.debuggingEnabled); diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index 74d8af8d4687..e68f1b9bc68c 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -29,6 +29,18 @@ enum JavascriptMode { unrestricted, } +/// Describes the state of MixedContent support in webSettings. +enum MixedContentMode { + /// WebSettings.MIXED_CONTENT_ALWAYS_ALLOW + MIXED_CONTENT_ALWAYS_ALLOW, + + /// WebSettings.MIXED_CONTENT_NEVER_ALLOW + MIXED_CONTENT_NEVER_ALLOW, + + /// WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE + MIXED_CONTENT_COMPATIBILITY_MODE, +} + /// A message that was sent by JavaScript code running in a [WebView]. class JavascriptMessage { /// Constructs a JavaScript message object. @@ -96,8 +108,7 @@ class SurfaceAndroidWebView extends AndroidWebView { ) { return AndroidViewSurface( controller: controller as AndroidViewController, - gestureRecognizers: gestureRecognizers ?? - const >{}, + gestureRecognizers: gestureRecognizers ?? const >{}, hitTestBehavior: PlatformViewHitTestBehavior.opaque, ); }, @@ -136,8 +147,7 @@ class SurfaceAndroidWebView extends AndroidWebView { /// `navigation` should be handled. /// /// See also: [WebView.navigationDelegate]. -typedef FutureOr NavigationDelegate( - NavigationRequest navigation); +typedef FutureOr NavigationDelegate(NavigationRequest navigation); /// Signature for when a [WebView] has started loading a page. typedef void PageStartedCallback(String url); @@ -180,7 +190,7 @@ class JavascriptChannel { JavascriptChannel({ required this.name, required this.onMessageReceived, - }) : assert(name != null), + }) : assert(name != null), assert(onMessageReceived != null), assert(_validChannelNames.hasMatch(name)); @@ -218,6 +228,7 @@ class WebView extends StatefulWidget { this.onWebViewCreated, this.initialUrl, this.javascriptMode = JavascriptMode.disabled, + this.mixedContentMode = MixedContentMode.MIXED_CONTENT_NEVER_ALLOW, this.javascriptChannels, this.navigationDelegate, this.gestureRecognizers, @@ -228,8 +239,7 @@ class WebView extends StatefulWidget { this.debuggingEnabled = false, this.gestureNavigationEnabled = false, this.userAgent, - this.initialMediaPlaybackPolicy = - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, + this.initialMediaPlaybackPolicy = AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, this.allowsInlineMediaPlayback = false, }) : assert(javascriptMode != null), assert(initialMediaPlaybackPolicy != null), @@ -289,6 +299,9 @@ class WebView extends StatefulWidget { /// Whether Javascript execution is enabled. final JavascriptMode javascriptMode; + /// MixedContentMode in WebSettings + final MixedContentMode mixedContentMode; + /// The set of [JavascriptChannel]s available to JavaScript code running in the web view. /// /// For each [JavascriptChannel] in the set, a channel object is made available for the @@ -420,8 +433,7 @@ class WebView extends StatefulWidget { } class _WebViewState extends State { - final Completer _controller = - Completer(); + final Completer _controller = Completer(); late _PlatformCallbacksHandler _platformCallbacksHandler; @@ -454,8 +466,7 @@ class _WebViewState extends State { } void _onWebViewPlatformCreated(WebViewPlatformController? webViewPlatform) { - final WebViewController controller = WebViewController._( - widget, webViewPlatform!, _platformCallbacksHandler); + final WebViewController controller = WebViewController._(widget, webViewPlatform!, _platformCallbacksHandler); _controller.complete(controller); if (widget.onWebViewCreated != null) { widget.onWebViewCreated!(controller); @@ -463,12 +474,10 @@ class _WebViewState extends State { } void _assertJavascriptChannelNamesAreUnique() { - if (widget.javascriptChannels == null || - widget.javascriptChannels!.isEmpty) { + if (widget.javascriptChannels == null || widget.javascriptChannels!.isEmpty) { return; } - assert(_extractChannelNames(widget.javascriptChannels).length == - widget.javascriptChannels!.length); + assert(_extractChannelNames(widget.javascriptChannels).length == widget.javascriptChannels!.length); } } @@ -485,6 +494,7 @@ CreationParams _creationParamsfromWidget(WebView widget) { WebSettings _webSettingsFromWidget(WebView widget) { return WebSettings( javascriptMode: widget.javascriptMode, + mixedContentMode: widget.mixedContentMode, hasNavigationDelegate: widget.navigationDelegate != null, hasProgressTracking: widget.onProgress != null, debuggingEnabled: widget.debuggingEnabled, @@ -495,9 +505,9 @@ WebSettings _webSettingsFromWidget(WebView widget) { } // This method assumes that no fields in `currentValue` are null. -WebSettings _clearUnchangedWebSettings( - WebSettings currentValue, WebSettings newValue) { +WebSettings _clearUnchangedWebSettings(WebSettings currentValue, WebSettings newValue) { assert(currentValue.javascriptMode != null); + assert(currentValue.mixedContentMode != null); assert(currentValue.hasNavigationDelegate != null); assert(currentValue.hasProgressTracking != null); assert(currentValue.debuggingEnabled != null); @@ -508,6 +518,7 @@ WebSettings _clearUnchangedWebSettings( assert(newValue.userAgent != null); JavascriptMode? javascriptMode; + MixedContentMode? mixedContentMode; bool? hasNavigationDelegate; bool? hasProgressTracking; bool? debuggingEnabled; @@ -515,6 +526,9 @@ WebSettings _clearUnchangedWebSettings( if (currentValue.javascriptMode != newValue.javascriptMode) { javascriptMode = newValue.javascriptMode; } + if (currentValue.mixedContentMode != newValue.mixedContentMode) { + mixedContentMode = newValue.mixedContentMode; + } if (currentValue.hasNavigationDelegate != newValue.hasNavigationDelegate) { hasNavigationDelegate = newValue.hasNavigationDelegate; } @@ -530,6 +544,7 @@ WebSettings _clearUnchangedWebSettings( return WebSettings( javascriptMode: javascriptMode, + mixedContentMode: mixedContentMode, hasNavigationDelegate: hasNavigationDelegate, hasProgressTracking: hasProgressTracking, debuggingEnabled: debuggingEnabled, @@ -538,9 +553,8 @@ WebSettings _clearUnchangedWebSettings( } Set _extractChannelNames(Set? channels) { - final Set channelNames = channels == null - ? {} - : channels.map((JavascriptChannel channel) => channel.name).toSet(); + final Set channelNames = + channels == null ? {} : channels.map((JavascriptChannel channel) => channel.name).toSet(); return channelNames; } @@ -552,8 +566,7 @@ class _PlatformCallbacksHandler implements WebViewPlatformCallbacksHandler { WebView _widget; // Maps a channel name to a channel. - final Map _javascriptChannels = - {}; + final Map _javascriptChannels = {}; @override void onJavaScriptChannelMessage(String channel, String message) { @@ -565,11 +578,9 @@ class _PlatformCallbacksHandler implements WebViewPlatformCallbacksHandler { required String url, required bool isForMainFrame, }) async { - final NavigationRequest request = - NavigationRequest._(url: url, isForMainFrame: isForMainFrame); - final bool allowNavigation = _widget.navigationDelegate == null || - await _widget.navigationDelegate!(request) == - NavigationDecision.navigate; + final NavigationRequest request = NavigationRequest._(url: url, isForMainFrame: isForMainFrame); + final bool allowNavigation = + _widget.navigationDelegate == null || await _widget.navigationDelegate!(request) == NavigationDecision.navigate; return allowNavigation; } @@ -717,24 +728,18 @@ class WebViewController { } Future _updateSettings(WebSettings newSettings) { - final WebSettings update = - _clearUnchangedWebSettings(_settings, newSettings); + final WebSettings update = _clearUnchangedWebSettings(_settings, newSettings); _settings = newSettings; return _webViewPlatformController.updateSettings(update); } - Future _updateJavascriptChannels( - Set? newChannels) async { - final Set currentChannels = - _platformCallbacksHandler._javascriptChannels.keys.toSet(); + Future _updateJavascriptChannels(Set? newChannels) async { + final Set currentChannels = _platformCallbacksHandler._javascriptChannels.keys.toSet(); final Set newChannelNames = _extractChannelNames(newChannels); - final Set channelsToAdd = - newChannelNames.difference(currentChannels); - final Set channelsToRemove = - currentChannels.difference(newChannelNames); + final Set channelsToAdd = newChannelNames.difference(currentChannels); + final Set channelsToRemove = currentChannels.difference(newChannelNames); if (channelsToRemove.isNotEmpty) { - await _webViewPlatformController - .removeJavascriptChannels(channelsToRemove); + await _webViewPlatformController.removeJavascriptChannels(channelsToRemove); } if (channelsToAdd.isNotEmpty) { await _webViewPlatformController.addJavascriptChannels(channelsToAdd); @@ -760,8 +765,8 @@ class WebViewController { /// embedded in the main frame HTML has been loaded. Future evaluateJavascript(String javascriptString) { if (_settings.javascriptMode == JavascriptMode.disabled) { - return Future.error(FlutterError( - 'JavaScript mode must be enabled/unrestricted when calling evaluateJavascript.')); + return Future.error( + FlutterError('JavaScript mode must be enabled/unrestricted when calling evaluateJavascript.')); } // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. // https://github.com/flutter/flutter/issues/26431