From 3b9f646bf76a241c290f17a77028a450c09e990c Mon Sep 17 00:00:00 2001 From: Amir Hardon Date: Tue, 6 Aug 2019 12:40:06 -0700 Subject: [PATCH 1/5] Support controlling the auto media playback policy --- packages/webview_flutter/CHANGELOG.md | 5 + .../webviewflutter/FlutterWebView.java | 8 + .../example/test_driver/webview.dart | 153 ++++++++++++++++++ .../ios/Classes/FlutterWebView.m | 28 +++- .../lib/platform_interface.dart | 14 +- .../lib/src/webview_method_channel.dart | 3 +- .../webview_flutter/lib/webview_flutter.dart | 36 ++++- packages/webview_flutter/pubspec.yaml | 2 +- 8 files changed, 240 insertions(+), 9 deletions(-) diff --git a/packages/webview_flutter/CHANGELOG.md b/packages/webview_flutter/CHANGELOG.md index d5801c7a6516..eb7b1bc4684f 100644 --- a/packages/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.3.11 + +* Add an initialAutoMediaPlaybackPolicy setting for controlling how auto media + playback is restricted. + ## 0.3.10+5 * Add dependency on `androidx.annotation:annotation:1.0.0`. 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 838987714d31..80431ad4cd5b 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 @@ -51,6 +51,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler { registerJavaScriptChannelNames((List) params.get(JS_CHANNEL_NAMES_FIELD)); } + updateAutoMediaPlaybackPolicy((Integer) params.get("autoMediaPlaybackPolicy")); if (params.containsKey("initialUrl")) { String url = (String) params.get("initialUrl"); webView.loadUrl(url); @@ -251,6 +252,13 @@ private void updateJsMode(int 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. + boolean requireUserGesture = mode != 1; + webView.getSettings().setMediaPlaybackRequiresUserGesture(requireUserGesture); + } + private void registerJavaScriptChannelNames(List channelNames) { for (String channelName : channelNames) { webView.addJavascriptInterface( diff --git a/packages/webview_flutter/example/test_driver/webview.dart b/packages/webview_flutter/example/test_driver/webview.dart index 7a290a05bd5b..9f90ef4f0c09 100644 --- a/packages/webview_flutter/example/test_driver/webview.dart +++ b/packages/webview_flutter/example/test_driver/webview.dart @@ -3,7 +3,9 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; +import 'dart:convert'; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -132,9 +134,160 @@ void main() { await controller.evaluateJavascript('Echo.postMessage("hello");'); expect(messagesReceived, equals(['hello'])); }); + + group('Media playback policy', () { + final String audioTest = ''' + + Audio auto play + + + + + + + '''; + final String audioTestBase64 = + base64Encode(const Utf8Encoder().convert(audioTest)); + + test('Auto media playback', () async { + Completer controllerCompleter = + Completer(); + Completer pageLoaded = Completer(); + + await pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: WebView( + key: GlobalKey(), + initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', + onWebViewCreated: (WebViewController controller) { + controllerCompleter.complete(controller); + }, + javascriptMode: JavascriptMode.unrestricted, + onPageFinished: (String url) { + pageLoaded.complete(null); + }, + initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, + ), + ), + ); + WebViewController controller = await controllerCompleter.future; + await pageLoaded.future; + + String isPaused = await controller.evaluateJavascript('isPaused();'); + expect(isPaused, _webviewBool(false)); + + controllerCompleter = Completer(); + pageLoaded = Completer(); + + // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy + await pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: WebView( + key: GlobalKey(), + initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', + onWebViewCreated: (WebViewController controller) { + controllerCompleter.complete(controller); + }, + javascriptMode: JavascriptMode.unrestricted, + onPageFinished: (String url) { + pageLoaded.complete(null); + }, + initialMediaPlaybackPolicy: + AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, + ), + ), + ); + + controller = await controllerCompleter.future; + await pageLoaded.future; + + isPaused = await controller.evaluateJavascript('isPaused();'); + expect(isPaused, _webviewBool(true)); + }); + + test('Changes to initialMediaPlaybackPolocy are ignored', () async { + final Completer controllerCompleter = + Completer(); + Completer pageLoaded = Completer(); + + final GlobalKey key = GlobalKey(); + await pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: WebView( + key: key, + initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', + onWebViewCreated: (WebViewController controller) { + controllerCompleter.complete(controller); + }, + javascriptMode: JavascriptMode.unrestricted, + onPageFinished: (String url) { + pageLoaded.complete(null); + }, + initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, + ), + ), + ); + final WebViewController controller = await controllerCompleter.future; + await pageLoaded.future; + + String isPaused = await controller.evaluateJavascript('isPaused();'); + expect(isPaused, _webviewBool(false)); + + pageLoaded = Completer(); + + await pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: WebView( + key: key, + initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64', + onWebViewCreated: (WebViewController controller) { + controllerCompleter.complete(controller); + }, + javascriptMode: JavascriptMode.unrestricted, + onPageFinished: (String url) { + pageLoaded.complete(null); + }, + initialMediaPlaybackPolicy: + AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, + ), + ), + ); + + await controller.reload(); + + await pageLoaded.future; + + isPaused = await controller.evaluateJavascript('isPaused();'); + expect(isPaused, _webviewBool(false)); + }); + }); } Future pumpWidget(Widget widget) { runApp(widget); return WidgetsBinding.instance.endOfFrame; } + +// JavaScript booleans evaluate to different string values on Android and iOS. +// This utility method returns the string boolean value of the current platform. +String _webviewBool(bool value) { + if (defaultTargetPlatform == TargetPlatform.iOS) { + return value ? '1' : '0'; + } + return value ? 'true' : 'false'; +} diff --git a/packages/webview_flutter/ios/Classes/FlutterWebView.m b/packages/webview_flutter/ios/Classes/FlutterWebView.m index c56d5c7dbcc3..d8919562bc0e 100644 --- a/packages/webview_flutter/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/ios/Classes/FlutterWebView.m @@ -62,8 +62,11 @@ - (instancetype)initWithFrame:(CGRect)frame [self registerJavaScriptChannels:_javaScriptChannelNames controller:userContentController]; } + NSDictionary* settings = args[@"settings"]; + WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init]; configuration.userContentController = userContentController; + [self updateAutoMediaPlaybackPolicy:args[@"autoMediaPlaybackPolicy"] inConfiguration:configuration]; _webView = [[WKWebView alloc] initWithFrame:frame configuration:configuration]; _navigationDelegate = [[FLTWKNavigationDelegate alloc] initWithChannel:_channel]; @@ -72,7 +75,7 @@ - (instancetype)initWithFrame:(CGRect)frame [_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { [weakSelf onMethodCall:call result:result]; }]; - NSDictionary* settings = args[@"settings"]; + [self applySettings:settings]; // TODO(amirh): return an error if apply settings failed once it's possible to do so. // https://github.com/flutter/flutter/issues/36228 @@ -271,6 +274,29 @@ - (void)updateJsMode:(NSNumber*)mode { } } +- (void)updateAutoMediaPlaybackPolicy:(NSNumber*)policy inConfiguration:(WKWebViewConfiguration*)configuration { + switch ([policy integerValue]) { + case 0: // require_user_action_for_all_media_types + NSLog(@"requiring user action for all types"); + if (@available(iOS 10.0, *)) { + configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeAll; + } else { + configuration.mediaPlaybackRequiresUserAction = true; + } + break; + case 1: // always_allow + NSLog(@"allowing auto playback"); + if (@available(iOS 10.0, *)) { + configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; + } else { + configuration.mediaPlaybackRequiresUserAction = false; + } + break; + default: + NSLog(@"webview_flutter: unknown auto media plaback policy: %@", policy); + } +} + - (bool)loadRequest:(NSDictionary*)request { if (!request) { return false; diff --git a/packages/webview_flutter/lib/platform_interface.dart b/packages/webview_flutter/lib/platform_interface.dart index bfb5b6255421..4e6b8b86cacf 100644 --- a/packages/webview_flutter/lib/platform_interface.dart +++ b/packages/webview_flutter/lib/platform_interface.dart @@ -183,9 +183,16 @@ class WebSettings { } /// Configuration to use when creating a new [WebViewPlatformController]. +/// +/// The `autoMediaPlaybackPolicy` parameter must not be null. class CreationParams { - CreationParams( - {this.initialUrl, this.webSettings, this.javascriptChannelNames}); + CreationParams({ + this.initialUrl, + this.webSettings, + this.javascriptChannelNames, + this.autoMediaPlaybackPolicy = + AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, + }) : assert(autoMediaPlaybackPolicy != null); /// The initialUrl to load in the webview. /// @@ -210,6 +217,9 @@ class CreationParams { // to PlatformWebView. final Set javascriptChannelNames; + /// Which restrictions apply on automatic media playback. + final AutoMediaPlaybackPolicy autoMediaPlaybackPolicy; + @override String toString() { return '$runtimeType(initialUrl: $initialUrl, settings: $webSettings, javascriptChannelNames: $javascriptChannelNames)'; diff --git a/packages/webview_flutter/lib/src/webview_method_channel.dart b/packages/webview_flutter/lib/src/webview_method_channel.dart index ce9988743853..a914e1828b6b 100644 --- a/packages/webview_flutter/lib/src/webview_method_channel.dart +++ b/packages/webview_flutter/lib/src/webview_method_channel.dart @@ -103,7 +103,7 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController { 'removeJavascriptChannels', javascriptChannelNames.toList()); } - /// Method channel mplementation for [WebViewPlatform.clearCookies]. + /// Method channel implementation for [WebViewPlatform.clearCookies]. static Future clearCookies() { return _cookieManagerChannel .invokeMethod('clearCookies') @@ -135,6 +135,7 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController { 'initialUrl': creationParams.initialUrl, 'settings': _webSettingsToMap(creationParams.webSettings), 'javascriptChannelNames': creationParams.javascriptChannelNames.toList(), + 'autoMediaPlaybackPolicy': creationParams.autoMediaPlaybackPolicy.index, }; } } diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index 3c0175e2f22b..2b0cca092e5d 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -72,6 +72,23 @@ typedef NavigationDecision NavigationDelegate(NavigationRequest navigation); /// Signature for when a [WebView] has finished loading a page. typedef void PageFinishedCallback(String url); +/// Specifies possible restrictions on automatic media playback. +/// +/// This is typically used in [WebView.initialMediaPlaybackPolicy]. +enum AutoMediaPlaybackPolicy { + /// Starting any kind of media playback requires a user action. + /// + /// For example: JavaScript code cannot start playing media unless the code was executed + /// as a result of a user action (like a touch event). + require_user_action_for_all_media_types, + + /// Starting any kind of media playback is always allowed. + /// + /// For example: JavaScript code that's triggered when the page is loaded can start playing + /// video or audio. + always_allow, +} + final RegExp _validChannelNames = RegExp('^[a-zA-Z_][a-zA-Z0-9]*\$'); /// A named channel for receiving messaged from JavaScript code running inside a web view. @@ -110,7 +127,7 @@ class WebView extends StatefulWidget { /// The web view can be controlled using a `WebViewController` that is passed to the /// `onWebViewCreated` callback once the web view is created. /// - /// The `javascriptMode` parameter must not be null. + /// The `javascriptMode` and `autoMediaPlaybackPolicy` parameters must not be null. const WebView({ Key key, this.onWebViewCreated, @@ -121,7 +138,10 @@ class WebView extends StatefulWidget { this.gestureRecognizers, this.onPageFinished, this.debuggingEnabled = false, + this.initialMediaPlaybackPolicy = + AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, }) : assert(javascriptMode != null), + assert(initialMediaPlaybackPolicy != null), super(key: key); static WebViewPlatform _platform; @@ -255,6 +275,12 @@ class WebView extends StatefulWidget { /// By default `debuggingEnabled` is false. final bool debuggingEnabled; + /// Which restrictions apply on automatic media playback. + /// + /// This initial value is applied to the platform's webview upon creation. Any following + /// changes to this parameter are ignored (as long as the state of the [WebView] is preserved). + final AutoMediaPlaybackPolicy initialMediaPlaybackPolicy; + @override State createState() => _WebViewState(); } @@ -317,6 +343,7 @@ CreationParams _creationParamsfromWidget(WebView widget) { initialUrl: widget.initialUrl, webSettings: _webSettingsFromWidget(widget), javascriptChannelNames: _extractChannelNames(widget.javascriptChannels), + autoMediaPlaybackPolicy: widget.initialMediaPlaybackPolicy, ); } @@ -351,9 +378,10 @@ WebSettings _clearUnchangedWebSettings( } return WebSettings( - javascriptMode: javascriptMode, - hasNavigationDelegate: hasNavigationDelegate, - debuggingEnabled: debuggingEnabled); + javascriptMode: javascriptMode, + hasNavigationDelegate: hasNavigationDelegate, + debuggingEnabled: debuggingEnabled, + ); } Set _extractChannelNames(Set channels) { diff --git a/packages/webview_flutter/pubspec.yaml b/packages/webview_flutter/pubspec.yaml index c4940c215e0e..6cde699ea336 100644 --- a/packages/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. -version: 0.3.10+5 +version: 0.3.11 author: Flutter Team homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter From 35aa216835c64473db4d0ab5d5437a9a193831bc Mon Sep 17 00:00:00 2001 From: Amir Hardon Date: Tue, 6 Aug 2019 16:38:59 -0700 Subject: [PATCH 2/5] format --- packages/webview_flutter/ios/Classes/FlutterWebView.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/ios/Classes/FlutterWebView.m b/packages/webview_flutter/ios/Classes/FlutterWebView.m index d8919562bc0e..c8b7c8798e30 100644 --- a/packages/webview_flutter/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/ios/Classes/FlutterWebView.m @@ -66,7 +66,8 @@ - (instancetype)initWithFrame:(CGRect)frame WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init]; configuration.userContentController = userContentController; - [self updateAutoMediaPlaybackPolicy:args[@"autoMediaPlaybackPolicy"] inConfiguration:configuration]; + [self updateAutoMediaPlaybackPolicy:args[@"autoMediaPlaybackPolicy"] + inConfiguration:configuration]; _webView = [[WKWebView alloc] initWithFrame:frame configuration:configuration]; _navigationDelegate = [[FLTWKNavigationDelegate alloc] initWithChannel:_channel]; @@ -274,7 +275,8 @@ - (void)updateJsMode:(NSNumber*)mode { } } -- (void)updateAutoMediaPlaybackPolicy:(NSNumber*)policy inConfiguration:(WKWebViewConfiguration*)configuration { +- (void)updateAutoMediaPlaybackPolicy:(NSNumber*)policy + inConfiguration:(WKWebViewConfiguration*)configuration { switch ([policy integerValue]) { case 0: // require_user_action_for_all_media_types NSLog(@"requiring user action for all types"); From 4394cda62ac5585ed91adc2b8c3df7528dea454c Mon Sep 17 00:00:00 2001 From: Amir Hardon Date: Wed, 7 Aug 2019 10:11:54 -0700 Subject: [PATCH 3/5] review comments followup --- packages/webview_flutter/ios/Classes/FlutterWebView.m | 2 +- packages/webview_flutter/lib/webview_flutter.dart | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/webview_flutter/ios/Classes/FlutterWebView.m b/packages/webview_flutter/ios/Classes/FlutterWebView.m index c8b7c8798e30..87cb0f57377b 100644 --- a/packages/webview_flutter/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/ios/Classes/FlutterWebView.m @@ -295,7 +295,7 @@ - (void)updateAutoMediaPlaybackPolicy:(NSNumber*)policy } break; default: - NSLog(@"webview_flutter: unknown auto media plaback policy: %@", policy); + NSLog(@"webview_flutter: unknown auto media playback policy: %@", policy); } } diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index 2b0cca092e5d..4335ed27ada8 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -75,6 +75,8 @@ typedef void PageFinishedCallback(String url); /// Specifies possible restrictions on automatic media playback. /// /// This is typically used in [WebView.initialMediaPlaybackPolicy]. +// The method channel implementation is marshalling this enum to the value's index, so the order +// is important. enum AutoMediaPlaybackPolicy { /// Starting any kind of media playback requires a user action. /// @@ -279,6 +281,8 @@ class WebView extends StatefulWidget { /// /// This initial value is applied to the platform's webview upon creation. Any following /// changes to this parameter are ignored (as long as the state of the [WebView] is preserved). + /// + /// The default policy is [AutoMediaPlaybackPolicy.require_user_action_for_all_media_types]. final AutoMediaPlaybackPolicy initialMediaPlaybackPolicy; @override From 3b7b0e2bcdc72834a4e30f087880cc952411eb90 Mon Sep 17 00:00:00 2001 From: Amir Hardon Date: Wed, 7 Aug 2019 12:52:41 -0700 Subject: [PATCH 4/5] use a bundled audio asset in the test --- .../example/assets/sample_audio.ogg | Bin 0 -> 36870 bytes packages/webview_flutter/example/pubspec.yaml | 2 + .../example/test_driver/webview.dart | 54 ++++++++++-------- 3 files changed, 33 insertions(+), 23 deletions(-) create mode 100644 packages/webview_flutter/example/assets/sample_audio.ogg diff --git a/packages/webview_flutter/example/assets/sample_audio.ogg b/packages/webview_flutter/example/assets/sample_audio.ogg new file mode 100644 index 0000000000000000000000000000000000000000..27e17104277b38ae70d3936f5afb4ea32477ec22 GIT binary patch literal 36870 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 XfG+?r+!9a#U;+;`01|XOAOHdYynO-T literal 0 HcmV?d00001 diff --git a/packages/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/example/pubspec.yaml index 6ec8a1ddd0ea..d050ea67cd70 100644 --- a/packages/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/example/pubspec.yaml @@ -20,3 +20,5 @@ dev_dependencies: flutter: uses-material-design: true + assets: + - assets/sample_audio.ogg diff --git a/packages/webview_flutter/example/test_driver/webview.dart b/packages/webview_flutter/example/test_driver/webview.dart index 9f90ef4f0c09..b6abff94e6b2 100644 --- a/packages/webview_flutter/example/test_driver/webview.dart +++ b/packages/webview_flutter/example/test_driver/webview.dart @@ -4,8 +4,10 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:typed_data'; import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -136,29 +138,35 @@ void main() { }); group('Media playback policy', () { - final String audioTest = ''' - - Audio auto play - - - - - - - '''; - final String audioTestBase64 = - base64Encode(const Utf8Encoder().convert(audioTest)); + String audioTestBase64; + setUpAll(() async { + final ByteData audioData = + await rootBundle.load('assets/sample_audio.ogg'); + final String base64AudioData = + base64Encode(Uint8List.view(audioData.buffer)); + final String audioTest = ''' + + Audio auto play + + + + + + + '''; + audioTestBase64 = base64Encode(const Utf8Encoder().convert(audioTest)); + }); test('Auto media playback', () async { Completer controllerCompleter = From e69ae45950eabfad1e556080a907100320365ca5 Mon Sep 17 00:00:00 2001 From: Amir Hardon Date: Wed, 7 Aug 2019 13:07:49 -0700 Subject: [PATCH 5/5] bump the version for the sample app --- packages/webview_flutter/example/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/example/pubspec.yaml index d050ea67cd70..8657cfde0f8b 100644 --- a/packages/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/example/pubspec.yaml @@ -1,7 +1,7 @@ name: webview_flutter_example description: Demonstrates how to use the webview_flutter plugin. -version: 1.0.0+1 +version: 1.0.1 environment: sdk: ">=2.0.0-dev.68.0 <3.0.0"