From 73551b807b1af9de55d3627a441ef2da011820d5 Mon Sep 17 00:00:00 2001 From: Yuki Oya Date: Mon, 27 May 2019 14:38:25 +0900 Subject: [PATCH 1/9] Add UserAgent setting --- .../plugins/webviewflutter/FlutterWebView.java | 7 +++++++ .../webview_flutter/ios/Classes/FlutterWebView.m | 13 +++++++++++++ packages/webview_flutter/lib/webview_flutter.dart | 11 +++++++++++ 3 files changed, 31 insertions(+) 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 a2bed900ad3f..91136fe8bf6f 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 @@ -207,6 +207,9 @@ private void applySettings(Map settings) { webView.setWebContentsDebuggingEnabled(debuggingEnabled); break; + case "userAgent": + updateUserAgent((String) settings.get(key)); + break; default: throw new IllegalArgumentException("Unknown WebView setting: " + key); } @@ -233,6 +236,10 @@ private void registerJavaScriptChannelNames(List channelNames) { } } + private void updateUserAgent(String userAgent) { + webView.getSettings().setUserAgentString(userAgent); + } + @Override public void dispose() { methodChannel.setMethodCallHandler(null); diff --git a/packages/webview_flutter/ios/Classes/FlutterWebView.m b/packages/webview_flutter/ios/Classes/FlutterWebView.m index afc2f9921d05..131750b6ee9a 100644 --- a/packages/webview_flutter/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/ios/Classes/FlutterWebView.m @@ -236,6 +236,9 @@ - (void)applySettings:(NSDictionary*)settings { } else if ([key isEqualToString:@"hasNavigationDelegate"]) { NSNumber* hasDartNavigationDelegate = settings[key]; _navigationDelegate.hasDartNavigationDelegate = [hasDartNavigationDelegate boolValue]; + } else if ([key isEqualToString:@"userAgent"]) { + NSString* userAgent = settings[key]; + [self updateUserAgent:[userAgent isEqual:[NSNull null]] ? nil : userAgent]; } else { NSLog(@"webview_flutter: unknown setting key: %@", key); } @@ -274,6 +277,16 @@ - (bool)loadRequest:(NSDictionary*)request { return false; } +- (void)updateUserAgent:(NSString*)userAgent { + if (@available(iOS 9.0, *)) { + [_webView setCustomUserAgent:userAgent]; + } else if (userAgent) { + [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"UserAgent" : userAgent}]; + WKWebViewConfiguration* configuration = _webView.configuration; + _webView = [[WKWebView alloc] initWithFrame:_webView.frame configuration:configuration]; + } +} + - (bool)loadUrl:(NSString*)url { return [self loadUrl:url withHeaders:[NSMutableDictionary dictionary]]; } diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index 432de6e95d5a..33b3f39763f5 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -119,6 +119,7 @@ class WebView extends StatefulWidget { this.javascriptMode = JavascriptMode.disabled, this.javascriptChannels, this.navigationDelegate, + this.userAgent, this.gestureRecognizers, this.onPageFinished, this.debuggingEnabled = false, @@ -256,6 +257,9 @@ class WebView extends StatefulWidget { /// By default `debuggingEnabled` is false. final bool debuggingEnabled; + /// The custom UserAgent. + final String userAgent; + @override State createState() => _WebViewState(); } @@ -349,6 +353,7 @@ class _WebSettings { this.javascriptMode, this.hasNavigationDelegate, this.debuggingEnabled, + this.userAgent, }); static _WebSettings fromWidget(WebView widget) { @@ -356,18 +361,21 @@ class _WebSettings { javascriptMode: widget.javascriptMode, hasNavigationDelegate: widget.navigationDelegate != null, debuggingEnabled: widget.debuggingEnabled, + userAgent: widget.userAgent, ); } final JavascriptMode javascriptMode; final bool hasNavigationDelegate; final bool debuggingEnabled; + final String userAgent; Map toMap() { return { 'jsMode': javascriptMode.index, 'hasNavigationDelegate': hasNavigationDelegate, 'debuggingEnabled': debuggingEnabled, + 'userAgent': userAgent, }; } @@ -384,6 +392,9 @@ class _WebSettings { updates['debuggingEnabled'] = newSettings.debuggingEnabled; } + if (userAgent != newSettings.userAgent) { + updates['userAgent'] = newSettings.userAgent; + } return updates; } } From d90171407c128958185a597ffc73c027652bb21e Mon Sep 17 00:00:00 2001 From: Yuki Oya Date: Mon, 1 Apr 2019 10:45:47 +0900 Subject: [PATCH 2/9] Add UserAgent getter --- .../plugins/webviewflutter/FlutterWebView.java | 8 ++++++++ .../ios/Classes/FlutterWebView.m | 18 ++++++++++++++++++ .../webview_flutter/lib/webview_flutter.dart | 5 +++++ 3 files changed, 31 insertions(+) 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 91136fe8bf6f..16e6d23f9a8b 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 @@ -63,6 +63,9 @@ public void onMethodCall(MethodCall methodCall, Result result) { case "loadUrl": loadUrl(methodCall, result); break; + case "userAgent": + userAgent(methodCall, result); + break; case "updateSettings": updateSettings(methodCall, result); break; @@ -144,6 +147,11 @@ private void currentUrl(Result result) { result.success(webView.getUrl()); } + private void userAgent(MethodCall methodCall, Result result) { + String userAgent = webView.getSettings().getUserAgentString(); + result.success(userAgent); + } + @SuppressWarnings("unchecked") private void updateSettings(MethodCall methodCall, Result result) { applySettings((Map) methodCall.arguments); diff --git a/packages/webview_flutter/ios/Classes/FlutterWebView.m b/packages/webview_flutter/ios/Classes/FlutterWebView.m index 131750b6ee9a..2a836bf8e637 100644 --- a/packages/webview_flutter/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/ios/Classes/FlutterWebView.m @@ -112,6 +112,8 @@ - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [self onRemoveJavaScriptChannels:call result:result]; } else if ([[call method] isEqualToString:@"clearCache"]) { [self clearCache:result]; + } else if ([[call method] isEqualToString:@"userAgent"]) { + [self onUserAgent:call result:result]; } else { result(FlutterMethodNotImplemented); } @@ -228,6 +230,22 @@ - (void)clearCache:(FlutterResult)result { } } +- (void)onUserAgent:(FlutterMethodCall*)call result:(FlutterResult)result { + [_webView evaluateJavaScript:@"navigator.userAgent" + completionHandler:^(NSString* userAgent, NSError* error) { + if (error) { + result([FlutterError + errorWithCode:@"userAgent_failed" + message:@"Failed getting UserAgent" + details:[NSString stringWithFormat: + @"webview_flutter: fail evaluating JavaScript: %@", + [error localizedDescription]]]); + } else { + result(userAgent); + } + }]; +} + - (void)applySettings:(NSDictionary*)settings { for (NSString* key in settings) { if ([key isEqualToString:@"jsMode"]) { diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index 33b3f39763f5..8a8ed13d9c0b 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -540,6 +540,11 @@ class WebViewController { return _channel.invokeMethod("reload"); } + /// Accessor the UserAgent. + Future userAgent() async { + return await _channel.invokeMethod('userAgent'); + } + /// Clears all caches used by the [WebView]. /// /// The following caches are cleared: From f527cbb59a18f97cd0d43427497e3b2f11d1d977 Mon Sep 17 00:00:00 2001 From: Yuki Oya Date: Mon, 27 May 2019 14:39:28 +0900 Subject: [PATCH 3/9] Add test UserAgent --- .../test/webview_flutter_test.dart | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/packages/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/test/webview_flutter_test.dart index a973e43e6081..55dc9e346be2 100644 --- a/packages/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/test/webview_flutter_test.dart @@ -70,6 +70,42 @@ void main() { expect(platformWebView.javascriptMode, JavascriptMode.disabled); }); + testWidgets('Set UserAgent', (WidgetTester tester) async { + await tester.pumpWidget(const WebView( + initialUrl: 'https://youtube.com', + javascriptMode: JavascriptMode.unrestricted, + )); + + final FakePlatformWebView platformWebView = + fakePlatformViewsController.lastCreatedView; + + expect(platformWebView.userAgent, isNull); + + await tester.pumpWidget(const WebView( + initialUrl: 'https://youtube.com', + javascriptMode: JavascriptMode.unrestricted, + userAgent: 'UA', + )); + + expect(platformWebView.userAgent, 'UA'); + }); + + testWidgets('Get UserAgent', (WidgetTester tester) async { + WebViewController controller; + await tester.pumpWidget( + WebView( + initialUrl: 'https://youtube.com', + javascriptMode: JavascriptMode.unrestricted, + userAgent: 'UA', + onWebViewCreated: (WebViewController webViewController) { + controller = webViewController; + }, + ), + ); + expect(controller, isNotNull); + expect(await controller.userAgent(), 'UA'); + }); + testWidgets('Load url', (WidgetTester tester) async { WebViewController controller; await tester.pumpWidget( @@ -823,6 +859,7 @@ class FakePlatformWebView { hasNavigationDelegate = params['settings']['hasNavigationDelegate'] ?? false; debuggingEnabled = params['settings']['debuggingEnabled']; + userAgent = params['settings']['userAgent']; channel = MethodChannel( 'plugins.flutter.io/webview_$id', const StandardMethodCodec()); @@ -842,6 +879,7 @@ class FakePlatformWebView { bool hasNavigationDelegate; bool debuggingEnabled; + String userAgent; Future onMethodCall(MethodCall call) { switch (call.method) { @@ -849,6 +887,8 @@ class FakePlatformWebView { final Map request = call.arguments; _loadUrl(request['url']); return Future.sync(() {}); + case 'userAgent': + return Future.value(userAgent); case 'updateSettings': if (call.arguments['jsMode'] != null) { javascriptMode = JavascriptMode.values[call.arguments['jsMode']]; @@ -859,6 +899,9 @@ class FakePlatformWebView { if (call.arguments['debuggingEnabled'] != null) { debuggingEnabled = call.arguments['debuggingEnabled']; } + if (call.arguments['userAgent'] != null) { + userAgent = call.arguments['userAgent']; + } break; case 'canGoBack': return Future.sync(() => currentPosition > 0); From d1ff6c6eafaac2db50ffe4e12a1a7e354d8e80ef Mon Sep 17 00:00:00 2001 From: Yuki Oya Date: Mon, 1 Apr 2019 11:23:23 +0900 Subject: [PATCH 4/9] Fix support target for setting UserAgent --- packages/webview_flutter/ios/Classes/FlutterWebView.m | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/webview_flutter/ios/Classes/FlutterWebView.m b/packages/webview_flutter/ios/Classes/FlutterWebView.m index 2a836bf8e637..b53c01dead09 100644 --- a/packages/webview_flutter/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/ios/Classes/FlutterWebView.m @@ -298,10 +298,8 @@ - (bool)loadRequest:(NSDictionary*)request { - (void)updateUserAgent:(NSString*)userAgent { if (@available(iOS 9.0, *)) { [_webView setCustomUserAgent:userAgent]; - } else if (userAgent) { - [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"UserAgent" : userAgent}]; - WKWebViewConfiguration* configuration = _webView.configuration; - _webView = [[WKWebView alloc] initWithFrame:_webView.frame configuration:configuration]; + } else { + NSLog(@"Updating UserAgent is not supported for Flutter WebViews prior to iOS 9."); } } From 9d10411d0f698bdb4a8e01fb2f08697709fe016a Mon Sep 17 00:00:00 2001 From: Yuki Oya Date: Mon, 27 May 2019 14:44:32 +0900 Subject: [PATCH 5/9] Add e2e test --- .../example/test_driver/webview.dart | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/webview_flutter/example/test_driver/webview.dart b/packages/webview_flutter/example/test_driver/webview.dart index 7a290a05bd5b..aef26e9749bf 100644 --- a/packages/webview_flutter/example/test_driver/webview.dart +++ b/packages/webview_flutter/example/test_driver/webview.dart @@ -132,6 +132,28 @@ void main() { await controller.evaluateJavascript('Echo.postMessage("hello");'); expect(messagesReceived, equals(['hello'])); }); + + test('userAgent', () async { + final Completer controllerCompleter = + Completer(); + await pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: WebView( + key: GlobalKey(), + initialUrl: 'https://flutter.dev/', + javascriptMode: JavascriptMode.unrestricted, + userAgent: 'UA', + onWebViewCreated: (WebViewController controller) { + controllerCompleter.complete(controller); + }, + ), + ), + ); + final WebViewController controller = await controllerCompleter.future; + final String userAgent = await controller.userAgent(); + expect(userAgent, 'UA'); + }); } Future pumpWidget(Widget widget) { From ec1a90a4f813b61e36cca7b7817bfcfd44f23ea3 Mon Sep 17 00:00:00 2001 From: Yuki Oya Date: Mon, 27 May 2019 14:46:43 +0900 Subject: [PATCH 6/9] Fix getter docs --- packages/webview_flutter/lib/webview_flutter.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index 8a8ed13d9c0b..32d0bc234068 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -540,7 +540,7 @@ class WebViewController { return _channel.invokeMethod("reload"); } - /// Accessor the UserAgent. + /// Returns the User-Agent value that will be used for subsequent HTTP requests. Future userAgent() async { return await _channel.invokeMethod('userAgent'); } From 01dd5ada3296040069bad120f20309259c45ebdc Mon Sep 17 00:00:00 2001 From: Yuki Oya Date: Mon, 27 May 2019 14:48:21 +0900 Subject: [PATCH 7/9] Fix getter name --- .../io/flutter/plugins/webviewflutter/FlutterWebView.java | 6 +++--- packages/webview_flutter/example/test_driver/webview.dart | 2 +- packages/webview_flutter/ios/Classes/FlutterWebView.m | 6 +++--- packages/webview_flutter/lib/webview_flutter.dart | 4 ++-- packages/webview_flutter/test/webview_flutter_test.dart | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) 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 16e6d23f9a8b..a6de2939ec1e 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 @@ -63,8 +63,8 @@ public void onMethodCall(MethodCall methodCall, Result result) { case "loadUrl": loadUrl(methodCall, result); break; - case "userAgent": - userAgent(methodCall, result); + case "getUserAgent": + getUserAgent(methodCall, result); break; case "updateSettings": updateSettings(methodCall, result); @@ -147,7 +147,7 @@ private void currentUrl(Result result) { result.success(webView.getUrl()); } - private void userAgent(MethodCall methodCall, Result result) { + private void getUserAgent(MethodCall methodCall, Result result) { String userAgent = webView.getSettings().getUserAgentString(); result.success(userAgent); } diff --git a/packages/webview_flutter/example/test_driver/webview.dart b/packages/webview_flutter/example/test_driver/webview.dart index aef26e9749bf..865e9d7b4e0f 100644 --- a/packages/webview_flutter/example/test_driver/webview.dart +++ b/packages/webview_flutter/example/test_driver/webview.dart @@ -151,7 +151,7 @@ void main() { ), ); final WebViewController controller = await controllerCompleter.future; - final String userAgent = await controller.userAgent(); + final String userAgent = await controller.getUserAgent(); expect(userAgent, 'UA'); }); } diff --git a/packages/webview_flutter/ios/Classes/FlutterWebView.m b/packages/webview_flutter/ios/Classes/FlutterWebView.m index b53c01dead09..dc5948164fc3 100644 --- a/packages/webview_flutter/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/ios/Classes/FlutterWebView.m @@ -112,8 +112,8 @@ - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [self onRemoveJavaScriptChannels:call result:result]; } else if ([[call method] isEqualToString:@"clearCache"]) { [self clearCache:result]; - } else if ([[call method] isEqualToString:@"userAgent"]) { - [self onUserAgent:call result:result]; + } else if ([[call method] isEqualToString:@"getUserAgent"]) { + [self onGetUserAgent:call result:result]; } else { result(FlutterMethodNotImplemented); } @@ -230,7 +230,7 @@ - (void)clearCache:(FlutterResult)result { } } -- (void)onUserAgent:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)onGetUserAgent:(FlutterMethodCall*)call result:(FlutterResult)result { [_webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(NSString* userAgent, NSError* error) { if (error) { diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index 32d0bc234068..f2e4c13ad1dd 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -541,8 +541,8 @@ class WebViewController { } /// Returns the User-Agent value that will be used for subsequent HTTP requests. - Future userAgent() async { - return await _channel.invokeMethod('userAgent'); + Future getUserAgent() async { + return await _channel.invokeMethod('getUserAgent'); } /// Clears all caches used by the [WebView]. diff --git a/packages/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/test/webview_flutter_test.dart index 55dc9e346be2..7a7a98c80c2c 100644 --- a/packages/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/test/webview_flutter_test.dart @@ -103,7 +103,7 @@ void main() { ), ); expect(controller, isNotNull); - expect(await controller.userAgent(), 'UA'); + expect(await controller.getUserAgent(), 'UA'); }); testWidgets('Load url', (WidgetTester tester) async { From 92a27f86f585f44a78b748e5daca6ad68e5866b1 Mon Sep 17 00:00:00 2001 From: Yuki Oya Date: Mon, 27 May 2019 15:10:47 +0900 Subject: [PATCH 8/9] Add rebuild test case --- .../example/test_driver/webview.dart | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/webview_flutter/example/test_driver/webview.dart b/packages/webview_flutter/example/test_driver/webview.dart index 865e9d7b4e0f..544f80e2de6e 100644 --- a/packages/webview_flutter/example/test_driver/webview.dart +++ b/packages/webview_flutter/example/test_driver/webview.dart @@ -134,7 +134,7 @@ void main() { }); test('userAgent', () async { - final Completer controllerCompleter = + final Completer controllerCompleter01 = Completer(); await pumpWidget( Directionality( @@ -143,16 +143,37 @@ void main() { key: GlobalKey(), initialUrl: 'https://flutter.dev/', javascriptMode: JavascriptMode.unrestricted, - userAgent: 'UA', + userAgent: 'UA-01', onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); + controllerCompleter01.complete(controller); }, ), ), ); - final WebViewController controller = await controllerCompleter.future; - final String userAgent = await controller.getUserAgent(); - expect(userAgent, 'UA'); + final WebViewController controller01 = await controllerCompleter01.future; + final String userAgent01 = await controller01.getUserAgent(); + expect(userAgent01, 'UA-01'); + + // rebuilds a WebView with a different user agent. + final Completer controllerCompleter02 = + Completer(); + await pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: WebView( + key: GlobalKey(), + initialUrl: 'https://flutter.dev/', + javascriptMode: JavascriptMode.unrestricted, + userAgent: 'UA-02', + onWebViewCreated: (WebViewController controller) { + controllerCompleter02.complete(controller); + }, + ), + ), + ); + final WebViewController controller02 = await controllerCompleter02.future; + final String userAgent02 = await controller02.getUserAgent(); + expect(userAgent02, 'UA-02'); }); } From 7d40cd13585b2df99cc14ad9bb723b79080ef586 Mon Sep 17 00:00:00 2001 From: Yuki Oya Date: Tue, 28 May 2019 11:00:02 +0900 Subject: [PATCH 9/9] Fix format --- packages/webview_flutter/example/test_driver/webview.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/example/test_driver/webview.dart b/packages/webview_flutter/example/test_driver/webview.dart index 544f80e2de6e..179348201c1b 100644 --- a/packages/webview_flutter/example/test_driver/webview.dart +++ b/packages/webview_flutter/example/test_driver/webview.dart @@ -135,7 +135,7 @@ void main() { test('userAgent', () async { final Completer controllerCompleter01 = - Completer(); + Completer(); await pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -156,7 +156,7 @@ void main() { // rebuilds a WebView with a different user agent. final Completer controllerCompleter02 = - Completer(); + Completer(); await pumpWidget( Directionality( textDirection: TextDirection.ltr,