[webview_flutter_wkwebview] Implementation of WebKitWebViewController for WebKit (#6105)
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart
new file mode 100644
index 0000000..48e6faf
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart
@@ -0,0 +1,47 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import '../../foundation/foundation.dart';
+import '../../web_kit/web_kit.dart';
+
+/// Handles constructing objects and calling static methods for the WebKit
+/// native library.
+///
+/// This class provides dependency injection for the implementations of the
+/// platform interface classes. Improving the ease of unit testing and/or
+/// overriding the underlying WebKit classes.
+///
+/// By default each function calls the default constructor of the WebKit class
+/// it intends to return.
+class WebKitProxy {
+ /// Constructs a [WebKitProxy].
+ const WebKitProxy({
+ this.createWebView = WKWebView.new,
+ this.createWebViewConfiguration = WKWebViewConfiguration.new,
+ this.createScriptMessageHandler = WKScriptMessageHandler.new,
+ });
+
+ /// Constructs a [WKWebView].
+ final WKWebView Function(
+ WKWebViewConfiguration configuration, {
+ void Function(
+ String keyPath,
+ NSObject object,
+ Map<NSKeyValueChangeKey, Object?> change,
+ )
+ observeValue,
+ }) createWebView;
+
+ /// Constructs a [WKWebViewConfiguration].
+ final WKWebViewConfiguration Function() createWebViewConfiguration;
+
+ /// Constructs a [WKScriptMessageHandler].
+ final WKScriptMessageHandler Function({
+ required void Function(
+ WKUserContentController userContentController,
+ WKScriptMessage message,
+ )
+ didReceiveScriptMessage,
+ }) createScriptMessageHandler;
+}
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart
new file mode 100644
index 0000000..9d76b5c
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart
@@ -0,0 +1,354 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:math';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:path/path.dart' as path;
+import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart';
+
+import '../../common/weak_reference_utils.dart';
+import '../../foundation/foundation.dart';
+import '../../web_kit/web_kit.dart';
+import 'webkit_proxy.dart';
+
+/// Object specifying creation parameters for a [WebKitWebViewController].
+@immutable
+class WebKitWebViewControllerCreationParams
+ extends PlatformWebViewControllerCreationParams {
+ /// Constructs a [WebKitWebViewControllerCreationParams].
+ WebKitWebViewControllerCreationParams({
+ @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(),
+ }) : _configuration = webKitProxy.createWebViewConfiguration();
+
+ /// Constructs a [WebKitWebViewControllerCreationParams] using a
+ /// [PlatformWebViewControllerCreationParams].
+ WebKitWebViewControllerCreationParams.fromPlatformWebViewControllerCreationParams(
+ // Recommended placeholder to prevent being broken by platform interface.
+ // ignore: avoid_unused_constructor_parameters
+ PlatformWebViewControllerCreationParams params, {
+ @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(),
+ }) : this(webKitProxy: webKitProxy);
+
+ final WKWebViewConfiguration _configuration;
+}
+
+/// An implementation of [PlatformWebViewController] with the WebKit api.
+class WebKitWebViewController extends PlatformWebViewController {
+ /// Constructs a [WebKitWebViewController].
+ WebKitWebViewController(
+ PlatformWebViewControllerCreationParams params, {
+ @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(),
+ }) : super.implementation(params is WebKitWebViewControllerCreationParams
+ ? params
+ : WebKitWebViewControllerCreationParams
+ .fromPlatformWebViewControllerCreationParams(params)) {
+ _webView = webKitProxy.createWebView(
+ (params as WebKitWebViewControllerCreationParams)._configuration);
+ }
+
+ late final WKWebView _webView;
+
+ final Map<String, WebKitJavaScriptChannelParams> _javaScriptChannelParams =
+ <String, WebKitJavaScriptChannelParams>{};
+
+ bool _zoomEnabled = true;
+
+ @override
+ Future<void> loadFile(String absoluteFilePath) {
+ return _webView.loadFileUrl(
+ absoluteFilePath,
+ readAccessUrl: path.dirname(absoluteFilePath),
+ );
+ }
+
+ @override
+ Future<void> loadFlutterAsset(String key) {
+ assert(key.isNotEmpty);
+ return _webView.loadFlutterAsset(key);
+ }
+
+ @override
+ Future<void> loadHtmlString(String html, {String? baseUrl}) {
+ return _webView.loadHtmlString(html, baseUrl: baseUrl);
+ }
+
+ @override
+ Future<void> loadRequest(LoadRequestParams params) {
+ if (!params.uri.hasScheme) {
+ throw ArgumentError(
+ 'LoadRequestParams#uri is required to have a scheme.',
+ );
+ }
+
+ return _webView.loadRequest(NSUrlRequest(
+ url: params.uri.toString(),
+ allHttpHeaderFields: params.headers,
+ httpMethod: describeEnum(params.method),
+ httpBody: params.body,
+ ));
+ }
+
+ @override
+ Future<void> addJavaScriptChannel(
+ JavaScriptChannelParams javaScriptChannelParams,
+ ) {
+ final WebKitJavaScriptChannelParams webKitParams =
+ javaScriptChannelParams is WebKitJavaScriptChannelParams
+ ? javaScriptChannelParams
+ : WebKitJavaScriptChannelParams.fromJavaScriptChannelParams(
+ javaScriptChannelParams);
+
+ _javaScriptChannelParams[webKitParams.name] = webKitParams;
+
+ final String wrapperSource =
+ 'window.${webKitParams.name} = webkit.messageHandlers.${webKitParams.name};';
+ final WKUserScript wrapperScript = WKUserScript(
+ wrapperSource,
+ WKUserScriptInjectionTime.atDocumentStart,
+ isMainFrameOnly: false,
+ );
+ _webView.configuration.userContentController.addUserScript(wrapperScript);
+ return _webView.configuration.userContentController.addScriptMessageHandler(
+ webKitParams._messageHandler,
+ webKitParams.name,
+ );
+ }
+
+ @override
+ Future<void> removeJavaScriptChannel(String javaScriptChannelName) async {
+ assert(javaScriptChannelName.isNotEmpty);
+ if (!_javaScriptChannelParams.containsKey(javaScriptChannelName)) {
+ return;
+ }
+ await _resetUserScripts(removedJavaScriptChannel: javaScriptChannelName);
+ }
+
+ @override
+ Future<String?> currentUrl() => _webView.getUrl();
+
+ @override
+ Future<bool> canGoBack() => _webView.canGoBack();
+
+ @override
+ Future<bool> canGoForward() => _webView.canGoForward();
+
+ @override
+ Future<void> goBack() => _webView.goBack();
+
+ @override
+ Future<void> goForward() => _webView.goForward();
+
+ @override
+ Future<void> reload() => _webView.reload();
+
+ @override
+ Future<void> clearCache() {
+ return _webView.configuration.websiteDataStore.removeDataOfTypes(
+ <WKWebsiteDataType>{
+ WKWebsiteDataType.memoryCache,
+ WKWebsiteDataType.diskCache,
+ WKWebsiteDataType.offlineWebApplicationCache,
+ },
+ DateTime.fromMillisecondsSinceEpoch(0),
+ );
+ }
+
+ @override
+ Future<void> clearLocalStorage() {
+ return _webView.configuration.websiteDataStore.removeDataOfTypes(
+ <WKWebsiteDataType>{WKWebsiteDataType.localStorage},
+ DateTime.fromMillisecondsSinceEpoch(0),
+ );
+ }
+
+ @override
+ Future<void> runJavaScript(String javaScript) async {
+ try {
+ await _webView.evaluateJavaScript(javaScript);
+ } on PlatformException catch (exception) {
+ // WebKit will throw an error when the type of the evaluated value is
+ // unsupported. This also goes for `null` and `undefined` on iOS 14+. For
+ // example, when running a void function. For ease of use, this specific
+ // error is ignored when no return value is expected.
+ if (exception.details is! NSError ||
+ exception.details.code !=
+ WKErrorCode.javaScriptResultTypeIsUnsupported) {
+ rethrow;
+ }
+ }
+ }
+
+ @override
+ Future<String> runJavaScriptReturningResult(String javaScript) async {
+ final Object? result = await _webView.evaluateJavaScript(javaScript);
+ if (result == null) {
+ throw ArgumentError(
+ 'Result of JavaScript execution returned a `null` value. '
+ 'Use `runJavascript` when expecting a null return value.',
+ );
+ }
+ return result.toString();
+ }
+
+ /// Controls whether inline playback of HTML5 videos is allowed.
+ Future<void> setAllowsInlineMediaPlayback(bool allow) {
+ return _webView.configuration.setAllowsInlineMediaPlayback(allow);
+ }
+
+ @override
+ Future<String?> getTitle() => _webView.getTitle();
+
+ @override
+ Future<void> scrollTo(int x, int y) {
+ return _webView.scrollView.setContentOffset(Point<double>(
+ x.toDouble(),
+ y.toDouble(),
+ ));
+ }
+
+ @override
+ Future<void> scrollBy(int x, int y) {
+ return _webView.scrollView.scrollBy(Point<double>(
+ x.toDouble(),
+ y.toDouble(),
+ ));
+ }
+
+ @override
+ Future<Point<int>> getScrollPosition() async {
+ final Point<double> offset = await _webView.scrollView.getContentOffset();
+ return Point<int>(offset.x.round(), offset.y.round());
+ }
+
+ // TODO(bparrishMines): This is unique to iOS. Override should be removed if
+ // this is removed from the platform interface before webview_flutter version
+ // 4.0.0.
+ @override
+ Future<void> enableGestureNavigation(bool enabled) {
+ return _webView.setAllowsBackForwardNavigationGestures(enabled);
+ }
+
+ @override
+ Future<void> setBackgroundColor(Color color) {
+ return Future.wait(<Future<void>>[
+ _webView.scrollView.setBackgroundColor(color),
+ _webView.setOpaque(false),
+ _webView.setBackgroundColor(Colors.transparent),
+ ]);
+ }
+
+ @override
+ Future<void> setJavaScriptMode(JavaScriptMode javaScriptMode) {
+ switch (javaScriptMode) {
+ case JavaScriptMode.disabled:
+ return _webView.configuration.preferences.setJavaScriptEnabled(false);
+ case JavaScriptMode.unrestricted:
+ return _webView.configuration.preferences.setJavaScriptEnabled(true);
+ }
+ }
+
+ @override
+ Future<void> setUserAgent(String? userAgent) {
+ return _webView.setCustomUserAgent(userAgent);
+ }
+
+ @override
+ Future<void> enableZoom(bool enabled) async {
+ if (_zoomEnabled == enabled) {
+ return;
+ }
+
+ _zoomEnabled = enabled;
+ if (enabled) {
+ await _resetUserScripts();
+ } else {
+ await _disableZoom();
+ }
+ }
+
+ Future<void> _disableZoom() {
+ const WKUserScript userScript = WKUserScript(
+ "var meta = document.createElement('meta');\n"
+ "meta.name = 'viewport';\n"
+ "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, "
+ "user-scalable=no';\n"
+ "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);",
+ WKUserScriptInjectionTime.atDocumentEnd,
+ isMainFrameOnly: true,
+ );
+ return _webView.configuration.userContentController
+ .addUserScript(userScript);
+ }
+
+ // WKWebView does not support removing a single user script, so all user
+ // scripts and all message handlers are removed instead. And the JavaScript
+ // channels that shouldn't be removed are re-registered. Note that this
+ // workaround could interfere with exposing support for custom scripts from
+ // applications.
+ Future<void> _resetUserScripts({String? removedJavaScriptChannel}) async {
+ _webView.configuration.userContentController.removeAllUserScripts();
+ // TODO(bparrishMines): This can be replaced with
+ // `removeAllScriptMessageHandlers` once Dart supports runtime version
+ // checking. (e.g. The equivalent to @availability in Objective-C.)
+ _javaScriptChannelParams.keys.forEach(
+ _webView.configuration.userContentController.removeScriptMessageHandler,
+ );
+
+ _javaScriptChannelParams.remove(removedJavaScriptChannel);
+
+ await Future.wait(<Future<void>>[
+ for (JavaScriptChannelParams params in _javaScriptChannelParams.values)
+ addJavaScriptChannel(params),
+ // Zoom is disabled with a WKUserScript, so this adds it back if it was
+ // removed above.
+ if (!_zoomEnabled) _disableZoom(),
+ ]);
+ }
+}
+
+/// An implementation of [JavaScriptChannelParams] with the WebKit api.
+///
+/// See [WebKitWebViewController.addJavaScriptChannel].
+@immutable
+class WebKitJavaScriptChannelParams extends JavaScriptChannelParams {
+ /// Constructs a [WebKitJavaScriptChannelParams].
+ WebKitJavaScriptChannelParams({
+ required super.name,
+ required super.onMessageReceived,
+ @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(),
+ }) : assert(name.isNotEmpty),
+ _messageHandler = webKitProxy.createScriptMessageHandler(
+ didReceiveScriptMessage: withWeakRefenceTo(
+ onMessageReceived,
+ (WeakReference<void Function(JavaScriptMessage)> weakReference) {
+ return (
+ WKUserContentController controller,
+ WKScriptMessage message,
+ ) {
+ if (weakReference.target != null) {
+ weakReference.target!(
+ JavaScriptMessage(message: message.body!.toString()),
+ );
+ }
+ };
+ },
+ ),
+ );
+
+ /// Constructs a [WebKitJavaScriptChannelParams] using a
+ /// [JavaScriptChannelParams].
+ WebKitJavaScriptChannelParams.fromJavaScriptChannelParams(
+ JavaScriptChannelParams params, {
+ @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(),
+ }) : this(
+ name: params.name,
+ onMessageReceived: params.onMessageReceived,
+ webKitProxy: webKitProxy,
+ );
+
+ final WKScriptMessageHandler _messageHandler;
+}
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart
new file mode 100644
index 0000000..b808086
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart
@@ -0,0 +1,17 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart';
+
+import 'webkit_webview_controller.dart';
+
+/// Implementation of [WebViewPlatform] using the WebKit Api.
+class WebKitWebViewPlatform extends WebViewPlatform {
+ @override
+ WebKitWebViewController createPlatformWebViewController(
+ PlatformWebViewControllerCreationParams params,
+ ) {
+ return WebKitWebViewController(params);
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart
new file mode 100644
index 0000000..2a593fb
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart
@@ -0,0 +1,7 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library webview_flutter_wkwebview;
+
+export 'src/webkit_webview_controller.dart';
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart
new file mode 100644
index 0000000..cf5da17
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart
@@ -0,0 +1,746 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:math';
+// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231)
+// ignore: unnecessary_import
+import 'dart:typed_data';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart';
+import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart';
+import 'package:webview_flutter_wkwebview/src/v4/src/webkit_proxy.dart';
+import 'package:webview_flutter_wkwebview/src/v4/webview_flutter_wkwebview.dart';
+import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart';
+
+import 'webkit_webview_controller_test.mocks.dart';
+
+@GenerateMocks(<Type>[
+ UIScrollView,
+ WKPreferences,
+ WKUserContentController,
+ WKWebsiteDataStore,
+ WKWebView,
+ WKWebViewConfiguration,
+])
+void main() {
+ WidgetsFlutterBinding.ensureInitialized();
+
+ group('WebKitWebViewController', () {
+ WebKitWebViewController createControllerWithMocks({
+ MockUIScrollView? mockScrollView,
+ MockWKPreferences? mockPreferences,
+ MockWKUserContentController? mockUserContentController,
+ MockWKWebsiteDataStore? mockWebsiteDataStore,
+ MockWKWebView Function(
+ WKWebViewConfiguration configuration, {
+ void Function(
+ String keyPath,
+ NSObject object,
+ Map<NSKeyValueChangeKey, Object?> change,
+ )?
+ observeValue,
+ })?
+ createMockWebView,
+ MockWKWebViewConfiguration? mockWebViewConfiguration,
+ }) {
+ final MockWKWebViewConfiguration nonNullMockWebViewConfiguration =
+ mockWebViewConfiguration ?? MockWKWebViewConfiguration();
+ late final MockWKWebView nonNullMockWebView;
+
+ final PlatformWebViewControllerCreationParams controllerCreationParams =
+ WebKitWebViewControllerCreationParams(
+ webKitProxy: WebKitProxy(
+ createWebViewConfiguration: () => nonNullMockWebViewConfiguration,
+ ),
+ );
+
+ final WebKitWebViewController controller = WebKitWebViewController(
+ controllerCreationParams,
+ webKitProxy: WebKitProxy(
+ createWebView: (
+ _, {
+ void Function(
+ String keyPath,
+ NSObject object,
+ Map<NSKeyValueChangeKey, Object?> change,
+ )?
+ observeValue,
+ }) {
+ nonNullMockWebView = createMockWebView == null
+ ? MockWKWebView()
+ : createMockWebView(
+ nonNullMockWebViewConfiguration,
+ observeValue: observeValue,
+ );
+ return nonNullMockWebView;
+ },
+ ),
+ );
+
+ when(nonNullMockWebView.scrollView)
+ .thenReturn(mockScrollView ?? MockUIScrollView());
+ when(nonNullMockWebView.configuration)
+ .thenReturn(nonNullMockWebViewConfiguration);
+
+ when(nonNullMockWebViewConfiguration.preferences)
+ .thenReturn(mockPreferences ?? MockWKPreferences());
+ when(nonNullMockWebViewConfiguration.userContentController).thenReturn(
+ mockUserContentController ?? MockWKUserContentController());
+ when(nonNullMockWebViewConfiguration.websiteDataStore)
+ .thenReturn(mockWebsiteDataStore ?? MockWKWebsiteDataStore());
+
+ return controller;
+ }
+
+ test('loadFile', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ await controller.loadFile('/path/to/file.html');
+ verify(mockWebView.loadFileUrl(
+ '/path/to/file.html',
+ readAccessUrl: '/path/to',
+ ));
+ });
+
+ test('loadFlutterAsset', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ await controller.loadFlutterAsset('test_assets/index.html');
+ verify(mockWebView.loadFlutterAsset('test_assets/index.html'));
+ });
+
+ test('loadHtmlString', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ const String htmlString = '<html><body>Test data.</body></html>';
+ await controller.loadHtmlString(htmlString, baseUrl: 'baseUrl');
+
+ verify(mockWebView.loadHtmlString(
+ '<html><body>Test data.</body></html>',
+ baseUrl: 'baseUrl',
+ ));
+ });
+
+ group('loadRequest', () {
+ test('Throws ArgumentError for empty scheme', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ expect(
+ () async => await controller.loadRequest(
+ LoadRequestParams(
+ uri: Uri.parse('www.google.com'),
+ method: LoadRequestMethod.get,
+ headers: const <String, String>{},
+ ),
+ ),
+ throwsA(isA<ArgumentError>()),
+ );
+ });
+
+ test('GET without headers', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ await controller.loadRequest(
+ LoadRequestParams(
+ uri: Uri.parse('https://www.google.com'),
+ method: LoadRequestMethod.get,
+ headers: const <String, String>{},
+ ),
+ );
+
+ final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny))
+ .captured
+ .single as NSUrlRequest;
+ expect(request.url, 'https://www.google.com');
+ expect(request.allHttpHeaderFields, <String, String>{});
+ expect(request.httpMethod, 'get');
+ });
+
+ test('GET with headers', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ await controller.loadRequest(
+ LoadRequestParams(
+ uri: Uri.parse('https://www.google.com'),
+ method: LoadRequestMethod.get,
+ headers: const <String, String>{'a': 'header'},
+ ),
+ );
+
+ final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny))
+ .captured
+ .single as NSUrlRequest;
+ expect(request.url, 'https://www.google.com');
+ expect(request.allHttpHeaderFields, <String, String>{'a': 'header'});
+ expect(request.httpMethod, 'get');
+ });
+
+ test('POST without body', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ await controller.loadRequest(LoadRequestParams(
+ uri: Uri.parse('https://www.google.com'),
+ method: LoadRequestMethod.post,
+ headers: const <String, String>{},
+ ));
+
+ final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny))
+ .captured
+ .single as NSUrlRequest;
+ expect(request.url, 'https://www.google.com');
+ expect(request.httpMethod, 'post');
+ });
+
+ test('POST with body', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ await controller.loadRequest(LoadRequestParams(
+ uri: Uri.parse('https://www.google.com'),
+ method: LoadRequestMethod.post,
+ body: Uint8List.fromList('Test Body'.codeUnits),
+ headers: const <String, String>{},
+ ));
+
+ final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny))
+ .captured
+ .single as NSUrlRequest;
+ expect(request.url, 'https://www.google.com');
+ expect(request.httpMethod, 'post');
+ expect(
+ request.httpBody,
+ Uint8List.fromList('Test Body'.codeUnits),
+ );
+ });
+ });
+
+ test('canGoBack', () {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ when(mockWebView.canGoBack()).thenAnswer(
+ (_) => Future<bool>.value(false),
+ );
+ expect(controller.canGoBack(), completion(false));
+ });
+
+ test('canGoForward', () {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ when(mockWebView.canGoForward()).thenAnswer(
+ (_) => Future<bool>.value(true),
+ );
+ expect(controller.canGoForward(), completion(true));
+ });
+
+ test('goBack', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ await controller.goBack();
+ verify(mockWebView.goBack());
+ });
+
+ test('goForward', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ await controller.goForward();
+ verify(mockWebView.goForward());
+ });
+
+ test('reload', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ await controller.reload();
+ verify(mockWebView.reload());
+ });
+
+ test('enableGestureNavigation', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ await controller.enableGestureNavigation(true);
+ verify(mockWebView.setAllowsBackForwardNavigationGestures(true));
+ });
+
+ test('runJavaScriptReturningResult', () {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer(
+ (_) => Future<String>.value('returnString'),
+ );
+ expect(
+ controller.runJavaScriptReturningResult('runJavaScript'),
+ completion('returnString'),
+ );
+ });
+
+ test('runJavaScriptReturningResult throws error on null return value', () {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer(
+ (_) => Future<String?>.value(null),
+ );
+ expect(
+ () => controller.runJavaScriptReturningResult('runJavaScript'),
+ throwsArgumentError,
+ );
+ });
+
+ test('runJavaScript', () {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer(
+ (_) => Future<String>.value('returnString'),
+ );
+ expect(
+ controller.runJavaScript('runJavaScript'),
+ completes,
+ );
+ });
+
+ test('runJavaScript ignores exception with unsupported javaScript type',
+ () {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ when(mockWebView.evaluateJavaScript('runJavaScript'))
+ .thenThrow(PlatformException(
+ code: '',
+ details: const NSError(
+ code: WKErrorCode.javaScriptResultTypeIsUnsupported,
+ domain: '',
+ localizedDescription: '',
+ ),
+ ));
+ expect(
+ controller.runJavaScript('runJavaScript'),
+ completes,
+ );
+ });
+
+ test('getTitle', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ when(mockWebView.getTitle())
+ .thenAnswer((_) => Future<String>.value('Web Title'));
+ expect(controller.getTitle(), completion('Web Title'));
+ });
+
+ test('currentUrl', () {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ when(mockWebView.getUrl())
+ .thenAnswer((_) => Future<String>.value('myUrl.com'));
+ expect(controller.currentUrl(), completion('myUrl.com'));
+ });
+
+ test('scrollTo', () async {
+ final MockUIScrollView mockScrollView = MockUIScrollView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ mockScrollView: mockScrollView,
+ );
+
+ await controller.scrollTo(2, 4);
+ verify(mockScrollView.setContentOffset(const Point<double>(2.0, 4.0)));
+ });
+
+ test('scrollBy', () async {
+ final MockUIScrollView mockScrollView = MockUIScrollView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ mockScrollView: mockScrollView,
+ );
+
+ await controller.scrollBy(2, 4);
+ verify(mockScrollView.scrollBy(const Point<double>(2.0, 4.0)));
+ });
+
+ test('getScrollPosition', () {
+ final MockUIScrollView mockScrollView = MockUIScrollView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ mockScrollView: mockScrollView,
+ );
+
+ when(mockScrollView.getContentOffset()).thenAnswer(
+ (_) => Future<Point<double>>.value(const Point<double>(8.0, 16.0)),
+ );
+ expect(
+ controller.getScrollPosition(),
+ completion(const Point<double>(8.0, 16.0)),
+ );
+ });
+
+ test('disable zoom', () async {
+ final MockWKUserContentController mockUserContentController =
+ MockWKUserContentController();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ mockUserContentController: mockUserContentController,
+ );
+
+ await controller.enableZoom(false);
+
+ final WKUserScript zoomScript =
+ verify(mockUserContentController.addUserScript(captureAny))
+ .captured
+ .first as WKUserScript;
+ expect(zoomScript.isMainFrameOnly, isTrue);
+ expect(zoomScript.injectionTime, WKUserScriptInjectionTime.atDocumentEnd);
+ expect(
+ zoomScript.source,
+ "var meta = document.createElement('meta');\n"
+ "meta.name = 'viewport';\n"
+ "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, "
+ "user-scalable=no';\n"
+ "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);",
+ );
+ });
+
+ test('setBackgroundColor', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+ final MockUIScrollView mockScrollView = MockUIScrollView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ mockScrollView: mockScrollView,
+ );
+
+ controller.setBackgroundColor(Colors.red);
+
+ verify(mockWebView.setOpaque(false));
+ verify(mockWebView.setBackgroundColor(Colors.transparent));
+ verify(mockScrollView.setBackgroundColor(Colors.red));
+ });
+
+ test('userAgent', () async {
+ final MockWKWebView mockWebView = MockWKWebView();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ createMockWebView: (_, {dynamic observeValue}) => mockWebView,
+ );
+
+ await controller.setUserAgent('MyUserAgent');
+ verify(mockWebView.setCustomUserAgent('MyUserAgent'));
+ });
+
+ test('enable JavaScript', () async {
+ final MockWKPreferences mockPreferences = MockWKPreferences();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ mockPreferences: mockPreferences,
+ );
+
+ await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
+
+ verify(mockPreferences.setJavaScriptEnabled(true));
+ });
+
+ test('disable JavaScript', () async {
+ final MockWKPreferences mockPreferences = MockWKPreferences();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ mockPreferences: mockPreferences,
+ );
+
+ await controller.setJavaScriptMode(JavaScriptMode.disabled);
+
+ verify(mockPreferences.setJavaScriptEnabled(false));
+ });
+
+ test('clearCache', () {
+ final MockWKWebsiteDataStore mockWebsiteDataStore =
+ MockWKWebsiteDataStore();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ mockWebsiteDataStore: mockWebsiteDataStore,
+ );
+ when(
+ mockWebsiteDataStore.removeDataOfTypes(
+ <WKWebsiteDataType>{
+ WKWebsiteDataType.memoryCache,
+ WKWebsiteDataType.diskCache,
+ WKWebsiteDataType.offlineWebApplicationCache,
+ },
+ DateTime.fromMillisecondsSinceEpoch(0),
+ ),
+ ).thenAnswer((_) => Future<bool>.value(false));
+
+ expect(controller.clearCache(), completes);
+ });
+
+ test('clearLocalStorage', () {
+ final MockWKWebsiteDataStore mockWebsiteDataStore =
+ MockWKWebsiteDataStore();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ mockWebsiteDataStore: mockWebsiteDataStore,
+ );
+ when(
+ mockWebsiteDataStore.removeDataOfTypes(
+ <WKWebsiteDataType>{WKWebsiteDataType.localStorage},
+ DateTime.fromMillisecondsSinceEpoch(0),
+ ),
+ ).thenAnswer((_) => Future<bool>.value(false));
+
+ expect(controller.clearLocalStorage(), completes);
+ });
+
+ test('addJavaScriptChannel', () async {
+ final WebKitProxy webKitProxy = WebKitProxy(
+ createScriptMessageHandler: ({
+ required void Function(
+ WKUserContentController userContentController,
+ WKScriptMessage message,
+ )
+ didReceiveScriptMessage,
+ }) {
+ return WKScriptMessageHandler.detached(
+ didReceiveScriptMessage: didReceiveScriptMessage,
+ );
+ },
+ );
+
+ final WebKitJavaScriptChannelParams javaScriptChannelParams =
+ WebKitJavaScriptChannelParams(
+ name: 'name',
+ onMessageReceived: (JavaScriptMessage message) {},
+ webKitProxy: webKitProxy,
+ );
+
+ final MockWKUserContentController mockUserContentController =
+ MockWKUserContentController();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ mockUserContentController: mockUserContentController,
+ );
+
+ await controller.addJavaScriptChannel(javaScriptChannelParams);
+ verify(mockUserContentController.addScriptMessageHandler(
+ argThat(isA<WKScriptMessageHandler>()),
+ 'name',
+ ));
+
+ final WKUserScript userScript =
+ verify(mockUserContentController.addUserScript(captureAny))
+ .captured
+ .single as WKUserScript;
+ expect(userScript.source, 'window.name = webkit.messageHandlers.name;');
+ expect(
+ userScript.injectionTime,
+ WKUserScriptInjectionTime.atDocumentStart,
+ );
+ });
+
+ test('removeJavaScriptChannel', () async {
+ final WebKitProxy webKitProxy = WebKitProxy(
+ createScriptMessageHandler: ({
+ required void Function(
+ WKUserContentController userContentController,
+ WKScriptMessage message,
+ )
+ didReceiveScriptMessage,
+ }) {
+ return WKScriptMessageHandler.detached(
+ didReceiveScriptMessage: didReceiveScriptMessage,
+ );
+ },
+ );
+
+ final WebKitJavaScriptChannelParams javaScriptChannelParams =
+ WebKitJavaScriptChannelParams(
+ name: 'name',
+ onMessageReceived: (JavaScriptMessage message) {},
+ webKitProxy: webKitProxy,
+ );
+
+ final MockWKUserContentController mockUserContentController =
+ MockWKUserContentController();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ mockUserContentController: mockUserContentController,
+ );
+
+ await controller.addJavaScriptChannel(javaScriptChannelParams);
+ reset(mockUserContentController);
+
+ await controller.removeJavaScriptChannel('name');
+
+ verify(mockUserContentController.removeAllUserScripts());
+ verify(mockUserContentController.removeScriptMessageHandler('name'));
+
+ verifyNoMoreInteractions(mockUserContentController);
+ });
+
+ test('removeJavaScriptChannel with zoom disabled', () async {
+ final WebKitProxy webKitProxy = WebKitProxy(
+ createScriptMessageHandler: ({
+ required void Function(
+ WKUserContentController userContentController,
+ WKScriptMessage message,
+ )
+ didReceiveScriptMessage,
+ }) {
+ return WKScriptMessageHandler.detached(
+ didReceiveScriptMessage: didReceiveScriptMessage,
+ );
+ },
+ );
+
+ final WebKitJavaScriptChannelParams javaScriptChannelParams =
+ WebKitJavaScriptChannelParams(
+ name: 'name',
+ onMessageReceived: (JavaScriptMessage message) {},
+ webKitProxy: webKitProxy,
+ );
+
+ final MockWKUserContentController mockUserContentController =
+ MockWKUserContentController();
+
+ final WebKitWebViewController controller = createControllerWithMocks(
+ mockUserContentController: mockUserContentController,
+ );
+
+ await controller.enableZoom(false);
+ await controller.addJavaScriptChannel(javaScriptChannelParams);
+ clearInteractions(mockUserContentController);
+ await controller.removeJavaScriptChannel('name');
+
+ final WKUserScript zoomScript =
+ verify(mockUserContentController.addUserScript(captureAny))
+ .captured
+ .first as WKUserScript;
+ expect(zoomScript.isMainFrameOnly, isTrue);
+ expect(zoomScript.injectionTime, WKUserScriptInjectionTime.atDocumentEnd);
+ expect(
+ zoomScript.source,
+ "var meta = document.createElement('meta');\n"
+ "meta.name = 'viewport';\n"
+ "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, "
+ "user-scalable=no';\n"
+ "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);",
+ );
+ });
+ });
+
+ group('WebKitJavaScriptChannelParams', () {
+ test('onMessageReceived', () async {
+ late final WKScriptMessageHandler messageHandler;
+
+ final WebKitProxy webKitProxy = WebKitProxy(
+ createScriptMessageHandler: ({
+ required void Function(
+ WKUserContentController userContentController,
+ WKScriptMessage message,
+ )
+ didReceiveScriptMessage,
+ }) {
+ messageHandler = WKScriptMessageHandler.detached(
+ didReceiveScriptMessage: didReceiveScriptMessage,
+ );
+ return messageHandler;
+ },
+ );
+
+ late final String callbackMessage;
+ WebKitJavaScriptChannelParams(
+ name: 'name',
+ onMessageReceived: (JavaScriptMessage message) {
+ callbackMessage = message.message;
+ },
+ webKitProxy: webKitProxy,
+ );
+
+ messageHandler.didReceiveScriptMessage(
+ MockWKUserContentController(),
+ const WKScriptMessage(name: 'name', body: 'myMessage'),
+ );
+
+ expect(callbackMessage, 'myMessage');
+ });
+ });
+}
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart
new file mode 100644
index 0000000..6138fdd
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart
@@ -0,0 +1,414 @@
+// Mocks generated by Mockito 5.2.0 from annotations
+// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart.
+// Do not manually edit this file.
+
+import 'dart:async' as _i5;
+import 'dart:math' as _i2;
+import 'dart:ui' as _i6;
+
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'
+ as _i7;
+import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart' as _i3;
+import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i4;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+
+class _FakePoint_0<T extends num> extends _i1.Fake implements _i2.Point<T> {}
+
+class _FakeUIScrollView_1 extends _i1.Fake implements _i3.UIScrollView {}
+
+class _FakeWKPreferences_2 extends _i1.Fake implements _i4.WKPreferences {}
+
+class _FakeWKUserContentController_3 extends _i1.Fake
+ implements _i4.WKUserContentController {}
+
+class _FakeWKHttpCookieStore_4 extends _i1.Fake
+ implements _i4.WKHttpCookieStore {}
+
+class _FakeWKWebsiteDataStore_5 extends _i1.Fake
+ implements _i4.WKWebsiteDataStore {}
+
+class _FakeWKWebViewConfiguration_6 extends _i1.Fake
+ implements _i4.WKWebViewConfiguration {}
+
+class _FakeWKWebView_7 extends _i1.Fake implements _i4.WKWebView {}
+
+/// A class which mocks [UIScrollView].
+///
+/// See the documentation for Mockito's code generation for more information.
+// ignore: must_be_immutable
+class MockUIScrollView extends _i1.Mock implements _i3.UIScrollView {
+ MockUIScrollView() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i5.Future<_i2.Point<double>> getContentOffset() => (super.noSuchMethod(
+ Invocation.method(#getContentOffset, []),
+ returnValue: Future<_i2.Point<double>>.value(_FakePoint_0<double>()))
+ as _i5.Future<_i2.Point<double>>);
+ @override
+ _i5.Future<void> scrollBy(_i2.Point<double>? offset) =>
+ (super.noSuchMethod(Invocation.method(#scrollBy, [offset]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setContentOffset(_i2.Point<double>? offset) =>
+ (super.noSuchMethod(Invocation.method(#setContentOffset, [offset]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i3.UIScrollView copy() => (super.noSuchMethod(Invocation.method(#copy, []),
+ returnValue: _FakeUIScrollView_1()) as _i3.UIScrollView);
+ @override
+ _i5.Future<void> setBackgroundColor(_i6.Color? color) =>
+ (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setOpaque(bool? opaque) =>
+ (super.noSuchMethod(Invocation.method(#setOpaque, [opaque]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> addObserver(_i7.NSObject? observer,
+ {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #addObserver, [observer], {#keyPath: keyPath, #options: options}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> removeObserver(_i7.NSObject? observer, {String? keyPath}) =>
+ (super.noSuchMethod(
+ Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+}
+
+/// A class which mocks [WKPreferences].
+///
+/// See the documentation for Mockito's code generation for more information.
+// ignore: must_be_immutable
+class MockWKPreferences extends _i1.Mock implements _i4.WKPreferences {
+ MockWKPreferences() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i5.Future<void> setJavaScriptEnabled(bool? enabled) =>
+ (super.noSuchMethod(Invocation.method(#setJavaScriptEnabled, [enabled]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i4.WKPreferences copy() => (super.noSuchMethod(Invocation.method(#copy, []),
+ returnValue: _FakeWKPreferences_2()) as _i4.WKPreferences);
+ @override
+ _i5.Future<void> addObserver(_i7.NSObject? observer,
+ {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #addObserver, [observer], {#keyPath: keyPath, #options: options}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> removeObserver(_i7.NSObject? observer, {String? keyPath}) =>
+ (super.noSuchMethod(
+ Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+}
+
+/// A class which mocks [WKUserContentController].
+///
+/// See the documentation for Mockito's code generation for more information.
+// ignore: must_be_immutable
+class MockWKUserContentController extends _i1.Mock
+ implements _i4.WKUserContentController {
+ MockWKUserContentController() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i5.Future<void> addScriptMessageHandler(
+ _i4.WKScriptMessageHandler? handler, String? name) =>
+ (super.noSuchMethod(
+ Invocation.method(#addScriptMessageHandler, [handler, name]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> removeScriptMessageHandler(String? name) => (super
+ .noSuchMethod(Invocation.method(#removeScriptMessageHandler, [name]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> removeAllScriptMessageHandlers() => (super.noSuchMethod(
+ Invocation.method(#removeAllScriptMessageHandlers, []),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> addUserScript(_i4.WKUserScript? userScript) =>
+ (super.noSuchMethod(Invocation.method(#addUserScript, [userScript]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> removeAllUserScripts() =>
+ (super.noSuchMethod(Invocation.method(#removeAllUserScripts, []),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i4.WKUserContentController copy() =>
+ (super.noSuchMethod(Invocation.method(#copy, []),
+ returnValue: _FakeWKUserContentController_3())
+ as _i4.WKUserContentController);
+ @override
+ _i5.Future<void> addObserver(_i7.NSObject? observer,
+ {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #addObserver, [observer], {#keyPath: keyPath, #options: options}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> removeObserver(_i7.NSObject? observer, {String? keyPath}) =>
+ (super.noSuchMethod(
+ Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+}
+
+/// A class which mocks [WKWebsiteDataStore].
+///
+/// See the documentation for Mockito's code generation for more information.
+// ignore: must_be_immutable
+class MockWKWebsiteDataStore extends _i1.Mock
+ implements _i4.WKWebsiteDataStore {
+ MockWKWebsiteDataStore() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i4.WKHttpCookieStore get httpCookieStore =>
+ (super.noSuchMethod(Invocation.getter(#httpCookieStore),
+ returnValue: _FakeWKHttpCookieStore_4()) as _i4.WKHttpCookieStore);
+ @override
+ _i5.Future<bool> removeDataOfTypes(
+ Set<_i4.WKWebsiteDataType>? dataTypes, DateTime? since) =>
+ (super.noSuchMethod(
+ Invocation.method(#removeDataOfTypes, [dataTypes, since]),
+ returnValue: Future<bool>.value(false)) as _i5.Future<bool>);
+ @override
+ _i4.WKWebsiteDataStore copy() =>
+ (super.noSuchMethod(Invocation.method(#copy, []),
+ returnValue: _FakeWKWebsiteDataStore_5()) as _i4.WKWebsiteDataStore);
+ @override
+ _i5.Future<void> addObserver(_i7.NSObject? observer,
+ {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #addObserver, [observer], {#keyPath: keyPath, #options: options}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> removeObserver(_i7.NSObject? observer, {String? keyPath}) =>
+ (super.noSuchMethod(
+ Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+}
+
+/// A class which mocks [WKWebView].
+///
+/// See the documentation for Mockito's code generation for more information.
+// ignore: must_be_immutable
+class MockWKWebView extends _i1.Mock implements _i4.WKWebView {
+ MockWKWebView() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i4.WKWebViewConfiguration get configuration =>
+ (super.noSuchMethod(Invocation.getter(#configuration),
+ returnValue: _FakeWKWebViewConfiguration_6())
+ as _i4.WKWebViewConfiguration);
+ @override
+ _i3.UIScrollView get scrollView =>
+ (super.noSuchMethod(Invocation.getter(#scrollView),
+ returnValue: _FakeUIScrollView_1()) as _i3.UIScrollView);
+ @override
+ _i5.Future<void> setUIDelegate(_i4.WKUIDelegate? delegate) =>
+ (super.noSuchMethod(Invocation.method(#setUIDelegate, [delegate]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setNavigationDelegate(_i4.WKNavigationDelegate? delegate) =>
+ (super.noSuchMethod(Invocation.method(#setNavigationDelegate, [delegate]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<String?> getUrl() =>
+ (super.noSuchMethod(Invocation.method(#getUrl, []),
+ returnValue: Future<String?>.value()) as _i5.Future<String?>);
+ @override
+ _i5.Future<double> getEstimatedProgress() =>
+ (super.noSuchMethod(Invocation.method(#getEstimatedProgress, []),
+ returnValue: Future<double>.value(0.0)) as _i5.Future<double>);
+ @override
+ _i5.Future<void> loadRequest(_i7.NSUrlRequest? request) =>
+ (super.noSuchMethod(Invocation.method(#loadRequest, [request]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> loadHtmlString(String? string, {String? baseUrl}) =>
+ (super.noSuchMethod(
+ Invocation.method(#loadHtmlString, [string], {#baseUrl: baseUrl}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> loadFileUrl(String? url, {String? readAccessUrl}) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #loadFileUrl, [url], {#readAccessUrl: readAccessUrl}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> loadFlutterAsset(String? key) =>
+ (super.noSuchMethod(Invocation.method(#loadFlutterAsset, [key]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<bool> canGoBack() =>
+ (super.noSuchMethod(Invocation.method(#canGoBack, []),
+ returnValue: Future<bool>.value(false)) as _i5.Future<bool>);
+ @override
+ _i5.Future<bool> canGoForward() =>
+ (super.noSuchMethod(Invocation.method(#canGoForward, []),
+ returnValue: Future<bool>.value(false)) as _i5.Future<bool>);
+ @override
+ _i5.Future<void> goBack() =>
+ (super.noSuchMethod(Invocation.method(#goBack, []),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> goForward() =>
+ (super.noSuchMethod(Invocation.method(#goForward, []),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> reload() =>
+ (super.noSuchMethod(Invocation.method(#reload, []),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<String?> getTitle() =>
+ (super.noSuchMethod(Invocation.method(#getTitle, []),
+ returnValue: Future<String?>.value()) as _i5.Future<String?>);
+ @override
+ _i5.Future<void> setAllowsBackForwardNavigationGestures(bool? allow) =>
+ (super.noSuchMethod(
+ Invocation.method(#setAllowsBackForwardNavigationGestures, [allow]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setCustomUserAgent(String? userAgent) =>
+ (super.noSuchMethod(Invocation.method(#setCustomUserAgent, [userAgent]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<Object?> evaluateJavaScript(String? javaScriptString) => (super
+ .noSuchMethod(Invocation.method(#evaluateJavaScript, [javaScriptString]),
+ returnValue: Future<Object?>.value()) as _i5.Future<Object?>);
+ @override
+ _i4.WKWebView copy() => (super.noSuchMethod(Invocation.method(#copy, []),
+ returnValue: _FakeWKWebView_7()) as _i4.WKWebView);
+ @override
+ _i5.Future<void> setBackgroundColor(_i6.Color? color) =>
+ (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setOpaque(bool? opaque) =>
+ (super.noSuchMethod(Invocation.method(#setOpaque, [opaque]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> addObserver(_i7.NSObject? observer,
+ {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #addObserver, [observer], {#keyPath: keyPath, #options: options}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> removeObserver(_i7.NSObject? observer, {String? keyPath}) =>
+ (super.noSuchMethod(
+ Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+}
+
+/// A class which mocks [WKWebViewConfiguration].
+///
+/// See the documentation for Mockito's code generation for more information.
+// ignore: must_be_immutable
+class MockWKWebViewConfiguration extends _i1.Mock
+ implements _i4.WKWebViewConfiguration {
+ MockWKWebViewConfiguration() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i4.WKUserContentController get userContentController =>
+ (super.noSuchMethod(Invocation.getter(#userContentController),
+ returnValue: _FakeWKUserContentController_3())
+ as _i4.WKUserContentController);
+ @override
+ _i4.WKPreferences get preferences =>
+ (super.noSuchMethod(Invocation.getter(#preferences),
+ returnValue: _FakeWKPreferences_2()) as _i4.WKPreferences);
+ @override
+ _i4.WKWebsiteDataStore get websiteDataStore =>
+ (super.noSuchMethod(Invocation.getter(#websiteDataStore),
+ returnValue: _FakeWKWebsiteDataStore_5()) as _i4.WKWebsiteDataStore);
+ @override
+ _i5.Future<void> setAllowsInlineMediaPlayback(bool? allow) => (super
+ .noSuchMethod(Invocation.method(#setAllowsInlineMediaPlayback, [allow]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> setMediaTypesRequiringUserActionForPlayback(
+ Set<_i4.WKAudiovisualMediaType>? types) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #setMediaTypesRequiringUserActionForPlayback, [types]),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i4.WKWebViewConfiguration copy() =>
+ (super.noSuchMethod(Invocation.method(#copy, []),
+ returnValue: _FakeWKWebViewConfiguration_6())
+ as _i4.WKWebViewConfiguration);
+ @override
+ _i5.Future<void> addObserver(_i7.NSObject? observer,
+ {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #addObserver, [observer], {#keyPath: keyPath, #options: options}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+ @override
+ _i5.Future<void> removeObserver(_i7.NSObject? observer, {String? keyPath}) =>
+ (super.noSuchMethod(
+ Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}),
+ returnValue: Future<void>.value(),
+ returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+}