From ed979283318c470e01c96d1da0191633cc506740 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 27 Jan 2024 18:41:10 +0100 Subject: [PATCH] windows: fixed dealloc webviews logic, implemented takeScreenshot, added WebViewEnvironment and WebViewEnvironmentSettings classes --- .../lib/src/exchangeable_enum_generator.dart | 13 +- .../lib/in_app_webiew_example.screen.dart | 1 + flutter_inappwebview/example/lib/main.dart | 8 + .../Flutter/GeneratedPluginRegistrant.swift | 2 - flutter_inappwebview/example/pubspec.yaml | 16 +- .../src/in_app_webview/in_app_webview.dart | 3 + flutter_inappwebview/lib/src/main.dart | 1 + .../lib/src/webview_environment/main.dart | 1 + .../webview_environment.dart | 35 ++ flutter_inappwebview/pubspec.yaml | 11 +- .../platform_inappwebview_controller.dart | 1 + .../platform_inappwebview_widget.dart | 8 + .../lib/src/inappwebview_platform.dart | 21 ++ .../lib/src/main.dart | 1 + .../lib/src/types/compress_format.dart | 32 +- .../lib/src/types/compress_format.g.dart | 26 +- .../src/types/screenshot_configuration.dart | 31 +- .../src/types/screenshot_configuration.g.dart | 29 +- .../lib/src/webview_environment/main.dart | 2 + .../platform_webview_environment.dart | 106 ++++++ .../webview_environment_settings.dart | 25 ++ .../webview_environment_settings.g.dart | 70 ++++ .../pubspec.yaml | 2 +- .../example/pubspec.lock | 7 +- .../lib/src/cookie_manager.dart | 333 ++++-------------- .../find_interaction_controller.dart | 10 +- .../src/in_app_browser/in_app_browser.dart | 4 +- .../headless_in_app_webview.dart | 4 +- .../src/in_app_webview/in_app_webview.dart | 15 +- .../in_app_webview_controller.dart | 11 +- .../lib/src/inappwebview_platform.dart | 34 ++ .../lib/src/main.dart | 1 + .../src/web_message/web_message_channel.dart | 20 +- .../lib/src/web_message/web_message_port.dart | 6 +- .../lib/src/webview_environment/main.dart | 1 + .../webview_environment.dart | 102 ++++++ flutter_inappwebview_windows/pubspec.yaml | 3 +- .../windows/CMakeLists.txt | 14 + .../windows/cookie_manager.cpp | 120 +++++++ .../windows/cookie_manager.h | 36 ++ .../flutter_inappwebview_windows_plugin.cpp | 4 + .../flutter_inappwebview_windows_plugin.h | 4 + .../headless_in_app_webview.cpp | 10 +- .../headless_in_app_webview_manager.cpp | 13 +- .../windows/in_app_browser/in_app_browser.cpp | 14 +- .../windows/in_app_browser/in_app_browser.h | 1 + .../in_app_browser/in_app_browser_manager.cpp | 4 +- .../windows/in_app_webview/in_app_webview.cpp | 193 ++++++---- .../windows/in_app_webview/in_app_webview.h | 14 +- .../in_app_webview/in_app_webview_manager.cpp | 15 +- .../webview_channel_delegate.cpp | 10 + .../windows/types/rect.cpp | 25 ++ .../windows/types/rect.h | 27 ++ .../types/screenshot_configuration.cpp | 48 +++ .../windows/types/screenshot_configuration.h | 38 ++ .../windows/types/web_resource_error.cpp | 2 +- .../windows/types/web_resource_error.h | 4 +- .../windows/types/web_resource_response.cpp | 2 +- .../windows/types/web_resource_response.h | 4 +- .../windows/utils/flutter.h | 50 ++- .../windows/utils/string.h | 34 ++ .../webview_environment.cpp | 59 ++++ .../webview_environment/webview_environment.h | 30 ++ .../webview_environment_channel_delegate.cpp | 47 +++ .../webview_environment_channel_delegate.h | 25 ++ .../webview_environment_manager.cpp | 63 ++++ .../webview_environment_manager.h | 33 ++ .../webview_environment_settings.cpp | 27 ++ .../webview_environment_settings.h | 29 ++ 69 files changed, 1547 insertions(+), 448 deletions(-) create mode 100644 flutter_inappwebview/lib/src/webview_environment/main.dart create mode 100644 flutter_inappwebview/lib/src/webview_environment/webview_environment.dart create mode 100644 flutter_inappwebview_platform_interface/lib/src/webview_environment/main.dart create mode 100644 flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart create mode 100644 flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.dart create mode 100644 flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.g.dart create mode 100644 flutter_inappwebview_windows/lib/src/webview_environment/main.dart create mode 100644 flutter_inappwebview_windows/lib/src/webview_environment/webview_environment.dart create mode 100644 flutter_inappwebview_windows/windows/cookie_manager.cpp create mode 100644 flutter_inappwebview_windows/windows/cookie_manager.h create mode 100644 flutter_inappwebview_windows/windows/types/rect.cpp create mode 100644 flutter_inappwebview_windows/windows/types/rect.h create mode 100644 flutter_inappwebview_windows/windows/types/screenshot_configuration.cpp create mode 100644 flutter_inappwebview_windows/windows/types/screenshot_configuration.h create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment.cpp create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment.h create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.cpp create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.h create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.cpp create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.h diff --git a/dev_packages/generators/lib/src/exchangeable_enum_generator.dart b/dev_packages/generators/lib/src/exchangeable_enum_generator.dart index c362e681..c7802cfa 100644 --- a/dev_packages/generators/lib/src/exchangeable_enum_generator.dart +++ b/dev_packages/generators/lib/src/exchangeable_enum_generator.dart @@ -140,6 +140,7 @@ class ExchangeableEnumGenerator []; var hasWebSupport = false; var webSupportValue = null; + var allPlatformsWithoutValue = true; if (platforms.isNotEmpty) { for (var platform in platforms) { final targetPlatformName = @@ -150,6 +151,9 @@ class ExchangeableEnumGenerator ? platformValueField.toIntValue() ?? "'${platformValueField.toStringValue()}'" : null; + if (allPlatformsWithoutValue && platformValue != null) { + allPlatformsWithoutValue = false; + } if (targetPlatformName == "web") { hasWebSupport = true; webSupportValue = platformValue; @@ -170,8 +174,13 @@ class ExchangeableEnumGenerator nativeValueBody += "return $defaultValue;"; nativeValueBody += "}"; - classBuffer.writeln( - "static final $fieldName = $extClassName._internalMultiPlatform($constantValue, $nativeValueBody);"); + if (!allPlatformsWithoutValue) { + classBuffer.writeln( + "static final $fieldName = $extClassName._internalMultiPlatform($constantValue, $nativeValueBody);"); + } else { + classBuffer.writeln( + "static const $fieldName = $extClassName._internal($constantValue, ${defaultValue ?? constantValue});"); + } } else { classBuffer.writeln( "static const $fieldName = $extClassName._internal($constantValue, $constantValue);"); diff --git a/flutter_inappwebview/example/lib/in_app_webiew_example.screen.dart b/flutter_inappwebview/example/lib/in_app_webiew_example.screen.dart index 79dc016f..0cd746dd 100755 --- a/flutter_inappwebview/example/lib/in_app_webiew_example.screen.dart +++ b/flutter_inappwebview/example/lib/in_app_webiew_example.screen.dart @@ -115,6 +115,7 @@ class _InAppWebViewExampleScreenState extends State { children: [ InAppWebView( key: webViewKey, + webViewEnvironment: webViewEnvironment, initialUrlRequest: URLRequest(url: WebUri('https://flutter.dev')), // initialUrlRequest: diff --git a/flutter_inappwebview/example/lib/main.dart b/flutter_inappwebview/example/lib/main.dart index d94bc1b5..881061dd 100755 --- a/flutter_inappwebview/example/lib/main.dart +++ b/flutter_inappwebview/example/lib/main.dart @@ -15,6 +15,7 @@ import 'package:pointer_interceptor/pointer_interceptor.dart'; // import 'package:permission_handler/permission_handler.dart'; final localhostServer = InAppLocalhostServer(documentRoot: 'assets'); +WebViewEnvironment? webViewEnvironment; Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -22,6 +23,13 @@ Future main() async { // await Permission.microphone.request(); // await Permission.storage.request(); + if (!kIsWeb && defaultTargetPlatform == TargetPlatform.windows) { + webViewEnvironment = await WebViewEnvironment.create(settings: + WebViewEnvironmentSettings( + userDataFolder: 'custom_path' + )); + } + if (!kIsWeb && defaultTargetPlatform == TargetPlatform.android) { await InAppWebViewController.setWebContentsDebuggingEnabled(kDebugMode); } diff --git a/flutter_inappwebview/example/macos/Flutter/GeneratedPluginRegistrant.swift b/flutter_inappwebview/example/macos/Flutter/GeneratedPluginRegistrant.swift index 959e5aa0..a1cdfd0c 100644 --- a/flutter_inappwebview/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/flutter_inappwebview/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,12 +5,10 @@ import FlutterMacOS import Foundation -import flutter_inappwebview_macos import path_provider_foundation import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/flutter_inappwebview/example/pubspec.yaml b/flutter_inappwebview/example/pubspec.yaml index 59aa16aa..dffe2a82 100755 --- a/flutter_inappwebview/example/pubspec.yaml +++ b/flutter_inappwebview/example/pubspec.yaml @@ -35,14 +35,14 @@ dependencies: dependency_overrides: flutter_inappwebview_platform_interface: path: ../../flutter_inappwebview_platform_interface - flutter_inappwebview_android: - path: ../../flutter_inappwebview_android - flutter_inappwebview_ios: - path: ../../flutter_inappwebview_ios - flutter_inappwebview_macos: - path: ../../flutter_inappwebview_macos - flutter_inappwebview_web: - path: ../../flutter_inappwebview_web + #flutter_inappwebview_android: + # path: ../../flutter_inappwebview_android + #flutter_inappwebview_ios: + # path: ../../flutter_inappwebview_ios + #flutter_inappwebview_macos: + # path: ../../flutter_inappwebview_macos + #flutter_inappwebview_web: + # path: ../../flutter_inappwebview_web flutter_inappwebview_windows: path: ../../flutter_inappwebview_windows diff --git a/flutter_inappwebview/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview/lib/src/in_app_webview/in_app_webview.dart index e2490bfc..b885aa3c 100755 --- a/flutter_inappwebview/lib/src/in_app_webview/in_app_webview.dart +++ b/flutter_inappwebview/lib/src/in_app_webview/in_app_webview.dart @@ -9,6 +9,7 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import '../webview_environment/webview_environment.dart'; import 'headless_in_app_webview.dart'; import 'in_app_webview_controller.dart'; import '../find_interaction/find_interaction_controller.dart'; @@ -42,6 +43,7 @@ class InAppWebView extends StatefulWidget { InAppWebViewKeepAlive? keepAlive, bool? preventGestureDelay, TextDirection? layoutDirection, + WebViewEnvironment? webViewEnvironment, @Deprecated('Use onGeolocationPermissionsHidePrompt instead') void Function(InAppWebViewController controller)? androidOnGeolocationPermissionsHidePrompt, @@ -310,6 +312,7 @@ class InAppWebView extends StatefulWidget { findInteractionController: findInteractionController?.platform, contextMenu: contextMenu, layoutDirection: layoutDirection, + webViewEnvironment: webViewEnvironment?.platform, onWebViewCreated: onWebViewCreated != null ? (controller) => onWebViewCreated.call(controller) : null, diff --git a/flutter_inappwebview/lib/src/main.dart b/flutter_inappwebview/lib/src/main.dart index 2b968fee..64e28fb3 100644 --- a/flutter_inappwebview/lib/src/main.dart +++ b/flutter_inappwebview/lib/src/main.dart @@ -15,3 +15,4 @@ export 'webview_asset_loader.dart'; export 'tracing_controller.dart'; export 'process_global_config.dart'; export 'in_app_localhost_server.dart'; +export 'webview_environment/main.dart'; diff --git a/flutter_inappwebview/lib/src/webview_environment/main.dart b/flutter_inappwebview/lib/src/webview_environment/main.dart new file mode 100644 index 00000000..ec25d6bb --- /dev/null +++ b/flutter_inappwebview/lib/src/webview_environment/main.dart @@ -0,0 +1 @@ +export 'webview_environment.dart'; \ No newline at end of file diff --git a/flutter_inappwebview/lib/src/webview_environment/webview_environment.dart b/flutter_inappwebview/lib/src/webview_environment/webview_environment.dart new file mode 100644 index 00000000..4b89d135 --- /dev/null +++ b/flutter_inappwebview/lib/src/webview_environment/webview_environment.dart @@ -0,0 +1,35 @@ +import 'dart:core'; + +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment} +class WebViewEnvironment { + /// Constructs a [WebViewEnvironment]. + /// + /// See [WebViewEnvironment.fromPlatformCreationParams] for setting parameters for + /// a specific platform. + WebViewEnvironment.fromPlatformCreationParams({ + required PlatformWebViewEnvironmentCreationParams params, + }) : this.fromPlatform(platform: PlatformWebViewEnvironment(params)); + + /// Constructs a [WebViewEnvironment] from a specific platform implementation. + WebViewEnvironment.fromPlatform({required this.platform}); + + /// Implementation of [PlatformWebViewEnvironment] for the current platform. + final PlatformWebViewEnvironment platform; + + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.id} + String get id => platform.id; + + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.settings} + WebViewEnvironmentSettings? get settings => platform.settings; + + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.create} + static Future create( + {WebViewEnvironmentSettings? settings}) async { + return WebViewEnvironment.fromPlatform(platform: await PlatformWebViewEnvironment.static().create(settings: settings)); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.dispose} + Future dispose() => platform.dispose(); +} \ No newline at end of file diff --git a/flutter_inappwebview/pubspec.yaml b/flutter_inappwebview/pubspec.yaml index ef480d4a..03b3d522 100755 --- a/flutter_inappwebview/pubspec.yaml +++ b/flutter_inappwebview/pubspec.yaml @@ -18,11 +18,12 @@ environment: dependencies: flutter: sdk: flutter - flutter_inappwebview_platform_interface: ^1.0.10 - flutter_inappwebview_android: ^1.0.12 - flutter_inappwebview_ios: ^1.0.13 - flutter_inappwebview_macos: ^1.0.11 - flutter_inappwebview_web: ^1.0.8 + flutter_inappwebview_platform_interface: #^1.0.10 + path: ../flutter_inappwebview_platform_interface + #flutter_inappwebview_android: ^1.0.12 + #flutter_inappwebview_ios: ^1.0.13 + #flutter_inappwebview_macos: ^1.0.11 + #flutter_inappwebview_web: ^1.0.8 dev_dependencies: flutter_test: diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart index eb01d7c5..077f9180 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart @@ -750,6 +750,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ///- iOS ([Official API - WKWebView.takeSnapshot](https://developer.apple.com/documentation/webkit/wkwebview/2873260-takesnapshot)) ///- MacOS ([Official API - WKWebView.takeSnapshot](https://developer.apple.com/documentation/webkit/wkwebview/2873260-takesnapshot)) + ///- Windows ///{@endtemplate} Future takeScreenshot( {ScreenshotConfiguration? screenshotConfiguration}) { diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart index c79090a6..0f3aa670 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart @@ -5,6 +5,7 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import '../inappwebview_platform.dart'; import '../types/disposable.dart'; +import '../webview_environment/platform_webview_environment.dart'; import 'in_app_webview_keep_alive.dart'; import 'platform_webview.dart'; import 'platform_headless_in_app_webview.dart'; @@ -24,6 +25,7 @@ class PlatformInAppWebViewWidgetCreationParams this.headlessWebView, this.keepAlive, this.preventGestureDelay, + this.webViewEnvironment, super.controllerFromPlatform, super.windowId, super.onWebViewCreated, @@ -181,6 +183,12 @@ class PlatformInAppWebViewWidgetCreationParams ///**Officially Supported Platforms/Implementations**: ///- iOS final bool? preventGestureDelay; + + ///Used create the [PlatformInAppWebViewWidget] using the specified environment. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows + final PlatformWebViewEnvironment? webViewEnvironment; } /// Interface for a platform implementation of a web view widget. diff --git a/flutter_inappwebview_platform_interface/lib/src/inappwebview_platform.dart b/flutter_inappwebview_platform_interface/lib/src/inappwebview_platform.dart index a216e5b9..683ffbdb 100644 --- a/flutter_inappwebview_platform_interface/lib/src/inappwebview_platform.dart +++ b/flutter_inappwebview_platform_interface/lib/src/inappwebview_platform.dart @@ -24,6 +24,7 @@ import 'platform_tracing_controller.dart'; import 'platform_webview_asset_loader.dart'; import 'platform_webview_feature.dart'; import 'in_app_localhost_server.dart'; +import 'webview_environment/platform_webview_environment.dart'; /// Interface for a platform implementation of a WebView. abstract class InAppWebViewPlatform extends PlatformInterface { @@ -430,4 +431,24 @@ abstract class InAppWebViewPlatform extends PlatformInterface { throw UnimplementedError( 'createPlatformChromeSafariBrowserStatic is not implemented on the current platform.'); } + + /// Creates a new [PlatformWebViewEnvironment]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewEnvironment] in `flutter_inappwebview` instead. + PlatformWebViewEnvironment createPlatformWebViewEnvironment( + PlatformWebViewEnvironmentCreationParams params, + ) { + throw UnimplementedError( + 'createPlatformWebViewEnvironment is not implemented on the current platform.'); + } + + /// Creates a new empty [PlatformWebViewEnvironment] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewEnvironment] in `flutter_inappwebview` instead. + PlatformWebViewEnvironment createPlatformWebViewEnvironmentStatic() { + throw UnimplementedError( + 'createPlatformWebViewEnvironmentStatic is not implemented on the current platform.'); + } } diff --git a/flutter_inappwebview_platform_interface/lib/src/main.dart b/flutter_inappwebview_platform_interface/lib/src/main.dart index 5b8d6cb7..bf9626ae 100644 --- a/flutter_inappwebview_platform_interface/lib/src/main.dart +++ b/flutter_inappwebview_platform_interface/lib/src/main.dart @@ -1,5 +1,6 @@ export 'inappwebview_platform.dart'; export 'types/main.dart'; +export 'webview_environment/main.dart'; export 'in_app_webview/main.dart'; export 'in_app_browser/main.dart'; export 'chrome_safari_browser/main.dart'; diff --git a/flutter_inappwebview_platform_interface/lib/src/types/compress_format.dart b/flutter_inappwebview_platform_interface/lib/src/types/compress_format.dart index f7967e24..23a59412 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/compress_format.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/compress_format.dart @@ -11,36 +11,48 @@ class CompressFormat_ { ///Compress to the `PNG` format. ///PNG is lossless, so `quality` is ignored. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform(), + EnumIOSPlatform(), + EnumMacOSPlatform(), + EnumWindowsPlatform(), + ]) static const PNG = const CompressFormat_._internal("PNG"); ///Compress to the `JPEG` format. ///Quality of `0` means compress for the smallest size. ///`100` means compress for max visual quality. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform(), + EnumIOSPlatform(), + EnumMacOSPlatform(), + EnumWindowsPlatform(), + ]) static const JPEG = const CompressFormat_._internal("JPEG"); ///Compress to the `WEBP` lossy format. ///Quality of `0` means compress for the smallest size. ///`100` means compress for max visual quality. - /// - ///**NOTE**: available only on Android. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform(), + EnumWindowsPlatform(), + ]) static const WEBP = const CompressFormat_._internal("WEBP"); ///Compress to the `WEBP` lossy format. ///Quality of `0` means compress for the smallest size. ///`100` means compress for max visual quality. - /// - ///**NOTE**: available only on Android. - /// - ///**NOTE for Android**: available on Android 30+. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform(available: '30'), + ]) static const WEBP_LOSSY = const CompressFormat_._internal("WEBP_LOSSY"); ///Compress to the `WEBP` lossless format. ///Quality refers to how much effort to put into compression. ///A value of `0` means to compress quickly, resulting in a relatively large file size. ///`100` means to spend more time compressing, resulting in a smaller file. - /// - ///**NOTE**: available only on Android. - /// - ///**NOTE for Android**: available on Android 30+. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform(available: '30'), + ]) static const WEBP_LOSSLESS = const CompressFormat_._internal("WEBP_LOSSLESS"); } diff --git a/flutter_inappwebview_platform_interface/lib/src/types/compress_format.g.dart b/flutter_inappwebview_platform_interface/lib/src/types/compress_format.g.dart index bad4b339..befe838f 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/compress_format.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/compress_format.g.dart @@ -19,17 +19,31 @@ class CompressFormat { ///Compress to the `JPEG` format. ///Quality of `0` means compress for the smallest size. ///`100` means compress for max visual quality. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///- Windows static const JPEG = CompressFormat._internal('JPEG', 'JPEG'); ///Compress to the `PNG` format. ///PNG is lossless, so `quality` is ignored. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///- Windows static const PNG = CompressFormat._internal('PNG', 'PNG'); ///Compress to the `WEBP` lossy format. ///Quality of `0` means compress for the smallest size. ///`100` means compress for max visual quality. /// - ///**NOTE**: available only on Android. + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- Windows static const WEBP = CompressFormat._internal('WEBP', 'WEBP'); ///Compress to the `WEBP` lossless format. @@ -37,9 +51,8 @@ class CompressFormat { ///A value of `0` means to compress quickly, resulting in a relatively large file size. ///`100` means to spend more time compressing, resulting in a smaller file. /// - ///**NOTE**: available only on Android. - /// - ///**NOTE for Android**: available on Android 30+. + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView 30+ static const WEBP_LOSSLESS = CompressFormat._internal('WEBP_LOSSLESS', 'WEBP_LOSSLESS'); @@ -47,9 +60,8 @@ class CompressFormat { ///Quality of `0` means compress for the smallest size. ///`100` means compress for max visual quality. /// - ///**NOTE**: available only on Android. - /// - ///**NOTE for Android**: available on Android 30+. + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView 30+ static const WEBP_LOSSY = CompressFormat._internal('WEBP_LOSSY', 'WEBP_LOSSY'); diff --git a/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.dart b/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.dart index ce033a58..8d364fb0 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.dart @@ -12,6 +12,12 @@ class ScreenshotConfiguration_ { ///The portion of your web view to capture, specified as a rectangle in the view’s coordinate system. ///The default value of this property is `null`, which captures everything in the view’s bounds rectangle. ///If you specify a custom rectangle, it must lie within the bounds rectangle of the `WebView` object. + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform(), + WindowsPlatform() + ]) InAppWebViewRect_? rect; ///The width of the captured image, in points. @@ -19,14 +25,31 @@ class ScreenshotConfiguration_ { ///The web view maintains the aspect ratio of the captured content, but scales it to match the width you specify. /// ///The default value of this property is `null`, which returns an image whose size matches the original size of the captured rectangle. + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform() + ]) double? snapshotWidth; ///The compression format of the captured image. ///The default value is [CompressFormat.PNG]. + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform(), + WindowsPlatform() + ]) CompressFormat_ compressFormat; ///Hint to the compressor, `0-100`. The value is interpreted differently depending on the [CompressFormat]. ///[CompressFormat.PNG] is lossless, so this value is ignored. + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform(), + WindowsPlatform() + ]) int quality; ///Use [afterScreenUpdates] instead. @@ -36,10 +59,10 @@ class ScreenshotConfiguration_ { ///A Boolean value that indicates whether to take the snapshot after incorporating any pending screen updates. ///The default value of this property is `true`, which causes the web view to incorporate any recent changes to the view’s content and then generate the snapshot. ///If you change the value to `false`, the `WebView` takes the snapshot immediately, and before incorporating any new changes. - /// - ///**NOTE**: available only on iOS. - /// - ///**NOTE for iOS**: Available from iOS 13.0+. + @SupportedPlatforms(platforms: [ + IOSPlatform(available: '13.0'), + MacOSPlatform(available: '10.15'), + ]) bool afterScreenUpdates; @ExchangeableObjectConstructor() diff --git a/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.g.dart b/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.g.dart index 1a22c107..460c7890 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.g.dart @@ -12,13 +12,19 @@ class ScreenshotConfiguration { ///The default value of this property is `true`, which causes the web view to incorporate any recent changes to the view’s content and then generate the snapshot. ///If you change the value to `false`, the `WebView` takes the snapshot immediately, and before incorporating any new changes. /// - ///**NOTE**: available only on iOS. - /// - ///**NOTE for iOS**: Available from iOS 13.0+. + ///**Officially Supported Platforms/Implementations**: + ///- iOS 13.0+ + ///- MacOS 10.15+ bool afterScreenUpdates; ///The compression format of the captured image. ///The default value is [CompressFormat.PNG]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///- Windows CompressFormat compressFormat; ///Use [afterScreenUpdates] instead. @@ -27,11 +33,23 @@ class ScreenshotConfiguration { ///Hint to the compressor, `0-100`. The value is interpreted differently depending on the [CompressFormat]. ///[CompressFormat.PNG] is lossless, so this value is ignored. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///- Windows int quality; ///The portion of your web view to capture, specified as a rectangle in the view’s coordinate system. ///The default value of this property is `null`, which captures everything in the view’s bounds rectangle. ///If you specify a custom rectangle, it must lie within the bounds rectangle of the `WebView` object. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///- Windows InAppWebViewRect? rect; ///The width of the captured image, in points. @@ -39,6 +57,11 @@ class ScreenshotConfiguration { ///The web view maintains the aspect ratio of the captured content, but scales it to match the width you specify. /// ///The default value of this property is `null`, which returns an image whose size matches the original size of the captured rectangle. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS double? snapshotWidth; ScreenshotConfiguration( {this.rect, diff --git a/flutter_inappwebview_platform_interface/lib/src/webview_environment/main.dart b/flutter_inappwebview_platform_interface/lib/src/webview_environment/main.dart new file mode 100644 index 00000000..09b2d039 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/webview_environment/main.dart @@ -0,0 +1,2 @@ +export 'platform_webview_environment.dart'; +export 'webview_environment_settings.dart' show WebViewEnvironmentSettings; \ No newline at end of file diff --git a/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart b/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart new file mode 100644 index 00000000..9bbe5bb5 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart @@ -0,0 +1,106 @@ +import 'package:flutter/foundation.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import '../debug_logging_settings.dart'; +import '../inappwebview_platform.dart'; +import '../types/disposable.dart'; +import 'webview_environment_settings.dart'; + +/// Object specifying creation parameters for creating a [PlatformWebViewEnvironment]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +@immutable +class PlatformWebViewEnvironmentCreationParams { + /// Used by the platform implementation to create a new [PlatformWebViewEnvironment]. + const PlatformWebViewEnvironmentCreationParams({this.settings}); + + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.settings} + final WebViewEnvironmentSettings? settings; +} + +///Controls a WebView Environment used by WebView instances. +/// +///**Officially Supported Platforms/Implementations**: +///- Windows +abstract class PlatformWebViewEnvironment extends PlatformInterface + implements Disposable { + ///Debug settings used by [PlatformWebViewEnvironment]. + static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings( + maxLogMessageLength: 1000 + ); + + /// Creates a new [PlatformInAppWebViewController] + factory PlatformWebViewEnvironment( + PlatformWebViewEnvironmentCreationParams params) { + assert( + InAppWebViewPlatform.instance != null, + 'A platform implementation for `flutter_inappwebview` has not been set. Please ' + 'ensure that an implementation of `InAppWebViewPlatform` has been set to ' + '`InAppWebViewPlatform.instance` before use. For unit testing, ' + '`InAppWebViewPlatform.instance` can be set with your own test implementation.', + ); + final PlatformWebViewEnvironment webViewEnvironment = + InAppWebViewPlatform.instance! + .createPlatformWebViewEnvironment(params); + PlatformInterface.verify(webViewEnvironment, _token); + return webViewEnvironment; + } + + /// Creates a new [PlatformWebViewEnvironment] to access static methods. + factory PlatformWebViewEnvironment.static() { + assert( + InAppWebViewPlatform.instance != null, + 'A platform implementation for `flutter_inappwebview` has not been set. Please ' + 'ensure that an implementation of `InAppWebViewPlatform` has been set to ' + '`InAppWebViewPlatform.instance` before use. For unit testing, ' + '`InAppWebViewPlatform.instance` can be set with your own test implementation.', + ); + final PlatformWebViewEnvironment webViewEnvironment = + InAppWebViewPlatform.instance! + .createPlatformWebViewEnvironmentStatic(); + PlatformInterface.verify(webViewEnvironment, _token); + return webViewEnvironment; + } + + /// Used by the platform implementation to create a new [PlatformWebViewEnvironment]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformWebViewEnvironment.implementation(this.params) + : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformWebViewEnvironment]. + final PlatformWebViewEnvironmentCreationParams params; + + ///{@template flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.id} + /// WebView Environment ID. + ///{@endtemplate} + String get id => + throw UnimplementedError('id is not implemented on the current platform'); + + ///{@template flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.settings} + /// WebView Environment settings. + ///{@endtemplate} + WebViewEnvironmentSettings? get settings => params.settings; + + ///{@template flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.create} + ///Initializes the [PlatformWebViewEnvironment] using [settings]. + ///{@endtemplate} + Future create( + {WebViewEnvironmentSettings? settings}) { + throw UnimplementedError( + 'create is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.dispose} + ///Disposes the WebView Environment reference. + ///{@endtemplate} + Future dispose() { + throw UnimplementedError( + 'dispose is not implemented on the current platform'); + } +} \ No newline at end of file diff --git a/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.dart b/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.dart new file mode 100644 index 00000000..6c14a6d4 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.dart @@ -0,0 +1,25 @@ +import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; + +import 'platform_webview_environment.dart'; + +part 'webview_environment_settings.g.dart'; + +///This class represents all the [PlatformWebViewEnvironment] settings available. +@ExchangeableObject(copyMethod: true) +class WebViewEnvironmentSettings_ { + final String? browserExecutableFolder; + final String? userDataFolder; + final String? additionalBrowserArguments; + final bool? allowSingleSignOnUsingOSPrimaryAccount; + final String? language; + final String? targetCompatibleBrowserVersion; + + WebViewEnvironmentSettings_({ + this.browserExecutableFolder, + this.userDataFolder, + this.additionalBrowserArguments, + this.allowSingleSignOnUsingOSPrimaryAccount, + this.language, + this.targetCompatibleBrowserVersion + }); +} \ No newline at end of file diff --git a/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.g.dart b/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.g.dart new file mode 100644 index 00000000..c89b9f53 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.g.dart @@ -0,0 +1,70 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'webview_environment_settings.dart'; + +// ************************************************************************** +// ExchangeableObjectGenerator +// ************************************************************************** + +///This class represents all the [PlatformWebViewEnvironment] settings available. +class WebViewEnvironmentSettings { + final String? additionalBrowserArguments; + final bool? allowSingleSignOnUsingOSPrimaryAccount; + final String? browserExecutableFolder; + final String? language; + final String? targetCompatibleBrowserVersion; + final String? userDataFolder; + WebViewEnvironmentSettings( + {this.additionalBrowserArguments, + this.allowSingleSignOnUsingOSPrimaryAccount, + this.browserExecutableFolder, + this.language, + this.targetCompatibleBrowserVersion, + this.userDataFolder}); + + ///Gets a possible [WebViewEnvironmentSettings] instance from a [Map] value. + static WebViewEnvironmentSettings? fromMap(Map? map) { + if (map == null) { + return null; + } + final instance = WebViewEnvironmentSettings( + additionalBrowserArguments: map['additionalBrowserArguments'], + allowSingleSignOnUsingOSPrimaryAccount: + map['allowSingleSignOnUsingOSPrimaryAccount'], + browserExecutableFolder: map['browserExecutableFolder'], + language: map['language'], + targetCompatibleBrowserVersion: map['targetCompatibleBrowserVersion'], + userDataFolder: map['userDataFolder'], + ); + return instance; + } + + ///Converts instance to a map. + Map toMap() { + return { + "additionalBrowserArguments": additionalBrowserArguments, + "allowSingleSignOnUsingOSPrimaryAccount": + allowSingleSignOnUsingOSPrimaryAccount, + "browserExecutableFolder": browserExecutableFolder, + "language": language, + "targetCompatibleBrowserVersion": targetCompatibleBrowserVersion, + "userDataFolder": userDataFolder, + }; + } + + ///Converts instance to a map. + Map toJson() { + return toMap(); + } + + ///Returns a copy of WebViewEnvironmentSettings. + WebViewEnvironmentSettings copy() { + return WebViewEnvironmentSettings.fromMap(toMap()) ?? + WebViewEnvironmentSettings(); + } + + @override + String toString() { + return 'WebViewEnvironmentSettings{additionalBrowserArguments: $additionalBrowserArguments, allowSingleSignOnUsingOSPrimaryAccount: $allowSingleSignOnUsingOSPrimaryAccount, browserExecutableFolder: $browserExecutableFolder, language: $language, targetCompatibleBrowserVersion: $targetCompatibleBrowserVersion, userDataFolder: $userDataFolder}'; + } +} diff --git a/flutter_inappwebview_platform_interface/pubspec.yaml b/flutter_inappwebview_platform_interface/pubspec.yaml index bbb0f0a9..a3d59800 100644 --- a/flutter_inappwebview_platform_interface/pubspec.yaml +++ b/flutter_inappwebview_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_inappwebview_platform_interface description: A common platform interface for the flutter_inappwebview plugin. -version: 1.0.10 +version: 1.0.11 homepage: https://inappwebview.dev/ repository: https://github.com/pichillilorenzo/flutter_inappwebview/tree/master/flutter_inappwebview_platform_interface issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues diff --git a/flutter_inappwebview_windows/example/pubspec.lock b/flutter_inappwebview_windows/example/pubspec.lock index d24b5eee..847582b6 100644 --- a/flutter_inappwebview_windows/example/pubspec.lock +++ b/flutter_inappwebview_windows/example/pubspec.lock @@ -86,10 +86,9 @@ packages: flutter_inappwebview_platform_interface: dependency: transitive description: - name: flutter_inappwebview_platform_interface - sha256: "545fd4c25a07d2775f7d5af05a979b2cac4fbf79393b0a7f5d33ba39ba4f6187" - url: "https://pub.dev" - source: hosted + path: "../../flutter_inappwebview_platform_interface" + relative: true + source: path version: "1.0.10" flutter_inappwebview_windows: dependency: "direct main" diff --git a/flutter_inappwebview_windows/lib/src/cookie_manager.dart b/flutter_inappwebview_windows/lib/src/cookie_manager.dart index a95dc409..81903ca7 100644 --- a/flutter_inappwebview_windows/lib/src/cookie_manager.dart +++ b/flutter_inappwebview_windows/lib/src/cookie_manager.dart @@ -5,56 +5,54 @@ import 'package:flutter/services.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; -import 'in_app_webview/headless_in_app_webview.dart'; -import 'platform_util.dart'; - -/// Object specifying creation parameters for creating a [MacOSCookieManager]. +/// Object specifying creation parameters for creating a [WindowsCookieManager]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformCookieManagerCreationParams] for /// more information. @immutable -class MacOSCookieManagerCreationParams +class WindowsCookieManagerCreationParams extends PlatformCookieManagerCreationParams { - /// Creates a new [MacOSCookieManagerCreationParams] instance. - const MacOSCookieManagerCreationParams( - // This parameter prevents breaking changes later. - // ignore: avoid_unused_constructor_parameters - PlatformCookieManagerCreationParams params, - ) : super(); + /// Creates a new [WindowsCookieManagerCreationParams] instance. + const WindowsCookieManagerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformCookieManagerCreationParams params, + ) : super(); - /// Creates a [MacOSCookieManagerCreationParams] instance based on [PlatformCookieManagerCreationParams]. - factory MacOSCookieManagerCreationParams.fromPlatformCookieManagerCreationParams( + /// Creates a [WindowsCookieManagerCreationParams] instance based on [PlatformCookieManagerCreationParams]. + factory WindowsCookieManagerCreationParams.fromPlatformCookieManagerCreationParams( PlatformCookieManagerCreationParams params) { - return MacOSCookieManagerCreationParams(params); + return WindowsCookieManagerCreationParams(params); } } ///{@macro flutter_inappwebview_platform_interface.PlatformCookieManager} -class MacOSCookieManager extends PlatformCookieManager with ChannelController { - /// Creates a new [MacOSCookieManager]. - MacOSCookieManager(PlatformCookieManagerCreationParams params) +class WindowsCookieManager extends PlatformCookieManager + with ChannelController { + /// Creates a new [WindowsCookieManager]. + WindowsCookieManager(PlatformCookieManagerCreationParams params) : super.implementation( - params is MacOSCookieManagerCreationParams - ? params - : MacOSCookieManagerCreationParams - .fromPlatformCookieManagerCreationParams(params), - ) { + params is WindowsCookieManagerCreationParams + ? params + : WindowsCookieManagerCreationParams + .fromPlatformCookieManagerCreationParams(params), + ) { channel = const MethodChannel( 'com.pichillilorenzo/flutter_inappwebview_cookiemanager'); handler = handleMethod; initMethodCallHandler(); } - static MacOSCookieManager? _instance; + static WindowsCookieManager? _instance; - ///Gets the [MacOSCookieManager] shared instance. - static MacOSCookieManager instance() { + ///Gets the [WindowsCookieManager] shared instance. + static WindowsCookieManager instance() { return (_instance != null) ? _instance! : _init(); } - static MacOSCookieManager _init() { - _instance = MacOSCookieManager(MacOSCookieManagerCreationParams( + static WindowsCookieManager _init() { + _instance = WindowsCookieManager(WindowsCookieManagerCreationParams( const PlatformCookieManagerCreationParams())); return _instance!; } @@ -64,40 +62,23 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { @override Future setCookie( {required WebUri url, - required String name, - required String value, - String path = "/", - String? domain, - int? expiresDate, - int? maxAge, - bool? isSecure, - bool? isHttpOnly, - HTTPCookieSameSitePolicy? sameSite, - @Deprecated("Use webViewController instead") - PlatformInAppWebViewController? iosBelow11WebViewController, - PlatformInAppWebViewController? webViewController}) async { - webViewController = webViewController ?? iosBelow11WebViewController; - + required String name, + required String value, + String path = "/", + String? domain, + int? expiresDate, + int? maxAge, + bool? isSecure, + bool? isHttpOnly, + HTTPCookieSameSitePolicy? sameSite, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { assert(url.toString().isNotEmpty); assert(name.isNotEmpty); assert(value.isNotEmpty); assert(path.isNotEmpty); - if (await _shouldUseJavascript()) { - await _setCookieWithJavaScript( - url: url, - name: name, - value: value, - domain: domain, - path: path, - expiresDate: expiresDate, - maxAge: maxAge, - isSecure: isSecure, - sameSite: sameSite, - webViewController: webViewController); - return true; - } - Map args = {}; args.putIfAbsent('url', () => url.toString()); args.putIfAbsent('name', () => name); @@ -113,72 +94,14 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { return await channel?.invokeMethod('setCookie', args) ?? false; } - Future _setCookieWithJavaScript( - {required WebUri url, - required String name, - required String value, - String path = "/", - String? domain, - int? expiresDate, - int? maxAge, - bool? isSecure, - HTTPCookieSameSitePolicy? sameSite, - PlatformInAppWebViewController? webViewController}) async { - var cookieValue = name + "=" + value + "; Path=" + path; - - if (domain != null) cookieValue += "; Domain=" + domain; - - if (expiresDate != null) - cookieValue += "; Expires=" + await _getCookieExpirationDate(expiresDate); - - if (maxAge != null) cookieValue += "; Max-Age=" + maxAge.toString(); - - if (isSecure != null && isSecure) cookieValue += "; Secure"; - - if (sameSite != null) - cookieValue += "; SameSite=" + sameSite.toNativeValue(); - - cookieValue += ";"; - - if (webViewController != null) { - final javaScriptEnabled = - (await webViewController.getSettings())?.javaScriptEnabled ?? false; - if (javaScriptEnabled) { - await webViewController.evaluateJavascript( - source: 'document.cookie="$cookieValue"'); - return; - } - } - - final setCookieCompleter = Completer(); - final headlessWebView = - WindowsHeadlessInAppWebView(WindowsHeadlessInAppWebViewCreationParams( - initialUrlRequest: URLRequest(url: url), - onLoadStop: (controller, url) async { - await controller.evaluateJavascript( - source: 'document.cookie="$cookieValue"'); - setCookieCompleter.complete(); - })); - await headlessWebView.run(); - await setCookieCompleter.future; - await headlessWebView.dispose(); - } - @override Future> getCookies( {required WebUri url, - @Deprecated("Use webViewController instead") - PlatformInAppWebViewController? iosBelow11WebViewController, - PlatformInAppWebViewController? webViewController}) async { + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { assert(url.toString().isNotEmpty); - webViewController = webViewController ?? iosBelow11WebViewController; - - if (await _shouldUseJavascript()) { - return await _getCookiesWithJavaScript( - url: url, webViewController: webViewController); - } - List cookies = []; Map args = {}; @@ -195,7 +118,7 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { isSessionOnly: cookieMap["isSessionOnly"], domain: cookieMap["domain"], sameSite: - HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), + HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), isSecure: cookieMap["isSecure"], isHttpOnly: cookieMap["isHttpOnly"], path: cookieMap["path"])); @@ -203,84 +126,16 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { return cookies; } - Future> _getCookiesWithJavaScript( - {required WebUri url, - PlatformInAppWebViewController? webViewController}) async { - assert(url.toString().isNotEmpty); - - List cookies = []; - - if (webViewController != null) { - final javaScriptEnabled = - (await webViewController.getSettings())?.javaScriptEnabled ?? false; - if (javaScriptEnabled) { - List documentCookies = (await webViewController - .evaluateJavascript(source: 'document.cookie') as String) - .split(';') - .map((documentCookie) => documentCookie.trim()) - .toList(); - documentCookies.forEach((documentCookie) { - List cookie = documentCookie.split('='); - if (cookie.length > 1) { - cookies.add(Cookie( - name: cookie[0], - value: cookie[1], - )); - } - }); - return cookies; - } - } - - final pageLoaded = Completer(); - final headlessWebView = - WindowsHeadlessInAppWebView(WindowsHeadlessInAppWebViewCreationParams( - initialUrlRequest: URLRequest(url: url), - onLoadStop: (controller, url) async { - pageLoaded.complete(); - }, - )); - await headlessWebView.run(); - await pageLoaded.future; - - List documentCookies = (await headlessWebView.webViewController! - .evaluateJavascript(source: 'document.cookie') as String) - .split(';') - .map((documentCookie) => documentCookie.trim()) - .toList(); - documentCookies.forEach((documentCookie) { - List cookie = documentCookie.split('='); - if (cookie.length > 1) { - cookies.add(Cookie( - name: cookie[0], - value: cookie[1], - )); - } - }); - await headlessWebView.dispose(); - return cookies; - } - @override Future getCookie( {required WebUri url, - required String name, - @Deprecated("Use webViewController instead") - PlatformInAppWebViewController? iosBelow11WebViewController, - PlatformInAppWebViewController? webViewController}) async { + required String name, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { assert(url.toString().isNotEmpty); assert(name.isNotEmpty); - webViewController = webViewController ?? iosBelow11WebViewController; - - if (await _shouldUseJavascript()) { - List cookies = await _getCookiesWithJavaScript( - url: url, webViewController: webViewController); - return cookies - .cast() - .firstWhere((cookie) => cookie!.name == name, orElse: () => null); - } - Map args = {}; args.putIfAbsent('url', () => url.toString()); List cookies = @@ -307,29 +162,15 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { @override Future deleteCookie( {required WebUri url, - required String name, - String path = "/", - String? domain, - @Deprecated("Use webViewController instead") - PlatformInAppWebViewController? iosBelow11WebViewController, - PlatformInAppWebViewController? webViewController}) async { + required String name, + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { assert(url.toString().isNotEmpty); assert(name.isNotEmpty); - webViewController = webViewController ?? iosBelow11WebViewController; - - if (await _shouldUseJavascript()) { - await _setCookieWithJavaScript( - url: url, - name: name, - value: "", - path: path, - domain: domain, - maxAge: -1, - webViewController: webViewController); - return true; - } - Map args = {}; args.putIfAbsent('url', () => url.toString()); args.putIfAbsent('name', () => name); @@ -341,31 +182,13 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { @override Future deleteCookies( {required WebUri url, - String path = "/", - String? domain, - @Deprecated("Use webViewController instead") - PlatformInAppWebViewController? iosBelow11WebViewController, - PlatformInAppWebViewController? webViewController}) async { + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { assert(url.toString().isNotEmpty); - webViewController = webViewController ?? iosBelow11WebViewController; - - if (await _shouldUseJavascript()) { - List cookies = await _getCookiesWithJavaScript( - url: url, webViewController: webViewController); - for (var i = 0; i < cookies.length; i++) { - await _setCookieWithJavaScript( - url: url, - name: cookies[i].name, - value: "", - path: path, - domain: domain, - maxAge: -1, - webViewController: webViewController); - } - return true; - } - Map args = {}; args.putIfAbsent('url', () => url.toString()); args.putIfAbsent('domain', () => domain); @@ -380,46 +203,10 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { } @override - Future> getAllCookies() async { - List cookies = []; - + Future removeSessionCookies() async { Map args = {}; - List cookieListMap = - await channel?.invokeMethod('getAllCookies', args) ?? []; - cookieListMap = cookieListMap.cast>(); - - cookieListMap.forEach((cookieMap) { - cookies.add(Cookie( - name: cookieMap["name"], - value: cookieMap["value"], - expiresDate: cookieMap["expiresDate"], - isSessionOnly: cookieMap["isSessionOnly"], - domain: cookieMap["domain"], - sameSite: - HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), - isSecure: cookieMap["isSecure"], - isHttpOnly: cookieMap["isHttpOnly"], - path: cookieMap["path"])); - }); - return cookies; - } - - Future _getCookieExpirationDate(int expiresDate) async { - var platformUtil = PlatformUtil.instance(); - var dateTime = DateTime.fromMillisecondsSinceEpoch(expiresDate).toUtc(); - return !kIsWeb - ? await platformUtil.formatDate( - date: dateTime, - format: 'EEE, dd MMM yyyy hh:mm:ss z', - locale: 'en_US', - timezone: 'GMT') - : await platformUtil.getWebCookieExpirationDate(date: dateTime); - } - - Future _shouldUseJavascript() async { - final platformUtil = PlatformUtil.instance(); - final systemVersion = await platformUtil.getSystemVersion(); - return systemVersion.compareTo("11") == -1; + return await channel?.invokeMethod('removeSessionCookies', args) ?? + false; } @override @@ -428,6 +215,6 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { } } -extension InternalCookieManager on MacOSCookieManager { +extension InternalCookieManager on WindowsCookieManager { get handleMethod => _handleMethod; } diff --git a/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart b/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart index ca291cbc..9835059a 100644 --- a/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart +++ b/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart @@ -2,7 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; -/// Object specifying creation parameters for creating a [MacOSFindInteractionController]. +/// Object specifying creation parameters for creating a [WindowsFindInteractionController]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformFindInteractionControllerCreationParams] for @@ -25,10 +25,10 @@ class MacOSFindInteractionControllerCreationParams } ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController} -class MacOSFindInteractionController extends PlatformFindInteractionController +class WindowsFindInteractionController extends PlatformFindInteractionController with ChannelController { - /// Constructs a [MacOSFindInteractionController]. - MacOSFindInteractionController( + /// Constructs a [WindowsFindInteractionController]. + WindowsFindInteractionController( PlatformFindInteractionControllerCreationParams params) : super.implementation( params is MacOSFindInteractionControllerCreationParams @@ -114,7 +114,7 @@ class MacOSFindInteractionController extends PlatformFindInteractionController } } -extension InternalFindInteractionController on MacOSFindInteractionController { +extension InternalFindInteractionController on WindowsFindInteractionController { void init(dynamic id) { channel = MethodChannel( 'com.pichillilorenzo/flutter_inappwebview_find_interaction_$id'); diff --git a/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart b/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart index e37a3068..4c1f892d 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart @@ -32,13 +32,13 @@ class WindowsInAppBrowserCreationParams contextMenu: params.contextMenu, pullToRefreshController: params.pullToRefreshController, findInteractionController: - params.findInteractionController as MacOSFindInteractionController?, + params.findInteractionController as WindowsFindInteractionController?, initialUserScripts: params.initialUserScripts, windowId: params.windowId); } @override - final MacOSFindInteractionController? findInteractionController; + final WindowsFindInteractionController? findInteractionController; } ///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser} diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart index 843b144a..0f6299a7 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart @@ -238,10 +238,10 @@ class WindowsHeadlessInAppWebViewCreationParams initialUserScripts: params.initialUserScripts, pullToRefreshController: params.pullToRefreshController, findInteractionController: params.findInteractionController - as MacOSFindInteractionController?); + as WindowsFindInteractionController?); @override - final MacOSFindInteractionController? findInteractionController; + final WindowsFindInteractionController? findInteractionController; } ///{@macro flutter_inappwebview_platform_interface.PlatformHeadlessInAppWebView} diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart index 1a35ae3b..df3d744a 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart @@ -23,6 +23,7 @@ class WindowsInAppWebViewWidgetCreationParams super.keepAlive, super.preventGestureDelay, super.windowId, + super.webViewEnvironment, super.onWebViewCreated, super.onLoadStart, super.onLoadStop, @@ -145,6 +146,7 @@ class WindowsInAppWebViewWidgetCreationParams keepAlive: params.keepAlive, preventGestureDelay: params.preventGestureDelay, windowId: params.windowId, + webViewEnvironment: params.webViewEnvironment, onWebViewCreated: params.onWebViewCreated, onLoadStart: params.onLoadStart, onLoadStop: params.onLoadStop, @@ -248,10 +250,10 @@ class WindowsInAppWebViewWidgetCreationParams initialUserScripts: params.initialUserScripts, pullToRefreshController: params.pullToRefreshController, findInteractionController: params.findInteractionController - as MacOSFindInteractionController?); + as WindowsFindInteractionController?); @override - final MacOSFindInteractionController? findInteractionController; + final WindowsFindInteractionController? findInteractionController; } ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} @@ -267,12 +269,12 @@ class WindowsInAppWebViewWidget extends PlatformInAppWebViewWidget { .fromPlatformInAppWebViewWidgetCreationParams(params), ); - WindowsInAppWebViewWidgetCreationParams get _macosParams => + WindowsInAppWebViewWidgetCreationParams get _windowsParams => params as WindowsInAppWebViewWidgetCreationParams; WindowsInAppWebViewController? _controller; - WindowsHeadlessInAppWebView? get _macosHeadlessInAppWebView => + WindowsHeadlessInAppWebView? get _windowsHeadlessInAppWebView => params.headlessWebView as WindowsHeadlessInAppWebView?; @override @@ -316,6 +318,7 @@ class WindowsInAppWebViewWidget extends PlatformInAppWebViewWidget { 'initialUserScripts': params.initialUserScripts?.map((e) => e.toMap()).toList() ?? [], 'keepAliveId': params.keepAlive?.id, + 'webViewEnvironmentId': params.webViewEnvironment?.id, }, ); } @@ -326,11 +329,11 @@ class WindowsInAppWebViewWidget extends PlatformInAppWebViewWidget { viewId = params.headlessWebView?.id; } viewId = params.keepAlive?.id ?? viewId ?? id; - _macosHeadlessInAppWebView?.internalDispose(); + _windowsHeadlessInAppWebView?.internalDispose(); _controller = WindowsInAppWebViewController( PlatformInAppWebViewControllerCreationParams( id: viewId, webviewParams: params)); - _macosParams.findInteractionController?.init(viewId); + _windowsParams.findInteractionController?.init(viewId); debugLog( className: runtimeType.toString(), id: viewId?.toString(), diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart index 020d109b..b6373a8b 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart @@ -74,7 +74,7 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController }; Set _webMessageListenerObjNames = Set(); Map _injectedScriptsFromURL = {}; - Set _webMessageChannels = Set(); + Set _webMessageChannels = Set(); Set _webMessageListeners = Set(); // static map that contains the properties to be saved and restored for keep alive feature @@ -187,7 +187,7 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController _userScripts = props.userScripts; _webMessageListenerObjNames = props.webMessageListenerObjNames; _webMessageChannels = - props.webMessageChannels as Set; + props.webMessageChannels as Set; _webMessageListeners = props.webMessageListeners as Set; } @@ -2007,7 +2007,8 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController Map args = {}; args.putIfAbsent( 'screenshotConfiguration', () => screenshotConfiguration?.toMap()); - return await channel?.invokeMethod('takeScreenshot', args); + final base64 = await channel?.invokeMethod('takeScreenshot', args); + return base64 != null ? base64Decode(base64) : null; } @override @@ -2460,12 +2461,12 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController } @override - Future createWebMessageChannel() async { + Future createWebMessageChannel() async { Map args = {}; Map? result = (await channel?.invokeMethod('createWebMessageChannel', args)) ?.cast(); - final webMessageChannel = MacOSWebMessageChannel.static().fromMap(result); + final webMessageChannel = WindowsWebMessageChannel.static().fromMap(result); if (webMessageChannel != null) { _webMessageChannels.add(webMessageChannel); } diff --git a/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart b/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart index 3cbfaa80..93ffb9be 100644 --- a/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart +++ b/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart @@ -1,9 +1,11 @@ import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'cookie_manager.dart'; import 'in_app_browser/in_app_browser.dart'; import 'in_app_webview/in_app_webview.dart'; import 'in_app_webview/in_app_webview_controller.dart'; import 'in_app_webview/headless_in_app_webview.dart'; +import 'webview_environment/webview_environment.dart'; /// Implementation of [InAppWebViewPlatform] using the WebKit API. class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { @@ -12,6 +14,18 @@ class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { InAppWebViewPlatform.instance = WindowsInAppWebViewPlatform(); } + /// Creates a new [WindowsCookieManager]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [CookieManager] in `flutter_inappwebview` instead. + @override + WindowsCookieManager createPlatformCookieManager( + PlatformCookieManagerCreationParams params, + ) { + return WindowsCookieManager(params); + } + + /// Creates a new [WindowsInAppWebViewController]. /// /// This function should only be called by the app-facing package. @@ -73,4 +87,24 @@ class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { ) { return WindowsHeadlessInAppWebView(params); } + + /// Creates a new [WindowsWebViewEnvironment]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewEnvironment] in `flutter_inappwebview` instead. + @override + WindowsWebViewEnvironment createPlatformWebViewEnvironment( + PlatformWebViewEnvironmentCreationParams params, + ) { + return WindowsWebViewEnvironment(params); + } + + /// Creates a new empty [WindowsWebViewEnvironment] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewEnvironment] in `flutter_inappwebview` instead. + @override + WindowsWebViewEnvironment createPlatformWebViewEnvironmentStatic() { + return WindowsWebViewEnvironment.static(); + } } diff --git a/flutter_inappwebview_windows/lib/src/main.dart b/flutter_inappwebview_windows/lib/src/main.dart index f5fe68f1..4fce8f8c 100644 --- a/flutter_inappwebview_windows/lib/src/main.dart +++ b/flutter_inappwebview_windows/lib/src/main.dart @@ -8,3 +8,4 @@ export 'http_auth_credentials_database.dart' export 'web_message/main.dart'; export 'print_job/main.dart'; export 'find_interaction/main.dart'; +export 'webview_environment/main.dart'; diff --git a/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart b/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart index aedbc5d1..2077c759 100644 --- a/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart +++ b/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart @@ -3,7 +3,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; import 'web_message_port.dart'; -/// Object specifying creation parameters for creating a [MacOSWebMessageChannel]. +/// Object specifying creation parameters for creating a [WindowsWebMessageChannel]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformWebMessageChannelCreationParams] for @@ -31,10 +31,10 @@ class MacOSWebMessageChannelCreationParams } ///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageChannel} -class MacOSWebMessageChannel extends PlatformWebMessageChannel +class WindowsWebMessageChannel extends PlatformWebMessageChannel with ChannelController { - /// Constructs a [MacOSWebMessageChannel]. - MacOSWebMessageChannel(PlatformWebMessageChannelCreationParams params) + /// Constructs a [WindowsWebMessageChannel]. + WindowsWebMessageChannel(PlatformWebMessageChannelCreationParams params) : super.implementation( params is MacOSWebMessageChannelCreationParams ? params @@ -47,7 +47,7 @@ class MacOSWebMessageChannel extends PlatformWebMessageChannel initMethodCallHandler(); } - static final MacOSWebMessageChannel _staticValue = MacOSWebMessageChannel( + static final WindowsWebMessageChannel _staticValue = WindowsWebMessageChannel( MacOSWebMessageChannelCreationParams( id: '', port1: @@ -56,7 +56,7 @@ class MacOSWebMessageChannel extends PlatformWebMessageChannel MacOSWebMessagePortCreationParams(index: 1)))); /// Provide static access. - factory MacOSWebMessageChannel.static() { + factory WindowsWebMessageChannel.static() { return _staticValue; } @@ -64,11 +64,11 @@ class MacOSWebMessageChannel extends PlatformWebMessageChannel MacOSWebMessagePort get _macosPort2 => port2 as MacOSWebMessagePort; - static MacOSWebMessageChannel? _fromMap(Map? map) { + static WindowsWebMessageChannel? _fromMap(Map? map) { if (map == null) { return null; } - var webMessageChannel = MacOSWebMessageChannel( + var webMessageChannel = WindowsWebMessageChannel( MacOSWebMessageChannelCreationParams( id: map["id"], port1: MacOSWebMessagePort( @@ -100,7 +100,7 @@ class MacOSWebMessageChannel extends PlatformWebMessageChannel } @override - MacOSWebMessageChannel? fromMap(Map? map) { + WindowsWebMessageChannel? fromMap(Map? map) { return _fromMap(map); } @@ -115,6 +115,6 @@ class MacOSWebMessageChannel extends PlatformWebMessageChannel } } -extension InternalWebMessageChannel on MacOSWebMessageChannel { +extension InternalWebMessageChannel on WindowsWebMessageChannel { MethodChannel? get internalChannel => channel; } diff --git a/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart b/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart index c0682298..be3ecece 100644 --- a/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart +++ b/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart @@ -31,7 +31,7 @@ class MacOSWebMessagePortCreationParams ///{@macro flutter_inappwebview_platform_interface.PlatformWebMessagePort} class MacOSWebMessagePort extends PlatformWebMessagePort { WebMessageCallback? _onMessage; - late MacOSWebMessageChannel _webMessageChannel; + late WindowsWebMessageChannel _webMessageChannel; /// Constructs a [MacOSWebMessagePort]. MacOSWebMessagePort(PlatformWebMessagePortCreationParams params) @@ -89,7 +89,7 @@ extension InternalWebMessagePort on MacOSWebMessagePort { WebMessageCallback? get onMessage => _onMessage; void set onMessage(WebMessageCallback? value) => _onMessage = value; - MacOSWebMessageChannel get webMessageChannel => _webMessageChannel; - void set webMessageChannel(MacOSWebMessageChannel value) => + WindowsWebMessageChannel get webMessageChannel => _webMessageChannel; + void set webMessageChannel(WindowsWebMessageChannel value) => _webMessageChannel = value; } diff --git a/flutter_inappwebview_windows/lib/src/webview_environment/main.dart b/flutter_inappwebview_windows/lib/src/webview_environment/main.dart new file mode 100644 index 00000000..64d0520b --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/webview_environment/main.dart @@ -0,0 +1 @@ +export 'webview_environment.dart' hide InternalWindowsWebViewEnvironment; \ No newline at end of file diff --git a/flutter_inappwebview_windows/lib/src/webview_environment/webview_environment.dart b/flutter_inappwebview_windows/lib/src/webview_environment/webview_environment.dart new file mode 100644 index 00000000..b15a8963 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/webview_environment/webview_environment.dart @@ -0,0 +1,102 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [WindowsWebViewEnvironment]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +@immutable +class WindowsWebViewEnvironmentCreationParams extends PlatformWebViewEnvironmentCreationParams { + /// Creates a new [WindowsInAppWebViewControllerCreationParams] instance. + const WindowsWebViewEnvironmentCreationParams({super.settings}); + + /// Creates a [WindowsInAppWebViewControllerCreationParams] instance based on [PlatformInAppWebViewControllerCreationParams]. + factory WindowsWebViewEnvironmentCreationParams.fromPlatformWebViewEnvironmentCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebViewEnvironmentCreationParams params) { + return WindowsWebViewEnvironmentCreationParams( + settings: params.settings + ); + } +} + +///Controls a WebView Environment used by WebView instances. +/// +///**Officially Supported Platforms/Implementations**: +///- Windows +class WindowsWebViewEnvironment extends PlatformWebViewEnvironment + with ChannelController { + static final MethodChannel _staticChannel = MethodChannel('com.pichillilorenzo/flutter_webview_environment'); + + @override + final String id = IdGenerator.generate(); + + WindowsWebViewEnvironment( + PlatformWebViewEnvironmentCreationParams params) + : super.implementation(params is WindowsWebViewEnvironmentCreationParams + ? params + : WindowsWebViewEnvironmentCreationParams + .fromPlatformWebViewEnvironmentCreationParams(params)); + + static final WindowsWebViewEnvironment _staticValue = + WindowsWebViewEnvironment( + WindowsWebViewEnvironmentCreationParams()); + + factory WindowsWebViewEnvironment.static() { + return _staticValue; + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + id: id, + debugLoggingSettings: + PlatformWebViewEnvironment.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + if (PlatformWebViewEnvironment.debugLoggingSettings.enabled) { + _debugLog(call.method, call.arguments); + } + + switch (call.method) { + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + Future create( + {WebViewEnvironmentSettings? settings}) async { + final env = WindowsWebViewEnvironment( + WindowsWebViewEnvironmentCreationParams(settings: settings) + ); + + Map args = {}; + args.putIfAbsent('id', () => env.id); + args.putIfAbsent('settings', () => env.settings?.toMap()); + await _staticChannel.invokeMethod( + 'create', args); + + env.channel = MethodChannel('com.pichillilorenzo/flutter_webview_environment_$id'); + env.handler = env.handleMethod; + env.initMethodCallHandler(); + return env; + } + + @override + Future dispose() async { + Map args = {}; + await channel?.invokeMethod('dispose', args); + disposeChannel(); + } +} + +extension InternalWindowsWebViewEnvironment on WindowsWebViewEnvironment { + get handleMethod => _handleMethod; +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/pubspec.yaml b/flutter_inappwebview_windows/pubspec.yaml index 354848d1..6741c0bb 100644 --- a/flutter_inappwebview_windows/pubspec.yaml +++ b/flutter_inappwebview_windows/pubspec.yaml @@ -18,7 +18,8 @@ environment: dependencies: flutter: sdk: flutter - flutter_inappwebview_platform_interface: ^1.0.10 + flutter_inappwebview_platform_interface: #^1.0.10 + path: ../flutter_inappwebview_platform_interface dev_dependencies: flutter_test: diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt index 2d8801db..aaac0851 100644 --- a/flutter_inappwebview_windows/windows/CMakeLists.txt +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -72,6 +72,10 @@ list(APPEND PLUGIN_SOURCES "types/plugin_script.h" "types/size_2d.cpp" "types/size_2d.h" + "types/rect.cpp" + "types/rect.h" + "types/screenshot_configuration.cpp" + "types/screenshot_configuration.h" "custom_platform_view/custom_platform_view.cc" "custom_platform_view/custom_platform_view.h" "custom_platform_view/texture_bridge.cc" @@ -88,6 +92,14 @@ list(APPEND PLUGIN_SOURCES "plugin_scripts_js/plugin_scripts_util.h" "plugin_scripts_js/javascript_bridge_js.cpp" "plugin_scripts_js/javascript_bridge_js.h" + "webview_environment/webview_environment_settings.cpp" + "webview_environment/webview_environment_settings.h" + "webview_environment/webview_environment.cpp" + "webview_environment/webview_environment.h" + "webview_environment/webview_environment_manager.cpp" + "webview_environment/webview_environment_manager.h" + "webview_environment/webview_environment_channel_delegate.cpp" + "webview_environment/webview_environment_channel_delegate.h" "in_app_webview/user_content_controller.cpp" "in_app_webview/user_content_controller.h" "in_app_webview/in_app_webview_settings.cpp" @@ -112,6 +124,8 @@ list(APPEND PLUGIN_SOURCES "in_app_browser/in_app_browser.h" "in_app_browser/in_app_browser_channel_delegate.cpp" "in_app_browser/in_app_browser_channel_delegate.h" + "cookie_manager.cpp" + "cookie_manager.h" ) # Define the plugin library target. Its name must not be changed (see comment diff --git a/flutter_inappwebview_windows/windows/cookie_manager.cpp b/flutter_inappwebview_windows/windows/cookie_manager.cpp new file mode 100644 index 00000000..d962755d --- /dev/null +++ b/flutter_inappwebview_windows/windows/cookie_manager.cpp @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include + +#include "cookie_manager.h" +#include "in_app_webview/in_app_webview.h" +#include "utils/log.h" + +namespace flutter_inappwebview_plugin +{ + using namespace Microsoft::WRL; + + CookieManager::CookieManager(const FlutterInappwebviewWindowsPlugin* plugin) + : ChannelDelegate(plugin->registrar->messenger(), CookieManager::METHOD_CHANNEL_NAME_PREFIX) + { + windowClass_.lpszClassName = CookieManager::CLASS_NAME; + windowClass_.lpfnWndProc = &DefWindowProc; + + RegisterClass(&windowClass_); + + auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, 0, + 0, 0, 0, + plugin->registrar->GetView()->GetNativeWindow(), + nullptr, + windowClass_.hInstance, nullptr); + + CreateInAppWebViewEnvParams webViewEnvParams = { + hwnd, + false + }; + + InAppWebView::createInAppWebViewEnv(webViewEnvParams, nullptr, + [=](wil::com_ptr webViewEnv, + wil::com_ptr webViewController, + wil::com_ptr webViewCompositionController) + { + if (webViewEnv && webViewController) { + webViewEnv_ = std::move(webViewEnv); + webViewController_ = std::move(webViewController); + webViewController_->get_CoreWebView2(&webView_); + webViewController_->put_IsVisible(false); + } + }); + } + + void CookieManager::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + auto& arguments = std::get(*method_call.arguments()); + auto& methodName = method_call.method_name(); + + if (string_equals(methodName, "setCookie")) { + if (!webView_) { + result->Success(false); + return; + } + + auto url = get_fl_map_value(arguments, "url"); + auto name = get_fl_map_value(arguments, "name"); + auto value = get_fl_map_value(arguments, "value"); + auto path = get_fl_map_value(arguments, "path"); + auto domain = get_optional_fl_map_value(arguments, "domain"); + auto expiresDate = get_optional_fl_map_value(arguments, "expiresDate"); + auto maxAge = get_optional_fl_map_value(arguments, "maxAge"); + auto isSecure = get_optional_fl_map_value(arguments, "isSecure"); + auto isHttpOnly = get_optional_fl_map_value(arguments, "isHttpOnly"); + auto sameSite = get_optional_fl_map_value(arguments, "sameSite"); + + nlohmann::json parameters = { + {"url", url}, + {"name", name}, + {"value", value}, + {"path", path} + }; + if (domain.has_value()) { + parameters["domain"] = domain.value(); + } + if (expiresDate.has_value()) { + parameters["expires"] = expiresDate.value() / 1000; + } + debugLog(maxAge.value()); + if (maxAge.has_value()) { + // time(NULL) represents the current unix timestamp in seconds + parameters["expires"] = time(NULL) + maxAge.value(); + } + if (isSecure.has_value()) { + parameters["secure"] = isSecure.value(); + } + if (isHttpOnly.has_value()) { + parameters["httpOnly"] = isHttpOnly.value(); + } + if (sameSite.has_value()) { + parameters["sameSite"] = sameSite.value(); + } + + auto result_ = std::shared_ptr>(std::move(result)); + auto hr = webView_->CallDevToolsProtocolMethod(L"Network.setCookie", utf8_to_wide(parameters.dump()).c_str(), Callback( + [this, result_](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + result_->Success(succeededOrLog(errorCode)); + return S_OK; + } + ).Get()); + + if (failedAndLog(hr)) { + result_->Success(false); + } + } + else { + result->NotImplemented(); + } + } + + CookieManager::~CookieManager() + { + debugLog("dealloc CookieManager"); + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/cookie_manager.h b/flutter_inappwebview_windows/windows/cookie_manager.h new file mode 100644 index 00000000..6d3d3daa --- /dev/null +++ b/flutter_inappwebview_windows/windows/cookie_manager.h @@ -0,0 +1,36 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_COOKIE_MANAGER_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_COOKIE_MANAGER_H_ + +#include +#include +#include +#include + +#include "flutter_inappwebview_windows_plugin.h" +#include "types/channel_delegate.h" + +namespace flutter_inappwebview_plugin +{ + class CookieManager : public ChannelDelegate + { + public: + static inline const wchar_t* CLASS_NAME = L"CookieManager"; + static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_cookiemanager"; + + const FlutterInappwebviewWindowsPlugin* plugin; + + CookieManager(const FlutterInappwebviewWindowsPlugin* plugin); + ~CookieManager(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + private: + wil::com_ptr webViewEnv_; + wil::com_ptr webViewController_; + wil::com_ptr webView_; + WNDCLASS windowClass_ = {}; + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_COOKIE_MANAGER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp index 57355649..a616a89b 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp @@ -2,9 +2,11 @@ #include +#include "cookie_manager.h" #include "headless_in_app_webview/headless_in_app_webview_manager.h" #include "in_app_browser/in_app_browser_manager.h" #include "in_app_webview/in_app_webview_manager.h" +#include "webview_environment/webview_environment_manager.h" #pragma comment(lib, "Shlwapi.lib") #pragma comment(lib, "dxgi.lib") @@ -23,9 +25,11 @@ namespace flutter_inappwebview_plugin FlutterInappwebviewWindowsPlugin::FlutterInappwebviewWindowsPlugin(flutter::PluginRegistrarWindows* registrar) : registrar(registrar) { + webViewEnvironmentManager = std::make_unique(this); inAppWebViewManager = std::make_unique(this); inAppBrowserManager = std::make_unique(this); headlessInAppWebViewManager = std::make_unique(this); + cookieManager = std::make_unique(this); } FlutterInappwebviewWindowsPlugin::~FlutterInappwebviewWindowsPlugin() diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h index 64566fa0..9d0bd403 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h @@ -6,16 +6,20 @@ namespace flutter_inappwebview_plugin { + class WebViewEnvironmentManager; class InAppWebViewManager; class InAppBrowserManager; class HeadlessInAppWebViewManager; + class CookieManager; class FlutterInappwebviewWindowsPlugin : public flutter::Plugin { public: flutter::PluginRegistrarWindows* registrar; + std::unique_ptr webViewEnvironmentManager; std::unique_ptr inAppWebViewManager; std::unique_ptr inAppBrowserManager; std::unique_ptr headlessInAppWebViewManager; + std::unique_ptr cookieManager; static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); diff --git a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.cpp b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.cpp index bf1db080..fc2ff2c1 100644 --- a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.cpp @@ -19,7 +19,6 @@ namespace flutter_inappwebview_plugin if (!webView) { return; } - webView->webViewController->put_IsVisible(false); } void HeadlessInAppWebView::setSize(const std::shared_ptr size) const @@ -48,12 +47,13 @@ namespace flutter_inappwebview_plugin HeadlessInAppWebView::~HeadlessInAppWebView() { debugLog("dealloc HeadlessInAppWebView"); + HWND parentWindow = nullptr; if (webView && webView->webViewController) { - HWND parentWindow; - if (succeededOrLog(webView->webViewController->get_ParentWindow(&parentWindow))) { - DestroyWindow(parentWindow); - } + webView->webViewController->get_ParentWindow(&parentWindow); } webView = nullptr; + if (parentWindow) { + DestroyWindow(parentWindow); + } } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp index a8803bae..9c6e3d9e 100644 --- a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp +++ b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp @@ -10,8 +10,10 @@ #include "../types/user_script.h" #include "../utils/flutter.h" #include "../utils/log.h" +#include "../utils/map.h" #include "../utils/string.h" #include "../utils/vector.h" +#include "../webview_environment/webview_environment_manager.h" #include "headless_in_app_webview_manager.h" namespace flutter_inappwebview_plugin @@ -54,6 +56,7 @@ namespace flutter_inappwebview_plugin auto initialFile = get_optional_fl_map_value(params, "initialFile"); auto initialDataMap = get_optional_fl_map_value(params, "initialData"); auto initialUserScriptList = get_optional_fl_map_value(params, "initialUserScripts"); + auto webViewEnvironmentId = get_optional_fl_map_value(*arguments, "webViewEnvironmentId"); RECT bounds; GetClientRect(plugin->registrar->GetView()->GetNativeWindow(), &bounds); @@ -67,7 +70,15 @@ namespace flutter_inappwebview_plugin nullptr, windowClass_.hInstance, nullptr); - InAppWebView::createInAppWebViewEnv(hwnd, false, + CreateInAppWebViewEnvParams webViewEnvParams = { + hwnd, + false + }; + + auto webViewEnvironment = webViewEnvironmentId.has_value() && map_contains(plugin->webViewEnvironmentManager->webViewEnvironments, webViewEnvironmentId.value()) + ? plugin->webViewEnvironmentManager->webViewEnvironments.at(webViewEnvironmentId.value()).get() : nullptr; + + InAppWebView::createInAppWebViewEnv(webViewEnvParams, webViewEnvironment, [=](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index a7ab72e9..baa749a5 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -2,6 +2,7 @@ #include "../utils/log.h" #include "../utils/strconv.h" +#include "../webview_environment/webview_environment_manager.h" #include "in_app_browser.h" #include "in_app_browser_manager.h" @@ -41,13 +42,22 @@ namespace flutter_inappwebview_plugin ShowWindow(m_hWnd, settings->hidden ? SW_HIDE : SW_SHOW); + CreateInAppWebViewEnvParams webViewEnvParams = { + m_hWnd, + false + }; + + InAppWebViewCreationParams webViewParams = { id, params.initialWebViewSettings, params.initialUserScripts }; - InAppWebView::createInAppWebViewEnv(m_hWnd, false, + auto webViewEnvironment = params.webViewEnvironmentId.has_value() && map_contains(plugin->webViewEnvironmentManager->webViewEnvironments, params.webViewEnvironmentId.value()) + ? plugin->webViewEnvironmentManager->webViewEnvironments.at(params.webViewEnvironmentId.value()).get() : nullptr; + + InAppWebView::createInAppWebViewEnv(webViewEnvParams, webViewEnvironment, [this, params, webViewParams](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) -> void { if (webViewEnv && webViewController) { @@ -83,13 +93,11 @@ namespace flutter_inappwebview_plugin void InAppBrowser::show() const { ShowWindow(m_hWnd, SW_SHOW); - webView->webViewController->put_IsVisible(true); } void InAppBrowser::hide() const { ShowWindow(m_hWnd, SW_HIDE); - webView->webViewController->put_IsVisible(false); } bool InAppBrowser::isHidden() const diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h index 9d419746..f0a0d677 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h @@ -24,6 +24,7 @@ namespace flutter_inappwebview_plugin const std::shared_ptr initialSettings; const std::shared_ptr initialWebViewSettings; const std::optional>> initialUserScripts; + const std::optional webViewEnvironmentId; }; class InAppBrowser { diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp index 96bd4fb8..b414c806 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -53,6 +53,7 @@ namespace flutter_inappwebview_plugin auto assetFilePath = get_optional_fl_map_value(*arguments, "assetFilePath"); auto data = get_optional_fl_map_value(*arguments, "data"); auto initialUserScriptList = get_optional_fl_map_value(*arguments, "initialUserScripts"); + auto webViewEnvironmentId = get_optional_fl_map_value(*arguments, "webViewEnvironmentId"); std::optional> urlRequest = urlRequestMap.has_value() ? std::make_shared(urlRequestMap.value()) : std::optional>{}; @@ -70,7 +71,8 @@ namespace flutter_inappwebview_plugin data, std::move(initialSettings), std::move(initialWebViewSettings), - initialUserScripts + initialUserScripts, + webViewEnvironmentId }; auto inAppBrowser = std::make_unique(plugin, params); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index 8f1f9658..fc69518a 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -15,6 +15,7 @@ #include "../utils/string.h" #include "in_app_webview.h" +#include ".plugin_symlinks/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h" #include "in_app_webview_manager.h" namespace flutter_inappwebview_plugin @@ -39,6 +40,7 @@ namespace flutter_inappwebview_plugin registerSurfaceEventHandlers(); } else { + this->webViewController->put_IsVisible(true); // Resize WebView to fit the bounds of the parent window RECT bounds; GetClientRect(parentWindow, &bounds); @@ -56,58 +58,70 @@ namespace flutter_inappwebview_plugin this->inAppBrowser = inAppBrowser; } - void InAppWebView::createInAppWebViewEnv(const HWND parentWindow, const bool willBeSurface, std::function webViewEnv, + void InAppWebView::createInAppWebViewEnv(const CreateInAppWebViewEnvParams& params, const WebViewEnvironment* webViewEnvironment, std::function webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController)> completionHandler) { - failedLog(CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, - Callback( - [parentWindow, completionHandler, willBeSurface](HRESULT result, wil::com_ptr env) -> HRESULT - { - if (failedAndLog(result) || !env) { - completionHandler(nullptr, nullptr, nullptr); - return E_FAIL; - } + auto callback = [params, completionHandler](HRESULT result, wil::com_ptr env) -> HRESULT + { + if (failedAndLog(result) || !env) { + completionHandler(nullptr, nullptr, nullptr); + return E_FAIL; + } - wil::com_ptr webViewEnv3; - if (willBeSurface && succeededOrLog(env->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) { - failedLog(webViewEnv3->CreateCoreWebView2CompositionController(parentWindow, Callback( - [completionHandler, env](HRESULT result, wil::com_ptr compositionController) -> HRESULT - { - wil::com_ptr webViewController = compositionController.try_query(); + wil::com_ptr webViewEnv3; + if (params.willBeSurface && succeededOrLog(env->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) { + failedLog(webViewEnv3->CreateCoreWebView2CompositionController(params.parentWindow, Callback( + [completionHandler, env](HRESULT result, wil::com_ptr compositionController) -> HRESULT + { + wil::com_ptr webViewController = compositionController.try_query(); - if (failedAndLog(result) || !webViewController) { - completionHandler(nullptr, nullptr, nullptr); - return E_FAIL; - } - - ICoreWebView2Controller3* webViewController3; - if (succeededOrLog(webViewController->QueryInterface(IID_PPV_ARGS(&webViewController3)))) { - webViewController3->put_BoundsMode(COREWEBVIEW2_BOUNDS_MODE_USE_RAW_PIXELS); - webViewController3->put_ShouldDetectMonitorScaleChanges(FALSE); - webViewController3->put_RasterizationScale(1.0); - } - - completionHandler(std::move(env), std::move(webViewController), std::move(compositionController)); - return S_OK; + if (failedAndLog(result) || !webViewController) { + completionHandler(nullptr, nullptr, nullptr); + return E_FAIL; } - ).Get())); - } - else { - failedLog(env->CreateCoreWebView2Controller(parentWindow, Callback( - [completionHandler, env](HRESULT result, wil::com_ptr controller) -> HRESULT - { - if (failedAndLog(result) || !controller) { - completionHandler(nullptr, nullptr, nullptr); - return E_FAIL; - } - completionHandler(std::move(env), std::move(controller), nullptr); - return S_OK; - }).Get())); - } - return S_OK; - }).Get())); + ICoreWebView2Controller3* webViewController3; + if (succeededOrLog(webViewController->QueryInterface(IID_PPV_ARGS(&webViewController3)))) { + webViewController3->put_BoundsMode(COREWEBVIEW2_BOUNDS_MODE_USE_RAW_PIXELS); + webViewController3->put_ShouldDetectMonitorScaleChanges(FALSE); + webViewController3->put_RasterizationScale(1.0); + } + + completionHandler(std::move(env), std::move(webViewController), std::move(compositionController)); + return S_OK; + } + ).Get())); + } + else { + failedLog(env->CreateCoreWebView2Controller(params.parentWindow, Callback( + [completionHandler, env](HRESULT result, wil::com_ptr controller) -> HRESULT + { + if (failedAndLog(result) || !controller) { + completionHandler(nullptr, nullptr, nullptr); + return E_FAIL; + } + + completionHandler(std::move(env), std::move(controller), nullptr); + return S_OK; + }).Get())); + } + return S_OK; + }; + + HRESULT hr; + if (webViewEnvironment && webViewEnvironment->env) { + hr = callback(S_OK, webViewEnvironment->env); + } + else { + hr = CreateCoreWebView2EnvironmentWithOptions( + nullptr, nullptr, nullptr, + Callback(callback).Get()); + } + + if (failedAndLog(hr)) { + completionHandler(nullptr, nullptr, nullptr); + } } void InAppWebView::initChannel(const std::optional> viewId, const std::optional channelName) @@ -168,10 +182,11 @@ namespace flutter_inappwebview_plugin } ).Get())); - userContentController->addPluginScript(std::move(createJavaScriptBridgePluginScript())); - - if (params.initialUserScripts.has_value()) { - userContentController->addUserOnlyScripts(params.initialUserScripts.value()); + if (userContentController) { + userContentController->addPluginScript(std::move(createJavaScriptBridgePluginScript())); + if (params.initialUserScripts.has_value()) { + userContentController->addUserOnlyScripts(params.initialUserScripts.value()); + } } registerEventHandlers(); @@ -392,18 +407,6 @@ namespace flutter_inappwebview_plugin return S_OK; } - /* - wil::unique_cotaskmem_string url; - args->get_Source(&url); - - wil::com_ptr uri; - failedLog(CreateUri( - url.get(), // NULL terminated URI - Uri_CREATE_ALLOW_RELATIVE, // Flags to control behavior - 0, // Reserved must be 0 - &uri)); - */ - wil::unique_cotaskmem_string json; if (succeededOrLog(args->get_WebMessageAsJson(&json))) { auto message = nlohmann::json::parse(wide_to_utf8(json.get())); @@ -650,7 +653,7 @@ namespace flutter_inappwebview_plugin return webView && succeededOrLog(webView->get_CanGoForward(&canGoForward_)) ? canGoForward_ : false; } - void InAppWebView::goBackOrForward(const int& steps) + void InAppWebView::goBackOrForward(const int64_t& steps) { getCopyBackForwardList( [this, steps](std::unique_ptr webHistory) @@ -682,7 +685,7 @@ namespace flutter_inappwebview_plugin ); } - void InAppWebView::canGoBackOrForward(const int& steps, std::function completionHandler) const + void InAppWebView::canGoBackOrForward(const int64_t& steps, std::function completionHandler) const { getCopyBackForwardList( [steps, completionHandler](std::unique_ptr webHistory) @@ -920,6 +923,55 @@ namespace flutter_inappwebview_plugin userContentController->removeAllUserOnlyScripts(); } + void InAppWebView::takeScreenshot(const std::optional> screenshotConfiguration, const std::function)> completionHandler) const + { + if (!webView) { + if (completionHandler) { + completionHandler(std::nullopt); + } + return; + } + + nlohmann::json parameters = { + {"captureBeyondViewport", true} + }; + if (screenshotConfiguration.has_value()) { + auto& scp = screenshotConfiguration.value(); + parameters["format"] = to_lowercase_copy(CompressFormatToString(scp->compressFormat)); + if (scp->compressFormat == CompressFormat::jpeg) { + parameters["quality"] = scp->quality; + } + if (scp->rect.has_value()) { + auto& rect = scp->rect.value(); + parameters["clip"] = { + {"x", rect->x}, + {"y", rect->y}, + {"width", rect->width}, + {"height", rect->height}, + {"scale", scaleFactor_} + }; + } + } + + auto hr = webView->CallDevToolsProtocolMethod(L"Page.captureScreenshot", utf8_to_wide(parameters.dump()).c_str(), Callback( + [this, completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + std::optional result = std::nullopt; + if (succeededOrLog(errorCode)) { + nlohmann::json json = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson)); + result = json["data"].get(); + } + if (completionHandler) { + completionHandler(result); + } + return S_OK; + } + ).Get()); + if (failedAndLog(hr) && completionHandler) { + completionHandler(std::nullopt); + } + } + // flutter_view void InAppWebView::setSurfaceSize(size_t width, size_t height, float scale_factor) { @@ -1187,15 +1239,16 @@ namespace flutter_inappwebview_plugin InAppWebView::~InAppWebView() { debugLog("dealloc InAppWebView"); - HWND parentWindow = nullptr; - if (webViewCompositionController && succeededOrLog(webViewController->get_ParentWindow(&parentWindow))) { - // if it's an InAppWebView, - // then destroy the Window created with it - DestroyWindow(parentWindow); - } + userContentController = nullptr; if (webView) { failedLog(webView->Stop()); } + HWND parentWindow = nullptr; + if (webViewCompositionController && webViewController && succeededOrLog(webViewController->get_ParentWindow(&parentWindow))) { + // if it's an InAppWebView (so webViewCompositionController will be not a nullptr!), + // then destroy the Window created with it + DestroyWindow(parentWindow); + } if (webViewController) { failedLog(webViewController->Close()); } @@ -1203,4 +1256,4 @@ namespace flutter_inappwebview_plugin inAppBrowser = nullptr; plugin = nullptr; } -} \ No newline at end of file +} diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index b5915c2f..ddf21439 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -14,6 +14,8 @@ #include "../types/navigation_action.h" #include "../types/url_request.h" #include "../types/web_history.h" +#include "../webview_environment/webview_environment.h" +#include ".plugin_symlinks/flutter_inappwebview_windows/windows/types/screenshot_configuration.h" #include "in_app_webview_settings.h" #include "user_content_controller.h" #include "webview_channel_delegate.h" @@ -81,6 +83,11 @@ namespace flutter_inappwebview_plugin const std::optional>> initialUserScripts; }; + struct CreateInAppWebViewEnvParams { + const HWND parentWindow; + const bool willBeSurface; + }; + class InAppWebView { public: @@ -107,7 +114,7 @@ namespace flutter_inappwebview_plugin wil::com_ptr webViewCompositionController); ~InAppWebView(); - static void createInAppWebViewEnv(const HWND parentWindow, const bool willBeSurface, std::function webViewEnv, + static void createInAppWebViewEnv(const CreateInAppWebViewEnvParams& params, const WebViewEnvironment* webViewEnvironment, std::function webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController)> completionHandler); @@ -147,8 +154,8 @@ namespace flutter_inappwebview_plugin bool canGoBack() const; void goForward(); bool canGoForward() const; - void goBackOrForward(const int& steps); - void canGoBackOrForward(const int& steps, std::function completionHandler) const; + void goBackOrForward(const int64_t& steps); + void canGoBackOrForward(const int64_t& steps, std::function completionHandler) const; bool isLoading() const { return isLoading_; @@ -161,6 +168,7 @@ namespace flutter_inappwebview_plugin void removeUserScript(const int64_t index, const std::shared_ptr userScript) const; void removeUserScriptsByGroupName(const std::string& groupName) const; void removeAllUserScripts() const; + void takeScreenshot(const std::optional> screenshotConfiguration, const std::function)> completionHandler) const; std::string pageFrameId() const { diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp index 41e3e965..6f4296dc 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp @@ -13,6 +13,8 @@ #include "../utils/vector.h" #include "in_app_webview_manager.h" +#include ".plugin_symlinks/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h" + namespace flutter_inappwebview_plugin { InAppWebViewManager::InAppWebViewManager(const FlutterInappwebviewWindowsPlugin* plugin) @@ -83,6 +85,7 @@ namespace flutter_inappwebview_plugin auto initialFile = get_optional_fl_map_value(*arguments, "initialFile"); auto initialDataMap = get_optional_fl_map_value(*arguments, "initialData"); auto initialUserScriptList = get_optional_fl_map_value(*arguments, "initialUserScripts"); + auto webViewEnvironmentId = get_optional_fl_map_value(*arguments, "webViewEnvironmentId"); RECT bounds; GetClientRect(plugin->registrar->GetView()->GetNativeWindow(), &bounds); @@ -93,7 +96,15 @@ namespace flutter_inappwebview_plugin nullptr, windowClass_.hInstance, nullptr); - InAppWebView::createInAppWebViewEnv(hwnd, true, + CreateInAppWebViewEnvParams webViewEnvParams = { + hwnd, + true + }; + + auto webViewEnvironment = webViewEnvironmentId.has_value() && map_contains(plugin->webViewEnvironmentManager->webViewEnvironments, webViewEnvironmentId.value()) + ? plugin->webViewEnvironmentManager->webViewEnvironments.at(webViewEnvironmentId.value()).get() : nullptr; + + InAppWebView::createInAppWebViewEnv(webViewEnvParams, webViewEnvironment, [=](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) @@ -182,4 +193,4 @@ namespace flutter_inappwebview_plugin UnregisterClass(windowClass_.lpszClassName, nullptr); plugin = nullptr; } -} \ No newline at end of file +} diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index a2648dc7..7f4d4ab3 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -159,6 +159,16 @@ namespace flutter_inappwebview_plugin webView->removeAllUserScripts(); result->Success(true); } + else if (string_equals(methodName, "takeScreenshot")) { + auto result_ = std::shared_ptr>(std::move(result)); + auto screenshotConfigurationMap = get_optional_fl_map_value(arguments, "screenshotConfiguration"); + std::optional> screenshotConfiguration = + screenshotConfigurationMap.has_value() ? std::make_unique(screenshotConfigurationMap.value()) : std::optional>{}; + webView->takeScreenshot(std::move(screenshotConfiguration), [result_ = std::move(result_)](const std::optional data) + { + result_->Success(make_fl_value(data)); + }); + } // for inAppBrowser else if (webView->inAppBrowser && string_equals(methodName, "show")) { webView->inAppBrowser->show(); diff --git a/flutter_inappwebview_windows/windows/types/rect.cpp b/flutter_inappwebview_windows/windows/types/rect.cpp new file mode 100644 index 00000000..a0a41ab0 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/rect.cpp @@ -0,0 +1,25 @@ +#include "rect.h" + +namespace flutter_inappwebview_plugin +{ + Rect::Rect(const double& x, const double& y, const double& width, const double& height) + : x(x), y(y), width(width), height(height) + {} + + Rect::Rect(const flutter::EncodableMap& map) + : x(get_fl_map_value(map, "x")), + y(get_fl_map_value(map, "y")), + width(get_fl_map_value(map, "width")), + height(get_fl_map_value(map, "height")) + {} + + flutter::EncodableMap Rect::toEncodableMap() const + { + return flutter::EncodableMap{ + {"x", x}, + {"y", y}, + {"width", width}, + {"height", height} + }; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/rect.h b/flutter_inappwebview_windows/windows/types/rect.h new file mode 100644 index 00000000..2472d084 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/rect.h @@ -0,0 +1,27 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_RECT_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_RECT_H_ + +#include +#include + +#include "../utils/flutter.h" + +namespace flutter_inappwebview_plugin +{ + class Rect + { + public: + const double x; + const double y; + const double width; + const double height; + + Rect(const double& x, const double& y, const double& width, const double& height); + Rect(const flutter::EncodableMap& map); + ~Rect() = default; + + flutter::EncodableMap toEncodableMap() const; + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_RECT_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/screenshot_configuration.cpp b/flutter_inappwebview_windows/windows/types/screenshot_configuration.cpp new file mode 100644 index 00000000..5e0f027e --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/screenshot_configuration.cpp @@ -0,0 +1,48 @@ +#include "../utils/flutter.h" +#include "../utils/map.h" +#include "screenshot_configuration.h" + +namespace flutter_inappwebview_plugin +{ + CompressFormat CompressFormatFromString(const std::string& compressFormat) + { + if (string_equals(compressFormat, "PNG")) { + return CompressFormat::png; + } + else if (string_equals(compressFormat, "JPEG")) { + return CompressFormat::jpeg; + } + else if (string_equals(compressFormat, "WEBP")) { + return CompressFormat::webp; + } + return CompressFormat::png; + } + + std::string CompressFormatToString(const CompressFormat& compressFormat) + { + switch (compressFormat) { + case CompressFormat::jpeg: + return "JPEG"; + case CompressFormat::webp: + return "WEBP"; + case CompressFormat::png: + default: + return "PNG"; + } + } + + ScreenshotConfiguration::ScreenshotConfiguration( + const CompressFormat& compressFormat, + const int64_t& quality, + const std::optional> rect + ) : compressFormat(compressFormat), quality(quality), rect(rect) + {} + + ScreenshotConfiguration::ScreenshotConfiguration(const flutter::EncodableMap& map) + : compressFormat(CompressFormatFromString(get_fl_map_value(map, "compressFormat"))), + quality(get_fl_map_value(map, "quality")), + rect(fl_map_contains_not_null(map, "rect") ? std::make_shared(get_fl_map_value(map, "rect")) : std::optional>{}) + {} + + ScreenshotConfiguration::~ScreenshotConfiguration() {} +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/screenshot_configuration.h b/flutter_inappwebview_windows/windows/types/screenshot_configuration.h new file mode 100644 index 00000000..f05ee7f0 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/screenshot_configuration.h @@ -0,0 +1,38 @@ +#ifndef FLUTTER_INAPPWEBVIEW_SCREENSHOT_CONFIGURATION_H_ +#define FLUTTER_INAPPWEBVIEW_SCREENSHOT_CONFIGURATION_H_ + +#include +#include +#include + +#include "../types/rect.h" +#include "../utils/string.h" + +namespace flutter_inappwebview_plugin +{ + enum CompressFormat { + png, + jpeg, + webp + }; + + CompressFormat CompressFormatFromString(const std::string& compressFormat); + std::string CompressFormatToString(const CompressFormat& compressFormat); + + class ScreenshotConfiguration + { + public: + const CompressFormat compressFormat; + const int64_t quality; + const std::optional> rect; + + ScreenshotConfiguration( + const CompressFormat& compressFormat, + const int64_t& quality, + const std::optional> rect + ); + ScreenshotConfiguration(const flutter::EncodableMap& map); + ~ScreenshotConfiguration(); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_SCREENSHOT_CONFIGURATION_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_error.cpp b/flutter_inappwebview_windows/windows/types/web_resource_error.cpp index f3fb3a36..5059a3dc 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_error.cpp +++ b/flutter_inappwebview_windows/windows/types/web_resource_error.cpp @@ -3,7 +3,7 @@ namespace flutter_inappwebview_plugin { - WebResourceError::WebResourceError(const std::string& description, const int type) + WebResourceError::WebResourceError(const std::string& description, const int64_t type) : description(description), type(type) {} diff --git a/flutter_inappwebview_windows/windows/types/web_resource_error.h b/flutter_inappwebview_windows/windows/types/web_resource_error.h index b1d2a55b..f5a0a5aa 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_error.h +++ b/flutter_inappwebview_windows/windows/types/web_resource_error.h @@ -33,9 +33,9 @@ namespace flutter_inappwebview_plugin { public: const std::string description; - const int type; + const int64_t type; - WebResourceError(const std::string& description, const int type); + WebResourceError(const std::string& description, const int64_t type); WebResourceError(const flutter::EncodableMap& map); ~WebResourceError() = default; diff --git a/flutter_inappwebview_windows/windows/types/web_resource_response.cpp b/flutter_inappwebview_windows/windows/types/web_resource_response.cpp index 5f76dc9c..2e84cd9e 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_response.cpp +++ b/flutter_inappwebview_windows/windows/types/web_resource_response.cpp @@ -3,7 +3,7 @@ namespace flutter_inappwebview_plugin { - WebResourceResponse::WebResourceResponse(const std::optional& statusCode) + WebResourceResponse::WebResourceResponse(const std::optional& statusCode) : statusCode(statusCode) {} diff --git a/flutter_inappwebview_windows/windows/types/web_resource_response.h b/flutter_inappwebview_windows/windows/types/web_resource_response.h index 123080aa..c428e0ad 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_response.h +++ b/flutter_inappwebview_windows/windows/types/web_resource_response.h @@ -9,9 +9,9 @@ namespace flutter_inappwebview_plugin class WebResourceResponse { public: - const std::optional statusCode; + const std::optional statusCode; - WebResourceResponse(const std::optional& statusCode); + WebResourceResponse(const std::optional& statusCode); WebResourceResponse(const flutter::EncodableMap& map); ~WebResourceResponse() = default; diff --git a/flutter_inappwebview_windows/windows/utils/flutter.h b/flutter_inappwebview_windows/windows/utils/flutter.h index 6bd271b6..45c1390c 100644 --- a/flutter_inappwebview_windows/windows/utils/flutter.h +++ b/flutter_inappwebview_windows/windows/utils/flutter.h @@ -80,23 +80,65 @@ namespace flutter_inappwebview_plugin return encodableMap; } - template + static inline bool fl_map_contains(const flutter::EncodableMap& map, const char* key) + { + return map_contains(map, make_fl_value(key)); + } + + static inline bool fl_map_contains_not_null(const flutter::EncodableMap& map, const char* key) + { + return fl_map_contains(map, key) && !map.at(make_fl_value(key)).IsNull(); + } + + template::value && !std::is_same::value), bool>::type* = nullptr> static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* key) { return std::get(map.at(make_fl_value(key))); } - template::value && !is_vector::value) || + template::value, bool>::type* = nullptr> + static inline int64_t get_fl_map_value(const flutter::EncodableMap& map, const char* key) + { + return map.at(make_fl_value(key)).LongValue(); + } + + template::value, bool>::type* = nullptr> + static inline int64_t get_fl_map_value(const flutter::EncodableMap& map, const char* key) + { + return map.at(make_fl_value(key)).LongValue(); + } + + template::value && !is_vector::value && !std::is_same::value && !std::is_same::value) || std::is_same::value || std::is_same::value), int>::type* = nullptr> static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* key) { - auto fl_key = make_fl_value(key); - if (map_contains(map, fl_key)) { + if (fl_map_contains_not_null(map, key)) { + auto fl_key = make_fl_value(key); return make_pointer_optional(std::get_if(&map.at(fl_key))); } return std::nullopt; } + template::value, bool>::type* = nullptr> + static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* key) + { + if (fl_map_contains_not_null(map, key)) { + auto fl_key = make_fl_value(key); + return std::make_optional(map.at(fl_key).LongValue()); + } + return std::nullopt; + } + + template::value, bool>::type* = nullptr> + static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* key) + { + if (fl_map_contains_not_null(map, key)) { + auto fl_key = make_fl_value(key); + return std::make_optional(map.at(fl_key).LongValue()); + } + return std::nullopt; + } + template static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* key, const T& defaultValue) { diff --git a/flutter_inappwebview_windows/windows/utils/string.h b/flutter_inappwebview_windows/windows/utils/string.h index 27b64731..ee912db8 100644 --- a/flutter_inappwebview_windows/windows/utils/string.h +++ b/flutter_inappwebview_windows/windows/utils/string.h @@ -1,6 +1,7 @@ #ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_ #define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_ +#include #include #include #include @@ -142,6 +143,39 @@ namespace flutter_inappwebview_plugin res.push_back(s.substr(pos_start)); return res; } + + template + void to_lowercase(const std::basic_string& s) + { + std::transform(s.begin(), s.end(), s.begin(), + [](const T v) { return static_cast(std::tolower(v)); }); + } + + template + std::basic_string to_lowercase_copy(const std::basic_string& s) + { + std::basic_string s2 = s; + std::transform(s2.begin(), s2.end(), s2.begin(), + [](const T v) { return static_cast(std::tolower(v)); }); + return s2; + } + + template + void to_uppercase(const std::basic_string& s) + { + std::transform(s.begin(), s.end(), s.begin(), + [](const T v) { return static_cast(std::toupper(v)); }); + return s2; + } + + template + std::basic_string to_uppercase_copy(const std::basic_string& s) + { + std::basic_string s2 = s; + std::transform(s2.begin(), s2.end(), s2.begin(), + [](const T v) { return static_cast(std::toupper(v)); }); + return s2; + } } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment.cpp b/flutter_inappwebview_windows/windows/webview_environment/webview_environment.cpp new file mode 100644 index 00000000..575f0d33 --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment.cpp @@ -0,0 +1,59 @@ +#include +#include + +#include "../utils/log.h" +#include "webview_environment.h" + +namespace flutter_inappwebview_plugin +{ + using namespace Microsoft::WRL; + + WebViewEnvironment::WebViewEnvironment(const FlutterInappwebviewWindowsPlugin* plugin, const std::string& id) + : plugin(plugin), id(id), + channelDelegate(std::make_unique(this, plugin->registrar->messenger())) + {} + + void WebViewEnvironment::create(const std::unique_ptr settings, const std::function completionHandler) + { + auto options = Make(); + if (settings) { + if (settings->additionalBrowserArguments.has_value()) { + options->put_AdditionalBrowserArguments(utf8_to_wide(settings->additionalBrowserArguments.value()).c_str()); + } + if (settings->allowSingleSignOnUsingOSPrimaryAccount.has_value()) { + options->put_AllowSingleSignOnUsingOSPrimaryAccount(settings->allowSingleSignOnUsingOSPrimaryAccount.value()); + } + if (settings->language.has_value()) { + options->put_Language(utf8_to_wide(settings->language.value()).c_str()); + } + if (settings->targetCompatibleBrowserVersion.has_value()) { + options->put_TargetCompatibleBrowserVersion(utf8_to_wide(settings->targetCompatibleBrowserVersion.value()).c_str()); + } + } + + auto hr = CreateCoreWebView2EnvironmentWithOptions( + settings && settings->browserExecutableFolder.has_value() ? utf8_to_wide(settings->browserExecutableFolder.value()).c_str() : nullptr, + settings && settings->userDataFolder.has_value() ? utf8_to_wide(settings->userDataFolder.value()).c_str() : nullptr, + options.Get(), + Callback( + [this, completionHandler](HRESULT result, wil::com_ptr environment) -> HRESULT + { + if (succeededOrLog(result)) { + env = std::move(environment); + } + if (completionHandler) { + completionHandler(result); + } + return S_OK; + }).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler(hr); + } + } + + WebViewEnvironment::~WebViewEnvironment() + { + debugLog("dealloc WebViewEnvironment"); + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment.h b/flutter_inappwebview_windows/windows/webview_environment/webview_environment.h new file mode 100644 index 00000000..849b48ba --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment.h @@ -0,0 +1,30 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_H_ + +#include +#include +#include + +#include "../flutter_inappwebview_windows_plugin.h" +#include "webview_environment_channel_delegate.h" +#include "webview_environment_settings.h" + +namespace flutter_inappwebview_plugin +{ + class WebViewEnvironment + { + public: + static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_webview_environment_"; + + const FlutterInappwebviewWindowsPlugin* plugin; + std::string id; + wil::com_ptr env; + std::unique_ptr channelDelegate; + + WebViewEnvironment(const FlutterInappwebviewWindowsPlugin* plugin, const std::string& id); + ~WebViewEnvironment(); + + void create(const std::unique_ptr settings, const std::function completionHandler); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.cpp b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.cpp new file mode 100644 index 00000000..d5b9e4d2 --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.cpp @@ -0,0 +1,47 @@ +#include "../utils/flutter.h" +#include "../utils/log.h" +#include "../utils/strconv.h" +#include "../utils/string.h" +#include "webview_environment.h" +#include "webview_environment_channel_delegate.h" + +#include "webview_environment_manager.h" + +namespace flutter_inappwebview_plugin +{ + WebViewEnvironmentChannelDelegate::WebViewEnvironmentChannelDelegate(WebViewEnvironment* webViewEnv, flutter::BinaryMessenger* messenger) + : webViewEnvironment(webViewEnv), ChannelDelegate(messenger, WebViewEnvironment::METHOD_CHANNEL_NAME_PREFIX + webViewEnv->id) + {} + + void WebViewEnvironmentChannelDelegate::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + if (!webViewEnvironment) { + result->Success(); + return; + } + + // auto& arguments = std::get(*method_call.arguments()); + auto& methodName = method_call.method_name(); + + if (string_equals(methodName, "dispose")) { + if (webViewEnvironment->plugin && webViewEnvironment->plugin->webViewEnvironmentManager) { + std::map>& webViewEnvironments = webViewEnvironment->plugin->webViewEnvironmentManager->webViewEnvironments; + auto& id = webViewEnvironment->id; + if (map_contains(webViewEnvironments, id)) { + webViewEnvironments.erase(id); + } + } + result->Success(); + } + else { + result->NotImplemented(); + } + } + + WebViewEnvironmentChannelDelegate::~WebViewEnvironmentChannelDelegate() + { + debugLog("dealloc WebViewEnvironmentChannelDelegate"); + webViewEnvironment = nullptr; + } +} diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.h b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.h new file mode 100644 index 00000000..b98e67ab --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.h @@ -0,0 +1,25 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CHANNEL_DELEGATE_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CHANNEL_DELEGATE_H_ + +#include "../types/channel_delegate.h" +#include + +namespace flutter_inappwebview_plugin +{ + class WebViewEnvironment; + + class WebViewEnvironmentChannelDelegate : public ChannelDelegate + { + public: + WebViewEnvironment* webViewEnvironment; + + WebViewEnvironmentChannelDelegate(WebViewEnvironment* webViewEnv, flutter::BinaryMessenger* messenger); + ~WebViewEnvironmentChannelDelegate(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CHANNEL_DELEGATE_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp new file mode 100644 index 00000000..b0b492b4 --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp @@ -0,0 +1,63 @@ +#include +#include +#include + +#include "../in_app_webview/in_app_webview_settings.h" +#include "../utils/flutter.h" +#include "../utils/log.h" +#include "../utils/string.h" +#include "../utils/vector.h" +#include "webview_environment_manager.h" + +namespace flutter_inappwebview_plugin +{ + WebViewEnvironmentManager::WebViewEnvironmentManager(const FlutterInappwebviewWindowsPlugin* plugin) + : plugin(plugin), + ChannelDelegate(plugin->registrar->messenger(), WebViewEnvironmentManager::METHOD_CHANNEL_NAME) + {} + + void WebViewEnvironmentManager::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + auto* arguments = std::get_if(method_call.arguments()); + auto& methodName = method_call.method_name(); + + if (string_equals(methodName, "create")) { + auto id = get_fl_map_value(*arguments, "id"); + auto settingsMap = get_optional_fl_map_value(*arguments, "settings"); + auto settings = settingsMap.has_value() ? std::make_unique(settingsMap.value()) : nullptr; + createWebViewEnvironment(id, std::move(settings), std::move(result)); + } + else { + result->NotImplemented(); + } + } + + void WebViewEnvironmentManager::createWebViewEnvironment(const std::string& id, std::unique_ptr settings, std::unique_ptr> result) + { + auto result_ = std::shared_ptr>(std::move(result)); + + auto webViewEnvironment = std::make_unique(plugin, id); + webViewEnvironment->create(std::move(settings), + [this, id, result_](HRESULT errorCode) + { + if (succeededOrLog(errorCode)) { + result_->Success(true); + } + else { + result_->Error("0", "Cannot create WebViewEnvironment: " + getHRMessage(errorCode)); + if (map_contains(webViewEnvironments, id)) { + webViewEnvironments.erase(id); + } + } + }); + webViewEnvironments.insert({ id, std::move(webViewEnvironment) }); + } + + WebViewEnvironmentManager::~WebViewEnvironmentManager() + { + debugLog("dealloc WebViewEnvironmentManager"); + webViewEnvironments.clear(); + plugin = nullptr; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h new file mode 100644 index 00000000..59c25b50 --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h @@ -0,0 +1,33 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_MANAGER_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_MANAGER_H_ + +#include +#include +#include +#include + +#include "../flutter_inappwebview_windows_plugin.h" +#include "../types/channel_delegate.h" +#include "webview_environment.h" + +namespace flutter_inappwebview_plugin +{ + class WebViewEnvironmentManager : public ChannelDelegate + { + public: + static inline const std::string METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_webview_environment"; + + const FlutterInappwebviewWindowsPlugin* plugin; + std::map> webViewEnvironments; + + WebViewEnvironmentManager(const FlutterInappwebviewWindowsPlugin* plugin); + ~WebViewEnvironmentManager(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + void createWebViewEnvironment(const std::string& id, std::unique_ptr settings, std::unique_ptr> result); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_MANAGER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.cpp b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.cpp new file mode 100644 index 00000000..24091a7d --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.cpp @@ -0,0 +1,27 @@ +#include "../utils/flutter.h" +#include "webview_environment_settings.h" + +namespace flutter_inappwebview_plugin +{ + WebViewEnvironmentSettings::WebViewEnvironmentSettings(const flutter::EncodableMap& map) + : browserExecutableFolder(get_optional_fl_map_value(map, "browserExecutableFolder")), + userDataFolder(get_optional_fl_map_value(map, "userDataFolder")), + additionalBrowserArguments(get_optional_fl_map_value(map, "additionalBrowserArguments")), + allowSingleSignOnUsingOSPrimaryAccount(get_optional_fl_map_value(map, "allowSingleSignOnUsingOSPrimaryAccount")), + language(get_optional_fl_map_value(map, "language")), + targetCompatibleBrowserVersion(get_optional_fl_map_value(map, "targetCompatibleBrowserVersion")) + {} + + flutter::EncodableMap WebViewEnvironmentSettings::toEncodableMap() const + { + return flutter::EncodableMap{ + {"browserExecutableFolder", make_fl_value(browserExecutableFolder)}, + {"userDataFolder", make_fl_value(userDataFolder)}, + {"additionalBrowserArguments", make_fl_value(additionalBrowserArguments)}, + {"allowSingleSignOnUsingOSPrimaryAccount", make_fl_value(allowSingleSignOnUsingOSPrimaryAccount)}, + {"language", make_fl_value(language)}, + {"targetCompatibleBrowserVersion", make_fl_value(targetCompatibleBrowserVersion)} + }; + } + +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.h b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.h new file mode 100644 index 00000000..12d1388c --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.h @@ -0,0 +1,29 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CREATION_PARAMS_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CREATION_PARAMS_H_ + +#include +#include +#include + +#include "../flutter_inappwebview_windows_plugin.h" + +namespace flutter_inappwebview_plugin +{ + class WebViewEnvironmentSettings + { + public: + const std::optional browserExecutableFolder; + const std::optional userDataFolder; + const std::optional additionalBrowserArguments; + const std::optional allowSingleSignOnUsingOSPrimaryAccount; + const std::optional language; + const std::optional targetCompatibleBrowserVersion; + + WebViewEnvironmentSettings() = default; + WebViewEnvironmentSettings(const flutter::EncodableMap& map); + ~WebViewEnvironmentSettings() = default; + + flutter::EncodableMap toEncodableMap() const; + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CREATION_PARAMS_H_ \ No newline at end of file