windows: fixed dealloc webviews logic, implemented takeScreenshot, added WebViewEnvironment and WebViewEnvironmentSettings classes

This commit is contained in:
unknown 2024-01-27 18:41:10 +01:00
parent 7721deb062
commit ed97928331
69 changed files with 1547 additions and 448 deletions

View File

@ -140,6 +140,7 @@ class ExchangeableEnumGenerator
<DartObject>[];
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);");

View File

@ -115,6 +115,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
children: [
InAppWebView(
key: webViewKey,
webViewEnvironment: webViewEnvironment,
initialUrlRequest:
URLRequest(url: WebUri('https://flutter.dev')),
// initialUrlRequest:

View File

@ -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);
}

View File

@ -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"))
}

View File

@ -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

View File

@ -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,

View File

@ -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';

View File

@ -0,0 +1 @@
export 'webview_environment.dart';

View File

@ -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<WebViewEnvironment> create(
{WebViewEnvironmentSettings? settings}) async {
return WebViewEnvironment.fromPlatform(platform: await PlatformWebViewEnvironment.static().create(settings: settings));
}
///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.dispose}
Future<void> dispose() => platform.dispose();
}

View File

@ -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:

View File

@ -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<Uint8List?> takeScreenshot(
{ScreenshotConfiguration? screenshotConfiguration}) {

View File

@ -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.

View File

@ -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.');
}
}

View File

@ -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';

View File

@ -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");
}

View File

@ -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');

View File

@ -12,6 +12,12 @@ class ScreenshotConfiguration_ {
///The portion of your web view to capture, specified as a rectangle in the views coordinate system.
///The default value of this property is `null`, which captures everything in the views 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 views 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()

View File

@ -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 views 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 views coordinate system.
///The default value of this property is `null`, which captures everything in the views 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,

View File

@ -0,0 +1,2 @@
export 'platform_webview_environment.dart';
export 'webview_environment_settings.dart' show WebViewEnvironmentSettings;

View File

@ -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<PlatformWebViewEnvironment> 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<void> dispose() {
throw UnimplementedError(
'dispose is not implemented on the current platform');
}
}

View File

@ -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
});
}

View File

@ -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<String, dynamic>? 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<String, dynamic> toMap() {
return {
"additionalBrowserArguments": additionalBrowserArguments,
"allowSingleSignOnUsingOSPrimaryAccount":
allowSingleSignOnUsingOSPrimaryAccount,
"browserExecutableFolder": browserExecutableFolder,
"language": language,
"targetCompatibleBrowserVersion": targetCompatibleBrowserVersion,
"userDataFolder": userDataFolder,
};
}
///Converts instance to a map.
Map<String, dynamic> 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}';
}
}

View File

@ -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

View File

@ -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"

View File

@ -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<bool> 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<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url.toString());
args.putIfAbsent('name', () => name);
@ -113,72 +94,14 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController {
return await channel?.invokeMethod<bool>('setCookie', args) ?? false;
}
Future<void> _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<void>();
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<List<Cookie>> 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<Cookie> cookies = [];
Map<String, dynamic> args = <String, dynamic>{};
@ -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<List<Cookie>> _getCookiesWithJavaScript(
{required WebUri url,
PlatformInAppWebViewController? webViewController}) async {
assert(url.toString().isNotEmpty);
List<Cookie> cookies = [];
if (webViewController != null) {
final javaScriptEnabled =
(await webViewController.getSettings())?.javaScriptEnabled ?? false;
if (javaScriptEnabled) {
List<String> documentCookies = (await webViewController
.evaluateJavascript(source: 'document.cookie') as String)
.split(';')
.map((documentCookie) => documentCookie.trim())
.toList();
documentCookies.forEach((documentCookie) {
List<String> cookie = documentCookie.split('=');
if (cookie.length > 1) {
cookies.add(Cookie(
name: cookie[0],
value: cookie[1],
));
}
});
return cookies;
}
}
final pageLoaded = Completer<void>();
final headlessWebView =
WindowsHeadlessInAppWebView(WindowsHeadlessInAppWebViewCreationParams(
initialUrlRequest: URLRequest(url: url),
onLoadStop: (controller, url) async {
pageLoaded.complete();
},
));
await headlessWebView.run();
await pageLoaded.future;
List<String> documentCookies = (await headlessWebView.webViewController!
.evaluateJavascript(source: 'document.cookie') as String)
.split(';')
.map((documentCookie) => documentCookie.trim())
.toList();
documentCookies.forEach((documentCookie) {
List<String> cookie = documentCookie.split('=');
if (cookie.length > 1) {
cookies.add(Cookie(
name: cookie[0],
value: cookie[1],
));
}
});
await headlessWebView.dispose();
return cookies;
}
@override
Future<Cookie?> 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<Cookie> cookies = await _getCookiesWithJavaScript(
url: url, webViewController: webViewController);
return cookies
.cast<Cookie?>()
.firstWhere((cookie) => cookie!.name == name, orElse: () => null);
}
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url.toString());
List<dynamic> cookies =
@ -307,29 +162,15 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController {
@override
Future<bool> 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<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url.toString());
args.putIfAbsent('name', () => name);
@ -341,31 +182,13 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController {
@override
Future<bool> 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<Cookie> 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<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('url', () => url.toString());
args.putIfAbsent('domain', () => domain);
@ -380,46 +203,10 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController {
}
@override
Future<List<Cookie>> getAllCookies() async {
List<Cookie> cookies = [];
Future<bool> removeSessionCookies() async {
Map<String, dynamic> args = <String, dynamic>{};
List<dynamic> cookieListMap =
await channel?.invokeMethod<List>('getAllCookies', args) ?? [];
cookieListMap = cookieListMap.cast<Map<dynamic, dynamic>>();
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<String> _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<bool> _shouldUseJavascript() async {
final platformUtil = PlatformUtil.instance();
final systemVersion = await platformUtil.getSystemVersion();
return systemVersion.compareTo("11") == -1;
return await channel?.invokeMethod<bool>('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;
}

View File

@ -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');

View File

@ -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}

View File

@ -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}

View File

@ -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(),

View File

@ -74,7 +74,7 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController
};
Set<String> _webMessageListenerObjNames = Set();
Map<String, ScriptHtmlTagAttributes> _injectedScriptsFromURL = {};
Set<MacOSWebMessageChannel> _webMessageChannels = Set();
Set<WindowsWebMessageChannel> _webMessageChannels = Set();
Set<MacOSWebMessageListener> _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<MacOSWebMessageChannel>;
props.webMessageChannels as Set<WindowsWebMessageChannel>;
_webMessageListeners =
props.webMessageListeners as Set<MacOSWebMessageListener>;
}
@ -2007,7 +2007,8 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent(
'screenshotConfiguration', () => screenshotConfiguration?.toMap());
return await channel?.invokeMethod<Uint8List?>('takeScreenshot', args);
final base64 = await channel?.invokeMethod<String?>('takeScreenshot', args);
return base64 != null ? base64Decode(base64) : null;
}
@override
@ -2460,12 +2461,12 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController
}
@override
Future<MacOSWebMessageChannel?> createWebMessageChannel() async {
Future<WindowsWebMessageChannel?> createWebMessageChannel() async {
Map<String, dynamic> args = <String, dynamic>{};
Map<String, dynamic>? result =
(await channel?.invokeMethod('createWebMessageChannel', args))
?.cast<String, dynamic>();
final webMessageChannel = MacOSWebMessageChannel.static().fromMap(result);
final webMessageChannel = WindowsWebMessageChannel.static().fromMap(result);
if (webMessageChannel != null) {
_webMessageChannels.add(webMessageChannel);
}

View File

@ -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();
}
}

View File

@ -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';

View File

@ -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<String, dynamic>? map) {
static WindowsWebMessageChannel? _fromMap(Map<String, dynamic>? 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<String, dynamic>? map) {
WindowsWebMessageChannel? fromMap(Map<String, dynamic>? map) {
return _fromMap(map);
}
@ -115,6 +115,6 @@ class MacOSWebMessageChannel extends PlatformWebMessageChannel
}
}
extension InternalWebMessageChannel on MacOSWebMessageChannel {
extension InternalWebMessageChannel on WindowsWebMessageChannel {
MethodChannel? get internalChannel => channel;
}

View File

@ -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;
}

View File

@ -0,0 +1 @@
export 'webview_environment.dart' hide InternalWindowsWebViewEnvironment;

View File

@ -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<dynamic> _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<WindowsWebViewEnvironment> create(
{WebViewEnvironmentSettings? settings}) async {
final env = WindowsWebViewEnvironment(
WindowsWebViewEnvironmentCreationParams(settings: settings)
);
Map<String, dynamic> args = <String, dynamic>{};
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<void> dispose() async {
Map<String, dynamic> args = <String, dynamic>{};
await channel?.invokeMethod('dispose', args);
disposeChannel();
}
}
extension InternalWindowsWebViewEnvironment on WindowsWebViewEnvironment {
get handleMethod => _handleMethod;
}

View File

@ -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:

View File

@ -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

View File

@ -0,0 +1,120 @@
#include <nlohmann/json.hpp>
#include <Shlwapi.h>
#include <time.h>
#include <winrt/base.h>
#include <wrl/event.h>
#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<ICoreWebView2Environment> webViewEnv,
wil::com_ptr<ICoreWebView2Controller> webViewController,
wil::com_ptr<ICoreWebView2CompositionController> 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<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
{
auto& arguments = std::get<flutter::EncodableMap>(*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<std::string>(arguments, "url");
auto name = get_fl_map_value<std::string>(arguments, "name");
auto value = get_fl_map_value<std::string>(arguments, "value");
auto path = get_fl_map_value<std::string>(arguments, "path");
auto domain = get_optional_fl_map_value<std::string>(arguments, "domain");
auto expiresDate = get_optional_fl_map_value<int64_t>(arguments, "expiresDate");
auto maxAge = get_optional_fl_map_value<int64_t>(arguments, "maxAge");
auto isSecure = get_optional_fl_map_value<bool>(arguments, "isSecure");
auto isHttpOnly = get_optional_fl_map_value<bool>(arguments, "isHttpOnly");
auto sameSite = get_optional_fl_map_value<std::string>(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<flutter::MethodResult<flutter::EncodableValue>>(std::move(result));
auto hr = webView_->CallDevToolsProtocolMethod(L"Network.setCookie", utf8_to_wide(parameters.dump()).c_str(), Callback<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
[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");
}
}

View File

@ -0,0 +1,36 @@
#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_COOKIE_MANAGER_H_
#define FLUTTER_INAPPWEBVIEW_PLUGIN_COOKIE_MANAGER_H_
#include <flutter/method_channel.h>
#include <flutter/standard_message_codec.h>
#include <WebView2.h>
#include <wil/com.h>
#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<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
private:
wil::com_ptr<ICoreWebView2Environment> webViewEnv_;
wil::com_ptr<ICoreWebView2Controller> webViewController_;
wil::com_ptr<ICoreWebView2> webView_;
WNDCLASS windowClass_ = {};
};
}
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_COOKIE_MANAGER_H_

View File

@ -2,9 +2,11 @@
#include <flutter/plugin_registrar_windows.h>
#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<WebViewEnvironmentManager>(this);
inAppWebViewManager = std::make_unique<InAppWebViewManager>(this);
inAppBrowserManager = std::make_unique<InAppBrowserManager>(this);
headlessInAppWebViewManager = std::make_unique<HeadlessInAppWebViewManager>(this);
cookieManager = std::make_unique<CookieManager>(this);
}
FlutterInappwebviewWindowsPlugin::~FlutterInappwebviewWindowsPlugin()

View File

@ -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> webViewEnvironmentManager;
std::unique_ptr<InAppWebViewManager> inAppWebViewManager;
std::unique_ptr<InAppBrowserManager> inAppBrowserManager;
std::unique_ptr<HeadlessInAppWebViewManager> headlessInAppWebViewManager;
std::unique_ptr<CookieManager> cookieManager;
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar);

View File

@ -19,7 +19,6 @@ namespace flutter_inappwebview_plugin
if (!webView) {
return;
}
webView->webViewController->put_IsVisible(false);
}
void HeadlessInAppWebView::setSize(const std::shared_ptr<Size2D> 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);
}
}
}

View File

@ -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<std::string>(params, "initialFile");
auto initialDataMap = get_optional_fl_map_value<flutter::EncodableMap>(params, "initialData");
auto initialUserScriptList = get_optional_fl_map_value<flutter::EncodableList>(params, "initialUserScripts");
auto webViewEnvironmentId = get_optional_fl_map_value<std::string>(*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<ICoreWebView2Environment> webViewEnv,
wil::com_ptr<ICoreWebView2Controller> webViewController,
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController)

View File

@ -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<ICoreWebView2Environment> webViewEnv, wil::com_ptr<ICoreWebView2Controller> webViewController, wil::com_ptr<ICoreWebView2CompositionController> 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

View File

@ -24,6 +24,7 @@ namespace flutter_inappwebview_plugin
const std::shared_ptr<InAppBrowserSettings> initialSettings;
const std::shared_ptr<InAppWebViewSettings> initialWebViewSettings;
const std::optional<std::vector<std::shared_ptr<UserScript>>> initialUserScripts;
const std::optional<std::string> webViewEnvironmentId;
};
class InAppBrowser {

View File

@ -53,6 +53,7 @@ namespace flutter_inappwebview_plugin
auto assetFilePath = get_optional_fl_map_value<std::string>(*arguments, "assetFilePath");
auto data = get_optional_fl_map_value<std::string>(*arguments, "data");
auto initialUserScriptList = get_optional_fl_map_value<flutter::EncodableList>(*arguments, "initialUserScripts");
auto webViewEnvironmentId = get_optional_fl_map_value<std::string>(*arguments, "webViewEnvironmentId");
std::optional<std::shared_ptr<URLRequest>> urlRequest = urlRequestMap.has_value() ? std::make_shared<URLRequest>(urlRequestMap.value()) : std::optional<std::shared_ptr<URLRequest>>{};
@ -70,7 +71,8 @@ namespace flutter_inappwebview_plugin
data,
std::move(initialSettings),
std::move(initialWebViewSettings),
initialUserScripts
initialUserScripts,
webViewEnvironmentId
};
auto inAppBrowser = std::make_unique<InAppBrowser>(plugin, params);

View File

@ -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<void(wil::com_ptr<ICoreWebView2Environment> webViewEnv,
void InAppWebView::createInAppWebViewEnv(const CreateInAppWebViewEnvParams& params, const WebViewEnvironment* webViewEnvironment, std::function<void(wil::com_ptr<ICoreWebView2Environment> webViewEnv,
wil::com_ptr<ICoreWebView2Controller> webViewController,
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController)> completionHandler)
{
failedLog(CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr,
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
[parentWindow, completionHandler, willBeSurface](HRESULT result, wil::com_ptr<ICoreWebView2Environment> env) -> HRESULT
{
if (failedAndLog(result) || !env) {
completionHandler(nullptr, nullptr, nullptr);
return E_FAIL;
}
auto callback = [params, completionHandler](HRESULT result, wil::com_ptr<ICoreWebView2Environment> env) -> HRESULT
{
if (failedAndLog(result) || !env) {
completionHandler(nullptr, nullptr, nullptr);
return E_FAIL;
}
wil::com_ptr<ICoreWebView2Environment3> webViewEnv3;
if (willBeSurface && succeededOrLog(env->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) {
failedLog(webViewEnv3->CreateCoreWebView2CompositionController(parentWindow, Callback<ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler>(
[completionHandler, env](HRESULT result, wil::com_ptr<ICoreWebView2CompositionController> compositionController) -> HRESULT
{
wil::com_ptr<ICoreWebView2Controller3> webViewController = compositionController.try_query<ICoreWebView2Controller3>();
wil::com_ptr<ICoreWebView2Environment3> webViewEnv3;
if (params.willBeSurface && succeededOrLog(env->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) {
failedLog(webViewEnv3->CreateCoreWebView2CompositionController(params.parentWindow, Callback<ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler>(
[completionHandler, env](HRESULT result, wil::com_ptr<ICoreWebView2CompositionController> compositionController) -> HRESULT
{
wil::com_ptr<ICoreWebView2Controller3> webViewController = compositionController.try_query<ICoreWebView2Controller3>();
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<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
[completionHandler, env](HRESULT result, wil::com_ptr<ICoreWebView2Controller> 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<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
[completionHandler, env](HRESULT result, wil::com_ptr<ICoreWebView2Controller> 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<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(callback).Get());
}
if (failedAndLog(hr)) {
completionHandler(nullptr, nullptr, nullptr);
}
}
void InAppWebView::initChannel(const std::optional<std::variant<std::string, int64_t>> viewId, const std::optional<std::string> 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<IUri> 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> webHistory)
@ -682,7 +685,7 @@ namespace flutter_inappwebview_plugin
);
}
void InAppWebView::canGoBackOrForward(const int& steps, std::function<void(bool)> completionHandler) const
void InAppWebView::canGoBackOrForward(const int64_t& steps, std::function<void(bool)> completionHandler) const
{
getCopyBackForwardList(
[steps, completionHandler](std::unique_ptr<WebHistory> webHistory)
@ -920,6 +923,55 @@ namespace flutter_inappwebview_plugin
userContentController->removeAllUserOnlyScripts();
}
void InAppWebView::takeScreenshot(const std::optional<std::shared_ptr<ScreenshotConfiguration>> screenshotConfiguration, const std::function<void(const std::optional<std::string>)> 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<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
[this, completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson)
{
std::optional<std::string> result = std::nullopt;
if (succeededOrLog(errorCode)) {
nlohmann::json json = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson));
result = json["data"].get<std::string>();
}
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;
}
}
}

View File

@ -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<std::vector<std::shared_ptr<UserScript>>> initialUserScripts;
};
struct CreateInAppWebViewEnvParams {
const HWND parentWindow;
const bool willBeSurface;
};
class InAppWebView
{
public:
@ -107,7 +114,7 @@ namespace flutter_inappwebview_plugin
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController);
~InAppWebView();
static void createInAppWebViewEnv(const HWND parentWindow, const bool willBeSurface, std::function<void(wil::com_ptr<ICoreWebView2Environment> webViewEnv,
static void createInAppWebViewEnv(const CreateInAppWebViewEnvParams& params, const WebViewEnvironment* webViewEnvironment, std::function<void(wil::com_ptr<ICoreWebView2Environment> webViewEnv,
wil::com_ptr<ICoreWebView2Controller> webViewController,
wil::com_ptr<ICoreWebView2CompositionController> 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<void(bool)> completionHandler) const;
void goBackOrForward(const int64_t& steps);
void canGoBackOrForward(const int64_t& steps, std::function<void(bool)> 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> userScript) const;
void removeUserScriptsByGroupName(const std::string& groupName) const;
void removeAllUserScripts() const;
void takeScreenshot(const std::optional<std::shared_ptr<ScreenshotConfiguration>> screenshotConfiguration, const std::function<void(const std::optional<std::string>)> completionHandler) const;
std::string pageFrameId() const
{

View File

@ -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<std::string>(*arguments, "initialFile");
auto initialDataMap = get_optional_fl_map_value<flutter::EncodableMap>(*arguments, "initialData");
auto initialUserScriptList = get_optional_fl_map_value<flutter::EncodableList>(*arguments, "initialUserScripts");
auto webViewEnvironmentId = get_optional_fl_map_value<std::string>(*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<ICoreWebView2Environment> webViewEnv,
wil::com_ptr<ICoreWebView2Controller> webViewController,
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController)
@ -182,4 +193,4 @@ namespace flutter_inappwebview_plugin
UnregisterClass(windowClass_.lpszClassName, nullptr);
plugin = nullptr;
}
}
}

View File

@ -159,6 +159,16 @@ namespace flutter_inappwebview_plugin
webView->removeAllUserScripts();
result->Success(true);
}
else if (string_equals(methodName, "takeScreenshot")) {
auto result_ = std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>(std::move(result));
auto screenshotConfigurationMap = get_optional_fl_map_value<flutter::EncodableMap>(arguments, "screenshotConfiguration");
std::optional<std::unique_ptr<ScreenshotConfiguration>> screenshotConfiguration =
screenshotConfigurationMap.has_value() ? std::make_unique<ScreenshotConfiguration>(screenshotConfigurationMap.value()) : std::optional<std::unique_ptr<ScreenshotConfiguration>>{};
webView->takeScreenshot(std::move(screenshotConfiguration), [result_ = std::move(result_)](const std::optional<std::string> data)
{
result_->Success(make_fl_value(data));
});
}
// for inAppBrowser
else if (webView->inAppBrowser && string_equals(methodName, "show")) {
webView->inAppBrowser->show();

View File

@ -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<double>(map, "x")),
y(get_fl_map_value<double>(map, "y")),
width(get_fl_map_value<double>(map, "width")),
height(get_fl_map_value<double>(map, "height"))
{}
flutter::EncodableMap Rect::toEncodableMap() const
{
return flutter::EncodableMap{
{"x", x},
{"y", y},
{"width", width},
{"height", height}
};
}
}

View File

@ -0,0 +1,27 @@
#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_RECT_H_
#define FLUTTER_INAPPWEBVIEW_PLUGIN_RECT_H_
#include <flutter/standard_method_codec.h>
#include <optional>
#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_

View File

@ -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<std::shared_ptr<Rect>> rect
) : compressFormat(compressFormat), quality(quality), rect(rect)
{}
ScreenshotConfiguration::ScreenshotConfiguration(const flutter::EncodableMap& map)
: compressFormat(CompressFormatFromString(get_fl_map_value<std::string>(map, "compressFormat"))),
quality(get_fl_map_value<int>(map, "quality")),
rect(fl_map_contains_not_null(map, "rect") ? std::make_shared<Rect>(get_fl_map_value<flutter::EncodableMap>(map, "rect")) : std::optional<std::shared_ptr<Rect>>{})
{}
ScreenshotConfiguration::~ScreenshotConfiguration() {}
}

View File

@ -0,0 +1,38 @@
#ifndef FLUTTER_INAPPWEBVIEW_SCREENSHOT_CONFIGURATION_H_
#define FLUTTER_INAPPWEBVIEW_SCREENSHOT_CONFIGURATION_H_
#include <flutter/standard_method_codec.h>
#include <optional>
#include <string>
#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<std::shared_ptr<Rect>> rect;
ScreenshotConfiguration(
const CompressFormat& compressFormat,
const int64_t& quality,
const std::optional<std::shared_ptr<Rect>> rect
);
ScreenshotConfiguration(const flutter::EncodableMap& map);
~ScreenshotConfiguration();
};
}
#endif //FLUTTER_INAPPWEBVIEW_SCREENSHOT_CONFIGURATION_H_

View File

@ -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)
{}

View File

@ -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;

View File

@ -3,7 +3,7 @@
namespace flutter_inappwebview_plugin
{
WebResourceResponse::WebResourceResponse(const std::optional<int>& statusCode)
WebResourceResponse::WebResourceResponse(const std::optional<int64_t>& statusCode)
: statusCode(statusCode)
{}

View File

@ -9,9 +9,9 @@ namespace flutter_inappwebview_plugin
class WebResourceResponse
{
public:
const std::optional<int> statusCode;
const std::optional<int64_t> statusCode;
WebResourceResponse(const std::optional<int>& statusCode);
WebResourceResponse(const std::optional<int64_t>& statusCode);
WebResourceResponse(const flutter::EncodableMap& map);
~WebResourceResponse() = default;

View File

@ -80,23 +80,65 @@ namespace flutter_inappwebview_plugin
return encodableMap;
}
template<typename T>
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<typename T, typename std::enable_if<(!std::is_same<T, int32_t>::value && !std::is_same<T, int64_t>::value), bool>::type* = nullptr>
static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* key)
{
return std::get<T>(map.at(make_fl_value(key)));
}
template<typename T, typename std::enable_if<((!is_mappish<T>::value && !is_vector<T>::value) ||
template<typename T, typename std::enable_if<std::is_same<T, int32_t>::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<typename T, typename std::enable_if<std::is_same<T, int64_t>::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<typename T, typename std::enable_if<((!is_mappish<T>::value && !is_vector<T>::value && !std::is_same<T, int32_t>::value && !std::is_same<T, int64_t>::value) ||
std::is_same<T, flutter::EncodableMap>::value || std::is_same<T, flutter::EncodableList>::value), int>::type* = nullptr>
static inline std::optional<T> get_optional_fl_map_value(const flutter::EncodableMap& map, const char* key)
{
auto fl_key = make_fl_value(key);
if (map_contains<flutter::EncodableValue, flutter::EncodableValue>(map, fl_key)) {
if (fl_map_contains_not_null(map, key)) {
auto fl_key = make_fl_value(key);
return make_pointer_optional<T>(std::get_if<T>(&map.at(fl_key)));
}
return std::nullopt;
}
template<typename T, typename std::enable_if<std::is_same<T, int32_t>::value, bool>::type* = nullptr>
static inline std::optional<int64_t> 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<int64_t>(map.at(fl_key).LongValue());
}
return std::nullopt;
}
template<typename T, typename std::enable_if<std::is_same<T, int64_t>::value, bool>::type* = nullptr>
static inline std::optional<int64_t> 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<int64_t>(map.at(fl_key).LongValue());
}
return std::nullopt;
}
template<typename T>
static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* key, const T& defaultValue)
{

View File

@ -1,6 +1,7 @@
#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_
#define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_
#include <algorithm>
#include <numeric>
#include <optional>
#include <string>
@ -142,6 +143,39 @@ namespace flutter_inappwebview_plugin
res.push_back(s.substr(pos_start));
return res;
}
template <typename T>
void to_lowercase(const std::basic_string<T>& s)
{
std::transform(s.begin(), s.end(), s.begin(),
[](const T v) { return static_cast<T>(std::tolower(v)); });
}
template <typename T>
std::basic_string<T> to_lowercase_copy(const std::basic_string<T>& s)
{
std::basic_string<T> s2 = s;
std::transform(s2.begin(), s2.end(), s2.begin(),
[](const T v) { return static_cast<T>(std::tolower(v)); });
return s2;
}
template <typename T>
void to_uppercase(const std::basic_string<T>& s)
{
std::transform(s.begin(), s.end(), s.begin(),
[](const T v) { return static_cast<T>(std::toupper(v)); });
return s2;
}
template <typename T>
std::basic_string<T> to_uppercase_copy(const std::basic_string<T>& s)
{
std::basic_string<T> s2 = s;
std::transform(s2.begin(), s2.end(), s2.begin(),
[](const T v) { return static_cast<T>(std::toupper(v)); });
return s2;
}
}
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_

View File

@ -0,0 +1,59 @@
#include <WebView2EnvironmentOptions.h>
#include <wil/wrl.h>
#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<WebViewEnvironmentChannelDelegate>(this, plugin->registrar->messenger()))
{}
void WebViewEnvironment::create(const std::unique_ptr<WebViewEnvironmentSettings> settings, const std::function<void(HRESULT)> completionHandler)
{
auto options = Make<CoreWebView2EnvironmentOptions>();
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<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
[this, completionHandler](HRESULT result, wil::com_ptr<ICoreWebView2Environment> 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");
}
}

View File

@ -0,0 +1,30 @@
#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_H_
#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_H_
#include <functional>
#include <WebView2.h>
#include <wil/com.h>
#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<ICoreWebView2Environment> env;
std::unique_ptr<WebViewEnvironmentChannelDelegate> channelDelegate;
WebViewEnvironment(const FlutterInappwebviewWindowsPlugin* plugin, const std::string& id);
~WebViewEnvironment();
void create(const std::unique_ptr<WebViewEnvironmentSettings> settings, const std::function<void(HRESULT)> completionHandler);
};
}
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_H_

View File

@ -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<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
{
if (!webViewEnvironment) {
result->Success();
return;
}
// auto& arguments = std::get<flutter::EncodableMap>(*method_call.arguments());
auto& methodName = method_call.method_name();
if (string_equals(methodName, "dispose")) {
if (webViewEnvironment->plugin && webViewEnvironment->plugin->webViewEnvironmentManager) {
std::map<std::string, std::unique_ptr<WebViewEnvironment>>& 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;
}
}

View File

@ -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 <flutter/method_channel.h>
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<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
};
}
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CHANNEL_DELEGATE_H_

View File

@ -0,0 +1,63 @@
#include <DispatcherQueue.h>
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>
#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<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
{
auto* arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
auto& methodName = method_call.method_name();
if (string_equals(methodName, "create")) {
auto id = get_fl_map_value<std::string>(*arguments, "id");
auto settingsMap = get_optional_fl_map_value<flutter::EncodableMap>(*arguments, "settings");
auto settings = settingsMap.has_value() ? std::make_unique<WebViewEnvironmentSettings>(settingsMap.value()) : nullptr;
createWebViewEnvironment(id, std::move(settings), std::move(result));
}
else {
result->NotImplemented();
}
}
void WebViewEnvironmentManager::createWebViewEnvironment(const std::string& id, std::unique_ptr<WebViewEnvironmentSettings> settings, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
{
auto result_ = std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>(std::move(result));
auto webViewEnvironment = std::make_unique<WebViewEnvironment>(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;
}
}

View File

@ -0,0 +1,33 @@
#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_MANAGER_H_
#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_MANAGER_H_
#include <flutter/method_channel.h>
#include <map>
#include <string>
#include <winrt/base.h>
#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<std::string, std::unique_ptr<WebViewEnvironment>> webViewEnvironments;
WebViewEnvironmentManager(const FlutterInappwebviewWindowsPlugin* plugin);
~WebViewEnvironmentManager();
void HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
void createWebViewEnvironment(const std::string& id, std::unique_ptr<WebViewEnvironmentSettings> settings, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
};
}
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_MANAGER_H_

View File

@ -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<std::string>(map, "browserExecutableFolder")),
userDataFolder(get_optional_fl_map_value<std::string>(map, "userDataFolder")),
additionalBrowserArguments(get_optional_fl_map_value<std::string>(map, "additionalBrowserArguments")),
allowSingleSignOnUsingOSPrimaryAccount(get_optional_fl_map_value<bool>(map, "allowSingleSignOnUsingOSPrimaryAccount")),
language(get_optional_fl_map_value<std::string>(map, "language")),
targetCompatibleBrowserVersion(get_optional_fl_map_value<std::string>(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)}
};
}
}

View File

@ -0,0 +1,29 @@
#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CREATION_PARAMS_H_
#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CREATION_PARAMS_H_
#include <flutter/standard_method_codec.h>
#include <optional>
#include <string>
#include "../flutter_inappwebview_windows_plugin.h"
namespace flutter_inappwebview_plugin
{
class WebViewEnvironmentSettings
{
public:
const std::optional<std::string> browserExecutableFolder;
const std::optional<std::string> userDataFolder;
const std::optional<std::string> additionalBrowserArguments;
const std::optional<bool> allowSingleSignOnUsingOSPrimaryAccount;
const std::optional<std::string> language;
const std::optional<std::string> targetCompatibleBrowserVersion;
WebViewEnvironmentSettings() = default;
WebViewEnvironmentSettings(const flutter::EncodableMap& map);
~WebViewEnvironmentSettings() = default;
flutter::EncodableMap toEncodableMap() const;
};
}
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CREATION_PARAMS_H_