From 81b72b3213256fb8c4febe1efdc3585a4877d44c Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Jan 2024 04:51:16 +0100 Subject: [PATCH 01/21] basic initial windows support --- flutter_inappwebview/example/.metadata | 26 +- flutter_inappwebview/example/lib/main.dart | 76 +- flutter_inappwebview/example/pubspec.yaml | 2 + .../example/test/widget_test.dart | 30 + .../example/windows/.gitignore | 19 + .../example/windows/CMakeLists.txt | 108 + .../example/windows/flutter/CMakeLists.txt | 109 + .../flutter/generated_plugin_registrant.cc | 20 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 26 + .../example/windows/runner/CMakeLists.txt | 40 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 71 + .../example/windows/runner/flutter_window.h | 33 + .../example/windows/runner/main.cpp | 43 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 65 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 288 ++ .../example/windows/runner/win32_window.h | 102 + .../example/pubspec.lock | 2 +- flutter_inappwebview_windows/.gitignore | 29 + flutter_inappwebview_windows/.metadata | 30 + flutter_inappwebview_windows/CHANGELOG.md | 3 + flutter_inappwebview_windows/LICENSE | 1 + flutter_inappwebview_windows/README.md | 15 + .../analysis_options.yaml | 15 + .../example/.gitignore | 43 + .../example/README.md | 16 + .../example/analysis_options.yaml | 28 + .../plugin_integration_test.dart | 0 .../example/lib/main.dart | 0 .../example/pubspec.lock | 283 ++ .../example/pubspec.yaml | 85 + .../example/test/widget_test.dart | 0 .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 110 + .../example/windows/flutter/CMakeLists.txt | 109 + .../flutter/generated_plugin_registrant.cc | 14 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 24 + .../example/windows/runner/CMakeLists.txt | 40 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 71 + .../example/windows/runner/flutter_window.h | 33 + .../example/windows/runner/main.cpp | 43 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 65 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 288 ++ .../example/windows/runner/win32_window.h | 102 + .../lib/flutter_inappwebview_windows.dart | 3 + .../lib/src/cookie_manager.dart | 433 +++ .../find_interaction_controller.dart | 124 + .../lib/src/find_interaction/main.dart | 2 + .../src/http_auth_credentials_database.dart | 155 + .../src/in_app_browser/in_app_browser.dart | 373 +++ .../lib/src/in_app_browser/main.dart | 1 + .../src/in_app_webview/_static_channel.dart | 4 + .../headless_in_app_webview.dart | 433 +++ .../src/in_app_webview/in_app_webview.dart | 408 +++ .../in_app_webview_controller.dart | 2689 +++++++++++++++++ .../lib/src/in_app_webview/main.dart | 3 + .../lib/src/inappwebview_platform.dart | 32 + .../lib/src/main.dart | 10 + .../lib/src/platform_util.dart | 64 + .../lib/src/print_job/main.dart | 1 + .../src/print_job/print_job_controller.dart | 73 + .../lib/src/web_message/main.dart | 3 + .../src/web_message/web_message_channel.dart | 120 + .../src/web_message/web_message_listener.dart | 164 + .../lib/src/web_message/web_message_port.dart | 95 + .../lib/src/web_storage/main.dart | 2 + .../lib/src/web_storage/web_storage.dart | 257 ++ .../src/web_storage/web_storage_manager.dart | 134 + flutter_inappwebview_windows/pubspec.yaml | 78 + .../flutter_inappwebview_windows_test.dart | 0 .../windows/.gitignore | 19 + .../windows/CMakeLists.txt | 151 + ...utter_inappwebview_windows_base_plugin.cpp | 14 + ...flutter_inappwebview_windows_base_plugin.h | 18 + .../flutter_inappwebview_windows_plugin.cpp | 35 + .../flutter_inappwebview_windows_plugin.h | 30 + ...tter_inappwebview_windows_plugin_c_api.cpp | 12 + .../windows/in_app_browser/in_app_browser.cpp | 238 ++ .../windows/in_app_browser/in_app_browser.h | 51 + .../in_app_browser_channel_delegate.cpp | 40 + .../in_app_browser_channel_delegate.h | 26 + .../in_app_browser/in_app_browser_manager.cpp | 56 + .../in_app_browser/in_app_browser_manager.h | 35 + .../windows/in_app_webview/in_app_webview.cpp | 114 + .../windows/in_app_webview/in_app_webview.h | 39 + .../webview_channel_delegate.cpp | 67 + .../in_app_webview/webview_channel_delegate.h | 31 + ...lutter_inappwebview_windows_plugin_c_api.h | 23 + ...utter_inappwebview_windows_plugin_test.cpp | 43 + .../windows/types/channel_delegate.cpp | 35 + .../windows/types/channel_delegate.h | 27 + .../windows/types/url_request.cpp | 13 + .../windows/types/url_request.h | 20 + .../windows/utils/strconv.h | 572 ++++ .../windows/utils/util.h | 41 + 106 files changed, 10310 insertions(+), 7 deletions(-) create mode 100644 flutter_inappwebview/example/test/widget_test.dart create mode 100644 flutter_inappwebview/example/windows/.gitignore create mode 100644 flutter_inappwebview/example/windows/CMakeLists.txt create mode 100644 flutter_inappwebview/example/windows/flutter/CMakeLists.txt create mode 100644 flutter_inappwebview/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 flutter_inappwebview/example/windows/flutter/generated_plugin_registrant.h create mode 100644 flutter_inappwebview/example/windows/flutter/generated_plugins.cmake create mode 100644 flutter_inappwebview/example/windows/runner/CMakeLists.txt create mode 100644 flutter_inappwebview/example/windows/runner/Runner.rc create mode 100644 flutter_inappwebview/example/windows/runner/flutter_window.cpp create mode 100644 flutter_inappwebview/example/windows/runner/flutter_window.h create mode 100644 flutter_inappwebview/example/windows/runner/main.cpp create mode 100644 flutter_inappwebview/example/windows/runner/resource.h create mode 100644 flutter_inappwebview/example/windows/runner/resources/app_icon.ico create mode 100644 flutter_inappwebview/example/windows/runner/runner.exe.manifest create mode 100644 flutter_inappwebview/example/windows/runner/utils.cpp create mode 100644 flutter_inappwebview/example/windows/runner/utils.h create mode 100644 flutter_inappwebview/example/windows/runner/win32_window.cpp create mode 100644 flutter_inappwebview/example/windows/runner/win32_window.h create mode 100644 flutter_inappwebview_windows/.gitignore create mode 100644 flutter_inappwebview_windows/.metadata create mode 100644 flutter_inappwebview_windows/CHANGELOG.md create mode 100644 flutter_inappwebview_windows/LICENSE create mode 100644 flutter_inappwebview_windows/README.md create mode 100644 flutter_inappwebview_windows/analysis_options.yaml create mode 100644 flutter_inappwebview_windows/example/.gitignore create mode 100644 flutter_inappwebview_windows/example/README.md create mode 100644 flutter_inappwebview_windows/example/analysis_options.yaml create mode 100644 flutter_inappwebview_windows/example/integration_test/plugin_integration_test.dart create mode 100644 flutter_inappwebview_windows/example/lib/main.dart create mode 100644 flutter_inappwebview_windows/example/pubspec.lock create mode 100644 flutter_inappwebview_windows/example/pubspec.yaml create mode 100644 flutter_inappwebview_windows/example/test/widget_test.dart create mode 100644 flutter_inappwebview_windows/example/windows/.gitignore create mode 100644 flutter_inappwebview_windows/example/windows/CMakeLists.txt create mode 100644 flutter_inappwebview_windows/example/windows/flutter/CMakeLists.txt create mode 100644 flutter_inappwebview_windows/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 flutter_inappwebview_windows/example/windows/flutter/generated_plugin_registrant.h create mode 100644 flutter_inappwebview_windows/example/windows/flutter/generated_plugins.cmake create mode 100644 flutter_inappwebview_windows/example/windows/runner/CMakeLists.txt create mode 100644 flutter_inappwebview_windows/example/windows/runner/Runner.rc create mode 100644 flutter_inappwebview_windows/example/windows/runner/flutter_window.cpp create mode 100644 flutter_inappwebview_windows/example/windows/runner/flutter_window.h create mode 100644 flutter_inappwebview_windows/example/windows/runner/main.cpp create mode 100644 flutter_inappwebview_windows/example/windows/runner/resource.h create mode 100644 flutter_inappwebview_windows/example/windows/runner/resources/app_icon.ico create mode 100644 flutter_inappwebview_windows/example/windows/runner/runner.exe.manifest create mode 100644 flutter_inappwebview_windows/example/windows/runner/utils.cpp create mode 100644 flutter_inappwebview_windows/example/windows/runner/utils.h create mode 100644 flutter_inappwebview_windows/example/windows/runner/win32_window.cpp create mode 100644 flutter_inappwebview_windows/example/windows/runner/win32_window.h create mode 100644 flutter_inappwebview_windows/lib/flutter_inappwebview_windows.dart create mode 100644 flutter_inappwebview_windows/lib/src/cookie_manager.dart create mode 100644 flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart create mode 100644 flutter_inappwebview_windows/lib/src/find_interaction/main.dart create mode 100644 flutter_inappwebview_windows/lib/src/http_auth_credentials_database.dart create mode 100644 flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart create mode 100644 flutter_inappwebview_windows/lib/src/in_app_browser/main.dart create mode 100644 flutter_inappwebview_windows/lib/src/in_app_webview/_static_channel.dart create mode 100644 flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart create mode 100644 flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart create mode 100644 flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart create mode 100644 flutter_inappwebview_windows/lib/src/in_app_webview/main.dart create mode 100644 flutter_inappwebview_windows/lib/src/inappwebview_platform.dart create mode 100644 flutter_inappwebview_windows/lib/src/main.dart create mode 100644 flutter_inappwebview_windows/lib/src/platform_util.dart create mode 100644 flutter_inappwebview_windows/lib/src/print_job/main.dart create mode 100644 flutter_inappwebview_windows/lib/src/print_job/print_job_controller.dart create mode 100644 flutter_inappwebview_windows/lib/src/web_message/main.dart create mode 100644 flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart create mode 100644 flutter_inappwebview_windows/lib/src/web_message/web_message_listener.dart create mode 100644 flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart create mode 100644 flutter_inappwebview_windows/lib/src/web_storage/main.dart create mode 100644 flutter_inappwebview_windows/lib/src/web_storage/web_storage.dart create mode 100644 flutter_inappwebview_windows/lib/src/web_storage/web_storage_manager.dart create mode 100644 flutter_inappwebview_windows/pubspec.yaml create mode 100644 flutter_inappwebview_windows/test/flutter_inappwebview_windows_test.dart create mode 100644 flutter_inappwebview_windows/windows/.gitignore create mode 100644 flutter_inappwebview_windows/windows/CMakeLists.txt create mode 100644 flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.cpp create mode 100644 flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.h create mode 100644 flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp create mode 100644 flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h create mode 100644 flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin_c_api.cpp create mode 100644 flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp create mode 100644 flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h create mode 100644 flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp create mode 100644 flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.h create mode 100644 flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp create mode 100644 flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h create mode 100644 flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp create mode 100644 flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h create mode 100644 flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp create mode 100644 flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h create mode 100644 flutter_inappwebview_windows/windows/include/flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h create mode 100644 flutter_inappwebview_windows/windows/test/flutter_inappwebview_windows_plugin_test.cpp create mode 100644 flutter_inappwebview_windows/windows/types/channel_delegate.cpp create mode 100644 flutter_inappwebview_windows/windows/types/channel_delegate.h create mode 100644 flutter_inappwebview_windows/windows/types/url_request.cpp create mode 100644 flutter_inappwebview_windows/windows/types/url_request.h create mode 100644 flutter_inappwebview_windows/windows/utils/strconv.h create mode 100644 flutter_inappwebview_windows/windows/utils/util.h diff --git a/flutter_inappwebview/example/.metadata b/flutter_inappwebview/example/.metadata index 8f550764..ba5723a2 100755 --- a/flutter_inappwebview/example/.metadata +++ b/flutter_inappwebview/example/.metadata @@ -4,5 +4,27 @@ # This file should be version controlled and should not be manually edited. version: - revision: d927c9331005f81157fa39dff7b5dab415ad330b - channel: master + revision: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: windows + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/flutter_inappwebview/example/lib/main.dart b/flutter_inappwebview/example/lib/main.dart index 21760f26..da75e5b6 100755 --- a/flutter_inappwebview/example/lib/main.dart +++ b/flutter_inappwebview/example/lib/main.dart @@ -26,10 +26,6 @@ Future main() async { await InAppWebViewController.setWebContentsDebuggingEnabled(kDebugMode); } - if (!kIsWeb) { - await localhostServer.start(); - } - runApp(MyApp()); } @@ -75,6 +71,41 @@ PointerInterceptor myDrawer({required BuildContext context}) { }, ) ]; + } else if (defaultTargetPlatform == TargetPlatform.macOS || + defaultTargetPlatform == TargetPlatform.windows || + defaultTargetPlatform == TargetPlatform.linux) { + children = [ + // ListTile( + // title: Text('InAppWebView'), + // onTap: () { + // Navigator.pushReplacementNamed(context, '/'); + // }, + // ), + // ListTile( + // title: Text('InAppBrowser'), + // onTap: () { + // Navigator.pushReplacementNamed(context, '/InAppBrowser'); + // }, + // ), + ListTile( + title: Text('InAppBrowser'), + onTap: () { + Navigator.pushReplacementNamed(context, '/'); + }, + ), + ListTile( + title: Text('WebAuthenticationSession'), + onTap: () { + Navigator.pushReplacementNamed(context, '/WebAuthenticationSession'); + }, + ), + ListTile( + title: Text('HeadlessInAppWebView'), + onTap: () { + Navigator.pushReplacementNamed(context, '/HeadlessInAppWebView'); + }, + ), + ]; } else if (defaultTargetPlatform == TargetPlatform.macOS) { children = [ // ListTile( @@ -108,6 +139,34 @@ PointerInterceptor myDrawer({required BuildContext context}) { }, ), ]; + } else if (defaultTargetPlatform == TargetPlatform.windows || + defaultTargetPlatform == TargetPlatform.linux) { + children = [ + // ListTile( + // title: Text('InAppWebView'), + // onTap: () { + // Navigator.pushReplacementNamed(context, '/'); + // }, + // ), + // ListTile( + // title: Text('InAppBrowser'), + // onTap: () { + // Navigator.pushReplacementNamed(context, '/InAppBrowser'); + // }, + // ), + ListTile( + title: Text('InAppBrowser'), + onTap: () { + Navigator.pushReplacementNamed(context, '/'); + }, + ), + ListTile( + title: Text('HeadlessInAppWebView'), + onTap: () { + Navigator.pushReplacementNamed(context, '/HeadlessInAppWebView'); + }, + ), + ]; } return PointerInterceptor( child: Drawer( @@ -160,6 +219,15 @@ class _MyAppState extends State { '/WebAuthenticationSession': (context) => WebAuthenticationSessionExampleScreen(), }); + } else if (defaultTargetPlatform == TargetPlatform.windows || + defaultTargetPlatform == TargetPlatform.linux) { + return MaterialApp(initialRoute: '/', routes: { + // '/': (context) => InAppWebViewExampleScreen(), + // '/InAppBrowser': (context) => InAppBrowserExampleScreen(), + '/': (context) => InAppBrowserExampleScreen(), + '/HeadlessInAppWebView': (context) => + HeadlessInAppWebViewExampleScreen(), + }); } return MaterialApp(initialRoute: '/', routes: { '/': (context) => InAppWebViewExampleScreen(), diff --git a/flutter_inappwebview/example/pubspec.yaml b/flutter_inappwebview/example/pubspec.yaml index fa737046..59aa16aa 100755 --- a/flutter_inappwebview/example/pubspec.yaml +++ b/flutter_inappwebview/example/pubspec.yaml @@ -43,6 +43,8 @@ dependency_overrides: path: ../../flutter_inappwebview_macos flutter_inappwebview_web: path: ../../flutter_inappwebview_web + flutter_inappwebview_windows: + path: ../../flutter_inappwebview_windows dev_dependencies: flutter_test: diff --git a/flutter_inappwebview/example/test/widget_test.dart b/flutter_inappwebview/example/test/widget_test.dart new file mode 100644 index 00000000..092d222f --- /dev/null +++ b/flutter_inappwebview/example/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:example/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/flutter_inappwebview/example/windows/.gitignore b/flutter_inappwebview/example/windows/.gitignore new file mode 100644 index 00000000..5d57396c --- /dev/null +++ b/flutter_inappwebview/example/windows/.gitignore @@ -0,0 +1,19 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +cmake-build-* \ No newline at end of file diff --git a/flutter_inappwebview/example/windows/CMakeLists.txt b/flutter_inappwebview/example/windows/CMakeLists.txt new file mode 100644 index 00000000..d960948a --- /dev/null +++ b/flutter_inappwebview/example/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(example LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/flutter_inappwebview/example/windows/flutter/CMakeLists.txt b/flutter_inappwebview/example/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..903f4899 --- /dev/null +++ b/flutter_inappwebview/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/flutter_inappwebview/example/windows/flutter/generated_plugin_registrant.cc b/flutter_inappwebview/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..031b8695 --- /dev/null +++ b/flutter_inappwebview/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,20 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); +} diff --git a/flutter_inappwebview/example/windows/flutter/generated_plugin_registrant.h b/flutter_inappwebview/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/flutter_inappwebview/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/flutter_inappwebview/example/windows/flutter/generated_plugins.cmake b/flutter_inappwebview/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..997d0b80 --- /dev/null +++ b/flutter_inappwebview/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,26 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + flutter_inappwebview_windows + permission_handler_windows + url_launcher_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/flutter_inappwebview/example/windows/runner/CMakeLists.txt b/flutter_inappwebview/example/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..394917c0 --- /dev/null +++ b/flutter_inappwebview/example/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/flutter_inappwebview/example/windows/runner/Runner.rc b/flutter_inappwebview/example/windows/runner/Runner.rc new file mode 100644 index 00000000..302ceb4b --- /dev/null +++ b/flutter_inappwebview/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.pichillilorenzo" "\0" + VALUE "FileDescription", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2023 com.pichillilorenzo. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/flutter_inappwebview/example/windows/runner/flutter_window.cpp b/flutter_inappwebview/example/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..955ee303 --- /dev/null +++ b/flutter_inappwebview/example/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/flutter_inappwebview/example/windows/runner/flutter_window.h b/flutter_inappwebview/example/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/flutter_inappwebview/example/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/flutter_inappwebview/example/windows/runner/main.cpp b/flutter_inappwebview/example/windows/runner/main.cpp new file mode 100644 index 00000000..a61bf80d --- /dev/null +++ b/flutter_inappwebview/example/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/flutter_inappwebview/example/windows/runner/resource.h b/flutter_inappwebview/example/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/flutter_inappwebview/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/flutter_inappwebview/example/windows/runner/resources/app_icon.ico b/flutter_inappwebview/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 zcmeHQc|26z|35SKE&G-*mXah&B~fFkXr)DEO&hIfqby^T&>|8^_Ub8Vp#`BLl3lbZ zvPO!8k!2X>cg~Elr=IVxo~J*a`+9wR=A83c-k-DFd(XM&UI1VKCqM@V;DDtJ09WB} zRaHKiW(GT00brH|0EeTeKVbpbGZg?nK6-j827q-+NFM34gXjqWxJ*a#{b_apGN<-L_m3#8Z26atkEn& ze87Bvv^6vVmM+p+cQ~{u%=NJF>#(d;8{7Q{^rWKWNtf14H}>#&y7$lqmY6xmZryI& z($uy?c5-+cPnt2%)R&(KIWEXww>Cnz{OUpT>W$CbO$h1= z#4BPMkFG1Y)x}Ui+WXr?Z!w!t_hjRq8qTaWpu}FH{MsHlU{>;08goVLm{V<&`itk~ zE_Ys=D(hjiy+5=?=$HGii=Y5)jMe9|wWoD_K07(}edAxh`~LBorOJ!Cf@f{_gNCC| z%{*04ViE!#>@hc1t5bb+NO>ncf@@Dv01K!NxH$3Eg1%)|wLyMDF8^d44lV!_Sr}iEWefOaL z8f?ud3Q%Sen39u|%00W<#!E=-RpGa+H8}{ulxVl4mwpjaU+%2pzmi{3HM)%8vb*~-M9rPUAfGCSos8GUXp02|o~0BTV2l#`>>aFV&_P$ejS;nGwSVP8 zMbOaG7<7eKD>c12VdGH;?2@q7535sa7MN*L@&!m?L`ASG%boY7(&L5imY#EQ$KrBB z4@_tfP5m50(T--qv1BJcD&aiH#b-QC>8#7Fx@3yXlonJI#aEIi=8&ChiVpc#N=5le zM*?rDIdcpawoc5kizv$GEjnveyrp3sY>+5_R5;>`>erS%JolimF=A^EIsAK zsPoVyyUHCgf0aYr&alx`<)eb6Be$m&`JYSuBu=p8j%QlNNp$-5C{b4#RubPb|CAIS zGE=9OFLP7?Hgc{?k45)84biT0k&-C6C%Q}aI~q<(7BL`C#<6HyxaR%!dFx7*o^laG z=!GBF^cwK$IA(sn9y6>60Rw{mYRYkp%$jH z*xQM~+bp)G$_RhtFPYx2HTsWk80+p(uqv9@I9)y{b$7NK53rYL$ezbmRjdXS?V}fj zWxX_feWoLFNm3MG7pMUuFPs$qrQWO9!l2B(SIuy2}S|lHNbHzoE+M2|Zxhjq9+Ws8c{*}x^VAib7SbxJ*Q3EnY5lgI9 z=U^f3IW6T=TWaVj+2N%K3<%Un;CF(wUp`TC&Y|ZjyFu6co^uqDDB#EP?DV5v_dw~E zIRK*BoY9y-G_ToU2V_XCX4nJ32~`czdjT!zwme zGgJ0nOk3U4@IE5JwtM}pwimLjk{ln^*4HMU%Fl4~n(cnsLB}Ja-jUM>xIB%aY;Nq8 z)Fp8dv1tkqKanv<68o@cN|%thj$+f;zGSO7H#b+eMAV8xH$hLggtt?O?;oYEgbq@= zV(u9bbd12^%;?nyk6&$GPI%|+<_mEpJGNfl*`!KV;VfmZWw{n{rnZ51?}FDh8we_L z8OI9nE31skDqJ5Oa_ybn7|5@ui>aC`s34p4ZEu6-s!%{uU45$Zd1=p$^^dZBh zu<*pDDPLW+c>iWO$&Z_*{VSQKg7=YEpS3PssPn1U!lSm6eZIho*{@&20e4Y_lRklKDTUCKI%o4Pc<|G^Xgu$J^Q|B87U;`c1zGwf^-zH*VQ^x+i^OUWE0yd z;{FJq)2w!%`x7yg@>uGFFf-XJl4H`YtUG%0slGKOlXV`q?RP>AEWg#x!b{0RicxGhS!3$p7 zij;{gm!_u@D4$Ox%>>bPtLJ> zwKtYz?T_DR1jN>DkkfGU^<#6sGz|~p*I{y`aZ>^Di#TC|Z!7j_O1=Wo8thuit?WxR zh9_S>kw^{V^|g}HRUF=dcq>?q(pHxw!8rx4dC6vbQVmIhmICF#zU!HkHpQ>9S%Uo( zMw{eC+`&pb=GZRou|3;Po1}m46H6NGd$t<2mQh}kaK-WFfmj_66_17BX0|j-E2fe3Jat}ijpc53 zJV$$;PC<5aW`{*^Z6e5##^`Ed#a0nwJDT#Qq~^e8^JTA=z^Kl>La|(UQ!bI@#ge{Dzz@61p-I)kc2?ZxFt^QQ}f%ldLjO*GPj(5)V9IyuUakJX=~GnTgZ4$5!3E=V#t`yOG4U z(gphZB6u2zsj=qNFLYShhg$}lNpO`P9xOSnO*$@@UdMYES*{jJVj|9z-}F^riksLK zbsU+4-{281P9e2UjY6tse^&a)WM1MFw;p#_dHhWI7p&U*9TR0zKdVuQed%6{otTsq z$f~S!;wg#Bd9kez=Br{m|66Wv z#g1xMup<0)H;c2ZO6su_ii&m8j&+jJz4iKnGZ&wxoQX|5a>v&_e#6WA!MB_4asTxLRGQCC5cI(em z%$ZfeqP>!*q5kU>a+BO&ln=4Jm>Ef(QE8o&RgLkk%2}4Tf}U%IFP&uS7}&|Q-)`5< z+e>;s#4cJ-z%&-^&!xsYx777Wt(wZY9(3(avmr|gRe4cD+a8&!LY`1^T?7x{E<=kdY9NYw>A;FtTvQ=Y&1M%lyZPl$ss1oY^Sl8we}n}Aob#6 zl4jERwnt9BlSoWb@3HxYgga(752Vu6Y)k4yk9u~Kw>cA5&LHcrvn1Y-HoIuFWg~}4 zEw4bR`mXZQIyOAzo)FYqg?$5W<;^+XX%Uz61{-L6@eP|lLH%|w?g=rFc;OvEW;^qh z&iYXGhVt(G-q<+_j}CTbPS_=K>RKN0&;dubh0NxJyDOHFF;<1k!{k#7b{|Qok9hac z;gHz}6>H6C6RnB`Tt#oaSrX0p-j-oRJ;_WvS-qS--P*8}V943RT6kou-G=A+7QPGQ z!ze^UGxtW3FC0$|(lY9^L!Lx^?Q8cny(rR`es5U;-xBhphF%_WNu|aO<+e9%6LuZq zt(0PoagJG<%hyuf;te}n+qIl_Ej;czWdc{LX^pS>77s9t*2b4s5dvP_!L^3cwlc)E!(!kGrg~FescVT zZCLeua3f4;d;Tk4iXzt}g}O@nlK3?_o91_~@UMIl?@77Qc$IAlLE95#Z=TES>2E%z zxUKpK{_HvGF;5%Q7n&vA?`{%8ohlYT_?(3A$cZSi)MvIJygXD}TS-3UwyUxGLGiJP znblO~G|*uA^|ac8E-w#}uBtg|s_~s&t>-g0X%zIZ@;o_wNMr_;{KDg^O=rg`fhDZu zFp(VKd1Edj%F zWHPl+)FGj%J1BO3bOHVfH^3d1F{)*PL&sRX`~(-Zy3&9UQX)Z;c51tvaI2E*E7!)q zcz|{vpK7bjxix(k&6=OEIBJC!9lTkUbgg?4-yE{9+pFS)$Ar@vrIf`D0Bnsed(Cf? zObt2CJ>BKOl>q8PyFO6w)+6Iz`LW%T5^R`U_NIW0r1dWv6OY=TVF?N=EfA(k(~7VBW(S;Tu5m4Lg8emDG-(mOSSs=M9Q&N8jc^Y4&9RqIsk(yO_P(mcCr}rCs%1MW1VBrn=0-oQN(Xj!k%iKV zb%ricBF3G4S1;+8lzg5PbZ|$Se$)I=PwiK=cDpHYdov2QO1_a-*dL4KUi|g&oh>(* zq$<`dQ^fat`+VW?m)?_KLn&mp^-@d=&7yGDt<=XwZZC=1scwxO2^RRI7n@g-1o8ps z)&+et_~)vr8aIF1VY1Qrq~Xe``KJrQSnAZ{CSq3yP;V*JC;mmCT6oRLSs7=GA?@6g zUooM}@tKtx(^|aKK8vbaHlUQqwE0}>j&~YlN3H#vKGm@u)xxS?n9XrOWUfCRa< z`20Fld2f&;gg7zpo{Adh+mqNntMc-D$N^yWZAZRI+u1T1zWHPxk{+?vcS1D>08>@6 zLhE@`gt1Y9mAK6Z4p|u(5I%EkfU7rKFSM=E4?VG9tI;a*@?6!ey{lzN5=Y-!$WFSe z&2dtO>^0@V4WRc#L&P%R(?@KfSblMS+N+?xUN$u3K4Ys%OmEh+tq}fnU}i>6YHM?< zlnL2gl~sF!j!Y4E;j3eIU-lfa`RsOL*Tt<%EFC0gPzoHfNWAfKFIKZN8}w~(Yi~=q z>=VNLO2|CjkxP}RkutxjV#4fWYR1KNrPYq5ha9Wl+u>ipsk*I(HS@iLnmGH9MFlTU zaFZ*KSR0px>o+pL7BbhB2EC1%PJ{67_ z#kY&#O4@P=OV#-79y_W>Gv2dxL*@G7%LksNSqgId9v;2xJ zrh8uR!F-eU$NMx@S*+sk=C~Dxr9Qn7TfWnTupuHKuQ$;gGiBcU>GF5sWx(~4IP3`f zWE;YFO*?jGwYh%C3X<>RKHC-DZ!*r;cIr}GLOno^3U4tFSSoJp%oHPiSa%nh=Zgn% z14+8v@ygy0>UgEN1bczD6wK45%M>psM)y^)IfG*>3ItX|TzV*0i%@>L(VN!zdKb8S?Qf7BhjNpziA zR}?={-eu>9JDcl*R=OP9B8N$IcCETXah9SUDhr{yrld{G;PnCWRsPD7!eOOFBTWUQ=LrA_~)mFf&!zJX!Oc-_=kT<}m|K52 z)M=G#;p;Rdb@~h5D{q^K;^fX-m5V}L%!wVC2iZ1uu401Ll}#rocTeK|7FAeBRhNdQ zCc2d^aQnQp=MpOmak60N$OgS}a;p(l9CL`o4r(e-nN}mQ?M&isv-P&d$!8|1D1I(3-z!wi zTgoo)*Mv`gC?~bm?S|@}I|m-E2yqPEvYybiD5azInexpK8?9q*$9Yy9-t%5jU8~ym zgZDx>!@ujQ=|HJnwp^wv-FdD{RtzO9SnyfB{mH_(c!jHL*$>0o-(h(eqe*ZwF6Lvu z{7rkk%PEqaA>o+f{H02tzZ@TWy&su?VNw43! z-X+rN`6llvpUms3ZiSt)JMeztB~>9{J8SPmYs&qohxdYFi!ra8KR$35Zp9oR)eFC4 zE;P31#3V)n`w$fZ|4X-|%MX`xZDM~gJyl2W;O$H25*=+1S#%|53>|LyH za@yh+;325%Gq3;J&a)?%7X%t@WXcWL*BaaR*7UEZad4I8iDt7^R_Fd`XeUo256;sAo2F!HcIQKk;h})QxEsPE5BcKc7WyerTchgKmrfRX z!x#H_%cL#B9TWAqkA4I$R^8{%do3Y*&(;WFmJ zU7Dih{t1<{($VtJRl9|&EB?|cJ)xse!;}>6mSO$o5XIx@V|AA8ZcoD88ZM?C*;{|f zZVmf94_l1OmaICt`2sTyG!$^UeTHx9YuUP!omj(r|7zpm5475|yXI=rR>>fteLI+| z)MoiGho0oEt=*J(;?VY0QzwCqw@cVm?d7Y!z0A@u#H?sCJ*ecvyhj& z-F77lO;SH^dmf?L>3i>?Z*U}Em4ZYV_CjgfvzYsRZ+1B!Uo6H6mbS<-FFL`ytqvb& zE7+)2ahv-~dz(Hs+f})z{*4|{)b=2!RZK;PWwOnO=hG7xG`JU5>bAvUbdYd_CjvtHBHgtGdlO+s^9ca^Bv3`t@VRX2_AD$Ckg36OcQRF zXD6QtGfHdw*hx~V(MV-;;ZZF#dJ-piEF+s27z4X1qi5$!o~xBnvf=uopcn7ftfsZc zy@(PuOk`4GL_n(H9(E2)VUjqRCk9kR?w)v@xO6Jm_Mx})&WGEl=GS0#)0FAq^J*o! zAClhvoTsNP*-b~rN{8Yym3g{01}Ep^^Omf=SKqvN?{Q*C4HNNAcrowIa^mf+3PRy! z*_G-|3i8a;+q;iP@~Of_$(vtFkB8yOyWt2*K)vAn9El>=D;A$CEx6b*XF@4y_6M+2 zpeW`RHoI_p(B{%(&jTHI->hmNmZjHUj<@;7w0mx3&koy!2$@cfX{sN19Y}euYJFn& z1?)+?HCkD0MRI$~uB2UWri})0bru_B;klFdwsLc!ne4YUE;t41JqfG# zZJq6%vbsdx!wYeE<~?>o4V`A3?lN%MnKQ`z=uUivQN^vzJ|C;sdQ37Qn?;lpzg})y z)_2~rUdH}zNwX;Tp0tJ78+&I=IwOQ-fl30R79O8@?Ub8IIA(6I`yHn%lARVL`%b8+ z4$8D-|MZZWxc_)vu6@VZN!HsI$*2NOV&uMxBNzIbRgy%ob_ zhwEH{J9r$!dEix9XM7n&c{S(h>nGm?el;gaX0@|QnzFD@bne`el^CO$yXC?BDJ|Qg z+y$GRoR`?ST1z^e*>;!IS@5Ovb7*RlN>BV_UC!7E_F;N#ky%1J{+iixp(dUJj93aK zzHNN>R-oN7>kykHClPnoPTIj7zc6KM(Pnlb(|s??)SMb)4!sMHU^-ntJwY5Big7xv zb1Ew`Xj;|D2kzGja*C$eS44(d&RMU~c_Y14V9_TLTz0J#uHlsx`S6{nhsA0dWZ#cG zJ?`fO50E>*X4TQLv#nl%3GOk*UkAgt=IY+u0LNXqeln3Z zv$~&Li`ZJOKkFuS)dJRA>)b_Da%Q~axwA_8zNK{BH{#}#m}zGcuckz}riDE-z_Ms> zR8-EqAMcfyGJCtvTpaUVQtajhUS%c@Yj}&6Zz;-M7MZzqv3kA7{SuW$oW#=0az2wQ zg-WG@Vb4|D`pl~Il54N7Hmsauc_ne-a!o5#j3WaBBh@Wuefb!QJIOn5;d)%A#s+5% zuD$H=VNux9bE-}1&bcYGZ+>1Fo;3Z@e&zX^n!?JK*adSbONm$XW9z;Q^L>9U!}Toj2WdafJ%oL#h|yWWwyAGxzfrAWdDTtaKl zK4`5tDpPg5>z$MNv=X0LZ0d6l%D{(D8oT@+w0?ce$DZ6pv>{1&Ok67Ix1 zH}3=IEhPJEhItCC8E=`T`N5(k?G=B4+xzZ?<4!~ ze~z6Wk9!CHTI(0rLJ4{JU?E-puc;xusR?>G?;4vt;q~iI9=kDL=z0Rr%O$vU`30X$ zDZRFyZ`(omOy@u|i6h;wtJlP;+}$|Ak|k2dea7n?U1*$T!sXqqOjq^NxLPMmk~&qI zYg0W?yK8T(6+Ea+$YyspKK?kP$+B`~t3^Pib_`!6xCs32!i@pqXfFV6PmBIR<-QW= zN8L{pt0Vap0x`Gzn#E@zh@H)0FfVfA_Iu4fjYZ+umO1LXIbVc$pY+E234u)ttcrl$ z>s92z4vT%n6cMb>=XT6;l0+9e(|CZG)$@C7t7Z7Ez@a)h)!hyuV&B5K%%)P5?Lk|C zZZSVzdXp{@OXSP0hoU-gF8s8Um(#xzjP2Vem zec#-^JqTa&Y#QJ>-FBxd7tf`XB6e^JPUgagB8iBSEps;92KG`!#mvVcPQ5yNC-GEG zTiHEDYfH+0O15}r^+ z#jxj=@x8iNHWALe!P3R67TwmhItn**0JwnzSV2O&KE8KcT+0hWH^OPD1pwiuyx=b@ zNf5Jh0{9X)8;~Es)$t@%(3!OnbY+`@?i{mGX7Yy}8T_*0a6g;kaFPq;*=px5EhO{Cp%1kI<0?*|h8v!6WnO3cCJRF2-CRrU3JiLJnj@6;L)!0kWYAc_}F{2P))3HmCrz zQ&N&gE70;`!6*eJ4^1IR{f6j4(-l&X!tjHxkbHA^Zhrnhr9g{exN|xrS`5Pq=#Xf& zG%P=#ra-TyVFfgW%cZo5OSIwFL9WtXAlFOa+ubmI5t*3=g#Y zF%;70p5;{ZeFL}&}yOY1N1*Q;*<(kTB!7vM$QokF)yr2FlIU@$Ph58$Bz z0J?xQG=MlS4L6jA22eS42g|9*9pX@$#*sUeM(z+t?hr@r5J&D1rx}2pW&m*_`VDCW zUYY@v-;bAO0HqoAgbbiGGC<=ryf96}3pouhy3XJrX+!!u*O_>Si38V{uJmQ&USptX zKp#l(?>%^7;2%h(q@YWS#9;a!JhKlkR#Vd)ERILlgu!Hr@jA@V;sk4BJ-H#p*4EqC zDGjC*tl=@3Oi6)Bn^QwFpul18fpkbpg0+peH$xyPBqb%`$OUhPKyWb32o7clB*9Z< zN=i~NLjavrLtwgJ01bufP+>p-jR2I95|TpmKpQL2!oV>g(4RvS2pK4*ou%m(h6r3A zX#s&`9LU1ZG&;{CkOK!4fLDTnBys`M!vuz>Q&9OZ0hGQl!~!jSDg|~s*w52opC{sB ze|Cf2luD(*G13LcOAGA!s2FjSK8&IE5#W%J25w!vM0^VyQM!t)inj&RTiJ!wXzFgz z3^IqzB7I0L$llljsGq})thBy9UOyjtFO_*hYM_sgcMk>44jeH0V1FDyELc{S1F-;A zS;T^k^~4biG&V*Irq}O;e}j$$+E_#G?HKIn05iP3j|87TkGK~SqG!-KBg5+mN(aLm z8ybhIM`%C19UX$H$KY6JgXbY$0AT%rEpHC;u`rQ$Y=rxUdsc5*Kvc8jaYaO$^)cI6){P6K0r)I6DY4Wr4&B zLQUBraey#0HV|&c4v7PVo3n$zHj99(TZO^3?Ly%C4nYvJTL9eLBLHsM3WKKD>5!B` zQ=BsR3aR6PD(Fa>327E2HAu5TM~Wusc!)>~(gM)+3~m;92Jd;FnSib=M5d6;;5{%R zb4V7DEJ0V!CP-F*oU?gkc>ksUtAYP&V4ND5J>J2^jt*vcFflQWCrB&fLdT%O59PVJ zhid#toR=FNgD!q3&r8#wEBr`!wzvQu5zX?Q>nlSJ4i@WC*CN*-xU66F^V5crWevQ9gsq$I@z1o(a=k7LL~ z7m_~`o;_Ozha1$8Q}{WBehvAlO4EL60y5}8GDrZ< zXh&F}71JbW2A~8KfEWj&UWV#4+Z4p`b{uAj4&WC zha`}X@3~+Iz^WRlOHU&KngK>#j}+_o@LdBC1H-`gT+krWX3-;!)6?{FBp~%20a}FL zFP9%Emqcwa#(`=G>BBZ0qZDQhmZKJg_g8<=bBFKWr!dyg(YkpE+|R*SGpDVU!+VlU zFC54^DLv}`qa%49T>nNiA9Q7Ips#!Xx90tCU2gvK`(F+GPcL=J^>No{)~we#o@&mUb6c$ zCc*<|NJBk-#+{j9xkQ&ujB zI~`#kN~7W!f*-}wkG~Ld!JqZ@tK}eeSnsS5J1fMFXm|`LJx&}5`@dK3W^7#Wnm+_P zBZkp&j1fa2Y=eIjJ0}gh85jt43kaIXXv?xmo@eHrka!Z|vQv12HN#+!I5E z`(fbuW>gFiJL|uXJ!vKt#z3e3HlVdboH7;e#i3(2<)Fg-I@BR!qY#eof3MFZ&*Y@l zI|KJf&ge@p2Dq09Vu$$Qxb7!}{m-iRk@!)%KL)txi3;~Z4Pb}u@GsW;ELiWeG9V51 znX#}B&4Y2E7-H=OpNE@q{%hFLxwIpBF2t{vPREa8_{linXT;#1vMRWjOzLOP$-hf( z>=?$0;~~PnkqY;~K{EM6Vo-T(0K{A0}VUGmu*hR z{tw3hvBN%N3G3Yw`X5Te+F{J`(3w1s3-+1EbnFQKcrgrX1Jqvs@ADGe%M0s$EbK$$ zK)=y=upBc6SjGYAACCcI=Y*6Fi8_jgwZlLxD26fnQfJmb8^gHRN5(TemhX@0e=vr> zg`W}6U>x6VhoA3DqsGGD9uL1DhB3!OXO=k}59TqD@(0Nb{)Ut_luTioK_>7wjc!5C zIr@w}b`Fez3)0wQfKl&bae7;PcTA7%?f2xucM0G)wt_KO!Ewx>F~;=BI0j=Fb4>pp zv}0R^xM4eti~+^+gE$6b81p(kwzuDti(-K9bc|?+pJEl@H+jSYuxZQV8rl8 zjp@M{#%qItIUFN~KcO9Hed*`$5A-2~pAo~K&<-Q+`9`$CK>rzqAI4w~$F%vs9s{~x zg4BP%Gy*@m?;D6=SRX?888Q6peF@_4Z->8wAH~Cn!R$|Hhq2cIzFYqT_+cDourHbY z0qroxJnrZ4Gh+Ay+F`_c%+KRT>y3qw{)89?=hJ@=KO=@ep)aBJ$c!JHfBMJpsP*3G za7|)VJJ8B;4?n{~ldJF7%jmb`-ftIvNd~ekoufG(`K(3=LNc;HBY& z(lp#q8XAD#cIf}k49zX_i`*fO+#!zKA&%T3j@%)R+#yag067CU%yUEe47>wzGU8^` z1EXFT^@I!{J!F8!X?S6ph8J=gUi5tl93*W>7}_uR<2N2~e}FaG?}KPyugQ=-OGEZs z!GBoyYY+H*ANn4?Z)X4l+7H%`17i5~zRlRIX?t)6_eu=g2Q`3WBhxSUeea+M-S?RL zX9oBGKn%a!H+*hx4d2(I!gsi+@SQK%<{X22M~2tMulJoa)0*+z9=-YO+;DFEm5eE1U9b^B(Z}2^9!Qk`!A$wUE z7$Ar5?NRg2&G!AZqnmE64eh^Anss3i!{}%6@Et+4rr!=}!SBF8eZ2*J3ujCWbl;3; z48H~goPSv(8X61fKKdpP!Z7$88NL^Z?j`!^*I?-P4X^pMxyWz~@$(UeAcTSDd(`vO z{~rc;9|GfMJcApU3k}22a!&)k4{CU!e_ny^Y3cO;tOvOMKEyWz!vG(Kp*;hB?d|R3`2X~=5a6#^o5@qn?J-bI8Ppip{-yG z!k|VcGsq!jF~}7DMr49Wap-s&>o=U^T0!Lcy}!(bhtYsPQy z4|EJe{12QL#=c(suQ89Mhw9<`bui%nx7Nep`C&*M3~vMEACmcRYYRGtANq$F%zh&V zc)cEVeHz*Z1N)L7k-(k3np#{GcDh2Q@ya0YHl*n7fl*ZPAsbU-a94MYYtA#&!c`xGIaV;yzsmrjfieTEtqB_WgZp2*NplHx=$O{M~2#i_vJ{ps-NgK zQsxKK_CBM2PP_je+Xft`(vYfXXgIUr{=PA=7a8`2EHk)Ym2QKIforz# tySWtj{oF3N9@_;i*Fv5S)9x^z=nlWP>jpp-9)52ZmLVA=i*%6g{{fxOO~wEK literal 0 HcmV?d00001 diff --git a/flutter_inappwebview/example/windows/runner/runner.exe.manifest b/flutter_inappwebview/example/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..a42ea768 --- /dev/null +++ b/flutter_inappwebview/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/flutter_inappwebview/example/windows/runner/utils.cpp b/flutter_inappwebview/example/windows/runner/utils.cpp new file mode 100644 index 00000000..b2b08734 --- /dev/null +++ b/flutter_inappwebview/example/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length <= 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/flutter_inappwebview/example/windows/runner/utils.h b/flutter_inappwebview/example/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/flutter_inappwebview/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/flutter_inappwebview/example/windows/runner/win32_window.cpp b/flutter_inappwebview/example/windows/runner/win32_window.cpp new file mode 100644 index 00000000..60608d0f --- /dev/null +++ b/flutter_inappwebview/example/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/flutter_inappwebview/example/windows/runner/win32_window.h b/flutter_inappwebview/example/windows/runner/win32_window.h new file mode 100644 index 00000000..e901dde6 --- /dev/null +++ b/flutter_inappwebview/example/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/flutter_inappwebview_android/example/pubspec.lock b/flutter_inappwebview_android/example/pubspec.lock index 42f7e459..f3b10fd2 100644 --- a/flutter_inappwebview_android/example/pubspec.lock +++ b/flutter_inappwebview_android/example/pubspec.lock @@ -81,7 +81,7 @@ packages: path: ".." relative: true source: path - version: "1.0.11" + version: "1.0.12" flutter_inappwebview_internal_annotations: dependency: transitive description: diff --git a/flutter_inappwebview_windows/.gitignore b/flutter_inappwebview_windows/.gitignore new file mode 100644 index 00000000..ac5aa989 --- /dev/null +++ b/flutter_inappwebview_windows/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/flutter_inappwebview_windows/.metadata b/flutter_inappwebview_windows/.metadata new file mode 100644 index 00000000..f0767e5b --- /dev/null +++ b/flutter_inappwebview_windows/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: windows + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/flutter_inappwebview_windows/CHANGELOG.md b/flutter_inappwebview_windows/CHANGELOG.md new file mode 100644 index 00000000..41cc7d81 --- /dev/null +++ b/flutter_inappwebview_windows/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/flutter_inappwebview_windows/LICENSE b/flutter_inappwebview_windows/LICENSE new file mode 100644 index 00000000..ba75c69f --- /dev/null +++ b/flutter_inappwebview_windows/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/flutter_inappwebview_windows/README.md b/flutter_inappwebview_windows/README.md new file mode 100644 index 00000000..c410a93e --- /dev/null +++ b/flutter_inappwebview_windows/README.md @@ -0,0 +1,15 @@ +# flutter_inappwebview_windows + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/flutter_inappwebview_windows/analysis_options.yaml b/flutter_inappwebview_windows/analysis_options.yaml new file mode 100644 index 00000000..84964c30 --- /dev/null +++ b/flutter_inappwebview_windows/analysis_options.yaml @@ -0,0 +1,15 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + constant_identifier_names: ignore + deprecated_member_use_from_same_package: ignore + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options +analyzer: + errors: + deprecated_member_use: ignore + deprecated_member_use_from_same_package: ignore + unnecessary_cast: ignore + unnecessary_import: ignore diff --git a/flutter_inappwebview_windows/example/.gitignore b/flutter_inappwebview_windows/example/.gitignore new file mode 100644 index 00000000..29a3a501 --- /dev/null +++ b/flutter_inappwebview_windows/example/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/flutter_inappwebview_windows/example/README.md b/flutter_inappwebview_windows/example/README.md new file mode 100644 index 00000000..a07c91fa --- /dev/null +++ b/flutter_inappwebview_windows/example/README.md @@ -0,0 +1,16 @@ +# flutter_inappwebview_windows_example + +Demonstrates how to use the flutter_inappwebview_windows plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/flutter_inappwebview_windows/example/analysis_options.yaml b/flutter_inappwebview_windows/example/analysis_options.yaml new file mode 100644 index 00000000..0d290213 --- /dev/null +++ b/flutter_inappwebview_windows/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/flutter_inappwebview_windows/example/integration_test/plugin_integration_test.dart b/flutter_inappwebview_windows/example/integration_test/plugin_integration_test.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_windows/example/lib/main.dart b/flutter_inappwebview_windows/example/lib/main.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_windows/example/pubspec.lock b/flutter_inappwebview_windows/example/pubspec.lock new file mode 100644 index 00000000..d24b5eee --- /dev/null +++ b/flutter_inappwebview_windows/example/pubspec.lock @@ -0,0 +1,283 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" + source: hosted + version: "1.0.6" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "5f80fd30e208ddded7dbbcd0d569e7995f9f63d45ea3f548d8dd4c0b473fb4c8" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: "545fd4c25a07d2775f7d5af05a979b2cac4fbf79393b0a7f5d33ba39ba4f6187" + url: "https://pub.dev" + source: hosted + version: "1.0.10" + flutter_inappwebview_windows: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "1.0.11" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + meta: + dependency: transitive + description: + name: meta + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + url: "https://pub.dev" + source: hosted + version: "1.10.0" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + platform: + dependency: transitive + description: + name: platform + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 + url: "https://pub.dev" + source: hosted + version: "2.1.7" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + url: "https://pub.dev" + source: hosted + version: "11.10.0" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" + url: "https://pub.dev" + source: hosted + version: "3.0.2" +sdks: + dart: ">=3.2.3 <4.0.0" + flutter: ">=3.0.0" diff --git a/flutter_inappwebview_windows/example/pubspec.yaml b/flutter_inappwebview_windows/example/pubspec.yaml new file mode 100644 index 00000000..fc697f2f --- /dev/null +++ b/flutter_inappwebview_windows/example/pubspec.yaml @@ -0,0 +1,85 @@ +name: flutter_inappwebview_windows_example +description: "Demonstrates how to use the flutter_inappwebview_windows plugin." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=3.2.3 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + flutter_inappwebview_windows: + # When depending on this package from a real application you should use: + # flutter_inappwebview_windows: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + integration_test: + sdk: flutter + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter_inappwebview_windows/example/test/widget_test.dart b/flutter_inappwebview_windows/example/test/widget_test.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_windows/example/windows/.gitignore b/flutter_inappwebview_windows/example/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/flutter_inappwebview_windows/example/windows/CMakeLists.txt b/flutter_inappwebview_windows/example/windows/CMakeLists.txt new file mode 100644 index 00000000..3241da61 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/CMakeLists.txt @@ -0,0 +1,110 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(flutter_inappwebview_windows_example LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "flutter_inappwebview_windows_example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Enable the test target. +set(include_flutter_inappwebview_windows_tests TRUE) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/flutter_inappwebview_windows/example/windows/flutter/CMakeLists.txt b/flutter_inappwebview_windows/example/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..903f4899 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/flutter_inappwebview_windows/example/windows/flutter/generated_plugin_registrant.cc b/flutter_inappwebview_windows/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..3b4ee903 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi")); +} diff --git a/flutter_inappwebview_windows/example/windows/flutter/generated_plugin_registrant.h b/flutter_inappwebview_windows/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/flutter_inappwebview_windows/example/windows/flutter/generated_plugins.cmake b/flutter_inappwebview_windows/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..61c79a21 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + flutter_inappwebview_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/flutter_inappwebview_windows/example/windows/runner/CMakeLists.txt b/flutter_inappwebview_windows/example/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..394917c0 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/flutter_inappwebview_windows/example/windows/runner/Runner.rc b/flutter_inappwebview_windows/example/windows/runner/Runner.rc new file mode 100644 index 00000000..1815f6d8 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.pichillilorenzo" "\0" + VALUE "FileDescription", "flutter_inappwebview_windows_example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "flutter_inappwebview_windows_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2023 com.pichillilorenzo. All rights reserved." "\0" + VALUE "OriginalFilename", "flutter_inappwebview_windows_example.exe" "\0" + VALUE "ProductName", "flutter_inappwebview_windows_example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/flutter_inappwebview_windows/example/windows/runner/flutter_window.cpp b/flutter_inappwebview_windows/example/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..955ee303 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/flutter_inappwebview_windows/example/windows/runner/flutter_window.h b/flutter_inappwebview_windows/example/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/flutter_inappwebview_windows/example/windows/runner/main.cpp b/flutter_inappwebview_windows/example/windows/runner/main.cpp new file mode 100644 index 00000000..52028bc4 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"flutter_inappwebview_windows_example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/flutter_inappwebview_windows/example/windows/runner/resource.h b/flutter_inappwebview_windows/example/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/flutter_inappwebview_windows/example/windows/runner/resources/app_icon.ico b/flutter_inappwebview_windows/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 zcmeHQc|26z|35SKE&G-*mXah&B~fFkXr)DEO&hIfqby^T&>|8^_Ub8Vp#`BLl3lbZ zvPO!8k!2X>cg~Elr=IVxo~J*a`+9wR=A83c-k-DFd(XM&UI1VKCqM@V;DDtJ09WB} zRaHKiW(GT00brH|0EeTeKVbpbGZg?nK6-j827q-+NFM34gXjqWxJ*a#{b_apGN<-L_m3#8Z26atkEn& ze87Bvv^6vVmM+p+cQ~{u%=NJF>#(d;8{7Q{^rWKWNtf14H}>#&y7$lqmY6xmZryI& z($uy?c5-+cPnt2%)R&(KIWEXww>Cnz{OUpT>W$CbO$h1= z#4BPMkFG1Y)x}Ui+WXr?Z!w!t_hjRq8qTaWpu}FH{MsHlU{>;08goVLm{V<&`itk~ zE_Ys=D(hjiy+5=?=$HGii=Y5)jMe9|wWoD_K07(}edAxh`~LBorOJ!Cf@f{_gNCC| z%{*04ViE!#>@hc1t5bb+NO>ncf@@Dv01K!NxH$3Eg1%)|wLyMDF8^d44lV!_Sr}iEWefOaL z8f?ud3Q%Sen39u|%00W<#!E=-RpGa+H8}{ulxVl4mwpjaU+%2pzmi{3HM)%8vb*~-M9rPUAfGCSos8GUXp02|o~0BTV2l#`>>aFV&_P$ejS;nGwSVP8 zMbOaG7<7eKD>c12VdGH;?2@q7535sa7MN*L@&!m?L`ASG%boY7(&L5imY#EQ$KrBB z4@_tfP5m50(T--qv1BJcD&aiH#b-QC>8#7Fx@3yXlonJI#aEIi=8&ChiVpc#N=5le zM*?rDIdcpawoc5kizv$GEjnveyrp3sY>+5_R5;>`>erS%JolimF=A^EIsAK zsPoVyyUHCgf0aYr&alx`<)eb6Be$m&`JYSuBu=p8j%QlNNp$-5C{b4#RubPb|CAIS zGE=9OFLP7?Hgc{?k45)84biT0k&-C6C%Q}aI~q<(7BL`C#<6HyxaR%!dFx7*o^laG z=!GBF^cwK$IA(sn9y6>60Rw{mYRYkp%$jH z*xQM~+bp)G$_RhtFPYx2HTsWk80+p(uqv9@I9)y{b$7NK53rYL$ezbmRjdXS?V}fj zWxX_feWoLFNm3MG7pMUuFPs$qrQWO9!l2B(SIuy2}S|lHNbHzoE+M2|Zxhjq9+Ws8c{*}x^VAib7SbxJ*Q3EnY5lgI9 z=U^f3IW6T=TWaVj+2N%K3<%Un;CF(wUp`TC&Y|ZjyFu6co^uqDDB#EP?DV5v_dw~E zIRK*BoY9y-G_ToU2V_XCX4nJ32~`czdjT!zwme zGgJ0nOk3U4@IE5JwtM}pwimLjk{ln^*4HMU%Fl4~n(cnsLB}Ja-jUM>xIB%aY;Nq8 z)Fp8dv1tkqKanv<68o@cN|%thj$+f;zGSO7H#b+eMAV8xH$hLggtt?O?;oYEgbq@= zV(u9bbd12^%;?nyk6&$GPI%|+<_mEpJGNfl*`!KV;VfmZWw{n{rnZ51?}FDh8we_L z8OI9nE31skDqJ5Oa_ybn7|5@ui>aC`s34p4ZEu6-s!%{uU45$Zd1=p$^^dZBh zu<*pDDPLW+c>iWO$&Z_*{VSQKg7=YEpS3PssPn1U!lSm6eZIho*{@&20e4Y_lRklKDTUCKI%o4Pc<|G^Xgu$J^Q|B87U;`c1zGwf^-zH*VQ^x+i^OUWE0yd z;{FJq)2w!%`x7yg@>uGFFf-XJl4H`YtUG%0slGKOlXV`q?RP>AEWg#x!b{0RicxGhS!3$p7 zij;{gm!_u@D4$Ox%>>bPtLJ> zwKtYz?T_DR1jN>DkkfGU^<#6sGz|~p*I{y`aZ>^Di#TC|Z!7j_O1=Wo8thuit?WxR zh9_S>kw^{V^|g}HRUF=dcq>?q(pHxw!8rx4dC6vbQVmIhmICF#zU!HkHpQ>9S%Uo( zMw{eC+`&pb=GZRou|3;Po1}m46H6NGd$t<2mQh}kaK-WFfmj_66_17BX0|j-E2fe3Jat}ijpc53 zJV$$;PC<5aW`{*^Z6e5##^`Ed#a0nwJDT#Qq~^e8^JTA=z^Kl>La|(UQ!bI@#ge{Dzz@61p-I)kc2?ZxFt^QQ}f%ldLjO*GPj(5)V9IyuUakJX=~GnTgZ4$5!3E=V#t`yOG4U z(gphZB6u2zsj=qNFLYShhg$}lNpO`P9xOSnO*$@@UdMYES*{jJVj|9z-}F^riksLK zbsU+4-{281P9e2UjY6tse^&a)WM1MFw;p#_dHhWI7p&U*9TR0zKdVuQed%6{otTsq z$f~S!;wg#Bd9kez=Br{m|66Wv z#g1xMup<0)H;c2ZO6su_ii&m8j&+jJz4iKnGZ&wxoQX|5a>v&_e#6WA!MB_4asTxLRGQCC5cI(em z%$ZfeqP>!*q5kU>a+BO&ln=4Jm>Ef(QE8o&RgLkk%2}4Tf}U%IFP&uS7}&|Q-)`5< z+e>;s#4cJ-z%&-^&!xsYx777Wt(wZY9(3(avmr|gRe4cD+a8&!LY`1^T?7x{E<=kdY9NYw>A;FtTvQ=Y&1M%lyZPl$ss1oY^Sl8we}n}Aob#6 zl4jERwnt9BlSoWb@3HxYgga(752Vu6Y)k4yk9u~Kw>cA5&LHcrvn1Y-HoIuFWg~}4 zEw4bR`mXZQIyOAzo)FYqg?$5W<;^+XX%Uz61{-L6@eP|lLH%|w?g=rFc;OvEW;^qh z&iYXGhVt(G-q<+_j}CTbPS_=K>RKN0&;dubh0NxJyDOHFF;<1k!{k#7b{|Qok9hac z;gHz}6>H6C6RnB`Tt#oaSrX0p-j-oRJ;_WvS-qS--P*8}V943RT6kou-G=A+7QPGQ z!ze^UGxtW3FC0$|(lY9^L!Lx^?Q8cny(rR`es5U;-xBhphF%_WNu|aO<+e9%6LuZq zt(0PoagJG<%hyuf;te}n+qIl_Ej;czWdc{LX^pS>77s9t*2b4s5dvP_!L^3cwlc)E!(!kGrg~FescVT zZCLeua3f4;d;Tk4iXzt}g}O@nlK3?_o91_~@UMIl?@77Qc$IAlLE95#Z=TES>2E%z zxUKpK{_HvGF;5%Q7n&vA?`{%8ohlYT_?(3A$cZSi)MvIJygXD}TS-3UwyUxGLGiJP znblO~G|*uA^|ac8E-w#}uBtg|s_~s&t>-g0X%zIZ@;o_wNMr_;{KDg^O=rg`fhDZu zFp(VKd1Edj%F zWHPl+)FGj%J1BO3bOHVfH^3d1F{)*PL&sRX`~(-Zy3&9UQX)Z;c51tvaI2E*E7!)q zcz|{vpK7bjxix(k&6=OEIBJC!9lTkUbgg?4-yE{9+pFS)$Ar@vrIf`D0Bnsed(Cf? zObt2CJ>BKOl>q8PyFO6w)+6Iz`LW%T5^R`U_NIW0r1dWv6OY=TVF?N=EfA(k(~7VBW(S;Tu5m4Lg8emDG-(mOSSs=M9Q&N8jc^Y4&9RqIsk(yO_P(mcCr}rCs%1MW1VBrn=0-oQN(Xj!k%iKV zb%ricBF3G4S1;+8lzg5PbZ|$Se$)I=PwiK=cDpHYdov2QO1_a-*dL4KUi|g&oh>(* zq$<`dQ^fat`+VW?m)?_KLn&mp^-@d=&7yGDt<=XwZZC=1scwxO2^RRI7n@g-1o8ps z)&+et_~)vr8aIF1VY1Qrq~Xe``KJrQSnAZ{CSq3yP;V*JC;mmCT6oRLSs7=GA?@6g zUooM}@tKtx(^|aKK8vbaHlUQqwE0}>j&~YlN3H#vKGm@u)xxS?n9XrOWUfCRa< z`20Fld2f&;gg7zpo{Adh+mqNntMc-D$N^yWZAZRI+u1T1zWHPxk{+?vcS1D>08>@6 zLhE@`gt1Y9mAK6Z4p|u(5I%EkfU7rKFSM=E4?VG9tI;a*@?6!ey{lzN5=Y-!$WFSe z&2dtO>^0@V4WRc#L&P%R(?@KfSblMS+N+?xUN$u3K4Ys%OmEh+tq}fnU}i>6YHM?< zlnL2gl~sF!j!Y4E;j3eIU-lfa`RsOL*Tt<%EFC0gPzoHfNWAfKFIKZN8}w~(Yi~=q z>=VNLO2|CjkxP}RkutxjV#4fWYR1KNrPYq5ha9Wl+u>ipsk*I(HS@iLnmGH9MFlTU zaFZ*KSR0px>o+pL7BbhB2EC1%PJ{67_ z#kY&#O4@P=OV#-79y_W>Gv2dxL*@G7%LksNSqgId9v;2xJ zrh8uR!F-eU$NMx@S*+sk=C~Dxr9Qn7TfWnTupuHKuQ$;gGiBcU>GF5sWx(~4IP3`f zWE;YFO*?jGwYh%C3X<>RKHC-DZ!*r;cIr}GLOno^3U4tFSSoJp%oHPiSa%nh=Zgn% z14+8v@ygy0>UgEN1bczD6wK45%M>psM)y^)IfG*>3ItX|TzV*0i%@>L(VN!zdKb8S?Qf7BhjNpziA zR}?={-eu>9JDcl*R=OP9B8N$IcCETXah9SUDhr{yrld{G;PnCWRsPD7!eOOFBTWUQ=LrA_~)mFf&!zJX!Oc-_=kT<}m|K52 z)M=G#;p;Rdb@~h5D{q^K;^fX-m5V}L%!wVC2iZ1uu401Ll}#rocTeK|7FAeBRhNdQ zCc2d^aQnQp=MpOmak60N$OgS}a;p(l9CL`o4r(e-nN}mQ?M&isv-P&d$!8|1D1I(3-z!wi zTgoo)*Mv`gC?~bm?S|@}I|m-E2yqPEvYybiD5azInexpK8?9q*$9Yy9-t%5jU8~ym zgZDx>!@ujQ=|HJnwp^wv-FdD{RtzO9SnyfB{mH_(c!jHL*$>0o-(h(eqe*ZwF6Lvu z{7rkk%PEqaA>o+f{H02tzZ@TWy&su?VNw43! z-X+rN`6llvpUms3ZiSt)JMeztB~>9{J8SPmYs&qohxdYFi!ra8KR$35Zp9oR)eFC4 zE;P31#3V)n`w$fZ|4X-|%MX`xZDM~gJyl2W;O$H25*=+1S#%|53>|LyH za@yh+;325%Gq3;J&a)?%7X%t@WXcWL*BaaR*7UEZad4I8iDt7^R_Fd`XeUo256;sAo2F!HcIQKk;h})QxEsPE5BcKc7WyerTchgKmrfRX z!x#H_%cL#B9TWAqkA4I$R^8{%do3Y*&(;WFmJ zU7Dih{t1<{($VtJRl9|&EB?|cJ)xse!;}>6mSO$o5XIx@V|AA8ZcoD88ZM?C*;{|f zZVmf94_l1OmaICt`2sTyG!$^UeTHx9YuUP!omj(r|7zpm5475|yXI=rR>>fteLI+| z)MoiGho0oEt=*J(;?VY0QzwCqw@cVm?d7Y!z0A@u#H?sCJ*ecvyhj& z-F77lO;SH^dmf?L>3i>?Z*U}Em4ZYV_CjgfvzYsRZ+1B!Uo6H6mbS<-FFL`ytqvb& zE7+)2ahv-~dz(Hs+f})z{*4|{)b=2!RZK;PWwOnO=hG7xG`JU5>bAvUbdYd_CjvtHBHgtGdlO+s^9ca^Bv3`t@VRX2_AD$Ckg36OcQRF zXD6QtGfHdw*hx~V(MV-;;ZZF#dJ-piEF+s27z4X1qi5$!o~xBnvf=uopcn7ftfsZc zy@(PuOk`4GL_n(H9(E2)VUjqRCk9kR?w)v@xO6Jm_Mx})&WGEl=GS0#)0FAq^J*o! zAClhvoTsNP*-b~rN{8Yym3g{01}Ep^^Omf=SKqvN?{Q*C4HNNAcrowIa^mf+3PRy! z*_G-|3i8a;+q;iP@~Of_$(vtFkB8yOyWt2*K)vAn9El>=D;A$CEx6b*XF@4y_6M+2 zpeW`RHoI_p(B{%(&jTHI->hmNmZjHUj<@;7w0mx3&koy!2$@cfX{sN19Y}euYJFn& z1?)+?HCkD0MRI$~uB2UWri})0bru_B;klFdwsLc!ne4YUE;t41JqfG# zZJq6%vbsdx!wYeE<~?>o4V`A3?lN%MnKQ`z=uUivQN^vzJ|C;sdQ37Qn?;lpzg})y z)_2~rUdH}zNwX;Tp0tJ78+&I=IwOQ-fl30R79O8@?Ub8IIA(6I`yHn%lARVL`%b8+ z4$8D-|MZZWxc_)vu6@VZN!HsI$*2NOV&uMxBNzIbRgy%ob_ zhwEH{J9r$!dEix9XM7n&c{S(h>nGm?el;gaX0@|QnzFD@bne`el^CO$yXC?BDJ|Qg z+y$GRoR`?ST1z^e*>;!IS@5Ovb7*RlN>BV_UC!7E_F;N#ky%1J{+iixp(dUJj93aK zzHNN>R-oN7>kykHClPnoPTIj7zc6KM(Pnlb(|s??)SMb)4!sMHU^-ntJwY5Big7xv zb1Ew`Xj;|D2kzGja*C$eS44(d&RMU~c_Y14V9_TLTz0J#uHlsx`S6{nhsA0dWZ#cG zJ?`fO50E>*X4TQLv#nl%3GOk*UkAgt=IY+u0LNXqeln3Z zv$~&Li`ZJOKkFuS)dJRA>)b_Da%Q~axwA_8zNK{BH{#}#m}zGcuckz}riDE-z_Ms> zR8-EqAMcfyGJCtvTpaUVQtajhUS%c@Yj}&6Zz;-M7MZzqv3kA7{SuW$oW#=0az2wQ zg-WG@Vb4|D`pl~Il54N7Hmsauc_ne-a!o5#j3WaBBh@Wuefb!QJIOn5;d)%A#s+5% zuD$H=VNux9bE-}1&bcYGZ+>1Fo;3Z@e&zX^n!?JK*adSbONm$XW9z;Q^L>9U!}Toj2WdafJ%oL#h|yWWwyAGxzfrAWdDTtaKl zK4`5tDpPg5>z$MNv=X0LZ0d6l%D{(D8oT@+w0?ce$DZ6pv>{1&Ok67Ix1 zH}3=IEhPJEhItCC8E=`T`N5(k?G=B4+xzZ?<4!~ ze~z6Wk9!CHTI(0rLJ4{JU?E-puc;xusR?>G?;4vt;q~iI9=kDL=z0Rr%O$vU`30X$ zDZRFyZ`(omOy@u|i6h;wtJlP;+}$|Ak|k2dea7n?U1*$T!sXqqOjq^NxLPMmk~&qI zYg0W?yK8T(6+Ea+$YyspKK?kP$+B`~t3^Pib_`!6xCs32!i@pqXfFV6PmBIR<-QW= zN8L{pt0Vap0x`Gzn#E@zh@H)0FfVfA_Iu4fjYZ+umO1LXIbVc$pY+E234u)ttcrl$ z>s92z4vT%n6cMb>=XT6;l0+9e(|CZG)$@C7t7Z7Ez@a)h)!hyuV&B5K%%)P5?Lk|C zZZSVzdXp{@OXSP0hoU-gF8s8Um(#xzjP2Vem zec#-^JqTa&Y#QJ>-FBxd7tf`XB6e^JPUgagB8iBSEps;92KG`!#mvVcPQ5yNC-GEG zTiHEDYfH+0O15}r^+ z#jxj=@x8iNHWALe!P3R67TwmhItn**0JwnzSV2O&KE8KcT+0hWH^OPD1pwiuyx=b@ zNf5Jh0{9X)8;~Es)$t@%(3!OnbY+`@?i{mGX7Yy}8T_*0a6g;kaFPq;*=px5EhO{Cp%1kI<0?*|h8v!6WnO3cCJRF2-CRrU3JiLJnj@6;L)!0kWYAc_}F{2P))3HmCrz zQ&N&gE70;`!6*eJ4^1IR{f6j4(-l&X!tjHxkbHA^Zhrnhr9g{exN|xrS`5Pq=#Xf& zG%P=#ra-TyVFfgW%cZo5OSIwFL9WtXAlFOa+ubmI5t*3=g#Y zF%;70p5;{ZeFL}&}yOY1N1*Q;*<(kTB!7vM$QokF)yr2FlIU@$Ph58$Bz z0J?xQG=MlS4L6jA22eS42g|9*9pX@$#*sUeM(z+t?hr@r5J&D1rx}2pW&m*_`VDCW zUYY@v-;bAO0HqoAgbbiGGC<=ryf96}3pouhy3XJrX+!!u*O_>Si38V{uJmQ&USptX zKp#l(?>%^7;2%h(q@YWS#9;a!JhKlkR#Vd)ERILlgu!Hr@jA@V;sk4BJ-H#p*4EqC zDGjC*tl=@3Oi6)Bn^QwFpul18fpkbpg0+peH$xyPBqb%`$OUhPKyWb32o7clB*9Z< zN=i~NLjavrLtwgJ01bufP+>p-jR2I95|TpmKpQL2!oV>g(4RvS2pK4*ou%m(h6r3A zX#s&`9LU1ZG&;{CkOK!4fLDTnBys`M!vuz>Q&9OZ0hGQl!~!jSDg|~s*w52opC{sB ze|Cf2luD(*G13LcOAGA!s2FjSK8&IE5#W%J25w!vM0^VyQM!t)inj&RTiJ!wXzFgz z3^IqzB7I0L$llljsGq})thBy9UOyjtFO_*hYM_sgcMk>44jeH0V1FDyELc{S1F-;A zS;T^k^~4biG&V*Irq}O;e}j$$+E_#G?HKIn05iP3j|87TkGK~SqG!-KBg5+mN(aLm z8ybhIM`%C19UX$H$KY6JgXbY$0AT%rEpHC;u`rQ$Y=rxUdsc5*Kvc8jaYaO$^)cI6){P6K0r)I6DY4Wr4&B zLQUBraey#0HV|&c4v7PVo3n$zHj99(TZO^3?Ly%C4nYvJTL9eLBLHsM3WKKD>5!B` zQ=BsR3aR6PD(Fa>327E2HAu5TM~Wusc!)>~(gM)+3~m;92Jd;FnSib=M5d6;;5{%R zb4V7DEJ0V!CP-F*oU?gkc>ksUtAYP&V4ND5J>J2^jt*vcFflQWCrB&fLdT%O59PVJ zhid#toR=FNgD!q3&r8#wEBr`!wzvQu5zX?Q>nlSJ4i@WC*CN*-xU66F^V5crWevQ9gsq$I@z1o(a=k7LL~ z7m_~`o;_Ozha1$8Q}{WBehvAlO4EL60y5}8GDrZ< zXh&F}71JbW2A~8KfEWj&UWV#4+Z4p`b{uAj4&WC zha`}X@3~+Iz^WRlOHU&KngK>#j}+_o@LdBC1H-`gT+krWX3-;!)6?{FBp~%20a}FL zFP9%Emqcwa#(`=G>BBZ0qZDQhmZKJg_g8<=bBFKWr!dyg(YkpE+|R*SGpDVU!+VlU zFC54^DLv}`qa%49T>nNiA9Q7Ips#!Xx90tCU2gvK`(F+GPcL=J^>No{)~we#o@&mUb6c$ zCc*<|NJBk-#+{j9xkQ&ujB zI~`#kN~7W!f*-}wkG~Ld!JqZ@tK}eeSnsS5J1fMFXm|`LJx&}5`@dK3W^7#Wnm+_P zBZkp&j1fa2Y=eIjJ0}gh85jt43kaIXXv?xmo@eHrka!Z|vQv12HN#+!I5E z`(fbuW>gFiJL|uXJ!vKt#z3e3HlVdboH7;e#i3(2<)Fg-I@BR!qY#eof3MFZ&*Y@l zI|KJf&ge@p2Dq09Vu$$Qxb7!}{m-iRk@!)%KL)txi3;~Z4Pb}u@GsW;ELiWeG9V51 znX#}B&4Y2E7-H=OpNE@q{%hFLxwIpBF2t{vPREa8_{linXT;#1vMRWjOzLOP$-hf( z>=?$0;~~PnkqY;~K{EM6Vo-T(0K{A0}VUGmu*hR z{tw3hvBN%N3G3Yw`X5Te+F{J`(3w1s3-+1EbnFQKcrgrX1Jqvs@ADGe%M0s$EbK$$ zK)=y=upBc6SjGYAACCcI=Y*6Fi8_jgwZlLxD26fnQfJmb8^gHRN5(TemhX@0e=vr> zg`W}6U>x6VhoA3DqsGGD9uL1DhB3!OXO=k}59TqD@(0Nb{)Ut_luTioK_>7wjc!5C zIr@w}b`Fez3)0wQfKl&bae7;PcTA7%?f2xucM0G)wt_KO!Ewx>F~;=BI0j=Fb4>pp zv}0R^xM4eti~+^+gE$6b81p(kwzuDti(-K9bc|?+pJEl@H+jSYuxZQV8rl8 zjp@M{#%qItIUFN~KcO9Hed*`$5A-2~pAo~K&<-Q+`9`$CK>rzqAI4w~$F%vs9s{~x zg4BP%Gy*@m?;D6=SRX?888Q6peF@_4Z->8wAH~Cn!R$|Hhq2cIzFYqT_+cDourHbY z0qroxJnrZ4Gh+Ay+F`_c%+KRT>y3qw{)89?=hJ@=KO=@ep)aBJ$c!JHfBMJpsP*3G za7|)VJJ8B;4?n{~ldJF7%jmb`-ftIvNd~ekoufG(`K(3=LNc;HBY& z(lp#q8XAD#cIf}k49zX_i`*fO+#!zKA&%T3j@%)R+#yag067CU%yUEe47>wzGU8^` z1EXFT^@I!{J!F8!X?S6ph8J=gUi5tl93*W>7}_uR<2N2~e}FaG?}KPyugQ=-OGEZs z!GBoyYY+H*ANn4?Z)X4l+7H%`17i5~zRlRIX?t)6_eu=g2Q`3WBhxSUeea+M-S?RL zX9oBGKn%a!H+*hx4d2(I!gsi+@SQK%<{X22M~2tMulJoa)0*+z9=-YO+;DFEm5eE1U9b^B(Z}2^9!Qk`!A$wUE z7$Ar5?NRg2&G!AZqnmE64eh^Anss3i!{}%6@Et+4rr!=}!SBF8eZ2*J3ujCWbl;3; z48H~goPSv(8X61fKKdpP!Z7$88NL^Z?j`!^*I?-P4X^pMxyWz~@$(UeAcTSDd(`vO z{~rc;9|GfMJcApU3k}22a!&)k4{CU!e_ny^Y3cO;tOvOMKEyWz!vG(Kp*;hB?d|R3`2X~=5a6#^o5@qn?J-bI8Ppip{-yG z!k|VcGsq!jF~}7DMr49Wap-s&>o=U^T0!Lcy}!(bhtYsPQy z4|EJe{12QL#=c(suQ89Mhw9<`bui%nx7Nep`C&*M3~vMEACmcRYYRGtANq$F%zh&V zc)cEVeHz*Z1N)L7k-(k3np#{GcDh2Q@ya0YHl*n7fl*ZPAsbU-a94MYYtA#&!c`xGIaV;yzsmrjfieTEtqB_WgZp2*NplHx=$O{M~2#i_vJ{ps-NgK zQsxKK_CBM2PP_je+Xft`(vYfXXgIUr{=PA=7a8`2EHk)Ym2QKIforz# tySWtj{oF3N9@_;i*Fv5S)9x^z=nlWP>jpp-9)52ZmLVA=i*%6g{{fxOO~wEK literal 0 HcmV?d00001 diff --git a/flutter_inappwebview_windows/example/windows/runner/runner.exe.manifest b/flutter_inappwebview_windows/example/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..a42ea768 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/flutter_inappwebview_windows/example/windows/runner/utils.cpp b/flutter_inappwebview_windows/example/windows/runner/utils.cpp new file mode 100644 index 00000000..b2b08734 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length <= 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/flutter_inappwebview_windows/example/windows/runner/utils.h b/flutter_inappwebview_windows/example/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/flutter_inappwebview_windows/example/windows/runner/win32_window.cpp b/flutter_inappwebview_windows/example/windows/runner/win32_window.cpp new file mode 100644 index 00000000..60608d0f --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/flutter_inappwebview_windows/example/windows/runner/win32_window.h b/flutter_inappwebview_windows/example/windows/runner/win32_window.h new file mode 100644 index 00000000..e901dde6 --- /dev/null +++ b/flutter_inappwebview_windows/example/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/flutter_inappwebview_windows/lib/flutter_inappwebview_windows.dart b/flutter_inappwebview_windows/lib/flutter_inappwebview_windows.dart new file mode 100644 index 00000000..4cd071c6 --- /dev/null +++ b/flutter_inappwebview_windows/lib/flutter_inappwebview_windows.dart @@ -0,0 +1,3 @@ +library flutter_inappwebview_windows; + +export 'src/main.dart'; diff --git a/flutter_inappwebview_windows/lib/src/cookie_manager.dart b/flutter_inappwebview_windows/lib/src/cookie_manager.dart new file mode 100644 index 00000000..bf9d5a9b --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/cookie_manager.dart @@ -0,0 +1,433 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +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]. +/// +/// 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 + 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 [MacOSCookieManagerCreationParams] instance based on [PlatformCookieManagerCreationParams]. + factory MacOSCookieManagerCreationParams.fromPlatformCookieManagerCreationParams( + PlatformCookieManagerCreationParams params) { + return MacOSCookieManagerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformCookieManager} +class MacOSCookieManager extends PlatformCookieManager with ChannelController { + /// Creates a new [MacOSCookieManager]. + MacOSCookieManager(PlatformCookieManagerCreationParams params) + : super.implementation( + params is MacOSCookieManagerCreationParams + ? params + : MacOSCookieManagerCreationParams + .fromPlatformCookieManagerCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_cookiemanager'); + handler = handleMethod; + initMethodCallHandler(); + } + + static MacOSCookieManager? _instance; + + ///Gets the [MacOSCookieManager] shared instance. + static MacOSCookieManager instance() { + return (_instance != null) ? _instance! : _init(); + } + + static MacOSCookieManager _init() { + _instance = MacOSCookieManager(MacOSCookieManagerCreationParams( + const PlatformCookieManagerCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future setCookie( + {required WebUri url, + required String name, + required String value, + String path = "/", + String? domain, + int? expiresDate, + int? maxAge, + bool? isSecure, + bool? isHttpOnly, + HTTPCookieSameSitePolicy? sameSite, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + webViewController = webViewController ?? iosBelow11WebViewController; + + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + assert(value.isNotEmpty); + assert(path.isNotEmpty); + + if (await _shouldUseJavascript()) { + await _setCookieWithJavaScript( + url: url, + name: name, + value: value, + domain: domain, + path: path, + expiresDate: expiresDate, + maxAge: maxAge, + isSecure: isSecure, + sameSite: sameSite, + webViewController: webViewController); + return true; + } + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('name', () => name); + args.putIfAbsent('value', () => value); + args.putIfAbsent('domain', () => domain); + args.putIfAbsent('path', () => path); + args.putIfAbsent('expiresDate', () => expiresDate?.toString()); + args.putIfAbsent('maxAge', () => maxAge); + args.putIfAbsent('isSecure', () => isSecure); + args.putIfAbsent('isHttpOnly', () => isHttpOnly); + args.putIfAbsent('sameSite', () => sameSite?.toNativeValue()); + + return await channel?.invokeMethod('setCookie', args) ?? false; + } + + Future _setCookieWithJavaScript( + {required WebUri url, + required String name, + required String value, + String path = "/", + String? domain, + int? expiresDate, + int? maxAge, + bool? isSecure, + HTTPCookieSameSitePolicy? sameSite, + PlatformInAppWebViewController? webViewController}) async { + var cookieValue = name + "=" + value + "; Path=" + path; + + if (domain != null) cookieValue += "; Domain=" + domain; + + if (expiresDate != null) + cookieValue += "; Expires=" + await _getCookieExpirationDate(expiresDate); + + if (maxAge != null) cookieValue += "; Max-Age=" + maxAge.toString(); + + if (isSecure != null && isSecure) cookieValue += "; Secure"; + + if (sameSite != null) + cookieValue += "; SameSite=" + sameSite.toNativeValue(); + + cookieValue += ";"; + + if (webViewController != null) { + final javaScriptEnabled = + (await webViewController.getSettings())?.javaScriptEnabled ?? false; + if (javaScriptEnabled) { + await webViewController.evaluateJavascript( + source: 'document.cookie="$cookieValue"'); + return; + } + } + + final setCookieCompleter = Completer(); + final headlessWebView = + MacOSHeadlessInAppWebView(MacOSHeadlessInAppWebViewCreationParams( + initialUrlRequest: URLRequest(url: url), + onLoadStop: (controller, url) async { + await controller.evaluateJavascript( + source: 'document.cookie="$cookieValue"'); + setCookieCompleter.complete(); + })); + await headlessWebView.run(); + await setCookieCompleter.future; + await headlessWebView.dispose(); + } + + @override + Future> getCookies( + {required WebUri url, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + if (await _shouldUseJavascript()) { + return await _getCookiesWithJavaScript( + url: url, webViewController: webViewController); + } + + List cookies = []; + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + List cookieListMap = + await channel?.invokeMethod('getCookies', args) ?? []; + cookieListMap = cookieListMap.cast>(); + + cookieListMap.forEach((cookieMap) { + cookies.add(Cookie( + name: cookieMap["name"], + value: cookieMap["value"], + expiresDate: cookieMap["expiresDate"], + isSessionOnly: cookieMap["isSessionOnly"], + domain: cookieMap["domain"], + sameSite: + HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), + isSecure: cookieMap["isSecure"], + isHttpOnly: cookieMap["isHttpOnly"], + path: cookieMap["path"])); + }); + return cookies; + } + + Future> _getCookiesWithJavaScript( + {required WebUri url, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + List cookies = []; + + if (webViewController != null) { + final javaScriptEnabled = + (await webViewController.getSettings())?.javaScriptEnabled ?? false; + if (javaScriptEnabled) { + List documentCookies = (await webViewController + .evaluateJavascript(source: 'document.cookie') as String) + .split(';') + .map((documentCookie) => documentCookie.trim()) + .toList(); + documentCookies.forEach((documentCookie) { + List cookie = documentCookie.split('='); + if (cookie.length > 1) { + cookies.add(Cookie( + name: cookie[0], + value: cookie[1], + )); + } + }); + return cookies; + } + } + + final pageLoaded = Completer(); + final headlessWebView = + MacOSHeadlessInAppWebView(MacOSHeadlessInAppWebViewCreationParams( + initialUrlRequest: URLRequest(url: url), + onLoadStop: (controller, url) async { + pageLoaded.complete(); + }, + )); + await headlessWebView.run(); + await pageLoaded.future; + + List documentCookies = (await headlessWebView.webViewController! + .evaluateJavascript(source: 'document.cookie') as String) + .split(';') + .map((documentCookie) => documentCookie.trim()) + .toList(); + documentCookies.forEach((documentCookie) { + List cookie = documentCookie.split('='); + if (cookie.length > 1) { + cookies.add(Cookie( + name: cookie[0], + value: cookie[1], + )); + } + }); + await headlessWebView.dispose(); + return cookies; + } + + @override + Future getCookie( + {required WebUri url, + required String name, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + if (await _shouldUseJavascript()) { + List cookies = await _getCookiesWithJavaScript( + url: url, webViewController: webViewController); + return cookies + .cast() + .firstWhere((cookie) => cookie!.name == name, orElse: () => null); + } + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + List cookies = + await channel?.invokeMethod('getCookies', args) ?? []; + cookies = cookies.cast>(); + for (var i = 0; i < cookies.length; i++) { + cookies[i] = cookies[i].cast(); + if (cookies[i]["name"] == name) + return Cookie( + name: cookies[i]["name"], + value: cookies[i]["value"], + expiresDate: cookies[i]["expiresDate"], + isSessionOnly: cookies[i]["isSessionOnly"], + domain: cookies[i]["domain"], + sameSite: HTTPCookieSameSitePolicy.fromNativeValue( + cookies[i]["sameSite"]), + isSecure: cookies[i]["isSecure"], + isHttpOnly: cookies[i]["isHttpOnly"], + path: cookies[i]["path"]); + } + return null; + } + + @override + Future deleteCookie( + {required WebUri url, + required String name, + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + assert(name.isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + if (await _shouldUseJavascript()) { + await _setCookieWithJavaScript( + url: url, + name: name, + value: "", + path: path, + domain: domain, + maxAge: -1, + webViewController: webViewController); + return true; + } + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('name', () => name); + args.putIfAbsent('domain', () => domain); + args.putIfAbsent('path', () => path); + return await channel?.invokeMethod('deleteCookie', args) ?? false; + } + + @override + Future deleteCookies( + {required WebUri url, + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { + assert(url.toString().isNotEmpty); + + webViewController = webViewController ?? iosBelow11WebViewController; + + if (await _shouldUseJavascript()) { + List cookies = await _getCookiesWithJavaScript( + url: url, webViewController: webViewController); + for (var i = 0; i < cookies.length; i++) { + await _setCookieWithJavaScript( + url: url, + name: cookies[i].name, + value: "", + path: path, + domain: domain, + maxAge: -1, + webViewController: webViewController); + } + return true; + } + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('domain', () => domain); + args.putIfAbsent('path', () => path); + return await channel?.invokeMethod('deleteCookies', args) ?? false; + } + + @override + Future deleteAllCookies() async { + Map args = {}; + return await channel?.invokeMethod('deleteAllCookies', args) ?? false; + } + + @override + Future> getAllCookies() async { + List cookies = []; + + Map args = {}; + List cookieListMap = + await channel?.invokeMethod('getAllCookies', args) ?? []; + cookieListMap = cookieListMap.cast>(); + + cookieListMap.forEach((cookieMap) { + cookies.add(Cookie( + name: cookieMap["name"], + value: cookieMap["value"], + expiresDate: cookieMap["expiresDate"], + isSessionOnly: cookieMap["isSessionOnly"], + domain: cookieMap["domain"], + sameSite: + HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), + isSecure: cookieMap["isSecure"], + isHttpOnly: cookieMap["isHttpOnly"], + path: cookieMap["path"])); + }); + return cookies; + } + + Future _getCookieExpirationDate(int expiresDate) async { + var platformUtil = PlatformUtil.instance(); + var dateTime = DateTime.fromMillisecondsSinceEpoch(expiresDate).toUtc(); + return !kIsWeb + ? await platformUtil.formatDate( + date: dateTime, + format: 'EEE, dd MMM yyyy hh:mm:ss z', + locale: 'en_US', + timezone: 'GMT') + : await platformUtil.getWebCookieExpirationDate(date: dateTime); + } + + Future _shouldUseJavascript() async { + final platformUtil = PlatformUtil.instance(); + final systemVersion = await platformUtil.getSystemVersion(); + return systemVersion.compareTo("11") == -1; + } + + @override + void dispose() { + // empty + } +} + +extension InternalCookieManager on MacOSCookieManager { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart b/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart new file mode 100644 index 00000000..ca291cbc --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart @@ -0,0 +1,124 @@ +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]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformFindInteractionControllerCreationParams] for +/// more information. +@immutable +class MacOSFindInteractionControllerCreationParams + extends PlatformFindInteractionControllerCreationParams { + /// Creates a new [MacOSFindInteractionControllerCreationParams] instance. + const MacOSFindInteractionControllerCreationParams( + {super.onFindResultReceived}); + + /// Creates a [MacOSFindInteractionControllerCreationParams] instance based on [PlatformFindInteractionControllerCreationParams]. + factory MacOSFindInteractionControllerCreationParams.fromPlatformFindInteractionControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformFindInteractionControllerCreationParams params) { + return MacOSFindInteractionControllerCreationParams( + onFindResultReceived: params.onFindResultReceived); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController} +class MacOSFindInteractionController extends PlatformFindInteractionController + with ChannelController { + /// Constructs a [MacOSFindInteractionController]. + MacOSFindInteractionController( + PlatformFindInteractionControllerCreationParams params) + : super.implementation( + params is MacOSFindInteractionControllerCreationParams + ? params + : MacOSFindInteractionControllerCreationParams + .fromPlatformFindInteractionControllerCreationParams(params), + ); + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + debugLoggingSettings: + PlatformFindInteractionController.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + _debugLog(call.method, call.arguments); + + switch (call.method) { + case "onFindResultReceived": + if (onFindResultReceived != null) { + int activeMatchOrdinal = call.arguments["activeMatchOrdinal"]; + int numberOfMatches = call.arguments["numberOfMatches"]; + bool isDoneCounting = call.arguments["isDoneCounting"]; + onFindResultReceived!( + this, activeMatchOrdinal, numberOfMatches, isDoneCounting); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.findAll} + Future findAll({String? find}) async { + Map args = {}; + args.putIfAbsent('find', () => find); + await channel?.invokeMethod('findAll', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.findNext} + Future findNext({bool forward = true}) async { + Map args = {}; + args.putIfAbsent('forward', () => forward); + await channel?.invokeMethod('findNext', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.clearMatches} + Future clearMatches() async { + Map args = {}; + await channel?.invokeMethod('clearMatches', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.setSearchText} + Future setSearchText(String? searchText) async { + Map args = {}; + args.putIfAbsent('searchText', () => searchText); + await channel?.invokeMethod('setSearchText', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.getSearchText} + Future getSearchText() async { + Map args = {}; + return await channel?.invokeMethod('getSearchText', args); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.getActiveFindSession} + Future getActiveFindSession() async { + Map args = {}; + Map? result = + (await channel?.invokeMethod('getActiveFindSession', args)) + ?.cast(); + return FindSession.fromMap(result); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController.dispose} + @override + void dispose({bool isKeepAlive = false}) { + disposeChannel(removeMethodCallHandler: !isKeepAlive); + } +} + +extension InternalFindInteractionController on MacOSFindInteractionController { + void init(dynamic id) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_find_interaction_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } +} diff --git a/flutter_inappwebview_windows/lib/src/find_interaction/main.dart b/flutter_inappwebview_windows/lib/src/find_interaction/main.dart new file mode 100644 index 00000000..a7adaacf --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/find_interaction/main.dart @@ -0,0 +1,2 @@ +export 'find_interaction_controller.dart' + hide InternalFindInteractionController; diff --git a/flutter_inappwebview_windows/lib/src/http_auth_credentials_database.dart b/flutter_inappwebview_windows/lib/src/http_auth_credentials_database.dart new file mode 100644 index 00000000..28c9126f --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/http_auth_credentials_database.dart @@ -0,0 +1,155 @@ +import 'dart:async'; +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 [MacOSHttpAuthCredentialDatabase]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformHttpAuthCredentialDatabaseCreationParams] for +/// more information. +@immutable +class MacOSHttpAuthCredentialDatabaseCreationParams + extends PlatformHttpAuthCredentialDatabaseCreationParams { + /// Creates a new [MacOSHttpAuthCredentialDatabaseCreationParams] instance. + const MacOSHttpAuthCredentialDatabaseCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformHttpAuthCredentialDatabaseCreationParams params, + ) : super(); + + /// Creates a [MacOSHttpAuthCredentialDatabaseCreationParams] instance based on [PlatformHttpAuthCredentialDatabaseCreationParams]. + factory MacOSHttpAuthCredentialDatabaseCreationParams.fromPlatformHttpAuthCredentialDatabaseCreationParams( + PlatformHttpAuthCredentialDatabaseCreationParams params) { + return MacOSHttpAuthCredentialDatabaseCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformHttpAuthCredentialDatabase} +class MacOSHttpAuthCredentialDatabase extends PlatformHttpAuthCredentialDatabase + with ChannelController { + /// Creates a new [MacOSHttpAuthCredentialDatabase]. + MacOSHttpAuthCredentialDatabase( + PlatformHttpAuthCredentialDatabaseCreationParams params) + : super.implementation( + params is MacOSHttpAuthCredentialDatabaseCreationParams + ? params + : MacOSHttpAuthCredentialDatabaseCreationParams + .fromPlatformHttpAuthCredentialDatabaseCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_credential_database'); + handler = handleMethod; + initMethodCallHandler(); + } + + static MacOSHttpAuthCredentialDatabase? _instance; + + ///Gets the database shared instance. + static MacOSHttpAuthCredentialDatabase instance() { + return (_instance != null) ? _instance! : _init(); + } + + static MacOSHttpAuthCredentialDatabase _init() { + _instance = MacOSHttpAuthCredentialDatabase( + MacOSHttpAuthCredentialDatabaseCreationParams( + const PlatformHttpAuthCredentialDatabaseCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future> + getAllAuthCredentials() async { + Map args = {}; + List allCredentials = + await channel?.invokeMethod('getAllAuthCredentials', args) ?? []; + + List result = []; + + for (Map map in allCredentials) { + var element = URLProtectionSpaceHttpAuthCredentials.fromMap( + map.cast()); + if (element != null) { + result.add(element); + } + } + return result; + } + + @override + Future> getHttpAuthCredentials( + {required URLProtectionSpace protectionSpace}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + List credentialList = + await channel?.invokeMethod('getHttpAuthCredentials', args) ?? []; + List credentials = []; + for (Map map in credentialList) { + var credential = URLCredential.fromMap(map.cast()); + if (credential != null) { + credentials.add(credential); + } + } + return credentials; + } + + @override + Future setHttpAuthCredential( + {required URLProtectionSpace protectionSpace, + required URLCredential credential}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + args.putIfAbsent("username", () => credential.username); + args.putIfAbsent("password", () => credential.password); + await channel?.invokeMethod('setHttpAuthCredential', args); + } + + @override + Future removeHttpAuthCredential( + {required URLProtectionSpace protectionSpace, + required URLCredential credential}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + args.putIfAbsent("username", () => credential.username); + args.putIfAbsent("password", () => credential.password); + await channel?.invokeMethod('removeHttpAuthCredential', args); + } + + @override + Future removeHttpAuthCredentials( + {required URLProtectionSpace protectionSpace}) async { + Map args = {}; + args.putIfAbsent("host", () => protectionSpace.host); + args.putIfAbsent("protocol", () => protectionSpace.protocol); + args.putIfAbsent("realm", () => protectionSpace.realm); + args.putIfAbsent("port", () => protectionSpace.port); + await channel?.invokeMethod('removeHttpAuthCredentials', args); + } + + @override + Future clearAllAuthCredentials() async { + Map args = {}; + await channel?.invokeMethod('clearAllAuthCredentials', args); + } + + @override + void dispose() { + // empty + } +} + +extension InternalHttpAuthCredentialDatabase + on MacOSHttpAuthCredentialDatabase { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart b/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart new file mode 100644 index 00000000..1bf9a54b --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart @@ -0,0 +1,373 @@ +import 'dart:async'; +import 'dart:collection'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../find_interaction/find_interaction_controller.dart'; +import '../in_app_webview/in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [WindowsInAppBrowser]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformInAppBrowserCreationParams] for +/// more information. +class WindowsInAppBrowserCreationParams + extends PlatformInAppBrowserCreationParams { + /// Creates a new [WindowsInAppBrowserCreationParams] instance. + WindowsInAppBrowserCreationParams( + {super.contextMenu, + super.pullToRefreshController, + this.findInteractionController, + super.initialUserScripts, + super.windowId}); + + /// Creates a [WindowsInAppBrowserCreationParams] instance based on [PlatformInAppBrowserCreationParams]. + factory WindowsInAppBrowserCreationParams.fromPlatformInAppBrowserCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformInAppBrowserCreationParams params) { + return WindowsInAppBrowserCreationParams( + contextMenu: params.contextMenu, + pullToRefreshController: params.pullToRefreshController, + findInteractionController: + params.findInteractionController as MacOSFindInteractionController?, + initialUserScripts: params.initialUserScripts, + windowId: params.windowId); + } + + @override + final MacOSFindInteractionController? findInteractionController; +} + +///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser} +class WindowsInAppBrowser extends PlatformInAppBrowser with ChannelController { + @override + final String id = IdGenerator.generate(); + + /// Constructs a [WindowsInAppBrowser]. + WindowsInAppBrowser(PlatformInAppBrowserCreationParams params) + : super.implementation( + params is WindowsInAppBrowserCreationParams + ? params + : WindowsInAppBrowserCreationParams + .fromPlatformInAppBrowserCreationParams(params), + ) { + _contextMenu = params.contextMenu; + } + + static final WindowsInAppBrowser _staticValue = + WindowsInAppBrowser(WindowsInAppBrowserCreationParams()); + + /// Provide static access. + factory WindowsInAppBrowser.static() { + return _staticValue; + } + + WindowsInAppBrowserCreationParams get _macosParams => + params as WindowsInAppBrowserCreationParams; + + static const MethodChannel _staticChannel = + const MethodChannel('com.pichillilorenzo/flutter_inappbrowser'); + + ContextMenu? _contextMenu; + + @override + ContextMenu? get contextMenu => _contextMenu; + + Map _menuItems = HashMap(); + bool _isOpened = false; + WindowsInAppWebViewController? _webViewController; + + @override + WindowsInAppWebViewController? get webViewController { + return _isOpened ? _webViewController : null; + } + + _init() { + channel = MethodChannel('com.pichillilorenzo/flutter_inappbrowser_$id'); + handler = _handleMethod; + initMethodCallHandler(); + + _webViewController = WindowsInAppWebViewController.fromInAppBrowser( + WindowsInAppWebViewControllerCreationParams(id: id), + channel!, + this, + this.initialUserScripts); + _macosParams.findInteractionController?.init(id); + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + id: id, + debugLoggingSettings: PlatformInAppBrowser.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onBrowserCreated": + _debugLog(call.method, call.arguments); + eventHandler?.onBrowserCreated(); + break; + case "onMenuItemClicked": + _debugLog(call.method, call.arguments); + int id = call.arguments["id"].toInt(); + if (this._menuItems[id] != null) { + if (this._menuItems[id]?.onClick != null) { + this._menuItems[id]?.onClick!(); + } + } + break; + case "onMainWindowWillClose": + _debugLog(call.method, call.arguments); + eventHandler?.onMainWindowWillClose(); + break; + case "onExit": + _debugLog(call.method, call.arguments); + _isOpened = false; + final onExit = eventHandler?.onExit; + dispose(); + onExit?.call(); + break; + default: + return _webViewController?.handleMethod(call); + } + } + + Map _prepareOpenRequest( + {@Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) { + assert(!_isOpened, 'The browser is already opened.'); + _isOpened = true; + _init(); + + var initialSettings = settings?.toMap() ?? + options?.toMap() ?? + InAppBrowserClassSettings().toMap(); + + Map pullToRefreshSettings = + pullToRefreshController?.settings.toMap() ?? + pullToRefreshController?.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + List> menuItemList = []; + _menuItems.forEach((key, value) { + menuItemList.add(value.toMap()); + }); + + Map args = {}; + args.putIfAbsent('id', () => id); + args.putIfAbsent('settings', () => initialSettings); + args.putIfAbsent('contextMenu', () => contextMenu?.toMap() ?? {}); + args.putIfAbsent('windowId', () => windowId); + args.putIfAbsent('initialUserScripts', + () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []); + args.putIfAbsent('pullToRefreshSettings', () => pullToRefreshSettings); + args.putIfAbsent('menuItems', () => menuItemList); + return args; + } + + @override + Future openUrlRequest( + {required URLRequest urlRequest, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) async { + assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty); + + Map args = + _prepareOpenRequest(options: options, settings: settings); + args.putIfAbsent('urlRequest', () => urlRequest.toMap()); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future openFile( + {required String assetFilePath, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) async { + assert(assetFilePath.isNotEmpty); + + Map args = + _prepareOpenRequest(options: options, settings: settings); + args.putIfAbsent('assetFilePath', () => assetFilePath); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future openData( + {required String data, + String mimeType = "text/html", + String encoding = "utf8", + WebUri? baseUrl, + @Deprecated("Use historyUrl instead") Uri? androidHistoryUrl, + WebUri? historyUrl, + // ignore: deprecated_member_use_from_same_package + @Deprecated('Use settings instead') InAppBrowserClassOptions? options, + InAppBrowserClassSettings? settings}) async { + Map args = + _prepareOpenRequest(options: options, settings: settings); + args.putIfAbsent('data', () => data); + args.putIfAbsent('mimeType', () => mimeType); + args.putIfAbsent('encoding', () => encoding); + args.putIfAbsent('baseUrl', () => baseUrl?.toString() ?? "about:blank"); + args.putIfAbsent('historyUrl', + () => (historyUrl ?? androidHistoryUrl)?.toString() ?? "about:blank"); + await _staticChannel.invokeMethod('open', args); + } + + @override + Future openWithSystemBrowser({required WebUri url}) async { + assert(url.toString().isNotEmpty); + + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + return await _staticChannel.invokeMethod('openWithSystemBrowser', args); + } + + @override + void addMenuItem(InAppBrowserMenuItem menuItem) { + _menuItems[menuItem.id] = menuItem; + } + + @override + void addMenuItems(List menuItems) { + menuItems.forEach((menuItem) { + _menuItems[menuItem.id] = menuItem; + }); + } + + @override + bool removeMenuItem(InAppBrowserMenuItem menuItem) { + return _menuItems.remove(menuItem.id) != null; + } + + @override + void removeMenuItems(List menuItems) { + for (final menuItem in menuItems) { + removeMenuItem(menuItem); + } + } + + @override + void removeAllMenuItem() { + _menuItems.clear(); + } + + @override + bool hasMenuItem(InAppBrowserMenuItem menuItem) { + return _menuItems.containsKey(menuItem.id); + } + + @override + Future show() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + await channel?.invokeMethod('show', args); + } + + @override + Future hide() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + await channel?.invokeMethod('hide', args); + } + + @override + Future close() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + await channel?.invokeMethod('close', args); + } + + @override + Future isHidden() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + return await channel?.invokeMethod('isHidden', args) ?? false; + } + + @override + @Deprecated('Use setSettings instead') + Future setOptions({required InAppBrowserClassOptions options}) async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + args.putIfAbsent('settings', () => options.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + @Deprecated('Use getSettings instead') + Future getOptions() async { + assert(_isOpened, 'The browser is not opened.'); + Map args = {}; + + Map? options = + await channel?.invokeMethod('getSettings', args); + if (options != null) { + options = options.cast(); + return InAppBrowserClassOptions.fromMap(options as Map); + } + + return null; + } + + @override + Future setSettings( + {required InAppBrowserClassSettings settings}) async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + args.putIfAbsent('settings', () => settings.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + Future getSettings() async { + assert(_isOpened, 'The browser is not opened.'); + + Map args = {}; + + Map? settings = + await channel?.invokeMethod('getSettings', args); + if (settings != null) { + settings = settings.cast(); + return InAppBrowserClassSettings.fromMap( + settings as Map); + } + + return null; + } + + @override + bool isOpened() { + return this._isOpened; + } + + @override + @mustCallSuper + void dispose() { + super.dispose(); + disposeChannel(); + _webViewController?.dispose(); + _webViewController = null; + pullToRefreshController?.dispose(); + findInteractionController?.dispose(); + } +} + +extension InternalInAppBrowser on WindowsInAppBrowser { + void setContextMenu(ContextMenu? contextMenu) { + _contextMenu = contextMenu; + } +} diff --git a/flutter_inappwebview_windows/lib/src/in_app_browser/main.dart b/flutter_inappwebview_windows/lib/src/in_app_browser/main.dart new file mode 100644 index 00000000..e11eb8b1 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/in_app_browser/main.dart @@ -0,0 +1 @@ +export 'in_app_browser.dart' hide InternalInAppBrowser; diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/_static_channel.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/_static_channel.dart new file mode 100644 index 00000000..beb7de70 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/_static_channel.dart @@ -0,0 +1,4 @@ +import 'package:flutter/services.dart'; + +const IN_APP_WEBVIEW_STATIC_CHANNEL = + MethodChannel('com.pichillilorenzo/flutter_inappwebview_manager'); diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart new file mode 100644 index 00000000..047005db --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart @@ -0,0 +1,433 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import '../find_interaction/find_interaction_controller.dart'; +import 'in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [MacOSHeadlessInAppWebView]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformHeadlessInAppWebViewCreationParams] for +/// more information. +@immutable +class MacOSHeadlessInAppWebViewCreationParams + extends PlatformHeadlessInAppWebViewCreationParams { + /// Creates a new [MacOSHeadlessInAppWebViewCreationParams] instance. + MacOSHeadlessInAppWebViewCreationParams( + {super.controllerFromPlatform, + super.initialSize, + super.windowId, + super.onWebViewCreated, + super.onLoadStart, + super.onLoadStop, + @Deprecated('Use onReceivedError instead') super.onLoadError, + super.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") super.onLoadHttpError, + super.onReceivedHttpError, + super.onProgressChanged, + super.onConsoleMessage, + super.shouldOverrideUrlLoading, + super.onLoadResource, + super.onScrollChanged, + @Deprecated('Use onDownloadStartRequest instead') super.onDownloadStart, + super.onDownloadStartRequest, + @Deprecated('Use onLoadResourceWithCustomScheme instead') + super.onLoadResourceCustomScheme, + super.onLoadResourceWithCustomScheme, + super.onCreateWindow, + super.onCloseWindow, + super.onJsAlert, + super.onJsConfirm, + super.onJsPrompt, + super.onReceivedHttpAuthRequest, + super.onReceivedServerTrustAuthRequest, + super.onReceivedClientCertRequest, + @Deprecated('Use FindInteractionController.onFindResultReceived instead') + super.onFindResultReceived, + super.shouldInterceptAjaxRequest, + super.onAjaxReadyStateChange, + super.onAjaxProgress, + super.shouldInterceptFetchRequest, + super.onUpdateVisitedHistory, + @Deprecated("Use onPrintRequest instead") super.onPrint, + super.onPrintRequest, + super.onLongPressHitTestResult, + super.onEnterFullscreen, + super.onExitFullscreen, + super.onPageCommitVisible, + super.onTitleChanged, + super.onWindowFocus, + super.onWindowBlur, + super.onOverScrolled, + super.onZoomScaleChanged, + @Deprecated('Use onSafeBrowsingHit instead') + super.androidOnSafeBrowsingHit, + super.onSafeBrowsingHit, + @Deprecated('Use onPermissionRequest instead') + super.androidOnPermissionRequest, + super.onPermissionRequest, + @Deprecated('Use onGeolocationPermissionsShowPrompt instead') + super.androidOnGeolocationPermissionsShowPrompt, + super.onGeolocationPermissionsShowPrompt, + @Deprecated('Use onGeolocationPermissionsHidePrompt instead') + super.androidOnGeolocationPermissionsHidePrompt, + super.onGeolocationPermissionsHidePrompt, + @Deprecated('Use shouldInterceptRequest instead') + super.androidShouldInterceptRequest, + super.shouldInterceptRequest, + @Deprecated('Use onRenderProcessGone instead') + super.androidOnRenderProcessGone, + super.onRenderProcessGone, + @Deprecated('Use onRenderProcessResponsive instead') + super.androidOnRenderProcessResponsive, + super.onRenderProcessResponsive, + @Deprecated('Use onRenderProcessUnresponsive instead') + super.androidOnRenderProcessUnresponsive, + super.onRenderProcessUnresponsive, + @Deprecated('Use onFormResubmission instead') + super.androidOnFormResubmission, + super.onFormResubmission, + @Deprecated('Use onZoomScaleChanged instead') super.androidOnScaleChanged, + @Deprecated('Use onReceivedIcon instead') super.androidOnReceivedIcon, + super.onReceivedIcon, + @Deprecated('Use onReceivedTouchIconUrl instead') + super.androidOnReceivedTouchIconUrl, + super.onReceivedTouchIconUrl, + @Deprecated('Use onJsBeforeUnload instead') super.androidOnJsBeforeUnload, + super.onJsBeforeUnload, + @Deprecated('Use onReceivedLoginRequest instead') + super.androidOnReceivedLoginRequest, + super.onReceivedLoginRequest, + super.onPermissionRequestCanceled, + super.onRequestFocus, + @Deprecated('Use onWebContentProcessDidTerminate instead') + super.iosOnWebContentProcessDidTerminate, + super.onWebContentProcessDidTerminate, + @Deprecated( + 'Use onDidReceiveServerRedirectForProvisionalNavigation instead') + super.iosOnDidReceiveServerRedirectForProvisionalNavigation, + super.onDidReceiveServerRedirectForProvisionalNavigation, + @Deprecated('Use onNavigationResponse instead') + super.iosOnNavigationResponse, + super.onNavigationResponse, + @Deprecated('Use shouldAllowDeprecatedTLS instead') + super.iosShouldAllowDeprecatedTLS, + super.shouldAllowDeprecatedTLS, + super.onCameraCaptureStateChanged, + super.onMicrophoneCaptureStateChanged, + super.onContentSizeChanged, + super.initialUrlRequest, + super.initialFile, + super.initialData, + @Deprecated('Use initialSettings instead') super.initialOptions, + super.initialSettings, + super.contextMenu, + super.initialUserScripts, + super.pullToRefreshController, + this.findInteractionController}); + + /// Creates a [MacOSHeadlessInAppWebViewCreationParams] instance based on [PlatformHeadlessInAppWebViewCreationParams]. + MacOSHeadlessInAppWebViewCreationParams.fromPlatformHeadlessInAppWebViewCreationParams( + PlatformHeadlessInAppWebViewCreationParams params) + : this( + controllerFromPlatform: params.controllerFromPlatform, + initialSize: params.initialSize, + windowId: params.windowId, + onWebViewCreated: params.onWebViewCreated, + onLoadStart: params.onLoadStart, + onLoadStop: params.onLoadStop, + onLoadError: params.onLoadError, + onReceivedError: params.onReceivedError, + onLoadHttpError: params.onLoadHttpError, + onReceivedHttpError: params.onReceivedHttpError, + onProgressChanged: params.onProgressChanged, + onConsoleMessage: params.onConsoleMessage, + shouldOverrideUrlLoading: params.shouldOverrideUrlLoading, + onLoadResource: params.onLoadResource, + onScrollChanged: params.onScrollChanged, + onDownloadStart: params.onDownloadStart, + onDownloadStartRequest: params.onDownloadStartRequest, + onLoadResourceCustomScheme: params.onLoadResourceCustomScheme, + onLoadResourceWithCustomScheme: + params.onLoadResourceWithCustomScheme, + onCreateWindow: params.onCreateWindow, + onCloseWindow: params.onCloseWindow, + onJsAlert: params.onJsAlert, + onJsConfirm: params.onJsConfirm, + onJsPrompt: params.onJsPrompt, + onReceivedHttpAuthRequest: params.onReceivedHttpAuthRequest, + onReceivedServerTrustAuthRequest: + params.onReceivedServerTrustAuthRequest, + onReceivedClientCertRequest: params.onReceivedClientCertRequest, + onFindResultReceived: params.onFindResultReceived, + shouldInterceptAjaxRequest: params.shouldInterceptAjaxRequest, + onAjaxReadyStateChange: params.onAjaxReadyStateChange, + onAjaxProgress: params.onAjaxProgress, + shouldInterceptFetchRequest: params.shouldInterceptFetchRequest, + onUpdateVisitedHistory: params.onUpdateVisitedHistory, + onPrint: params.onPrint, + onPrintRequest: params.onPrintRequest, + onLongPressHitTestResult: params.onLongPressHitTestResult, + onEnterFullscreen: params.onEnterFullscreen, + onExitFullscreen: params.onExitFullscreen, + onPageCommitVisible: params.onPageCommitVisible, + onTitleChanged: params.onTitleChanged, + onWindowFocus: params.onWindowFocus, + onWindowBlur: params.onWindowBlur, + onOverScrolled: params.onOverScrolled, + onZoomScaleChanged: params.onZoomScaleChanged, + androidOnSafeBrowsingHit: params.androidOnSafeBrowsingHit, + onSafeBrowsingHit: params.onSafeBrowsingHit, + androidOnPermissionRequest: params.androidOnPermissionRequest, + onPermissionRequest: params.onPermissionRequest, + androidOnGeolocationPermissionsShowPrompt: + params.androidOnGeolocationPermissionsShowPrompt, + onGeolocationPermissionsShowPrompt: + params.onGeolocationPermissionsShowPrompt, + androidOnGeolocationPermissionsHidePrompt: + params.androidOnGeolocationPermissionsHidePrompt, + onGeolocationPermissionsHidePrompt: + params.onGeolocationPermissionsHidePrompt, + androidShouldInterceptRequest: params.androidShouldInterceptRequest, + shouldInterceptRequest: params.shouldInterceptRequest, + androidOnRenderProcessGone: params.androidOnRenderProcessGone, + onRenderProcessGone: params.onRenderProcessGone, + androidOnRenderProcessResponsive: + params.androidOnRenderProcessResponsive, + onRenderProcessResponsive: params.onRenderProcessResponsive, + androidOnRenderProcessUnresponsive: + params.androidOnRenderProcessUnresponsive, + onRenderProcessUnresponsive: params.onRenderProcessUnresponsive, + androidOnFormResubmission: params.androidOnFormResubmission, + onFormResubmission: params.onFormResubmission, + androidOnScaleChanged: params.androidOnScaleChanged, + androidOnReceivedIcon: params.androidOnReceivedIcon, + onReceivedIcon: params.onReceivedIcon, + androidOnReceivedTouchIconUrl: params.androidOnReceivedTouchIconUrl, + onReceivedTouchIconUrl: params.onReceivedTouchIconUrl, + androidOnJsBeforeUnload: params.androidOnJsBeforeUnload, + onJsBeforeUnload: params.onJsBeforeUnload, + androidOnReceivedLoginRequest: params.androidOnReceivedLoginRequest, + onReceivedLoginRequest: params.onReceivedLoginRequest, + onPermissionRequestCanceled: params.onPermissionRequestCanceled, + onRequestFocus: params.onRequestFocus, + iosOnWebContentProcessDidTerminate: + params.iosOnWebContentProcessDidTerminate, + onWebContentProcessDidTerminate: + params.onWebContentProcessDidTerminate, + iosOnDidReceiveServerRedirectForProvisionalNavigation: + params.iosOnDidReceiveServerRedirectForProvisionalNavigation, + onDidReceiveServerRedirectForProvisionalNavigation: + params.onDidReceiveServerRedirectForProvisionalNavigation, + iosOnNavigationResponse: params.iosOnNavigationResponse, + onNavigationResponse: params.onNavigationResponse, + iosShouldAllowDeprecatedTLS: params.iosShouldAllowDeprecatedTLS, + shouldAllowDeprecatedTLS: params.shouldAllowDeprecatedTLS, + onCameraCaptureStateChanged: params.onCameraCaptureStateChanged, + onMicrophoneCaptureStateChanged: + params.onMicrophoneCaptureStateChanged, + onContentSizeChanged: params.onContentSizeChanged, + initialUrlRequest: params.initialUrlRequest, + initialFile: params.initialFile, + initialData: params.initialData, + initialOptions: params.initialOptions, + initialSettings: params.initialSettings, + contextMenu: params.contextMenu, + initialUserScripts: params.initialUserScripts, + pullToRefreshController: params.pullToRefreshController, + findInteractionController: params.findInteractionController + as MacOSFindInteractionController?); + + @override + final MacOSFindInteractionController? findInteractionController; +} + +///{@macro flutter_inappwebview_platform_interface.PlatformHeadlessInAppWebView} +class MacOSHeadlessInAppWebView extends PlatformHeadlessInAppWebView + with ChannelController { + @override + late final String id; + + bool _started = false; + bool _running = false; + + static const MethodChannel _sharedChannel = + const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview'); + + WindowsInAppWebViewController? _webViewController; + + /// Constructs a [MacOSHeadlessInAppWebView]. + MacOSHeadlessInAppWebView(PlatformHeadlessInAppWebViewCreationParams params) + : super.implementation( + params is MacOSHeadlessInAppWebViewCreationParams + ? params + : MacOSHeadlessInAppWebViewCreationParams + .fromPlatformHeadlessInAppWebViewCreationParams(params), + ) { + id = IdGenerator.generate(); + } + + @override + WindowsInAppWebViewController? get webViewController => _webViewController; + + dynamic _controllerFromPlatform; + + MacOSHeadlessInAppWebViewCreationParams get _macosParams => + params as MacOSHeadlessInAppWebViewCreationParams; + + _init() { + _webViewController = WindowsInAppWebViewController( + WindowsInAppWebViewControllerCreationParams(id: id, webviewParams: params), + ); + _controllerFromPlatform = + params.controllerFromPlatform?.call(_webViewController!) ?? + _webViewController!; + _macosParams.findInteractionController?.init(id); + channel = + MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview_$id'); + handler = _handleMethod; + initMethodCallHandler(); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onWebViewCreated": + if (params.onWebViewCreated != null && _webViewController != null) { + params.onWebViewCreated!(_controllerFromPlatform); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + Future run() async { + if (_started) { + return; + } + _started = true; + _init(); + + final initialSettings = params.initialSettings ?? InAppWebViewSettings(); + _inferInitialSettings(initialSettings); + + Map settingsMap = + (params.initialSettings != null ? initialSettings.toMap() : null) ?? + params.initialOptions?.toMap() ?? + initialSettings.toMap(); + + Map pullToRefreshSettings = + _macosParams.pullToRefreshController?.params.settings.toMap() ?? + _macosParams.pullToRefreshController?.params.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + Map args = {}; + args.putIfAbsent('id', () => id); + args.putIfAbsent( + 'params', + () => { + 'initialUrlRequest': params.initialUrlRequest?.toMap(), + 'initialFile': params.initialFile, + 'initialData': params.initialData?.toMap(), + 'initialSettings': settingsMap, + 'contextMenu': params.contextMenu?.toMap() ?? {}, + 'windowId': params.windowId, + 'initialUserScripts': + params.initialUserScripts?.map((e) => e.toMap()).toList() ?? + [], + 'pullToRefreshSettings': pullToRefreshSettings, + 'initialSize': params.initialSize.toMap() + }); + await _sharedChannel.invokeMethod('run', args); + _running = true; + } + + void _inferInitialSettings(InAppWebViewSettings settings) { + if (params.shouldOverrideUrlLoading != null && + settings.useShouldOverrideUrlLoading == null) { + settings.useShouldOverrideUrlLoading = true; + } + if (params.onLoadResource != null && settings.useOnLoadResource == null) { + settings.useOnLoadResource = true; + } + if (params.onDownloadStartRequest != null && + settings.useOnDownloadStart == null) { + settings.useOnDownloadStart = true; + } + if (params.shouldInterceptAjaxRequest != null && + settings.useShouldInterceptAjaxRequest == null) { + settings.useShouldInterceptAjaxRequest = true; + } + if (params.shouldInterceptFetchRequest != null && + settings.useShouldInterceptFetchRequest == null) { + settings.useShouldInterceptFetchRequest = true; + } + if (params.shouldInterceptRequest != null && + settings.useShouldInterceptRequest == null) { + settings.useShouldInterceptRequest = true; + } + if (params.onRenderProcessGone != null && + settings.useOnRenderProcessGone == null) { + settings.useOnRenderProcessGone = true; + } + if (params.onNavigationResponse != null && + settings.useOnNavigationResponse == null) { + settings.useOnNavigationResponse = true; + } + } + + @override + bool isRunning() { + return _running; + } + + @override + Future setSize(Size size) async { + if (!_running) { + return; + } + + Map args = {}; + args.putIfAbsent('size', () => size.toMap()); + await channel?.invokeMethod('setSize', args); + } + + @override + Future getSize() async { + if (!_running) { + return null; + } + + Map args = {}; + Map sizeMap = + (await channel?.invokeMethod('getSize', args))?.cast(); + return MapSize.fromMap(sizeMap); + } + + @override + Future dispose() async { + if (!_running) { + return; + } + Map args = {}; + await channel?.invokeMethod('dispose', args); + disposeChannel(); + _started = false; + _running = false; + _webViewController?.dispose(); + _webViewController = null; + _controllerFromPlatform = null; + _macosParams.pullToRefreshController?.dispose(); + _macosParams.findInteractionController?.dispose(); + } +} + +extension InternalHeadlessInAppWebView on MacOSHeadlessInAppWebView { + Future internalDispose() async { + _started = false; + _running = false; + } +} diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart new file mode 100644 index 00000000..c1fe8925 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart @@ -0,0 +1,408 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'headless_in_app_webview.dart'; + +import '../find_interaction/find_interaction_controller.dart'; +import 'in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [PlatformInAppWebViewWidget]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +class MacOSInAppWebViewWidgetCreationParams + extends PlatformInAppWebViewWidgetCreationParams { + MacOSInAppWebViewWidgetCreationParams( + {super.controllerFromPlatform, + super.key, + super.layoutDirection, + super.gestureRecognizers, + super.headlessWebView, + super.keepAlive, + super.preventGestureDelay, + super.windowId, + super.onWebViewCreated, + super.onLoadStart, + super.onLoadStop, + @Deprecated('Use onReceivedError instead') super.onLoadError, + super.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") super.onLoadHttpError, + super.onReceivedHttpError, + super.onProgressChanged, + super.onConsoleMessage, + super.shouldOverrideUrlLoading, + super.onLoadResource, + super.onScrollChanged, + @Deprecated('Use onDownloadStartRequest instead') super.onDownloadStart, + super.onDownloadStartRequest, + @Deprecated('Use onLoadResourceWithCustomScheme instead') + super.onLoadResourceCustomScheme, + super.onLoadResourceWithCustomScheme, + super.onCreateWindow, + super.onCloseWindow, + super.onJsAlert, + super.onJsConfirm, + super.onJsPrompt, + super.onReceivedHttpAuthRequest, + super.onReceivedServerTrustAuthRequest, + super.onReceivedClientCertRequest, + @Deprecated('Use FindInteractionController.onFindResultReceived instead') + super.onFindResultReceived, + super.shouldInterceptAjaxRequest, + super.onAjaxReadyStateChange, + super.onAjaxProgress, + super.shouldInterceptFetchRequest, + super.onUpdateVisitedHistory, + @Deprecated("Use onPrintRequest instead") super.onPrint, + super.onPrintRequest, + super.onLongPressHitTestResult, + super.onEnterFullscreen, + super.onExitFullscreen, + super.onPageCommitVisible, + super.onTitleChanged, + super.onWindowFocus, + super.onWindowBlur, + super.onOverScrolled, + super.onZoomScaleChanged, + @Deprecated('Use onSafeBrowsingHit instead') + super.androidOnSafeBrowsingHit, + super.onSafeBrowsingHit, + @Deprecated('Use onPermissionRequest instead') + super.androidOnPermissionRequest, + super.onPermissionRequest, + @Deprecated('Use onGeolocationPermissionsShowPrompt instead') + super.androidOnGeolocationPermissionsShowPrompt, + super.onGeolocationPermissionsShowPrompt, + @Deprecated('Use onGeolocationPermissionsHidePrompt instead') + super.androidOnGeolocationPermissionsHidePrompt, + super.onGeolocationPermissionsHidePrompt, + @Deprecated('Use shouldInterceptRequest instead') + super.androidShouldInterceptRequest, + super.shouldInterceptRequest, + @Deprecated('Use onRenderProcessGone instead') + super.androidOnRenderProcessGone, + super.onRenderProcessGone, + @Deprecated('Use onRenderProcessResponsive instead') + super.androidOnRenderProcessResponsive, + super.onRenderProcessResponsive, + @Deprecated('Use onRenderProcessUnresponsive instead') + super.androidOnRenderProcessUnresponsive, + super.onRenderProcessUnresponsive, + @Deprecated('Use onFormResubmission instead') + super.androidOnFormResubmission, + super.onFormResubmission, + @Deprecated('Use onZoomScaleChanged instead') super.androidOnScaleChanged, + @Deprecated('Use onReceivedIcon instead') super.androidOnReceivedIcon, + super.onReceivedIcon, + @Deprecated('Use onReceivedTouchIconUrl instead') + super.androidOnReceivedTouchIconUrl, + super.onReceivedTouchIconUrl, + @Deprecated('Use onJsBeforeUnload instead') super.androidOnJsBeforeUnload, + super.onJsBeforeUnload, + @Deprecated('Use onReceivedLoginRequest instead') + super.androidOnReceivedLoginRequest, + super.onReceivedLoginRequest, + super.onPermissionRequestCanceled, + super.onRequestFocus, + @Deprecated('Use onWebContentProcessDidTerminate instead') + super.iosOnWebContentProcessDidTerminate, + super.onWebContentProcessDidTerminate, + @Deprecated( + 'Use onDidReceiveServerRedirectForProvisionalNavigation instead') + super.iosOnDidReceiveServerRedirectForProvisionalNavigation, + super.onDidReceiveServerRedirectForProvisionalNavigation, + @Deprecated('Use onNavigationResponse instead') + super.iosOnNavigationResponse, + super.onNavigationResponse, + @Deprecated('Use shouldAllowDeprecatedTLS instead') + super.iosShouldAllowDeprecatedTLS, + super.shouldAllowDeprecatedTLS, + super.onCameraCaptureStateChanged, + super.onMicrophoneCaptureStateChanged, + super.onContentSizeChanged, + super.initialUrlRequest, + super.initialFile, + super.initialData, + @Deprecated('Use initialSettings instead') super.initialOptions, + super.initialSettings, + super.contextMenu, + super.initialUserScripts, + super.pullToRefreshController, + this.findInteractionController}); + + /// Constructs a [MacOSInAppWebViewWidgetCreationParams] using a + /// [PlatformInAppWebViewWidgetCreationParams]. + MacOSInAppWebViewWidgetCreationParams.fromPlatformInAppWebViewWidgetCreationParams( + PlatformInAppWebViewWidgetCreationParams params) + : this( + controllerFromPlatform: params.controllerFromPlatform, + key: params.key, + layoutDirection: params.layoutDirection, + gestureRecognizers: params.gestureRecognizers, + headlessWebView: params.headlessWebView, + keepAlive: params.keepAlive, + preventGestureDelay: params.preventGestureDelay, + windowId: params.windowId, + onWebViewCreated: params.onWebViewCreated, + onLoadStart: params.onLoadStart, + onLoadStop: params.onLoadStop, + onLoadError: params.onLoadError, + onReceivedError: params.onReceivedError, + onLoadHttpError: params.onLoadHttpError, + onReceivedHttpError: params.onReceivedHttpError, + onProgressChanged: params.onProgressChanged, + onConsoleMessage: params.onConsoleMessage, + shouldOverrideUrlLoading: params.shouldOverrideUrlLoading, + onLoadResource: params.onLoadResource, + onScrollChanged: params.onScrollChanged, + onDownloadStart: params.onDownloadStart, + onDownloadStartRequest: params.onDownloadStartRequest, + onLoadResourceCustomScheme: params.onLoadResourceCustomScheme, + onLoadResourceWithCustomScheme: + params.onLoadResourceWithCustomScheme, + onCreateWindow: params.onCreateWindow, + onCloseWindow: params.onCloseWindow, + onJsAlert: params.onJsAlert, + onJsConfirm: params.onJsConfirm, + onJsPrompt: params.onJsPrompt, + onReceivedHttpAuthRequest: params.onReceivedHttpAuthRequest, + onReceivedServerTrustAuthRequest: + params.onReceivedServerTrustAuthRequest, + onReceivedClientCertRequest: params.onReceivedClientCertRequest, + onFindResultReceived: params.onFindResultReceived, + shouldInterceptAjaxRequest: params.shouldInterceptAjaxRequest, + onAjaxReadyStateChange: params.onAjaxReadyStateChange, + onAjaxProgress: params.onAjaxProgress, + shouldInterceptFetchRequest: params.shouldInterceptFetchRequest, + onUpdateVisitedHistory: params.onUpdateVisitedHistory, + onPrint: params.onPrint, + onPrintRequest: params.onPrintRequest, + onLongPressHitTestResult: params.onLongPressHitTestResult, + onEnterFullscreen: params.onEnterFullscreen, + onExitFullscreen: params.onExitFullscreen, + onPageCommitVisible: params.onPageCommitVisible, + onTitleChanged: params.onTitleChanged, + onWindowFocus: params.onWindowFocus, + onWindowBlur: params.onWindowBlur, + onOverScrolled: params.onOverScrolled, + onZoomScaleChanged: params.onZoomScaleChanged, + androidOnSafeBrowsingHit: params.androidOnSafeBrowsingHit, + onSafeBrowsingHit: params.onSafeBrowsingHit, + androidOnPermissionRequest: params.androidOnPermissionRequest, + onPermissionRequest: params.onPermissionRequest, + androidOnGeolocationPermissionsShowPrompt: + params.androidOnGeolocationPermissionsShowPrompt, + onGeolocationPermissionsShowPrompt: + params.onGeolocationPermissionsShowPrompt, + androidOnGeolocationPermissionsHidePrompt: + params.androidOnGeolocationPermissionsHidePrompt, + onGeolocationPermissionsHidePrompt: + params.onGeolocationPermissionsHidePrompt, + androidShouldInterceptRequest: params.androidShouldInterceptRequest, + shouldInterceptRequest: params.shouldInterceptRequest, + androidOnRenderProcessGone: params.androidOnRenderProcessGone, + onRenderProcessGone: params.onRenderProcessGone, + androidOnRenderProcessResponsive: + params.androidOnRenderProcessResponsive, + onRenderProcessResponsive: params.onRenderProcessResponsive, + androidOnRenderProcessUnresponsive: + params.androidOnRenderProcessUnresponsive, + onRenderProcessUnresponsive: params.onRenderProcessUnresponsive, + androidOnFormResubmission: params.androidOnFormResubmission, + onFormResubmission: params.onFormResubmission, + androidOnScaleChanged: params.androidOnScaleChanged, + androidOnReceivedIcon: params.androidOnReceivedIcon, + onReceivedIcon: params.onReceivedIcon, + androidOnReceivedTouchIconUrl: params.androidOnReceivedTouchIconUrl, + onReceivedTouchIconUrl: params.onReceivedTouchIconUrl, + androidOnJsBeforeUnload: params.androidOnJsBeforeUnload, + onJsBeforeUnload: params.onJsBeforeUnload, + androidOnReceivedLoginRequest: params.androidOnReceivedLoginRequest, + onReceivedLoginRequest: params.onReceivedLoginRequest, + onPermissionRequestCanceled: params.onPermissionRequestCanceled, + onRequestFocus: params.onRequestFocus, + iosOnWebContentProcessDidTerminate: + params.iosOnWebContentProcessDidTerminate, + onWebContentProcessDidTerminate: + params.onWebContentProcessDidTerminate, + iosOnDidReceiveServerRedirectForProvisionalNavigation: + params.iosOnDidReceiveServerRedirectForProvisionalNavigation, + onDidReceiveServerRedirectForProvisionalNavigation: + params.onDidReceiveServerRedirectForProvisionalNavigation, + iosOnNavigationResponse: params.iosOnNavigationResponse, + onNavigationResponse: params.onNavigationResponse, + iosShouldAllowDeprecatedTLS: params.iosShouldAllowDeprecatedTLS, + shouldAllowDeprecatedTLS: params.shouldAllowDeprecatedTLS, + onCameraCaptureStateChanged: params.onCameraCaptureStateChanged, + onMicrophoneCaptureStateChanged: + params.onMicrophoneCaptureStateChanged, + onContentSizeChanged: params.onContentSizeChanged, + initialUrlRequest: params.initialUrlRequest, + initialFile: params.initialFile, + initialData: params.initialData, + initialOptions: params.initialOptions, + initialSettings: params.initialSettings, + contextMenu: params.contextMenu, + initialUserScripts: params.initialUserScripts, + pullToRefreshController: params.pullToRefreshController, + findInteractionController: params.findInteractionController + as MacOSFindInteractionController?); + + @override + final MacOSFindInteractionController? findInteractionController; +} + +///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} +class MacOSInAppWebViewWidget extends PlatformInAppWebViewWidget { + /// Constructs a [MacOSInAppWebViewWidget]. + /// + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} + MacOSInAppWebViewWidget(PlatformInAppWebViewWidgetCreationParams params) + : super.implementation( + params is MacOSInAppWebViewWidgetCreationParams + ? params + : MacOSInAppWebViewWidgetCreationParams + .fromPlatformInAppWebViewWidgetCreationParams(params), + ); + + MacOSInAppWebViewWidgetCreationParams get _macosParams => + params as MacOSInAppWebViewWidgetCreationParams; + + WindowsInAppWebViewController? _controller; + + MacOSHeadlessInAppWebView? get _macosHeadlessInAppWebView => + params.headlessWebView as MacOSHeadlessInAppWebView?; + + @override + Widget build(BuildContext context) { + final initialSettings = params.initialSettings ?? InAppWebViewSettings(); + _inferInitialSettings(initialSettings); + + Map settingsMap = + (params.initialSettings != null ? initialSettings.toMap() : null) ?? + // ignore: deprecated_member_use_from_same_package + params.initialOptions?.toMap() ?? + initialSettings.toMap(); + + Map pullToRefreshSettings = + params.pullToRefreshController?.params.settings.toMap() ?? + // ignore: deprecated_member_use_from_same_package + params.pullToRefreshController?.params.options.toMap() ?? + PullToRefreshSettings(enabled: false).toMap(); + + if ((params.headlessWebView?.isRunning() ?? false) && + params.keepAlive != null) { + final headlessId = params.headlessWebView?.id; + if (headlessId != null) { + // force keep alive id to match headless webview id + params.keepAlive?.id = headlessId; + } + } + + return UiKitView( + viewType: 'com.pichillilorenzo/flutter_inappwebview', + onPlatformViewCreated: _onPlatformViewCreated, + gestureRecognizers: params.gestureRecognizers, + creationParams: { + 'initialUrlRequest': params.initialUrlRequest?.toMap(), + 'initialFile': params.initialFile, + 'initialData': params.initialData?.toMap(), + 'initialSettings': settingsMap, + 'contextMenu': params.contextMenu?.toMap() ?? {}, + 'windowId': params.windowId, + 'headlessWebViewId': params.headlessWebView?.isRunning() ?? false + ? params.headlessWebView?.id + : null, + 'initialUserScripts': + params.initialUserScripts?.map((e) => e.toMap()).toList() ?? [], + 'pullToRefreshSettings': pullToRefreshSettings, + 'keepAliveId': params.keepAlive?.id, + 'preventGestureDelay': params.preventGestureDelay + }, + creationParamsCodec: const StandardMessageCodec(), + ); + } + + void _onPlatformViewCreated(int id) { + dynamic viewId = id; + if (params.headlessWebView?.isRunning() ?? false) { + viewId = params.headlessWebView?.id; + } + viewId = params.keepAlive?.id ?? viewId ?? id; + _macosHeadlessInAppWebView?.internalDispose(); + _controller = WindowsInAppWebViewController( + PlatformInAppWebViewControllerCreationParams( + id: viewId, webviewParams: params)); + _macosParams.findInteractionController?.init(viewId); + debugLog( + className: runtimeType.toString(), + id: viewId?.toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: "onWebViewCreated", + args: []); + if (params.onWebViewCreated != null) { + params.onWebViewCreated!( + params.controllerFromPlatform?.call(_controller!) ?? _controller!); + } + } + + void _inferInitialSettings(InAppWebViewSettings settings) { + if (params.shouldOverrideUrlLoading != null && + settings.useShouldOverrideUrlLoading == null) { + settings.useShouldOverrideUrlLoading = true; + } + if (params.onLoadResource != null && settings.useOnLoadResource == null) { + settings.useOnLoadResource = true; + } + if (params.onDownloadStartRequest != null && + settings.useOnDownloadStart == null) { + settings.useOnDownloadStart = true; + } + if ((params.shouldInterceptAjaxRequest != null || + params.onAjaxProgress != null || + params.onAjaxReadyStateChange != null) && + settings.useShouldInterceptAjaxRequest == null) { + settings.useShouldInterceptAjaxRequest = true; + } + if (params.shouldInterceptFetchRequest != null && + settings.useShouldInterceptFetchRequest == null) { + settings.useShouldInterceptFetchRequest = true; + } + if (params.shouldInterceptRequest != null && + settings.useShouldInterceptRequest == null) { + settings.useShouldInterceptRequest = true; + } + if (params.onRenderProcessGone != null && + settings.useOnRenderProcessGone == null) { + settings.useOnRenderProcessGone = true; + } + if (params.onNavigationResponse != null && + settings.useOnNavigationResponse == null) { + settings.useOnNavigationResponse = true; + } + } + + @override + void dispose() { + dynamic viewId = _controller?.getViewId(); + debugLog( + className: runtimeType.toString(), + id: viewId?.toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: "dispose", + args: []); + final isKeepAlive = params.keepAlive != null; + _controller?.dispose(isKeepAlive: isKeepAlive); + _controller = null; + params.findInteractionController?.dispose(isKeepAlive: isKeepAlive); + } + + @override + T controllerFromPlatform(PlatformInAppWebViewController controller) { + // unused + throw UnimplementedError(); + } +} diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart new file mode 100644 index 00000000..12d07af4 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart @@ -0,0 +1,2689 @@ +import 'dart:io'; +import 'dart:collection'; +import 'dart:convert'; +import 'dart:core'; +import 'dart:developer' as developer; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../web_message/main.dart'; + +import '../in_app_browser/in_app_browser.dart'; +import '../web_storage/web_storage.dart'; + +import 'headless_in_app_webview.dart'; +import '_static_channel.dart'; + +import '../print_job/main.dart'; + +///List of forbidden names for JavaScript handlers. +// ignore: non_constant_identifier_names +final _JAVASCRIPT_HANDLER_FORBIDDEN_NAMES = UnmodifiableListView([ + "onLoadResource", + "shouldInterceptAjaxRequest", + "onAjaxReadyStateChange", + "onAjaxProgress", + "shouldInterceptFetchRequest", + "onPrintRequest", + "onWindowFocus", + "onWindowBlur", + "callAsyncJavaScript", + "evaluateJavaScriptWithContentWorld" +]); + +/// Object specifying creation parameters for creating a [WindowsInAppWebViewController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformInAppWebViewControllerCreationParams] for +/// more information. +@immutable +class WindowsInAppWebViewControllerCreationParams + extends PlatformInAppWebViewControllerCreationParams { + /// Creates a new [WindowsInAppWebViewControllerCreationParams] instance. + const WindowsInAppWebViewControllerCreationParams( + {required super.id, super.webviewParams}); + + /// Creates a [WindowsInAppWebViewControllerCreationParams] instance based on [PlatformInAppWebViewControllerCreationParams]. + factory WindowsInAppWebViewControllerCreationParams.fromPlatformInAppWebViewControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformInAppWebViewControllerCreationParams params) { + return WindowsInAppWebViewControllerCreationParams( + id: params.id, webviewParams: params.webviewParams); + } +} + +///Controls a WebView, such as an [InAppWebView] widget instance, a [MacOSHeadlessInAppWebView] instance or [WindowsInAppBrowser] WebView instance. +/// +///If you are using the [InAppWebView] widget, an [InAppWebViewController] instance can be obtained by setting the [InAppWebView.onWebViewCreated] +///callback. Instead, if you are using an [WindowsInAppBrowser] instance, you can get it through the [WindowsInAppBrowser.webViewController] attribute. +class WindowsInAppWebViewController extends PlatformInAppWebViewController + with ChannelController { + static final MethodChannel _staticChannel = IN_APP_WEBVIEW_STATIC_CHANNEL; + + // List of properties to be saved and restored for keep alive feature + Map _javaScriptHandlersMap = + HashMap(); + Map> _userScripts = { + UserScriptInjectionTime.AT_DOCUMENT_START: [], + UserScriptInjectionTime.AT_DOCUMENT_END: [] + }; + Set _webMessageListenerObjNames = Set(); + Map _injectedScriptsFromURL = {}; + Set _webMessageChannels = Set(); + Set _webMessageListeners = Set(); + + // static map that contains the properties to be saved and restored for keep alive feature + static final Map + _keepAliveMap = {}; + + WindowsInAppBrowser? _inAppBrowser; + + PlatformInAppBrowserEvents? get _inAppBrowserEventHandler => + _inAppBrowser?.eventHandler; + + dynamic _controllerFromPlatform; + + @override + late MacOSWebStorage webStorage; + + WindowsInAppWebViewController( + PlatformInAppWebViewControllerCreationParams params) + : super.implementation(params is WindowsInAppWebViewControllerCreationParams + ? params + : WindowsInAppWebViewControllerCreationParams + .fromPlatformInAppWebViewControllerCreationParams(params)) { + channel = MethodChannel('com.pichillilorenzo/flutter_inappwebview_$id'); + handler = handleMethod; + initMethodCallHandler(); + + final initialUserScripts = webviewParams?.initialUserScripts; + if (initialUserScripts != null) { + for (final userScript in initialUserScripts) { + if (userScript.injectionTime == + UserScriptInjectionTime.AT_DOCUMENT_START) { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_START] + ?.add(userScript); + } else { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_END] + ?.add(userScript); + } + } + } + + this._init(params); + } + + static final WindowsInAppWebViewController _staticValue = + WindowsInAppWebViewController( + WindowsInAppWebViewControllerCreationParams(id: null)); + + factory WindowsInAppWebViewController.static() { + return _staticValue; + } + + WindowsInAppWebViewController.fromInAppBrowser( + PlatformInAppWebViewControllerCreationParams params, + MethodChannel channel, + WindowsInAppBrowser inAppBrowser, + UnmodifiableListView? initialUserScripts) + : super.implementation(params is WindowsInAppWebViewControllerCreationParams + ? params + : WindowsInAppWebViewControllerCreationParams + .fromPlatformInAppWebViewControllerCreationParams(params)) { + this.channel = channel; + this._inAppBrowser = inAppBrowser; + + if (initialUserScripts != null) { + for (final userScript in initialUserScripts) { + if (userScript.injectionTime == + UserScriptInjectionTime.AT_DOCUMENT_START) { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_START] + ?.add(userScript); + } else { + this + ._userScripts[UserScriptInjectionTime.AT_DOCUMENT_END] + ?.add(userScript); + } + } + } + this._init(params); + } + + void _init(PlatformInAppWebViewControllerCreationParams params) { + _controllerFromPlatform = + params.webviewParams?.controllerFromPlatform?.call(this) ?? this; + + webStorage = MacOSWebStorage(MacOSWebStorageCreationParams( + localStorage: MacOSLocalStorage.defaultStorage(controller: this), + sessionStorage: MacOSSessionStorage.defaultStorage(controller: this))); + + if (params.webviewParams is PlatformInAppWebViewWidgetCreationParams) { + final keepAlive = + (params.webviewParams as PlatformInAppWebViewWidgetCreationParams) + .keepAlive; + if (keepAlive != null) { + InAppWebViewControllerKeepAliveProps? props = _keepAliveMap[keepAlive]; + if (props == null) { + // save controller properties to restore it later + _keepAliveMap[keepAlive] = InAppWebViewControllerKeepAliveProps( + injectedScriptsFromURL: _injectedScriptsFromURL, + javaScriptHandlersMap: _javaScriptHandlersMap, + userScripts: _userScripts, + webMessageListenerObjNames: _webMessageListenerObjNames, + webMessageChannels: _webMessageChannels, + webMessageListeners: _webMessageListeners); + } else { + // restore controller properties + _injectedScriptsFromURL = props.injectedScriptsFromURL; + _javaScriptHandlersMap = props.javaScriptHandlersMap; + _userScripts = props.userScripts; + _webMessageListenerObjNames = props.webMessageListenerObjNames; + _webMessageChannels = + props.webMessageChannels as Set; + _webMessageListeners = + props.webMessageListeners as Set; + } + } + } + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + name: _inAppBrowser == null + ? "WebView" + : _inAppBrowser.runtimeType.toString(), + id: (getViewId() ?? _inAppBrowser?.id).toString(), + debugLoggingSettings: + PlatformInAppWebViewController.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + if (PlatformInAppWebViewController.debugLoggingSettings.enabled && + call.method != "onCallJsHandler") { + _debugLog(call.method, call.arguments); + } + + switch (call.method) { + case "onLoadStart": + _injectedScriptsFromURL.clear(); + if ((webviewParams != null && webviewParams!.onLoadStart != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && webviewParams!.onLoadStart != null) + webviewParams!.onLoadStart!(_controllerFromPlatform, uri); + else + _inAppBrowserEventHandler!.onLoadStart(uri); + } + break; + case "onLoadStop": + if ((webviewParams != null && webviewParams!.onLoadStop != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && webviewParams!.onLoadStop != null) + webviewParams!.onLoadStop!(_controllerFromPlatform, uri); + else + _inAppBrowserEventHandler!.onLoadStop(uri); + } + break; + case "onReceivedError": + if ((webviewParams != null && + (webviewParams!.onReceivedError != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadError != null)) || + _inAppBrowserEventHandler != null) { + WebResourceRequest request = WebResourceRequest.fromMap( + call.arguments["request"].cast())!; + WebResourceError error = WebResourceError.fromMap( + call.arguments["error"].cast())!; + var isForMainFrame = request.isForMainFrame ?? false; + + if (webviewParams != null) { + if (webviewParams!.onReceivedError != null) + webviewParams!.onReceivedError!( + _controllerFromPlatform, request, error); + else if (isForMainFrame) { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadError!(_controllerFromPlatform, request.url, + error.type.toNativeValue() ?? -1, error.description); + } + } else { + if (isForMainFrame) { + _inAppBrowserEventHandler!.onLoadError(request.url, + error.type.toNativeValue() ?? -1, error.description); + } + _inAppBrowserEventHandler!.onReceivedError(request, error); + } + } + break; + case "onReceivedHttpError": + if ((webviewParams != null && + (webviewParams!.onReceivedHttpError != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadHttpError != null)) || + _inAppBrowserEventHandler != null) { + WebResourceRequest request = WebResourceRequest.fromMap( + call.arguments["request"].cast())!; + WebResourceResponse errorResponse = WebResourceResponse.fromMap( + call.arguments["errorResponse"].cast())!; + var isForMainFrame = request.isForMainFrame ?? false; + + if (webviewParams != null) { + if (webviewParams!.onReceivedHttpError != null) + webviewParams!.onReceivedHttpError!( + _controllerFromPlatform, request, errorResponse); + else if (isForMainFrame) { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadHttpError!( + _controllerFromPlatform, + request.url, + errorResponse.statusCode ?? -1, + errorResponse.reasonPhrase ?? ''); + } + } else { + if (isForMainFrame) { + _inAppBrowserEventHandler!.onLoadHttpError( + request.url, + errorResponse.statusCode ?? -1, + errorResponse.reasonPhrase ?? ''); + } + _inAppBrowserEventHandler! + .onReceivedHttpError(request, errorResponse); + } + } + break; + case "onProgressChanged": + if ((webviewParams != null && + webviewParams!.onProgressChanged != null) || + _inAppBrowserEventHandler != null) { + int progress = call.arguments["progress"]; + if (webviewParams != null && webviewParams!.onProgressChanged != null) + webviewParams!.onProgressChanged!( + _controllerFromPlatform, progress); + else + _inAppBrowserEventHandler!.onProgressChanged(progress); + } + break; + case "shouldOverrideUrlLoading": + if ((webviewParams != null && + webviewParams!.shouldOverrideUrlLoading != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + NavigationAction navigationAction = + NavigationAction.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.shouldOverrideUrlLoading != null) + return (await webviewParams!.shouldOverrideUrlLoading!( + _controllerFromPlatform, navigationAction)) + ?.toNativeValue(); + return (await _inAppBrowserEventHandler! + .shouldOverrideUrlLoading(navigationAction)) + ?.toNativeValue(); + } + break; + case "onConsoleMessage": + if ((webviewParams != null && + webviewParams!.onConsoleMessage != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + ConsoleMessage consoleMessage = ConsoleMessage.fromMap(arguments)!; + if (webviewParams != null && webviewParams!.onConsoleMessage != null) + webviewParams!.onConsoleMessage!( + _controllerFromPlatform, consoleMessage); + else + _inAppBrowserEventHandler!.onConsoleMessage(consoleMessage); + } + break; + case "onScrollChanged": + if ((webviewParams != null && webviewParams!.onScrollChanged != null) || + _inAppBrowserEventHandler != null) { + int x = call.arguments["x"]; + int y = call.arguments["y"]; + if (webviewParams != null && webviewParams!.onScrollChanged != null) + webviewParams!.onScrollChanged!(_controllerFromPlatform, x, y); + else + _inAppBrowserEventHandler!.onScrollChanged(x, y); + } + break; + case "onDownloadStartRequest": + if ((webviewParams != null && + // ignore: deprecated_member_use_from_same_package + (webviewParams!.onDownloadStart != null || + webviewParams!.onDownloadStartRequest != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + DownloadStartRequest downloadStartRequest = + DownloadStartRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onDownloadStartRequest != null) + webviewParams!.onDownloadStartRequest!( + _controllerFromPlatform, downloadStartRequest); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onDownloadStart!( + _controllerFromPlatform, downloadStartRequest.url); + } + } else { + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .onDownloadStart(downloadStartRequest.url); + _inAppBrowserEventHandler! + .onDownloadStartRequest(downloadStartRequest); + } + } + break; + case "onLoadResourceWithCustomScheme": + if ((webviewParams != null && + (webviewParams!.onLoadResourceWithCustomScheme != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onLoadResourceCustomScheme != null)) || + _inAppBrowserEventHandler != null) { + Map requestMap = + call.arguments["request"].cast(); + WebResourceRequest request = WebResourceRequest.fromMap(requestMap)!; + + if (webviewParams != null) { + if (webviewParams!.onLoadResourceWithCustomScheme != null) + return (await webviewParams!.onLoadResourceWithCustomScheme!( + _controllerFromPlatform, request)) + ?.toMap(); + else { + return (await params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .onLoadResourceCustomScheme!( + _controllerFromPlatform, request.url)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onLoadResourceWithCustomScheme(request)) ?? + (await _inAppBrowserEventHandler! + .onLoadResourceCustomScheme(request.url))) + ?.toMap(); + } + } + break; + case "onCreateWindow": + if ((webviewParams != null && webviewParams!.onCreateWindow != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + CreateWindowAction createWindowAction = + CreateWindowAction.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onCreateWindow != null) + return await webviewParams!.onCreateWindow!( + _controllerFromPlatform, createWindowAction); + else + return await _inAppBrowserEventHandler! + .onCreateWindow(createWindowAction); + } + break; + case "onCloseWindow": + if (webviewParams != null && webviewParams!.onCloseWindow != null) + webviewParams!.onCloseWindow!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onCloseWindow(); + break; + case "onTitleChanged": + if ((webviewParams != null && webviewParams!.onTitleChanged != null) || + _inAppBrowserEventHandler != null) { + String? title = call.arguments["title"]; + if (webviewParams != null && webviewParams!.onTitleChanged != null) + webviewParams!.onTitleChanged!(_controllerFromPlatform, title); + else + _inAppBrowserEventHandler!.onTitleChanged(title); + } + break; + case "onGeolocationPermissionsShowPrompt": + if ((webviewParams != null && + (webviewParams!.onGeolocationPermissionsShowPrompt != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnGeolocationPermissionsShowPrompt != + null)) || + _inAppBrowserEventHandler != null) { + String origin = call.arguments["origin"]; + + if (webviewParams != null) { + if (webviewParams!.onGeolocationPermissionsShowPrompt != null) + return (await webviewParams!.onGeolocationPermissionsShowPrompt!( + _controllerFromPlatform, origin)) + ?.toMap(); + else { + return (await params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .androidOnGeolocationPermissionsShowPrompt!( + _controllerFromPlatform, origin)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onGeolocationPermissionsShowPrompt(origin)) ?? + (await _inAppBrowserEventHandler! + .androidOnGeolocationPermissionsShowPrompt(origin))) + ?.toMap(); + } + } + break; + case "onGeolocationPermissionsHidePrompt": + if (webviewParams != null && + (webviewParams!.onGeolocationPermissionsHidePrompt != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnGeolocationPermissionsHidePrompt != + null)) { + if (webviewParams!.onGeolocationPermissionsHidePrompt != null) + webviewParams! + .onGeolocationPermissionsHidePrompt!(_controllerFromPlatform); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnGeolocationPermissionsHidePrompt!( + _controllerFromPlatform); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler!.onGeolocationPermissionsHidePrompt(); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnGeolocationPermissionsHidePrompt(); + } + break; + case "shouldInterceptRequest": + if ((webviewParams != null && + (webviewParams!.shouldInterceptRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidShouldInterceptRequest != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + WebResourceRequest request = WebResourceRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.shouldInterceptRequest != null) + return (await webviewParams!.shouldInterceptRequest!( + _controllerFromPlatform, request)) + ?.toMap(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidShouldInterceptRequest!( + _controllerFromPlatform, request)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .shouldInterceptRequest(request)) ?? + (await _inAppBrowserEventHandler! + .androidShouldInterceptRequest(request))) + ?.toMap(); + } + } + break; + case "onRenderProcessUnresponsive": + if ((webviewParams != null && + (webviewParams!.onRenderProcessUnresponsive != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessUnresponsive != + null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams != null) { + if (webviewParams!.onRenderProcessUnresponsive != null) + return (await webviewParams!.onRenderProcessUnresponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnRenderProcessUnresponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onRenderProcessUnresponsive(uri)) ?? + (await _inAppBrowserEventHandler! + .androidOnRenderProcessUnresponsive(uri))) + ?.toNativeValue(); + } + } + break; + case "onRenderProcessResponsive": + if ((webviewParams != null && + (webviewParams!.onRenderProcessResponsive != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessResponsive != null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams != null) { + if (webviewParams!.onRenderProcessResponsive != null) + return (await webviewParams!.onRenderProcessResponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnRenderProcessResponsive!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onRenderProcessResponsive(uri)) ?? + (await _inAppBrowserEventHandler! + .androidOnRenderProcessResponsive(uri))) + ?.toNativeValue(); + } + } + break; + case "onRenderProcessGone": + if ((webviewParams != null && + (webviewParams!.onRenderProcessGone != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessGone != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + RenderProcessGoneDetail detail = + RenderProcessGoneDetail.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onRenderProcessGone != null) + webviewParams!.onRenderProcessGone!( + _controllerFromPlatform, detail); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnRenderProcessGone!( + _controllerFromPlatform, detail); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler!.onRenderProcessGone(detail); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.androidOnRenderProcessGone(detail); + } + } + break; + case "onFormResubmission": + if ((webviewParams != null && + (webviewParams!.onFormResubmission != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnFormResubmission != null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + + if (webviewParams != null) { + if (webviewParams!.onFormResubmission != null) + return (await webviewParams!.onFormResubmission!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnFormResubmission!( + _controllerFromPlatform, uri)) + ?.toNativeValue(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onFormResubmission(uri)) ?? + // ignore: deprecated_member_use_from_same_package + (await _inAppBrowserEventHandler! + .androidOnFormResubmission(uri))) + ?.toNativeValue(); + } + } + break; + case "onZoomScaleChanged": + if ((webviewParams != null && + // ignore: deprecated_member_use_from_same_package + (webviewParams!.androidOnScaleChanged != null || + webviewParams!.onZoomScaleChanged != null)) || + _inAppBrowserEventHandler != null) { + double oldScale = call.arguments["oldScale"]; + double newScale = call.arguments["newScale"]; + + if (webviewParams != null) { + if (webviewParams!.onZoomScaleChanged != null) + webviewParams!.onZoomScaleChanged!( + _controllerFromPlatform, oldScale, newScale); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnScaleChanged!( + _controllerFromPlatform, oldScale, newScale); + } + } else { + _inAppBrowserEventHandler!.onZoomScaleChanged(oldScale, newScale); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnScaleChanged(oldScale, newScale); + } + } + break; + case "onReceivedIcon": + if ((webviewParams != null && + (webviewParams!.onReceivedIcon != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedIcon != null)) || + _inAppBrowserEventHandler != null) { + Uint8List icon = + Uint8List.fromList(call.arguments["icon"].cast()); + + if (webviewParams != null) { + if (webviewParams!.onReceivedIcon != null) + webviewParams!.onReceivedIcon!(_controllerFromPlatform, icon); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedIcon!( + _controllerFromPlatform, icon); + } + } else { + _inAppBrowserEventHandler!.onReceivedIcon(icon); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.androidOnReceivedIcon(icon); + } + } + break; + case "onReceivedTouchIconUrl": + if ((webviewParams != null && + (webviewParams!.onReceivedTouchIconUrl != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedTouchIconUrl != null)) || + _inAppBrowserEventHandler != null) { + String url = call.arguments["url"]; + bool precomposed = call.arguments["precomposed"]; + WebUri uri = WebUri(url); + + if (webviewParams != null) { + if (webviewParams!.onReceivedTouchIconUrl != null) + webviewParams!.onReceivedTouchIconUrl!( + _controllerFromPlatform, uri, precomposed); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedTouchIconUrl!( + _controllerFromPlatform, uri, precomposed); + } + } else { + _inAppBrowserEventHandler!.onReceivedTouchIconUrl(uri, precomposed); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnReceivedTouchIconUrl(uri, precomposed); + } + } + break; + case "onJsAlert": + if ((webviewParams != null && webviewParams!.onJsAlert != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsAlertRequest jsAlertRequest = JsAlertRequest.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onJsAlert != null) + return (await webviewParams!.onJsAlert!( + _controllerFromPlatform, jsAlertRequest)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler!.onJsAlert(jsAlertRequest)) + ?.toMap(); + } + break; + case "onJsConfirm": + if ((webviewParams != null && webviewParams!.onJsConfirm != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsConfirmRequest jsConfirmRequest = + JsConfirmRequest.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onJsConfirm != null) + return (await webviewParams!.onJsConfirm!( + _controllerFromPlatform, jsConfirmRequest)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onJsConfirm(jsConfirmRequest)) + ?.toMap(); + } + break; + case "onJsPrompt": + if ((webviewParams != null && webviewParams!.onJsPrompt != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsPromptRequest jsPromptRequest = JsPromptRequest.fromMap(arguments)!; + + if (webviewParams != null && webviewParams!.onJsPrompt != null) + return (await webviewParams!.onJsPrompt!( + _controllerFromPlatform, jsPromptRequest)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onJsPrompt(jsPromptRequest)) + ?.toMap(); + } + break; + case "onJsBeforeUnload": + if ((webviewParams != null && + (webviewParams!.onJsBeforeUnload != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnJsBeforeUnload != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + JsBeforeUnloadRequest jsBeforeUnloadRequest = + JsBeforeUnloadRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onJsBeforeUnload != null) + return (await webviewParams!.onJsBeforeUnload!( + _controllerFromPlatform, jsBeforeUnloadRequest)) + ?.toMap(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnJsBeforeUnload!( + _controllerFromPlatform, jsBeforeUnloadRequest)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onJsBeforeUnload(jsBeforeUnloadRequest)) ?? + (await _inAppBrowserEventHandler! + .androidOnJsBeforeUnload(jsBeforeUnloadRequest))) + ?.toMap(); + } + } + break; + case "onSafeBrowsingHit": + if ((webviewParams != null && + (webviewParams!.onSafeBrowsingHit != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnSafeBrowsingHit != null)) || + _inAppBrowserEventHandler != null) { + String url = call.arguments["url"]; + SafeBrowsingThreat? threatType = + SafeBrowsingThreat.fromNativeValue(call.arguments["threatType"]); + WebUri uri = WebUri(url); + + if (webviewParams != null) { + if (webviewParams!.onSafeBrowsingHit != null) + return (await webviewParams!.onSafeBrowsingHit!( + _controllerFromPlatform, uri, threatType)) + ?.toMap(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.androidOnSafeBrowsingHit!( + _controllerFromPlatform, uri, threatType)) + ?.toMap(); + } + } else { + return ((await _inAppBrowserEventHandler! + .onSafeBrowsingHit(uri, threatType)) ?? + (await _inAppBrowserEventHandler! + .androidOnSafeBrowsingHit(uri, threatType))) + ?.toMap(); + } + } + break; + case "onReceivedLoginRequest": + if ((webviewParams != null && + (webviewParams!.onReceivedLoginRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedLoginRequest != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + LoginRequest loginRequest = LoginRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onReceivedLoginRequest != null) + webviewParams!.onReceivedLoginRequest!( + _controllerFromPlatform, loginRequest); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnReceivedLoginRequest!( + _controllerFromPlatform, loginRequest); + } + } else { + _inAppBrowserEventHandler!.onReceivedLoginRequest(loginRequest); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler! + .androidOnReceivedLoginRequest(loginRequest); + } + } + break; + case "onPermissionRequestCanceled": + if ((webviewParams != null && + webviewParams!.onPermissionRequestCanceled != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + PermissionRequest permissionRequest = + PermissionRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onPermissionRequestCanceled != null) + webviewParams!.onPermissionRequestCanceled!( + _controllerFromPlatform, permissionRequest); + else + _inAppBrowserEventHandler! + .onPermissionRequestCanceled(permissionRequest); + } + break; + case "onRequestFocus": + if ((webviewParams != null && webviewParams!.onRequestFocus != null) || + _inAppBrowserEventHandler != null) { + if (webviewParams != null && webviewParams!.onRequestFocus != null) + webviewParams!.onRequestFocus!(_controllerFromPlatform); + else + _inAppBrowserEventHandler!.onRequestFocus(); + } + break; + case "onReceivedHttpAuthRequest": + if ((webviewParams != null && + webviewParams!.onReceivedHttpAuthRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + HttpAuthenticationChallenge challenge = + HttpAuthenticationChallenge.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onReceivedHttpAuthRequest != null) + return (await webviewParams!.onReceivedHttpAuthRequest!( + _controllerFromPlatform, challenge)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onReceivedHttpAuthRequest(challenge)) + ?.toMap(); + } + break; + case "onReceivedServerTrustAuthRequest": + if ((webviewParams != null && + webviewParams!.onReceivedServerTrustAuthRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + ServerTrustChallenge challenge = + ServerTrustChallenge.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onReceivedServerTrustAuthRequest != null) + return (await webviewParams!.onReceivedServerTrustAuthRequest!( + _controllerFromPlatform, challenge)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onReceivedServerTrustAuthRequest(challenge)) + ?.toMap(); + } + break; + case "onReceivedClientCertRequest": + if ((webviewParams != null && + webviewParams!.onReceivedClientCertRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + ClientCertChallenge challenge = + ClientCertChallenge.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onReceivedClientCertRequest != null) + return (await webviewParams!.onReceivedClientCertRequest!( + _controllerFromPlatform, challenge)) + ?.toMap(); + else + return (await _inAppBrowserEventHandler! + .onReceivedClientCertRequest(challenge)) + ?.toMap(); + } + break; + case "onFindResultReceived": + if ((webviewParams != null && + (webviewParams!.onFindResultReceived != null || + (webviewParams!.findInteractionController != null && + webviewParams!.findInteractionController!.params + .onFindResultReceived != + null))) || + _inAppBrowserEventHandler != null) { + int activeMatchOrdinal = call.arguments["activeMatchOrdinal"]; + int numberOfMatches = call.arguments["numberOfMatches"]; + bool isDoneCounting = call.arguments["isDoneCounting"]; + if (webviewParams != null) { + if (webviewParams!.findInteractionController != null && + webviewParams!.findInteractionController!.params + .onFindResultReceived != + null) + webviewParams! + .findInteractionController!.params.onFindResultReceived!( + webviewParams!.findInteractionController!, + activeMatchOrdinal, + numberOfMatches, + isDoneCounting); + else + webviewParams!.onFindResultReceived!(_controllerFromPlatform, + activeMatchOrdinal, numberOfMatches, isDoneCounting); + } else { + if (_inAppBrowser!.findInteractionController != null && + _inAppBrowser! + .findInteractionController!.onFindResultReceived != + null) + _inAppBrowser!.findInteractionController!.onFindResultReceived!( + webviewParams!.findInteractionController!, + activeMatchOrdinal, + numberOfMatches, + isDoneCounting); + else + _inAppBrowserEventHandler!.onFindResultReceived( + activeMatchOrdinal, numberOfMatches, isDoneCounting); + } + } + break; + case "onPermissionRequest": + if ((webviewParams != null && + (webviewParams!.onPermissionRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.androidOnPermissionRequest != null)) || + _inAppBrowserEventHandler != null) { + String origin = call.arguments["origin"]; + List resources = call.arguments["resources"].cast(); + + Map arguments = + call.arguments.cast(); + PermissionRequest permissionRequest = + PermissionRequest.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onPermissionRequest != null) + return (await webviewParams!.onPermissionRequest!( + _controllerFromPlatform, permissionRequest)) + ?.toMap(); + else { + return (await webviewParams!.androidOnPermissionRequest!( + _controllerFromPlatform, origin, resources)) + ?.toMap(); + } + } else { + return (await _inAppBrowserEventHandler! + .onPermissionRequest(permissionRequest)) + ?.toMap() ?? + (await _inAppBrowserEventHandler! + .androidOnPermissionRequest(origin, resources)) + ?.toMap(); + } + } + break; + case "onUpdateVisitedHistory": + if ((webviewParams != null && + webviewParams!.onUpdateVisitedHistory != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + bool? isReload = call.arguments["isReload"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && + webviewParams!.onUpdateVisitedHistory != null) + webviewParams!.onUpdateVisitedHistory!( + _controllerFromPlatform, uri, isReload); + else + _inAppBrowserEventHandler!.onUpdateVisitedHistory(uri, isReload); + } + break; + case "onWebContentProcessDidTerminate": + if (webviewParams != null && + (webviewParams!.onWebContentProcessDidTerminate != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.iosOnWebContentProcessDidTerminate != null)) { + if (webviewParams!.onWebContentProcessDidTerminate != null) + webviewParams! + .onWebContentProcessDidTerminate!(_controllerFromPlatform); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams! + .iosOnWebContentProcessDidTerminate!(_controllerFromPlatform); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler!.onWebContentProcessDidTerminate(); + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.iosOnWebContentProcessDidTerminate(); + } + break; + case "onPageCommitVisible": + if ((webviewParams != null && + webviewParams!.onPageCommitVisible != null) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + WebUri? uri = url != null ? WebUri(url) : null; + if (webviewParams != null && + webviewParams!.onPageCommitVisible != null) + webviewParams!.onPageCommitVisible!(_controllerFromPlatform, uri); + else + _inAppBrowserEventHandler!.onPageCommitVisible(uri); + } + break; + case "onDidReceiveServerRedirectForProvisionalNavigation": + if (webviewParams != null && + (webviewParams! + .onDidReceiveServerRedirectForProvisionalNavigation != + null || + params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .iosOnDidReceiveServerRedirectForProvisionalNavigation != + null)) { + if (webviewParams! + .onDidReceiveServerRedirectForProvisionalNavigation != + null) + webviewParams!.onDidReceiveServerRedirectForProvisionalNavigation!( + _controllerFromPlatform); + else { + params + .webviewParams! + // ignore: deprecated_member_use_from_same_package + .iosOnDidReceiveServerRedirectForProvisionalNavigation!( + _controllerFromPlatform); + } + } else if (_inAppBrowserEventHandler != null) { + _inAppBrowserEventHandler! + .onDidReceiveServerRedirectForProvisionalNavigation(); + _inAppBrowserEventHandler! + .iosOnDidReceiveServerRedirectForProvisionalNavigation(); + } + break; + case "onNavigationResponse": + if ((webviewParams != null && + (webviewParams!.onNavigationResponse != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.iosOnNavigationResponse != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + // ignore: deprecated_member_use_from_same_package + IOSWKNavigationResponse iosOnNavigationResponse = + // ignore: deprecated_member_use_from_same_package + IOSWKNavigationResponse.fromMap(arguments)!; + + NavigationResponse navigationResponse = + NavigationResponse.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.onNavigationResponse != null) + return (await webviewParams!.onNavigationResponse!( + _controllerFromPlatform, navigationResponse)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.iosOnNavigationResponse!( + _controllerFromPlatform, iosOnNavigationResponse)) + ?.toNativeValue(); + } + } else { + return (await _inAppBrowserEventHandler! + .onNavigationResponse(navigationResponse)) + ?.toNativeValue() ?? + (await _inAppBrowserEventHandler! + .iosOnNavigationResponse(iosOnNavigationResponse)) + ?.toNativeValue(); + } + } + break; + case "shouldAllowDeprecatedTLS": + if ((webviewParams != null && + (webviewParams!.shouldAllowDeprecatedTLS != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.iosShouldAllowDeprecatedTLS != null)) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + URLAuthenticationChallenge challenge = + URLAuthenticationChallenge.fromMap(arguments)!; + + if (webviewParams != null) { + if (webviewParams!.shouldAllowDeprecatedTLS != null) + return (await webviewParams!.shouldAllowDeprecatedTLS!( + _controllerFromPlatform, challenge)) + ?.toNativeValue(); + else { + // ignore: deprecated_member_use_from_same_package + return (await webviewParams!.iosShouldAllowDeprecatedTLS!( + _controllerFromPlatform, challenge)) + ?.toNativeValue(); + } + } else { + return (await _inAppBrowserEventHandler! + .shouldAllowDeprecatedTLS(challenge)) + ?.toNativeValue() ?? + // ignore: deprecated_member_use_from_same_package + (await _inAppBrowserEventHandler! + .iosShouldAllowDeprecatedTLS(challenge)) + ?.toNativeValue(); + } + } + break; + case "onLongPressHitTestResult": + if ((webviewParams != null && + webviewParams!.onLongPressHitTestResult != null) || + _inAppBrowserEventHandler != null) { + Map arguments = + call.arguments.cast(); + InAppWebViewHitTestResult hitTestResult = + InAppWebViewHitTestResult.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onLongPressHitTestResult != null) + webviewParams!.onLongPressHitTestResult!( + _controllerFromPlatform, hitTestResult); + else + _inAppBrowserEventHandler!.onLongPressHitTestResult(hitTestResult); + } + break; + case "onCreateContextMenu": + ContextMenu? contextMenu; + if (webviewParams != null && webviewParams!.contextMenu != null) { + contextMenu = webviewParams!.contextMenu; + } else if (_inAppBrowserEventHandler != null && + _inAppBrowser!.contextMenu != null) { + contextMenu = _inAppBrowser!.contextMenu; + } + + if (contextMenu != null && contextMenu.onCreateContextMenu != null) { + Map arguments = + call.arguments.cast(); + InAppWebViewHitTestResult hitTestResult = + InAppWebViewHitTestResult.fromMap(arguments)!; + + contextMenu.onCreateContextMenu!(hitTestResult); + } + break; + case "onHideContextMenu": + ContextMenu? contextMenu; + if (webviewParams != null && webviewParams!.contextMenu != null) { + contextMenu = webviewParams!.contextMenu; + } else if (_inAppBrowserEventHandler != null && + _inAppBrowser!.contextMenu != null) { + contextMenu = _inAppBrowser!.contextMenu; + } + + if (contextMenu != null && contextMenu.onHideContextMenu != null) { + contextMenu.onHideContextMenu!(); + } + break; + case "onContextMenuActionItemClicked": + ContextMenu? contextMenu; + if (webviewParams != null && webviewParams!.contextMenu != null) { + contextMenu = webviewParams!.contextMenu; + } else if (_inAppBrowserEventHandler != null && + _inAppBrowser!.contextMenu != null) { + contextMenu = _inAppBrowser!.contextMenu; + } + + if (contextMenu != null) { + int? androidId = call.arguments["androidId"]; + String? iosId = call.arguments["iosId"]; + dynamic id = call.arguments["id"]; + String title = call.arguments["title"]; + + ContextMenuItem menuItemClicked = ContextMenuItem( + id: id, + // ignore: deprecated_member_use_from_same_package + androidId: androidId, + // ignore: deprecated_member_use_from_same_package + iosId: iosId, + title: title, + action: null); + + for (var menuItem in contextMenu.menuItems) { + if (menuItem.id == id) { + menuItemClicked = menuItem; + if (menuItem.action != null) { + menuItem.action!(); + } + break; + } + } + + if (contextMenu.onContextMenuActionItemClicked != null) { + contextMenu.onContextMenuActionItemClicked!(menuItemClicked); + } + } + break; + case "onEnterFullscreen": + if (webviewParams != null && webviewParams!.onEnterFullscreen != null) + webviewParams!.onEnterFullscreen!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onEnterFullscreen(); + break; + case "onExitFullscreen": + if (webviewParams != null && webviewParams!.onExitFullscreen != null) + webviewParams!.onExitFullscreen!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onExitFullscreen(); + break; + case "onOverScrolled": + if ((webviewParams != null && webviewParams!.onOverScrolled != null) || + _inAppBrowserEventHandler != null) { + int x = call.arguments["x"]; + int y = call.arguments["y"]; + bool clampedX = call.arguments["clampedX"]; + bool clampedY = call.arguments["clampedY"]; + + if (webviewParams != null && webviewParams!.onOverScrolled != null) + webviewParams!.onOverScrolled!( + _controllerFromPlatform, x, y, clampedX, clampedY); + else + _inAppBrowserEventHandler!.onOverScrolled(x, y, clampedX, clampedY); + } + break; + case "onWindowFocus": + if (webviewParams != null && webviewParams!.onWindowFocus != null) + webviewParams!.onWindowFocus!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowFocus(); + break; + case "onWindowBlur": + if (webviewParams != null && webviewParams!.onWindowBlur != null) + webviewParams!.onWindowBlur!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowBlur(); + break; + case "onPrintRequest": + if ((webviewParams != null && + (webviewParams!.onPrintRequest != null || + // ignore: deprecated_member_use_from_same_package + webviewParams!.onPrint != null)) || + _inAppBrowserEventHandler != null) { + String? url = call.arguments["url"]; + String? printJobId = call.arguments["printJobId"]; + WebUri? uri = url != null ? WebUri(url) : null; + MacOSPrintJobController? printJob = printJobId != null + ? MacOSPrintJobController( + MacOSPrintJobControllerCreationParams(id: printJobId)) + : null; + + if (webviewParams != null) { + if (webviewParams!.onPrintRequest != null) + return await webviewParams!.onPrintRequest!( + _controllerFromPlatform, uri, printJob); + else { + // ignore: deprecated_member_use_from_same_package + webviewParams!.onPrint!(_controllerFromPlatform, uri); + return false; + } + } else { + // ignore: deprecated_member_use_from_same_package + _inAppBrowserEventHandler!.onPrint(uri); + return await _inAppBrowserEventHandler! + .onPrintRequest(uri, printJob); + } + } + break; + case "onInjectedScriptLoaded": + String id = call.arguments[0]; + var onLoadCallback = _injectedScriptsFromURL[id]?.onLoad; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onLoadCallback != null) { + onLoadCallback(); + } + break; + case "onInjectedScriptError": + String id = call.arguments[0]; + var onErrorCallback = _injectedScriptsFromURL[id]?.onError; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onErrorCallback != null) { + onErrorCallback(); + } + break; + case "onCameraCaptureStateChanged": + if ((webviewParams != null && + webviewParams!.onCameraCaptureStateChanged != null) || + _inAppBrowserEventHandler != null) { + var oldState = + MediaCaptureState.fromNativeValue(call.arguments["oldState"]); + var newState = + MediaCaptureState.fromNativeValue(call.arguments["newState"]); + + if (webviewParams != null && + webviewParams!.onCameraCaptureStateChanged != null) + webviewParams!.onCameraCaptureStateChanged!( + _controllerFromPlatform, oldState, newState); + else + _inAppBrowserEventHandler! + .onCameraCaptureStateChanged(oldState, newState); + } + break; + case "onMicrophoneCaptureStateChanged": + if ((webviewParams != null && + webviewParams!.onMicrophoneCaptureStateChanged != null) || + _inAppBrowserEventHandler != null) { + var oldState = + MediaCaptureState.fromNativeValue(call.arguments["oldState"]); + var newState = + MediaCaptureState.fromNativeValue(call.arguments["newState"]); + + if (webviewParams != null && + webviewParams!.onMicrophoneCaptureStateChanged != null) + webviewParams!.onMicrophoneCaptureStateChanged!( + _controllerFromPlatform, oldState, newState); + else + _inAppBrowserEventHandler! + .onMicrophoneCaptureStateChanged(oldState, newState); + } + break; + case "onContentSizeChanged": + if ((webviewParams != null && + webviewParams!.onContentSizeChanged != null) || + _inAppBrowserEventHandler != null) { + var oldContentSize = MapSize.fromMap( + call.arguments["oldContentSize"]?.cast())!; + var newContentSize = MapSize.fromMap( + call.arguments["newContentSize"]?.cast())!; + + if (webviewParams != null && + webviewParams!.onContentSizeChanged != null) + webviewParams!.onContentSizeChanged!( + _controllerFromPlatform, oldContentSize, newContentSize); + else + _inAppBrowserEventHandler! + .onContentSizeChanged(oldContentSize, newContentSize); + } + break; + case "onCallJsHandler": + String handlerName = call.arguments["handlerName"]; + // decode args to json + List args = jsonDecode(call.arguments["args"]); + + _debugLog(handlerName, args); + + switch (handlerName) { + case "onLoadResource": + if ((webviewParams != null && + webviewParams!.onLoadResource != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + arguments["startTime"] = arguments["startTime"] is int + ? arguments["startTime"].toDouble() + : arguments["startTime"]; + arguments["duration"] = arguments["duration"] is int + ? arguments["duration"].toDouble() + : arguments["duration"]; + + var response = LoadedResource.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onLoadResource != null) + webviewParams!.onLoadResource!( + _controllerFromPlatform, response); + else + _inAppBrowserEventHandler!.onLoadResource(response); + } + return null; + case "shouldInterceptAjaxRequest": + if ((webviewParams != null && + webviewParams!.shouldInterceptAjaxRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + AjaxRequest request = AjaxRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.shouldInterceptAjaxRequest != null) + return jsonEncode( + await params.webviewParams!.shouldInterceptAjaxRequest!( + _controllerFromPlatform, request)); + else + return jsonEncode(await _inAppBrowserEventHandler! + .shouldInterceptAjaxRequest(request)); + } + return null; + case "onAjaxReadyStateChange": + if ((webviewParams != null && + webviewParams!.onAjaxReadyStateChange != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + AjaxRequest request = AjaxRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onAjaxReadyStateChange != null) + return (await webviewParams!.onAjaxReadyStateChange!( + _controllerFromPlatform, request)) + ?.toNativeValue(); + else + return (await _inAppBrowserEventHandler! + .onAjaxReadyStateChange(request)) + ?.toNativeValue(); + } + return null; + case "onAjaxProgress": + if ((webviewParams != null && + webviewParams!.onAjaxProgress != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + AjaxRequest request = AjaxRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.onAjaxProgress != null) + return (await webviewParams!.onAjaxProgress!( + _controllerFromPlatform, request)) + ?.toNativeValue(); + else + return (await _inAppBrowserEventHandler! + .onAjaxProgress(request)) + ?.toNativeValue(); + } + return null; + case "shouldInterceptFetchRequest": + if ((webviewParams != null && + webviewParams!.shouldInterceptFetchRequest != null) || + _inAppBrowserEventHandler != null) { + Map arguments = args[0].cast(); + FetchRequest request = FetchRequest.fromMap(arguments)!; + + if (webviewParams != null && + webviewParams!.shouldInterceptFetchRequest != null) + return jsonEncode( + await webviewParams!.shouldInterceptFetchRequest!( + _controllerFromPlatform, request)); + else + return jsonEncode(await _inAppBrowserEventHandler! + .shouldInterceptFetchRequest(request)); + } + return null; + case "onWindowFocus": + if (webviewParams != null && webviewParams!.onWindowFocus != null) + webviewParams!.onWindowFocus!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowFocus(); + return null; + case "onWindowBlur": + if (webviewParams != null && webviewParams!.onWindowBlur != null) + webviewParams!.onWindowBlur!(_controllerFromPlatform); + else if (_inAppBrowserEventHandler != null) + _inAppBrowserEventHandler!.onWindowBlur(); + return null; + case "onInjectedScriptLoaded": + String id = args[0]; + var onLoadCallback = _injectedScriptsFromURL[id]?.onLoad; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onLoadCallback != null) { + onLoadCallback(); + } + return null; + case "onInjectedScriptError": + String id = args[0]; + var onErrorCallback = _injectedScriptsFromURL[id]?.onError; + if ((webviewParams != null || _inAppBrowserEventHandler != null) && + onErrorCallback != null) { + onErrorCallback(); + } + return null; + } + + if (_javaScriptHandlersMap.containsKey(handlerName)) { + // convert result to json + try { + return jsonEncode(await _javaScriptHandlersMap[handlerName]!(args)); + } catch (error, stacktrace) { + developer.log(error.toString() + '\n' + stacktrace.toString(), + name: 'JavaScript Handler "$handlerName"'); + throw Exception(error.toString().replaceFirst('Exception: ', '')); + } + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + Future getUrl() async { + Map args = {}; + String? url = await channel?.invokeMethod('getUrl', args); + return url != null ? WebUri(url) : null; + } + + @override + Future getTitle() async { + Map args = {}; + return await channel?.invokeMethod('getTitle', args); + } + + @override + Future getProgress() async { + Map args = {}; + return await channel?.invokeMethod('getProgress', args); + } + + @override + Future getHtml() async { + String? html; + + InAppWebViewSettings? settings = await getSettings(); + if (settings != null && settings.javaScriptEnabled == true) { + html = await evaluateJavascript( + source: "window.document.getElementsByTagName('html')[0].outerHTML;"); + if (html != null && html.isNotEmpty) return html; + } + + var webviewUrl = await getUrl(); + if (webviewUrl == null) { + return html; + } + + if (webviewUrl.isScheme("file")) { + var assetPathSplit = webviewUrl.toString().split("/flutter_assets/"); + var assetPath = assetPathSplit[assetPathSplit.length - 1]; + try { + var bytes = await rootBundle.load(assetPath); + html = utf8.decode(bytes.buffer.asUint8List()); + } catch (e) {} + } else { + try { + HttpClient client = HttpClient(); + var htmlRequest = await client.getUrl(webviewUrl); + html = + await (await htmlRequest.close()).transform(Utf8Decoder()).join(); + } catch (e) { + developer.log(e.toString(), name: this.runtimeType.toString()); + } + } + + return html; + } + + @override + Future> getFavicons() async { + List favicons = []; + + var webviewUrl = await getUrl(); + + if (webviewUrl == null) { + return favicons; + } + + String? manifestUrl; + + var html = await getHtml(); + if (html == null || html.isEmpty) { + return favicons; + } + var assetPathBase; + + if (webviewUrl.isScheme("file")) { + var assetPathSplit = webviewUrl.toString().split("/flutter_assets/"); + assetPathBase = assetPathSplit[0] + "/flutter_assets/"; + } + + InAppWebViewSettings? settings = await getSettings(); + if (settings != null && settings.javaScriptEnabled == true) { + List> links = (await evaluateJavascript(source: """ +(function() { + var linkNodes = document.head.getElementsByTagName("link"); + var links = []; + for (var i = 0; i < linkNodes.length; i++) { + var linkNode = linkNodes[i]; + if (linkNode.rel === 'manifest') { + links.push( + { + rel: linkNode.rel, + href: linkNode.href, + sizes: null + } + ); + } else if (linkNode.rel != null && linkNode.rel.indexOf('icon') >= 0) { + links.push( + { + rel: linkNode.rel, + href: linkNode.href, + sizes: linkNode.sizes != null && linkNode.sizes.value != "" ? linkNode.sizes.value : null + } + ); + } + } + return links; +})(); +"""))?.cast>() ?? []; + for (var link in links) { + if (link["rel"] == "manifest") { + manifestUrl = link["href"]; + if (!_isUrlAbsolute(manifestUrl!)) { + if (manifestUrl.startsWith("/")) { + manifestUrl = manifestUrl.substring(1); + } + manifestUrl = ((assetPathBase == null) + ? webviewUrl.scheme + "://" + webviewUrl.host + "/" + : assetPathBase) + + manifestUrl; + } + continue; + } + favicons.addAll(_createFavicons(webviewUrl, assetPathBase, link["href"], + link["rel"], link["sizes"], false)); + } + } + + // try to get /favicon.ico + try { + HttpClient client = HttpClient(); + var faviconUrl = + webviewUrl.scheme + "://" + webviewUrl.host + "/favicon.ico"; + var faviconUri = WebUri(faviconUrl); + var headRequest = await client.headUrl(faviconUri); + var headResponse = await headRequest.close(); + if (headResponse.statusCode == 200) { + favicons.add(Favicon(url: faviconUri, rel: "shortcut icon")); + } + } catch (e) { + developer.log("/favicon.ico file not found: " + e.toString(), + name: runtimeType.toString()); + } + + // try to get the manifest file + HttpClientRequest? manifestRequest; + HttpClientResponse? manifestResponse; + bool manifestFound = false; + if (manifestUrl == null) { + manifestUrl = + webviewUrl.scheme + "://" + webviewUrl.host + "/manifest.json"; + } + try { + HttpClient client = HttpClient(); + manifestRequest = await client.getUrl(Uri.parse(manifestUrl)); + manifestResponse = await manifestRequest.close(); + manifestFound = manifestResponse.statusCode == 200 && + manifestResponse.headers.contentType?.mimeType == "application/json"; + } catch (e) { + developer.log("Manifest file not found: " + e.toString(), + name: this.runtimeType.toString()); + } + + if (manifestFound) { + try { + Map manifest = json + .decode(await manifestResponse!.transform(Utf8Decoder()).join()); + if (manifest.containsKey("icons")) { + for (Map icon in manifest["icons"]) { + favicons.addAll(_createFavicons(webviewUrl, assetPathBase, + icon["src"], icon["rel"], icon["sizes"], true)); + } + } + } catch (e) { + developer.log( + "Cannot get favicons from Manifest file. It might not have a valid format: " + + e.toString(), + error: e, + name: runtimeType.toString()); + } + } + + return favicons; + } + + bool _isUrlAbsolute(String url) { + return url.startsWith("http://") || url.startsWith("https://"); + } + + List _createFavicons(WebUri url, String? assetPathBase, + String urlIcon, String? rel, String? sizes, bool isManifest) { + List favicons = []; + + List urlSplit = urlIcon.split("/"); + if (!_isUrlAbsolute(urlIcon)) { + if (urlIcon.startsWith("/")) { + urlIcon = urlIcon.substring(1); + } + urlIcon = ((assetPathBase == null) + ? url.scheme + "://" + url.host + "/" + : assetPathBase) + + urlIcon; + } + if (isManifest) { + rel = (sizes != null) + ? urlSplit[urlSplit.length - 1] + .replaceFirst("-" + sizes, "") + .split(" ")[0] + .split(".")[0] + : null; + } + if (sizes != null && sizes.isNotEmpty && sizes != "any") { + List sizesSplit = sizes.split(" "); + for (String size in sizesSplit) { + int width = int.parse(size.split("x")[0]); + int height = int.parse(size.split("x")[1]); + favicons.add(Favicon( + url: WebUri(urlIcon), rel: rel, width: width, height: height)); + } + } else { + favicons.add( + Favicon(url: WebUri(urlIcon), rel: rel, width: null, height: null)); + } + + return favicons; + } + + @override + Future loadUrl( + {required URLRequest urlRequest, + @Deprecated('Use allowingReadAccessTo instead') + Uri? iosAllowingReadAccessTo, + WebUri? allowingReadAccessTo}) async { + assert(urlRequest.url != null && urlRequest.url.toString().isNotEmpty); + assert( + allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file")); + assert(iosAllowingReadAccessTo == null || + iosAllowingReadAccessTo.isScheme("file")); + + Map args = {}; + args.putIfAbsent('urlRequest', () => urlRequest.toMap()); + args.putIfAbsent( + 'allowingReadAccessTo', + () => + allowingReadAccessTo?.toString() ?? + iosAllowingReadAccessTo?.toString()); + await channel?.invokeMethod('loadUrl', args); + } + + @override + Future postUrl( + {required WebUri url, required Uint8List postData}) async { + assert(url.toString().isNotEmpty); + Map args = {}; + args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('postData', () => postData); + await channel?.invokeMethod('postUrl', args); + } + + @override + Future loadData( + {required String data, + String mimeType = "text/html", + String encoding = "utf8", + WebUri? baseUrl, + @Deprecated('Use historyUrl instead') Uri? androidHistoryUrl, + WebUri? historyUrl, + @Deprecated('Use allowingReadAccessTo instead') + Uri? iosAllowingReadAccessTo, + WebUri? allowingReadAccessTo}) async { + assert( + allowingReadAccessTo == null || allowingReadAccessTo.isScheme("file")); + assert(iosAllowingReadAccessTo == null || + iosAllowingReadAccessTo.isScheme("file")); + + Map args = {}; + args.putIfAbsent('data', () => data); + args.putIfAbsent('mimeType', () => mimeType); + args.putIfAbsent('encoding', () => encoding); + args.putIfAbsent('baseUrl', () => baseUrl?.toString() ?? "about:blank"); + args.putIfAbsent( + 'historyUrl', + () => + historyUrl?.toString() ?? + androidHistoryUrl?.toString() ?? + "about:blank"); + args.putIfAbsent( + 'allowingReadAccessTo', + () => + allowingReadAccessTo?.toString() ?? + iosAllowingReadAccessTo?.toString()); + await channel?.invokeMethod('loadData', args); + } + + @override + Future loadFile({required String assetFilePath}) async { + assert(assetFilePath.isNotEmpty); + Map args = {}; + args.putIfAbsent('assetFilePath', () => assetFilePath); + await channel?.invokeMethod('loadFile', args); + } + + @override + Future reload() async { + Map args = {}; + await channel?.invokeMethod('reload', args); + } + + @override + Future goBack() async { + Map args = {}; + await channel?.invokeMethod('goBack', args); + } + + @override + Future canGoBack() async { + Map args = {}; + return await channel?.invokeMethod('canGoBack', args) ?? false; + } + + @override + Future goForward() async { + Map args = {}; + await channel?.invokeMethod('goForward', args); + } + + @override + Future canGoForward() async { + Map args = {}; + return await channel?.invokeMethod('canGoForward', args) ?? false; + } + + @override + Future goBackOrForward({required int steps}) async { + Map args = {}; + args.putIfAbsent('steps', () => steps); + await channel?.invokeMethod('goBackOrForward', args); + } + + @override + Future canGoBackOrForward({required int steps}) async { + Map args = {}; + args.putIfAbsent('steps', () => steps); + return await channel?.invokeMethod('canGoBackOrForward', args) ?? + false; + } + + @override + Future goTo({required WebHistoryItem historyItem}) async { + var steps = historyItem.offset; + if (steps != null) { + await goBackOrForward(steps: steps); + } + } + + @override + Future isLoading() async { + Map args = {}; + return await channel?.invokeMethod('isLoading', args) ?? false; + } + + @override + Future stopLoading() async { + Map args = {}; + await channel?.invokeMethod('stopLoading', args); + } + + @override + Future evaluateJavascript( + {required String source, ContentWorld? contentWorld}) async { + Map args = {}; + args.putIfAbsent('source', () => source); + args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); + var data = await channel?.invokeMethod('evaluateJavascript', args); + return data; + } + + @override + Future injectJavascriptFileFromUrl( + {required WebUri urlFile, + ScriptHtmlTagAttributes? scriptHtmlTagAttributes}) async { + assert(urlFile.toString().isNotEmpty); + var id = scriptHtmlTagAttributes?.id; + if (scriptHtmlTagAttributes != null && id != null) { + _injectedScriptsFromURL[id] = scriptHtmlTagAttributes; + } + Map args = {}; + args.putIfAbsent('urlFile', () => urlFile.toString()); + args.putIfAbsent( + 'scriptHtmlTagAttributes', () => scriptHtmlTagAttributes?.toMap()); + await channel?.invokeMethod('injectJavascriptFileFromUrl', args); + } + + @override + Future injectJavascriptFileFromAsset( + {required String assetFilePath}) async { + String source = await rootBundle.loadString(assetFilePath); + return await evaluateJavascript(source: source); + } + + @override + Future injectCSSCode({required String source}) async { + Map args = {}; + args.putIfAbsent('source', () => source); + await channel?.invokeMethod('injectCSSCode', args); + } + + @override + Future injectCSSFileFromUrl( + {required WebUri urlFile, + CSSLinkHtmlTagAttributes? cssLinkHtmlTagAttributes}) async { + assert(urlFile.toString().isNotEmpty); + Map args = {}; + args.putIfAbsent('urlFile', () => urlFile.toString()); + args.putIfAbsent( + 'cssLinkHtmlTagAttributes', () => cssLinkHtmlTagAttributes?.toMap()); + await channel?.invokeMethod('injectCSSFileFromUrl', args); + } + + @override + Future injectCSSFileFromAsset({required String assetFilePath}) async { + String source = await rootBundle.loadString(assetFilePath); + await injectCSSCode(source: source); + } + + @override + void addJavaScriptHandler( + {required String handlerName, + required JavaScriptHandlerCallback callback}) { + assert(!_JAVASCRIPT_HANDLER_FORBIDDEN_NAMES.contains(handlerName), + '"$handlerName" is a forbidden name!'); + this._javaScriptHandlersMap[handlerName] = (callback); + } + + @override + JavaScriptHandlerCallback? removeJavaScriptHandler( + {required String handlerName}) { + return this._javaScriptHandlersMap.remove(handlerName); + } + + @override + bool hasJavaScriptHandler({required String handlerName}) { + return this._javaScriptHandlersMap.containsKey(handlerName); + } + + @override + Future takeScreenshot( + {ScreenshotConfiguration? screenshotConfiguration}) async { + Map args = {}; + args.putIfAbsent( + 'screenshotConfiguration', () => screenshotConfiguration?.toMap()); + return await channel?.invokeMethod('takeScreenshot', args); + } + + @override + @Deprecated('Use setSettings instead') + Future setOptions({required InAppWebViewGroupOptions options}) async { + InAppWebViewSettings settings = + InAppWebViewSettings.fromMap(options.toMap()) ?? InAppWebViewSettings(); + await setSettings(settings: settings); + } + + @override + @Deprecated('Use getSettings instead') + Future getOptions() async { + InAppWebViewSettings? settings = await getSettings(); + + Map? options = settings?.toMap(); + if (options != null) { + options = options.cast(); + return InAppWebViewGroupOptions.fromMap(options as Map); + } + + return null; + } + + @override + Future setSettings({required InAppWebViewSettings settings}) async { + Map args = {}; + + args.putIfAbsent('settings', () => settings.toMap()); + await channel?.invokeMethod('setSettings', args); + } + + @override + Future getSettings() async { + Map args = {}; + + Map? settings = + await channel?.invokeMethod('getSettings', args); + if (settings != null) { + settings = settings.cast(); + return InAppWebViewSettings.fromMap(settings as Map); + } + + return null; + } + + @override + Future getCopyBackForwardList() async { + Map args = {}; + Map? result = + (await channel?.invokeMethod('getCopyBackForwardList', args)) + ?.cast(); + return WebHistory.fromMap(result); + } + + @override + @Deprecated("Use InAppWebViewController.clearAllCache instead") + Future clearCache() async { + Map args = {}; + await channel?.invokeMethod('clearCache', args); + } + + @override + @Deprecated("Use FindInteractionController.findAll instead") + Future findAllAsync({required String find}) async { + Map args = {}; + args.putIfAbsent('find', () => find); + await channel?.invokeMethod('findAll', args); + } + + @override + @Deprecated("Use FindInteractionController.findNext instead") + Future findNext({required bool forward}) async { + Map args = {}; + args.putIfAbsent('forward', () => forward); + await channel?.invokeMethod('findNext', args); + } + + @override + @Deprecated("Use FindInteractionController.clearMatches instead") + Future clearMatches() async { + Map args = {}; + await channel?.invokeMethod('clearMatches', args); + } + + @override + @Deprecated("Use tRexRunnerHtml instead") + Future getTRexRunnerHtml() async { + return await tRexRunnerHtml; + } + + @override + @Deprecated("Use tRexRunnerCss instead") + Future getTRexRunnerCss() async { + return await tRexRunnerCss; + } + + @override + Future scrollTo( + {required int x, required int y, bool animated = false}) async { + Map args = {}; + args.putIfAbsent('x', () => x); + args.putIfAbsent('y', () => y); + args.putIfAbsent('animated', () => animated); + await channel?.invokeMethod('scrollTo', args); + } + + @override + Future scrollBy( + {required int x, required int y, bool animated = false}) async { + Map args = {}; + args.putIfAbsent('x', () => x); + args.putIfAbsent('y', () => y); + args.putIfAbsent('animated', () => animated); + await channel?.invokeMethod('scrollBy', args); + } + + @override + Future pauseTimers() async { + Map args = {}; + await channel?.invokeMethod('pauseTimers', args); + } + + @override + Future resumeTimers() async { + Map args = {}; + await channel?.invokeMethod('resumeTimers', args); + } + + @override + Future printCurrentPage( + {PrintJobSettings? settings}) async { + Map args = {}; + args.putIfAbsent("settings", () => settings?.toMap()); + String? jobId = + await channel?.invokeMethod('printCurrentPage', args); + if (jobId != null) { + return MacOSPrintJobController( + PlatformPrintJobControllerCreationParams(id: jobId)); + } + return null; + } + + @override + Future getContentHeight() async { + Map args = {}; + var height = await channel?.invokeMethod('getContentHeight', args); + if (height == null || height == 0) { + // try to use javascript + var scrollHeight = await evaluateJavascript( + source: "document.documentElement.scrollHeight;"); + if (scrollHeight != null && scrollHeight is num) { + height = scrollHeight.toInt(); + } + } + return height; + } + + @override + Future getContentWidth() async { + Map args = {}; + var height = await channel?.invokeMethod('getContentWidth', args); + if (height == null || height == 0) { + // try to use javascript + var scrollHeight = await evaluateJavascript( + source: "document.documentElement.scrollWidth;"); + if (scrollHeight != null && scrollHeight is num) { + height = scrollHeight.toInt(); + } + } + return height; + } + + @override + Future zoomBy( + {required double zoomFactor, + @Deprecated('Use animated instead') bool? iosAnimated, + bool animated = false}) async { + Map args = {}; + args.putIfAbsent('zoomFactor', () => zoomFactor); + args.putIfAbsent('animated', () => iosAnimated ?? animated); + return await channel?.invokeMethod('zoomBy', args); + } + + @override + Future getOriginalUrl() async { + Map args = {}; + String? url = await channel?.invokeMethod('getOriginalUrl', args); + return url != null ? WebUri(url) : null; + } + + @override + @Deprecated('Use getZoomScale instead') + Future getScale() async { + return await getZoomScale(); + } + + @override + Future getSelectedText() async { + Map args = {}; + return await channel?.invokeMethod('getSelectedText', args); + } + + @override + Future> getMetaTags() async { + List metaTags = []; + + List>? metaTagList = + (await evaluateJavascript(source: """ +(function() { + var metaTags = []; + var metaTagNodes = document.head.getElementsByTagName('meta'); + for (var i = 0; i < metaTagNodes.length; i++) { + var metaTagNode = metaTagNodes[i]; + + var otherAttributes = metaTagNode.getAttributeNames(); + var nameIndex = otherAttributes.indexOf("name"); + if (nameIndex !== -1) otherAttributes.splice(nameIndex, 1); + var contentIndex = otherAttributes.indexOf("content"); + if (contentIndex !== -1) otherAttributes.splice(contentIndex, 1); + + var attrs = []; + for (var j = 0; j < otherAttributes.length; j++) { + var otherAttribute = otherAttributes[j]; + attrs.push( + { + name: otherAttribute, + value: metaTagNode.getAttribute(otherAttribute) + } + ); + } + + metaTags.push( + { + name: metaTagNode.name, + content: metaTagNode.content, + attrs: attrs + } + ); + } + return metaTags; +})(); + """))?.cast>(); + + if (metaTagList == null) { + return metaTags; + } + + for (var metaTag in metaTagList) { + var attrs = []; + + for (var metaTagAttr in metaTag["attrs"]) { + attrs.add(MetaTagAttribute( + name: metaTagAttr["name"], value: metaTagAttr["value"])); + } + + metaTags.add(MetaTag( + name: metaTag["name"], content: metaTag["content"], attrs: attrs)); + } + + return metaTags; + } + + @override + Future getMetaThemeColor() async { + Color? themeColor; + + try { + Map args = {}; + themeColor = UtilColor.fromStringRepresentation( + await channel?.invokeMethod('getMetaThemeColor', args)); + return themeColor; + } catch (e) { + // not implemented + } + + // try using javascript + var metaTags = await getMetaTags(); + MetaTag? metaTagThemeColor; + + for (var metaTag in metaTags) { + if (metaTag.name == "theme-color") { + metaTagThemeColor = metaTag; + break; + } + } + + if (metaTagThemeColor == null) { + return null; + } + + var colorValue = metaTagThemeColor.content; + + themeColor = colorValue != null + ? UtilColor.fromStringRepresentation(colorValue) + : null; + + return themeColor; + } + + @override + Future getScrollX() async { + Map args = {}; + return await channel?.invokeMethod('getScrollX', args); + } + + @override + Future getScrollY() async { + Map args = {}; + return await channel?.invokeMethod('getScrollY', args); + } + + @override + Future getCertificate() async { + Map args = {}; + Map? sslCertificateMap = + (await channel?.invokeMethod('getCertificate', args)) + ?.cast(); + return SslCertificate.fromMap(sslCertificateMap); + } + + @override + Future addUserScript({required UserScript userScript}) async { + assert(webviewParams?.windowId == null); + + Map args = {}; + args.putIfAbsent('userScript', () => userScript.toMap()); + if (!(_userScripts[userScript.injectionTime]?.contains(userScript) ?? + false)) { + _userScripts[userScript.injectionTime]?.add(userScript); + await channel?.invokeMethod('addUserScript', args); + } + } + + @override + Future addUserScripts({required List userScripts}) async { + assert(webviewParams?.windowId == null); + + for (var i = 0; i < userScripts.length; i++) { + await addUserScript(userScript: userScripts[i]); + } + } + + @override + Future removeUserScript({required UserScript userScript}) async { + assert(webviewParams?.windowId == null); + + var index = _userScripts[userScript.injectionTime]?.indexOf(userScript); + if (index == null || index == -1) { + return false; + } + + _userScripts[userScript.injectionTime]?.remove(userScript); + Map args = {}; + args.putIfAbsent('userScript', () => userScript.toMap()); + args.putIfAbsent('index', () => index); + await channel?.invokeMethod('removeUserScript', args); + + return true; + } + + @override + Future removeUserScriptsByGroupName({required String groupName}) async { + assert(webviewParams?.windowId == null); + + final List userScriptsAtDocumentStart = List.from( + _userScripts[UserScriptInjectionTime.AT_DOCUMENT_START] ?? []); + for (final userScript in userScriptsAtDocumentStart) { + if (userScript.groupName == groupName) { + _userScripts[userScript.injectionTime]?.remove(userScript); + } + } + + final List userScriptsAtDocumentEnd = + List.from(_userScripts[UserScriptInjectionTime.AT_DOCUMENT_END] ?? []); + for (final userScript in userScriptsAtDocumentEnd) { + if (userScript.groupName == groupName) { + _userScripts[userScript.injectionTime]?.remove(userScript); + } + } + + Map args = {}; + args.putIfAbsent('groupName', () => groupName); + await channel?.invokeMethod('removeUserScriptsByGroupName', args); + } + + @override + Future removeUserScripts( + {required List userScripts}) async { + assert(webviewParams?.windowId == null); + + for (final userScript in userScripts) { + await removeUserScript(userScript: userScript); + } + } + + @override + Future removeAllUserScripts() async { + assert(webviewParams?.windowId == null); + + _userScripts[UserScriptInjectionTime.AT_DOCUMENT_START]?.clear(); + _userScripts[UserScriptInjectionTime.AT_DOCUMENT_END]?.clear(); + + Map args = {}; + await channel?.invokeMethod('removeAllUserScripts', args); + } + + @override + bool hasUserScript({required UserScript userScript}) { + return _userScripts[userScript.injectionTime]?.contains(userScript) ?? + false; + } + + @override + Future callAsyncJavaScript( + {required String functionBody, + Map arguments = const {}, + ContentWorld? contentWorld}) async { + Map args = {}; + args.putIfAbsent('functionBody', () => functionBody); + args.putIfAbsent('arguments', () => arguments); + args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); + var data = await channel?.invokeMethod('callAsyncJavaScript', args); + if (data == null) { + return null; + } + return CallAsyncJavaScriptResult( + value: data["value"], error: data["error"]); + } + + @override + Future saveWebArchive( + {required String filePath, bool autoname = false}) async { + if (!autoname) { + assert( + filePath.endsWith("." + WebArchiveFormat.WEBARCHIVE.toNativeValue())); + } + + Map args = {}; + args.putIfAbsent("filePath", () => filePath); + args.putIfAbsent("autoname", () => autoname); + return await channel?.invokeMethod('saveWebArchive', args); + } + + @override + Future isSecureContext() async { + Map args = {}; + return await channel?.invokeMethod('isSecureContext', args) ?? false; + } + + @override + Future createWebMessageChannel() async { + Map args = {}; + Map? result = + (await channel?.invokeMethod('createWebMessageChannel', args)) + ?.cast(); + final webMessageChannel = MacOSWebMessageChannel.static().fromMap(result); + if (webMessageChannel != null) { + _webMessageChannels.add(webMessageChannel); + } + return webMessageChannel; + } + + @override + Future postWebMessage( + {required WebMessage message, WebUri? targetOrigin}) async { + if (targetOrigin == null) { + targetOrigin = WebUri(''); + } + Map args = {}; + args.putIfAbsent('message', () => message.toMap()); + args.putIfAbsent('targetOrigin', () => targetOrigin.toString()); + await channel?.invokeMethod('postWebMessage', args); + } + + @override + Future addWebMessageListener( + PlatformWebMessageListener webMessageListener) async { + assert(!_webMessageListeners.contains(webMessageListener), + "${webMessageListener} was already added."); + assert( + !_webMessageListenerObjNames + .contains(webMessageListener.params.jsObjectName), + "jsObjectName ${webMessageListener.params.jsObjectName} was already added."); + _webMessageListeners.add(webMessageListener as MacOSWebMessageListener); + _webMessageListenerObjNames.add(webMessageListener.params.jsObjectName); + + Map args = {}; + args.putIfAbsent('webMessageListener', () => webMessageListener.toMap()); + await channel?.invokeMethod('addWebMessageListener', args); + } + + @override + bool hasWebMessageListener(PlatformWebMessageListener webMessageListener) { + return _webMessageListeners.contains(webMessageListener) || + _webMessageListenerObjNames + .contains(webMessageListener.params.jsObjectName); + } + + @override + Future canScrollVertically() async { + Map args = {}; + return await channel?.invokeMethod('canScrollVertically', args) ?? + false; + } + + @override + Future canScrollHorizontally() async { + Map args = {}; + return await channel?.invokeMethod('canScrollHorizontally', args) ?? + false; + } + + @override + Future reloadFromOrigin() async { + Map args = {}; + await channel?.invokeMethod('reloadFromOrigin', args); + } + + @override + Future createPdf( + {@Deprecated("Use pdfConfiguration instead") + // ignore: deprecated_member_use_from_same_package + IOSWKPDFConfiguration? iosWKPdfConfiguration, + PDFConfiguration? pdfConfiguration}) async { + Map args = {}; + args.putIfAbsent('pdfConfiguration', + () => pdfConfiguration?.toMap() ?? iosWKPdfConfiguration?.toMap()); + return await channel?.invokeMethod('createPdf', args); + } + + @override + Future createWebArchiveData() async { + Map args = {}; + return await channel?.invokeMethod('createWebArchiveData', args); + } + + @override + Future hasOnlySecureContent() async { + Map args = {}; + return await channel?.invokeMethod('hasOnlySecureContent', args) ?? + false; + } + + @override + Future pauseAllMediaPlayback() async { + Map args = {}; + return await channel?.invokeMethod('pauseAllMediaPlayback', args); + } + + @override + Future setAllMediaPlaybackSuspended({required bool suspended}) async { + Map args = {}; + args.putIfAbsent("suspended", () => suspended); + return await channel?.invokeMethod('setAllMediaPlaybackSuspended', args); + } + + @override + Future closeAllMediaPresentations() async { + Map args = {}; + return await channel?.invokeMethod('closeAllMediaPresentations', args); + } + + @override + Future requestMediaPlaybackState() async { + Map args = {}; + return MediaPlaybackState.fromNativeValue( + await channel?.invokeMethod('requestMediaPlaybackState', args)); + } + + @override + Future isInFullscreen() async { + Map args = {}; + return await channel?.invokeMethod('isInFullscreen', args) ?? false; + } + + @override + Future getCameraCaptureState() async { + Map args = {}; + return MediaCaptureState.fromNativeValue( + await channel?.invokeMethod('getCameraCaptureState', args)); + } + + @override + Future setCameraCaptureState({required MediaCaptureState state}) async { + Map args = {}; + args.putIfAbsent('state', () => state.toNativeValue()); + await channel?.invokeMethod('setCameraCaptureState', args); + } + + @override + Future getMicrophoneCaptureState() async { + Map args = {}; + return MediaCaptureState.fromNativeValue( + await channel?.invokeMethod('getMicrophoneCaptureState', args)); + } + + @override + Future setMicrophoneCaptureState( + {required MediaCaptureState state}) async { + Map args = {}; + args.putIfAbsent('state', () => state.toNativeValue()); + await channel?.invokeMethod('setMicrophoneCaptureState', args); + } + + @override + Future loadSimulatedRequest( + {required URLRequest urlRequest, + required Uint8List data, + URLResponse? urlResponse}) async { + Map args = {}; + args.putIfAbsent('urlRequest', () => urlRequest.toMap()); + args.putIfAbsent('data', () => data); + args.putIfAbsent('urlResponse', () => urlResponse?.toMap()); + await channel?.invokeMethod('loadSimulatedRequest', args); + } + + @override + Future getDefaultUserAgent() async { + Map args = {}; + return await _staticChannel.invokeMethod( + 'getDefaultUserAgent', args) ?? + ''; + } + + @override + Future handlesURLScheme(String urlScheme) async { + Map args = {}; + args.putIfAbsent('urlScheme', () => urlScheme); + return await _staticChannel.invokeMethod('handlesURLScheme', args); + } + + @override + Future disposeKeepAlive(InAppWebViewKeepAlive keepAlive) async { + Map args = {}; + args.putIfAbsent('keepAliveId', () => keepAlive.id); + await _staticChannel.invokeMethod('disposeKeepAlive', args); + _keepAliveMap[keepAlive] = null; + } + + @override + Future clearAllCache({bool includeDiskFiles = true}) async { + Map args = {}; + args.putIfAbsent('includeDiskFiles', () => includeDiskFiles); + await _staticChannel.invokeMethod('clearAllCache', args); + } + + @override + Future get tRexRunnerHtml async => await rootBundle.loadString( + 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.html'); + + @override + Future get tRexRunnerCss async => await rootBundle.loadString( + 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.css'); + + @override + dynamic getViewId() { + return id; + } + + @override + void dispose({bool isKeepAlive = false}) { + disposeChannel(removeMethodCallHandler: !isKeepAlive); + _inAppBrowser = null; + webStorage.dispose(); + if (!isKeepAlive) { + _controllerFromPlatform = null; + _javaScriptHandlersMap.clear(); + _userScripts.clear(); + _webMessageListenerObjNames.clear(); + _injectedScriptsFromURL.clear(); + for (final webMessageChannel in _webMessageChannels) { + webMessageChannel.dispose(); + } + _webMessageChannels.clear(); + for (final webMessageListener in _webMessageListeners) { + webMessageListener.dispose(); + } + _webMessageListeners.clear(); + } + } +} + +extension InternalInAppWebViewController on WindowsInAppWebViewController { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/main.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/main.dart new file mode 100644 index 00000000..b83b0611 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/main.dart @@ -0,0 +1,3 @@ +export 'in_app_webview_controller.dart' hide InternalInAppWebViewController; +export 'in_app_webview.dart'; +export 'headless_in_app_webview.dart' hide InternalHeadlessInAppWebView; diff --git a/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart b/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart new file mode 100644 index 00000000..1cd197d0 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart @@ -0,0 +1,32 @@ +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import 'in_app_browser/in_app_browser.dart'; + +/// Implementation of [InAppWebViewPlatform] using the WebKit API. +class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { + /// Registers this class as the default instance of [InAppWebViewPlatform]. + static void registerWith() { + InAppWebViewPlatform.instance = WindowsInAppWebViewPlatform(); + } + + /// Creates a new [WindowsInAppBrowser]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppBrowser] in `flutter_inappwebview` instead. + @override + WindowsInAppBrowser createPlatformInAppBrowser( + PlatformInAppBrowserCreationParams params, + ) { + return WindowsInAppBrowser(params); + } + + /// Creates a new empty [WindowsInAppBrowser] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppBrowser] in `flutter_inappwebview` instead. + @override + WindowsInAppBrowser createPlatformInAppBrowserStatic() { + return WindowsInAppBrowser.static(); + } + +} diff --git a/flutter_inappwebview_windows/lib/src/main.dart b/flutter_inappwebview_windows/lib/src/main.dart new file mode 100644 index 00000000..f5fe68f1 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/main.dart @@ -0,0 +1,10 @@ +export 'inappwebview_platform.dart'; +export 'in_app_webview/main.dart'; +export 'in_app_browser/main.dart'; +export 'web_storage/main.dart'; +export 'cookie_manager.dart' hide InternalCookieManager; +export 'http_auth_credentials_database.dart' + hide InternalHttpAuthCredentialDatabase; +export 'web_message/main.dart'; +export 'print_job/main.dart'; +export 'find_interaction/main.dart'; diff --git a/flutter_inappwebview_windows/lib/src/platform_util.dart b/flutter_inappwebview_windows/lib/src/platform_util.dart new file mode 100644 index 00000000..27e96478 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/platform_util.dart @@ -0,0 +1,64 @@ +import 'package:flutter/services.dart'; + +///Platform native utilities +class PlatformUtil { + static PlatformUtil? _instance; + static const MethodChannel _channel = + MethodChannel('com.pichillilorenzo/flutter_inappwebview_platformutil'); + + PlatformUtil._(); + + ///Get [PlatformUtil] instance. + static PlatformUtil instance() { + return (_instance != null) ? _instance! : _init(); + } + + static PlatformUtil _init() { + _channel.setMethodCallHandler((call) async { + try { + return await _handleMethod(call); + } on Error catch (e) { + print(e); + print(e.stackTrace); + } + }); + _instance = PlatformUtil._(); + return _instance!; + } + + static Future _handleMethod(MethodCall call) async {} + + String? _cachedSystemVersion; + + ///Get current platform system version. + Future getSystemVersion() async { + if (_cachedSystemVersion != null) { + return _cachedSystemVersion!; + } + Map args = {}; + _cachedSystemVersion = + await _channel.invokeMethod('getSystemVersion', args); + return _cachedSystemVersion!; + } + + ///Format date. + Future formatDate( + {required DateTime date, + required String format, + String locale = "en_US", + String timezone = "UTC"}) async { + Map args = {}; + args.putIfAbsent('date', () => date.millisecondsSinceEpoch); + args.putIfAbsent('format', () => format); + args.putIfAbsent('locale', () => locale); + args.putIfAbsent('timezone', () => timezone); + return await _channel.invokeMethod('formatDate', args); + } + + ///Get cookie expiration date used by Web platform. + Future getWebCookieExpirationDate({required DateTime date}) async { + Map args = {}; + args.putIfAbsent('date', () => date.millisecondsSinceEpoch); + return await _channel.invokeMethod('getWebCookieExpirationDate', args); + } +} diff --git a/flutter_inappwebview_windows/lib/src/print_job/main.dart b/flutter_inappwebview_windows/lib/src/print_job/main.dart new file mode 100644 index 00000000..4e70ad94 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/print_job/main.dart @@ -0,0 +1 @@ +export 'print_job_controller.dart'; diff --git a/flutter_inappwebview_windows/lib/src/print_job/print_job_controller.dart b/flutter_inappwebview_windows/lib/src/print_job/print_job_controller.dart new file mode 100644 index 00000000..be3136d6 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/print_job/print_job_controller.dart @@ -0,0 +1,73 @@ +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 [MacOSPrintJobController]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformPrintJobControllerCreationParams] for +/// more information. +@immutable +class MacOSPrintJobControllerCreationParams + extends PlatformPrintJobControllerCreationParams { + /// Creates a new [MacOSPrintJobControllerCreationParams] instance. + const MacOSPrintJobControllerCreationParams( + {required super.id, super.onComplete}); + + /// Creates a [MacOSPrintJobControllerCreationParams] instance based on [PlatformPrintJobControllerCreationParams]. + factory MacOSPrintJobControllerCreationParams.fromPlatformPrintJobControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformPrintJobControllerCreationParams params) { + return MacOSPrintJobControllerCreationParams( + id: params.id, onComplete: params.onComplete); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformPrintJobController} +class MacOSPrintJobController extends PlatformPrintJobController + with ChannelController { + /// Constructs a [MacOSPrintJobController]. + MacOSPrintJobController(PlatformPrintJobControllerCreationParams params) + : super.implementation( + params is MacOSPrintJobControllerCreationParams + ? params + : MacOSPrintJobControllerCreationParams + .fromPlatformPrintJobControllerCreationParams(params), + ) { + onComplete = params.onComplete; + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_printjobcontroller_${params.id}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onComplete": + bool completed = call.arguments["completed"]; + String? error = call.arguments["error"]; + if (onComplete != null) { + onComplete!(completed, error); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + } + + @override + Future getInfo() async { + Map args = {}; + Map? infoMap = + (await channel?.invokeMethod('getInfo', args))?.cast(); + return PrintJobInfo.fromMap(infoMap); + } + + @override + Future dispose() async { + Map args = {}; + await channel?.invokeMethod('dispose', args); + disposeChannel(); + } +} diff --git a/flutter_inappwebview_windows/lib/src/web_message/main.dart b/flutter_inappwebview_windows/lib/src/web_message/main.dart new file mode 100644 index 00000000..d41e30c7 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/web_message/main.dart @@ -0,0 +1,3 @@ +export 'web_message_port.dart' hide InternalWebMessagePort; +export 'web_message_channel.dart' hide InternalWebMessageChannel; +export 'web_message_listener.dart'; diff --git a/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart b/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart new file mode 100644 index 00000000..aedbc5d1 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart @@ -0,0 +1,120 @@ +import 'package:flutter/foundation.dart'; +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]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebMessageChannelCreationParams] for +/// more information. +@immutable +class MacOSWebMessageChannelCreationParams + extends PlatformWebMessageChannelCreationParams { + /// Creates a new [MacOSWebMessageChannelCreationParams] instance. + const MacOSWebMessageChannelCreationParams( + {required super.id, required super.port1, required super.port2}); + + /// Creates a [MacOSWebMessageChannelCreationParams] instance based on [PlatformWebMessageChannelCreationParams]. + factory MacOSWebMessageChannelCreationParams.fromPlatformWebMessageChannelCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebMessageChannelCreationParams params) { + return MacOSWebMessageChannelCreationParams( + id: params.id, port1: params.port1, port2: params.port2); + } + + @override + String toString() { + return 'MacOSWebMessageChannelCreationParams{id: $id, port1: $port1, port2: $port2}'; + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageChannel} +class MacOSWebMessageChannel extends PlatformWebMessageChannel + with ChannelController { + /// Constructs a [MacOSWebMessageChannel]. + MacOSWebMessageChannel(PlatformWebMessageChannelCreationParams params) + : super.implementation( + params is MacOSWebMessageChannelCreationParams + ? params + : MacOSWebMessageChannelCreationParams + .fromPlatformWebMessageChannelCreationParams(params), + ) { + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_web_message_channel_${params.id}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + static final MacOSWebMessageChannel _staticValue = MacOSWebMessageChannel( + MacOSWebMessageChannelCreationParams( + id: '', + port1: + MacOSWebMessagePort(MacOSWebMessagePortCreationParams(index: 0)), + port2: MacOSWebMessagePort( + MacOSWebMessagePortCreationParams(index: 1)))); + + /// Provide static access. + factory MacOSWebMessageChannel.static() { + return _staticValue; + } + + MacOSWebMessagePort get _macosPort1 => port1 as MacOSWebMessagePort; + + MacOSWebMessagePort get _macosPort2 => port2 as MacOSWebMessagePort; + + static MacOSWebMessageChannel? _fromMap(Map? map) { + if (map == null) { + return null; + } + var webMessageChannel = MacOSWebMessageChannel( + MacOSWebMessageChannelCreationParams( + id: map["id"], + port1: MacOSWebMessagePort( + MacOSWebMessagePortCreationParams(index: 0)), + port2: MacOSWebMessagePort( + MacOSWebMessagePortCreationParams(index: 1)))); + webMessageChannel._macosPort1.webMessageChannel = webMessageChannel; + webMessageChannel._macosPort2.webMessageChannel = webMessageChannel; + return webMessageChannel; + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onMessage": + int index = call.arguments["index"]; + var port = index == 0 ? _macosPort1 : _macosPort2; + if (port.onMessage != null) { + WebMessage? message = call.arguments["message"] != null + ? WebMessage.fromMap( + call.arguments["message"].cast()) + : null; + port.onMessage!(message); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + MacOSWebMessageChannel? fromMap(Map? map) { + return _fromMap(map); + } + + @override + void dispose() { + disposeChannel(); + } + + @override + String toString() { + return 'MacOSWebMessageChannel{id: $id, port1: $port1, port2: $port2}'; + } +} + +extension InternalWebMessageChannel on MacOSWebMessageChannel { + MethodChannel? get internalChannel => channel; +} diff --git a/flutter_inappwebview_windows/lib/src/web_message/web_message_listener.dart b/flutter_inappwebview_windows/lib/src/web_message/web_message_listener.dart new file mode 100644 index 00000000..b2791eba --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/web_message/web_message_listener.dart @@ -0,0 +1,164 @@ +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 [MacOSWebMessageListener]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebMessageListenerCreationParams] for +/// more information. +@immutable +class MacOSWebMessageListenerCreationParams + extends PlatformWebMessageListenerCreationParams { + /// Creates a new [MacOSWebMessageListenerCreationParams] instance. + const MacOSWebMessageListenerCreationParams( + {required this.allowedOriginRules, + required super.jsObjectName, + super.onPostMessage}); + + /// Creates a [MacOSWebMessageListenerCreationParams] instance based on [PlatformWebMessageListenerCreationParams]. + factory MacOSWebMessageListenerCreationParams.fromPlatformWebMessageListenerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebMessageListenerCreationParams params) { + return MacOSWebMessageListenerCreationParams( + allowedOriginRules: params.allowedOriginRules ?? Set.from(["*"]), + jsObjectName: params.jsObjectName, + onPostMessage: params.onPostMessage); + } + + @override + final Set allowedOriginRules; + + @override + String toString() { + return 'MacOSWebMessageListenerCreationParams{jsObjectName: $jsObjectName, allowedOriginRules: $allowedOriginRules, onPostMessage: $onPostMessage}'; + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageListener} +class MacOSWebMessageListener extends PlatformWebMessageListener + with ChannelController { + /// Constructs a [MacOSWebMessageListener]. + MacOSWebMessageListener(PlatformWebMessageListenerCreationParams params) + : super.implementation( + params is MacOSWebMessageListenerCreationParams + ? params + : MacOSWebMessageListenerCreationParams + .fromPlatformWebMessageListenerCreationParams(params), + ) { + assert(!this._macosParams.allowedOriginRules.contains(""), + "allowedOriginRules cannot contain empty strings"); + channel = MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_web_message_listener_${_id}_${params.jsObjectName}'); + handler = _handleMethod; + initMethodCallHandler(); + } + + ///Message Listener ID used internally. + final String _id = IdGenerator.generate(); + + MacOSJavaScriptReplyProxy? _replyProxy; + + MacOSWebMessageListenerCreationParams get _macosParams => + params as MacOSWebMessageListenerCreationParams; + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case "onPostMessage": + if (_replyProxy == null) { + _replyProxy = MacOSJavaScriptReplyProxy( + PlatformJavaScriptReplyProxyCreationParams( + webMessageListener: this)); + } + if (onPostMessage != null) { + WebMessage? message = call.arguments["message"] != null + ? WebMessage.fromMap( + call.arguments["message"].cast()) + : null; + WebUri? sourceOrigin = call.arguments["sourceOrigin"] != null + ? WebUri(call.arguments["sourceOrigin"]) + : null; + bool isMainFrame = call.arguments["isMainFrame"]; + onPostMessage!(message, sourceOrigin, isMainFrame, _replyProxy!); + } + break; + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + void dispose() { + disposeChannel(); + } + + @override + Map toMap() { + return { + "id": _id, + "jsObjectName": params.jsObjectName, + "allowedOriginRules": _macosParams.allowedOriginRules.toList(), + }; + } + + @override + Map toJson() { + return this.toMap(); + } + + @override + String toString() { + return 'MacOSWebMessageListener{id: ${_id}, jsObjectName: ${params.jsObjectName}, allowedOriginRules: ${params.allowedOriginRules}, replyProxy: $_replyProxy}'; + } +} + +/// Object specifying creation parameters for creating a [MacOSJavaScriptReplyProxy]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformJavaScriptReplyProxyCreationParams] for +/// more information. +@immutable +class MacOSJavaScriptReplyProxyCreationParams + extends PlatformJavaScriptReplyProxyCreationParams { + /// Creates a new [MacOSJavaScriptReplyProxyCreationParams] instance. + const MacOSJavaScriptReplyProxyCreationParams( + {required super.webMessageListener}); + + /// Creates a [MacOSJavaScriptReplyProxyCreationParams] instance based on [PlatformJavaScriptReplyProxyCreationParams]. + factory MacOSJavaScriptReplyProxyCreationParams.fromPlatformJavaScriptReplyProxyCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformJavaScriptReplyProxyCreationParams params) { + return MacOSJavaScriptReplyProxyCreationParams( + webMessageListener: params.webMessageListener); + } +} + +///{@macro flutter_inappwebview_platform_interface.JavaScriptReplyProxy} +class MacOSJavaScriptReplyProxy extends PlatformJavaScriptReplyProxy { + /// Constructs a [MacOSWebMessageListener]. + MacOSJavaScriptReplyProxy(PlatformJavaScriptReplyProxyCreationParams params) + : super.implementation( + params is MacOSJavaScriptReplyProxyCreationParams + ? params + : MacOSJavaScriptReplyProxyCreationParams + .fromPlatformJavaScriptReplyProxyCreationParams(params), + ); + + MacOSWebMessageListener get _macosWebMessageListener => + params.webMessageListener as MacOSWebMessageListener; + + @override + Future postMessage(WebMessage message) async { + Map args = {}; + args.putIfAbsent('message', () => message.toMap()); + await _macosWebMessageListener.channel?.invokeMethod('postMessage', args); + } + + @override + String toString() { + return 'MacOSJavaScriptReplyProxy{}'; + } +} diff --git a/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart b/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart new file mode 100644 index 00000000..c0682298 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart @@ -0,0 +1,95 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import 'web_message_channel.dart'; + +/// Object specifying creation parameters for creating a [MacOSWebMessagePort]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebMessagePortCreationParams] for +/// more information. +@immutable +class MacOSWebMessagePortCreationParams + extends PlatformWebMessagePortCreationParams { + /// Creates a new [MacOSWebMessagePortCreationParams] instance. + const MacOSWebMessagePortCreationParams({required super.index}); + + /// Creates a [MacOSWebMessagePortCreationParams] instance based on [PlatformWebMessagePortCreationParams]. + factory MacOSWebMessagePortCreationParams.fromPlatformWebMessagePortCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebMessagePortCreationParams params) { + return MacOSWebMessagePortCreationParams(index: params.index); + } + + @override + String toString() { + return 'MacOSWebMessagePortCreationParams{index: $index}'; + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebMessagePort} +class MacOSWebMessagePort extends PlatformWebMessagePort { + WebMessageCallback? _onMessage; + late MacOSWebMessageChannel _webMessageChannel; + + /// Constructs a [MacOSWebMessagePort]. + MacOSWebMessagePort(PlatformWebMessagePortCreationParams params) + : super.implementation( + params is MacOSWebMessagePortCreationParams + ? params + : MacOSWebMessagePortCreationParams + .fromPlatformWebMessagePortCreationParams(params), + ); + + @override + Future setWebMessageCallback(WebMessageCallback? onMessage) async { + Map args = {}; + args.putIfAbsent('index', () => params.index); + await _webMessageChannel.internalChannel + ?.invokeMethod('setWebMessageCallback', args); + this._onMessage = onMessage; + } + + @override + Future postMessage(WebMessage message) async { + Map args = {}; + args.putIfAbsent('index', () => params.index); + args.putIfAbsent('message', () => message.toMap()); + await _webMessageChannel.internalChannel?.invokeMethod('postMessage', args); + } + + @override + Future close() async { + Map args = {}; + args.putIfAbsent('index', () => params.index); + await _webMessageChannel.internalChannel?.invokeMethod('close', args); + } + + @override + Map toMap() { + return { + "index": params.index, + "webMessageChannelId": this._webMessageChannel.params.id + }; + } + + @override + Map toJson() { + return toMap(); + } + + @override + String toString() { + return 'MacOSWebMessagePort{index: ${params.index}}'; + } +} + +extension InternalWebMessagePort on MacOSWebMessagePort { + WebMessageCallback? get onMessage => _onMessage; + void set onMessage(WebMessageCallback? value) => _onMessage = value; + + MacOSWebMessageChannel get webMessageChannel => _webMessageChannel; + void set webMessageChannel(MacOSWebMessageChannel value) => + _webMessageChannel = value; +} diff --git a/flutter_inappwebview_windows/lib/src/web_storage/main.dart b/flutter_inappwebview_windows/lib/src/web_storage/main.dart new file mode 100644 index 00000000..7265ae52 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/web_storage/main.dart @@ -0,0 +1,2 @@ +export 'web_storage.dart'; +export 'web_storage_manager.dart' hide InternalWebStorageManager; diff --git a/flutter_inappwebview_windows/lib/src/web_storage/web_storage.dart b/flutter_inappwebview_windows/lib/src/web_storage/web_storage.dart new file mode 100644 index 00000000..e884747b --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/web_storage/web_storage.dart @@ -0,0 +1,257 @@ +import 'dart:convert'; + +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +import '../in_app_webview/in_app_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [MacOSWebStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebStorageCreationParams] for +/// more information. +class MacOSWebStorageCreationParams extends PlatformWebStorageCreationParams { + /// Creates a new [MacOSWebStorageCreationParams] instance. + MacOSWebStorageCreationParams( + {required super.localStorage, required super.sessionStorage}); + + /// Creates a [MacOSWebStorageCreationParams] instance based on [PlatformWebStorageCreationParams]. + factory MacOSWebStorageCreationParams.fromPlatformWebStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebStorageCreationParams params) { + return MacOSWebStorageCreationParams( + localStorage: params.localStorage, + sessionStorage: params.sessionStorage); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebStorage} +class MacOSWebStorage extends PlatformWebStorage { + /// Constructs a [MacOSWebStorage]. + MacOSWebStorage(PlatformWebStorageCreationParams params) + : super.implementation( + params is MacOSWebStorageCreationParams + ? params + : MacOSWebStorageCreationParams + .fromPlatformWebStorageCreationParams(params), + ); + + @override + PlatformLocalStorage get localStorage => params.localStorage; + + @override + PlatformSessionStorage get sessionStorage => params.sessionStorage; + + @override + void dispose() { + localStorage.dispose(); + sessionStorage.dispose(); + } +} + +/// Object specifying creation parameters for creating a [MacOSStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformStorageCreationParams] for +/// more information. +class MacOSStorageCreationParams extends PlatformStorageCreationParams { + /// Creates a new [MacOSStorageCreationParams] instance. + MacOSStorageCreationParams( + {required super.controller, required super.webStorageType}); + + /// Creates a [MacOSStorageCreationParams] instance based on [PlatformStorageCreationParams]. + factory MacOSStorageCreationParams.fromPlatformStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformStorageCreationParams params) { + return MacOSStorageCreationParams( + controller: params.controller, webStorageType: params.webStorageType); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformStorage} +abstract class MacOSStorage implements PlatformStorage { + @override + WindowsInAppWebViewController? controller; + + @override + Future length() async { + var result = await controller?.evaluateJavascript(source: """ + window.$webStorageType.length; + """); + return result != null ? int.parse(json.decode(result)) : null; + } + + @override + Future setItem({required String key, required dynamic value}) async { + var encodedValue = json.encode(value); + await controller?.evaluateJavascript(source: """ + window.$webStorageType.setItem("$key", ${value is String ? encodedValue : "JSON.stringify($encodedValue)"}); + """); + } + + @override + Future getItem({required String key}) async { + var itemValue = await controller?.evaluateJavascript(source: """ + window.$webStorageType.getItem("$key"); + """); + + if (itemValue == null) { + return null; + } + + try { + return json.decode(itemValue); + } catch (e) {} + + return itemValue; + } + + @override + Future removeItem({required String key}) async { + await controller?.evaluateJavascript(source: """ + window.$webStorageType.removeItem("$key"); + """); + } + + @override + Future> getItems() async { + var webStorageItems = []; + + List>? items = + (await controller?.evaluateJavascript(source: """ +(function() { + var webStorageItems = []; + for(var i = 0; i < window.$webStorageType.length; i++){ + var key = window.$webStorageType.key(i); + webStorageItems.push( + { + key: key, + value: window.$webStorageType.getItem(key) + } + ); + } + return webStorageItems; +})(); + """))?.cast>(); + + if (items == null) { + return webStorageItems; + } + + for (var item in items) { + webStorageItems + .add(WebStorageItem(key: item["key"], value: item["value"])); + } + + return webStorageItems; + } + + @override + Future clear() async { + await controller?.evaluateJavascript(source: """ + window.$webStorageType.clear(); + """); + } + + @override + Future key({required int index}) async { + var result = await controller?.evaluateJavascript(source: """ + window.$webStorageType.key($index); + """); + return result != null ? json.decode(result) : null; + } + + @override + void dispose() { + controller = null; + } +} + +/// Object specifying creation parameters for creating a [MacOSLocalStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformLocalStorageCreationParams] for +/// more information. +class MacOSLocalStorageCreationParams + extends PlatformLocalStorageCreationParams { + /// Creates a new [MacOSLocalStorageCreationParams] instance. + MacOSLocalStorageCreationParams(super.params); + + /// Creates a [MacOSLocalStorageCreationParams] instance based on [PlatformLocalStorageCreationParams]. + factory MacOSLocalStorageCreationParams.fromPlatformLocalStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformLocalStorageCreationParams params) { + return MacOSLocalStorageCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformLocalStorage} +class MacOSLocalStorage extends PlatformLocalStorage with MacOSStorage { + /// Constructs a [MacOSLocalStorage]. + MacOSLocalStorage(PlatformLocalStorageCreationParams params) + : super.implementation( + params is MacOSLocalStorageCreationParams + ? params + : MacOSLocalStorageCreationParams + .fromPlatformLocalStorageCreationParams(params), + ); + + /// Default storage + factory MacOSLocalStorage.defaultStorage( + {required PlatformInAppWebViewController? controller}) { + return MacOSLocalStorage(MacOSLocalStorageCreationParams( + PlatformLocalStorageCreationParams(PlatformStorageCreationParams( + controller: controller, + webStorageType: WebStorageType.LOCAL_STORAGE)))); + } + + @override + WindowsInAppWebViewController? get controller => + params.controller as WindowsInAppWebViewController?; +} + +/// Object specifying creation parameters for creating a [MacOSSessionStorage]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformSessionStorageCreationParams] for +/// more information. +class MacOSSessionStorageCreationParams + extends PlatformSessionStorageCreationParams { + /// Creates a new [MacOSSessionStorageCreationParams] instance. + MacOSSessionStorageCreationParams(super.params); + + /// Creates a [MacOSSessionStorageCreationParams] instance based on [PlatformSessionStorageCreationParams]. + factory MacOSSessionStorageCreationParams.fromPlatformSessionStorageCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformSessionStorageCreationParams params) { + return MacOSSessionStorageCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformSessionStorage} +class MacOSSessionStorage extends PlatformSessionStorage with MacOSStorage { + /// Constructs a [MacOSSessionStorage]. + MacOSSessionStorage(PlatformSessionStorageCreationParams params) + : super.implementation( + params is MacOSSessionStorageCreationParams + ? params + : MacOSSessionStorageCreationParams + .fromPlatformSessionStorageCreationParams(params), + ); + + /// Default storage + factory MacOSSessionStorage.defaultStorage( + {required PlatformInAppWebViewController? controller}) { + return MacOSSessionStorage(MacOSSessionStorageCreationParams( + PlatformSessionStorageCreationParams(PlatformStorageCreationParams( + controller: controller, + webStorageType: WebStorageType.SESSION_STORAGE)))); + } + + @override + WindowsInAppWebViewController? get controller => + params.controller as WindowsInAppWebViewController?; +} diff --git a/flutter_inappwebview_windows/lib/src/web_storage/web_storage_manager.dart b/flutter_inappwebview_windows/lib/src/web_storage/web_storage_manager.dart new file mode 100644 index 00000000..b281f0d7 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/web_storage/web_storage_manager.dart @@ -0,0 +1,134 @@ +import 'dart:async'; + +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 [MacOSWebStorageManager]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformWebStorageManagerCreationParams] for +/// more information. +@immutable +class MacOSWebStorageManagerCreationParams + extends PlatformWebStorageManagerCreationParams { + /// Creates a new [MacOSWebStorageManagerCreationParams] instance. + const MacOSWebStorageManagerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformWebStorageManagerCreationParams params, + ) : super(); + + /// Creates a [MacOSWebStorageManagerCreationParams] instance based on [PlatformWebStorageManagerCreationParams]. + factory MacOSWebStorageManagerCreationParams.fromPlatformWebStorageManagerCreationParams( + PlatformWebStorageManagerCreationParams params) { + return MacOSWebStorageManagerCreationParams(params); + } +} + +///{@macro flutter_inappwebview_platform_interface.PlatformWebStorageManager} +class MacOSWebStorageManager extends PlatformWebStorageManager + with ChannelController { + /// Creates a new [MacOSWebStorageManager]. + MacOSWebStorageManager(PlatformWebStorageManagerCreationParams params) + : super.implementation( + params is MacOSWebStorageManagerCreationParams + ? params + : MacOSWebStorageManagerCreationParams + .fromPlatformWebStorageManagerCreationParams(params), + ) { + channel = const MethodChannel( + 'com.pichillilorenzo/flutter_inappwebview_webstoragemanager'); + handler = handleMethod; + initMethodCallHandler(); + } + + static MacOSWebStorageManager? _instance; + + ///Gets the WebStorage manager shared instance. + static MacOSWebStorageManager instance() { + return (_instance != null) ? _instance! : _init(); + } + + static MacOSWebStorageManager _init() { + _instance = MacOSWebStorageManager(MacOSWebStorageManagerCreationParams( + const PlatformWebStorageManagerCreationParams())); + return _instance!; + } + + Future _handleMethod(MethodCall call) async {} + + @override + Future> fetchDataRecords( + {required Set dataTypes}) async { + List recordList = []; + List dataTypesList = []; + for (var dataType in dataTypes) { + dataTypesList.add(dataType.toNativeValue()); + } + Map args = {}; + args.putIfAbsent("dataTypes", () => dataTypesList); + List> records = + (await channel?.invokeMethod('fetchDataRecords', args)) + ?.cast>() ?? + []; + for (var record in records) { + List dataTypesString = record["dataTypes"].cast(); + Set dataTypes = Set(); + for (var dataTypeValue in dataTypesString) { + var dataType = WebsiteDataType.fromNativeValue(dataTypeValue); + if (dataType != null) { + dataTypes.add(dataType); + } + } + recordList.add(WebsiteDataRecord( + displayName: record["displayName"], dataTypes: dataTypes)); + } + return recordList; + } + + @override + Future removeDataFor( + {required Set dataTypes, + required List dataRecords}) async { + List dataTypesList = []; + for (var dataType in dataTypes) { + dataTypesList.add(dataType.toNativeValue()); + } + + List> recordList = []; + for (var record in dataRecords) { + recordList.add(record.toMap()); + } + + Map args = {}; + args.putIfAbsent("dataTypes", () => dataTypesList); + args.putIfAbsent("recordList", () => recordList); + await channel?.invokeMethod('removeDataFor', args); + } + + @override + Future removeDataModifiedSince( + {required Set dataTypes, required DateTime date}) async { + List dataTypesList = []; + for (var dataType in dataTypes) { + dataTypesList.add(dataType.toNativeValue()); + } + + var timestamp = date.millisecondsSinceEpoch; + + Map args = {}; + args.putIfAbsent("dataTypes", () => dataTypesList); + args.putIfAbsent("timestamp", () => timestamp); + await channel?.invokeMethod('removeDataModifiedSince', args); + } + + @override + void dispose() { + // empty + } +} + +extension InternalWebStorageManager on MacOSWebStorageManager { + get handleMethod => _handleMethod; +} diff --git a/flutter_inappwebview_windows/pubspec.yaml b/flutter_inappwebview_windows/pubspec.yaml new file mode 100644 index 00000000..354848d1 --- /dev/null +++ b/flutter_inappwebview_windows/pubspec.yaml @@ -0,0 +1,78 @@ +name: flutter_inappwebview_windows +description: Windows implementation of the flutter_inappwebview plugin. +version: 1.0.11 +homepage: https://inappwebview.dev/ +repository: https://github.com/pichillilorenzo/flutter_inappwebview/tree/master/flutter_inappwebview_windows +issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues +topics: + - html + - webview + - webview-flutter + - inappwebview + - browser + +environment: + sdk: ">=2.17.0 <4.0.0" + flutter: ">=3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_inappwebview_platform_interface: ^1.0.10 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + windows: + pluginClass: FlutterInappwebviewWindowsPluginCApi + dartPluginClass: WindowsInAppWebViewPlatform + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter_inappwebview_windows/test/flutter_inappwebview_windows_test.dart b/flutter_inappwebview_windows/test/flutter_inappwebview_windows_test.dart new file mode 100644 index 00000000..e69de29b diff --git a/flutter_inappwebview_windows/windows/.gitignore b/flutter_inappwebview_windows/windows/.gitignore new file mode 100644 index 00000000..d87ced66 --- /dev/null +++ b/flutter_inappwebview_windows/windows/.gitignore @@ -0,0 +1,19 @@ +flutter/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +packages/ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt new file mode 100644 index 00000000..532e96c5 --- /dev/null +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -0,0 +1,151 @@ +# The Flutter tooling requires that developers have a version of Visual Studio +# installed that includes CMake 3.14 or later. You should not increase this +# version, as doing so will cause the plugin to fail to compile for some +# customers of the plugin. +cmake_minimum_required(VERSION 3.14) + +set(WIL_VERSION "1.0.231216.1") +set(WEBVIEW_VERSION "1.0.2210.55") + +message(VERBOSE "CMake system version is ${CMAKE_SYSTEM_VERSION} (using SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION})") + +# Project-level configuration. +set(PROJECT_NAME "flutter_inappwebview_windows") +project(${PROJECT_NAME} LANGUAGES CXX) + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# This value is used when generating builds using this plugin, so it must +# not be changed +set(PLUGIN_NAME "flutter_inappwebview_windows_plugin") + +set(NUGET_URL https://dist.nuget.org/win-x86-commandline/latest/nuget.exe) +set(NUGET_SHA256 852b71cc8c8c2d40d09ea49d321ff56fd2397b9d6ea9f96e532530307bbbafd3) + +find_program(NUGET nuget) +if(NOT NUGET) + message(NOTICE "Nuget is not installed.") + set(NUGET ${CMAKE_BINARY_DIR}/nuget.exe) + if (NOT EXISTS ${NUGET}) + message(NOTICE "Attempting to download nuget.") + file(DOWNLOAD ${NUGET_URL} ${NUGET}) + endif() + + file(SHA256 ${NUGET} NUGET_DL_HASH) + if (NOT NUGET_DL_HASH STREQUAL NUGET_SHA256) + message(FATAL_ERROR "Integrity check for ${NUGET} failed.") + endif() +endif() + +add_custom_target(${PROJECT_NAME}_DEPENDENCIES_DOWNLOAD ALL) +add_custom_command( + TARGET ${PROJECT_NAME}_DEPENDENCIES_DOWNLOAD PRE_BUILD + COMMAND ${NUGET} install Microsoft.Windows.ImplementationLibrary -Version ${WIL_VERSION} -ExcludeVersion -OutputDirectory ${CMAKE_BINARY_DIR}/packages + COMMAND ${NUGET} install Microsoft.Web.WebView2 -Version ${WEBVIEW_VERSION} -ExcludeVersion -OutputDirectory ${CMAKE_BINARY_DIR}/packages + DEPENDS ${NUGET} +) + +# Any new source files that you add to the plugin should be added here. +list(APPEND PLUGIN_SOURCES + "flutter_inappwebview_windows_base_plugin.h" + "flutter_inappwebview_windows_base_plugin.cpp" + "flutter_inappwebview_windows_plugin.cpp" + "flutter_inappwebview_windows_plugin.h" + "utils/strconv.h" + "utils/util.h" + "types/channel_delegate.cpp" + "types/channel_delegate.h" + "types/url_request.cpp" + "types/url_request.h" + "in_app_webview/in_app_webview.cpp" + "in_app_webview/in_app_webview.h" + "in_app_webview/webview_channel_delegate.cpp" + "in_app_webview/webview_channel_delegate.h" + "in_app_browser/in_app_browser_manager.cpp" + "in_app_browser/in_app_browser_manager.h" + "in_app_browser/in_app_browser.cpp" + "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" +) + +# Define the plugin library target. Its name must not be changed (see comment +# on PLUGIN_NAME above). +add_library(${PLUGIN_NAME} SHARED + "include/flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h" + "flutter_inappwebview_windows_plugin_c_api.cpp" + ${PLUGIN_SOURCES} +) + +# Apply a standard set of build settings that are configured in the +# application-level CMakeLists.txt. This can be removed for plugins that want +# full control over build settings. +apply_standard_settings(${PLUGIN_NAME}) + +target_link_libraries(${PLUGIN_NAME} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Web.WebView2/build/native/Microsoft.Web.WebView2.targets) +target_link_libraries(${PLUGIN_NAME} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.ImplementationLibrary/build/native/Microsoft.Windows.ImplementationLibrary.targets) + +# Symbols are hidden by default to reduce the chance of accidental conflicts +# between plugins. This should not be removed; any symbols that should be +# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro. +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) + +# Source include directories and library dependencies. Add any plugin-specific +# dependencies here. +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) + +# List of absolute paths to libraries that should be bundled with the plugin. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(flutter_inappwebview_windows_bundled_libraries + PARENT_SCOPE +) + +# === Tests === +# These unit tests can be run from a terminal after building the example, or +# from Visual Studio after opening the generated solution file. + +# Only enable test builds when building the example (which sets this variable) +# so that plugin clients aren't building the tests. +if (${include_${PROJECT_NAME}_tests}) +set(TEST_RUNNER "${PROJECT_NAME}_test") +enable_testing() + +# Add the Google Test dependency. +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/release-1.11.0.zip +) +# Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +# Disable install commands for gtest so it doesn't end up in the bundle. +set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE) +FetchContent_MakeAvailable(googletest) + +# The plugin's C API is not very useful for unit testing, so build the sources +# directly into the test binary rather than using the DLL. +add_executable(${TEST_RUNNER} + test/flutter_inappwebview_windows_plugin_test.cpp + ${PLUGIN_SOURCES} +) +apply_standard_settings(${TEST_RUNNER}) +target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin) +target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock) +# flutter_wrapper_plugin has link dependencies on the Flutter DLL. +add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${FLUTTER_LIBRARY}" $ +) + +# Enable automatic test discovery. +include(GoogleTest) +gtest_discover_tests(${TEST_RUNNER}) +endif() diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.cpp b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.cpp new file mode 100644 index 00000000..45c7cbc2 --- /dev/null +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.cpp @@ -0,0 +1,14 @@ +#include "flutter_inappwebview_windows_base_plugin.h" + +namespace flutter_inappwebview_plugin +{ + FlutterInappwebviewWindowsBasePlugin::FlutterInappwebviewWindowsBasePlugin(flutter::PluginRegistrarWindows* registrar) : registrar(registrar) + { + + } + + FlutterInappwebviewWindowsBasePlugin::~FlutterInappwebviewWindowsBasePlugin() + { + + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.h b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.h new file mode 100644 index 00000000..e442060b --- /dev/null +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_BASE_PLUGIN_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_BASE_PLUGIN_H_ + +#include + +namespace flutter_inappwebview_plugin +{ + class FlutterInappwebviewWindowsBasePlugin : public flutter::Plugin, public std::enable_shared_from_this { + public: + flutter::PluginRegistrarWindows* registrar; + + FlutterInappwebviewWindowsBasePlugin(flutter::PluginRegistrarWindows* registrar); + + virtual ~FlutterInappwebviewWindowsBasePlugin(); + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_BASE_PLUGIN_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp new file mode 100644 index 00000000..4574c7e9 --- /dev/null +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp @@ -0,0 +1,35 @@ +#include "flutter_inappwebview_windows_plugin.h" + +// This must be included before many other Windows headers. +#include + +// For getPlatformVersion; remove unless needed for your plugin implementation. +#include + +#include +#include +#include + +#include +#include + +namespace flutter_inappwebview_plugin +{ + // static + void FlutterInappwebviewWindowsPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto plugin = std::make_unique(registrar); + registrar->AddPlugin(std::move(plugin)); + } + + FlutterInappwebviewWindowsPlugin::FlutterInappwebviewWindowsPlugin(flutter::PluginRegistrarWindows* registrar) + : FlutterInappwebviewWindowsBasePlugin(registrar) + { + inAppBrowserManager = std::make_unique(this); + } + + FlutterInappwebviewWindowsPlugin::~FlutterInappwebviewWindowsPlugin() + { + inAppBrowserManager.release(); + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h new file mode 100644 index 00000000..e152a4ef --- /dev/null +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h @@ -0,0 +1,30 @@ +#ifndef FLUTTER_PLUGIN_FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_H_ +#define FLUTTER_PLUGIN_FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_H_ + +#include +#include + +#include + +#include "flutter_inappwebview_windows_base_plugin.h" + +#include "in_app_browser/in_app_browser_manager.h" + +namespace flutter_inappwebview_plugin +{ + class FlutterInappwebviewWindowsPlugin : public FlutterInappwebviewWindowsBasePlugin { + public: + std::unique_ptr inAppBrowserManager; + + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + FlutterInappwebviewWindowsPlugin(flutter::PluginRegistrarWindows* registrar); + + virtual ~FlutterInappwebviewWindowsPlugin(); + + // Disallow copy and assign. + FlutterInappwebviewWindowsPlugin(const FlutterInappwebviewWindowsPlugin&) = delete; + FlutterInappwebviewWindowsPlugin& operator=(const FlutterInappwebviewWindowsPlugin&) = delete; + }; +} +#endif // FLUTTER_PLUGIN_FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_H_ diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin_c_api.cpp b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin_c_api.cpp new file mode 100644 index 00000000..f49d1a29 --- /dev/null +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin_c_api.cpp @@ -0,0 +1,12 @@ +#include "include/flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h" + +#include + +#include "flutter_inappwebview_windows_plugin.h" + +void FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + flutter_inappwebview_plugin::FlutterInappwebviewWindowsPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp new file mode 100644 index 00000000..b822373b --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -0,0 +1,238 @@ +#include +#include "in_app_browser.h" + +#include +#include + +#include "flutter/event_channel.h" +#include "flutter/plugin_registrar.h" +#include "flutter/plugin_registrar_windows.h" +#include "flutter/method_channel.h" +#include "flutter/encodable_value.h" + +#include +#include +#include +#include +#include +#include "../utils/strconv.h" + +#include +#include + +namespace flutter_inappwebview_plugin +{ + using namespace Microsoft::WRL; + + const wchar_t* CLASS_NAME = L"InAppBrowser"; + + InAppBrowser::InAppBrowser(FlutterInappwebviewWindowsBasePlugin* plugin, const InAppBrowserCreationParams& params) + : plugin(plugin), + m_hInstance(GetModuleHandle(nullptr)), + id(params.id), + initialUrlRequest(params.urlRequest), + channelDelegate(std::make_unique(id, plugin->registrar->messenger())) + { + WNDCLASS wndClass = {}; + wndClass.lpszClassName = CLASS_NAME; + wndClass.hInstance = m_hInstance; + wndClass.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.lpfnWndProc = InAppBrowser::WndProc; + + RegisterClass(&wndClass); + + m_hWnd = CreateWindowEx( + 0, // Optional window styles. + CLASS_NAME, // Window class + L"Learn to Program Windows", // Window text + WS_OVERLAPPEDWINDOW, // Window style + + // Size and position + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + + NULL, // Parent window + NULL, // Menu + m_hInstance,// Instance handle + this // Additional application data + ); + + ShowWindow(m_hWnd, SW_SHOW); + + webView = std::make_unique(plugin, id, m_hWnd, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id, [this]() -> void { + if (channelDelegate) { + channelDelegate->onBrowserCreated(); + } + + if (initialUrlRequest.has_value()) { + webView->loadUrl(initialUrlRequest.value()); + } + }); + + // <-- WebView2 sample code starts here --> + // Step 3 - Create a single WebView within the parent window + // Locate the browser and set up the environment for WebView + //CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, + // Callback( + // [hWnd = m_hWnd, inAppBrowser = this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT { + + // // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd + // env->CreateCoreWebView2Controller(hWnd, Callback( + // [hWnd, inAppBrowser](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT { + // if (controller != nullptr) { + // inAppBrowser->webviewController = controller; + // inAppBrowser->webviewController->get_CoreWebView2(&inAppBrowser->webview); + // } + + // // Add a few settings for the webview + // // The demo step is redundant since the values are the default settings + // wil::com_ptr settings; + // inAppBrowser->webview->get_Settings(&settings); + // settings->put_IsScriptEnabled(TRUE); + // settings->put_AreDefaultScriptDialogsEnabled(TRUE); + // settings->put_IsWebMessageEnabled(TRUE); + + // // Resize WebView to fit the bounds of the parent window + // RECT bounds; + // GetClientRect(hWnd, &bounds); + // inAppBrowser->webviewController->put_Bounds(bounds); + + // auto url = inAppBrowser->initialUrlRequest.value().url.value(); + // std::wstring stemp = ansi_to_wide(url); + + // // Schedule an async task to navigate to Bing + // inAppBrowser->webview->Navigate(stemp.c_str()); + + // // + // // Step 4 - Navigation events + // // register an ICoreWebView2NavigationStartingEventHandler to cancel any non-https navigation + // EventRegistrationToken token; + // inAppBrowser->webview->add_NavigationStarting(Callback( + // [](ICoreWebView2* webview, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT { + // wil::unique_cotaskmem_string uri; + // args->get_Uri(&uri); + // std::wstring source(uri.get()); + // if (source.substr(0, 5) != L"https") { + // args->put_Cancel(true); + // } + // return S_OK; + // }).Get(), &token); + // // + + // // + // // Step 5 - Scripting + // // Schedule an async task to add initialization script that freezes the Object object + // inAppBrowser->webview->AddScriptToExecuteOnDocumentCreated(L"Object.freeze(Object);", nullptr); + // // Schedule an async task to get the document URL + // inAppBrowser->webview->ExecuteScript(L"window.document.URL;", Callback( + // [](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT { + // LPCWSTR URL = resultObjectAsJson; + // OutputDebugStringW(URL); + // //doSomethingWithURL(URL); + // return S_OK; + // }).Get()); + // // + + // // + // // Step 6 - Communication between host and web content + // // Set an event handler for the host to return received message back to the web content + // inAppBrowser->webview->add_WebMessageReceived(Callback( + // [](ICoreWebView2* webview, ICoreWebView2WebMessageReceivedEventArgs* args) -> HRESULT { + // wil::unique_cotaskmem_string message; + // args->TryGetWebMessageAsString(&message); + // // processMessage(&message); + // webview->PostWebMessageAsString(message.get()); + // return S_OK; + // }).Get(), &token); + + // // Schedule an async task to add initialization script that + // // 1) Add an listener to print message from the host + // // 2) Post document URL to the host + // inAppBrowser->webview->AddScriptToExecuteOnDocumentCreated( + // L"window.chrome.webview.addEventListener(\'message\', event => alert(event.data));" \ + // L"window.chrome.webview.postMessage(window.document.URL);", + // nullptr); + // // + + // return S_OK; + // }).Get()); + // return S_OK; + // }).Get()); + } + + LRESULT CALLBACK InAppBrowser::WndProc( + HWND window, + UINT message, + WPARAM wparam, + LPARAM lparam + ) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast(window_struct->lpCreateParams)); + } + else if (InAppBrowser* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); + } + + LRESULT InAppBrowser::MessageHandler( + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam + ) noexcept { + switch (message) { + case WM_DESTROY: { + webView.reset(); + + // might receive multiple WM_DESTROY messages. + if (!destroyed_) { + destroyed_ = true; + + if (channelDelegate) { + channelDelegate->onExit(); + } + } + return 0; + } + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + return 0; + } + case WM_SIZE: { + RECT bounds; + GetClientRect(hwnd, &bounds); + if (webView) { + webView->webViewController->put_Bounds(bounds); + } + return 0; + } + case WM_ACTIVATE: { + return 0; + } + } + + return DefWindowProc(hwnd, message, wparam, lparam); + } + + InAppBrowser* InAppBrowser::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); + } + + InAppBrowser::~InAppBrowser() + { + std::cout << "dealloc InAppBrowser\n"; + webView.reset(); + SetWindowLongPtr(m_hWnd, GWLP_USERDATA, 0); + plugin = nullptr; + } + +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h new file mode 100644 index 00000000..c674718f --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h @@ -0,0 +1,51 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_H_ + +#include +#include +#include + +#include +#include "../flutter_inappwebview_windows_base_plugin.h" +#include "../in_app_webview/in_app_webview.h" +#include "../types/url_request.h" +#include "in_app_browser_channel_delegate.h" + +namespace flutter_inappwebview_plugin +{ + struct InAppBrowserCreationParams + { + std::string id; + std::optional urlRequest; + }; + + class InAppBrowser { + public: + static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappbrowser_"; + + static LRESULT CALLBACK WndProc(HWND window, + UINT message, + WPARAM wparam, + LPARAM lparam) noexcept; + + FlutterInappwebviewWindowsBasePlugin* plugin; + std::string id; + std::optional initialUrlRequest; + std::unique_ptr webView; + std::unique_ptr channelDelegate; + + InAppBrowser(FlutterInappwebviewWindowsBasePlugin* plugin, const InAppBrowserCreationParams& params); + ~InAppBrowser(); + + private: + HINSTANCE m_hInstance; + HWND m_hWnd; + bool destroyed_ = false; + static InAppBrowser* GetThisFromHandle(HWND window) noexcept; + LRESULT MessageHandler(HWND window, + UINT message, + WPARAM wparam, + LPARAM lparam) noexcept; + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp new file mode 100644 index 00000000..3fab5fab --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp @@ -0,0 +1,40 @@ +#include "in_app_browser.h" +#include "in_app_browser_channel_delegate.h" + +#include "../utils/util.h" + +namespace flutter_inappwebview_plugin +{ + InAppBrowserChannelDelegate::InAppBrowserChannelDelegate(const std::string id, flutter::BinaryMessenger* messenger) + : ChannelDelegate(messenger, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id) + { + + } + + void InAppBrowserChannelDelegate::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + result->NotImplemented(); + } + + void InAppBrowserChannelDelegate::onBrowserCreated() const + { + if (!channel) { + return; + } + channel->InvokeMethod("onBrowserCreated", nullptr); + } + + void InAppBrowserChannelDelegate::onExit() const + { + if (!channel) { + return; + } + channel->InvokeMethod("onExit", nullptr); + } + + InAppBrowserChannelDelegate::~InAppBrowserChannelDelegate() + { + std::cout << "dealloc InAppBrowserChannelDelegate\n"; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.h new file mode 100644 index 00000000..6c6fa5cb --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.h @@ -0,0 +1,26 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_CHANNEL_DELEGATE_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_CHANNEL_DELEGATE_H_ + +#include +#include + +#include "../types/channel_delegate.h" + +namespace flutter_inappwebview_plugin +{ + class InAppBrowserChannelDelegate : public ChannelDelegate + { + public: + InAppBrowserChannelDelegate(const std::string id, flutter::BinaryMessenger* messenger); + ~InAppBrowserChannelDelegate(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + void onBrowserCreated() const; + void onExit() const; + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_CHANNEL_DELEGATE_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp new file mode 100644 index 00000000..88870d79 --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -0,0 +1,56 @@ +#include +#include +#include + +#include "in_app_browser_manager.h" + +#include "../types/url_request.h" + +#include "../utils/util.h" + +namespace flutter_inappwebview_plugin +{ + InAppBrowserManager::InAppBrowserManager(FlutterInappwebviewWindowsBasePlugin* plugin) + : plugin(plugin), ChannelDelegate(plugin->registrar->messenger(), InAppBrowserManager::METHOD_CHANNEL_NAME) + { + + } + + void InAppBrowserManager::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + if (method_call.method_name().compare("open") == 0) { + auto* arguments = std::get_if(method_call.arguments()); + open(arguments); + result->Success(flutter::EncodableValue(true)); + } + else { + result->NotImplemented(); + } + } + + void InAppBrowserManager::open(const flutter::EncodableMap* arguments) + { + auto id = std::get(arguments->at(flutter::EncodableValue("id"))); + auto urlRequestMap = std::get_if(&arguments->at(flutter::EncodableValue("urlRequest"))); + auto urlRequest = make_pointer_optional(new URLRequest(*urlRequestMap)); + + InAppBrowserCreationParams params = { + id, + urlRequest + }; + auto inAppBrowser = std::make_unique(plugin, params); + browsers[id] = std::move(inAppBrowser); + } + + InAppBrowserManager::~InAppBrowserManager() + { + std::cout << "dealloc InAppBrowserManager\n"; + for (std::map>::iterator itr = browsers.begin(); itr != browsers.end(); itr++) + { + browsers.erase(itr->first); + } + browsers.clear(); + plugin = nullptr; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h new file mode 100644 index 00000000..389b27a0 --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h @@ -0,0 +1,35 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_MANAGER_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_MANAGER_H_ + +#include +#include +#include +#include + +#include "../flutter_inappwebview_windows_base_plugin.h" + +#include "in_app_browser.h" + +#include "../types/channel_delegate.h" + +namespace flutter_inappwebview_plugin +{ + class InAppBrowserManager : public ChannelDelegate + { + public: + static inline const std::string METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappbrowser"; + + FlutterInappwebviewWindowsBasePlugin* plugin; + std::map> browsers; + + InAppBrowserManager(FlutterInappwebviewWindowsBasePlugin* plugin); + ~InAppBrowserManager(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + void open(const flutter::EncodableMap* arguments); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_MANAGER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp new file mode 100644 index 00000000..bfaf0394 --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -0,0 +1,114 @@ +#include "in_app_webview.h" +#include +#include +#include "../utils/strconv.h" + +namespace flutter_inappwebview_plugin +{ + using namespace Microsoft::WRL; + + InAppWebView::InAppWebView(FlutterInappwebviewWindowsBasePlugin* plugin, std::variant id, const HWND parentWindow, const std::function completionHandler) + : plugin(plugin), id(id), channelDelegate(std::make_unique(this, plugin->registrar->messenger())) + { + createWebView(parentWindow, completionHandler); + } + + InAppWebView::InAppWebView(FlutterInappwebviewWindowsBasePlugin* plugin, std::variant id, const HWND parentWindow, const std::string& channelName, const std::function completionHandler) + : plugin(plugin), id(id), channelDelegate(std::make_unique(this, plugin->registrar->messenger(), channelName)) + { + createWebView(parentWindow, completionHandler); + } + + void InAppWebView::createWebView(const HWND parentWindow, const std::function completionHandler) + { + CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, + Callback( + [parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT { + // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd + env->CreateCoreWebView2Controller(parentWindow, Callback( + [parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT { + if (controller != nullptr) { + webViewController = controller; + webViewController->get_CoreWebView2(webView.put()); + } + + // Resize WebView to fit the bounds of the parent window + RECT bounds; + GetClientRect(parentWindow, &bounds); + webViewController->put_Bounds(bounds); + + registerEventHandlers(); + + completionHandler(); + + return S_OK; + }).Get()); + return S_OK; + }).Get()); + } + + void InAppWebView::registerEventHandlers() + { + if (!webView) { + return; + } + + webView->add_NavigationStarting( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args) { + if (channelDelegate) { + LPWSTR uri = nullptr; + std::optional url = SUCCEEDED(args->get_Uri(&uri)) ? wide_to_utf8(std::wstring(uri)) : std::optional{}; + channelDelegate->onLoadStart(url); + } + + args->put_Cancel(false); + + return S_OK; + } + ).Get(), nullptr); + + webView->add_NavigationCompleted( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args) { + if (channelDelegate) { + LPWSTR uri = nullptr; + std::optional url = SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(std::wstring(uri)) : std::optional{}; + channelDelegate->onLoadStop(url); + } + return S_OK; + } + ).Get(), nullptr); + } + + std::optional InAppWebView::getUrl() const + { + LPWSTR uri = nullptr; + return SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(std::wstring(uri)) : std::optional{}; + } + + void InAppWebView::loadUrl(const URLRequest urlRequest) const + { + if (!webView) { + return; + } + + auto url = urlRequest.url.value(); + std::wstring stemp = ansi_to_wide(url); + + // Schedule an async task to navigate to Bing + webView->Navigate(stemp.c_str()); + } + + InAppWebView::~InAppWebView() + { + std::cout << "dealloc InAppWebView\n"; + if (webView) { + webView->Stop(); + } + if (webViewController) { + webViewController->Close(); + } + plugin = nullptr; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h new file mode 100644 index 00000000..814f0a74 --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -0,0 +1,39 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_H_ + +#include + +#include +#include +#include "../types/url_request.h" +#include "webview_channel_delegate.h" +#include "../flutter_inappwebview_windows_base_plugin.h" + +namespace flutter_inappwebview_plugin +{ + using namespace Microsoft::WRL; + + class InAppWebView + { + public: + static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_"; + + FlutterInappwebviewWindowsBasePlugin* plugin; + std::variant id; + wil::com_ptr webViewController; + wil::com_ptr webView; + std::unique_ptr channelDelegate; + + InAppWebView(FlutterInappwebviewWindowsBasePlugin* plugin, std::variant id, const HWND parentWindow, const std::function completionHandler); + InAppWebView(FlutterInappwebviewWindowsBasePlugin* plugin, std::variant id, const HWND parentWindow, const std::string& channelName, const std::function completionHandler); + ~InAppWebView(); + + std::optional getUrl() const; + void loadUrl(const URLRequest urlRequest) const; + + private: + void createWebView(const HWND parentWindow, const std::function completionHandler); + void InAppWebView::registerEventHandlers(); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp new file mode 100644 index 00000000..a863b466 --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -0,0 +1,67 @@ +#include "in_app_webview.h" +#include "webview_channel_delegate.h" + +#include "../utils/util.h" +#include "../utils/strconv.h" + +namespace flutter_inappwebview_plugin +{ + WebViewChannelDelegate::WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger) + : webView(webView), ChannelDelegate(messenger, InAppWebView::METHOD_CHANNEL_NAME_PREFIX + variant_to_string(webView->id)) + { + + } + + WebViewChannelDelegate::WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger, const std::string& name) + : webView(webView), ChannelDelegate(messenger, name) + { + + } + + void WebViewChannelDelegate::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + if (!webView) { + result->Success(); + return; + } + + if (method_call.method_name().compare("getUrl") == 0) { + std::optional url = webView->getUrl(); + result->Success(url.has_value() ? flutter::EncodableValue(url.value()) : flutter::EncodableValue()); + } + else { + result->NotImplemented(); + } + } + + void WebViewChannelDelegate::onLoadStart(const std::optional url) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap { + {flutter::EncodableValue("url"), url.has_value() ? flutter::EncodableValue(url.value()) : flutter::EncodableValue()}, + }); + channel->InvokeMethod("onLoadStart", std::move(arguments)); + } + + void WebViewChannelDelegate::onLoadStop(const std::optional url) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap{ + {flutter::EncodableValue("url"), url.has_value() ? flutter::EncodableValue(url.value()) : flutter::EncodableValue()}, + }); + channel->InvokeMethod("onLoadStop", std::move(arguments)); + } + + WebViewChannelDelegate::~WebViewChannelDelegate() + { + std::cout << "dealloc WebViewChannelDelegate\n"; + webView = nullptr; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h new file mode 100644 index 00000000..3736ba5d --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h @@ -0,0 +1,31 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_CHANNEL_DELEGATE_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_CHANNEL_DELEGATE_H_ + +#include +#include + +#include "../types/channel_delegate.h" + +namespace flutter_inappwebview_plugin +{ + class InAppWebView; + + class WebViewChannelDelegate : public ChannelDelegate + { + public: + InAppWebView* webView; + + WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger); + WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger, const std::string& name); + ~WebViewChannelDelegate(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + void onLoadStart(const std::optional url) const; + void onLoadStop(const std::optional url) const; + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_CHANNEL_DELEGATE_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/include/flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h b/flutter_inappwebview_windows/windows/include/flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h new file mode 100644 index 00000000..019d5283 --- /dev/null +++ b/flutter_inappwebview_windows/windows/include/flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h @@ -0,0 +1,23 @@ +#ifndef FLUTTER_PLUGIN_FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_C_API_H_ +#define FLUTTER_PLUGIN_FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_C_API_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_C_API_H_ diff --git a/flutter_inappwebview_windows/windows/test/flutter_inappwebview_windows_plugin_test.cpp b/flutter_inappwebview_windows/windows/test/flutter_inappwebview_windows_plugin_test.cpp new file mode 100644 index 00000000..f2504fd6 --- /dev/null +++ b/flutter_inappwebview_windows/windows/test/flutter_inappwebview_windows_plugin_test.cpp @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "flutter_inappwebview_windows_plugin.h" + +namespace flutter_inappwebview_windows { +namespace test { + +namespace { + +using flutter::EncodableMap; +using flutter::EncodableValue; +using flutter::MethodCall; +using flutter::MethodResultFunctions; + +} // namespace + +TEST(FlutterInappwebviewWindowsPlugin, GetPlatformVersion) { + FlutterInappwebviewWindowsPlugin plugin; + // Save the reply value from the success callback. + std::string result_string; + plugin.HandleMethodCall( + MethodCall("getPlatformVersion", std::make_unique()), + std::make_unique>( + [&result_string](const EncodableValue* result) { + result_string = std::get(*result); + }, + nullptr, nullptr)); + + // Since the exact string varies by host, just ensure that it's a string + // with the expected format. + EXPECT_TRUE(result_string.rfind("Windows ", 0) == 0); +} + +} // namespace test +} // namespace flutter_inappwebview_windows diff --git a/flutter_inappwebview_windows/windows/types/channel_delegate.cpp b/flutter_inappwebview_windows/windows/types/channel_delegate.cpp new file mode 100644 index 00000000..b57de4ac --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/channel_delegate.cpp @@ -0,0 +1,35 @@ +#include +#include + +#include "channel_delegate.h" + +namespace flutter_inappwebview_plugin +{ + ChannelDelegate::ChannelDelegate(flutter::BinaryMessenger* messenger, const std::string& name) : messenger(messenger) + { + channel = std::make_shared>( + this->messenger, name, + &flutter::StandardMethodCodec::GetInstance() + ); + channel->SetMethodCallHandler( + [this](const auto& call, auto result) { + this->HandleMethodCall(call, std::move(result)); + }); + } + + void ChannelDelegate::HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + + } + + ChannelDelegate::~ChannelDelegate() + { + messenger = nullptr; + if (channel != nullptr) { + channel->SetMethodCallHandler(nullptr); + } + channel.reset(); + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/channel_delegate.h b/flutter_inappwebview_windows/windows/types/channel_delegate.h new file mode 100644 index 00000000..b494d5c9 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/channel_delegate.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include +#include + +#include "../flutter_inappwebview_windows_base_plugin.h" + +namespace flutter_inappwebview_plugin +{ + class ChannelDelegate + { + using FlutterMethodChannel = std::shared_ptr>; + + public: + FlutterMethodChannel channel; + flutter::BinaryMessenger* messenger; + + ChannelDelegate(flutter::BinaryMessenger* messenger, const std::string& name); + ~ChannelDelegate(); + + virtual void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + }; +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/url_request.cpp b/flutter_inappwebview_windows/windows/types/url_request.cpp new file mode 100644 index 00000000..2c74e213 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/url_request.cpp @@ -0,0 +1,13 @@ +#include "url_request.h" + +#include "../utils/util.h" + +namespace flutter_inappwebview_plugin +{ + URLRequest::URLRequest(const flutter::EncodableMap map) + : url(get_optional_flutter_value(map, "url")), + method(get_optional_flutter_value(map, "method")) + { + + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/url_request.h b/flutter_inappwebview_windows/windows/types/url_request.h new file mode 100644 index 00000000..5e5f9569 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/url_request.h @@ -0,0 +1,20 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_URL_REQUEST_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_URL_REQUEST_H_ + +#include + +#include + +namespace flutter_inappwebview_plugin +{ + class URLRequest + { + public: + const std::optional url; + const std::optional method; + + URLRequest(const flutter::EncodableMap map); + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_URL_REQUEST_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/strconv.h b/flutter_inappwebview_windows/windows/utils/strconv.h new file mode 100644 index 00000000..715a2297 --- /dev/null +++ b/flutter_inappwebview_windows/windows/utils/strconv.h @@ -0,0 +1,572 @@ +// from https://github.com/javacommons/strconv + +/* strconv.h v1.8.10 */ +/* Last Modified: 2021/08/30 21:53 */ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2019-2021 JavaCommons +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +----------------------------------------------------------------------------- + */ + +#ifndef STRCONV_H +#define STRCONV_H + +#include +#include +#include +#include +#include + +namespace flutter_inappwebview_plugin +{ +#if __cplusplus >= 201103L && !defined(STRCONV_CPP98) + static inline std::wstring cp_to_wide(const std::string& s, UINT codepage) + { + int in_length = (int)s.length(); + int out_length = MultiByteToWideChar(codepage, 0, s.c_str(), in_length, 0, 0); + std::wstring result(out_length, L'\0'); + if (out_length) + MultiByteToWideChar(codepage, 0, s.c_str(), in_length, &result[0], out_length); + return result; + } + static inline std::string wide_to_cp(const std::wstring& s, UINT codepage) + { + int in_length = (int)s.length(); + int out_length = WideCharToMultiByte(codepage, 0, s.c_str(), in_length, 0, 0, 0, 0); + std::string result(out_length, '\0'); + if (out_length) + WideCharToMultiByte(codepage, 0, s.c_str(), in_length, &result[0], out_length, 0, 0); + return result; + } +#else /* __cplusplus < 201103L */ + static inline std::wstring cp_to_wide(const std::string& s, UINT codepage) + { + int in_length = (int)s.length(); + int out_length = MultiByteToWideChar(codepage, 0, s.c_str(), in_length, 0, 0); + std::vector buffer(out_length); + if (out_length) + MultiByteToWideChar(codepage, 0, s.c_str(), in_length, &buffer[0], out_length); + std::wstring result(buffer.begin(), buffer.end()); + return result; + } + static inline std::string wide_to_cp(const std::wstring& s, UINT codepage) + { + int in_length = (int)s.length(); + int out_length = WideCharToMultiByte(codepage, 0, s.c_str(), in_length, 0, 0, 0, 0); + std::vector buffer(out_length); + if (out_length) + WideCharToMultiByte(codepage, 0, s.c_str(), in_length, &buffer[0], out_length, 0, 0); + std::string result(buffer.begin(), buffer.end()); + return result; + } +#endif + + static inline std::string cp_to_utf8(const std::string& s, UINT codepage) + { + if (codepage == CP_UTF8) + return s; + std::wstring wide = cp_to_wide(s, codepage); + return wide_to_cp(wide, CP_UTF8); + } + static inline std::string utf8_to_cp(const std::string& s, UINT codepage) + { + if (codepage == CP_UTF8) + return s; + std::wstring wide = cp_to_wide(s, CP_UTF8); + return wide_to_cp(wide, codepage); + } + + static inline std::wstring ansi_to_wide(const std::string& s) + { + return cp_to_wide(s, CP_ACP); + } + static inline std::string wide_to_ansi(const std::wstring& s) + { + return wide_to_cp(s, CP_ACP); + } + + static inline std::wstring sjis_to_wide(const std::string& s) + { + return cp_to_wide(s, 932); + } + static inline std::string wide_to_sjis(const std::wstring& s) + { + return wide_to_cp(s, 932); + } + + static inline std::wstring utf8_to_wide(const std::string& s) + { + return cp_to_wide(s, CP_UTF8); + } + static inline std::string wide_to_utf8(const std::wstring& s) + { + return wide_to_cp(s, CP_UTF8); + } + + static inline std::string ansi_to_utf8(const std::string& s) + { + return cp_to_utf8(s, CP_ACP); + } + static inline std::string utf8_to_ansi(const std::string& s) + { + return utf8_to_cp(s, CP_ACP); + } + + static inline std::string sjis_to_utf8(const std::string& s) + { + return cp_to_utf8(s, 932); + } + static inline std::string utf8_to_sjis(const std::string& s) + { + return utf8_to_cp(s, 932); + } + +#ifdef __cpp_char8_t + static inline std::u8string utf8_to_char8(const std::string& s) + { + return std::u8string(s.begin(), s.end()); + } + static inline std::string char8_to_utf8(const std::u8string& s) + { + return std::string(s.begin(), s.end()); + } + + static inline std::wstring char8_to_wide(const std::u8string& s) + { + return cp_to_wide(char8_to_utf8(s), CP_UTF8); + } + static inline std::u8string wide_to_char8(const std::wstring& s) + { + return utf8_to_char8(wide_to_cp(s, CP_UTF8)); + } + + static inline std::u8string cp_to_char8(const std::string& s, UINT codepage) + { + return utf8_to_char8(cp_to_utf8(s, codepage)); + } + static inline std::string char8_to_cp(const std::u8string& s, UINT codepage) + { + return utf8_to_cp(char8_to_utf8(s), codepage); + } + + static inline std::u8string ansi_to_char8(const std::string& s) + { + return cp_to_char8(s, CP_ACP); + } + static inline std::string char8_to_ansi(const std::u8string& s) + { + return char8_to_cp(s, CP_ACP); + } + + static inline std::u8string sjis_to_char8(const std::string& s) + { + return cp_to_char8(s, 932); + } + static inline std::string char8_to_sjis(const std::u8string& s) + { + return char8_to_cp(s, 932); + } +#endif + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + + static inline std::wstring vformat(const wchar_t* format, va_list args) + { + int len = _vsnwprintf(0, 0, format, args); + if (len < 0) + return L""; + std::vector buffer(len + 1); + len = _vsnwprintf(&buffer[0], len, format, args); + if (len < 0) + return L""; + buffer[len] = L'\0'; + return &buffer[0]; + } + static inline std::string vformat(const char* format, va_list args) + { + int len = _vsnprintf(0, 0, format, args); + if (len < 0) + return ""; + std::vector buffer(len + 1); + len = _vsnprintf(&buffer[0], len, format, args); + if (len < 0) + return ""; + buffer[len] = '\0'; + return &buffer[0]; + } +#ifdef __cpp_char8_t + static inline std::u8string vformat(const char8_t* format, va_list args) + { + int len = _vsnprintf(0, 0, (const char*)format, args); + if (len < 0) + return u8""; + std::vector buffer(len + 1); + len = _vsnprintf(&buffer[0], len, (const char*)format, args); + if (len < 0) + return u8""; + buffer[len] = '\0'; + return (char8_t*)&buffer[0]; + } +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + static inline std::wstring format(const wchar_t* format, ...) + { + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + return s; + } + static inline std::string format(const char* format, ...) + { + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + return s; + } +#ifdef __cpp_char8_t + static inline std::u8string format(const char8_t* format, ...) + { + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + return s; + } +#endif + + static inline void format(std::ostream& ostrm, const wchar_t* format, ...) + { + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + ostrm << wide_to_utf8(s) << std::flush; + } + static inline void format(std::ostream& ostrm, const char* format, ...) + { + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + ostrm << s << std::flush; + } +#ifdef __cpp_char8_t + static inline void format(std::ostream& ostrm, const char8_t* format, ...) + { + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + ostrm << char8_to_utf8(s) << std::flush; + } +#endif + + static inline std::string formatA(const wchar_t* format, ...) + { + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + return wide_to_ansi(s); + } + static inline std::string formatA(const char* format, ...) + { + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + return utf8_to_ansi(s); + } +#ifdef __cpp_char8_t + static inline std::string formatA(const char8_t* format, ...) + { + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + return char8_to_ansi(s); + } +#endif + + static inline void formatA(std::ostream& ostrm, const wchar_t* format, ...) + { + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + ostrm << wide_to_ansi(s) << std::flush; + } + static inline void formatA(std::ostream& ostrm, const char* format, ...) + { + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + ostrm << utf8_to_ansi(s) << std::flush; + } +#ifdef __cpp_char8_t + static inline void formatA(std::ostream& ostrm, const char8_t* format, ...) + { + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + ostrm << char8_to_ansi(s) << std::flush; + } +#endif + + static inline void dbgmsg(const wchar_t* title, const wchar_t* format, ...) + { + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + MessageBoxW(0, s.c_str(), title, MB_OK); + } + static inline void dbgmsg(const char* title, const char* format, ...) + { + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + MessageBoxW(0, utf8_to_wide(s).c_str(), utf8_to_wide(title).c_str(), MB_OK); + } +#ifdef __cpp_char8_t + static inline void dbgmsg(const char8_t* title, const char8_t* format, ...) + { + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + MessageBoxW(0, char8_to_wide(s).c_str(), char8_to_wide(title).c_str(), MB_OK); + } +#endif + + static inline HANDLE handle_for_ostream(std::ostream& ostrm) + { + if (&ostrm == &std::cout) + { + return GetStdHandle(STD_OUTPUT_HANDLE); + } + else if (&ostrm == &std::cerr) + { + return GetStdHandle(STD_ERROR_HANDLE); + } + return INVALID_HANDLE_VALUE; + } + static inline void dbgout(std::ostream& ostrm, const wchar_t* format, ...) + { + va_list args; + va_start(args, format); + std::wstring ws = vformat(format, args); + va_end(args); + HANDLE h = handle_for_ostream(ostrm); + if (h == INVALID_HANDLE_VALUE) + { + return; + } + DWORD dwNumberOfCharsWrite; + if (GetFileType(h) != FILE_TYPE_CHAR) + { + std::string s = wide_to_cp(ws, GetConsoleOutputCP()); + WriteFile(h, s.c_str(), (DWORD)s.size(), &dwNumberOfCharsWrite, NULL); + } + else + { + WriteConsoleW(h, + ws.c_str(), + (DWORD)ws.size(), + &dwNumberOfCharsWrite, + NULL); + } + } + static inline void dbgout(std::ostream& ostrm, const char* format, ...) + { + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + HANDLE h = handle_for_ostream(ostrm); + if (h == INVALID_HANDLE_VALUE) + { + return; + } + DWORD dwNumberOfCharsWrite; + if (GetFileType(h) != FILE_TYPE_CHAR) + { + s = utf8_to_cp(s, GetConsoleOutputCP()); + WriteFile(h, s.c_str(), (DWORD)s.size(), &dwNumberOfCharsWrite, NULL); + } + else + { + std::wstring ws = utf8_to_wide(s); + WriteConsoleW(h, + ws.c_str(), + (DWORD)ws.size(), + &dwNumberOfCharsWrite, + NULL); + } + } +#ifdef __cpp_char8_t + static inline void dbgout(std::ostream& ostrm, const char8_t* format, ...) + { + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + HANDLE h = handle_for_ostream(ostrm); + if (h == INVALID_HANDLE_VALUE) + { + return; + } + DWORD dwNumberOfCharsWrite; + if (GetFileType(h) != FILE_TYPE_CHAR) + { + std::string str = char8_to_cp(s, GetConsoleOutputCP()); + WriteFile(h, (const char*)str.c_str(), (DWORD)str.size(), &dwNumberOfCharsWrite, NULL); + } + else + { + std::wstring ws = char8_to_wide(s); + WriteConsoleW(h, + ws.c_str(), + (DWORD)ws.size(), + &dwNumberOfCharsWrite, + NULL); + } + } +#endif + + class unicode_ostream + { + private: + std::ostream* m_ostrm; + UINT m_target_cp; + bool is_ascii(const std::string& s) + { + for (std::size_t i = 0; i < s.size(); i++) + { + unsigned char c = (unsigned char)s[i]; + if (c > 0x7f) + return false; + } + return true; + } + + public: + unicode_ostream(std::ostream& ostrm, UINT target_cp = CP_ACP) : m_ostrm(&ostrm), m_target_cp(target_cp) {} + std::ostream& stream() { return *m_ostrm; } + void stream(std::ostream& ostrm) { m_ostrm = &ostrm; } + UINT target_cp() { return m_target_cp; } + void target_cp(UINT cp) { m_target_cp = cp; } + template + unicode_ostream& operator<<(const T& x) + { + std::ostringstream oss; + oss << x; + std::string output = oss.str(); + if (is_ascii(output)) + { + (*m_ostrm) << x; + } + else + { + (*m_ostrm) << utf8_to_cp(output, m_target_cp); + } + return *this; + } + unicode_ostream& operator<<(const std::wstring& x) + { + (*m_ostrm) << wide_to_cp(x, m_target_cp); + return *this; + } + unicode_ostream& operator<<(const wchar_t* x) + { + (*m_ostrm) << wide_to_cp(x, m_target_cp); + return *this; + } + unicode_ostream& operator<<(const std::string& x) + { + (*m_ostrm) << utf8_to_cp(x, m_target_cp); + return *this; + } + unicode_ostream& operator<<(const char* x) + { + (*m_ostrm) << utf8_to_cp(x, m_target_cp); + return *this; + } +#ifdef __cpp_char8_t + unicode_ostream& operator<<(const std::u8string& x) + { + (*m_ostrm) << char8_to_cp(x, m_target_cp); + return *this; + } + unicode_ostream& operator<<(const char8_t* x) + { + (*m_ostrm) << char8_to_cp(x, m_target_cp); + return *this; + } +#endif + unicode_ostream& operator<<(std::ostream& (*pf)(std::ostream&)) // For manipulators... + { + (*m_ostrm) << pf; + return *this; + } + unicode_ostream& operator<<(std::basic_ios& (*pf)(std::basic_ios&)) // For manipulators... + { + (*m_ostrm) << pf; + return *this; + } + }; +} + +#define U8(X) ((const char *)u8##X) +#define WIDE(X) (L##X) + +#endif /* STRCONV_H */ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/util.h b/flutter_inappwebview_windows/windows/utils/util.h new file mode 100644 index 00000000..9ff79419 --- /dev/null +++ b/flutter_inappwebview_windows/windows/utils/util.h @@ -0,0 +1,41 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ + +#include +#include + +namespace flutter_inappwebview_plugin +{ + template + static inline std::optional make_pointer_optional(const T* value) + { + return value == nullptr ? std::nullopt : std::make_optional(*value); + } + + template + static inline T get_flutter_value(const flutter::EncodableMap map, const char* string) + { + return std::get(map.at(flutter::EncodableValue(string))); + } + + template + static inline std::optional get_optional_flutter_value(const flutter::EncodableMap map, const char* string) + { + return make_pointer_optional(std::get_if(&map.at(flutter::EncodableValue(string)))); + } + + static inline std::string variant_to_string(std::variant var) + { + return std::visit([](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) + return arg; + else if constexpr (std::is_arithmetic_v) + return std::to_string(arg); + else + static_assert(always_false_v, "non-exhaustive visitor!"); + }, var); + } +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ \ No newline at end of file From 43aa01d9444e9e3ae6c75f52317923b98e6027b2 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 7 Jan 2024 03:26:14 +0100 Subject: [PATCH 02/21] added implementation for callback result, impl for shouldOverrideUrlLoading --- .../windows/CMakeLists.txt | 3 + .../flutter_inappwebview_windows_plugin.cpp | 12 +- .../windows/in_app_browser/in_app_browser.cpp | 3 +- .../in_app_browser_channel_delegate.cpp | 2 +- .../in_app_browser/in_app_browser_manager.cpp | 6 +- .../windows/in_app_webview/in_app_webview.cpp | 132 ++++++++++++++++-- .../windows/in_app_webview/in_app_webview.h | 2 + .../webview_channel_delegate.cpp | 35 ++++- .../in_app_webview/webview_channel_delegate.h | 11 ++ .../windows/types/base_callback_result.h | 53 +++++++ .../windows/types/channel_delegate.h | 9 +- .../windows/types/navigation_action.cpp | 20 +++ .../windows/types/navigation_action.h | 22 +++ .../windows/types/url_request.cpp | 24 +++- .../windows/types/url_request.h | 5 + .../windows/utils/util.h | 47 ++++++- 16 files changed, 343 insertions(+), 43 deletions(-) create mode 100644 flutter_inappwebview_windows/windows/types/base_callback_result.h create mode 100644 flutter_inappwebview_windows/windows/types/navigation_action.cpp create mode 100644 flutter_inappwebview_windows/windows/types/navigation_action.h diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt index 532e96c5..a2088f2f 100644 --- a/flutter_inappwebview_windows/windows/CMakeLists.txt +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -57,8 +57,11 @@ list(APPEND PLUGIN_SOURCES "utils/util.h" "types/channel_delegate.cpp" "types/channel_delegate.h" + "types/base_callback_result.h" "types/url_request.cpp" "types/url_request.h" + "types/navigation_action.cpp" + "types/navigation_action.h" "in_app_webview/in_app_webview.cpp" "in_app_webview/in_app_webview.h" "in_app_webview/webview_channel_delegate.cpp" diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp index 4574c7e9..b849e10b 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp @@ -1,17 +1,7 @@ #include "flutter_inappwebview_windows_plugin.h" -// This must be included before many other Windows headers. -#include - -// For getPlatformVersion; remove unless needed for your plugin implementation. -#include - #include #include -#include - -#include -#include namespace flutter_inappwebview_plugin { @@ -30,6 +20,6 @@ namespace flutter_inappwebview_plugin FlutterInappwebviewWindowsPlugin::~FlutterInappwebviewWindowsPlugin() { - inAppBrowserManager.release(); + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index b822373b..50bc8e6f 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -16,6 +16,7 @@ #include #include #include "../utils/strconv.h" +#include "../utils/util.h" #include #include @@ -229,7 +230,7 @@ namespace flutter_inappwebview_plugin InAppBrowser::~InAppBrowser() { - std::cout << "dealloc InAppBrowser\n"; + debugLog("dealloc InAppBrowser"); webView.reset(); SetWindowLongPtr(m_hWnd, GWLP_USERDATA, 0); plugin = nullptr; diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp index 3fab5fab..ebfee72c 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp @@ -35,6 +35,6 @@ namespace flutter_inappwebview_plugin InAppBrowserChannelDelegate::~InAppBrowserChannelDelegate() { - std::cout << "dealloc InAppBrowserChannelDelegate\n"; + debugLog("dealloc InAppBrowserChannelDelegate"); } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp index 88870d79..b7dad155 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -45,11 +45,7 @@ namespace flutter_inappwebview_plugin InAppBrowserManager::~InAppBrowserManager() { - std::cout << "dealloc InAppBrowserManager\n"; - for (std::map>::iterator itr = browsers.begin(); itr != browsers.end(); itr++) - { - browsers.erase(itr->first); - } + debugLog("dealloc InAppBrowserManager"); browsers.clear(); plugin = nullptr; } diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index bfaf0394..4ec9f748 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -1,7 +1,11 @@ +#pragma comment(lib, "Shlwapi.lib") + #include "in_app_webview.h" #include #include +#include #include "../utils/strconv.h" +#include "../utils/util.h" namespace flutter_inappwebview_plugin { @@ -24,6 +28,7 @@ namespace flutter_inappwebview_plugin CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, Callback( [parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT { + webViewEnv = env; // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd env->CreateCoreWebView2Controller(parentWindow, Callback( [parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT { @@ -56,13 +61,74 @@ namespace flutter_inappwebview_plugin webView->add_NavigationStarting( Callback( [this](ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args) { - if (channelDelegate) { - LPWSTR uri = nullptr; - std::optional url = SUCCEEDED(args->get_Uri(&uri)) ? wide_to_utf8(std::wstring(uri)) : std::optional{}; - channelDelegate->onLoadStart(url); + if (!channelDelegate) { + args->put_Cancel(false); + return S_OK; + } + + wil::unique_cotaskmem_string uri = nullptr; + std::optional url = SUCCEEDED(args->get_Uri(&uri)) ? wide_to_utf8(std::wstring(uri.get())) : std::optional{}; + + wil::unique_cotaskmem_string method = nullptr; + wil::com_ptr requestHeaders = nullptr; + std::optional> headers = std::optional>{}; + if (SUCCEEDED(args->get_RequestHeaders(&requestHeaders))) { + headers = std::make_optional>({}); + wil::com_ptr iterator; + requestHeaders->GetIterator(&iterator); + BOOL hasCurrent = FALSE; + while (SUCCEEDED(iterator->get_HasCurrentHeader(&hasCurrent)) && hasCurrent) + { + wil::unique_cotaskmem_string name; + wil::unique_cotaskmem_string value; + + if (SUCCEEDED(iterator->GetCurrentHeader(&name, &value))) { + headers->insert({ wide_to_utf8(std::wstring(name.get())), wide_to_utf8(std::wstring(value.get())) }); + } + + BOOL hasNext = FALSE; + iterator->MoveNext(&hasNext); + } + + requestHeaders->GetHeader(L"Flutter-InAppWebView-Request-Method", &method); + requestHeaders->RemoveHeader(L"Flutter-InAppWebView-Request-Method"); } - args->put_Cancel(false); + if (callShouldOverrideUrlLoading && method == nullptr) { + // for some reason, we can't cancel and load an URL with other HTTP methods other than GET, + // so ignore the shouldOverrideUrlLoading event. + + auto urlRequest = std::make_shared(url, std::nullopt, headers, std::nullopt); + auto navigationAction = std::make_unique( + urlRequest, + true + ); + + auto callback = std::make_unique(); + callback->nonNullSuccess = [this, urlRequest](const NavigationActionPolicy actionPolicy) { + callShouldOverrideUrlLoading = false; + if (actionPolicy == allow) { + loadUrl(*urlRequest); + } + return false; + }; + auto defaultBehaviour = [this, urlRequest](const std::optional actionPolicy) { + callShouldOverrideUrlLoading = false; + loadUrl(*urlRequest); + }; + callback->defaultBehaviour = defaultBehaviour; + callback->error = [defaultBehaviour](const std::string& error_code, const std::string& error_message, const flutter::EncodableValue* error_details) { + debugLog(error_code + ", " + error_message); + defaultBehaviour(std::nullopt); + }; + channelDelegate->shouldOverrideUrlLoading(std::move(navigationAction), std::move(callback)); + args->put_Cancel(true); + } + else { + callShouldOverrideUrlLoading = true; + channelDelegate->onLoadStart(url); + args->put_Cancel(false); + } return S_OK; } @@ -71,10 +137,19 @@ namespace flutter_inappwebview_plugin webView->add_NavigationCompleted( Callback( [this](ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args) { + COREWEBVIEW2_WEB_ERROR_STATUS web_error_status; + args->get_WebErrorStatus(&web_error_status); + debugLog("WebErrorStatus " + std::to_string(web_error_status) + "\n"); + + BOOL isSuccess; + args->get_IsSuccess(&isSuccess); + if (channelDelegate) { LPWSTR uri = nullptr; std::optional url = SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(std::wstring(uri)) : std::optional{}; - channelDelegate->onLoadStop(url); + if (isSuccess) { + channelDelegate->onLoadStop(url); + } } return S_OK; } @@ -89,20 +164,53 @@ namespace flutter_inappwebview_plugin void InAppWebView::loadUrl(const URLRequest urlRequest) const { - if (!webView) { + if (!webView || !urlRequest.url.has_value()) { return; } - auto url = urlRequest.url.value(); - std::wstring stemp = ansi_to_wide(url); + std::wstring url = ansi_to_wide(urlRequest.url.value()); - // Schedule an async task to navigate to Bing - webView->Navigate(stemp.c_str()); + wil::com_ptr webViewEnv2; + wil::com_ptr webView2; + if (SUCCEEDED(webViewEnv->QueryInterface(IID_PPV_ARGS(&webViewEnv2))) && SUCCEEDED(webView->QueryInterface(IID_PPV_ARGS(&webView2)))) { + wil::com_ptr webResourceRequest; + std::wstring method = urlRequest.method.has_value() ? ansi_to_wide(urlRequest.method.value()) : L"GET"; + + wil::com_ptr postDataStream = nullptr; + if (urlRequest.body.has_value()) { + auto postData = std::string(urlRequest.body->begin(), urlRequest.body->end()); + postDataStream = SHCreateMemStream( + reinterpret_cast(postData.data()), static_cast(postData.length())); + } + webViewEnv2->CreateWebResourceRequest( + url.c_str(), + method.c_str(), + postDataStream.get(), + L"", + &webResourceRequest + ); + wil::com_ptr requestHeaders; + if (SUCCEEDED(webResourceRequest->get_Headers(&requestHeaders))) { + if (urlRequest.method.has_value() && urlRequest.method.value().compare("GET") != 0) { + requestHeaders->SetHeader(L"Flutter-InAppWebView-Request-Method", method.c_str()); + } + if (urlRequest.headers.has_value()) { + auto& headers = urlRequest.headers.value(); + for (auto const& [key, val] : headers) { + requestHeaders->SetHeader(ansi_to_wide(key).c_str(), ansi_to_wide(val).c_str()); + } + } + } + webView2->NavigateWithWebResourceRequest(webResourceRequest.get()); + } + else { + webView->Navigate(url.c_str()); + } } InAppWebView::~InAppWebView() { - std::cout << "dealloc InAppWebView\n"; + debugLog("dealloc InAppWebView"); if (webView) { webView->Stop(); } diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index 814f0a74..833fc641 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -20,6 +20,7 @@ namespace flutter_inappwebview_plugin FlutterInappwebviewWindowsBasePlugin* plugin; std::variant id; + wil::com_ptr webViewEnv; wil::com_ptr webViewController; wil::com_ptr webView; std::unique_ptr channelDelegate; @@ -32,6 +33,7 @@ namespace flutter_inappwebview_plugin void loadUrl(const URLRequest urlRequest) const; private: + bool callShouldOverrideUrlLoading = true; void createWebView(const HWND parentWindow, const std::function completionHandler); void InAppWebView::registerEventHandlers(); }; diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index a863b466..d68dc2c1 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -3,6 +3,7 @@ #include "../utils/util.h" #include "../utils/strconv.h" +#include "../types/base_callback_result.h" namespace flutter_inappwebview_plugin { @@ -18,6 +19,16 @@ namespace flutter_inappwebview_plugin } + WebViewChannelDelegate::ShouldOverrideUrlLoadingCallback::ShouldOverrideUrlLoadingCallback() { + decodeResult = [](const flutter::EncodableValue* value) { + if (value->IsNull()) { + return cancel; + } + auto navigationPolicy = std::get(*value); + return static_cast(navigationPolicy); + }; + } + void WebViewChannelDelegate::HandleMethodCall(const flutter::MethodCall& method_call, std::unique_ptr> result) { @@ -27,8 +38,12 @@ namespace flutter_inappwebview_plugin } if (method_call.method_name().compare("getUrl") == 0) { - std::optional url = webView->getUrl(); - result->Success(url.has_value() ? flutter::EncodableValue(url.value()) : flutter::EncodableValue()); + result->Success(optional_to_fl_value(webView->getUrl())); + } else if (method_call.method_name().compare("loadUrl") == 0) { + auto& arguments = std::get(*method_call.arguments()); + auto urlRequest = std::make_unique(get_fl_map_value(arguments, "urlRequest")); + webView->loadUrl(*urlRequest); + result->Success(flutter::EncodableValue(true)); } else { result->NotImplemented(); @@ -42,7 +57,7 @@ namespace flutter_inappwebview_plugin } auto arguments = std::make_unique(flutter::EncodableMap { - {flutter::EncodableValue("url"), url.has_value() ? flutter::EncodableValue(url.value()) : flutter::EncodableValue()}, + {flutter::EncodableValue("url"), optional_to_fl_value(url)}, }); channel->InvokeMethod("onLoadStart", std::move(arguments)); } @@ -54,14 +69,24 @@ namespace flutter_inappwebview_plugin } auto arguments = std::make_unique(flutter::EncodableMap{ - {flutter::EncodableValue("url"), url.has_value() ? flutter::EncodableValue(url.value()) : flutter::EncodableValue()}, + {flutter::EncodableValue("url"), optional_to_fl_value(url)}, }); channel->InvokeMethod("onLoadStop", std::move(arguments)); } + void WebViewChannelDelegate::shouldOverrideUrlLoading(std::shared_ptr navigationAction, std::unique_ptr callback) + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(navigationAction->toEncodableMap()); + channel->InvokeMethod("shouldOverrideUrlLoading", std::move(arguments), std::move(callback)); + } + WebViewChannelDelegate::~WebViewChannelDelegate() { - std::cout << "dealloc WebViewChannelDelegate\n"; + debugLog("dealloc WebViewChannelDelegate"); webView = nullptr; } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h index 3736ba5d..a5bfe0a4 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h @@ -5,16 +5,26 @@ #include #include "../types/channel_delegate.h" +#include "../types/base_callback_result.h" +#include "../types/navigation_action.h" namespace flutter_inappwebview_plugin { class InAppWebView; + enum NavigationActionPolicy {cancel = 0, allow = 1}; + class WebViewChannelDelegate : public ChannelDelegate { public: InAppWebView* webView; + class ShouldOverrideUrlLoadingCallback : public BaseCallbackResult { + public: + ShouldOverrideUrlLoadingCallback(); + ~ShouldOverrideUrlLoadingCallback() = default; + }; + WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger); WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger, const std::string& name); ~WebViewChannelDelegate(); @@ -25,6 +35,7 @@ namespace flutter_inappwebview_plugin void onLoadStart(const std::optional url) const; void onLoadStop(const std::optional url) const; + void shouldOverrideUrlLoading(std::shared_ptr navigationAction, std::unique_ptr callback); }; } diff --git a/flutter_inappwebview_windows/windows/types/base_callback_result.h b/flutter_inappwebview_windows/windows/types/base_callback_result.h new file mode 100644 index 00000000..66958b5a --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/base_callback_result.h @@ -0,0 +1,53 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_BASE_CALLBACK_RESULT_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_BASE_CALLBACK_RESULT_H_ + +#include +#include +#include + +namespace flutter_inappwebview_plugin +{ + template + class BaseCallbackResult : public flutter::MethodResultFunctions + { + public: + flutter::ResultHandlerError error; + flutter::ResultHandlerNotImplemented notImplemented; + std::function nonNullSuccess = [](const T result) { return true; }; + std::function nullSuccess = []() { return true; }; + std::function result)> defaultBehaviour = [](const std::optional result) {}; + std::function(const flutter::EncodableValue* result)> decodeResult = [](const flutter::EncodableValue* result) { return std::nullopt; }; + + BaseCallbackResult() : + MethodResultFunctions( + [this](const flutter::EncodableValue* val) { + std::optional result = decodeResult ? decodeResult(val) : std::nullopt; + auto shouldRunDefaultBehaviour = false; + if (result.has_value()) { + shouldRunDefaultBehaviour = nonNullSuccess ? nonNullSuccess(result.value()) : shouldRunDefaultBehaviour; + } + else { + shouldRunDefaultBehaviour = nullSuccess ? nullSuccess() : shouldRunDefaultBehaviour; + } + if (shouldRunDefaultBehaviour && defaultBehaviour) { + defaultBehaviour(result); + } + }, + [this](const std::string& error_code, const std::string& error_message, const flutter::EncodableValue* error_details) { + if (error) { + error(error_code, error_message, error_details); + } + }, + [this]() { + if (defaultBehaviour) { + defaultBehaviour(std::nullopt); + } + if (notImplemented) { + notImplemented(); + } + }) {}; + virtual ~BaseCallbackResult() {}; + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_BASE_CALLBACK_RESULT_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/channel_delegate.h b/flutter_inappwebview_windows/windows/types/channel_delegate.h index b494d5c9..79c1b5d9 100644 --- a/flutter_inappwebview_windows/windows/types/channel_delegate.h +++ b/flutter_inappwebview_windows/windows/types/channel_delegate.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_CHANNEL_DELEGATE_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_CHANNEL_DELEGATE_H_ #include @@ -18,10 +19,12 @@ namespace flutter_inappwebview_plugin flutter::BinaryMessenger* messenger; ChannelDelegate(flutter::BinaryMessenger* messenger, const std::string& name); - ~ChannelDelegate(); + virtual ~ChannelDelegate(); virtual void HandleMethodCall( const flutter::MethodCall& method_call, std::unique_ptr> result); }; -} \ No newline at end of file +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_CHANNEL_DELEGATE_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/navigation_action.cpp b/flutter_inappwebview_windows/windows/types/navigation_action.cpp new file mode 100644 index 00000000..9cb94398 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/navigation_action.cpp @@ -0,0 +1,20 @@ +#include "navigation_action.h" + +#include "../utils/util.h" + +namespace flutter_inappwebview_plugin +{ + NavigationAction::NavigationAction(std::shared_ptr request, bool isForMainFrame) + : request(std::move(request)), isForMainFrame(isForMainFrame) + { + + } + + flutter::EncodableMap NavigationAction::toEncodableMap() + { + return flutter::EncodableMap{ + {flutter::EncodableValue("request"), request->toEncodableMap()}, + {flutter::EncodableValue("isForMainFrame"), flutter::EncodableValue(isForMainFrame)} + }; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/navigation_action.h b/flutter_inappwebview_windows/windows/types/navigation_action.h new file mode 100644 index 00000000..1b48753b --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/navigation_action.h @@ -0,0 +1,22 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_NAVIGATION_ACTION_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_NAVIGATION_ACTION_H_ + +#include +#include +#include "url_request.h" + +namespace flutter_inappwebview_plugin +{ + class NavigationAction + { + public: + const std::shared_ptr request; + const bool isForMainFrame; + + NavigationAction(std::shared_ptr request, bool isForMainFrame); + ~NavigationAction() = default; + flutter::EncodableMap toEncodableMap(); + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_NAVIGATION_ACTION_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/url_request.cpp b/flutter_inappwebview_windows/windows/types/url_request.cpp index 2c74e213..ddd7317c 100644 --- a/flutter_inappwebview_windows/windows/types/url_request.cpp +++ b/flutter_inappwebview_windows/windows/types/url_request.cpp @@ -4,10 +4,28 @@ namespace flutter_inappwebview_plugin { - URLRequest::URLRequest(const flutter::EncodableMap map) - : url(get_optional_flutter_value(map, "url")), - method(get_optional_flutter_value(map, "method")) + URLRequest::URLRequest(std::optional url, std::optional method, std::optional> headers, std::optional> body) + : url(url), method(method), headers(headers), body(body) { } + + URLRequest::URLRequest(const flutter::EncodableMap map) + : url(get_optional_fl_map_value(map, "url")), + method(get_optional_fl_map_value(map, "method")), + headers(get_optional_fl_map_value(map, "headers")), + body(get_optional_fl_map_value>(map, "body")) + { + + } + + flutter::EncodableMap URLRequest::toEncodableMap() + { + return flutter::EncodableMap{ + {flutter::EncodableValue("url"), optional_to_fl_value(url)}, + {flutter::EncodableValue("method"), optional_to_fl_value(method)}, + {flutter::EncodableValue("headers"), optional_to_fl_value(headers)}, + {flutter::EncodableValue("body"), optional_to_fl_value(body)} + }; + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/url_request.h b/flutter_inappwebview_windows/windows/types/url_request.h index 5e5f9569..d05dac7a 100644 --- a/flutter_inappwebview_windows/windows/types/url_request.h +++ b/flutter_inappwebview_windows/windows/types/url_request.h @@ -12,8 +12,13 @@ namespace flutter_inappwebview_plugin public: const std::optional url; const std::optional method; + const std::optional> headers; + const std::optional> body; + URLRequest(std::optional url, std::optional method, std::optional> headers, std::optional> body); URLRequest(const flutter::EncodableMap map); + ~URLRequest() = default; + flutter::EncodableMap toEncodableMap(); }; } diff --git a/flutter_inappwebview_windows/windows/utils/util.h b/flutter_inappwebview_windows/windows/utils/util.h index 9ff79419..843401f3 100644 --- a/flutter_inappwebview_windows/windows/utils/util.h +++ b/flutter_inappwebview_windows/windows/utils/util.h @@ -1,11 +1,21 @@ #ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ #define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ +#include #include #include +#include +#include "strconv.h" namespace flutter_inappwebview_plugin { + static inline void debugLog(const std::string& msg) { +#ifndef NDEBUG + std::cout << msg << std::endl; + OutputDebugString(ansi_to_wide(msg + "\n").c_str()); +#endif + } + template static inline std::optional make_pointer_optional(const T* value) { @@ -13,17 +23,50 @@ namespace flutter_inappwebview_plugin } template - static inline T get_flutter_value(const flutter::EncodableMap map, const char* string) + static inline T get_fl_map_value(const flutter::EncodableMap map, const char* string) { return std::get(map.at(flutter::EncodableValue(string))); } template - static inline std::optional get_optional_flutter_value(const flutter::EncodableMap map, const char* string) + static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap map, const char* string) { return make_pointer_optional(std::get_if(&map.at(flutter::EncodableValue(string)))); } + static inline std::optional> get_optional_fl_map_value(const flutter::EncodableMap map, const char* string) + { + auto mapValue = std::map{}; + auto flMap = std::get_if(&map.at(flutter::EncodableValue(string))); + if (flMap) { + for (auto itr = flMap->begin(); itr != flMap->end(); itr++) + { + mapValue.insert({ std::get(itr->first), std::get(itr->second) }); + } + } + return make_pointer_optional>(&mapValue); + } + + template + static inline flutter::EncodableValue optional_to_fl_value(const std::optional optional) + { + return optional.has_value() ? flutter::EncodableValue(optional.value()) : flutter::EncodableValue(); + } + + static inline flutter::EncodableValue optional_to_fl_value(const std::optional> optional) + { + if (!optional.has_value()) { + return flutter::EncodableValue(); + } + auto& mapValue = optional.value(); + auto encodableMap = flutter::EncodableMap{}; + for (auto const& [key, val] : mapValue) + { + encodableMap.insert({ flutter::EncodableValue(key), flutter::EncodableValue(val) }); + } + return encodableMap; + } + static inline std::string variant_to_string(std::variant var) { return std::visit([](auto&& arg) { From dcfeeb7497dea644e2b1d2da6fa0dc9d31e0f9ea Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 7 Jan 2024 23:28:46 +0100 Subject: [PATCH 03/21] windows: fixed in app browser dealloc, fixed on load stop event, implemented onReceivedError and onReceivedHttpError events --- .../lib/in_app_browser_example.screen.dart | 1 + .../platform_in_app_browser.dart | 9 ++ .../platform_inappwebview_controller.dart | 2 + .../src/types/web_resource_error_type.dart | 116 +++++++++++++++-- .../src/types/web_resource_error_type.g.dart | 120 ++++++++++++++++++ .../src/in_app_browser/in_app_browser.dart | 4 +- .../windows/CMakeLists.txt | 9 +- ...utter_inappwebview_windows_base_plugin.cpp | 14 -- ...flutter_inappwebview_windows_base_plugin.h | 18 --- .../flutter_inappwebview_windows_plugin.cpp | 4 +- .../flutter_inappwebview_windows_plugin.h | 11 +- .../windows/in_app_browser/in_app_browser.cpp | 42 ++---- .../windows/in_app_browser/in_app_browser.h | 7 +- .../in_app_browser_channel_delegate.cpp | 2 +- .../in_app_browser_channel_delegate.h | 2 +- .../in_app_browser/in_app_browser_manager.cpp | 13 +- .../in_app_browser/in_app_browser_manager.h | 6 +- .../windows/in_app_webview/in_app_webview.cpp | 78 +++++++++--- .../windows/in_app_webview/in_app_webview.h | 17 ++- .../webview_channel_delegate.cpp | 47 +++++-- .../in_app_webview/webview_channel_delegate.h | 11 +- .../windows/types/channel_delegate.cpp | 2 + .../windows/types/channel_delegate.h | 5 - .../windows/types/navigation_action.cpp | 2 +- .../windows/types/navigation_action.h | 2 +- .../windows/types/url_request.cpp | 17 +-- .../windows/types/url_request.h | 5 +- .../windows/types/web_resource_error.cpp | 27 ++++ .../windows/types/web_resource_error.h | 46 +++++++ .../windows/types/web_resource_request.cpp | 32 +++++ .../windows/types/web_resource_request.h | 26 ++++ .../windows/types/web_resource_response.cpp | 25 ++++ .../windows/types/web_resource_response.h | 22 ++++ .../windows/utils/flutter.h | 77 +++++++++++ .../windows/utils/util.h | 60 ++------- 35 files changed, 681 insertions(+), 200 deletions(-) delete mode 100644 flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.cpp delete mode 100644 flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.h create mode 100644 flutter_inappwebview_windows/windows/types/web_resource_error.cpp create mode 100644 flutter_inappwebview_windows/windows/types/web_resource_error.h create mode 100644 flutter_inappwebview_windows/windows/types/web_resource_request.cpp create mode 100644 flutter_inappwebview_windows/windows/types/web_resource_request.h create mode 100644 flutter_inappwebview_windows/windows/types/web_resource_response.cpp create mode 100644 flutter_inappwebview_windows/windows/types/web_resource_response.h create mode 100644 flutter_inappwebview_windows/windows/utils/flutter.h diff --git a/flutter_inappwebview/example/lib/in_app_browser_example.screen.dart b/flutter_inappwebview/example/lib/in_app_browser_example.screen.dart index 15f2bd7b..6d307cb8 100755 --- a/flutter_inappwebview/example/lib/in_app_browser_example.screen.dart +++ b/flutter_inappwebview/example/lib/in_app_browser_example.screen.dart @@ -30,6 +30,7 @@ class MyInAppBrowser extends InAppBrowser { @override Future onLoadStop(url) async { pullToRefreshController?.endRefreshing(); + print(await webViewController?.getUrl()); } @override diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart index 999b92a3..aa9b525b 100755 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart @@ -64,6 +64,7 @@ class PlatformInAppBrowserCreationParams { ///- Android native WebView ///- iOS ///- MacOS +///- Windows ///{@endtemplate} abstract class PlatformInAppBrowser extends PlatformInterface implements Disposable { @@ -172,6 +173,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future openUrlRequest( {required URLRequest urlRequest, @@ -464,6 +466,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} bool isOpened() { throw UnimplementedError( @@ -487,6 +490,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ///- iOS ///- MacOS + ///- Windows void onBrowserCreated() {} ///Event fired when the [PlatformInAppBrowser] window is closed. @@ -495,6 +499,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ///- iOS ///- MacOS + ///- Windows void onExit() {} ///Event fired when the main window is about to close. @@ -509,6 +514,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ([Official API - WebViewClient.onPageStarted](https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview)) + ///- Windows ([Official API - IWebView2WebView.add_NavigationStarting](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationstarting)) void onLoadStart(WebUri? url) {} ///Event fired when the [PlatformInAppBrowser] finishes loading an [url]. @@ -517,6 +523,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ([Official API - WebViewClient.onPageFinished](https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) + ///- Windows ([Official API - IWebView2WebView.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) void onLoadStop(WebUri? url) {} ///Use [onReceivedError] instead. @@ -529,6 +536,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ([Official API - WebViewClient.onReceivedError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceError))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) + ///- Windows ([Official API - IWebView2WebView.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) void onReceivedError(WebResourceRequest request, WebResourceError error) {} ///Use [onReceivedHttpError] instead. @@ -547,6 +555,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ([Official API - WebViewClient.onReceivedHttpError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) + ///- Windows ([Official API - IWebView2WebView.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) void onReceivedHttpError( WebResourceRequest request, WebResourceResponse errorResponse) {} diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart index 952ab49d..f9ce118b 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart @@ -134,6 +134,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.url](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url)) ///- MacOS ([Official API - WKWebView.url](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url)) ///- Web + ///- Windows ([Official API - IWebView2WebView.get_Source](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#get_source)) ///{@endtemplate} Future getUrl() { throw UnimplementedError( @@ -224,6 +225,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.load](https://developer.apple.com/documentation/webkit/wkwebview/1414954-load). If [allowingReadAccessTo] is used, [Official API - WKWebView.loadFileURL](https://developer.apple.com/documentation/webkit/wkwebview/1414973-loadfileurl)) ///- MacOS ([Official API - WKWebView.load](https://developer.apple.com/documentation/webkit/wkwebview/1414954-load). If [allowingReadAccessTo] is used, [Official API - WKWebView.loadFileURL](https://developer.apple.com/documentation/webkit/wkwebview/1414973-loadfileurl)) ///- Web + ///- Windows ([Official API - ICoreWebView2_2.NavigateWithWebResourceRequest](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2_2?view=webview2-1.0.2210.55#navigatewithwebresourcerequest)) ///{@endtemplate} Future loadUrl( {required URLRequest urlRequest, diff --git a/flutter_inappwebview_platform_interface/lib/src/types/web_resource_error_type.dart b/flutter_inappwebview_platform_interface/lib/src/types/web_resource_error_type.dart index 822de4cb..49b91fa8 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/web_resource_error_type.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/web_resource_error_type.dart @@ -60,7 +60,12 @@ class WebResourceErrorType_ { apiName: 'URLError.cannotConnectToHost', apiUrl: 'https://developer.apple.com/documentation/foundation/urlerror/code/2883001-cannotconnecttohost', - value: -1004) + value: -1004), + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_CANNOT_CONNECT', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 12) ]) static const CANNOT_CONNECT_TO_HOST = WebResourceErrorType_._internal("CANNOT_CONNECT_TO_HOST"); @@ -124,7 +129,12 @@ class WebResourceErrorType_ { apiName: 'URLError.cannotFindHost', apiUrl: 'https://developer.apple.com/documentation/foundation/urlerror/code/2883157-cannotfindhost', - value: -1003) + value: -1003), + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_HOST_NAME_NOT_RESOLVED', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 13) ]) static const HOST_LOOKUP = WebResourceErrorType_._internal("HOST_LOOKUP"); @@ -186,7 +196,12 @@ class WebResourceErrorType_ { apiName: 'URLError.timedOut', apiUrl: 'https://developer.apple.com/documentation/foundation/urlerror/code/2883027-timedout', - value: -1001) + value: -1001), + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_TIMEOUT', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 7) ]) static const TIMEOUT = WebResourceErrorType_._internal("TIMEOUT"); @@ -217,7 +232,12 @@ class WebResourceErrorType_ { apiName: 'URLError.unknown', apiUrl: 'https://developer.apple.com/documentation/foundation/urlerror/2293357-unknown', - value: -1) + value: -1), + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_UNKNOWN', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 0) ]) static const UNKNOWN = WebResourceErrorType_._internal("UNKNOWN"); @@ -276,7 +296,12 @@ class WebResourceErrorType_ { apiName: 'URLError.cancelled', apiUrl: 'https://developer.apple.com/documentation/foundation/urlerror/code/2883178-cancelled', - value: -999) + value: -999), + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_OPERATION_CANCELED', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 14) ]) static const CANCELLED = WebResourceErrorType_._internal("CANCELLED"); @@ -291,7 +316,12 @@ class WebResourceErrorType_ { apiName: 'URLError.networkConnectionLost', apiUrl: 'https://developer.apple.com/documentation/foundation/urlerror/2293759-networkconnectionlost', - value: -1005) + value: -1005), + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_DISCONNECTED', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 11) ]) static const NETWORK_CONNECTION_LOST = WebResourceErrorType_._internal("NETWORK_CONNECTION_LOST"); @@ -356,7 +386,12 @@ class WebResourceErrorType_ { apiName: 'URLError.badServerResponse', apiUrl: 'https://developer.apple.com/documentation/foundation/urlerror/2293606-badserverresponse', - value: -1011) + value: -1011), + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_ERROR_HTTP_INVALID_SERVER_RESPONSE', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 8) ]) static const BAD_SERVER_RESPONSE = WebResourceErrorType_._internal("BAD_SERVER_RESPONSE"); @@ -389,7 +424,12 @@ class WebResourceErrorType_ { apiName: 'URLError.userAuthenticationRequired', apiUrl: 'https://developer.apple.com/documentation/foundation/urlerror/2293560-userauthenticationrequired', - value: -1013) + value: -1013), + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_VALID_AUTHENTICATION_CREDENTIALS_REQUIRED', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 17), ]) static const USER_AUTHENTICATION_REQUIRED = WebResourceErrorType_._internal("USER_AUTHENTICATION_REQUIRED"); @@ -892,4 +932,64 @@ class WebResourceErrorType_ { ]) static const BACKGROUND_SESSION_WAS_DISCONNECTED = WebResourceErrorType_._internal("BACKGROUND_SESSION_WAS_DISCONNECTED"); + + ///Indicates that the host is unreachable. + @EnumSupportedPlatforms(platforms: [ + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_SERVER_UNREACHABLE', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 6), + ]) + static const SERVER_UNREACHABLE = WebResourceErrorType_._internal("SERVER_UNREACHABLE"); + + ///Indicates that the connection was stopped. + @EnumSupportedPlatforms(platforms: [ + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_ABORTED', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 9) + ]) + static const CONNECTION_ABORTED = WebResourceErrorType_._internal("CONNECTION_ABORTED"); + + ///Indicates that the connection was reset. + @EnumSupportedPlatforms(platforms: [ + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_RESET', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 10), + ]) + static const RESET = WebResourceErrorType_._internal("RESET"); + + ///Indicates that the request redirect failed. + @EnumSupportedPlatforms(platforms: [ + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_REDIRECT_FAILED', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 15), + ]) + static const REDIRECT_FAILED = WebResourceErrorType_._internal("REDIRECT_FAILED"); + + ///Indicates that an unexpected error occurred. + @EnumSupportedPlatforms(platforms: [ + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_UNEXPECTED_ERROR', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 16), + ]) + static const UNEXPECTED_ERROR = WebResourceErrorType_._internal("UNEXPECTED_ERROR"); + + ///Indicates that user lacks proper authentication credentials for a proxy server. + @EnumSupportedPlatforms(platforms: [ + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_WEB_ERROR_STATUS_VALID_PROXY_AUTHENTICATION_REQUIRED', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status', + value: 18), + ]) + static const VALID_PROXY_AUTHENTICATION_REQUIRED = WebResourceErrorType_._internal("VALID_PROXY_AUTHENTICATION_REQUIRED"); } diff --git a/flutter_inappwebview_platform_interface/lib/src/types/web_resource_error_type.g.dart b/flutter_inappwebview_platform_interface/lib/src/types/web_resource_error_type.g.dart index 4a4f905d..bbf3f6d8 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/web_resource_error_type.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/web_resource_error_type.g.dart @@ -97,6 +97,7 @@ class WebResourceErrorType { ///**Officially Supported Platforms/Implementations**: ///- iOS ([Official API - URLError.badServerResponse](https://developer.apple.com/documentation/foundation/urlerror/2293606-badserverresponse)) ///- MacOS ([Official API - URLError.badServerResponse](https://developer.apple.com/documentation/foundation/urlerror/2293606-badserverresponse)) + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_ERROR_HTTP_INVALID_SERVER_RESPONSE](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) static final BAD_SERVER_RESPONSE = WebResourceErrorType._internalMultiPlatform('BAD_SERVER_RESPONSE', () { switch (defaultTargetPlatform) { @@ -104,6 +105,8 @@ class WebResourceErrorType { return -1011; case TargetPlatform.macOS: return -1011; + case TargetPlatform.windows: + return 8; default: break; } @@ -154,6 +157,7 @@ class WebResourceErrorType { ///**Officially Supported Platforms/Implementations**: ///- iOS ([Official API - URLError.cancelled](https://developer.apple.com/documentation/foundation/urlerror/code/2883178-cancelled)) ///- MacOS ([Official API - URLError.cancelled](https://developer.apple.com/documentation/foundation/urlerror/code/2883178-cancelled)) + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_OPERATION_CANCELED](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) static final CANCELLED = WebResourceErrorType._internalMultiPlatform('CANCELLED', () { switch (defaultTargetPlatform) { @@ -161,6 +165,8 @@ class WebResourceErrorType { return -999; case TargetPlatform.macOS: return -999; + case TargetPlatform.windows: + return 14; default: break; } @@ -191,6 +197,7 @@ class WebResourceErrorType { ///- Android native WebView ([Official API - WebViewClient.ERROR_CONNECT](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_CONNECT)) ///- iOS ([Official API - URLError.cannotConnectToHost](https://developer.apple.com/documentation/foundation/urlerror/code/2883001-cannotconnecttohost)) ///- MacOS ([Official API - URLError.cannotConnectToHost](https://developer.apple.com/documentation/foundation/urlerror/code/2883001-cannotconnecttohost)) + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_CANNOT_CONNECT](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) static final CANNOT_CONNECT_TO_HOST = WebResourceErrorType._internalMultiPlatform('CANNOT_CONNECT_TO_HOST', () { switch (defaultTargetPlatform) { @@ -200,6 +207,8 @@ class WebResourceErrorType { return -1004; case TargetPlatform.macOS: return -1004; + case TargetPlatform.windows: + return 12; default: break; } @@ -408,6 +417,21 @@ class WebResourceErrorType { return null; }); + ///Indicates that the connection was stopped. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_ABORTED](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) + static final CONNECTION_ABORTED = + WebResourceErrorType._internalMultiPlatform('CONNECTION_ABORTED', () { + switch (defaultTargetPlatform) { + case TargetPlatform.windows: + return 9; + default: + break; + } + return null; + }); + ///The length of the resource data exceeds the maximum allowed. /// ///**Officially Supported Platforms/Implementations**: @@ -558,6 +582,7 @@ class WebResourceErrorType { ///- Android native WebView ([Official API - WebViewClient.ERROR_HOST_LOOKUP](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_HOST_LOOKUP)) ///- iOS ([Official API - URLError.cannotFindHost](https://developer.apple.com/documentation/foundation/urlerror/code/2883157-cannotfindhost)) ///- MacOS ([Official API - URLError.cannotFindHost](https://developer.apple.com/documentation/foundation/urlerror/code/2883157-cannotfindhost)) + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_HOST_NAME_NOT_RESOLVED](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) static final HOST_LOOKUP = WebResourceErrorType._internalMultiPlatform('HOST_LOOKUP', () { switch (defaultTargetPlatform) { @@ -567,6 +592,8 @@ class WebResourceErrorType { return -1003; case TargetPlatform.macOS: return -1003; + case TargetPlatform.windows: + return 13; default: break; } @@ -611,6 +638,7 @@ class WebResourceErrorType { ///**Officially Supported Platforms/Implementations**: ///- iOS ([Official API - URLError.networkConnectionLost](https://developer.apple.com/documentation/foundation/urlerror/2293759-networkconnectionlost)) ///- MacOS ([Official API - URLError.networkConnectionLost](https://developer.apple.com/documentation/foundation/urlerror/2293759-networkconnectionlost)) + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_DISCONNECTED](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) static final NETWORK_CONNECTION_LOST = WebResourceErrorType._internalMultiPlatform('NETWORK_CONNECTION_LOST', () { @@ -619,6 +647,8 @@ class WebResourceErrorType { return -1005; case TargetPlatform.macOS: return -1005; + case TargetPlatform.windows: + return 11; default: break; } @@ -678,6 +708,21 @@ class WebResourceErrorType { return null; }); + ///Indicates that the request redirect failed. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_REDIRECT_FAILED](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) + static final REDIRECT_FAILED = + WebResourceErrorType._internalMultiPlatform('REDIRECT_FAILED', () { + switch (defaultTargetPlatform) { + case TargetPlatform.windows: + return 15; + default: + break; + } + return null; + }); + ///A redirect was specified by way of server response code, but the server didn’t accompany this code with a redirect URL. /// ///**Officially Supported Platforms/Implementations**: @@ -716,6 +761,20 @@ class WebResourceErrorType { return null; }); + ///Indicates that the connection was reset. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_RESET](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) + static final RESET = WebResourceErrorType._internalMultiPlatform('RESET', () { + switch (defaultTargetPlatform) { + case TargetPlatform.windows: + return 10; + default: + break; + } + return null; + }); + ///A requested resource couldn't be retrieved. ///This error can indicate a file-not-found situation, or decoding problems that prevent data from being processed correctly. /// @@ -830,12 +889,28 @@ class WebResourceErrorType { return null; }); + ///Indicates that the host is unreachable. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_SERVER_UNREACHABLE](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) + static final SERVER_UNREACHABLE = + WebResourceErrorType._internalMultiPlatform('SERVER_UNREACHABLE', () { + switch (defaultTargetPlatform) { + case TargetPlatform.windows: + return 6; + default: + break; + } + return null; + }); + ///Connection timed out. /// ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebViewClient.ERROR_TIMEOUT](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_TIMEOUT)) ///- iOS ([Official API - URLError.timedOut](https://developer.apple.com/documentation/foundation/urlerror/code/2883027-timedout)) ///- MacOS ([Official API - URLError.timedOut](https://developer.apple.com/documentation/foundation/urlerror/code/2883027-timedout)) + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_TIMEOUT](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) static final TIMEOUT = WebResourceErrorType._internalMultiPlatform('TIMEOUT', () { switch (defaultTargetPlatform) { @@ -845,6 +920,8 @@ class WebResourceErrorType { return -1001; case TargetPlatform.macOS: return -1001; + case TargetPlatform.windows: + return 7; default: break; } @@ -887,12 +964,28 @@ class WebResourceErrorType { return null; }); + ///Indicates that an unexpected error occurred. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_UNEXPECTED_ERROR](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) + static final UNEXPECTED_ERROR = + WebResourceErrorType._internalMultiPlatform('UNEXPECTED_ERROR', () { + switch (defaultTargetPlatform) { + case TargetPlatform.windows: + return 16; + default: + break; + } + return null; + }); + ///The URL Loading System encountered an error that it can’t interpret. /// ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebViewClient.ERROR_UNKNOWN](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_UNKNOWN)) ///- iOS ([Official API - URLError.unknown](https://developer.apple.com/documentation/foundation/urlerror/2293357-unknown)) ///- MacOS ([Official API - URLError.unknown](https://developer.apple.com/documentation/foundation/urlerror/2293357-unknown)) + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_UNKNOWN](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) static final UNKNOWN = WebResourceErrorType._internalMultiPlatform('UNKNOWN', () { switch (defaultTargetPlatform) { @@ -902,6 +995,8 @@ class WebResourceErrorType { return -1; case TargetPlatform.macOS: return -1; + case TargetPlatform.windows: + return 0; default: break; } @@ -982,6 +1077,7 @@ class WebResourceErrorType { ///**Officially Supported Platforms/Implementations**: ///- iOS ([Official API - URLError.userAuthenticationRequired](https://developer.apple.com/documentation/foundation/urlerror/2293560-userauthenticationrequired)) ///- MacOS ([Official API - URLError.userAuthenticationRequired](https://developer.apple.com/documentation/foundation/urlerror/2293560-userauthenticationrequired)) + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_VALID_AUTHENTICATION_CREDENTIALS_REQUIRED](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) static final USER_AUTHENTICATION_REQUIRED = WebResourceErrorType._internalMultiPlatform( 'USER_AUTHENTICATION_REQUIRED', () { @@ -990,6 +1086,8 @@ class WebResourceErrorType { return -1013; case TargetPlatform.macOS: return -1013; + case TargetPlatform.windows: + return 17; default: break; } @@ -1016,6 +1114,22 @@ class WebResourceErrorType { return null; }); + ///Indicates that user lacks proper authentication credentials for a proxy server. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - COREWEBVIEW2_WEB_ERROR_STATUS_VALID_PROXY_AUTHENTICATION_REQUIRED](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_web_error_status)) + static final VALID_PROXY_AUTHENTICATION_REQUIRED = + WebResourceErrorType._internalMultiPlatform( + 'VALID_PROXY_AUTHENTICATION_REQUIRED', () { + switch (defaultTargetPlatform) { + case TargetPlatform.windows: + return 18; + default: + break; + } + return null; + }); + ///A server reported that a URL has a non-zero content length, but terminated the network connection gracefully without sending any data. /// ///**Officially Supported Platforms/Implementations**: @@ -1057,6 +1171,7 @@ class WebResourceErrorType { WebResourceErrorType.CANNOT_WRITE_TO_FILE, WebResourceErrorType.CLIENT_CERTIFICATE_REJECTED, WebResourceErrorType.CLIENT_CERTIFICATE_REQUIRED, + WebResourceErrorType.CONNECTION_ABORTED, WebResourceErrorType.DATA_LENGTH_EXCEEDS_MAXIMUM, WebResourceErrorType.DATA_NOT_ALLOWED, WebResourceErrorType.DOWNLOAD_DECODING_FAILED_MID_STREAM, @@ -1072,17 +1187,21 @@ class WebResourceErrorType { WebResourceErrorType.NOT_CONNECTED_TO_INTERNET, WebResourceErrorType.NO_PERMISSIONS_TO_READ_FILE, WebResourceErrorType.PROXY_AUTHENTICATION, + WebResourceErrorType.REDIRECT_FAILED, WebResourceErrorType.REDIRECT_TO_NON_EXISTENT_LOCATION, WebResourceErrorType.REQUEST_BODY_STREAM_EXHAUSTED, + WebResourceErrorType.RESET, WebResourceErrorType.RESOURCE_UNAVAILABLE, WebResourceErrorType.SECURE_CONNECTION_FAILED, WebResourceErrorType.SERVER_CERTIFICATE_HAS_BAD_DATE, WebResourceErrorType.SERVER_CERTIFICATE_HAS_UNKNOWN_ROOT, WebResourceErrorType.SERVER_CERTIFICATE_NOT_YET_VALID, WebResourceErrorType.SERVER_CERTIFICATE_UNTRUSTED, + WebResourceErrorType.SERVER_UNREACHABLE, WebResourceErrorType.TIMEOUT, WebResourceErrorType.TOO_MANY_REDIRECTS, WebResourceErrorType.TOO_MANY_REQUESTS, + WebResourceErrorType.UNEXPECTED_ERROR, WebResourceErrorType.UNKNOWN, WebResourceErrorType.UNSAFE_RESOURCE, WebResourceErrorType.UNSUPPORTED_AUTH_SCHEME, @@ -1090,6 +1209,7 @@ class WebResourceErrorType { WebResourceErrorType.USER_AUTHENTICATION_FAILED, WebResourceErrorType.USER_AUTHENTICATION_REQUIRED, WebResourceErrorType.USER_CANCELLED_AUTHENTICATION, + WebResourceErrorType.VALID_PROXY_AUTHENTICATION_REQUIRED, WebResourceErrorType.ZERO_BYTE_RESOURCE, ].toSet(); diff --git a/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart b/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart index 1bf9a54b..e37a3068 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart @@ -65,7 +65,7 @@ class WindowsInAppBrowser extends PlatformInAppBrowser with ChannelController { return _staticValue; } - WindowsInAppBrowserCreationParams get _macosParams => + WindowsInAppBrowserCreationParams get _windowsParams => params as WindowsInAppBrowserCreationParams; static const MethodChannel _staticChannel = @@ -95,7 +95,7 @@ class WindowsInAppBrowser extends PlatformInAppBrowser with ChannelController { channel!, this, this.initialUserScripts); - _macosParams.findInteractionController?.init(id); + _windowsParams.findInteractionController?.init(id); } _debugLog(String method, dynamic args) { diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt index a2088f2f..475fb4eb 100644 --- a/flutter_inappwebview_windows/windows/CMakeLists.txt +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -49,12 +49,11 @@ add_custom_command( # Any new source files that you add to the plugin should be added here. list(APPEND PLUGIN_SOURCES - "flutter_inappwebview_windows_base_plugin.h" - "flutter_inappwebview_windows_base_plugin.cpp" "flutter_inappwebview_windows_plugin.cpp" "flutter_inappwebview_windows_plugin.h" "utils/strconv.h" "utils/util.h" + "utils/flutter.h" "types/channel_delegate.cpp" "types/channel_delegate.h" "types/base_callback_result.h" @@ -62,6 +61,12 @@ list(APPEND PLUGIN_SOURCES "types/url_request.h" "types/navigation_action.cpp" "types/navigation_action.h" + "types/web_resource_error.cpp" + "types/web_resource_error.h" + "types/web_resource_request.cpp" + "types/web_resource_request.h" + "types/web_resource_response.cpp" + "types/web_resource_response.h" "in_app_webview/in_app_webview.cpp" "in_app_webview/in_app_webview.h" "in_app_webview/webview_channel_delegate.cpp" diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.cpp b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.cpp deleted file mode 100644 index 45c7cbc2..00000000 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "flutter_inappwebview_windows_base_plugin.h" - -namespace flutter_inappwebview_plugin -{ - FlutterInappwebviewWindowsBasePlugin::FlutterInappwebviewWindowsBasePlugin(flutter::PluginRegistrarWindows* registrar) : registrar(registrar) - { - - } - - FlutterInappwebviewWindowsBasePlugin::~FlutterInappwebviewWindowsBasePlugin() - { - - } -} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.h b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.h deleted file mode 100644 index e442060b..00000000 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_base_plugin.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_BASE_PLUGIN_H_ -#define FLUTTER_INAPPWEBVIEW_PLUGIN_BASE_PLUGIN_H_ - -#include - -namespace flutter_inappwebview_plugin -{ - class FlutterInappwebviewWindowsBasePlugin : public flutter::Plugin, public std::enable_shared_from_this { - public: - flutter::PluginRegistrarWindows* registrar; - - FlutterInappwebviewWindowsBasePlugin(flutter::PluginRegistrarWindows* registrar); - - virtual ~FlutterInappwebviewWindowsBasePlugin(); - }; -} - -#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_BASE_PLUGIN_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp index b849e10b..5e4070cb 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp @@ -3,6 +3,8 @@ #include #include +#include "in_app_browser/in_app_browser_manager.h" + namespace flutter_inappwebview_plugin { // static @@ -13,7 +15,7 @@ namespace flutter_inappwebview_plugin } FlutterInappwebviewWindowsPlugin::FlutterInappwebviewWindowsPlugin(flutter::PluginRegistrarWindows* registrar) - : FlutterInappwebviewWindowsBasePlugin(registrar) + : registrar(registrar) { inAppBrowserManager = std::make_unique(this); } diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h index e152a4ef..534ecabb 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h @@ -4,16 +4,13 @@ #include #include -#include - -#include "flutter_inappwebview_windows_base_plugin.h" - -#include "in_app_browser/in_app_browser_manager.h" - namespace flutter_inappwebview_plugin { - class FlutterInappwebviewWindowsPlugin : public FlutterInappwebviewWindowsBasePlugin { + class InAppBrowserManager; + + class FlutterInappwebviewWindowsPlugin : public flutter::Plugin { public: + flutter::PluginRegistrarWindows* registrar; std::unique_ptr inAppBrowserManager; static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index 50bc8e6f..0a158848 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -1,33 +1,11 @@ #include #include "in_app_browser.h" - -#include -#include - -#include "flutter/event_channel.h" -#include "flutter/plugin_registrar.h" -#include "flutter/plugin_registrar_windows.h" -#include "flutter/method_channel.h" -#include "flutter/encodable_value.h" - -#include -#include -#include -#include -#include -#include "../utils/strconv.h" #include "../utils/util.h" - -#include -#include +#include "in_app_browser_manager.h" namespace flutter_inappwebview_plugin { - using namespace Microsoft::WRL; - - const wchar_t* CLASS_NAME = L"InAppBrowser"; - - InAppBrowser::InAppBrowser(FlutterInappwebviewWindowsBasePlugin* plugin, const InAppBrowserCreationParams& params) + InAppBrowser::InAppBrowser(FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params) : plugin(plugin), m_hInstance(GetModuleHandle(nullptr)), id(params.id), @@ -35,7 +13,7 @@ namespace flutter_inappwebview_plugin channelDelegate(std::make_unique(id, plugin->registrar->messenger())) { WNDCLASS wndClass = {}; - wndClass.lpszClassName = CLASS_NAME; + wndClass.lpszClassName = InAppBrowser::CLASS_NAME; wndClass.hInstance = m_hInstance; wndClass.hIcon = LoadIcon(NULL, IDI_WINLOGO); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); @@ -45,9 +23,9 @@ namespace flutter_inappwebview_plugin m_hWnd = CreateWindowEx( 0, // Optional window styles. - CLASS_NAME, // Window class - L"Learn to Program Windows", // Window text - WS_OVERLAPPEDWINDOW, // Window style + InAppBrowser::CLASS_NAME, // Window class + L"", // Window text + WS_OVERLAPPEDWINDOW, // Window style // Size and position CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, @@ -186,15 +164,19 @@ namespace flutter_inappwebview_plugin ) noexcept { switch (message) { case WM_DESTROY: { - webView.reset(); - // might receive multiple WM_DESTROY messages. if (!destroyed_) { destroyed_ = true; + webView.reset(); + if (channelDelegate) { channelDelegate->onExit(); } + + if (plugin && plugin->inAppBrowserManager) { + plugin->inAppBrowserManager->browsers.erase(id); + } } return 0; } diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h index c674718f..a3adb62d 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h @@ -6,7 +6,7 @@ #include #include -#include "../flutter_inappwebview_windows_base_plugin.h" +#include "../flutter_inappwebview_windows_plugin.h" #include "../in_app_webview/in_app_webview.h" #include "../types/url_request.h" #include "in_app_browser_channel_delegate.h" @@ -22,19 +22,20 @@ namespace flutter_inappwebview_plugin class InAppBrowser { public: static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappbrowser_"; + static inline const wchar_t* CLASS_NAME = L"InAppBrowser"; static LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept; - FlutterInappwebviewWindowsBasePlugin* plugin; + FlutterInappwebviewWindowsPlugin* plugin; std::string id; std::optional initialUrlRequest; std::unique_ptr webView; std::unique_ptr channelDelegate; - InAppBrowser(FlutterInappwebviewWindowsBasePlugin* plugin, const InAppBrowserCreationParams& params); + InAppBrowser(FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params); ~InAppBrowser(); private: diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp index ebfee72c..9a253806 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp @@ -5,7 +5,7 @@ namespace flutter_inappwebview_plugin { - InAppBrowserChannelDelegate::InAppBrowserChannelDelegate(const std::string id, flutter::BinaryMessenger* messenger) + InAppBrowserChannelDelegate::InAppBrowserChannelDelegate(const std::string& id, flutter::BinaryMessenger* messenger) : ChannelDelegate(messenger, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id) { diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.h index 6c6fa5cb..488f69f1 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.h @@ -11,7 +11,7 @@ namespace flutter_inappwebview_plugin class InAppBrowserChannelDelegate : public ChannelDelegate { public: - InAppBrowserChannelDelegate(const std::string id, flutter::BinaryMessenger* messenger); + InAppBrowserChannelDelegate(const std::string& id, flutter::BinaryMessenger* messenger); ~InAppBrowserChannelDelegate(); void HandleMethodCall( diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp index b7dad155..a421aaee 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -1,16 +1,17 @@ #include #include #include +#include #include "in_app_browser_manager.h" #include "../types/url_request.h" -#include "../utils/util.h" +#include "../utils/flutter.h" namespace flutter_inappwebview_plugin { - InAppBrowserManager::InAppBrowserManager(FlutterInappwebviewWindowsBasePlugin* plugin) + InAppBrowserManager::InAppBrowserManager(FlutterInappwebviewWindowsPlugin* plugin) : plugin(plugin), ChannelDelegate(plugin->registrar->messenger(), InAppBrowserManager::METHOD_CHANNEL_NAME) { @@ -31,16 +32,16 @@ namespace flutter_inappwebview_plugin void InAppBrowserManager::open(const flutter::EncodableMap* arguments) { - auto id = std::get(arguments->at(flutter::EncodableValue("id"))); - auto urlRequestMap = std::get_if(&arguments->at(flutter::EncodableValue("urlRequest"))); - auto urlRequest = make_pointer_optional(new URLRequest(*urlRequestMap)); + auto id = get_fl_map_value(*arguments, "id"); + auto urlRequestMap = get_optional_fl_map_value(*arguments, "urlRequest"); + std::optional urlRequest = urlRequestMap.has_value() ? std::make_optional(urlRequestMap.value()) : std::optional{}; InAppBrowserCreationParams params = { id, urlRequest }; auto inAppBrowser = std::make_unique(plugin, params); - browsers[id] = std::move(inAppBrowser); + browsers.insert({ id, std::move(inAppBrowser) }); } InAppBrowserManager::~InAppBrowserManager() diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h index 389b27a0..b0093c51 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h @@ -6,7 +6,7 @@ #include #include -#include "../flutter_inappwebview_windows_base_plugin.h" +#include "../flutter_inappwebview_windows_plugin.h" #include "in_app_browser.h" @@ -19,10 +19,10 @@ namespace flutter_inappwebview_plugin public: static inline const std::string METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappbrowser"; - FlutterInappwebviewWindowsBasePlugin* plugin; + FlutterInappwebviewWindowsPlugin* plugin; std::map> browsers; - InAppBrowserManager(FlutterInappwebviewWindowsBasePlugin* plugin); + InAppBrowserManager(FlutterInappwebviewWindowsPlugin* plugin); ~InAppBrowserManager(); void HandleMethodCall( diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index 4ec9f748..51187943 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -6,18 +6,20 @@ #include #include "../utils/strconv.h" #include "../utils/util.h" +#include "../types/web_resource_request.h" +#include "../types/web_resource_error.h" namespace flutter_inappwebview_plugin { using namespace Microsoft::WRL; - InAppWebView::InAppWebView(FlutterInappwebviewWindowsBasePlugin* plugin, std::variant id, const HWND parentWindow, const std::function completionHandler) + InAppWebView::InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::function completionHandler) : plugin(plugin), id(id), channelDelegate(std::make_unique(this, plugin->registrar->messenger())) { createWebView(parentWindow, completionHandler); } - InAppWebView::InAppWebView(FlutterInappwebviewWindowsBasePlugin* plugin, std::variant id, const HWND parentWindow, const std::string& channelName, const std::function completionHandler) + InAppWebView::InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::string& channelName, const std::function completionHandler) : plugin(plugin), id(id), channelDelegate(std::make_unique(this, plugin->registrar->messenger(), channelName)) { createWebView(parentWindow, completionHandler); @@ -29,7 +31,7 @@ namespace flutter_inappwebview_plugin Callback( [parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT { webViewEnv = env; - // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd + // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window HWND env->CreateCoreWebView2Controller(parentWindow, Callback( [parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT { if (controller != nullptr) { @@ -67,9 +69,9 @@ namespace flutter_inappwebview_plugin } wil::unique_cotaskmem_string uri = nullptr; - std::optional url = SUCCEEDED(args->get_Uri(&uri)) ? wide_to_utf8(std::wstring(uri.get())) : std::optional{}; + std::optional url = SUCCEEDED(args->get_Uri(&uri)) ? wide_to_utf8(uri.get()) : std::optional{}; - wil::unique_cotaskmem_string method = nullptr; + wil::unique_cotaskmem_string requestMethod = nullptr; wil::com_ptr requestHeaders = nullptr; std::optional> headers = std::optional>{}; if (SUCCEEDED(args->get_RequestHeaders(&requestHeaders))) { @@ -83,26 +85,33 @@ namespace flutter_inappwebview_plugin wil::unique_cotaskmem_string value; if (SUCCEEDED(iterator->GetCurrentHeader(&name, &value))) { - headers->insert({ wide_to_utf8(std::wstring(name.get())), wide_to_utf8(std::wstring(value.get())) }); + headers->insert({ wide_to_utf8(name.get()), wide_to_utf8(value.get()) }); } BOOL hasNext = FALSE; iterator->MoveNext(&hasNext); } - requestHeaders->GetHeader(L"Flutter-InAppWebView-Request-Method", &method); + requestHeaders->GetHeader(L"Flutter-InAppWebView-Request-Method", &requestMethod); requestHeaders->RemoveHeader(L"Flutter-InAppWebView-Request-Method"); } - if (callShouldOverrideUrlLoading && method == nullptr) { - // for some reason, we can't cancel and load an URL with other HTTP methods other than GET, - // so ignore the shouldOverrideUrlLoading event. + std::optional method = requestMethod ? wide_to_utf8(requestMethod.get()) : std::optional{}; - auto urlRequest = std::make_shared(url, std::nullopt, headers, std::nullopt); - auto navigationAction = std::make_unique( - urlRequest, - true - ); + auto urlRequest = std::make_shared(url, method, headers, std::nullopt); + auto navigationAction = std::make_shared( + urlRequest, + true + ); + + UINT64 navigationId; + if (SUCCEEDED(args->get_NavigationId(&navigationId))) { + navigationActions.insert({navigationId, navigationAction}); + } + + if (callShouldOverrideUrlLoading && requestMethod == nullptr) { + // for some reason, we can't cancel and load an URL with other HTTP methods than GET, + // so ignore the shouldOverrideUrlLoading event. auto callback = std::make_unique(); callback->nonNullSuccess = [this, urlRequest](const NavigationActionPolicy actionPolicy) { @@ -137,9 +146,17 @@ namespace flutter_inappwebview_plugin webView->add_NavigationCompleted( Callback( [this](ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args) { - COREWEBVIEW2_WEB_ERROR_STATUS web_error_status; - args->get_WebErrorStatus(&web_error_status); - debugLog("WebErrorStatus " + std::to_string(web_error_status) + "\n"); + std::shared_ptr navigationAction; + UINT64 navigationId; + if (SUCCEEDED(args->get_NavigationId(&navigationId))) { + navigationAction = map_at_or_null(navigationActions, navigationId); + if (navigationAction) { + navigationActions.erase(navigationId); + } + } + + COREWEBVIEW2_WEB_ERROR_STATUS webErrorType = COREWEBVIEW2_WEB_ERROR_STATUS_UNKNOWN; + args->get_WebErrorStatus(&webErrorType); BOOL isSuccess; args->get_IsSuccess(&isSuccess); @@ -150,7 +167,21 @@ namespace flutter_inappwebview_plugin if (isSuccess) { channelDelegate->onLoadStop(url); } + else if (!InAppWebView::isSslError(webErrorType) && navigationAction) { + auto webResourceRequest = std::make_unique(url, navigationAction->request->method, navigationAction->request->headers, navigationAction->isForMainFrame); + int httpStatusCode = 0; + wil::com_ptr args2; + if (SUCCEEDED(args->QueryInterface(IID_PPV_ARGS(&args2))) && SUCCEEDED(args2->get_HttpStatusCode(&httpStatusCode)) && httpStatusCode >= 400) { + auto webResourceResponse = std::make_unique(httpStatusCode); + channelDelegate->onReceivedHttpError(std::move(webResourceRequest), std::move(webResourceResponse)); + } + else if (httpStatusCode < 400) { + auto webResourceError = std::make_unique(WebErrorStatusDescription[webErrorType], webErrorType); + channelDelegate->onReceivedError(std::move(webResourceRequest), std::move(webResourceError)); + } + } } + return S_OK; } ).Get(), nullptr); @@ -159,10 +190,10 @@ namespace flutter_inappwebview_plugin std::optional InAppWebView::getUrl() const { LPWSTR uri = nullptr; - return SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(std::wstring(uri)) : std::optional{}; + return SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(uri) : std::optional{}; } - void InAppWebView::loadUrl(const URLRequest urlRequest) const + void InAppWebView::loadUrl(const URLRequest& urlRequest) const { if (!webView || !urlRequest.url.has_value()) { return; @@ -191,7 +222,7 @@ namespace flutter_inappwebview_plugin ); wil::com_ptr requestHeaders; if (SUCCEEDED(webResourceRequest->get_Headers(&requestHeaders))) { - if (urlRequest.method.has_value() && urlRequest.method.value().compare("GET") != 0) { + if (method.compare(L"GET") != 0) { requestHeaders->SetHeader(L"Flutter-InAppWebView-Request-Method", method.c_str()); } if (urlRequest.headers.has_value()) { @@ -208,6 +239,10 @@ namespace flutter_inappwebview_plugin } } + bool InAppWebView::isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus) { + return webErrorStatus >= COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_COMMON_NAME_IS_INCORRECT && webErrorStatus <= COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID; + } + InAppWebView::~InAppWebView() { debugLog("dealloc InAppWebView"); @@ -217,6 +252,7 @@ namespace flutter_inappwebview_plugin if (webViewController) { webViewController->Close(); } + navigationActions.clear(); plugin = nullptr; } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index 833fc641..145b2512 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -6,8 +6,9 @@ #include #include #include "../types/url_request.h" +#include "../types/navigation_action.h" #include "webview_channel_delegate.h" -#include "../flutter_inappwebview_windows_base_plugin.h" +#include "../flutter_inappwebview_windows_plugin.h" namespace flutter_inappwebview_plugin { @@ -18,20 +19,22 @@ namespace flutter_inappwebview_plugin public: static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_"; - FlutterInappwebviewWindowsBasePlugin* plugin; - std::variant id; + const FlutterInappwebviewWindowsPlugin* plugin; + const std::variant id; wil::com_ptr webViewEnv; wil::com_ptr webViewController; wil::com_ptr webView; - std::unique_ptr channelDelegate; + const std::unique_ptr channelDelegate; + std::map> navigationActions = {}; - InAppWebView(FlutterInappwebviewWindowsBasePlugin* plugin, std::variant id, const HWND parentWindow, const std::function completionHandler); - InAppWebView(FlutterInappwebviewWindowsBasePlugin* plugin, std::variant id, const HWND parentWindow, const std::string& channelName, const std::function completionHandler); + InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::function completionHandler); + InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::string& channelName, const std::function completionHandler); ~InAppWebView(); std::optional getUrl() const; - void loadUrl(const URLRequest urlRequest) const; + void loadUrl(const URLRequest& urlRequest) const; + static bool isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus); private: bool callShouldOverrideUrlLoading = true; void createWebView(const HWND parentWindow, const std::function completionHandler); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index d68dc2c1..2e6f0d9f 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -1,14 +1,14 @@ #include "in_app_webview.h" #include "webview_channel_delegate.h" -#include "../utils/util.h" +#include "../utils/flutter.h" #include "../utils/strconv.h" #include "../types/base_callback_result.h" namespace flutter_inappwebview_plugin { WebViewChannelDelegate::WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger) - : webView(webView), ChannelDelegate(messenger, InAppWebView::METHOD_CHANNEL_NAME_PREFIX + variant_to_string(webView->id)) + : webView(webView), ChannelDelegate(messenger, InAppWebView::METHOD_CHANNEL_NAME_PREFIX + variant_to_string(webView->id)) { } @@ -38,43 +38,44 @@ namespace flutter_inappwebview_plugin } if (method_call.method_name().compare("getUrl") == 0) { - result->Success(optional_to_fl_value(webView->getUrl())); - } else if (method_call.method_name().compare("loadUrl") == 0) { + result->Success(make_fl_value(webView->getUrl())); + } + else if (method_call.method_name().compare("loadUrl") == 0) { auto& arguments = std::get(*method_call.arguments()); auto urlRequest = std::make_unique(get_fl_map_value(arguments, "urlRequest")); webView->loadUrl(*urlRequest); - result->Success(flutter::EncodableValue(true)); + result->Success(make_fl_value(true)); } else { result->NotImplemented(); } } - void WebViewChannelDelegate::onLoadStart(const std::optional url) const + void WebViewChannelDelegate::onLoadStart(const std::optional& url) const { if (!channel) { return; } auto arguments = std::make_unique(flutter::EncodableMap { - {flutter::EncodableValue("url"), optional_to_fl_value(url)}, + {flutter::EncodableValue("url"), make_fl_value(url)}, }); channel->InvokeMethod("onLoadStart", std::move(arguments)); } - void WebViewChannelDelegate::onLoadStop(const std::optional url) const + void WebViewChannelDelegate::onLoadStop(const std::optional& url) const { if (!channel) { return; } auto arguments = std::make_unique(flutter::EncodableMap{ - {flutter::EncodableValue("url"), optional_to_fl_value(url)}, + {flutter::EncodableValue("url"), make_fl_value(url)}, }); channel->InvokeMethod("onLoadStop", std::move(arguments)); } - void WebViewChannelDelegate::shouldOverrideUrlLoading(std::shared_ptr navigationAction, std::unique_ptr callback) + void WebViewChannelDelegate::shouldOverrideUrlLoading(std::shared_ptr navigationAction, std::unique_ptr callback) const { if (!channel) { return; @@ -84,6 +85,32 @@ namespace flutter_inappwebview_plugin channel->InvokeMethod("shouldOverrideUrlLoading", std::move(arguments), std::move(callback)); } + void WebViewChannelDelegate::onReceivedError(std::shared_ptr request, std::shared_ptr error) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap{ + {flutter::EncodableValue("request"), request->toEncodableMap()}, + {flutter::EncodableValue("error"), error->toEncodableMap()}, + }); + channel->InvokeMethod("onReceivedError", std::move(arguments)); + } + + void WebViewChannelDelegate::onReceivedHttpError(std::shared_ptr request, std::shared_ptr errorResponse) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap{ + {flutter::EncodableValue("request"), request->toEncodableMap()}, + {flutter::EncodableValue("errorResponse"), errorResponse->toEncodableMap()}, + }); + channel->InvokeMethod("onReceivedHttpError", std::move(arguments)); + } + WebViewChannelDelegate::~WebViewChannelDelegate() { debugLog("dealloc WebViewChannelDelegate"); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h index a5bfe0a4..768c5191 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h @@ -7,6 +7,9 @@ #include "../types/channel_delegate.h" #include "../types/base_callback_result.h" #include "../types/navigation_action.h" +#include "../types/web_resource_request.h" +#include "../types/web_resource_error.h" +#include "../types/web_resource_response.h" namespace flutter_inappwebview_plugin { @@ -33,9 +36,11 @@ namespace flutter_inappwebview_plugin const flutter::MethodCall& method_call, std::unique_ptr> result); - void onLoadStart(const std::optional url) const; - void onLoadStop(const std::optional url) const; - void shouldOverrideUrlLoading(std::shared_ptr navigationAction, std::unique_ptr callback); + void onLoadStart(const std::optional& url) const; + void onLoadStop(const std::optional& url) const; + void shouldOverrideUrlLoading(std::shared_ptr navigationAction, std::unique_ptr callback) const; + void WebViewChannelDelegate::onReceivedError(std::shared_ptr request, std::shared_ptr error) const; + void WebViewChannelDelegate::onReceivedHttpError(std::shared_ptr request, std::shared_ptr error) const; }; } diff --git a/flutter_inappwebview_windows/windows/types/channel_delegate.cpp b/flutter_inappwebview_windows/windows/types/channel_delegate.cpp index b57de4ac..bdec9099 100644 --- a/flutter_inappwebview_windows/windows/types/channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/types/channel_delegate.cpp @@ -3,6 +3,8 @@ #include "channel_delegate.h" +#include "../utils/util.h" + namespace flutter_inappwebview_plugin { ChannelDelegate::ChannelDelegate(flutter::BinaryMessenger* messenger, const std::string& name) : messenger(messenger) diff --git a/flutter_inappwebview_windows/windows/types/channel_delegate.h b/flutter_inappwebview_windows/windows/types/channel_delegate.h index 79c1b5d9..c637e02f 100644 --- a/flutter_inappwebview_windows/windows/types/channel_delegate.h +++ b/flutter_inappwebview_windows/windows/types/channel_delegate.h @@ -3,11 +3,6 @@ #include -#include -#include - -#include "../flutter_inappwebview_windows_base_plugin.h" - namespace flutter_inappwebview_plugin { class ChannelDelegate diff --git a/flutter_inappwebview_windows/windows/types/navigation_action.cpp b/flutter_inappwebview_windows/windows/types/navigation_action.cpp index 9cb94398..9541dcdb 100644 --- a/flutter_inappwebview_windows/windows/types/navigation_action.cpp +++ b/flutter_inappwebview_windows/windows/types/navigation_action.cpp @@ -4,7 +4,7 @@ namespace flutter_inappwebview_plugin { - NavigationAction::NavigationAction(std::shared_ptr request, bool isForMainFrame) + NavigationAction::NavigationAction(std::shared_ptr request, const bool& isForMainFrame) : request(std::move(request)), isForMainFrame(isForMainFrame) { diff --git a/flutter_inappwebview_windows/windows/types/navigation_action.h b/flutter_inappwebview_windows/windows/types/navigation_action.h index 1b48753b..974b4478 100644 --- a/flutter_inappwebview_windows/windows/types/navigation_action.h +++ b/flutter_inappwebview_windows/windows/types/navigation_action.h @@ -13,7 +13,7 @@ namespace flutter_inappwebview_plugin const std::shared_ptr request; const bool isForMainFrame; - NavigationAction(std::shared_ptr request, bool isForMainFrame); + NavigationAction(std::shared_ptr request, const bool& isForMainFrame); ~NavigationAction() = default; flutter::EncodableMap toEncodableMap(); }; diff --git a/flutter_inappwebview_windows/windows/types/url_request.cpp b/flutter_inappwebview_windows/windows/types/url_request.cpp index ddd7317c..01789955 100644 --- a/flutter_inappwebview_windows/windows/types/url_request.cpp +++ b/flutter_inappwebview_windows/windows/types/url_request.cpp @@ -1,19 +1,20 @@ #include "url_request.h" -#include "../utils/util.h" +#include "../utils/flutter.h" namespace flutter_inappwebview_plugin { - URLRequest::URLRequest(std::optional url, std::optional method, std::optional> headers, std::optional> body) + URLRequest::URLRequest(const std::optional& url, const std::optional& method, + const std::optional>& headers, const std::optional>& body) : url(url), method(method), headers(headers), body(body) { } - URLRequest::URLRequest(const flutter::EncodableMap map) + URLRequest::URLRequest(const flutter::EncodableMap& map) : url(get_optional_fl_map_value(map, "url")), method(get_optional_fl_map_value(map, "method")), - headers(get_optional_fl_map_value(map, "headers")), + headers(get_optional_fl_map_value(map, "headers")), body(get_optional_fl_map_value>(map, "body")) { @@ -22,10 +23,10 @@ namespace flutter_inappwebview_plugin flutter::EncodableMap URLRequest::toEncodableMap() { return flutter::EncodableMap{ - {flutter::EncodableValue("url"), optional_to_fl_value(url)}, - {flutter::EncodableValue("method"), optional_to_fl_value(method)}, - {flutter::EncodableValue("headers"), optional_to_fl_value(headers)}, - {flutter::EncodableValue("body"), optional_to_fl_value(body)} + {make_fl_value("url"), make_fl_value(url)}, + {make_fl_value("method"), make_fl_value(method)}, + {make_fl_value("headers"), make_fl_value(headers)}, + {make_fl_value("body"), make_fl_value(body)} }; } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/url_request.h b/flutter_inappwebview_windows/windows/types/url_request.h index d05dac7a..0bebc7ad 100644 --- a/flutter_inappwebview_windows/windows/types/url_request.h +++ b/flutter_inappwebview_windows/windows/types/url_request.h @@ -15,8 +15,9 @@ namespace flutter_inappwebview_plugin const std::optional> headers; const std::optional> body; - URLRequest(std::optional url, std::optional method, std::optional> headers, std::optional> body); - URLRequest(const flutter::EncodableMap map); + URLRequest(const std::optional& url, const std::optional& method, + const std::optional>& headers, const std::optional>& body); + URLRequest(const flutter::EncodableMap& map); ~URLRequest() = default; flutter::EncodableMap toEncodableMap(); }; diff --git a/flutter_inappwebview_windows/windows/types/web_resource_error.cpp b/flutter_inappwebview_windows/windows/types/web_resource_error.cpp new file mode 100644 index 00000000..cdadc378 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/web_resource_error.cpp @@ -0,0 +1,27 @@ +#include "web_resource_error.h" + +#include "../utils/flutter.h" + +namespace flutter_inappwebview_plugin +{ + WebResourceError::WebResourceError(const std::string& description, const int type) + : description(description), type(type) + { + + } + + WebResourceError::WebResourceError(const flutter::EncodableMap& map) + : description(get_fl_map_value(map, "description")), + type(get_fl_map_value(map, "type")) + { + + } + + flutter::EncodableMap WebResourceError::toEncodableMap() + { + return flutter::EncodableMap{ + {make_fl_value("description"), make_fl_value(description)}, + {make_fl_value("type"), make_fl_value(type)} + }; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_error.h b/flutter_inappwebview_windows/windows/types/web_resource_error.h new file mode 100644 index 00000000..42f6d797 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/web_resource_error.h @@ -0,0 +1,46 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_ERROR_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_ERROR_H_ + +#include + +#include + +namespace flutter_inappwebview_plugin +{ + static const std::string WebErrorStatusDescription[] = + { + "Indicates that an unknown error occurred.", + "Indicates that the SSL certificate common name does not match the web address.", + "Indicates that the SSL certificate has expired.", + "Indicates that the SSL client certificate contains errors.", + "Indicates that the SSL certificate has been revoked.", + "Indicates that the SSL certificate is not valid.", + "Indicates that the host is unreachable.", + "Indicates that the connection has timed out.", + "Indicates that the server returned an invalid or unrecognized response.", + "Indicates that the connection was stopped.", + "Indicates that the connection was reset.", + "Indicates that the Internet connection has been lost.", + "Indicates that a connection to the destination was not established.", + "Indicates that the provided host name was not able to be resolved.", + "Indicates that the operation was canceled.", + "Indicates that the request redirect failed.", + "Indicates that an unexpected error occurred.", + "Indicates that user is prompted with a login, waiting on user action.", + "Indicates that user lacks proper authentication credentials for a proxy server.", + }; + + class WebResourceError + { + public: + const std::string description; + const int type; + + WebResourceError(const std::string& description, const int type); + WebResourceError(const flutter::EncodableMap& map); + ~WebResourceError() = default; + flutter::EncodableMap toEncodableMap(); + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_ERROR_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_request.cpp b/flutter_inappwebview_windows/windows/types/web_resource_request.cpp new file mode 100644 index 00000000..c017e199 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/web_resource_request.cpp @@ -0,0 +1,32 @@ +#include "web_resource_request.h" + +#include "../utils/flutter.h" + +namespace flutter_inappwebview_plugin +{ + WebResourceRequest::WebResourceRequest(const std::optional& url, const std::optional& method, + const std::optional>& headers, const std::optional& isForMainFrame) + : url(url), method(method), headers(headers), isForMainFrame(isForMainFrame) + { + + } + + WebResourceRequest::WebResourceRequest(const flutter::EncodableMap& map) + : url(get_optional_fl_map_value(map, "url")), + method(get_optional_fl_map_value(map, "method")), + headers(get_optional_fl_map_value(map, "headers")), + isForMainFrame(get_optional_fl_map_value(map, "isForMainFrame")) + { + + } + + flutter::EncodableMap WebResourceRequest::toEncodableMap() + { + return flutter::EncodableMap{ + {make_fl_value("url"), make_fl_value(url)}, + {make_fl_value("method"), make_fl_value(method)}, + {make_fl_value("headers"), make_fl_value(headers)}, + {make_fl_value("isForMainFrame"), make_fl_value(isForMainFrame)} + }; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_request.h b/flutter_inappwebview_windows/windows/types/web_resource_request.h new file mode 100644 index 00000000..4f8ac07f --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/web_resource_request.h @@ -0,0 +1,26 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_REQUEST_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_REQUEST_H_ + +#include + +#include + +namespace flutter_inappwebview_plugin +{ + class WebResourceRequest + { + public: + const std::optional url; + const std::optional method; + const std::optional> headers; + const std::optional isForMainFrame; + + WebResourceRequest(const std::optional& url, const std::optional& method, + const std::optional>& headers, const std::optional& isForMainFrame); + WebResourceRequest(const flutter::EncodableMap& map); + ~WebResourceRequest() = default; + flutter::EncodableMap toEncodableMap(); + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_REQUEST_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_response.cpp b/flutter_inappwebview_windows/windows/types/web_resource_response.cpp new file mode 100644 index 00000000..5d1d748d --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/web_resource_response.cpp @@ -0,0 +1,25 @@ +#include "web_resource_response.h" + +#include "../utils/flutter.h" + +namespace flutter_inappwebview_plugin +{ + WebResourceResponse::WebResourceResponse(const std::optional& statusCode) + : statusCode(statusCode) + { + + } + + WebResourceResponse::WebResourceResponse(const flutter::EncodableMap& map) + : statusCode(get_optional_fl_map_value(map, "statusCode")) + { + + } + + flutter::EncodableMap WebResourceResponse::toEncodableMap() + { + return flutter::EncodableMap{ + {make_fl_value("statusCode"), make_fl_value(statusCode)} + }; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_response.h b/flutter_inappwebview_windows/windows/types/web_resource_response.h new file mode 100644 index 00000000..caed73b3 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/web_resource_response.h @@ -0,0 +1,22 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_RESPONSE_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_RESPONSE_H_ + +#include + +#include + +namespace flutter_inappwebview_plugin +{ + class WebResourceResponse + { + public: + const std::optional statusCode; + + WebResourceResponse(const std::optional& statusCode); + WebResourceResponse(const flutter::EncodableMap& map); + ~WebResourceResponse() = default; + flutter::EncodableMap toEncodableMap(); + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_RESPONSE_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/flutter.h b/flutter_inappwebview_windows/windows/utils/flutter.h new file mode 100644 index 00000000..e08ac630 --- /dev/null +++ b/flutter_inappwebview_windows/windows/utils/flutter.h @@ -0,0 +1,77 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_FLUTTER_UTIL_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_FLUTTER_UTIL_H_ + +#include +#include +#include + +#include "util.h" + +namespace flutter_inappwebview_plugin +{ + static inline flutter::EncodableValue make_fl_value() + { + return flutter::EncodableValue(); + } + + template + static inline flutter::EncodableValue make_fl_value(const T& val) + { + return flutter::EncodableValue(val); + } + + template + static inline flutter::EncodableValue make_fl_value(const T* val) + { + return val == nullptr ? make_fl_value() : flutter::EncodableValue(val); + } + + template + static inline flutter::EncodableValue make_fl_value(const std::optional& optional) + { + return optional.has_value() ? make_fl_value(optional.value()) : make_fl_value(); + } + + static inline flutter::EncodableValue make_fl_value(const std::optional>& optional) + { + if (!optional.has_value()) { + return make_fl_value(); + } + auto& mapValue = optional.value(); + auto encodableMap = flutter::EncodableMap{}; + for (auto const& [key, val] : mapValue) + { + encodableMap.insert({ make_fl_value(key), make_fl_value(val) }); + } + return encodableMap; + } + + template + static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* string) + { + return std::get(map.at(make_fl_value(string))); + } + + template + static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) + { + return make_pointer_optional(std::get_if(&map.at(make_fl_value(string)))); + } + + template + static inline std::optional> get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) + { + auto flMap = std::get_if(&map.at(make_fl_value(string))); + if (flMap) { + auto mapValue = std::map{}; + for (auto itr = flMap->begin(); itr != flMap->end(); itr++) + { + mapValue.insert({ std::get(itr->first), std::get(itr->second) }); + } + return make_pointer_optional>(&mapValue); + } + return std::nullopt; + } +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_FLUTTER_UTIL_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/util.h b/flutter_inappwebview_windows/windows/utils/util.h index 843401f3..34562bce 100644 --- a/flutter_inappwebview_windows/windows/utils/util.h +++ b/flutter_inappwebview_windows/windows/utils/util.h @@ -22,52 +22,7 @@ namespace flutter_inappwebview_plugin return value == nullptr ? std::nullopt : std::make_optional(*value); } - template - static inline T get_fl_map_value(const flutter::EncodableMap map, const char* string) - { - return std::get(map.at(flutter::EncodableValue(string))); - } - - template - static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap map, const char* string) - { - return make_pointer_optional(std::get_if(&map.at(flutter::EncodableValue(string)))); - } - - static inline std::optional> get_optional_fl_map_value(const flutter::EncodableMap map, const char* string) - { - auto mapValue = std::map{}; - auto flMap = std::get_if(&map.at(flutter::EncodableValue(string))); - if (flMap) { - for (auto itr = flMap->begin(); itr != flMap->end(); itr++) - { - mapValue.insert({ std::get(itr->first), std::get(itr->second) }); - } - } - return make_pointer_optional>(&mapValue); - } - - template - static inline flutter::EncodableValue optional_to_fl_value(const std::optional optional) - { - return optional.has_value() ? flutter::EncodableValue(optional.value()) : flutter::EncodableValue(); - } - - static inline flutter::EncodableValue optional_to_fl_value(const std::optional> optional) - { - if (!optional.has_value()) { - return flutter::EncodableValue(); - } - auto& mapValue = optional.value(); - auto encodableMap = flutter::EncodableMap{}; - for (auto const& [key, val] : mapValue) - { - encodableMap.insert({ flutter::EncodableValue(key), flutter::EncodableValue(val) }); - } - return encodableMap; - } - - static inline std::string variant_to_string(std::variant var) + static inline std::string variant_to_string(const std::variant& var) { return std::visit([](auto&& arg) { using T = std::decay_t; @@ -79,6 +34,19 @@ namespace flutter_inappwebview_plugin static_assert(always_false_v, "non-exhaustive visitor!"); }, var); } + + template + static inline bool map_contains(const std::map& map, const K& key) + { + return map.find(key) != map.end(); + } + + template + static inline T map_at_or_null(const std::map& map, const K& key) + { + auto itr = map.find(key); + return itr != map.end() ? itr->second : nullptr; + } } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ \ No newline at end of file From 8499949a223c19fb51f4e22737ea0010f5b990bd Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 Jan 2024 12:36:48 +0100 Subject: [PATCH 04/21] windows c++ code format --- .../lib/in_app_browser_example.screen.dart | 1 - .../flutter_inappwebview_windows_plugin.cpp | 29 +- .../flutter_inappwebview_windows_plugin.h | 24 +- ...tter_inappwebview_windows_plugin_c_api.cpp | 9 +- .../windows/in_app_browser/in_app_browser.cpp | 373 ++++---- .../windows/in_app_browser/in_app_browser.h | 66 +- .../in_app_browser_channel_delegate.cpp | 53 +- .../in_app_browser_channel_delegate.h | 22 +- .../in_app_browser/in_app_browser_manager.cpp | 74 +- .../in_app_browser/in_app_browser_manager.h | 34 +- .../windows/in_app_webview/in_app_webview.cpp | 446 ++++----- .../windows/in_app_webview/in_app_webview.h | 54 +- .../webview_channel_delegate.cpp | 215 +++-- .../in_app_webview/webview_channel_delegate.h | 54 +- ...lutter_inappwebview_windows_plugin_c_api.h | 2 +- .../windows/types/base_callback_result.h | 86 +- .../windows/types/channel_delegate.cpp | 52 +- .../windows/types/channel_delegate.h | 24 +- .../windows/types/navigation_action.cpp | 25 +- .../windows/types/navigation_action.h | 20 +- .../windows/types/url_request.cpp | 45 +- .../windows/types/url_request.h | 30 +- .../windows/types/web_resource_error.cpp | 35 +- .../windows/types/web_resource_error.h | 66 +- .../windows/types/web_resource_request.cpp | 45 +- .../windows/types/web_resource_request.h | 28 +- .../windows/types/web_resource_response.cpp | 31 +- .../windows/types/web_resource_response.h | 20 +- .../windows/utils/flutter.h | 141 +-- .../windows/utils/strconv.h | 892 +++++++++--------- .../windows/utils/util.h | 75 +- 31 files changed, 1538 insertions(+), 1533 deletions(-) diff --git a/flutter_inappwebview/example/lib/in_app_browser_example.screen.dart b/flutter_inappwebview/example/lib/in_app_browser_example.screen.dart index 6d307cb8..15f2bd7b 100755 --- a/flutter_inappwebview/example/lib/in_app_browser_example.screen.dart +++ b/flutter_inappwebview/example/lib/in_app_browser_example.screen.dart @@ -30,7 +30,6 @@ class MyInAppBrowser extends InAppBrowser { @override Future onLoadStop(url) async { pullToRefreshController?.endRefreshing(); - print(await webViewController?.getUrl()); } @override diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp index 5e4070cb..74eb2287 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp @@ -7,21 +7,20 @@ namespace flutter_inappwebview_plugin { - // static - void FlutterInappwebviewWindowsPlugin::RegisterWithRegistrar( - flutter::PluginRegistrarWindows* registrar) { - auto plugin = std::make_unique(registrar); - registrar->AddPlugin(std::move(plugin)); - } + // static + void FlutterInappwebviewWindowsPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) + { + auto plugin = std::make_unique(registrar); + registrar->AddPlugin(std::move(plugin)); + } - FlutterInappwebviewWindowsPlugin::FlutterInappwebviewWindowsPlugin(flutter::PluginRegistrarWindows* registrar) - : registrar(registrar) - { - inAppBrowserManager = std::make_unique(this); - } + FlutterInappwebviewWindowsPlugin::FlutterInappwebviewWindowsPlugin(flutter::PluginRegistrarWindows* registrar) + : registrar(registrar) + { + inAppBrowserManager = std::make_unique(this); + } - FlutterInappwebviewWindowsPlugin::~FlutterInappwebviewWindowsPlugin() - { - - } + FlutterInappwebviewWindowsPlugin::~FlutterInappwebviewWindowsPlugin() + {} } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h index 534ecabb..ffa0a576 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h @@ -6,22 +6,22 @@ namespace flutter_inappwebview_plugin { - class InAppBrowserManager; + class InAppBrowserManager; - class FlutterInappwebviewWindowsPlugin : public flutter::Plugin { - public: - flutter::PluginRegistrarWindows* registrar; - std::unique_ptr inAppBrowserManager; + class FlutterInappwebviewWindowsPlugin : public flutter::Plugin { + public: + flutter::PluginRegistrarWindows* registrar; + std::unique_ptr inAppBrowserManager; - static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); - FlutterInappwebviewWindowsPlugin(flutter::PluginRegistrarWindows* registrar); + FlutterInappwebviewWindowsPlugin(flutter::PluginRegistrarWindows* registrar); - virtual ~FlutterInappwebviewWindowsPlugin(); + virtual ~FlutterInappwebviewWindowsPlugin(); - // Disallow copy and assign. - FlutterInappwebviewWindowsPlugin(const FlutterInappwebviewWindowsPlugin&) = delete; - FlutterInappwebviewWindowsPlugin& operator=(const FlutterInappwebviewWindowsPlugin&) = delete; - }; + // Disallow copy and assign. + FlutterInappwebviewWindowsPlugin(const FlutterInappwebviewWindowsPlugin&) = delete; + FlutterInappwebviewWindowsPlugin& operator=(const FlutterInappwebviewWindowsPlugin&) = delete; + }; } #endif // FLUTTER_PLUGIN_FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_H_ diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin_c_api.cpp b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin_c_api.cpp index f49d1a29..9fc6f673 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin_c_api.cpp +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin_c_api.cpp @@ -5,8 +5,9 @@ #include "flutter_inappwebview_windows_plugin.h" void FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( - FlutterDesktopPluginRegistrarRef registrar) { - flutter_inappwebview_plugin::FlutterInappwebviewWindowsPlugin::RegisterWithRegistrar( - flutter::PluginRegistrarManager::GetInstance() - ->GetRegistrar(registrar)); + FlutterDesktopPluginRegistrarRef registrar) +{ + flutter_inappwebview_plugin::FlutterInappwebviewWindowsPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); } diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index 0a158848..afa81412 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -1,221 +1,226 @@ #include -#include "in_app_browser.h" + #include "../utils/util.h" +#include "in_app_browser.h" #include "in_app_browser_manager.h" namespace flutter_inappwebview_plugin { - InAppBrowser::InAppBrowser(FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params) - : plugin(plugin), - m_hInstance(GetModuleHandle(nullptr)), - id(params.id), - initialUrlRequest(params.urlRequest), - channelDelegate(std::make_unique(id, plugin->registrar->messenger())) - { - WNDCLASS wndClass = {}; - wndClass.lpszClassName = InAppBrowser::CLASS_NAME; - wndClass.hInstance = m_hInstance; - wndClass.hIcon = LoadIcon(NULL, IDI_WINLOGO); - wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); - wndClass.lpfnWndProc = InAppBrowser::WndProc; + InAppBrowser::InAppBrowser(FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params) + : plugin(plugin), + m_hInstance(GetModuleHandle(nullptr)), + id(params.id), + initialUrlRequest(params.urlRequest), + channelDelegate(std::make_unique(id, plugin->registrar->messenger())) + { + WNDCLASS wndClass = {}; + wndClass.lpszClassName = InAppBrowser::CLASS_NAME; + wndClass.hInstance = m_hInstance; + wndClass.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.lpfnWndProc = InAppBrowser::WndProc; - RegisterClass(&wndClass); + RegisterClass(&wndClass); - m_hWnd = CreateWindowEx( - 0, // Optional window styles. - InAppBrowser::CLASS_NAME, // Window class - L"", // Window text - WS_OVERLAPPEDWINDOW, // Window style + m_hWnd = CreateWindowEx( + 0, // Optional window styles. + InAppBrowser::CLASS_NAME, // Window class + L"", // Window text + WS_OVERLAPPEDWINDOW, // Window style - // Size and position - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + // Size and position + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - NULL, // Parent window - NULL, // Menu - m_hInstance,// Instance handle - this // Additional application data - ); + NULL, // Parent window + NULL, // Menu + m_hInstance,// Instance handle + this // Additional application data + ); - ShowWindow(m_hWnd, SW_SHOW); + ShowWindow(m_hWnd, SW_SHOW); - webView = std::make_unique(plugin, id, m_hWnd, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id, [this]() -> void { - if (channelDelegate) { - channelDelegate->onBrowserCreated(); - } + webView = std::make_unique(plugin, id, m_hWnd, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id, [this]() -> void + { + if (channelDelegate) { + channelDelegate->onBrowserCreated(); + } - if (initialUrlRequest.has_value()) { - webView->loadUrl(initialUrlRequest.value()); - } - }); + if (initialUrlRequest.has_value()) { + webView->loadUrl(initialUrlRequest.value()); + } + }); - // <-- WebView2 sample code starts here --> - // Step 3 - Create a single WebView within the parent window - // Locate the browser and set up the environment for WebView - //CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, - // Callback( - // [hWnd = m_hWnd, inAppBrowser = this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT { + // <-- WebView2 sample code starts here --> + // Step 3 - Create a single WebView within the parent window + // Locate the browser and set up the environment for WebView + //CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, + // Callback( + // [hWnd = m_hWnd, inAppBrowser = this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT { - // // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd - // env->CreateCoreWebView2Controller(hWnd, Callback( - // [hWnd, inAppBrowser](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT { - // if (controller != nullptr) { - // inAppBrowser->webviewController = controller; - // inAppBrowser->webviewController->get_CoreWebView2(&inAppBrowser->webview); - // } + // // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd + // env->CreateCoreWebView2Controller(hWnd, Callback( + // [hWnd, inAppBrowser](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT { + // if (controller != nullptr) { + // inAppBrowser->webviewController = controller; + // inAppBrowser->webviewController->get_CoreWebView2(&inAppBrowser->webview); + // } - // // Add a few settings for the webview - // // The demo step is redundant since the values are the default settings - // wil::com_ptr settings; - // inAppBrowser->webview->get_Settings(&settings); - // settings->put_IsScriptEnabled(TRUE); - // settings->put_AreDefaultScriptDialogsEnabled(TRUE); - // settings->put_IsWebMessageEnabled(TRUE); + // // Add a few settings for the webview + // // The demo step is redundant since the values are the default settings + // wil::com_ptr settings; + // inAppBrowser->webview->get_Settings(&settings); + // settings->put_IsScriptEnabled(TRUE); + // settings->put_AreDefaultScriptDialogsEnabled(TRUE); + // settings->put_IsWebMessageEnabled(TRUE); - // // Resize WebView to fit the bounds of the parent window - // RECT bounds; - // GetClientRect(hWnd, &bounds); - // inAppBrowser->webviewController->put_Bounds(bounds); + // // Resize WebView to fit the bounds of the parent window + // RECT bounds; + // GetClientRect(hWnd, &bounds); + // inAppBrowser->webviewController->put_Bounds(bounds); - // auto url = inAppBrowser->initialUrlRequest.value().url.value(); - // std::wstring stemp = ansi_to_wide(url); + // auto url = inAppBrowser->initialUrlRequest.value().url.value(); + // std::wstring stemp = ansi_to_wide(url); - // // Schedule an async task to navigate to Bing - // inAppBrowser->webview->Navigate(stemp.c_str()); + // // Schedule an async task to navigate to Bing + // inAppBrowser->webview->Navigate(stemp.c_str()); - // // - // // Step 4 - Navigation events - // // register an ICoreWebView2NavigationStartingEventHandler to cancel any non-https navigation - // EventRegistrationToken token; - // inAppBrowser->webview->add_NavigationStarting(Callback( - // [](ICoreWebView2* webview, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT { - // wil::unique_cotaskmem_string uri; - // args->get_Uri(&uri); - // std::wstring source(uri.get()); - // if (source.substr(0, 5) != L"https") { - // args->put_Cancel(true); - // } - // return S_OK; - // }).Get(), &token); - // // + // // + // // Step 4 - Navigation events + // // register an ICoreWebView2NavigationStartingEventHandler to cancel any non-https navigation + // EventRegistrationToken token; + // inAppBrowser->webview->add_NavigationStarting(Callback( + // [](ICoreWebView2* webview, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT { + // wil::unique_cotaskmem_string uri; + // args->get_Uri(&uri); + // std::wstring source(uri.get()); + // if (source.substr(0, 5) != L"https") { + // args->put_Cancel(true); + // } + // return S_OK; + // }).Get(), &token); + // // - // // - // // Step 5 - Scripting - // // Schedule an async task to add initialization script that freezes the Object object - // inAppBrowser->webview->AddScriptToExecuteOnDocumentCreated(L"Object.freeze(Object);", nullptr); - // // Schedule an async task to get the document URL - // inAppBrowser->webview->ExecuteScript(L"window.document.URL;", Callback( - // [](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT { - // LPCWSTR URL = resultObjectAsJson; - // OutputDebugStringW(URL); - // //doSomethingWithURL(URL); - // return S_OK; - // }).Get()); - // // + // // + // // Step 5 - Scripting + // // Schedule an async task to add initialization script that freezes the Object object + // inAppBrowser->webview->AddScriptToExecuteOnDocumentCreated(L"Object.freeze(Object);", nullptr); + // // Schedule an async task to get the document URL + // inAppBrowser->webview->ExecuteScript(L"window.document.URL;", Callback( + // [](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT { + // LPCWSTR URL = resultObjectAsJson; + // OutputDebugStringW(URL); + // //doSomethingWithURL(URL); + // return S_OK; + // }).Get()); + // // - // // - // // Step 6 - Communication between host and web content - // // Set an event handler for the host to return received message back to the web content - // inAppBrowser->webview->add_WebMessageReceived(Callback( - // [](ICoreWebView2* webview, ICoreWebView2WebMessageReceivedEventArgs* args) -> HRESULT { - // wil::unique_cotaskmem_string message; - // args->TryGetWebMessageAsString(&message); - // // processMessage(&message); - // webview->PostWebMessageAsString(message.get()); - // return S_OK; - // }).Get(), &token); + // // + // // Step 6 - Communication between host and web content + // // Set an event handler for the host to return received message back to the web content + // inAppBrowser->webview->add_WebMessageReceived(Callback( + // [](ICoreWebView2* webview, ICoreWebView2WebMessageReceivedEventArgs* args) -> HRESULT { + // wil::unique_cotaskmem_string message; + // args->TryGetWebMessageAsString(&message); + // // processMessage(&message); + // webview->PostWebMessageAsString(message.get()); + // return S_OK; + // }).Get(), &token); - // // Schedule an async task to add initialization script that - // // 1) Add an listener to print message from the host - // // 2) Post document URL to the host - // inAppBrowser->webview->AddScriptToExecuteOnDocumentCreated( - // L"window.chrome.webview.addEventListener(\'message\', event => alert(event.data));" \ + // // Schedule an async task to add initialization script that + // // 1) Add an listener to print message from the host + // // 2) Post document URL to the host + // inAppBrowser->webview->AddScriptToExecuteOnDocumentCreated( + // L"window.chrome.webview.addEventListener(\'message\', event => alert(event.data));" \ // L"window.chrome.webview.postMessage(window.document.URL);", - // nullptr); - // // + // nullptr); + // // - // return S_OK; - // }).Get()); - // return S_OK; - // }).Get()); - } + // return S_OK; + // }).Get()); + // return S_OK; + // }).Get()); + } - LRESULT CALLBACK InAppBrowser::WndProc( - HWND window, - UINT message, - WPARAM wparam, - LPARAM lparam - ) noexcept { - if (message == WM_NCCREATE) { - auto window_struct = reinterpret_cast(lparam); - SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast(window_struct->lpCreateParams)); - } - else if (InAppBrowser* that = GetThisFromHandle(window)) { - return that->MessageHandler(window, message, wparam, lparam); - } + LRESULT CALLBACK InAppBrowser::WndProc( + HWND window, + UINT message, + WPARAM wparam, + LPARAM lparam + ) noexcept + { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast(window_struct->lpCreateParams)); + } + else if (InAppBrowser* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } - return DefWindowProc(window, message, wparam, lparam); - } + return DefWindowProc(window, message, wparam, lparam); + } - LRESULT InAppBrowser::MessageHandler( - HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam - ) noexcept { - switch (message) { - case WM_DESTROY: { - // might receive multiple WM_DESTROY messages. - if (!destroyed_) { - destroyed_ = true; + LRESULT InAppBrowser::MessageHandler( + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam + ) noexcept + { + switch (message) { + case WM_DESTROY: { + // might receive multiple WM_DESTROY messages. + if (!destroyed_) { + destroyed_ = true; - webView.reset(); + webView.reset(); - if (channelDelegate) { - channelDelegate->onExit(); - } + if (channelDelegate) { + channelDelegate->onExit(); + } - if (plugin && plugin->inAppBrowserManager) { - plugin->inAppBrowserManager->browsers.erase(id); - } - } - return 0; - } - case WM_DPICHANGED: { - auto newRectSize = reinterpret_cast(lparam); - LONG newWidth = newRectSize->right - newRectSize->left; - LONG newHeight = newRectSize->bottom - newRectSize->top; + if (plugin && plugin->inAppBrowserManager) { + plugin->inAppBrowserManager->browsers.erase(id); + } + } + return 0; + } + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; - SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, - newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - return 0; - } - case WM_SIZE: { - RECT bounds; - GetClientRect(hwnd, &bounds); - if (webView) { - webView->webViewController->put_Bounds(bounds); - } - return 0; - } - case WM_ACTIVATE: { - return 0; - } - } + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + return 0; + } + case WM_SIZE: { + RECT bounds; + GetClientRect(hwnd, &bounds); + if (webView) { + webView->webViewController->put_Bounds(bounds); + } + return 0; + } + case WM_ACTIVATE: { + return 0; + } + } - return DefWindowProc(hwnd, message, wparam, lparam); - } + return DefWindowProc(hwnd, message, wparam, lparam); + } - InAppBrowser* InAppBrowser::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( - GetWindowLongPtr(window, GWLP_USERDATA)); - } + InAppBrowser* InAppBrowser::GetThisFromHandle(HWND const window) noexcept + { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); + } - InAppBrowser::~InAppBrowser() - { - debugLog("dealloc InAppBrowser"); - webView.reset(); - SetWindowLongPtr(m_hWnd, GWLP_USERDATA, 0); - plugin = nullptr; - } + InAppBrowser::~InAppBrowser() + { + debugLog("dealloc InAppBrowser"); + webView.reset(); + SetWindowLongPtr(m_hWnd, GWLP_USERDATA, 0); + plugin = nullptr; + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h index a3adb62d..900fecde 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h @@ -1,11 +1,11 @@ #ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_H_ #define FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_H_ -#include -#include #include - +#include #include +#include + #include "../flutter_inappwebview_windows_plugin.h" #include "../in_app_webview/in_app_webview.h" #include "../types/url_request.h" @@ -13,40 +13,40 @@ namespace flutter_inappwebview_plugin { - struct InAppBrowserCreationParams - { - std::string id; - std::optional urlRequest; - }; + struct InAppBrowserCreationParams + { + std::string id; + std::optional urlRequest; + }; - class InAppBrowser { - public: - static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappbrowser_"; - static inline const wchar_t* CLASS_NAME = L"InAppBrowser"; + class InAppBrowser { + public: + static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappbrowser_"; + static inline const wchar_t* CLASS_NAME = L"InAppBrowser"; - static LRESULT CALLBACK WndProc(HWND window, - UINT message, - WPARAM wparam, - LPARAM lparam) noexcept; + static LRESULT CALLBACK WndProc(HWND window, + UINT message, + WPARAM wparam, + LPARAM lparam) noexcept; - FlutterInappwebviewWindowsPlugin* plugin; - std::string id; - std::optional initialUrlRequest; - std::unique_ptr webView; - std::unique_ptr channelDelegate; + FlutterInappwebviewWindowsPlugin* plugin; + std::string id; + std::optional initialUrlRequest; + std::unique_ptr webView; + std::unique_ptr channelDelegate; - InAppBrowser(FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params); - ~InAppBrowser(); + InAppBrowser(FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params); + ~InAppBrowser(); - private: - HINSTANCE m_hInstance; - HWND m_hWnd; - bool destroyed_ = false; - static InAppBrowser* GetThisFromHandle(HWND window) noexcept; - LRESULT MessageHandler(HWND window, - UINT message, - WPARAM wparam, - LPARAM lparam) noexcept; - }; + private: + HINSTANCE m_hInstance; + HWND m_hWnd; + bool destroyed_ = false; + static InAppBrowser* GetThisFromHandle(HWND window) noexcept; + LRESULT MessageHandler(HWND window, + UINT message, + WPARAM wparam, + LPARAM lparam) noexcept; + }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp index 9a253806..d3bc5961 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp @@ -1,40 +1,37 @@ +#include "../utils/util.h" #include "in_app_browser.h" #include "in_app_browser_channel_delegate.h" -#include "../utils/util.h" - namespace flutter_inappwebview_plugin { - InAppBrowserChannelDelegate::InAppBrowserChannelDelegate(const std::string& id, flutter::BinaryMessenger* messenger) - : ChannelDelegate(messenger, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id) - { + InAppBrowserChannelDelegate::InAppBrowserChannelDelegate(const std::string& id, flutter::BinaryMessenger* messenger) + : ChannelDelegate(messenger, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id) + {} - } + void InAppBrowserChannelDelegate::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + result->NotImplemented(); + } - void InAppBrowserChannelDelegate::HandleMethodCall(const flutter::MethodCall& method_call, - std::unique_ptr> result) - { - result->NotImplemented(); + void InAppBrowserChannelDelegate::onBrowserCreated() const + { + if (!channel) { + return; } + channel->InvokeMethod("onBrowserCreated", nullptr); + } - void InAppBrowserChannelDelegate::onBrowserCreated() const - { - if (!channel) { - return; - } - channel->InvokeMethod("onBrowserCreated", nullptr); + void InAppBrowserChannelDelegate::onExit() const + { + if (!channel) { + return; } + channel->InvokeMethod("onExit", nullptr); + } - void InAppBrowserChannelDelegate::onExit() const - { - if (!channel) { - return; - } - channel->InvokeMethod("onExit", nullptr); - } - - InAppBrowserChannelDelegate::~InAppBrowserChannelDelegate() - { - debugLog("dealloc InAppBrowserChannelDelegate"); - } + InAppBrowserChannelDelegate::~InAppBrowserChannelDelegate() + { + debugLog("dealloc InAppBrowserChannelDelegate"); + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.h index 488f69f1..d0032cff 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.h @@ -8,19 +8,19 @@ namespace flutter_inappwebview_plugin { - class InAppBrowserChannelDelegate : public ChannelDelegate - { - public: - InAppBrowserChannelDelegate(const std::string& id, flutter::BinaryMessenger* messenger); - ~InAppBrowserChannelDelegate(); + class InAppBrowserChannelDelegate : public ChannelDelegate + { + public: + InAppBrowserChannelDelegate(const std::string& id, flutter::BinaryMessenger* messenger); + ~InAppBrowserChannelDelegate(); - void HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result); + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); - void onBrowserCreated() const; - void onExit() const; - }; + void onBrowserCreated() const; + void onExit() const; + }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_CHANNEL_DELEGATE_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp index a421aaee..1e08d7ba 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -1,53 +1,47 @@ -#include #include #include -#include - -#include "in_app_browser_manager.h" #include "../types/url_request.h" - #include "../utils/flutter.h" +#include "in_app_browser_manager.h" namespace flutter_inappwebview_plugin { - InAppBrowserManager::InAppBrowserManager(FlutterInappwebviewWindowsPlugin* plugin) - : plugin(plugin), ChannelDelegate(plugin->registrar->messenger(), InAppBrowserManager::METHOD_CHANNEL_NAME) - { + InAppBrowserManager::InAppBrowserManager(FlutterInappwebviewWindowsPlugin* plugin) + : plugin(plugin), ChannelDelegate(plugin->registrar->messenger(), InAppBrowserManager::METHOD_CHANNEL_NAME) + {} - } + void InAppBrowserManager::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + if (method_call.method_name().compare("open") == 0) { + auto* arguments = std::get_if(method_call.arguments()); + open(arguments); + result->Success(flutter::EncodableValue(true)); + } + else { + result->NotImplemented(); + } + } - void InAppBrowserManager::HandleMethodCall(const flutter::MethodCall& method_call, - std::unique_ptr> result) - { - if (method_call.method_name().compare("open") == 0) { - auto* arguments = std::get_if(method_call.arguments()); - open(arguments); - result->Success(flutter::EncodableValue(true)); - } - else { - result->NotImplemented(); - } - } + void InAppBrowserManager::open(const flutter::EncodableMap* arguments) + { + auto id = get_fl_map_value(*arguments, "id"); + auto urlRequestMap = get_optional_fl_map_value(*arguments, "urlRequest"); + std::optional urlRequest = urlRequestMap.has_value() ? std::make_optional(urlRequestMap.value()) : std::optional{}; - void InAppBrowserManager::open(const flutter::EncodableMap* arguments) - { - auto id = get_fl_map_value(*arguments, "id"); - auto urlRequestMap = get_optional_fl_map_value(*arguments, "urlRequest"); - std::optional urlRequest = urlRequestMap.has_value() ? std::make_optional(urlRequestMap.value()) : std::optional{}; + InAppBrowserCreationParams params = { + id, + urlRequest + }; + auto inAppBrowser = std::make_unique(plugin, params); + browsers.insert({ id, std::move(inAppBrowser) }); + } - InAppBrowserCreationParams params = { - id, - urlRequest - }; - auto inAppBrowser = std::make_unique(plugin, params); - browsers.insert({ id, std::move(inAppBrowser) }); - } - - InAppBrowserManager::~InAppBrowserManager() - { - debugLog("dealloc InAppBrowserManager"); - browsers.clear(); - plugin = nullptr; - } + InAppBrowserManager::~InAppBrowserManager() + { + debugLog("dealloc InAppBrowserManager"); + browsers.clear(); + plugin = nullptr; + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h index b0093c51..91f813e2 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h @@ -1,35 +1,33 @@ #ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_MANAGER_H_ #define FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_MANAGER_H_ -#include -#include #include #include +#include +#include #include "../flutter_inappwebview_windows_plugin.h" - -#include "in_app_browser.h" - #include "../types/channel_delegate.h" +#include "in_app_browser.h" namespace flutter_inappwebview_plugin { - class InAppBrowserManager : public ChannelDelegate - { - public: - static inline const std::string METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappbrowser"; + class InAppBrowserManager : public ChannelDelegate + { + public: + static inline const std::string METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappbrowser"; - FlutterInappwebviewWindowsPlugin* plugin; - std::map> browsers; + FlutterInappwebviewWindowsPlugin* plugin; + std::map> browsers; - InAppBrowserManager(FlutterInappwebviewWindowsPlugin* plugin); - ~InAppBrowserManager(); + InAppBrowserManager(FlutterInappwebviewWindowsPlugin* plugin); + ~InAppBrowserManager(); - void HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result); + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); - void open(const flutter::EncodableMap* arguments); - }; + void open(const flutter::EncodableMap* arguments); + }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_MANAGER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index 51187943..2902c704 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -1,258 +1,266 @@ #pragma comment(lib, "Shlwapi.lib") -#include "in_app_webview.h" +#include #include #include -#include + +#include "../types/web_resource_error.h" +#include "../types/web_resource_request.h" #include "../utils/strconv.h" #include "../utils/util.h" -#include "../types/web_resource_request.h" -#include "../types/web_resource_error.h" +#include "in_app_webview.h" namespace flutter_inappwebview_plugin { - using namespace Microsoft::WRL; + using namespace Microsoft::WRL; - InAppWebView::InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::function completionHandler) - : plugin(plugin), id(id), channelDelegate(std::make_unique(this, plugin->registrar->messenger())) - { - createWebView(parentWindow, completionHandler); - } + InAppWebView::InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::function completionHandler) + : plugin(plugin), id(id), channelDelegate(std::make_unique(this, plugin->registrar->messenger())) + { + createWebView(parentWindow, completionHandler); + } - InAppWebView::InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::string& channelName, const std::function completionHandler) - : plugin(plugin), id(id), channelDelegate(std::make_unique(this, plugin->registrar->messenger(), channelName)) - { - createWebView(parentWindow, completionHandler); - } + InAppWebView::InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::string& channelName, const std::function completionHandler) + : plugin(plugin), id(id), channelDelegate(std::make_unique(this, plugin->registrar->messenger(), channelName)) + { + createWebView(parentWindow, completionHandler); + } - void InAppWebView::createWebView(const HWND parentWindow, const std::function completionHandler) - { - CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, - Callback( - [parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT { - webViewEnv = env; - // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window HWND - env->CreateCoreWebView2Controller(parentWindow, Callback( - [parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT { - if (controller != nullptr) { - webViewController = controller; - webViewController->get_CoreWebView2(webView.put()); - } + void InAppWebView::createWebView(const HWND parentWindow, const std::function completionHandler) + { + CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, + Callback( + [parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT + { + webViewEnv = env; + // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window HWND + env->CreateCoreWebView2Controller(parentWindow, Callback( + [parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT + { + if (controller != nullptr) { + webViewController = controller; + webViewController->get_CoreWebView2(webView.put()); + } - // Resize WebView to fit the bounds of the parent window - RECT bounds; - GetClientRect(parentWindow, &bounds); - webViewController->put_Bounds(bounds); + // Resize WebView to fit the bounds of the parent window + RECT bounds; + GetClientRect(parentWindow, &bounds); + webViewController->put_Bounds(bounds); - registerEventHandlers(); + registerEventHandlers(); - completionHandler(); + completionHandler(); - return S_OK; - }).Get()); - return S_OK; - }).Get()); - } + return S_OK; + }).Get()); + return S_OK; + }).Get()); + } - void InAppWebView::registerEventHandlers() - { - if (!webView) { - return; - } + void InAppWebView::registerEventHandlers() + { + if (!webView) { + return; + } - webView->add_NavigationStarting( - Callback( - [this](ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args) { - if (!channelDelegate) { - args->put_Cancel(false); - return S_OK; - } - - wil::unique_cotaskmem_string uri = nullptr; - std::optional url = SUCCEEDED(args->get_Uri(&uri)) ? wide_to_utf8(uri.get()) : std::optional{}; + webView->add_NavigationStarting( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args) + { + if (!channelDelegate) { + args->put_Cancel(false); + return S_OK; + } - wil::unique_cotaskmem_string requestMethod = nullptr; - wil::com_ptr requestHeaders = nullptr; - std::optional> headers = std::optional>{}; - if (SUCCEEDED(args->get_RequestHeaders(&requestHeaders))) { - headers = std::make_optional>({}); - wil::com_ptr iterator; - requestHeaders->GetIterator(&iterator); - BOOL hasCurrent = FALSE; - while (SUCCEEDED(iterator->get_HasCurrentHeader(&hasCurrent)) && hasCurrent) - { - wil::unique_cotaskmem_string name; - wil::unique_cotaskmem_string value; + wil::unique_cotaskmem_string uri = nullptr; + std::optional url = SUCCEEDED(args->get_Uri(&uri)) ? wide_to_utf8(uri.get()) : std::optional{}; - if (SUCCEEDED(iterator->GetCurrentHeader(&name, &value))) { - headers->insert({ wide_to_utf8(name.get()), wide_to_utf8(value.get()) }); - } + wil::unique_cotaskmem_string requestMethod = nullptr; + wil::com_ptr requestHeaders = nullptr; + std::optional> headers = std::optional>{}; + if (SUCCEEDED(args->get_RequestHeaders(&requestHeaders))) { + headers = std::make_optional>({}); + wil::com_ptr iterator; + requestHeaders->GetIterator(&iterator); + BOOL hasCurrent = FALSE; + while (SUCCEEDED(iterator->get_HasCurrentHeader(&hasCurrent)) && hasCurrent) { + wil::unique_cotaskmem_string name; + wil::unique_cotaskmem_string value; - BOOL hasNext = FALSE; - iterator->MoveNext(&hasNext); - } + if (SUCCEEDED(iterator->GetCurrentHeader(&name, &value))) { + headers->insert({ wide_to_utf8(name.get()), wide_to_utf8(value.get()) }); + } - requestHeaders->GetHeader(L"Flutter-InAppWebView-Request-Method", &requestMethod); - requestHeaders->RemoveHeader(L"Flutter-InAppWebView-Request-Method"); - } + BOOL hasNext = FALSE; + iterator->MoveNext(&hasNext); + } - std::optional method = requestMethod ? wide_to_utf8(requestMethod.get()) : std::optional{}; + requestHeaders->GetHeader(L"Flutter-InAppWebView-Request-Method", &requestMethod); + requestHeaders->RemoveHeader(L"Flutter-InAppWebView-Request-Method"); + } - auto urlRequest = std::make_shared(url, method, headers, std::nullopt); - auto navigationAction = std::make_shared( - urlRequest, - true - ); + std::optional method = requestMethod ? wide_to_utf8(requestMethod.get()) : std::optional{}; - UINT64 navigationId; - if (SUCCEEDED(args->get_NavigationId(&navigationId))) { - navigationActions.insert({navigationId, navigationAction}); - } + auto urlRequest = std::make_shared(url, method, headers, std::nullopt); + auto navigationAction = std::make_shared( + urlRequest, + true + ); - if (callShouldOverrideUrlLoading && requestMethod == nullptr) { - // for some reason, we can't cancel and load an URL with other HTTP methods than GET, - // so ignore the shouldOverrideUrlLoading event. - - auto callback = std::make_unique(); - callback->nonNullSuccess = [this, urlRequest](const NavigationActionPolicy actionPolicy) { - callShouldOverrideUrlLoading = false; - if (actionPolicy == allow) { - loadUrl(*urlRequest); - } - return false; - }; - auto defaultBehaviour = [this, urlRequest](const std::optional actionPolicy) { - callShouldOverrideUrlLoading = false; - loadUrl(*urlRequest); - }; - callback->defaultBehaviour = defaultBehaviour; - callback->error = [defaultBehaviour](const std::string& error_code, const std::string& error_message, const flutter::EncodableValue* error_details) { - debugLog(error_code + ", " + error_message); - defaultBehaviour(std::nullopt); - }; - channelDelegate->shouldOverrideUrlLoading(std::move(navigationAction), std::move(callback)); - args->put_Cancel(true); - } - else { - callShouldOverrideUrlLoading = true; - channelDelegate->onLoadStart(url); - args->put_Cancel(false); - } + UINT64 navigationId; + if (SUCCEEDED(args->get_NavigationId(&navigationId))) { + navigationActions.insert({ navigationId, navigationAction }); + } - return S_OK; - } - ).Get(), nullptr); + if (callShouldOverrideUrlLoading && requestMethod == nullptr) { + // for some reason, we can't cancel and load an URL with other HTTP methods than GET, + // so ignore the shouldOverrideUrlLoading event. - webView->add_NavigationCompleted( - Callback( - [this](ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args) { - std::shared_ptr navigationAction; - UINT64 navigationId; - if (SUCCEEDED(args->get_NavigationId(&navigationId))) { - navigationAction = map_at_or_null(navigationActions, navigationId); - if (navigationAction) { - navigationActions.erase(navigationId); - } - } + auto callback = std::make_unique(); + callback->nonNullSuccess = [this, urlRequest](const NavigationActionPolicy actionPolicy) + { + callShouldOverrideUrlLoading = false; + if (actionPolicy == allow) { + loadUrl(*urlRequest); + } + return false; + }; + auto defaultBehaviour = [this, urlRequest](const std::optional actionPolicy) + { + callShouldOverrideUrlLoading = false; + loadUrl(*urlRequest); + }; + callback->defaultBehaviour = defaultBehaviour; + callback->error = [defaultBehaviour](const std::string& error_code, const std::string& error_message, const flutter::EncodableValue* error_details) + { + debugLog(error_code + ", " + error_message); + defaultBehaviour(std::nullopt); + }; + channelDelegate->shouldOverrideUrlLoading(std::move(navigationAction), std::move(callback)); + args->put_Cancel(true); + } + else { + callShouldOverrideUrlLoading = true; + channelDelegate->onLoadStart(url); + args->put_Cancel(false); + } - COREWEBVIEW2_WEB_ERROR_STATUS webErrorType = COREWEBVIEW2_WEB_ERROR_STATUS_UNKNOWN; - args->get_WebErrorStatus(&webErrorType); + return S_OK; + } + ).Get(), nullptr); - BOOL isSuccess; - args->get_IsSuccess(&isSuccess); + webView->add_NavigationCompleted( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args) + { + std::shared_ptr navigationAction; + UINT64 navigationId; + if (SUCCEEDED(args->get_NavigationId(&navigationId))) { + navigationAction = map_at_or_null(navigationActions, navigationId); + if (navigationAction) { + navigationActions.erase(navigationId); + } + } - if (channelDelegate) { - LPWSTR uri = nullptr; - std::optional url = SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(std::wstring(uri)) : std::optional{}; - if (isSuccess) { - channelDelegate->onLoadStop(url); - } - else if (!InAppWebView::isSslError(webErrorType) && navigationAction) { - auto webResourceRequest = std::make_unique(url, navigationAction->request->method, navigationAction->request->headers, navigationAction->isForMainFrame); - int httpStatusCode = 0; - wil::com_ptr args2; - if (SUCCEEDED(args->QueryInterface(IID_PPV_ARGS(&args2))) && SUCCEEDED(args2->get_HttpStatusCode(&httpStatusCode)) && httpStatusCode >= 400) { - auto webResourceResponse = std::make_unique(httpStatusCode); - channelDelegate->onReceivedHttpError(std::move(webResourceRequest), std::move(webResourceResponse)); - } - else if (httpStatusCode < 400) { - auto webResourceError = std::make_unique(WebErrorStatusDescription[webErrorType], webErrorType); - channelDelegate->onReceivedError(std::move(webResourceRequest), std::move(webResourceError)); - } - } - } + COREWEBVIEW2_WEB_ERROR_STATUS webErrorType = COREWEBVIEW2_WEB_ERROR_STATUS_UNKNOWN; + args->get_WebErrorStatus(&webErrorType); - return S_OK; - } - ).Get(), nullptr); - } + BOOL isSuccess; + args->get_IsSuccess(&isSuccess); - std::optional InAppWebView::getUrl() const - { - LPWSTR uri = nullptr; - return SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(uri) : std::optional{}; - } + if (channelDelegate) { + LPWSTR uri = nullptr; + std::optional url = SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(std::wstring(uri)) : std::optional{}; + if (isSuccess) { + channelDelegate->onLoadStop(url); + } + else if (!InAppWebView::isSslError(webErrorType) && navigationAction) { + auto webResourceRequest = std::make_unique(url, navigationAction->request->method, navigationAction->request->headers, navigationAction->isForMainFrame); + int httpStatusCode = 0; + wil::com_ptr args2; + if (SUCCEEDED(args->QueryInterface(IID_PPV_ARGS(&args2))) && SUCCEEDED(args2->get_HttpStatusCode(&httpStatusCode)) && httpStatusCode >= 400) { + auto webResourceResponse = std::make_unique(httpStatusCode); + channelDelegate->onReceivedHttpError(std::move(webResourceRequest), std::move(webResourceResponse)); + } + else if (httpStatusCode < 400) { + auto webResourceError = std::make_unique(WebErrorStatusDescription[webErrorType], webErrorType); + channelDelegate->onReceivedError(std::move(webResourceRequest), std::move(webResourceError)); + } + } + } - void InAppWebView::loadUrl(const URLRequest& urlRequest) const - { - if (!webView || !urlRequest.url.has_value()) { - return; - } + return S_OK; + } + ).Get(), nullptr); + } - std::wstring url = ansi_to_wide(urlRequest.url.value()); + std::optional InAppWebView::getUrl() const + { + LPWSTR uri = nullptr; + return SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(uri) : std::optional{}; + } - wil::com_ptr webViewEnv2; - wil::com_ptr webView2; - if (SUCCEEDED(webViewEnv->QueryInterface(IID_PPV_ARGS(&webViewEnv2))) && SUCCEEDED(webView->QueryInterface(IID_PPV_ARGS(&webView2)))) { - wil::com_ptr webResourceRequest; - std::wstring method = urlRequest.method.has_value() ? ansi_to_wide(urlRequest.method.value()) : L"GET"; - - wil::com_ptr postDataStream = nullptr; - if (urlRequest.body.has_value()) { - auto postData = std::string(urlRequest.body->begin(), urlRequest.body->end()); - postDataStream = SHCreateMemStream( - reinterpret_cast(postData.data()), static_cast(postData.length())); - } - webViewEnv2->CreateWebResourceRequest( - url.c_str(), - method.c_str(), - postDataStream.get(), - L"", - &webResourceRequest - ); - wil::com_ptr requestHeaders; - if (SUCCEEDED(webResourceRequest->get_Headers(&requestHeaders))) { - if (method.compare(L"GET") != 0) { - requestHeaders->SetHeader(L"Flutter-InAppWebView-Request-Method", method.c_str()); - } - if (urlRequest.headers.has_value()) { - auto& headers = urlRequest.headers.value(); - for (auto const& [key, val] : headers) { - requestHeaders->SetHeader(ansi_to_wide(key).c_str(), ansi_to_wide(val).c_str()); - } - } - } - webView2->NavigateWithWebResourceRequest(webResourceRequest.get()); - } - else { - webView->Navigate(url.c_str()); - } - } + void InAppWebView::loadUrl(const URLRequest& urlRequest) const + { + if (!webView || !urlRequest.url.has_value()) { + return; + } - bool InAppWebView::isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus) { - return webErrorStatus >= COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_COMMON_NAME_IS_INCORRECT && webErrorStatus <= COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID; - } + std::wstring url = ansi_to_wide(urlRequest.url.value()); - InAppWebView::~InAppWebView() - { - debugLog("dealloc InAppWebView"); - if (webView) { - webView->Stop(); - } - if (webViewController) { - webViewController->Close(); - } - navigationActions.clear(); - plugin = nullptr; - } + wil::com_ptr webViewEnv2; + wil::com_ptr webView2; + if (SUCCEEDED(webViewEnv->QueryInterface(IID_PPV_ARGS(&webViewEnv2))) && SUCCEEDED(webView->QueryInterface(IID_PPV_ARGS(&webView2)))) { + wil::com_ptr webResourceRequest; + std::wstring method = urlRequest.method.has_value() ? ansi_to_wide(urlRequest.method.value()) : L"GET"; + + wil::com_ptr postDataStream = nullptr; + if (urlRequest.body.has_value()) { + auto postData = std::string(urlRequest.body->begin(), urlRequest.body->end()); + postDataStream = SHCreateMemStream( + reinterpret_cast(postData.data()), static_cast(postData.length())); + } + webViewEnv2->CreateWebResourceRequest( + url.c_str(), + method.c_str(), + postDataStream.get(), + L"", + &webResourceRequest + ); + wil::com_ptr requestHeaders; + if (SUCCEEDED(webResourceRequest->get_Headers(&requestHeaders))) { + if (method.compare(L"GET") != 0) { + requestHeaders->SetHeader(L"Flutter-InAppWebView-Request-Method", method.c_str()); + } + if (urlRequest.headers.has_value()) { + auto& headers = urlRequest.headers.value(); + for (auto const& [key, val] : headers) { + requestHeaders->SetHeader(ansi_to_wide(key).c_str(), ansi_to_wide(val).c_str()); + } + } + } + webView2->NavigateWithWebResourceRequest(webResourceRequest.get()); + } + else { + webView->Navigate(url.c_str()); + } + } + + bool InAppWebView::isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus) + { + return webErrorStatus >= COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_COMMON_NAME_IS_INCORRECT && webErrorStatus <= COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID; + } + + InAppWebView::~InAppWebView() + { + debugLog("dealloc InAppWebView"); + if (webView) { + webView->Stop(); + } + if (webViewController) { + webViewController->Close(); + } + navigationActions.clear(); + plugin = nullptr; + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index 145b2512..2043a469 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -2,43 +2,43 @@ #define FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_H_ #include - #include #include -#include "../types/url_request.h" -#include "../types/navigation_action.h" -#include "webview_channel_delegate.h" + #include "../flutter_inappwebview_windows_plugin.h" +#include "../types/navigation_action.h" +#include "../types/url_request.h" +#include "webview_channel_delegate.h" namespace flutter_inappwebview_plugin { - using namespace Microsoft::WRL; + using namespace Microsoft::WRL; - class InAppWebView - { - public: - static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_"; + class InAppWebView + { + public: + static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_"; - const FlutterInappwebviewWindowsPlugin* plugin; - const std::variant id; - wil::com_ptr webViewEnv; - wil::com_ptr webViewController; - wil::com_ptr webView; - const std::unique_ptr channelDelegate; - std::map> navigationActions = {}; + const FlutterInappwebviewWindowsPlugin* plugin; + const std::variant id; + wil::com_ptr webViewEnv; + wil::com_ptr webViewController; + wil::com_ptr webView; + const std::unique_ptr channelDelegate; + std::map> navigationActions = {}; - InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::function completionHandler); - InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::string& channelName, const std::function completionHandler); - ~InAppWebView(); + InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::function completionHandler); + InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::string& channelName, const std::function completionHandler); + ~InAppWebView(); - std::optional getUrl() const; - void loadUrl(const URLRequest& urlRequest) const; + std::optional getUrl() const; + void loadUrl(const URLRequest& urlRequest) const; - static bool isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus); - private: - bool callShouldOverrideUrlLoading = true; - void createWebView(const HWND parentWindow, const std::function completionHandler); - void InAppWebView::registerEventHandlers(); - }; + static bool isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus); + private: + bool callShouldOverrideUrlLoading = true; + void createWebView(const HWND parentWindow, const std::function completionHandler); + void InAppWebView::registerEventHandlers(); + }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index 2e6f0d9f..cb6d4f24 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -1,119 +1,116 @@ +#include "../types/base_callback_result.h" +#include "../utils/flutter.h" +#include "../utils/strconv.h" #include "in_app_webview.h" #include "webview_channel_delegate.h" -#include "../utils/flutter.h" -#include "../utils/strconv.h" -#include "../types/base_callback_result.h" - namespace flutter_inappwebview_plugin { - WebViewChannelDelegate::WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger) - : webView(webView), ChannelDelegate(messenger, InAppWebView::METHOD_CHANNEL_NAME_PREFIX + variant_to_string(webView->id)) - { + WebViewChannelDelegate::WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger) + : webView(webView), ChannelDelegate(messenger, InAppWebView::METHOD_CHANNEL_NAME_PREFIX + variant_to_string(webView->id)) + {} + WebViewChannelDelegate::WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger, const std::string& name) + : webView(webView), ChannelDelegate(messenger, name) + {} + + WebViewChannelDelegate::ShouldOverrideUrlLoadingCallback::ShouldOverrideUrlLoadingCallback() + { + decodeResult = [](const flutter::EncodableValue* value) + { + if (value->IsNull()) { + return cancel; + } + auto navigationPolicy = std::get(*value); + return static_cast(navigationPolicy); + }; + } + + void WebViewChannelDelegate::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + if (!webView) { + result->Success(); + return; } - WebViewChannelDelegate::WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger, const std::string& name) - : webView(webView), ChannelDelegate(messenger, name) - { - - } - - WebViewChannelDelegate::ShouldOverrideUrlLoadingCallback::ShouldOverrideUrlLoadingCallback() { - decodeResult = [](const flutter::EncodableValue* value) { - if (value->IsNull()) { - return cancel; - } - auto navigationPolicy = std::get(*value); - return static_cast(navigationPolicy); - }; - } - - void WebViewChannelDelegate::HandleMethodCall(const flutter::MethodCall& method_call, - std::unique_ptr> result) - { - if (!webView) { - result->Success(); - return; - } - - if (method_call.method_name().compare("getUrl") == 0) { - result->Success(make_fl_value(webView->getUrl())); - } - else if (method_call.method_name().compare("loadUrl") == 0) { - auto& arguments = std::get(*method_call.arguments()); - auto urlRequest = std::make_unique(get_fl_map_value(arguments, "urlRequest")); - webView->loadUrl(*urlRequest); - result->Success(make_fl_value(true)); - } - else { - result->NotImplemented(); - } - } - - void WebViewChannelDelegate::onLoadStart(const std::optional& url) const - { - if (!channel) { - return; - } - - auto arguments = std::make_unique(flutter::EncodableMap { - {flutter::EncodableValue("url"), make_fl_value(url)}, - }); - channel->InvokeMethod("onLoadStart", std::move(arguments)); - } - - void WebViewChannelDelegate::onLoadStop(const std::optional& url) const - { - if (!channel) { - return; - } - - auto arguments = std::make_unique(flutter::EncodableMap{ - {flutter::EncodableValue("url"), make_fl_value(url)}, - }); - channel->InvokeMethod("onLoadStop", std::move(arguments)); - } - - void WebViewChannelDelegate::shouldOverrideUrlLoading(std::shared_ptr navigationAction, std::unique_ptr callback) const - { - if (!channel) { - return; - } - - auto arguments = std::make_unique(navigationAction->toEncodableMap()); - channel->InvokeMethod("shouldOverrideUrlLoading", std::move(arguments), std::move(callback)); - } - - void WebViewChannelDelegate::onReceivedError(std::shared_ptr request, std::shared_ptr error) const - { - if (!channel) { - return; - } - - auto arguments = std::make_unique(flutter::EncodableMap{ - {flutter::EncodableValue("request"), request->toEncodableMap()}, - {flutter::EncodableValue("error"), error->toEncodableMap()}, - }); - channel->InvokeMethod("onReceivedError", std::move(arguments)); - } - - void WebViewChannelDelegate::onReceivedHttpError(std::shared_ptr request, std::shared_ptr errorResponse) const - { - if (!channel) { - return; - } - - auto arguments = std::make_unique(flutter::EncodableMap{ - {flutter::EncodableValue("request"), request->toEncodableMap()}, - {flutter::EncodableValue("errorResponse"), errorResponse->toEncodableMap()}, - }); - channel->InvokeMethod("onReceivedHttpError", std::move(arguments)); - } - - WebViewChannelDelegate::~WebViewChannelDelegate() - { - debugLog("dealloc WebViewChannelDelegate"); - webView = nullptr; + if (method_call.method_name().compare("getUrl") == 0) { + result->Success(make_fl_value(webView->getUrl())); } + else if (method_call.method_name().compare("loadUrl") == 0) { + auto& arguments = std::get(*method_call.arguments()); + auto urlRequest = std::make_unique(get_fl_map_value(arguments, "urlRequest")); + webView->loadUrl(*urlRequest); + result->Success(make_fl_value(true)); + } + else { + result->NotImplemented(); + } + } + + void WebViewChannelDelegate::onLoadStart(const std::optional& url) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap{ + {flutter::EncodableValue("url"), make_fl_value(url)}, + }); + channel->InvokeMethod("onLoadStart", std::move(arguments)); + } + + void WebViewChannelDelegate::onLoadStop(const std::optional& url) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap{ + {flutter::EncodableValue("url"), make_fl_value(url)}, + }); + channel->InvokeMethod("onLoadStop", std::move(arguments)); + } + + void WebViewChannelDelegate::shouldOverrideUrlLoading(std::shared_ptr navigationAction, std::unique_ptr callback) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(navigationAction->toEncodableMap()); + channel->InvokeMethod("shouldOverrideUrlLoading", std::move(arguments), std::move(callback)); + } + + void WebViewChannelDelegate::onReceivedError(std::shared_ptr request, std::shared_ptr error) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap{ + {flutter::EncodableValue("request"), request->toEncodableMap()}, + {flutter::EncodableValue("error"), error->toEncodableMap()}, + }); + channel->InvokeMethod("onReceivedError", std::move(arguments)); + } + + void WebViewChannelDelegate::onReceivedHttpError(std::shared_ptr request, std::shared_ptr errorResponse) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap{ + {flutter::EncodableValue("request"), request->toEncodableMap()}, + {flutter::EncodableValue("errorResponse"), errorResponse->toEncodableMap()}, + }); + channel->InvokeMethod("onReceivedHttpError", std::move(arguments)); + } + + WebViewChannelDelegate::~WebViewChannelDelegate() + { + debugLog("dealloc WebViewChannelDelegate"); + webView = nullptr; + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h index 768c5191..7f950d66 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h @@ -4,44 +4,44 @@ #include #include -#include "../types/channel_delegate.h" #include "../types/base_callback_result.h" +#include "../types/channel_delegate.h" #include "../types/navigation_action.h" -#include "../types/web_resource_request.h" #include "../types/web_resource_error.h" +#include "../types/web_resource_request.h" #include "../types/web_resource_response.h" namespace flutter_inappwebview_plugin { - class InAppWebView; + class InAppWebView; - enum NavigationActionPolicy {cancel = 0, allow = 1}; + enum NavigationActionPolicy { cancel = 0, allow = 1 }; - class WebViewChannelDelegate : public ChannelDelegate - { + class WebViewChannelDelegate : public ChannelDelegate + { + public: + InAppWebView* webView; + + class ShouldOverrideUrlLoadingCallback : public BaseCallbackResult { public: - InAppWebView* webView; - - class ShouldOverrideUrlLoadingCallback : public BaseCallbackResult { - public: - ShouldOverrideUrlLoadingCallback(); - ~ShouldOverrideUrlLoadingCallback() = default; - }; - - WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger); - WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger, const std::string& name); - ~WebViewChannelDelegate(); - - void HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result); - - void onLoadStart(const std::optional& url) const; - void onLoadStop(const std::optional& url) const; - void shouldOverrideUrlLoading(std::shared_ptr navigationAction, std::unique_ptr callback) const; - void WebViewChannelDelegate::onReceivedError(std::shared_ptr request, std::shared_ptr error) const; - void WebViewChannelDelegate::onReceivedHttpError(std::shared_ptr request, std::shared_ptr error) const; + ShouldOverrideUrlLoadingCallback(); + ~ShouldOverrideUrlLoadingCallback() = default; }; + + WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger); + WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger, const std::string& name); + ~WebViewChannelDelegate(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + void onLoadStart(const std::optional& url) const; + void onLoadStop(const std::optional& url) const; + void shouldOverrideUrlLoading(std::shared_ptr navigationAction, std::unique_ptr callback) const; + void WebViewChannelDelegate::onReceivedError(std::shared_ptr request, std::shared_ptr error) const; + void WebViewChannelDelegate::onReceivedHttpError(std::shared_ptr request, std::shared_ptr error) const; + }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_CHANNEL_DELEGATE_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/include/flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h b/flutter_inappwebview_windows/windows/include/flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h index 019d5283..6a318ec0 100644 --- a/flutter_inappwebview_windows/windows/include/flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h +++ b/flutter_inappwebview_windows/windows/include/flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h @@ -13,7 +13,7 @@ extern "C" { #endif -FLUTTER_PLUGIN_EXPORT void FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( + FLUTTER_PLUGIN_EXPORT void FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( FlutterDesktopPluginRegistrarRef registrar); #if defined(__cplusplus) diff --git a/flutter_inappwebview_windows/windows/types/base_callback_result.h b/flutter_inappwebview_windows/windows/types/base_callback_result.h index 66958b5a..d156e1e6 100644 --- a/flutter_inappwebview_windows/windows/types/base_callback_result.h +++ b/flutter_inappwebview_windows/windows/types/base_callback_result.h @@ -1,53 +1,57 @@ #ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_BASE_CALLBACK_RESULT_H_ #define FLUTTER_INAPPWEBVIEW_PLUGIN_BASE_CALLBACK_RESULT_H_ -#include #include +#include #include namespace flutter_inappwebview_plugin { - template - class BaseCallbackResult : public flutter::MethodResultFunctions - { - public: - flutter::ResultHandlerError error; - flutter::ResultHandlerNotImplemented notImplemented; - std::function nonNullSuccess = [](const T result) { return true; }; - std::function nullSuccess = []() { return true; }; - std::function result)> defaultBehaviour = [](const std::optional result) {}; - std::function(const flutter::EncodableValue* result)> decodeResult = [](const flutter::EncodableValue* result) { return std::nullopt; }; + template + class BaseCallbackResult : public flutter::MethodResultFunctions + { + public: + flutter::ResultHandlerError error; + flutter::ResultHandlerNotImplemented notImplemented; + std::function nonNullSuccess = [](const T result) { return true; }; + std::function nullSuccess = []() { return true; }; + std::function result)> defaultBehaviour = [](const std::optional result) {}; + std::function(const flutter::EncodableValue* result)> decodeResult = [](const flutter::EncodableValue* result) { return std::nullopt; }; - BaseCallbackResult() : - MethodResultFunctions( - [this](const flutter::EncodableValue* val) { - std::optional result = decodeResult ? decodeResult(val) : std::nullopt; - auto shouldRunDefaultBehaviour = false; - if (result.has_value()) { - shouldRunDefaultBehaviour = nonNullSuccess ? nonNullSuccess(result.value()) : shouldRunDefaultBehaviour; - } - else { - shouldRunDefaultBehaviour = nullSuccess ? nullSuccess() : shouldRunDefaultBehaviour; - } - if (shouldRunDefaultBehaviour && defaultBehaviour) { - defaultBehaviour(result); - } - }, - [this](const std::string& error_code, const std::string& error_message, const flutter::EncodableValue* error_details) { - if (error) { - error(error_code, error_message, error_details); - } - }, - [this]() { - if (defaultBehaviour) { - defaultBehaviour(std::nullopt); - } - if (notImplemented) { - notImplemented(); - } - }) {}; - virtual ~BaseCallbackResult() {}; - }; + BaseCallbackResult() : + MethodResultFunctions( + [this](const flutter::EncodableValue* val) + { + std::optional result = decodeResult ? decodeResult(val) : std::nullopt; + auto shouldRunDefaultBehaviour = false; + if (result.has_value()) { + shouldRunDefaultBehaviour = nonNullSuccess ? nonNullSuccess(result.value()) : shouldRunDefaultBehaviour; + } + else { + shouldRunDefaultBehaviour = nullSuccess ? nullSuccess() : shouldRunDefaultBehaviour; + } + if (shouldRunDefaultBehaviour && defaultBehaviour) { + defaultBehaviour(result); + } + }, + [this](const std::string& error_code, const std::string& error_message, const flutter::EncodableValue* error_details) + { + if (error) { + error(error_code, error_message, error_details); + } + }, + [this]() + { + if (defaultBehaviour) { + defaultBehaviour(std::nullopt); + } + if (notImplemented) { + notImplemented(); + } + }) + {}; + virtual ~BaseCallbackResult() {}; + }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_BASE_CALLBACK_RESULT_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/channel_delegate.cpp b/flutter_inappwebview_windows/windows/types/channel_delegate.cpp index bdec9099..983a2366 100644 --- a/flutter_inappwebview_windows/windows/types/channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/types/channel_delegate.cpp @@ -1,37 +1,35 @@ #include #include -#include "channel_delegate.h" - #include "../utils/util.h" +#include "channel_delegate.h" namespace flutter_inappwebview_plugin { - ChannelDelegate::ChannelDelegate(flutter::BinaryMessenger* messenger, const std::string& name) : messenger(messenger) - { - channel = std::make_shared>( - this->messenger, name, - &flutter::StandardMethodCodec::GetInstance() - ); - channel->SetMethodCallHandler( - [this](const auto& call, auto result) { - this->HandleMethodCall(call, std::move(result)); - }); - } + ChannelDelegate::ChannelDelegate(flutter::BinaryMessenger* messenger, const std::string& name) : messenger(messenger) + { + channel = std::make_shared>( + this->messenger, name, + &flutter::StandardMethodCodec::GetInstance() + ); + channel->SetMethodCallHandler( + [this](const auto& call, auto result) + { + this->HandleMethodCall(call, std::move(result)); + }); + } - void ChannelDelegate::HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result) - { + void ChannelDelegate::HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result) + {} - } - - ChannelDelegate::~ChannelDelegate() - { - messenger = nullptr; - if (channel != nullptr) { - channel->SetMethodCallHandler(nullptr); - } - channel.reset(); - } + ChannelDelegate::~ChannelDelegate() + { + messenger = nullptr; + if (channel != nullptr) { + channel->SetMethodCallHandler(nullptr); + } + channel.reset(); + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/channel_delegate.h b/flutter_inappwebview_windows/windows/types/channel_delegate.h index c637e02f..a412b7d1 100644 --- a/flutter_inappwebview_windows/windows/types/channel_delegate.h +++ b/flutter_inappwebview_windows/windows/types/channel_delegate.h @@ -5,21 +5,21 @@ namespace flutter_inappwebview_plugin { - class ChannelDelegate - { - using FlutterMethodChannel = std::shared_ptr>; + class ChannelDelegate + { + using FlutterMethodChannel = std::shared_ptr>; - public: - FlutterMethodChannel channel; - flutter::BinaryMessenger* messenger; + public: + FlutterMethodChannel channel; + flutter::BinaryMessenger* messenger; - ChannelDelegate(flutter::BinaryMessenger* messenger, const std::string& name); - virtual ~ChannelDelegate(); + ChannelDelegate(flutter::BinaryMessenger* messenger, const std::string& name); + virtual ~ChannelDelegate(); - virtual void HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result); - }; + virtual void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_CHANNEL_DELEGATE_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/navigation_action.cpp b/flutter_inappwebview_windows/windows/types/navigation_action.cpp index 9541dcdb..680d0483 100644 --- a/flutter_inappwebview_windows/windows/types/navigation_action.cpp +++ b/flutter_inappwebview_windows/windows/types/navigation_action.cpp @@ -1,20 +1,17 @@ +#include "../utils/flutter.h" #include "navigation_action.h" -#include "../utils/util.h" - namespace flutter_inappwebview_plugin { - NavigationAction::NavigationAction(std::shared_ptr request, const bool& isForMainFrame) - : request(std::move(request)), isForMainFrame(isForMainFrame) - { + NavigationAction::NavigationAction(std::shared_ptr request, const bool& isForMainFrame) + : request(std::move(request)), isForMainFrame(isForMainFrame) + {} - } - - flutter::EncodableMap NavigationAction::toEncodableMap() - { - return flutter::EncodableMap{ - {flutter::EncodableValue("request"), request->toEncodableMap()}, - {flutter::EncodableValue("isForMainFrame"), flutter::EncodableValue(isForMainFrame)} - }; - } + flutter::EncodableMap NavigationAction::toEncodableMap() const + { + return flutter::EncodableMap{ + {make_fl_value("request"), request->toEncodableMap()}, + {make_fl_value("isForMainFrame"), make_fl_value(isForMainFrame)} + }; + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/navigation_action.h b/flutter_inappwebview_windows/windows/types/navigation_action.h index 974b4478..ae100e63 100644 --- a/flutter_inappwebview_windows/windows/types/navigation_action.h +++ b/flutter_inappwebview_windows/windows/types/navigation_action.h @@ -3,20 +3,22 @@ #include #include + #include "url_request.h" namespace flutter_inappwebview_plugin { - class NavigationAction - { - public: - const std::shared_ptr request; - const bool isForMainFrame; + class NavigationAction + { + public: + const std::shared_ptr request; + const bool isForMainFrame; - NavigationAction(std::shared_ptr request, const bool& isForMainFrame); - ~NavigationAction() = default; - flutter::EncodableMap toEncodableMap(); - }; + NavigationAction(std::shared_ptr request, const bool& isForMainFrame); + ~NavigationAction() = default; + + flutter::EncodableMap toEncodableMap() const; + }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_NAVIGATION_ACTION_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/url_request.cpp b/flutter_inappwebview_windows/windows/types/url_request.cpp index 01789955..ec997b24 100644 --- a/flutter_inappwebview_windows/windows/types/url_request.cpp +++ b/flutter_inappwebview_windows/windows/types/url_request.cpp @@ -1,32 +1,27 @@ -#include "url_request.h" - #include "../utils/flutter.h" +#include "url_request.h" namespace flutter_inappwebview_plugin { - URLRequest::URLRequest(const std::optional& url, const std::optional& method, - const std::optional>& headers, const std::optional>& body) - : url(url), method(method), headers(headers), body(body) - { + URLRequest::URLRequest(const std::optional& url, const std::optional& method, + const std::optional>& headers, const std::optional>& body) + : url(url), method(method), headers(headers), body(body) + {} - } + URLRequest::URLRequest(const flutter::EncodableMap& map) + : url(get_optional_fl_map_value(map, "url")), + method(get_optional_fl_map_value(map, "method")), + headers(get_optional_fl_map_value(map, "headers")), + body(get_optional_fl_map_value>(map, "body")) + {} - URLRequest::URLRequest(const flutter::EncodableMap& map) - : url(get_optional_fl_map_value(map, "url")), - method(get_optional_fl_map_value(map, "method")), - headers(get_optional_fl_map_value(map, "headers")), - body(get_optional_fl_map_value>(map, "body")) - { - - } - - flutter::EncodableMap URLRequest::toEncodableMap() - { - return flutter::EncodableMap{ - {make_fl_value("url"), make_fl_value(url)}, - {make_fl_value("method"), make_fl_value(method)}, - {make_fl_value("headers"), make_fl_value(headers)}, - {make_fl_value("body"), make_fl_value(body)} - }; - } + flutter::EncodableMap URLRequest::toEncodableMap() const + { + return flutter::EncodableMap{ + {make_fl_value("url"), make_fl_value(url)}, + {make_fl_value("method"), make_fl_value(method)}, + {make_fl_value("headers"), make_fl_value(headers)}, + {make_fl_value("body"), make_fl_value(body)} + }; + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/url_request.h b/flutter_inappwebview_windows/windows/types/url_request.h index 0bebc7ad..37373d1c 100644 --- a/flutter_inappwebview_windows/windows/types/url_request.h +++ b/flutter_inappwebview_windows/windows/types/url_request.h @@ -2,25 +2,27 @@ #define FLUTTER_INAPPWEBVIEW_PLUGIN_URL_REQUEST_H_ #include - #include +#include "../utils/flutter.h" + namespace flutter_inappwebview_plugin { - class URLRequest - { - public: - const std::optional url; - const std::optional method; - const std::optional> headers; - const std::optional> body; + class URLRequest + { + public: + const std::optional url; + const std::optional method; + const std::optional> headers; + const std::optional> body; - URLRequest(const std::optional& url, const std::optional& method, - const std::optional>& headers, const std::optional>& body); - URLRequest(const flutter::EncodableMap& map); - ~URLRequest() = default; - flutter::EncodableMap toEncodableMap(); - }; + URLRequest(const std::optional& url, const std::optional& method, + const std::optional>& headers, const std::optional>& body); + URLRequest(const flutter::EncodableMap& map); + ~URLRequest() = default; + + flutter::EncodableMap toEncodableMap() const; + }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_URL_REQUEST_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_error.cpp b/flutter_inappwebview_windows/windows/types/web_resource_error.cpp index cdadc378..107fd682 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_error.cpp +++ b/flutter_inappwebview_windows/windows/types/web_resource_error.cpp @@ -1,27 +1,22 @@ -#include "web_resource_error.h" - #include "../utils/flutter.h" +#include "web_resource_error.h" namespace flutter_inappwebview_plugin { - WebResourceError::WebResourceError(const std::string& description, const int type) - : description(description), type(type) - { + WebResourceError::WebResourceError(const std::string& description, const int type) + : description(description), type(type) + {} - } + WebResourceError::WebResourceError(const flutter::EncodableMap& map) + : description(get_fl_map_value(map, "description")), + type(get_fl_map_value(map, "type")) + {} - WebResourceError::WebResourceError(const flutter::EncodableMap& map) - : description(get_fl_map_value(map, "description")), - type(get_fl_map_value(map, "type")) - { - - } - - flutter::EncodableMap WebResourceError::toEncodableMap() - { - return flutter::EncodableMap{ - {make_fl_value("description"), make_fl_value(description)}, - {make_fl_value("type"), make_fl_value(type)} - }; - } + flutter::EncodableMap WebResourceError::toEncodableMap() const + { + return flutter::EncodableMap{ + {make_fl_value("description"), make_fl_value(description)}, + {make_fl_value("type"), make_fl_value(type)} + }; + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_error.h b/flutter_inappwebview_windows/windows/types/web_resource_error.h index 42f6d797..b1d2a55b 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_error.h +++ b/flutter_inappwebview_windows/windows/types/web_resource_error.h @@ -2,45 +2,45 @@ #define FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_ERROR_H_ #include - #include namespace flutter_inappwebview_plugin { - static const std::string WebErrorStatusDescription[] = - { - "Indicates that an unknown error occurred.", - "Indicates that the SSL certificate common name does not match the web address.", - "Indicates that the SSL certificate has expired.", - "Indicates that the SSL client certificate contains errors.", - "Indicates that the SSL certificate has been revoked.", - "Indicates that the SSL certificate is not valid.", - "Indicates that the host is unreachable.", - "Indicates that the connection has timed out.", - "Indicates that the server returned an invalid or unrecognized response.", - "Indicates that the connection was stopped.", - "Indicates that the connection was reset.", - "Indicates that the Internet connection has been lost.", - "Indicates that a connection to the destination was not established.", - "Indicates that the provided host name was not able to be resolved.", - "Indicates that the operation was canceled.", - "Indicates that the request redirect failed.", - "Indicates that an unexpected error occurred.", - "Indicates that user is prompted with a login, waiting on user action.", - "Indicates that user lacks proper authentication credentials for a proxy server.", - }; + static const std::string WebErrorStatusDescription[] = + { + "Indicates that an unknown error occurred.", + "Indicates that the SSL certificate common name does not match the web address.", + "Indicates that the SSL certificate has expired.", + "Indicates that the SSL client certificate contains errors.", + "Indicates that the SSL certificate has been revoked.", + "Indicates that the SSL certificate is not valid.", + "Indicates that the host is unreachable.", + "Indicates that the connection has timed out.", + "Indicates that the server returned an invalid or unrecognized response.", + "Indicates that the connection was stopped.", + "Indicates that the connection was reset.", + "Indicates that the Internet connection has been lost.", + "Indicates that a connection to the destination was not established.", + "Indicates that the provided host name was not able to be resolved.", + "Indicates that the operation was canceled.", + "Indicates that the request redirect failed.", + "Indicates that an unexpected error occurred.", + "Indicates that user is prompted with a login, waiting on user action.", + "Indicates that user lacks proper authentication credentials for a proxy server.", + }; - class WebResourceError - { - public: - const std::string description; - const int type; + class WebResourceError + { + public: + const std::string description; + const int type; - WebResourceError(const std::string& description, const int type); - WebResourceError(const flutter::EncodableMap& map); - ~WebResourceError() = default; - flutter::EncodableMap toEncodableMap(); - }; + WebResourceError(const std::string& description, const int type); + WebResourceError(const flutter::EncodableMap& map); + ~WebResourceError() = default; + + flutter::EncodableMap toEncodableMap() const; + }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_ERROR_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_request.cpp b/flutter_inappwebview_windows/windows/types/web_resource_request.cpp index c017e199..3ab71d93 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_request.cpp +++ b/flutter_inappwebview_windows/windows/types/web_resource_request.cpp @@ -1,32 +1,27 @@ -#include "web_resource_request.h" - #include "../utils/flutter.h" +#include "web_resource_request.h" namespace flutter_inappwebview_plugin { - WebResourceRequest::WebResourceRequest(const std::optional& url, const std::optional& method, - const std::optional>& headers, const std::optional& isForMainFrame) - : url(url), method(method), headers(headers), isForMainFrame(isForMainFrame) - { + WebResourceRequest::WebResourceRequest(const std::optional& url, const std::optional& method, + const std::optional>& headers, const std::optional& isForMainFrame) + : url(url), method(method), headers(headers), isForMainFrame(isForMainFrame) + {} - } + WebResourceRequest::WebResourceRequest(const flutter::EncodableMap& map) + : url(get_optional_fl_map_value(map, "url")), + method(get_optional_fl_map_value(map, "method")), + headers(get_optional_fl_map_value(map, "headers")), + isForMainFrame(get_optional_fl_map_value(map, "isForMainFrame")) + {} - WebResourceRequest::WebResourceRequest(const flutter::EncodableMap& map) - : url(get_optional_fl_map_value(map, "url")), - method(get_optional_fl_map_value(map, "method")), - headers(get_optional_fl_map_value(map, "headers")), - isForMainFrame(get_optional_fl_map_value(map, "isForMainFrame")) - { - - } - - flutter::EncodableMap WebResourceRequest::toEncodableMap() - { - return flutter::EncodableMap{ - {make_fl_value("url"), make_fl_value(url)}, - {make_fl_value("method"), make_fl_value(method)}, - {make_fl_value("headers"), make_fl_value(headers)}, - {make_fl_value("isForMainFrame"), make_fl_value(isForMainFrame)} - }; - } + flutter::EncodableMap WebResourceRequest::toEncodableMap() const + { + return flutter::EncodableMap{ + {make_fl_value("url"), make_fl_value(url)}, + {make_fl_value("method"), make_fl_value(method)}, + {make_fl_value("headers"), make_fl_value(headers)}, + {make_fl_value("isForMainFrame"), make_fl_value(isForMainFrame)} + }; + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_request.h b/flutter_inappwebview_windows/windows/types/web_resource_request.h index 4f8ac07f..ea9bb587 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_request.h +++ b/flutter_inappwebview_windows/windows/types/web_resource_request.h @@ -2,25 +2,25 @@ #define FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_REQUEST_H_ #include - #include namespace flutter_inappwebview_plugin { - class WebResourceRequest - { - public: - const std::optional url; - const std::optional method; - const std::optional> headers; - const std::optional isForMainFrame; + class WebResourceRequest + { + public: + const std::optional url; + const std::optional method; + const std::optional> headers; + const std::optional isForMainFrame; - WebResourceRequest(const std::optional& url, const std::optional& method, - const std::optional>& headers, const std::optional& isForMainFrame); - WebResourceRequest(const flutter::EncodableMap& map); - ~WebResourceRequest() = default; - flutter::EncodableMap toEncodableMap(); - }; + WebResourceRequest(const std::optional& url, const std::optional& method, + const std::optional>& headers, const std::optional& isForMainFrame); + WebResourceRequest(const flutter::EncodableMap& map); + ~WebResourceRequest() = default; + + flutter::EncodableMap toEncodableMap() const; + }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_REQUEST_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_response.cpp b/flutter_inappwebview_windows/windows/types/web_resource_response.cpp index 5d1d748d..934b94fb 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_response.cpp +++ b/flutter_inappwebview_windows/windows/types/web_resource_response.cpp @@ -1,25 +1,20 @@ -#include "web_resource_response.h" - #include "../utils/flutter.h" +#include "web_resource_response.h" namespace flutter_inappwebview_plugin { - WebResourceResponse::WebResourceResponse(const std::optional& statusCode) - : statusCode(statusCode) - { + WebResourceResponse::WebResourceResponse(const std::optional& statusCode) + : statusCode(statusCode) + {} - } + WebResourceResponse::WebResourceResponse(const flutter::EncodableMap& map) + : statusCode(get_optional_fl_map_value(map, "statusCode")) + {} - WebResourceResponse::WebResourceResponse(const flutter::EncodableMap& map) - : statusCode(get_optional_fl_map_value(map, "statusCode")) - { - - } - - flutter::EncodableMap WebResourceResponse::toEncodableMap() - { - return flutter::EncodableMap{ - {make_fl_value("statusCode"), make_fl_value(statusCode)} - }; - } + flutter::EncodableMap WebResourceResponse::toEncodableMap() const + { + return flutter::EncodableMap{ + {make_fl_value("statusCode"), make_fl_value(statusCode)} + }; + } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_response.h b/flutter_inappwebview_windows/windows/types/web_resource_response.h index caed73b3..123080aa 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_response.h +++ b/flutter_inappwebview_windows/windows/types/web_resource_response.h @@ -2,21 +2,21 @@ #define FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_RESPONSE_H_ #include - #include namespace flutter_inappwebview_plugin { - class WebResourceResponse - { - public: - const std::optional statusCode; + class WebResourceResponse + { + public: + const std::optional statusCode; - WebResourceResponse(const std::optional& statusCode); - WebResourceResponse(const flutter::EncodableMap& map); - ~WebResourceResponse() = default; - flutter::EncodableMap toEncodableMap(); - }; + WebResourceResponse(const std::optional& statusCode); + WebResourceResponse(const flutter::EncodableMap& map); + ~WebResourceResponse() = default; + + flutter::EncodableMap toEncodableMap() const; + }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_RESOURCE_RESPONSE_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/flutter.h b/flutter_inappwebview_windows/windows/utils/flutter.h index e08ac630..d539333d 100644 --- a/flutter_inappwebview_windows/windows/utils/flutter.h +++ b/flutter_inappwebview_windows/windows/utils/flutter.h @@ -1,77 +1,110 @@ #ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_FLUTTER_UTIL_H_ #define FLUTTER_INAPPWEBVIEW_PLUGIN_FLUTTER_UTIL_H_ -#include -#include #include +#include +#include #include "util.h" namespace flutter_inappwebview_plugin { - static inline flutter::EncodableValue make_fl_value() - { - return flutter::EncodableValue(); - } + static inline flutter::EncodableValue make_fl_value() + { + return flutter::EncodableValue(); + } - template - static inline flutter::EncodableValue make_fl_value(const T& val) - { - return flutter::EncodableValue(val); - } + template + static inline flutter::EncodableValue make_fl_value(const T& val) + { + return flutter::EncodableValue(val); + } - template - static inline flutter::EncodableValue make_fl_value(const T* val) - { - return val == nullptr ? make_fl_value() : flutter::EncodableValue(val); - } + template + static inline flutter::EncodableValue make_fl_value(const T* val) + { + return val == nullptr ? make_fl_value() : flutter::EncodableValue(val); + } - template - static inline flutter::EncodableValue make_fl_value(const std::optional& optional) - { - return optional.has_value() ? make_fl_value(optional.value()) : make_fl_value(); + template + static inline flutter::EncodableValue make_fl_value(const std::vector& vec) + { + auto encodableList = flutter::EncodableList{}; + for (auto const& val : vec) { + encodableList.push_back(make_fl_value(val)); } + return encodableList; + } - static inline flutter::EncodableValue make_fl_value(const std::optional>& optional) - { - if (!optional.has_value()) { - return make_fl_value(); - } - auto& mapValue = optional.value(); - auto encodableMap = flutter::EncodableMap{}; - for (auto const& [key, val] : mapValue) - { - encodableMap.insert({ make_fl_value(key), make_fl_value(val) }); - } - return encodableMap; + template + static inline flutter::EncodableValue make_fl_value(const std::map& map) + { + auto encodableMap = flutter::EncodableMap{}; + for (auto const& [key, val] : map) { + encodableMap.insert({ make_fl_value(key), make_fl_value(val) }); } + return encodableMap; + } - template - static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* string) - { - return std::get(map.at(make_fl_value(string))); - } + template + static inline flutter::EncodableValue make_fl_value(const std::optional& optional) + { + return optional.has_value() ? make_fl_value(optional.value()) : make_fl_value(); + } - template - static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) - { - return make_pointer_optional(std::get_if(&map.at(make_fl_value(string)))); + template + static inline flutter::EncodableValue make_fl_value(const std::optional>& optional) + { + if (!optional.has_value()) { + return make_fl_value(); } + auto& vecValue = optional.value(); + auto encodableList = flutter::EncodableList{}; + for (auto const& val : vecValue) { + encodableList.push_back(make_fl_value(val)); + } + return encodableList; + } - template - static inline std::optional> get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) - { - auto flMap = std::get_if(&map.at(make_fl_value(string))); - if (flMap) { - auto mapValue = std::map{}; - for (auto itr = flMap->begin(); itr != flMap->end(); itr++) - { - mapValue.insert({ std::get(itr->first), std::get(itr->second) }); - } - return make_pointer_optional>(&mapValue); - } - return std::nullopt; + template + static inline flutter::EncodableValue make_fl_value(const std::optional>& optional) + { + if (!optional.has_value()) { + return make_fl_value(); } + auto& mapValue = optional.value(); + auto encodableMap = flutter::EncodableMap{}; + for (auto const& [key, val] : mapValue) { + encodableMap.insert({ make_fl_value(key), make_fl_value(val) }); + } + return encodableMap; + } + + template + static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* string) + { + return std::get(map.at(make_fl_value(string))); + } + + template + static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) + { + return make_pointer_optional(std::get_if(&map.at(make_fl_value(string)))); + } + + template + static inline std::optional> get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) + { + auto flMap = std::get_if(&map.at(make_fl_value(string))); + if (flMap) { + auto mapValue = std::map{}; + for (auto itr = flMap->begin(); itr != flMap->end(); itr++) { + mapValue.insert({ std::get(itr->first), std::get(itr->second) }); + } + return make_pointer_optional>(&mapValue); + } + return std::nullopt; + } } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_FLUTTER_UTIL_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/strconv.h b/flutter_inappwebview_windows/windows/utils/strconv.h index 715a2297..0f1483ff 100644 --- a/flutter_inappwebview_windows/windows/utils/strconv.h +++ b/flutter_inappwebview_windows/windows/utils/strconv.h @@ -57,152 +57,152 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace flutter_inappwebview_plugin { #if __cplusplus >= 201103L && !defined(STRCONV_CPP98) - static inline std::wstring cp_to_wide(const std::string& s, UINT codepage) - { - int in_length = (int)s.length(); - int out_length = MultiByteToWideChar(codepage, 0, s.c_str(), in_length, 0, 0); - std::wstring result(out_length, L'\0'); - if (out_length) - MultiByteToWideChar(codepage, 0, s.c_str(), in_length, &result[0], out_length); - return result; - } - static inline std::string wide_to_cp(const std::wstring& s, UINT codepage) - { - int in_length = (int)s.length(); - int out_length = WideCharToMultiByte(codepage, 0, s.c_str(), in_length, 0, 0, 0, 0); - std::string result(out_length, '\0'); - if (out_length) - WideCharToMultiByte(codepage, 0, s.c_str(), in_length, &result[0], out_length, 0, 0); - return result; - } + static inline std::wstring cp_to_wide(const std::string& s, UINT codepage) + { + int in_length = (int)s.length(); + int out_length = MultiByteToWideChar(codepage, 0, s.c_str(), in_length, 0, 0); + std::wstring result(out_length, L'\0'); + if (out_length) + MultiByteToWideChar(codepage, 0, s.c_str(), in_length, &result[0], out_length); + return result; + } + static inline std::string wide_to_cp(const std::wstring& s, UINT codepage) + { + int in_length = (int)s.length(); + int out_length = WideCharToMultiByte(codepage, 0, s.c_str(), in_length, 0, 0, 0, 0); + std::string result(out_length, '\0'); + if (out_length) + WideCharToMultiByte(codepage, 0, s.c_str(), in_length, &result[0], out_length, 0, 0); + return result; + } #else /* __cplusplus < 201103L */ - static inline std::wstring cp_to_wide(const std::string& s, UINT codepage) - { - int in_length = (int)s.length(); - int out_length = MultiByteToWideChar(codepage, 0, s.c_str(), in_length, 0, 0); - std::vector buffer(out_length); - if (out_length) - MultiByteToWideChar(codepage, 0, s.c_str(), in_length, &buffer[0], out_length); - std::wstring result(buffer.begin(), buffer.end()); - return result; - } - static inline std::string wide_to_cp(const std::wstring& s, UINT codepage) - { - int in_length = (int)s.length(); - int out_length = WideCharToMultiByte(codepage, 0, s.c_str(), in_length, 0, 0, 0, 0); - std::vector buffer(out_length); - if (out_length) - WideCharToMultiByte(codepage, 0, s.c_str(), in_length, &buffer[0], out_length, 0, 0); - std::string result(buffer.begin(), buffer.end()); - return result; - } + static inline std::wstring cp_to_wide(const std::string& s, UINT codepage) + { + int in_length = (int)s.length(); + int out_length = MultiByteToWideChar(codepage, 0, s.c_str(), in_length, 0, 0); + std::vector buffer(out_length); + if (out_length) + MultiByteToWideChar(codepage, 0, s.c_str(), in_length, &buffer[0], out_length); + std::wstring result(buffer.begin(), buffer.end()); + return result; + } + static inline std::string wide_to_cp(const std::wstring& s, UINT codepage) + { + int in_length = (int)s.length(); + int out_length = WideCharToMultiByte(codepage, 0, s.c_str(), in_length, 0, 0, 0, 0); + std::vector buffer(out_length); + if (out_length) + WideCharToMultiByte(codepage, 0, s.c_str(), in_length, &buffer[0], out_length, 0, 0); + std::string result(buffer.begin(), buffer.end()); + return result; + } #endif - static inline std::string cp_to_utf8(const std::string& s, UINT codepage) - { - if (codepage == CP_UTF8) - return s; - std::wstring wide = cp_to_wide(s, codepage); - return wide_to_cp(wide, CP_UTF8); - } - static inline std::string utf8_to_cp(const std::string& s, UINT codepage) - { - if (codepage == CP_UTF8) - return s; - std::wstring wide = cp_to_wide(s, CP_UTF8); - return wide_to_cp(wide, codepage); - } + static inline std::string cp_to_utf8(const std::string& s, UINT codepage) + { + if (codepage == CP_UTF8) + return s; + std::wstring wide = cp_to_wide(s, codepage); + return wide_to_cp(wide, CP_UTF8); + } + static inline std::string utf8_to_cp(const std::string& s, UINT codepage) + { + if (codepage == CP_UTF8) + return s; + std::wstring wide = cp_to_wide(s, CP_UTF8); + return wide_to_cp(wide, codepage); + } - static inline std::wstring ansi_to_wide(const std::string& s) - { - return cp_to_wide(s, CP_ACP); - } - static inline std::string wide_to_ansi(const std::wstring& s) - { - return wide_to_cp(s, CP_ACP); - } + static inline std::wstring ansi_to_wide(const std::string& s) + { + return cp_to_wide(s, CP_ACP); + } + static inline std::string wide_to_ansi(const std::wstring& s) + { + return wide_to_cp(s, CP_ACP); + } - static inline std::wstring sjis_to_wide(const std::string& s) - { - return cp_to_wide(s, 932); - } - static inline std::string wide_to_sjis(const std::wstring& s) - { - return wide_to_cp(s, 932); - } + static inline std::wstring sjis_to_wide(const std::string& s) + { + return cp_to_wide(s, 932); + } + static inline std::string wide_to_sjis(const std::wstring& s) + { + return wide_to_cp(s, 932); + } - static inline std::wstring utf8_to_wide(const std::string& s) - { - return cp_to_wide(s, CP_UTF8); - } - static inline std::string wide_to_utf8(const std::wstring& s) - { - return wide_to_cp(s, CP_UTF8); - } + static inline std::wstring utf8_to_wide(const std::string& s) + { + return cp_to_wide(s, CP_UTF8); + } + static inline std::string wide_to_utf8(const std::wstring& s) + { + return wide_to_cp(s, CP_UTF8); + } - static inline std::string ansi_to_utf8(const std::string& s) - { - return cp_to_utf8(s, CP_ACP); - } - static inline std::string utf8_to_ansi(const std::string& s) - { - return utf8_to_cp(s, CP_ACP); - } + static inline std::string ansi_to_utf8(const std::string& s) + { + return cp_to_utf8(s, CP_ACP); + } + static inline std::string utf8_to_ansi(const std::string& s) + { + return utf8_to_cp(s, CP_ACP); + } - static inline std::string sjis_to_utf8(const std::string& s) - { - return cp_to_utf8(s, 932); - } - static inline std::string utf8_to_sjis(const std::string& s) - { - return utf8_to_cp(s, 932); - } + static inline std::string sjis_to_utf8(const std::string& s) + { + return cp_to_utf8(s, 932); + } + static inline std::string utf8_to_sjis(const std::string& s) + { + return utf8_to_cp(s, 932); + } #ifdef __cpp_char8_t - static inline std::u8string utf8_to_char8(const std::string& s) - { - return std::u8string(s.begin(), s.end()); - } - static inline std::string char8_to_utf8(const std::u8string& s) - { - return std::string(s.begin(), s.end()); - } + static inline std::u8string utf8_to_char8(const std::string& s) + { + return std::u8string(s.begin(), s.end()); + } + static inline std::string char8_to_utf8(const std::u8string& s) + { + return std::string(s.begin(), s.end()); + } - static inline std::wstring char8_to_wide(const std::u8string& s) - { - return cp_to_wide(char8_to_utf8(s), CP_UTF8); - } - static inline std::u8string wide_to_char8(const std::wstring& s) - { - return utf8_to_char8(wide_to_cp(s, CP_UTF8)); - } + static inline std::wstring char8_to_wide(const std::u8string& s) + { + return cp_to_wide(char8_to_utf8(s), CP_UTF8); + } + static inline std::u8string wide_to_char8(const std::wstring& s) + { + return utf8_to_char8(wide_to_cp(s, CP_UTF8)); + } - static inline std::u8string cp_to_char8(const std::string& s, UINT codepage) - { - return utf8_to_char8(cp_to_utf8(s, codepage)); - } - static inline std::string char8_to_cp(const std::u8string& s, UINT codepage) - { - return utf8_to_cp(char8_to_utf8(s), codepage); - } + static inline std::u8string cp_to_char8(const std::string& s, UINT codepage) + { + return utf8_to_char8(cp_to_utf8(s, codepage)); + } + static inline std::string char8_to_cp(const std::u8string& s, UINT codepage) + { + return utf8_to_cp(char8_to_utf8(s), codepage); + } - static inline std::u8string ansi_to_char8(const std::string& s) - { - return cp_to_char8(s, CP_ACP); - } - static inline std::string char8_to_ansi(const std::u8string& s) - { - return char8_to_cp(s, CP_ACP); - } + static inline std::u8string ansi_to_char8(const std::string& s) + { + return cp_to_char8(s, CP_ACP); + } + static inline std::string char8_to_ansi(const std::u8string& s) + { + return char8_to_cp(s, CP_ACP); + } - static inline std::u8string sjis_to_char8(const std::string& s) - { - return cp_to_char8(s, 932); - } - static inline std::string char8_to_sjis(const std::u8string& s) - { - return char8_to_cp(s, 932); - } + static inline std::u8string sjis_to_char8(const std::string& s) + { + return cp_to_char8(s, 932); + } + static inline std::string char8_to_sjis(const std::u8string& s) + { + return char8_to_cp(s, 932); + } #endif #if defined(_MSC_VER) @@ -210,360 +210,346 @@ namespace flutter_inappwebview_plugin #pragma warning(disable : 4996) #endif - static inline std::wstring vformat(const wchar_t* format, va_list args) - { - int len = _vsnwprintf(0, 0, format, args); - if (len < 0) - return L""; - std::vector buffer(len + 1); - len = _vsnwprintf(&buffer[0], len, format, args); - if (len < 0) - return L""; - buffer[len] = L'\0'; - return &buffer[0]; - } - static inline std::string vformat(const char* format, va_list args) - { - int len = _vsnprintf(0, 0, format, args); - if (len < 0) - return ""; - std::vector buffer(len + 1); - len = _vsnprintf(&buffer[0], len, format, args); - if (len < 0) - return ""; - buffer[len] = '\0'; - return &buffer[0]; - } + static inline std::wstring vformat(const wchar_t* format, va_list args) + { + int len = _vsnwprintf(0, 0, format, args); + if (len < 0) + return L""; + std::vector buffer(len + 1); + len = _vsnwprintf(&buffer[0], len, format, args); + if (len < 0) + return L""; + buffer[len] = L'\0'; + return &buffer[0]; + } + static inline std::string vformat(const char* format, va_list args) + { + int len = _vsnprintf(0, 0, format, args); + if (len < 0) + return ""; + std::vector buffer(len + 1); + len = _vsnprintf(&buffer[0], len, format, args); + if (len < 0) + return ""; + buffer[len] = '\0'; + return &buffer[0]; + } #ifdef __cpp_char8_t - static inline std::u8string vformat(const char8_t* format, va_list args) - { - int len = _vsnprintf(0, 0, (const char*)format, args); - if (len < 0) - return u8""; - std::vector buffer(len + 1); - len = _vsnprintf(&buffer[0], len, (const char*)format, args); - if (len < 0) - return u8""; - buffer[len] = '\0'; - return (char8_t*)&buffer[0]; - } + static inline std::u8string vformat(const char8_t* format, va_list args) + { + int len = _vsnprintf(0, 0, (const char*)format, args); + if (len < 0) + return u8""; + std::vector buffer(len + 1); + len = _vsnprintf(&buffer[0], len, (const char*)format, args); + if (len < 0) + return u8""; + buffer[len] = '\0'; + return (char8_t*)&buffer[0]; + } #endif #if defined(_MSC_VER) #pragma warning(pop) #endif - static inline std::wstring format(const wchar_t* format, ...) - { - va_list args; - va_start(args, format); - std::wstring s = vformat(format, args); - va_end(args); - return s; - } - static inline std::string format(const char* format, ...) - { - va_list args; - va_start(args, format); - std::string s = vformat(format, args); - va_end(args); - return s; - } + static inline std::wstring format(const wchar_t* format, ...) + { + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + return s; + } + static inline std::string format(const char* format, ...) + { + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + return s; + } #ifdef __cpp_char8_t - static inline std::u8string format(const char8_t* format, ...) - { - va_list args; - va_start(args, format); - std::u8string s = vformat(format, args); - va_end(args); - return s; - } + static inline std::u8string format(const char8_t* format, ...) + { + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + return s; + } #endif - static inline void format(std::ostream& ostrm, const wchar_t* format, ...) - { - va_list args; - va_start(args, format); - std::wstring s = vformat(format, args); - va_end(args); - ostrm << wide_to_utf8(s) << std::flush; - } - static inline void format(std::ostream& ostrm, const char* format, ...) - { - va_list args; - va_start(args, format); - std::string s = vformat(format, args); - va_end(args); - ostrm << s << std::flush; - } + static inline void format(std::ostream& ostrm, const wchar_t* format, ...) + { + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + ostrm << wide_to_utf8(s) << std::flush; + } + static inline void format(std::ostream& ostrm, const char* format, ...) + { + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + ostrm << s << std::flush; + } #ifdef __cpp_char8_t - static inline void format(std::ostream& ostrm, const char8_t* format, ...) - { - va_list args; - va_start(args, format); - std::u8string s = vformat(format, args); - va_end(args); - ostrm << char8_to_utf8(s) << std::flush; - } + static inline void format(std::ostream& ostrm, const char8_t* format, ...) + { + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + ostrm << char8_to_utf8(s) << std::flush; + } #endif - static inline std::string formatA(const wchar_t* format, ...) - { - va_list args; - va_start(args, format); - std::wstring s = vformat(format, args); - va_end(args); - return wide_to_ansi(s); - } - static inline std::string formatA(const char* format, ...) - { - va_list args; - va_start(args, format); - std::string s = vformat(format, args); - va_end(args); - return utf8_to_ansi(s); - } + static inline std::string formatA(const wchar_t* format, ...) + { + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + return wide_to_ansi(s); + } + static inline std::string formatA(const char* format, ...) + { + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + return utf8_to_ansi(s); + } #ifdef __cpp_char8_t - static inline std::string formatA(const char8_t* format, ...) - { - va_list args; - va_start(args, format); - std::u8string s = vformat(format, args); - va_end(args); - return char8_to_ansi(s); - } + static inline std::string formatA(const char8_t* format, ...) + { + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + return char8_to_ansi(s); + } #endif - static inline void formatA(std::ostream& ostrm, const wchar_t* format, ...) - { - va_list args; - va_start(args, format); - std::wstring s = vformat(format, args); - va_end(args); - ostrm << wide_to_ansi(s) << std::flush; - } - static inline void formatA(std::ostream& ostrm, const char* format, ...) - { - va_list args; - va_start(args, format); - std::string s = vformat(format, args); - va_end(args); - ostrm << utf8_to_ansi(s) << std::flush; - } + static inline void formatA(std::ostream& ostrm, const wchar_t* format, ...) + { + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + ostrm << wide_to_ansi(s) << std::flush; + } + static inline void formatA(std::ostream& ostrm, const char* format, ...) + { + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + ostrm << utf8_to_ansi(s) << std::flush; + } #ifdef __cpp_char8_t - static inline void formatA(std::ostream& ostrm, const char8_t* format, ...) - { - va_list args; - va_start(args, format); - std::u8string s = vformat(format, args); - va_end(args); - ostrm << char8_to_ansi(s) << std::flush; - } + static inline void formatA(std::ostream& ostrm, const char8_t* format, ...) + { + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + ostrm << char8_to_ansi(s) << std::flush; + } #endif - static inline void dbgmsg(const wchar_t* title, const wchar_t* format, ...) - { - va_list args; - va_start(args, format); - std::wstring s = vformat(format, args); - va_end(args); - MessageBoxW(0, s.c_str(), title, MB_OK); - } - static inline void dbgmsg(const char* title, const char* format, ...) - { - va_list args; - va_start(args, format); - std::string s = vformat(format, args); - va_end(args); - MessageBoxW(0, utf8_to_wide(s).c_str(), utf8_to_wide(title).c_str(), MB_OK); - } + static inline void dbgmsg(const wchar_t* title, const wchar_t* format, ...) + { + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + MessageBoxW(0, s.c_str(), title, MB_OK); + } + static inline void dbgmsg(const char* title, const char* format, ...) + { + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + MessageBoxW(0, utf8_to_wide(s).c_str(), utf8_to_wide(title).c_str(), MB_OK); + } #ifdef __cpp_char8_t - static inline void dbgmsg(const char8_t* title, const char8_t* format, ...) - { - va_list args; - va_start(args, format); - std::u8string s = vformat(format, args); - va_end(args); - MessageBoxW(0, char8_to_wide(s).c_str(), char8_to_wide(title).c_str(), MB_OK); - } + static inline void dbgmsg(const char8_t* title, const char8_t* format, ...) + { + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + MessageBoxW(0, char8_to_wide(s).c_str(), char8_to_wide(title).c_str(), MB_OK); + } #endif - static inline HANDLE handle_for_ostream(std::ostream& ostrm) - { - if (&ostrm == &std::cout) - { - return GetStdHandle(STD_OUTPUT_HANDLE); - } - else if (&ostrm == &std::cerr) - { - return GetStdHandle(STD_ERROR_HANDLE); - } - return INVALID_HANDLE_VALUE; + static inline HANDLE handle_for_ostream(std::ostream& ostrm) + { + if (&ostrm == &std::cout) { + return GetStdHandle(STD_OUTPUT_HANDLE); } - static inline void dbgout(std::ostream& ostrm, const wchar_t* format, ...) - { - va_list args; - va_start(args, format); - std::wstring ws = vformat(format, args); - va_end(args); - HANDLE h = handle_for_ostream(ostrm); - if (h == INVALID_HANDLE_VALUE) - { - return; - } - DWORD dwNumberOfCharsWrite; - if (GetFileType(h) != FILE_TYPE_CHAR) - { - std::string s = wide_to_cp(ws, GetConsoleOutputCP()); - WriteFile(h, s.c_str(), (DWORD)s.size(), &dwNumberOfCharsWrite, NULL); - } - else - { - WriteConsoleW(h, - ws.c_str(), - (DWORD)ws.size(), - &dwNumberOfCharsWrite, - NULL); - } + else if (&ostrm == &std::cerr) { + return GetStdHandle(STD_ERROR_HANDLE); } - static inline void dbgout(std::ostream& ostrm, const char* format, ...) - { - va_list args; - va_start(args, format); - std::string s = vformat(format, args); - va_end(args); - HANDLE h = handle_for_ostream(ostrm); - if (h == INVALID_HANDLE_VALUE) - { - return; - } - DWORD dwNumberOfCharsWrite; - if (GetFileType(h) != FILE_TYPE_CHAR) - { - s = utf8_to_cp(s, GetConsoleOutputCP()); - WriteFile(h, s.c_str(), (DWORD)s.size(), &dwNumberOfCharsWrite, NULL); - } - else - { - std::wstring ws = utf8_to_wide(s); - WriteConsoleW(h, - ws.c_str(), - (DWORD)ws.size(), - &dwNumberOfCharsWrite, - NULL); - } + return INVALID_HANDLE_VALUE; + } + static inline void dbgout(std::ostream& ostrm, const wchar_t* format, ...) + { + va_list args; + va_start(args, format); + std::wstring ws = vformat(format, args); + va_end(args); + HANDLE h = handle_for_ostream(ostrm); + if (h == INVALID_HANDLE_VALUE) { + return; } + DWORD dwNumberOfCharsWrite; + if (GetFileType(h) != FILE_TYPE_CHAR) { + std::string s = wide_to_cp(ws, GetConsoleOutputCP()); + WriteFile(h, s.c_str(), (DWORD)s.size(), &dwNumberOfCharsWrite, NULL); + } + else { + WriteConsoleW(h, + ws.c_str(), + (DWORD)ws.size(), + &dwNumberOfCharsWrite, + NULL); + } + } + static inline void dbgout(std::ostream& ostrm, const char* format, ...) + { + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + HANDLE h = handle_for_ostream(ostrm); + if (h == INVALID_HANDLE_VALUE) { + return; + } + DWORD dwNumberOfCharsWrite; + if (GetFileType(h) != FILE_TYPE_CHAR) { + s = utf8_to_cp(s, GetConsoleOutputCP()); + WriteFile(h, s.c_str(), (DWORD)s.size(), &dwNumberOfCharsWrite, NULL); + } + else { + std::wstring ws = utf8_to_wide(s); + WriteConsoleW(h, + ws.c_str(), + (DWORD)ws.size(), + &dwNumberOfCharsWrite, + NULL); + } + } #ifdef __cpp_char8_t - static inline void dbgout(std::ostream& ostrm, const char8_t* format, ...) - { - va_list args; - va_start(args, format); - std::u8string s = vformat(format, args); - va_end(args); - HANDLE h = handle_for_ostream(ostrm); - if (h == INVALID_HANDLE_VALUE) - { - return; - } - DWORD dwNumberOfCharsWrite; - if (GetFileType(h) != FILE_TYPE_CHAR) - { - std::string str = char8_to_cp(s, GetConsoleOutputCP()); - WriteFile(h, (const char*)str.c_str(), (DWORD)str.size(), &dwNumberOfCharsWrite, NULL); - } - else - { - std::wstring ws = char8_to_wide(s); - WriteConsoleW(h, - ws.c_str(), - (DWORD)ws.size(), - &dwNumberOfCharsWrite, - NULL); - } + static inline void dbgout(std::ostream& ostrm, const char8_t* format, ...) + { + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + HANDLE h = handle_for_ostream(ostrm); + if (h == INVALID_HANDLE_VALUE) { + return; } + DWORD dwNumberOfCharsWrite; + if (GetFileType(h) != FILE_TYPE_CHAR) { + std::string str = char8_to_cp(s, GetConsoleOutputCP()); + WriteFile(h, (const char*)str.c_str(), (DWORD)str.size(), &dwNumberOfCharsWrite, NULL); + } + else { + std::wstring ws = char8_to_wide(s); + WriteConsoleW(h, + ws.c_str(), + (DWORD)ws.size(), + &dwNumberOfCharsWrite, + NULL); + } + } #endif - class unicode_ostream + class unicode_ostream + { + private: + std::ostream* m_ostrm; + UINT m_target_cp; + bool is_ascii(const std::string& s) { - private: - std::ostream* m_ostrm; - UINT m_target_cp; - bool is_ascii(const std::string& s) - { - for (std::size_t i = 0; i < s.size(); i++) - { - unsigned char c = (unsigned char)s[i]; - if (c > 0x7f) - return false; - } - return true; - } + for (std::size_t i = 0; i < s.size(); i++) { + unsigned char c = (unsigned char)s[i]; + if (c > 0x7f) + return false; + } + return true; + } - public: - unicode_ostream(std::ostream& ostrm, UINT target_cp = CP_ACP) : m_ostrm(&ostrm), m_target_cp(target_cp) {} - std::ostream& stream() { return *m_ostrm; } - void stream(std::ostream& ostrm) { m_ostrm = &ostrm; } - UINT target_cp() { return m_target_cp; } - void target_cp(UINT cp) { m_target_cp = cp; } - template - unicode_ostream& operator<<(const T& x) - { - std::ostringstream oss; - oss << x; - std::string output = oss.str(); - if (is_ascii(output)) - { - (*m_ostrm) << x; - } - else - { - (*m_ostrm) << utf8_to_cp(output, m_target_cp); - } - return *this; - } - unicode_ostream& operator<<(const std::wstring& x) - { - (*m_ostrm) << wide_to_cp(x, m_target_cp); - return *this; - } - unicode_ostream& operator<<(const wchar_t* x) - { - (*m_ostrm) << wide_to_cp(x, m_target_cp); - return *this; - } - unicode_ostream& operator<<(const std::string& x) - { - (*m_ostrm) << utf8_to_cp(x, m_target_cp); - return *this; - } - unicode_ostream& operator<<(const char* x) - { - (*m_ostrm) << utf8_to_cp(x, m_target_cp); - return *this; - } + public: + unicode_ostream(std::ostream& ostrm, UINT target_cp = CP_ACP) : m_ostrm(&ostrm), m_target_cp(target_cp) {} + std::ostream& stream() { return *m_ostrm; } + void stream(std::ostream& ostrm) { m_ostrm = &ostrm; } + UINT target_cp() { return m_target_cp; } + void target_cp(UINT cp) { m_target_cp = cp; } + template + unicode_ostream& operator<<(const T& x) + { + std::ostringstream oss; + oss << x; + std::string output = oss.str(); + if (is_ascii(output)) { + (*m_ostrm) << x; + } + else { + (*m_ostrm) << utf8_to_cp(output, m_target_cp); + } + return *this; + } + unicode_ostream& operator<<(const std::wstring& x) + { + (*m_ostrm) << wide_to_cp(x, m_target_cp); + return *this; + } + unicode_ostream& operator<<(const wchar_t* x) + { + (*m_ostrm) << wide_to_cp(x, m_target_cp); + return *this; + } + unicode_ostream& operator<<(const std::string& x) + { + (*m_ostrm) << utf8_to_cp(x, m_target_cp); + return *this; + } + unicode_ostream& operator<<(const char* x) + { + (*m_ostrm) << utf8_to_cp(x, m_target_cp); + return *this; + } #ifdef __cpp_char8_t - unicode_ostream& operator<<(const std::u8string& x) - { - (*m_ostrm) << char8_to_cp(x, m_target_cp); - return *this; - } - unicode_ostream& operator<<(const char8_t* x) - { - (*m_ostrm) << char8_to_cp(x, m_target_cp); - return *this; - } + unicode_ostream& operator<<(const std::u8string& x) + { + (*m_ostrm) << char8_to_cp(x, m_target_cp); + return *this; + } + unicode_ostream& operator<<(const char8_t* x) + { + (*m_ostrm) << char8_to_cp(x, m_target_cp); + return *this; + } #endif - unicode_ostream& operator<<(std::ostream& (*pf)(std::ostream&)) // For manipulators... - { - (*m_ostrm) << pf; - return *this; - } - unicode_ostream& operator<<(std::basic_ios& (*pf)(std::basic_ios&)) // For manipulators... - { - (*m_ostrm) << pf; - return *this; - } - }; + unicode_ostream& operator<<(std::ostream& (*pf)(std::ostream&)) // For manipulators... + { + (*m_ostrm) << pf; + return *this; + } + unicode_ostream& operator<<(std::basic_ios& (*pf)(std::basic_ios&)) // For manipulators... + { + (*m_ostrm) << pf; + return *this; + } + }; } #define U8(X) ((const char *)u8##X) diff --git a/flutter_inappwebview_windows/windows/utils/util.h b/flutter_inappwebview_windows/windows/utils/util.h index 34562bce..e619a246 100644 --- a/flutter_inappwebview_windows/windows/utils/util.h +++ b/flutter_inappwebview_windows/windows/utils/util.h @@ -1,52 +1,57 @@ #ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ #define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ -#include -#include -#include #include +#include +#include +#include +#include +#include + #include "strconv.h" namespace flutter_inappwebview_plugin { - static inline void debugLog(const std::string& msg) { + static inline void debugLog(const std::string& msg) + { #ifndef NDEBUG - std::cout << msg << std::endl; - OutputDebugString(ansi_to_wide(msg + "\n").c_str()); + std::cout << msg << std::endl; + OutputDebugString(ansi_to_wide(msg + "\n").c_str()); #endif - } + } - template - static inline std::optional make_pointer_optional(const T* value) - { - return value == nullptr ? std::nullopt : std::make_optional(*value); - } + template + static inline std::optional make_pointer_optional(const T* value) + { + return value == nullptr ? std::nullopt : std::make_optional(*value); + } - static inline std::string variant_to_string(const std::variant& var) - { - return std::visit([](auto&& arg) { - using T = std::decay_t; - if constexpr (std::is_same_v) - return arg; - else if constexpr (std::is_arithmetic_v) - return std::to_string(arg); - else - static_assert(always_false_v, "non-exhaustive visitor!"); - }, var); - } + static inline std::string variant_to_string(const std::variant& var) + { + return std::visit([](auto&& arg) + { + using T = std::decay_t; + if constexpr (std::is_same_v) + return arg; + else if constexpr (std::is_arithmetic_v) + return std::to_string(arg); + else + static_assert(always_false_v, "non-exhaustive visitor!"); + }, var); + } - template - static inline bool map_contains(const std::map& map, const K& key) - { - return map.find(key) != map.end(); - } + template + static inline bool map_contains(const std::map& map, const K& key) + { + return map.find(key) != map.end(); + } - template - static inline T map_at_or_null(const std::map& map, const K& key) - { - auto itr = map.find(key); - return itr != map.end() ? itr->second : nullptr; - } + template + static inline T map_at_or_null(const std::map& map, const K& key) + { + auto itr = map.find(key); + return itr != map.end() ? itr->second : nullptr; + } } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ \ No newline at end of file From 11659f22e190e12b39ef42fb245b2eb001c217fa Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 11 Jan 2024 11:10:10 +0100 Subject: [PATCH 05/21] windows: added initial webview settings, updated flutter.h get fl options implementations --- .vscode/settings.json | 75 +++++++++++++++++++ .../windows/CMakeLists.txt | 10 +-- .../windows/in_app_browser/in_app_browser.cpp | 11 ++- .../windows/in_app_browser/in_app_browser.h | 19 +++-- .../in_app_browser/in_app_browser_manager.cpp | 13 +++- .../in_app_browser/in_app_browser_manager.h | 4 +- .../in_app_browser_settings.cpp | 45 +++++++++++ .../in_app_browser/in_app_browser_settings.h | 27 +++++++ .../windows/in_app_webview/in_app_webview.cpp | 23 +++++- .../windows/in_app_webview/in_app_webview.h | 11 ++- .../in_app_webview_settings.cpp | 24 ++++++ .../in_app_webview/in_app_webview_settings.h | 26 +++++++ .../webview_channel_delegate.cpp | 12 +-- .../windows/types/navigation_action.cpp | 4 +- .../windows/types/url_request.cpp | 10 +-- .../windows/types/web_resource_error.cpp | 4 +- .../windows/types/web_resource_request.cpp | 10 +-- .../windows/types/web_resource_response.cpp | 2 +- .../windows/utils/flutter.h | 55 ++++++++++++-- .../windows/utils/util.h | 28 +++++++ 20 files changed, 359 insertions(+), 54 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.cpp create mode 100644 flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.h create mode 100644 flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.cpp create mode 100644 flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.h diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..70f8824f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,75 @@ +{ + "cmake.configureOnOpen": false, + "files.associations": { + "algorithm": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "coroutine": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "exception": "cpp", + "format": "cpp", + "forward_list": "cpp", + "functional": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "set": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "utility": "cpp", + "variant": "cpp", + "vector": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "xutility": "cpp" + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt index 475fb4eb..20c412dd 100644 --- a/flutter_inappwebview_windows/windows/CMakeLists.txt +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -22,7 +22,6 @@ cmake_policy(VERSION 3.14...3.25) set(PLUGIN_NAME "flutter_inappwebview_windows_plugin") set(NUGET_URL https://dist.nuget.org/win-x86-commandline/latest/nuget.exe) -set(NUGET_SHA256 852b71cc8c8c2d40d09ea49d321ff56fd2397b9d6ea9f96e532530307bbbafd3) find_program(NUGET nuget) if(NOT NUGET) @@ -32,11 +31,6 @@ if(NOT NUGET) message(NOTICE "Attempting to download nuget.") file(DOWNLOAD ${NUGET_URL} ${NUGET}) endif() - - file(SHA256 ${NUGET} NUGET_DL_HASH) - if (NOT NUGET_DL_HASH STREQUAL NUGET_SHA256) - message(FATAL_ERROR "Integrity check for ${NUGET} failed.") - endif() endif() add_custom_target(${PROJECT_NAME}_DEPENDENCIES_DOWNLOAD ALL) @@ -67,10 +61,14 @@ list(APPEND PLUGIN_SOURCES "types/web_resource_request.h" "types/web_resource_response.cpp" "types/web_resource_response.h" + "in_app_webview/in_app_webview_settings.cpp" + "in_app_webview/in_app_webview_settings.h" "in_app_webview/in_app_webview.cpp" "in_app_webview/in_app_webview.h" "in_app_webview/webview_channel_delegate.cpp" "in_app_webview/webview_channel_delegate.h" + "in_app_browser/in_app_browser_settings.cpp" + "in_app_browser/in_app_browser_settings.h" "in_app_browser/in_app_browser_manager.cpp" "in_app_browser/in_app_browser_manager.h" "in_app_browser/in_app_browser.cpp" diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index afa81412..219ca230 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -6,13 +6,15 @@ namespace flutter_inappwebview_plugin { - InAppBrowser::InAppBrowser(FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params) + InAppBrowser::InAppBrowser(const FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params) : plugin(plugin), m_hInstance(GetModuleHandle(nullptr)), id(params.id), initialUrlRequest(params.urlRequest), + settings(params.initialSettings), channelDelegate(std::make_unique(id, plugin->registrar->messenger())) { + WNDCLASS wndClass = {}; wndClass.lpszClassName = InAppBrowser::CLASS_NAME; wndClass.hInstance = m_hInstance; @@ -39,7 +41,12 @@ namespace flutter_inappwebview_plugin ShowWindow(m_hWnd, SW_SHOW); - webView = std::make_unique(plugin, id, m_hWnd, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id, [this]() -> void + InAppWebViewCreationParams webViewParams = { + id, + params.initialWebViewSettings + }; + + webView = std::make_unique(plugin, webViewParams, m_hWnd, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id, [this]() -> void { if (channelDelegate) { channelDelegate->onBrowserCreated(); diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h index 900fecde..9e91cf14 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h @@ -8,15 +8,19 @@ #include "../flutter_inappwebview_windows_plugin.h" #include "../in_app_webview/in_app_webview.h" +#include "../in_app_webview/in_app_webview_settings.h" #include "../types/url_request.h" #include "in_app_browser_channel_delegate.h" +#include "in_app_browser_settings.h" namespace flutter_inappwebview_plugin { struct InAppBrowserCreationParams { - std::string id; - std::optional urlRequest; + const std::string id; + const std::optional urlRequest; + const std::shared_ptr initialSettings; + const std::shared_ptr initialWebViewSettings; }; class InAppBrowser { @@ -29,17 +33,18 @@ namespace flutter_inappwebview_plugin WPARAM wparam, LPARAM lparam) noexcept; - FlutterInappwebviewWindowsPlugin* plugin; - std::string id; - std::optional initialUrlRequest; + const FlutterInappwebviewWindowsPlugin* plugin; + const std::string id; + const std::optional initialUrlRequest; std::unique_ptr webView; std::unique_ptr channelDelegate; + const std::shared_ptr settings; - InAppBrowser(FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params); + InAppBrowser(const FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params); ~InAppBrowser(); private: - HINSTANCE m_hInstance; + const HINSTANCE m_hInstance; HWND m_hWnd; bool destroyed_ = false; static InAppBrowser* GetThisFromHandle(HWND window) noexcept; diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp index 1e08d7ba..b07181f4 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -1,13 +1,15 @@ #include #include +#include "../in_app_webview/in_app_webview_settings.h" #include "../types/url_request.h" #include "../utils/flutter.h" #include "in_app_browser_manager.h" +#include "in_app_browser_settings.h" namespace flutter_inappwebview_plugin { - InAppBrowserManager::InAppBrowserManager(FlutterInappwebviewWindowsPlugin* plugin) + InAppBrowserManager::InAppBrowserManager(const FlutterInappwebviewWindowsPlugin* plugin) : plugin(plugin), ChannelDelegate(plugin->registrar->messenger(), InAppBrowserManager::METHOD_CHANNEL_NAME) {} @@ -30,10 +32,17 @@ namespace flutter_inappwebview_plugin auto urlRequestMap = get_optional_fl_map_value(*arguments, "urlRequest"); std::optional urlRequest = urlRequestMap.has_value() ? std::make_optional(urlRequestMap.value()) : std::optional{}; + auto settingsMap = get_fl_map_value(*arguments, "settings"); + auto initialSettings = std::make_unique(settingsMap); + auto initialWebViewSettings = std::make_unique(settingsMap); + InAppBrowserCreationParams params = { id, - urlRequest + urlRequest, + std::move(initialSettings), + std::move(initialWebViewSettings) }; + auto inAppBrowser = std::make_unique(plugin, params); browsers.insert({ id, std::move(inAppBrowser) }); } diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h index 91f813e2..31180fee 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h @@ -17,10 +17,10 @@ namespace flutter_inappwebview_plugin public: static inline const std::string METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappbrowser"; - FlutterInappwebviewWindowsPlugin* plugin; + const FlutterInappwebviewWindowsPlugin* plugin; std::map> browsers; - InAppBrowserManager(FlutterInappwebviewWindowsPlugin* plugin); + InAppBrowserManager(const FlutterInappwebviewWindowsPlugin* plugin); ~InAppBrowserManager(); void HandleMethodCall( diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.cpp new file mode 100644 index 00000000..4b5d9594 --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.cpp @@ -0,0 +1,45 @@ +#include "../utils/flutter.h" +#include "in_app_browser_settings.h" + +namespace flutter_inappwebview_plugin +{ + namespace + { + InAppBrowserWindowType inAppBrowserWindowTypeFromString(const std::string& s) + { + if (s.compare("CHILD") == 0) { + return child; + } + else if (s.compare("TABBED") == 0) { + return tabbed; + } + return window; + } + + std::string inAppBrowserWindowTypeToString(const InAppBrowserWindowType& t) + { + switch (t) { + case child: + return "CHILD"; + case tabbed: + return "TABBED"; + default: + return "WINDOW"; + } + } + } + + InAppBrowserSettings::InAppBrowserSettings() {}; + + InAppBrowserSettings::InAppBrowserSettings(const flutter::EncodableMap& encodableMap) + { + hidden = get_fl_map_value(encodableMap, "hidden", hidden); + windowType = inAppBrowserWindowTypeFromString(get_fl_map_value(encodableMap, "windowType", inAppBrowserWindowTypeToString(window))); + windowAlphaValue = get_fl_map_value(encodableMap, "windowAlphaValue", windowAlphaValue); + } + + InAppBrowserSettings::~InAppBrowserSettings() + { + debugLog("dealloc InAppBrowserSettings"); + }; +} diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.h new file mode 100644 index 00000000..c65bbe14 --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.h @@ -0,0 +1,27 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_SETTINGS_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_SETTINGS_H_ + +#include +#include + +namespace flutter_inappwebview_plugin +{ + enum InAppBrowserWindowType { + window, + child, + tabbed + }; + + class InAppBrowserSettings + { + public: + bool hidden = false; + InAppBrowserWindowType windowType = window; + double windowAlphaValue = 1.0; + + InAppBrowserSettings(); + InAppBrowserSettings(const flutter::EncodableMap& encodableMap); + ~InAppBrowserSettings(); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_SETTINGS_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index 2902c704..5f1f33dd 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -1,5 +1,6 @@ #pragma comment(lib, "Shlwapi.lib") +#include #include #include #include @@ -14,14 +15,14 @@ namespace flutter_inappwebview_plugin { using namespace Microsoft::WRL; - InAppWebView::InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::function completionHandler) - : plugin(plugin), id(id), channelDelegate(std::make_unique(this, plugin->registrar->messenger())) + InAppWebView::InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, const std::function completionHandler) + : plugin(plugin), id(params.id), settings(params.initialSettings), channelDelegate(std::make_unique(this, plugin->registrar->messenger())) { createWebView(parentWindow, completionHandler); } - InAppWebView::InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::string& channelName, const std::function completionHandler) - : plugin(plugin), id(id), channelDelegate(std::make_unique(this, plugin->registrar->messenger(), channelName)) + InAppWebView::InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, const std::string& channelName, const std::function completionHandler) + : plugin(plugin), id(params.id), settings(params.initialSettings), channelDelegate(std::make_unique(this, plugin->registrar->messenger(), channelName)) { createWebView(parentWindow, completionHandler); } @@ -42,6 +43,20 @@ namespace flutter_inappwebview_plugin webViewController->get_CoreWebView2(webView.put()); } + wil::com_ptr webView2Settings; + if (SUCCEEDED(webView->get_Settings(&webView2Settings))) { + webView2Settings->put_IsScriptEnabled(settings->javaScriptEnabled); + webView2Settings->put_IsZoomControlEnabled(settings->supportZoom); + webView2Settings->put_IsStatusBarEnabled(true); + + wil::com_ptr webView2Settings2; + if (SUCCEEDED(webView2Settings->QueryInterface(IID_PPV_ARGS(&webView2Settings2)))) { + if (!settings->userAgent.empty()) { + webView2Settings2->put_UserAgent(ansi_to_wide(settings->userAgent).c_str()); + } + } + } + // Resize WebView to fit the bounds of the parent window RECT bounds; GetClientRect(parentWindow, &bounds); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index 2043a469..d8fa450e 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -8,12 +8,18 @@ #include "../flutter_inappwebview_windows_plugin.h" #include "../types/navigation_action.h" #include "../types/url_request.h" +#include "in_app_webview_settings.h" #include "webview_channel_delegate.h" namespace flutter_inappwebview_plugin { using namespace Microsoft::WRL; + struct InAppWebViewCreationParams { + const std::variant id; + const std::shared_ptr initialSettings; + }; + class InAppWebView { public: @@ -26,9 +32,10 @@ namespace flutter_inappwebview_plugin wil::com_ptr webView; const std::unique_ptr channelDelegate; std::map> navigationActions = {}; + const std::shared_ptr settings; - InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::function completionHandler); - InAppWebView(FlutterInappwebviewWindowsPlugin* plugin, const std::variant& id, const HWND parentWindow, const std::string& channelName, const std::function completionHandler); + InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, const std::function completionHandler); + InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, const std::string& channelName, const std::function completionHandler); ~InAppWebView(); std::optional getUrl() const; diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.cpp new file mode 100644 index 00000000..c66561dc --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.cpp @@ -0,0 +1,24 @@ +#include "../utils/flutter.h" +#include "in_app_webview_settings.h" + +namespace flutter_inappwebview_plugin +{ + InAppWebViewSettings::InAppWebViewSettings() {}; + + InAppWebViewSettings::InAppWebViewSettings(const flutter::EncodableMap& encodableMap) + { + useShouldOverrideUrlLoading = get_fl_map_value(encodableMap, "useShouldOverrideUrlLoading", useShouldOverrideUrlLoading); + useOnLoadResource = get_fl_map_value(encodableMap, "useOnLoadResource", useOnLoadResource); + useOnDownloadStart = get_fl_map_value(encodableMap, "useOnDownloadStart", useOnDownloadStart); + userAgent = get_fl_map_value(encodableMap, "userAgent", userAgent); + javaScriptEnabled = get_fl_map_value(encodableMap, "javaScriptEnabled", javaScriptEnabled); + resourceCustomSchemes = get_fl_map_value(encodableMap, "resourceCustomSchemes", resourceCustomSchemes); + transparentBackground = get_fl_map_value(encodableMap, "transparentBackground", transparentBackground); + supportZoom = get_fl_map_value(encodableMap, "supportZoom", supportZoom); + } + + InAppWebViewSettings::~InAppWebViewSettings() + { + debugLog("dealloc InAppWebViewSettings"); + } +} diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.h new file mode 100644 index 00000000..ad74f450 --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.h @@ -0,0 +1,26 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_SETTINGS_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_SETTINGS_H_ + +#include +#include + +namespace flutter_inappwebview_plugin +{ + class InAppWebViewSettings + { + public: + bool useShouldOverrideUrlLoading = false; + bool useOnLoadResource = false; + bool useOnDownloadStart = false; + std::string userAgent; + bool javaScriptEnabled = true; + std::vector resourceCustomSchemes; + bool transparentBackground = false; + bool supportZoom = true; + + InAppWebViewSettings(); + InAppWebViewSettings(const flutter::EncodableMap& encodableMap); + ~InAppWebViewSettings(); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_SETTINGS_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index cb6d4f24..c6da13e6 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -55,7 +55,7 @@ namespace flutter_inappwebview_plugin } auto arguments = std::make_unique(flutter::EncodableMap{ - {flutter::EncodableValue("url"), make_fl_value(url)}, + {"url", make_fl_value(url)}, }); channel->InvokeMethod("onLoadStart", std::move(arguments)); } @@ -67,7 +67,7 @@ namespace flutter_inappwebview_plugin } auto arguments = std::make_unique(flutter::EncodableMap{ - {flutter::EncodableValue("url"), make_fl_value(url)}, + {"url", make_fl_value(url)}, }); channel->InvokeMethod("onLoadStop", std::move(arguments)); } @@ -89,8 +89,8 @@ namespace flutter_inappwebview_plugin } auto arguments = std::make_unique(flutter::EncodableMap{ - {flutter::EncodableValue("request"), request->toEncodableMap()}, - {flutter::EncodableValue("error"), error->toEncodableMap()}, + {"request", request->toEncodableMap()}, + {"error", error->toEncodableMap()}, }); channel->InvokeMethod("onReceivedError", std::move(arguments)); } @@ -102,8 +102,8 @@ namespace flutter_inappwebview_plugin } auto arguments = std::make_unique(flutter::EncodableMap{ - {flutter::EncodableValue("request"), request->toEncodableMap()}, - {flutter::EncodableValue("errorResponse"), errorResponse->toEncodableMap()}, + {"request", request->toEncodableMap()}, + {"errorResponse", errorResponse->toEncodableMap()}, }); channel->InvokeMethod("onReceivedHttpError", std::move(arguments)); } diff --git a/flutter_inappwebview_windows/windows/types/navigation_action.cpp b/flutter_inappwebview_windows/windows/types/navigation_action.cpp index 680d0483..4e499490 100644 --- a/flutter_inappwebview_windows/windows/types/navigation_action.cpp +++ b/flutter_inappwebview_windows/windows/types/navigation_action.cpp @@ -10,8 +10,8 @@ namespace flutter_inappwebview_plugin flutter::EncodableMap NavigationAction::toEncodableMap() const { return flutter::EncodableMap{ - {make_fl_value("request"), request->toEncodableMap()}, - {make_fl_value("isForMainFrame"), make_fl_value(isForMainFrame)} + {"request", request->toEncodableMap()}, + {"isForMainFrame", make_fl_value(isForMainFrame)} }; } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/url_request.cpp b/flutter_inappwebview_windows/windows/types/url_request.cpp index ec997b24..42340dd0 100644 --- a/flutter_inappwebview_windows/windows/types/url_request.cpp +++ b/flutter_inappwebview_windows/windows/types/url_request.cpp @@ -11,17 +11,17 @@ namespace flutter_inappwebview_plugin URLRequest::URLRequest(const flutter::EncodableMap& map) : url(get_optional_fl_map_value(map, "url")), method(get_optional_fl_map_value(map, "method")), - headers(get_optional_fl_map_value(map, "headers")), + headers(get_optional_fl_map_value>(map, "headers")), body(get_optional_fl_map_value>(map, "body")) {} flutter::EncodableMap URLRequest::toEncodableMap() const { return flutter::EncodableMap{ - {make_fl_value("url"), make_fl_value(url)}, - {make_fl_value("method"), make_fl_value(method)}, - {make_fl_value("headers"), make_fl_value(headers)}, - {make_fl_value("body"), make_fl_value(body)} + {"url", make_fl_value(url)}, + {"method", make_fl_value(method)}, + {"headers", make_fl_value(headers)}, + {"body", make_fl_value(body)} }; } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_error.cpp b/flutter_inappwebview_windows/windows/types/web_resource_error.cpp index 107fd682..f3fb3a36 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_error.cpp +++ b/flutter_inappwebview_windows/windows/types/web_resource_error.cpp @@ -15,8 +15,8 @@ namespace flutter_inappwebview_plugin flutter::EncodableMap WebResourceError::toEncodableMap() const { return flutter::EncodableMap{ - {make_fl_value("description"), make_fl_value(description)}, - {make_fl_value("type"), make_fl_value(type)} + {"description", make_fl_value(description)}, + {"type", make_fl_value(type)} }; } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_request.cpp b/flutter_inappwebview_windows/windows/types/web_resource_request.cpp index 3ab71d93..b7074207 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_request.cpp +++ b/flutter_inappwebview_windows/windows/types/web_resource_request.cpp @@ -11,17 +11,17 @@ namespace flutter_inappwebview_plugin WebResourceRequest::WebResourceRequest(const flutter::EncodableMap& map) : url(get_optional_fl_map_value(map, "url")), method(get_optional_fl_map_value(map, "method")), - headers(get_optional_fl_map_value(map, "headers")), + headers(get_optional_fl_map_value>(map, "headers")), isForMainFrame(get_optional_fl_map_value(map, "isForMainFrame")) {} flutter::EncodableMap WebResourceRequest::toEncodableMap() const { return flutter::EncodableMap{ - {make_fl_value("url"), make_fl_value(url)}, - {make_fl_value("method"), make_fl_value(method)}, - {make_fl_value("headers"), make_fl_value(headers)}, - {make_fl_value("isForMainFrame"), make_fl_value(isForMainFrame)} + {"url", make_fl_value(url)}, + {"method", make_fl_value(method)}, + {"headers", make_fl_value(headers)}, + {"isForMainFrame", make_fl_value(isForMainFrame)} }; } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_response.cpp b/flutter_inappwebview_windows/windows/types/web_resource_response.cpp index 934b94fb..5f76dc9c 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_response.cpp +++ b/flutter_inappwebview_windows/windows/types/web_resource_response.cpp @@ -14,7 +14,7 @@ namespace flutter_inappwebview_plugin flutter::EncodableMap WebResourceResponse::toEncodableMap() const { return flutter::EncodableMap{ - {make_fl_value("statusCode"), make_fl_value(statusCode)} + {"statusCode", make_fl_value(statusCode)} }; } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/flutter.h b/flutter_inappwebview_windows/windows/utils/flutter.h index d539333d..517a0075 100644 --- a/flutter_inappwebview_windows/windows/utils/flutter.h +++ b/flutter_inappwebview_windows/windows/utils/flutter.h @@ -2,8 +2,6 @@ #define FLUTTER_INAPPWEBVIEW_PLUGIN_FLUTTER_UTIL_H_ #include -#include -#include #include "util.h" @@ -86,25 +84,66 @@ namespace flutter_inappwebview_plugin return std::get(map.at(make_fl_value(string))); } - template + template::value && !is_vector::value) || + std::is_same::value || std::is_same::value), int>::type* = nullptr> static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) { return make_pointer_optional(std::get_if(&map.at(make_fl_value(string)))); } - template - static inline std::optional> get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) + template + static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* string, const T& defaultValue) { + auto optional = get_optional_fl_map_value(map, string); + return !optional.has_value() ? defaultValue : optional.value(); + } + + template::value && !std::is_same::value)>::type* = nullptr> + static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) + { + using K = typename T::key_type; + using V = typename T::mapped_type; + auto flMap = std::get_if(&map.at(make_fl_value(string))); if (flMap) { - auto mapValue = std::map{}; + T mapValue = {}; for (auto itr = flMap->begin(); itr != flMap->end(); itr++) { - mapValue.insert({ std::get(itr->first), std::get(itr->second) }); + mapValue.insert({ std::get(itr->first), std::get(itr->second) }); } - return make_pointer_optional>(&mapValue); + return make_pointer_optional(&mapValue); } return std::nullopt; } + + template + static inline std::map get_fl_map_value(const flutter::EncodableMap& map, const char* string, const std::map& defaultValue) + { + auto optional = get_optional_fl_map_value>(map, string); + return !optional.has_value() ? defaultValue : optional.value(); + } + + template::value && !std::is_same::value), bool>::type* = nullptr> + static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) + { + using V = typename T::value_type; + + auto flList = std::get_if(&map.at(make_fl_value(string))); + if (flList) { + T vecValue(flList->size()); + for (auto itr = flList->begin(); itr != flList->end(); itr++) { + vecValue.push_back(std::get(*itr)); + } + return make_pointer_optional(&vecValue); + } + return std::nullopt; + } + + template + static inline std::vector get_fl_map_value(const flutter::EncodableMap& map, const char* string, const std::vector& defaultValue) + { + auto optional = get_optional_fl_map_value>(map, string); + return !optional.has_value() ? defaultValue : optional.value(); + } } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_FLUTTER_UTIL_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/util.h b/flutter_inappwebview_windows/windows/utils/util.h index e619a246..2513ddd2 100644 --- a/flutter_inappwebview_windows/windows/utils/util.h +++ b/flutter_inappwebview_windows/windows/utils/util.h @@ -12,6 +12,34 @@ namespace flutter_inappwebview_plugin { + template + struct is_vector_impl : std::false_type { }; + + template + struct is_mappish_impl : std::false_type { }; + + template + struct is_vector_impl>::value> + > : std::true_type { }; + + template + struct is_vector_impl::value_type>::iterator>::value> + > : std::true_type { }; + + template + struct is_mappish_impl()[std::declval()])>> + : std::true_type { }; + + template + struct is_mappish : is_mappish_impl::type { }; + + template + struct is_vector : is_vector_impl::type { }; + static inline void debugLog(const std::string& msg) { #ifndef NDEBUG From 914316e6d35ec41f3ac08020370afaa0700978ce Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 16 Jan 2024 03:18:59 +0100 Subject: [PATCH 06/21] windows: added inappwebview widget support, added some other methods --- flutter_inappwebview/example/lib/main.dart | 62 +-- .../platform_in_app_browser.dart | 5 + .../platform_inappwebview_controller.dart | 9 +- .../platform_inappwebview_widget.dart | 1 + .../lib/src/cookie_manager.dart | 4 +- .../in_app_webview/custom_platform_view.dart | 414 ++++++++++++++ .../headless_in_app_webview.dart | 28 +- .../src/in_app_webview/in_app_webview.dart | 34 +- .../in_app_webview_controller.dart | 9 +- .../lib/src/inappwebview_platform.dart | 34 +- .../windows/CMakeLists.txt | 45 +- .../custom_platform_view.cc | 312 +++++++++++ .../custom_platform_view.h | 58 ++ .../custom_platform_view/graphics_context.cc | 158 ++++++ .../custom_platform_view/graphics_context.h | 54 ++ .../custom_platform_view/texture_bridge.cc | 189 +++++++ .../custom_platform_view/texture_bridge.h | 79 +++ .../texture_bridge_fallback.cc | 145 +++++ .../texture_bridge_fallback.h | 30 + .../texture_bridge_gpu.cc | 108 ++++ .../custom_platform_view/texture_bridge_gpu.h | 29 + .../util/composition.desktop.interop.h | 22 + .../util/cpuid/cpuinfo.cc | 80 +++ .../custom_platform_view/util/cpuid/cpuinfo.h | 105 ++++ .../util/cpuid/detail/cpuinfo_impl.h | 69 +++ .../util/cpuid/detail/extract_x86_flags.h | 43 ++ .../util/cpuid/detail/init_msvc_x86.h | 36 ++ .../util/cpuid/detail/init_unknown.h | 9 + .../custom_platform_view/util/d3dutil.h | 31 ++ .../util/direct3d11.interop.cc | 47 ++ .../util/direct3d11.interop.h | 51 ++ .../custom_platform_view/util/rohelper.cc | 244 ++++++++ .../custom_platform_view/util/rohelper.h | 97 ++++ .../util/string_converter.cc | 54 ++ .../util/string_converter.h | 8 + .../custom_platform_view/util/swizzle.h | 192 +++++++ .../flutter_inappwebview_windows_plugin.cpp | 6 + .../flutter_inappwebview_windows_plugin.h | 2 + .../windows/in_app_browser/in_app_browser.cpp | 57 +- .../windows/in_app_browser/in_app_browser.h | 7 + .../in_app_browser/in_app_browser_manager.cpp | 4 +- .../in_app_browser/in_app_browser_manager.h | 2 +- .../windows/in_app_webview/in_app_webview.cpp | 522 ++++++++++++++++-- .../windows/in_app_webview/in_app_webview.h | 115 +++- .../in_app_webview/in_app_webview_manager.cpp | 162 ++++++ .../in_app_webview/in_app_webview_manager.h | 58 ++ .../webview_channel_delegate.cpp | 57 +- .../in_app_webview/webview_channel_delegate.h | 5 +- .../windows/utils/util.h | 9 +- 49 files changed, 3735 insertions(+), 166 deletions(-) create mode 100644 flutter_inappwebview_windows/lib/src/in_app_webview/custom_platform_view.dart create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.cc create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/graphics_context.cc create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/graphics_context.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge.cc create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_fallback.cc create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_fallback.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_gpu.cc create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_gpu.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/composition.desktop.interop.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/cpuinfo.cc create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/cpuinfo.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/cpuinfo_impl.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/extract_x86_flags.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/init_msvc_x86.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/init_unknown.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/d3dutil.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/direct3d11.interop.cc create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/direct3d11.interop.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/rohelper.cc create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/rohelper.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/string_converter.cc create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/string_converter.h create mode 100644 flutter_inappwebview_windows/windows/custom_platform_view/util/swizzle.h create mode 100644 flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp create mode 100644 flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.h diff --git a/flutter_inappwebview/example/lib/main.dart b/flutter_inappwebview/example/lib/main.dart index da75e5b6..d94bc1b5 100755 --- a/flutter_inappwebview/example/lib/main.dart +++ b/flutter_inappwebview/example/lib/main.dart @@ -71,41 +71,6 @@ PointerInterceptor myDrawer({required BuildContext context}) { }, ) ]; - } else if (defaultTargetPlatform == TargetPlatform.macOS || - defaultTargetPlatform == TargetPlatform.windows || - defaultTargetPlatform == TargetPlatform.linux) { - children = [ - // ListTile( - // title: Text('InAppWebView'), - // onTap: () { - // Navigator.pushReplacementNamed(context, '/'); - // }, - // ), - // ListTile( - // title: Text('InAppBrowser'), - // onTap: () { - // Navigator.pushReplacementNamed(context, '/InAppBrowser'); - // }, - // ), - ListTile( - title: Text('InAppBrowser'), - onTap: () { - Navigator.pushReplacementNamed(context, '/'); - }, - ), - ListTile( - title: Text('WebAuthenticationSession'), - onTap: () { - Navigator.pushReplacementNamed(context, '/WebAuthenticationSession'); - }, - ), - ListTile( - title: Text('HeadlessInAppWebView'), - onTap: () { - Navigator.pushReplacementNamed(context, '/HeadlessInAppWebView'); - }, - ), - ]; } else if (defaultTargetPlatform == TargetPlatform.macOS) { children = [ // ListTile( @@ -142,22 +107,16 @@ PointerInterceptor myDrawer({required BuildContext context}) { } else if (defaultTargetPlatform == TargetPlatform.windows || defaultTargetPlatform == TargetPlatform.linux) { children = [ - // ListTile( - // title: Text('InAppWebView'), - // onTap: () { - // Navigator.pushReplacementNamed(context, '/'); - // }, - // ), - // ListTile( - // title: Text('InAppBrowser'), - // onTap: () { - // Navigator.pushReplacementNamed(context, '/InAppBrowser'); - // }, - // ), + ListTile( + title: Text('InAppWebView'), + onTap: () { + Navigator.pushReplacementNamed(context, '/'); + }, + ), ListTile( title: Text('InAppBrowser'), onTap: () { - Navigator.pushReplacementNamed(context, '/'); + Navigator.pushReplacementNamed(context, '/InAppBrowser'); }, ), ListTile( @@ -219,12 +178,11 @@ class _MyAppState extends State { '/WebAuthenticationSession': (context) => WebAuthenticationSessionExampleScreen(), }); - } else if (defaultTargetPlatform == TargetPlatform.windows || + } else if (defaultTargetPlatform == TargetPlatform.windows || defaultTargetPlatform == TargetPlatform.linux) { return MaterialApp(initialRoute: '/', routes: { - // '/': (context) => InAppWebViewExampleScreen(), - // '/InAppBrowser': (context) => InAppBrowserExampleScreen(), - '/': (context) => InAppBrowserExampleScreen(), + '/': (context) => InAppWebViewExampleScreen(), + '/InAppBrowser': (context) => InAppBrowserExampleScreen(), '/HeadlessInAppWebView': (context) => HeadlessInAppWebViewExampleScreen(), }); diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart index aa9b525b..acf5da2d 100755 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart @@ -372,6 +372,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future show() { throw UnimplementedError('show is not implemented on the current platform'); @@ -384,6 +385,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future hide() { throw UnimplementedError('hide is not implemented on the current platform'); @@ -396,6 +398,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future close() { throw UnimplementedError( @@ -409,6 +412,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future isHidden() { throw UnimplementedError( @@ -964,6 +968,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ([Official API - WebChromeClient.onReceivedTitle](https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTitle(android.webkit.WebView,%20java.lang.String))) ///- iOS ///- MacOS + ///- Windows ([Official API - IWebView2WebView.add_DocumentTitleChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_documenttitlechanged)) void onTitleChanged(String? title) {} ///Event fired to respond to the results of an over-scroll operation. diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart index f9ce118b..a8f2d9ba 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart @@ -134,7 +134,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.url](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url)) ///- MacOS ([Official API - WKWebView.url](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url)) ///- Web - ///- Windows ([Official API - IWebView2WebView.get_Source](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#get_source)) + ///- Windows ([Official API - IWebView2WebView.get_Source](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_source)) ///{@endtemplate} Future getUrl() { throw UnimplementedError( @@ -151,6 +151,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.title](https://developer.apple.com/documentation/webkit/wkwebview/1415015-title)) ///- MacOS ([Official API - WKWebView.title](https://developer.apple.com/documentation/webkit/wkwebview/1415015-title)) ///- Web + ///- Windows ([Official API - IWebView2WebView.get_DocumentTitle](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_documenttitle)) ///{@endtemplate} Future getTitle() { throw UnimplementedError( @@ -345,6 +346,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.reload](https://developer.apple.com/documentation/webkit/wkwebview/1414969-reload)) ///- MacOS ([Official API - WKWebView.reload](https://developer.apple.com/documentation/webkit/wkwebview/1414969-reload)) ///- Web ([Official API - Location.reload](https://developer.mozilla.org/en-US/docs/Web/API/Location/reload)) + ///- Windows ([Official API - IWebView2WebView.Reload](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#reload)) ///{@endtemplate} Future reload() { throw UnimplementedError( @@ -361,6 +363,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.goBack](https://developer.apple.com/documentation/webkit/wkwebview/1414952-goback)) ///- MacOS ([Official API - WKWebView.goBack](https://developer.apple.com/documentation/webkit/wkwebview/1414952-goback)) ///- Web ([Official API - History.back](https://developer.mozilla.org/en-US/docs/Web/API/History/back)) + ///- Windows ([Official API - IWebView2WebView.GoBack](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#goback)) ///{@endtemplate} Future goBack() { throw UnimplementedError( @@ -390,6 +393,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.goForward](https://developer.apple.com/documentation/webkit/wkwebview/1414993-goforward)) ///- MacOS ([Official API - WKWebView.goForward](https://developer.apple.com/documentation/webkit/wkwebview/1414993-goforward)) ///- Web ([Official API - History.forward](https://developer.mozilla.org/en-US/docs/Web/API/History/forward)) + ///- Windows ([Official API - IWebView2WebView.GoForward](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#goforward)) ///{@endtemplate} Future goForward() { throw UnimplementedError( @@ -492,7 +496,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///Those changes remain visible to all scripts, regardless of which content world you specify. ///For more information about content worlds, see [ContentWorld]. ///Available on iOS 14.0+ and MacOS 11.0+. - ///**NOTE**: not used on Web. + ///**NOTE**: not used on Web and on Windows platforms. /// ///**NOTE**: This method shouldn't be called in the [PlatformWebViewCreationParams.onWebViewCreated] or [PlatformWebViewCreationParams.onLoadStart] events, ///because, in these events, the `WebView` is not ready to handle it yet. @@ -506,6 +510,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.evaluateJavascript](https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript)) ///- MacOS ([Official API - WKWebView.evaluateJavascript](https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript)) ///- Web ([Official API - Window.eval](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval?retiredLocale=it)) + ///- Windows ([Official API - IWebView2WebView.ExecuteScript](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#executescript)) ///{@endtemplate} Future evaluateJavascript( {required String source, ContentWorld? contentWorld}) { diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart index 6f926191..c79090a6 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart @@ -192,6 +192,7 @@ class PlatformInAppWebViewWidgetCreationParams ///- Android native WebView ///- iOS ///- Web +///- Windows ///{@endtemplate} abstract class PlatformInAppWebViewWidget extends PlatformInterface implements Disposable { diff --git a/flutter_inappwebview_windows/lib/src/cookie_manager.dart b/flutter_inappwebview_windows/lib/src/cookie_manager.dart index bf9d5a9b..a95dc409 100644 --- a/flutter_inappwebview_windows/lib/src/cookie_manager.dart +++ b/flutter_inappwebview_windows/lib/src/cookie_manager.dart @@ -152,7 +152,7 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { final setCookieCompleter = Completer(); final headlessWebView = - MacOSHeadlessInAppWebView(MacOSHeadlessInAppWebViewCreationParams( + WindowsHeadlessInAppWebView(WindowsHeadlessInAppWebViewCreationParams( initialUrlRequest: URLRequest(url: url), onLoadStop: (controller, url) async { await controller.evaluateJavascript( @@ -234,7 +234,7 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { final pageLoaded = Completer(); final headlessWebView = - MacOSHeadlessInAppWebView(MacOSHeadlessInAppWebViewCreationParams( + WindowsHeadlessInAppWebView(WindowsHeadlessInAppWebViewCreationParams( initialUrlRequest: URLRequest(url: url), onLoadStop: (controller, url) async { pageLoaded.complete(); diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/custom_platform_view.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/custom_platform_view.dart new file mode 100644 index 00000000..1f41ab12 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/custom_platform_view.dart @@ -0,0 +1,414 @@ +import 'package:flutter/services.dart'; +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +const Map _cursors = { + 'none': SystemMouseCursors.none, + 'basic': SystemMouseCursors.basic, + 'click': SystemMouseCursors.click, + 'forbidden': SystemMouseCursors.forbidden, + 'wait': SystemMouseCursors.wait, + 'progress': SystemMouseCursors.progress, + 'contextMenu': SystemMouseCursors.contextMenu, + 'help': SystemMouseCursors.help, + 'text': SystemMouseCursors.text, + 'verticalText': SystemMouseCursors.verticalText, + 'cell': SystemMouseCursors.cell, + 'precise': SystemMouseCursors.precise, + 'move': SystemMouseCursors.move, + 'grab': SystemMouseCursors.grab, + 'grabbing': SystemMouseCursors.grabbing, + 'noDrop': SystemMouseCursors.noDrop, + 'alias': SystemMouseCursors.alias, + 'copy': SystemMouseCursors.copy, + 'disappearing': SystemMouseCursors.disappearing, + 'allScroll': SystemMouseCursors.allScroll, + 'resizeLeftRight': SystemMouseCursors.resizeLeftRight, + 'resizeUpDown': SystemMouseCursors.resizeUpDown, + 'resizeUpLeftDownRight': SystemMouseCursors.resizeUpLeftDownRight, + 'resizeUpRightDownLeft': SystemMouseCursors.resizeUpRightDownLeft, + 'resizeUp': SystemMouseCursors.resizeUp, + 'resizeDown': SystemMouseCursors.resizeDown, + 'resizeLeft': SystemMouseCursors.resizeLeft, + 'resizeRight': SystemMouseCursors.resizeRight, + 'resizeUpLeft': SystemMouseCursors.resizeUpLeft, + 'resizeUpRight': SystemMouseCursors.resizeUpRight, + 'resizeDownLeft': SystemMouseCursors.resizeDownLeft, + 'resizeDownRight': SystemMouseCursors.resizeDownRight, + 'resizeColumn': SystemMouseCursors.resizeColumn, + 'resizeRow': SystemMouseCursors.resizeRow, + 'zoomIn': SystemMouseCursors.zoomIn, + 'zoomOut': SystemMouseCursors.zoomOut, +}; + +SystemMouseCursor _getCursorByName(String name) => + _cursors[name] ?? SystemMouseCursors.basic; + +/// Pointer button type +// Order must match InAppWebViewPointerEventKind (see in_app_webview.h) +enum PointerButton { none, primary, secondary, tertiary } + +/// Pointer Event kind +// Order must match InAppWebViewPointerEventKind (see in_app_webview.h) +enum InAppWebViewPointerEventKind { activate, down, enter, leave, up, update } + +/// Attempts to translate a button constant such as [kPrimaryMouseButton] +/// to a [PointerButton] +PointerButton _getButton(int value) { + switch (value) { + case kPrimaryMouseButton: + return PointerButton.primary; + case kSecondaryMouseButton: + return PointerButton.secondary; + case kTertiaryButton: + return PointerButton.tertiary; + default: + return PointerButton.none; + } +} + +const String _pluginChannelPrefix = 'com.pichillilorenzo/flutter_inappwebview'; +const MethodChannel _pluginChannel = MethodChannel(_pluginChannelPrefix); + +class CustomFlutterViewControllerValue { + const CustomFlutterViewControllerValue({ + required this.isInitialized, + }); + + final bool isInitialized; + + CustomFlutterViewControllerValue copyWith({ + bool? isInitialized, + }) { + return CustomFlutterViewControllerValue( + isInitialized: isInitialized ?? this.isInitialized, + ); + } + + CustomFlutterViewControllerValue.uninitialized() + : this( + isInitialized: false, + ); +} + +/// Controls a WebView and provides streams for various change events. +class CustomPlatformViewController + extends ValueNotifier { + Completer _creatingCompleter = Completer(); + int _textureId = 0; + bool _isDisposed = false; + + Future get ready => _creatingCompleter.future; + + late MethodChannel _methodChannel; + late EventChannel _eventChannel; + StreamSubscription? _eventStreamSubscription; + + final StreamController _cursorStreamController = + StreamController.broadcast(); + + /// A stream reflecting the current cursor style. + Stream get _cursor => _cursorStreamController.stream; + + CustomPlatformViewController() + : super(CustomFlutterViewControllerValue.uninitialized()); + + /// Initializes the underlying platform view. + Future initialize( + {Function(int id)? onPlatformViewCreated, dynamic arguments}) async { + if (_isDisposed) { + return; + } + _textureId = (await _pluginChannel.invokeMethod( + 'createInAppWebView', arguments))!; + + _methodChannel = + MethodChannel('com.pichillilorenzo/custom_platform_view_$_textureId'); + _eventChannel = + EventChannel('com.pichillilorenzo/custom_platform_view_${_textureId}_events'); + _eventStreamSubscription = + _eventChannel.receiveBroadcastStream().listen((event) { + final map = event as Map; + switch (map['type']) { + case 'cursorChanged': + _cursorStreamController.add(_getCursorByName(map['value'])); + break; + } + }); + + _methodChannel.setMethodCallHandler((call) { + throw MissingPluginException('Unknown method ${call.method}'); + }); + + value = value.copyWith(isInitialized: true); + + _creatingCompleter.complete(); + + onPlatformViewCreated?.call(_textureId); + } + + @override + Future dispose() async { + await _creatingCompleter.future; + if (!_isDisposed) { + _isDisposed = true; + await _eventStreamSubscription?.cancel(); + await _pluginChannel.invokeMethod('dispose', {"id": _textureId}); + } + super.dispose(); + } + + /// Limits the number of frames per second to the given value. + Future setFpsLimit([int? maxFps = 0]) async { + if (_isDisposed) { + return; + } + assert(value.isInitialized); + return _methodChannel.invokeMethod('setFpsLimit', maxFps); + } + + /// Sends a Pointer (Touch) update + Future _setPointerUpdate(InAppWebViewPointerEventKind kind, int pointer, + Offset position, double size, double pressure) async { + if (_isDisposed) { + return; + } + assert(value.isInitialized); + return _methodChannel.invokeMethod('setPointerUpdate', + [pointer, kind.index, position.dx, position.dy, size, pressure]); + } + + /// Moves the virtual cursor to [position]. + Future _setCursorPos(Offset position) async { + if (_isDisposed) { + return; + } + assert(value.isInitialized); + return _methodChannel + .invokeMethod('setCursorPos', [position.dx, position.dy]); + } + + /// Indicates whether the specified [button] is currently down. + Future _setPointerButtonState(PointerButton button, bool isDown) async { + if (_isDisposed) { + return; + } + assert(value.isInitialized); + return _methodChannel.invokeMethod('setPointerButton', + {'button': button.index, 'isDown': isDown}); + } + + /// Sets the horizontal and vertical scroll delta. + Future _setScrollDelta(double dx, double dy) async { + if (_isDisposed) { + return; + } + assert(value.isInitialized); + return _methodChannel.invokeMethod('setScrollDelta', [dx, dy]); + } + + /// Sets the surface size to the provided [size]. + Future _setSize(Size size, double scaleFactor) async { + if (_isDisposed) { + return; + } + assert(value.isInitialized); + return _methodChannel + .invokeMethod('setSize', [size.width, size.height, scaleFactor]); + } +} + +class CustomPlatformView extends StatefulWidget { + /// An optional scale factor. Defaults to [FlutterView.devicePixelRatio] for + /// rendering in native resolution. + /// Setting this to 1.0 will disable high-DPI support. + /// This should only be needed to mimic old behavior before high-DPI support + /// was available. + final double? scaleFactor; + + /// The [FilterQuality] used for scaling the texture's contents. + /// Defaults to [FilterQuality.none] as this renders in native resolution + /// unless specifying a [scaleFactor]. + final FilterQuality filterQuality; + + final dynamic creationParams; + + final Function(int id)? onPlatformViewCreated; + + const CustomPlatformView( + {this.creationParams, + this.onPlatformViewCreated, + this.scaleFactor, + this.filterQuality = FilterQuality.none}); + + @override + _CustomPlatformViewState createState() => _CustomPlatformViewState(); +} + +class _CustomPlatformViewState extends State { + final GlobalKey _key = GlobalKey(); + final _downButtons = {}; + + PointerDeviceKind _pointerKind = PointerDeviceKind.unknown; + + MouseCursor _cursor = SystemMouseCursors.basic; + + final _controller = CustomPlatformViewController(); + final _focusNode = FocusNode(); + + StreamSubscription? _cursorSubscription; + + @override + void initState() { + super.initState(); + + _controller.initialize( + onPlatformViewCreated: (id) { + widget.onPlatformViewCreated?.call(id); + setState(() {}); + }, + arguments: widget.creationParams); + + // Report initial surface size + WidgetsBinding.instance.addPostFrameCallback((_) => _reportSurfaceSize()); + + _cursorSubscription = _controller._cursor.listen((cursor) { + setState(() { + _cursor = cursor; + }); + }); + } + + @override + Widget build(BuildContext context) { + return Focus( + autofocus: true, + focusNode: _focusNode, + canRequestFocus: true, + debugLabel: "flutter_inappwebview_windows_custom_platform_view", + onFocusChange: (focused) { + + }, + child: SizedBox.expand(key: _key, child: _buildInner()), + ); + } + + Widget _buildInner() { + return NotificationListener( + onNotification: (notification) { + _reportSurfaceSize(); + return true; + }, + child: SizeChangedLayoutNotifier( + child: _controller.value.isInitialized + ? Listener( + onPointerHover: (ev) { + // ev.kind is for whatever reason not set to touch + // even on touch input + if (_pointerKind == PointerDeviceKind.touch) { + // Ignoring hover events on touch for now + return; + } + _controller._setCursorPos(ev.localPosition); + }, + onPointerDown: (ev) { + if (!_focusNode.hasFocus) { + _focusNode.requestFocus(); + Future.delayed(const Duration(milliseconds: 50), () { + if (!_focusNode.hasFocus) { + _focusNode.requestFocus(); + } + }); + } + + _pointerKind = ev.kind; + if (ev.kind == PointerDeviceKind.touch) { + _controller._setPointerUpdate( + InAppWebViewPointerEventKind.down, + ev.pointer, + ev.localPosition, + ev.size, + ev.pressure); + return; + } + final button = _getButton(ev.buttons); + _downButtons[ev.pointer] = button; + _controller._setPointerButtonState(button, true); + }, + onPointerUp: (ev) { + _pointerKind = ev.kind; + if (ev.kind == PointerDeviceKind.touch) { + _controller._setPointerUpdate( + InAppWebViewPointerEventKind.up, + ev.pointer, + ev.localPosition, + ev.size, + ev.pressure); + return; + } + final button = _downButtons.remove(ev.pointer); + if (button != null) { + _controller._setPointerButtonState(button, false); + } + }, + onPointerCancel: (ev) { + _pointerKind = ev.kind; + final button = _downButtons.remove(ev.pointer); + if (button != null) { + _controller._setPointerButtonState(button, false); + } + }, + onPointerMove: (ev) { + _pointerKind = ev.kind; + if (ev.kind == PointerDeviceKind.touch) { + _controller._setPointerUpdate( + InAppWebViewPointerEventKind.update, + ev.pointer, + ev.localPosition, + ev.size, + ev.pressure); + } else { + _controller._setCursorPos(ev.localPosition); + } + }, + onPointerSignal: (signal) { + if (signal is PointerScrollEvent) { + _controller._setScrollDelta( + -signal.scrollDelta.dx, -signal.scrollDelta.dy); + } + }, + onPointerPanZoomUpdate: (ev) { + _controller._setScrollDelta( + ev.panDelta.dx, ev.panDelta.dy); + }, + child: MouseRegion( + cursor: _cursor, + child: Texture( + textureId: _controller._textureId, + filterQuality: widget.filterQuality, + )), + ) + : const SizedBox())); + } + + void _reportSurfaceSize() async { + final box = _key.currentContext?.findRenderObject() as RenderBox?; + if (box != null) { + await _controller.ready; + unawaited(_controller._setSize( + box.size, widget.scaleFactor ?? window.devicePixelRatio)); + } + } + + @override + void dispose() { + super.dispose(); + _cursorSubscription?.cancel(); + _controller.dispose(); + _focusNode.dispose(); + } +} diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart index 047005db..7354b20b 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart @@ -6,16 +6,16 @@ import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_pla import '../find_interaction/find_interaction_controller.dart'; import 'in_app_webview_controller.dart'; -/// Object specifying creation parameters for creating a [MacOSHeadlessInAppWebView]. +/// Object specifying creation parameters for creating a [WindowsHeadlessInAppWebView]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformHeadlessInAppWebViewCreationParams] for /// more information. @immutable -class MacOSHeadlessInAppWebViewCreationParams +class WindowsHeadlessInAppWebViewCreationParams extends PlatformHeadlessInAppWebViewCreationParams { - /// Creates a new [MacOSHeadlessInAppWebViewCreationParams] instance. - MacOSHeadlessInAppWebViewCreationParams( + /// Creates a new [WindowsHeadlessInAppWebViewCreationParams] instance. + WindowsHeadlessInAppWebViewCreationParams( {super.controllerFromPlatform, super.initialSize, super.windowId, @@ -128,8 +128,8 @@ class MacOSHeadlessInAppWebViewCreationParams super.pullToRefreshController, this.findInteractionController}); - /// Creates a [MacOSHeadlessInAppWebViewCreationParams] instance based on [PlatformHeadlessInAppWebViewCreationParams]. - MacOSHeadlessInAppWebViewCreationParams.fromPlatformHeadlessInAppWebViewCreationParams( + /// Creates a [WindowsHeadlessInAppWebViewCreationParams] instance based on [PlatformHeadlessInAppWebViewCreationParams]. + WindowsHeadlessInAppWebViewCreationParams.fromPlatformHeadlessInAppWebViewCreationParams( PlatformHeadlessInAppWebViewCreationParams params) : this( controllerFromPlatform: params.controllerFromPlatform, @@ -245,7 +245,7 @@ class MacOSHeadlessInAppWebViewCreationParams } ///{@macro flutter_inappwebview_platform_interface.PlatformHeadlessInAppWebView} -class MacOSHeadlessInAppWebView extends PlatformHeadlessInAppWebView +class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView with ChannelController { @override late final String id; @@ -258,12 +258,12 @@ class MacOSHeadlessInAppWebView extends PlatformHeadlessInAppWebView WindowsInAppWebViewController? _webViewController; - /// Constructs a [MacOSHeadlessInAppWebView]. - MacOSHeadlessInAppWebView(PlatformHeadlessInAppWebViewCreationParams params) + /// Constructs a [WindowsHeadlessInAppWebView]. + WindowsHeadlessInAppWebView(PlatformHeadlessInAppWebViewCreationParams params) : super.implementation( - params is MacOSHeadlessInAppWebViewCreationParams + params is WindowsHeadlessInAppWebViewCreationParams ? params - : MacOSHeadlessInAppWebViewCreationParams + : WindowsHeadlessInAppWebViewCreationParams .fromPlatformHeadlessInAppWebViewCreationParams(params), ) { id = IdGenerator.generate(); @@ -274,8 +274,8 @@ class MacOSHeadlessInAppWebView extends PlatformHeadlessInAppWebView dynamic _controllerFromPlatform; - MacOSHeadlessInAppWebViewCreationParams get _macosParams => - params as MacOSHeadlessInAppWebViewCreationParams; + WindowsHeadlessInAppWebViewCreationParams get _macosParams => + params as WindowsHeadlessInAppWebViewCreationParams; _init() { _webViewController = WindowsInAppWebViewController( @@ -425,7 +425,7 @@ class MacOSHeadlessInAppWebView extends PlatformHeadlessInAppWebView } } -extension InternalHeadlessInAppWebView on MacOSHeadlessInAppWebView { +extension InternalHeadlessInAppWebView on WindowsHeadlessInAppWebView { Future internalDispose() async { _started = false; _running = false; diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart index c1fe8925..09fac537 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; import 'headless_in_app_webview.dart'; @@ -7,13 +6,15 @@ import 'headless_in_app_webview.dart'; import '../find_interaction/find_interaction_controller.dart'; import 'in_app_webview_controller.dart'; +import 'custom_platform_view.dart'; + /// Object specifying creation parameters for creating a [PlatformInAppWebViewWidget]. /// /// Platform specific implementations can add additional fields by extending /// this class. -class MacOSInAppWebViewWidgetCreationParams +class WindowsInAppWebViewWidgetCreationParams extends PlatformInAppWebViewWidgetCreationParams { - MacOSInAppWebViewWidgetCreationParams( + WindowsInAppWebViewWidgetCreationParams( {super.controllerFromPlatform, super.key, super.layoutDirection, @@ -131,9 +132,9 @@ class MacOSInAppWebViewWidgetCreationParams super.pullToRefreshController, this.findInteractionController}); - /// Constructs a [MacOSInAppWebViewWidgetCreationParams] using a + /// Constructs a [WindowsInAppWebViewWidgetCreationParams] using a /// [PlatformInAppWebViewWidgetCreationParams]. - MacOSInAppWebViewWidgetCreationParams.fromPlatformInAppWebViewWidgetCreationParams( + WindowsInAppWebViewWidgetCreationParams.fromPlatformInAppWebViewWidgetCreationParams( PlatformInAppWebViewWidgetCreationParams params) : this( controllerFromPlatform: params.controllerFromPlatform, @@ -254,25 +255,25 @@ class MacOSInAppWebViewWidgetCreationParams } ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} -class MacOSInAppWebViewWidget extends PlatformInAppWebViewWidget { - /// Constructs a [MacOSInAppWebViewWidget]. +class WindowsInAppWebViewWidget extends PlatformInAppWebViewWidget { + /// Constructs a [WindowsInAppWebViewWidget]. /// ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} - MacOSInAppWebViewWidget(PlatformInAppWebViewWidgetCreationParams params) + WindowsInAppWebViewWidget(PlatformInAppWebViewWidgetCreationParams params) : super.implementation( - params is MacOSInAppWebViewWidgetCreationParams + params is WindowsInAppWebViewWidgetCreationParams ? params - : MacOSInAppWebViewWidgetCreationParams + : WindowsInAppWebViewWidgetCreationParams .fromPlatformInAppWebViewWidgetCreationParams(params), ); - MacOSInAppWebViewWidgetCreationParams get _macosParams => - params as MacOSInAppWebViewWidgetCreationParams; + WindowsInAppWebViewWidgetCreationParams get _macosParams => + params as WindowsInAppWebViewWidgetCreationParams; WindowsInAppWebViewController? _controller; - MacOSHeadlessInAppWebView? get _macosHeadlessInAppWebView => - params.headlessWebView as MacOSHeadlessInAppWebView?; + WindowsHeadlessInAppWebView? get _macosHeadlessInAppWebView => + params.headlessWebView as WindowsHeadlessInAppWebView?; @override Widget build(BuildContext context) { @@ -300,10 +301,8 @@ class MacOSInAppWebViewWidget extends PlatformInAppWebViewWidget { } } - return UiKitView( - viewType: 'com.pichillilorenzo/flutter_inappwebview', + return CustomPlatformView( onPlatformViewCreated: _onPlatformViewCreated, - gestureRecognizers: params.gestureRecognizers, creationParams: { 'initialUrlRequest': params.initialUrlRequest?.toMap(), 'initialFile': params.initialFile, @@ -320,7 +319,6 @@ class MacOSInAppWebViewWidget extends PlatformInAppWebViewWidget { 'keepAliveId': params.keepAlive?.id, 'preventGestureDelay': params.preventGestureDelay }, - creationParamsCodec: const StandardMessageCodec(), ); } diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart index 12d07af4..2878505e 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart @@ -57,7 +57,7 @@ class WindowsInAppWebViewControllerCreationParams } } -///Controls a WebView, such as an [InAppWebView] widget instance, a [MacOSHeadlessInAppWebView] instance or [WindowsInAppBrowser] WebView instance. +///Controls a WebView, such as an [InAppWebView] widget instance, a [WindowsHeadlessInAppWebView] instance or [WindowsInAppBrowser] WebView instance. /// ///If you are using the [InAppWebView] widget, an [InAppWebViewController] instance can be obtained by setting the [InAppWebView.onWebViewCreated] ///callback. Instead, if you are using an [WindowsInAppBrowser] instance, you can get it through the [WindowsInAppBrowser.webViewController] attribute. @@ -1923,6 +1923,13 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController args.putIfAbsent('source', () => source); args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); var data = await channel?.invokeMethod('evaluateJavascript', args); + if (data != null) { + try { + // try to json decode the data coming from JavaScript + // otherwise return it as it is. + data = json.decode(data); + } catch (e) {} + } return data; } diff --git a/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart b/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart index 1cd197d0..a91ade71 100644 --- a/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart +++ b/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart @@ -1,6 +1,8 @@ import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.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'; /// Implementation of [InAppWebViewPlatform] using the WebKit API. class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { @@ -9,6 +11,37 @@ class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { InAppWebViewPlatform.instance = WindowsInAppWebViewPlatform(); } + /// Creates a new [WindowsInAppWebViewController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebViewController] in `flutter_inappwebview` instead. + @override + WindowsInAppWebViewController createPlatformInAppWebViewController( + PlatformInAppWebViewControllerCreationParams params, + ) { + return WindowsInAppWebViewController(params); + } + + /// Creates a new empty [WindowsInAppWebViewController] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebViewController] in `flutter_inappwebview` instead. + @override + WindowsInAppWebViewController createPlatformInAppWebViewControllerStatic() { + return WindowsInAppWebViewController.static(); + } + + /// Creates a new [WindowsInAppWebViewWidget]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [InAppWebView] in `flutter_inappwebview` instead. + @override + WindowsInAppWebViewWidget createPlatformInAppWebViewWidget( + PlatformInAppWebViewWidgetCreationParams params, + ) { + return WindowsInAppWebViewWidget(params); + } + /// Creates a new [WindowsInAppBrowser]. /// /// This function should only be called by the app-facing package. @@ -28,5 +61,4 @@ class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { WindowsInAppBrowser createPlatformInAppBrowserStatic() { return WindowsInAppBrowser.static(); } - } diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt index 20c412dd..e662976f 100644 --- a/flutter_inappwebview_windows/windows/CMakeLists.txt +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -21,16 +21,9 @@ cmake_policy(VERSION 3.14...3.25) # not be changed set(PLUGIN_NAME "flutter_inappwebview_windows_plugin") -set(NUGET_URL https://dist.nuget.org/win-x86-commandline/latest/nuget.exe) - find_program(NUGET nuget) if(NOT NUGET) message(NOTICE "Nuget is not installed.") - set(NUGET ${CMAKE_BINARY_DIR}/nuget.exe) - if (NOT EXISTS ${NUGET}) - message(NOTICE "Attempting to download nuget.") - file(DOWNLOAD ${NUGET_URL} ${NUGET}) - endif() endif() add_custom_target(${PROJECT_NAME}_DEPENDENCIES_DOWNLOAD ALL) @@ -61,10 +54,25 @@ list(APPEND PLUGIN_SOURCES "types/web_resource_request.h" "types/web_resource_response.cpp" "types/web_resource_response.h" + "custom_platform_view/custom_platform_view.cc" + "custom_platform_view/custom_platform_view.h" + "custom_platform_view/texture_bridge.cc" + "custom_platform_view/texture_bridge.h" + "custom_platform_view/graphics_context.cc" + "custom_platform_view/graphics_context.h" + "custom_platform_view/util/direct3d11.interop.cc" + "custom_platform_view/util/direct3d11.interop.h" + "custom_platform_view/util/rohelper.cc" + "custom_platform_view/util/rohelper.h" + "custom_platform_view/util/string_converter.cc" + "custom_platform_view/util/string_converter.h" + "custom_platform_view/util/swizzle.h" "in_app_webview/in_app_webview_settings.cpp" "in_app_webview/in_app_webview_settings.h" "in_app_webview/in_app_webview.cpp" "in_app_webview/in_app_webview.h" + "in_app_webview/in_app_webview_manager.cpp" + "in_app_webview/in_app_webview_manager.h" "in_app_webview/webview_channel_delegate.cpp" "in_app_webview/webview_channel_delegate.h" "in_app_browser/in_app_browser_settings.cpp" @@ -85,6 +93,29 @@ add_library(${PLUGIN_NAME} SHARED ${PLUGIN_SOURCES} ) +if(NOT FLUTTER_WEBVIEW_WINDOWS_USE_TEXTURE_FALLBACK) + message(STATUS "Building with D3D texture support.") + target_compile_definitions("${PLUGIN_NAME}" PRIVATE + HAVE_FLUTTER_D3D_TEXTURE + ) + target_sources("${PLUGIN_NAME}" PRIVATE + "custom_platform_view/texture_bridge_gpu.cc" + "custom_platform_view/texture_bridge_gpu.h" + ) +else() + message(STATUS "Building with fallback PixelBuffer texture.") + target_sources("${PLUGIN_NAME}" PRIVATE + "custom_platform_view/texture_bridge_fallback.cc" + "custom_platform_view/texture_bridge_fallback.h" + "custom_platform_view/util/cpuid/cpuinfo.cc" + "custom_platform_view/util/cpuid/cpuinfo.h" + ) + # Enable AVX2 for pixel buffer conversions + if(MSVC) + target_compile_options(${PLUGIN_NAME} PRIVATE "/arch:AVX2") + endif() +endif() + # Apply a standard set of build settings that are configured in the # application-level CMakeLists.txt. This can be removed for plugins that want # full control over build settings. diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.cc b/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.cc new file mode 100644 index 00000000..e8de9cc4 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.cc @@ -0,0 +1,312 @@ +#include "custom_platform_view.h" + +#include +#include + +#ifdef HAVE_FLUTTER_D3D_TEXTURE +#include "texture_bridge_gpu.h" +#else +#include "texture_bridge_fallback.h" +#endif + +namespace flutter_inappwebview_plugin +{ + constexpr auto kErrorInvalidArgs = "invalidArguments"; + + constexpr auto kMethodSetSize = "setSize"; + constexpr auto kMethodSetCursorPos = "setCursorPos"; + constexpr auto kMethodSetPointerUpdate = "setPointerUpdate"; + constexpr auto kMethodSetPointerButton = "setPointerButton"; + constexpr auto kMethodSetScrollDelta = "setScrollDelta"; + constexpr auto kMethodSetFpsLimit = "setFpsLimit"; + + constexpr auto kEventType = "type"; + constexpr auto kEventValue = "value"; + + static const std::optional> GetPointFromArgs( + const flutter::EncodableValue* args) + { + const flutter::EncodableList* list = + std::get_if(args); + if (!list || list->size() != 2) { + return std::nullopt; + } + const auto x = std::get_if(&(*list)[0]); + const auto y = std::get_if(&(*list)[1]); + if (!x || !y) { + return std::nullopt; + } + return std::make_pair(*x, *y); + } + + static const std::optional> + GetPointAndScaleFactorFromArgs(const flutter::EncodableValue* args) + { + const flutter::EncodableList* list = + std::get_if(args); + if (!list || list->size() != 3) { + return std::nullopt; + } + const auto x = std::get_if(&(*list)[0]); + const auto y = std::get_if(&(*list)[1]); + const auto z = std::get_if(&(*list)[2]); + if (!x || !y || !z) { + return std::nullopt; + } + return std::make_tuple(*x, *y, *z); + } + + static const std::string& GetCursorName(const HCURSOR cursor) + { + // The cursor names correspond to the Flutter Engine names: + // in shell/platform/windows/flutter_window_win32.cc + static const std::string kDefaultCursorName = "basic"; + static const std::pair mappings[] = { + {"allScroll", IDC_SIZEALL}, + {kDefaultCursorName, IDC_ARROW}, + {"click", IDC_HAND}, + {"forbidden", IDC_NO}, + {"help", IDC_HELP}, + {"move", IDC_SIZEALL}, + {"none", nullptr}, + {"noDrop", IDC_NO}, + {"precise", IDC_CROSS}, + {"progress", IDC_APPSTARTING}, + {"text", IDC_IBEAM}, + {"resizeColumn", IDC_SIZEWE}, + {"resizeDown", IDC_SIZENS}, + {"resizeDownLeft", IDC_SIZENESW}, + {"resizeDownRight", IDC_SIZENWSE}, + {"resizeLeft", IDC_SIZEWE}, + {"resizeLeftRight", IDC_SIZEWE}, + {"resizeRight", IDC_SIZEWE}, + {"resizeRow", IDC_SIZENS}, + {"resizeUp", IDC_SIZENS}, + {"resizeUpDown", IDC_SIZENS}, + {"resizeUpLeft", IDC_SIZENWSE}, + {"resizeUpRight", IDC_SIZENESW}, + {"resizeUpLeftDownRight", IDC_SIZENWSE}, + {"resizeUpRightDownLeft", IDC_SIZENESW}, + {"wait", IDC_WAIT}, + }; + + static std::map cursors; + static bool initialized = false; + + if (!initialized) { + initialized = true; + for (const auto& pair : mappings) { + HCURSOR cursor_handle = LoadCursor(nullptr, pair.second); + if (cursor_handle) { + cursors[cursor_handle] = pair.first; + } + } + } + + const auto it = cursors.find(cursor); + if (it != cursors.end()) { + return it->second; + } + return kDefaultCursorName; + } + + CustomPlatformView::CustomPlatformView(flutter::BinaryMessenger* messenger, + flutter::TextureRegistrar* texture_registrar, + GraphicsContext* graphics_context, + HWND hwnd, + std::unique_ptr webView) + : hwnd_(hwnd), view(std::move(webView)), texture_registrar_(texture_registrar) + { +#ifdef HAVE_FLUTTER_D3D_TEXTURE + texture_bridge_ = + std::make_unique(graphics_context, view->surface()); + + flutter_texture_ = + std::make_unique(flutter::GpuSurfaceTexture( + kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle, + [bridge = static_cast(texture_bridge_.get())]( + size_t width, + size_t height) -> const FlutterDesktopGpuSurfaceDescriptor* + { + return bridge->GetSurfaceDescriptor(width, height); + })); +#else + texture_bridge_ = std::make_unique( + graphics_context, webview_->surface()); + + flutter_texture_ = + std::make_unique(flutter::PixelBufferTexture( + [bridge = static_cast(texture_bridge_.get())]( + size_t width, size_t height) -> const FlutterDesktopPixelBuffer* + { + return bridge->CopyPixelBuffer(width, height); + })); +#endif + + texture_id_ = texture_registrar->RegisterTexture(flutter_texture_.get()); + texture_bridge_->SetOnFrameAvailable( + [this]() { texture_registrar_->MarkTextureFrameAvailable(texture_id_); }); + // texture_bridge_->SetOnSurfaceSizeChanged([this](Size size) { + // view->SetSurfaceSize(size.width, size.height); + //}); + + const auto method_channel_name = "com.pichillilorenzo/custom_platform_view_" + std::to_string(texture_id_); + method_channel_ = + std::make_unique>( + messenger, method_channel_name, + &flutter::StandardMethodCodec::GetInstance()); + method_channel_->SetMethodCallHandler([this](const auto& call, auto result) + { + HandleMethodCall(call, std::move(result)); + }); + + const auto event_channel_name = "com.pichillilorenzo/custom_platform_view_" + std::to_string(texture_id_) + "_events"; + event_channel_ = + std::make_unique>( + messenger, event_channel_name, + &flutter::StandardMethodCodec::GetInstance()); + + auto handler = std::make_unique< + flutter::StreamHandlerFunctions>( + [this](const flutter::EncodableValue* arguments, + std::unique_ptr>&& + events) + { + event_sink_ = std::move(events); + RegisterEventHandlers(); + return nullptr; + }, + [this](const flutter::EncodableValue* arguments) + { + event_sink_ = nullptr; + return nullptr; + }); + + event_channel_->SetStreamHandler(std::move(handler)); + } + + CustomPlatformView::~CustomPlatformView() + { + debugLog("dealloc CustomPlatformView"); + method_channel_->SetMethodCallHandler(nullptr); + texture_registrar_->UnregisterTexture(texture_id_); + } + + void CustomPlatformView::RegisterEventHandlers() + { + if (!view) { + return; + } + + view->onSurfaceSizeChanged([this](size_t width, size_t height) + { + texture_bridge_->NotifySurfaceSizeChanged(); + }); + + view->onCursorChanged([this](const HCURSOR cursor) + { + const auto& name = GetCursorName(cursor); + const auto event = flutter::EncodableValue( + flutter::EncodableMap { {flutter::EncodableValue(kEventType), + flutter::EncodableValue("cursorChanged")}, + { flutter::EncodableValue(kEventValue), name }}); + EmitEvent(event); + }); + } + + void CustomPlatformView::HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + const auto& method_name = method_call.method_name(); + + // setCursorPos: [double x, double y] + if (method_name.compare(kMethodSetCursorPos) == 0) { + const auto point = GetPointFromArgs(method_call.arguments()); + if (point && view) { + view->setCursorPos(point->first, point->second); + return result->Success(); + } + return result->Error(kErrorInvalidArgs); + } + + // setPointerUpdate: + // [int pointer, int event, double x, double y, double size, double pressure] + if (method_name.compare(kMethodSetPointerUpdate) == 0) { + const flutter::EncodableList* list = + std::get_if(method_call.arguments()); + if (!list || list->size() != 6) { + return result->Error(kErrorInvalidArgs); + } + + const auto pointer = std::get_if(&(*list)[0]); + const auto event = std::get_if(&(*list)[1]); + const auto x = std::get_if(&(*list)[2]); + const auto y = std::get_if(&(*list)[3]); + const auto size = std::get_if(&(*list)[4]); + const auto pressure = std::get_if(&(*list)[5]); + + if (pointer && event && x && y && size && pressure && view) { + view->setPointerUpdate(*pointer, + static_cast(*event), + *x, *y, *size, *pressure); + return result->Success(); + } + return result->Error(kErrorInvalidArgs); + } + + // setScrollDelta: [double dx, double dy] + if (method_name.compare(kMethodSetScrollDelta) == 0) { + const auto delta = GetPointFromArgs(method_call.arguments()); + if (delta && view) { + view->setScrollDelta(delta->first, delta->second); + return result->Success(); + } + return result->Error(kErrorInvalidArgs); + } + + // setPointerButton: {"button": int, "isDown": bool} + if (method_name.compare(kMethodSetPointerButton) == 0) { + const auto& map = std::get(*method_call.arguments()); + + const auto button = map.find(flutter::EncodableValue("button")); + const auto isDown = map.find(flutter::EncodableValue("isDown")); + if (button != map.end() && isDown != map.end()) { + const auto buttonValue = std::get_if(&button->second); + const auto isDownValue = std::get_if(&isDown->second); + if (buttonValue && isDownValue && view) { + view->setPointerButtonState( + static_cast(*buttonValue), *isDownValue); + return result->Success(); + } + } + return result->Error(kErrorInvalidArgs); + } + + // setSize: [double width, double height, double scale_factor] + if (method_name.compare(kMethodSetSize) == 0) { + auto size = GetPointAndScaleFactorFromArgs(method_call.arguments()); + if (size && view) { + const auto [width, height, scale_factor] = size.value(); + + view->setSurfaceSize(static_cast(width), + static_cast(height), + static_cast(scale_factor)); + + texture_bridge_->Start(); + return result->Success(); + } + return result->Error(kErrorInvalidArgs); + } + + if (method_name.compare(kMethodSetFpsLimit) == 0) { + if (const auto value = std::get_if(method_call.arguments())) { + texture_bridge_->SetFpsLimit(*value == 0 ? std::nullopt + : std::make_optional(*value)); + return result->Success(); + } + } + + result->NotImplemented(); + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.h b/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.h new file mode 100644 index 00000000..6c4adfdb --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include "../in_app_webview/in_app_webview.h" +#include "graphics_context.h" +#include "texture_bridge.h" + +namespace flutter_inappwebview_plugin +{ + class CustomPlatformView { + public: + static inline const wchar_t* CLASS_NAME = L"CustomPlatformView"; + + const std::unique_ptr view; + + CustomPlatformView(flutter::BinaryMessenger* messenger, + flutter::TextureRegistrar* texture_registrar, + GraphicsContext* graphics_context, + HWND hwnd, + std::unique_ptr webView); + ~CustomPlatformView(); + + TextureBridge* texture_bridge() const { return texture_bridge_.get(); } + + int64_t texture_id() const { return texture_id_; } + private: + HWND hwnd_; + std::unique_ptr flutter_texture_; + std::unique_ptr texture_bridge_; + std::unique_ptr> event_sink_; + std::unique_ptr> + event_channel_; + std::unique_ptr> + method_channel_; + + flutter::TextureRegistrar* texture_registrar_; + int64_t texture_id_; + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + void RegisterEventHandlers(); + + template + void EmitEvent(const T& value) + { + if (event_sink_) { + event_sink_->Success(value); + } + } + }; +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/graphics_context.cc b/flutter_inappwebview_windows/windows/custom_platform_view/graphics_context.cc new file mode 100644 index 00000000..2bbc1e74 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/graphics_context.cc @@ -0,0 +1,158 @@ +#include "graphics_context.h" + +#include "util/d3dutil.h" +#include "util/direct3d11.interop.h" + +namespace flutter_inappwebview_plugin +{ + GraphicsContext::GraphicsContext(rx::RoHelper* rohelper) : rohelper_(rohelper) + { + device_ = CreateD3DDevice(); + if (!device_) { + return; + } + + device_->GetImmediateContext(device_context_.put()); + if (FAILED(CreateDirect3D11DeviceFromDXGIDevice( + device_.try_as().get(), + (IInspectable**)device_winrt_.put()))) { + return; + } + + valid_ = true; + } + + winrt::com_ptr + GraphicsContext::CreateCompositor() + { + HSTRING className; + HSTRING_HEADER classNameHeader; + + if (FAILED(rohelper_->GetStringReference( + RuntimeClass_Windows_UI_Composition_Compositor, &className, + &classNameHeader))) { + return nullptr; + } + + winrt::com_ptr af; + if (FAILED(rohelper_->GetActivationFactory( + className, __uuidof(IActivationFactory), af.put_void()))) { + return nullptr; + } + + winrt::com_ptr compositor; + if (FAILED(af->ActivateInstance( + reinterpret_cast(compositor.put())))) { + return nullptr; + } + + return compositor; + } + + winrt::com_ptr + GraphicsContext::CreateGraphicsCaptureItemFromVisual( + ABI::Windows::UI::Composition::IVisual* visual) const + { + HSTRING className; + HSTRING_HEADER classNameHeader; + + if (FAILED(rohelper_->GetStringReference( + RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem, &className, + &classNameHeader))) { + return nullptr; + } + + ABI::Windows::Graphics::Capture::IGraphicsCaptureItemStatics* + capture_item_statics; + if (FAILED(rohelper_->GetActivationFactory( + className, + __uuidof( + ABI::Windows::Graphics::Capture::IGraphicsCaptureItemStatics), + (void**)&capture_item_statics))) { + return nullptr; + } + + winrt::com_ptr + capture_item; + if (FAILED( + capture_item_statics->CreateFromVisual(visual, capture_item.put()))) { + return nullptr; + } + + return capture_item; + } + + winrt::com_ptr + GraphicsContext::CreateCaptureFramePool( + ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice* device, + ABI::Windows::Graphics::DirectX::DirectXPixelFormat pixelFormat, + INT32 numberOfBuffers, ABI::Windows::Graphics::SizeInt32 size) const + { + HSTRING className; + HSTRING_HEADER classNameHeader; + + if (FAILED(rohelper_->GetStringReference( + RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool, + &className, &classNameHeader))) { + return nullptr; + } + + ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePoolStatics* + capture_frame_pool_statics; + if (FAILED(rohelper_->GetActivationFactory( + className, + __uuidof(ABI::Windows::Graphics::Capture:: + IDirect3D11CaptureFramePoolStatics), + (void**)&capture_frame_pool_statics))) { + return nullptr; + } + + winrt::com_ptr + capture_frame_pool; + + if (FAILED(capture_frame_pool_statics->Create(device, pixelFormat, + numberOfBuffers, size, + capture_frame_pool.put()))) { + return nullptr; + } + + return capture_frame_pool; + } + + winrt::com_ptr + GraphicsContext::CreateFreeThreadedCaptureFramePool( + ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice* device, + ABI::Windows::Graphics::DirectX::DirectXPixelFormat pixelFormat, + INT32 numberOfBuffers, ABI::Windows::Graphics::SizeInt32 size) const + { + HSTRING className; + HSTRING_HEADER classNameHeader; + + if (FAILED(rohelper_->GetStringReference( + RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool, + &className, &classNameHeader))) { + return nullptr; + } + + ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePoolStatics2* + capture_frame_pool_statics; + if (FAILED(rohelper_->GetActivationFactory( + className, + __uuidof(ABI::Windows::Graphics::Capture:: + IDirect3D11CaptureFramePoolStatics2), + (void**)&capture_frame_pool_statics))) { + return nullptr; + } + + winrt::com_ptr + capture_frame_pool; + + if (FAILED(capture_frame_pool_statics->CreateFreeThreaded( + device, pixelFormat, numberOfBuffers, size, + capture_frame_pool.put()))) { + return nullptr; + } + + return capture_frame_pool; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/graphics_context.h b/flutter_inappwebview_windows/windows/custom_platform_view/graphics_context.h new file mode 100644 index 00000000..883bffe7 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/graphics_context.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include + +#include "util/rohelper.h" + +namespace flutter_inappwebview_plugin +{ + class GraphicsContext { + public: + GraphicsContext(rx::RoHelper* rohelper); + + inline bool IsValid() const { return valid_; } + + ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice* device() const + { + return device_winrt_.get(); + } + ID3D11Device* d3d_device() const { return device_.get(); } + ID3D11DeviceContext* d3d_device_context() const + { + return device_context_.get(); + } + + winrt::com_ptr CreateCompositor(); + + winrt::com_ptr + CreateGraphicsCaptureItemFromVisual( + ABI::Windows::UI::Composition::IVisual* visual) const; + + winrt::com_ptr + CreateCaptureFramePool( + ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice* device, + ABI::Windows::Graphics::DirectX::DirectXPixelFormat pixelFormat, + INT32 numberOfBuffers, ABI::Windows::Graphics::SizeInt32 size) const; + + winrt::com_ptr + CreateFreeThreadedCaptureFramePool( + ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice* device, + ABI::Windows::Graphics::DirectX::DirectXPixelFormat pixelFormat, + INT32 numberOfBuffers, ABI::Windows::Graphics::SizeInt32 size) const; + + private: + bool valid_ = false; + rx::RoHelper* rohelper_; + winrt::com_ptr + device_winrt_; + winrt::com_ptr device_{ nullptr }; + winrt::com_ptr device_context_{ nullptr }; + }; +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge.cc b/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge.cc new file mode 100644 index 00000000..658df19f --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge.cc @@ -0,0 +1,189 @@ +#include "texture_bridge.h" + +#include + +#include +#include +#include +#include + +#include "util/direct3d11.interop.h" + +namespace flutter_inappwebview_plugin +{ + const int kNumBuffers = 1; + + TextureBridge::TextureBridge(GraphicsContext* graphics_context, + ABI::Windows::UI::Composition::IVisual* visual) + : graphics_context_(graphics_context) + { + capture_item_ = + graphics_context_->CreateGraphicsCaptureItemFromVisual(visual); + assert(capture_item_); + + capture_item_->add_Closed( + Microsoft::WRL::Callback>( + [](ABI::Windows::Graphics::Capture::IGraphicsCaptureItem* item, + IInspectable* args) -> HRESULT + { + std::cerr << "Capture item was closed." << std::endl; + return S_OK; + }) + .Get(), + &on_closed_token_); + } + + TextureBridge::~TextureBridge() + { + const std::lock_guard lock(mutex_); + StopInternal(); + if (capture_item_) { + capture_item_->remove_Closed(on_closed_token_); + } + } + + bool TextureBridge::Start() + { + const std::lock_guard lock(mutex_); + if (is_running_ || !capture_item_) { + return false; + } + + ABI::Windows::Graphics::SizeInt32 size; + capture_item_->get_Size(&size); + + frame_pool_ = graphics_context_->CreateCaptureFramePool( + graphics_context_->device(), + static_cast( + kPixelFormat), + kNumBuffers, size); + assert(frame_pool_); + + frame_pool_->add_FrameArrived( + Microsoft::WRL::Callback>( + [this](ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool* + pool, + IInspectable* args) -> HRESULT + { + OnFrameArrived(); + return S_OK; + }) + .Get(), + &on_frame_arrived_token_); + + if (FAILED(frame_pool_->CreateCaptureSession(capture_item_.get(), + capture_session_.put()))) { + std::cerr << "Creating capture session failed." << std::endl; + return false; + } + + if (SUCCEEDED(capture_session_->StartCapture())) { + is_running_ = true; + return true; + } + + return false; + } + + void TextureBridge::Stop() + { + const std::lock_guard lock(mutex_); + StopInternal(); + } + + void TextureBridge::StopInternal() + { + if (is_running_) { + is_running_ = false; + frame_pool_->remove_FrameArrived(on_frame_arrived_token_); + auto closable = + capture_session_.try_as(); + assert(closable); + closable->Close(); + capture_session_ = nullptr; + } + } + + void TextureBridge::OnFrameArrived() + { + const std::lock_guard lock(mutex_); + if (!is_running_) { + return; + } + + bool has_frame = false; + + winrt::com_ptr + frame; + auto hr = frame_pool_->TryGetNextFrame(frame.put()); + if (SUCCEEDED(hr) && frame) { + winrt::com_ptr< + ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface> + frame_surface; + + if (SUCCEEDED(frame->get_Surface(frame_surface.put()))) { + last_frame_ = + TryGetDXGIInterfaceFromObject(frame_surface); + has_frame = !ShouldDropFrame(); + } + } + + if (needs_update_) { + ABI::Windows::Graphics::SizeInt32 size; + capture_item_->get_Size(&size); + frame_pool_->Recreate( + graphics_context_->device(), + static_cast( + kPixelFormat), + kNumBuffers, size); + needs_update_ = false; + } + + if (has_frame && frame_available_) { + frame_available_(); + } + } + + bool TextureBridge::ShouldDropFrame() + { + if (!frame_duration_.has_value()) { + return false; + } + auto now = std::chrono::high_resolution_clock::now(); + + bool should_drop_frame = false; + if (last_frame_timestamp_.has_value()) { + auto diff = std::chrono::duration_cast( + now - last_frame_timestamp_.value()); + should_drop_frame = diff < frame_duration_.value(); + } + + if (!should_drop_frame) { + last_frame_timestamp_ = now; + } + return should_drop_frame; + } + + void TextureBridge::NotifySurfaceSizeChanged() + { + const std::lock_guard lock(mutex_); + needs_update_ = true; + } + + void TextureBridge::SetFpsLimit(std::optional max_fps) + { + const std::lock_guard lock(mutex_); + auto value = max_fps.value_or(0); + if (value != 0) { + frame_duration_ = FrameDuration(1000.0 / value); + } + else { + frame_duration_.reset(); + last_frame_timestamp_.reset(); + } + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge.h b/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge.h new file mode 100644 index 00000000..122c6938 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +#include "graphics_context.h" + +namespace flutter_inappwebview_plugin +{ + typedef struct { + size_t width; + size_t height; + } Size; + + class TextureBridge { + public: + typedef std::function FrameAvailableCallback; + typedef std::function SurfaceSizeChangedCallback; + typedef std::chrono::duration FrameDuration; + + TextureBridge(GraphicsContext* graphics_context, + ABI::Windows::UI::Composition::IVisual* visual); + virtual ~TextureBridge(); + + bool Start(); + void Stop(); + + void SetOnFrameAvailable(FrameAvailableCallback callback) + { + frame_available_ = std::move(callback); + } + + void SetOnSurfaceSizeChanged(SurfaceSizeChangedCallback callback) + { + surface_size_changed_ = std::move(callback); + } + + void NotifySurfaceSizeChanged(); + void SetFpsLimit(std::optional max_fps); + + protected: + bool is_running_ = false; + + const GraphicsContext* graphics_context_; + std::mutex mutex_; + std::optional frame_duration_ = std::nullopt; + + FrameAvailableCallback frame_available_; + SurfaceSizeChangedCallback surface_size_changed_; + std::atomic needs_update_ = false; + winrt::com_ptr last_frame_; + std::optional + last_frame_timestamp_; + + winrt::com_ptr + capture_item_; + winrt::com_ptr + frame_pool_; + winrt::com_ptr + capture_session_; + + EventRegistrationToken on_closed_token_ = {}; + EventRegistrationToken on_frame_arrived_token_ = {}; + + virtual void StopInternal(); + void OnFrameArrived(); + bool ShouldDropFrame(); + + // corresponds to DXGI_FORMAT_B8G8R8A8_UNORM + static constexpr auto kPixelFormat = ABI::Windows::Graphics::DirectX:: + DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized; + }; +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_fallback.cc b/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_fallback.cc new file mode 100644 index 00000000..6aa5d58d --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_fallback.cc @@ -0,0 +1,145 @@ +#include "texture_bridge_fallback.h" + +#include + +#include "util/direct3d11.interop.h" +#include "util/swizzle.h" + +namespace flutter_inappwebview_plugin +{ + TextureBridgeFallback::TextureBridgeFallback( + GraphicsContext* graphics_context, + ABI::Windows::UI::Composition::IVisual* visual) + : TextureBridge(graphics_context, visual) + {} + + TextureBridgeFallback::~TextureBridgeFallback() + { + const std::lock_guard lock(buffer_mutex_); + } + + void TextureBridgeFallback::ProcessFrame( + winrt::com_ptr src_texture) + { + D3D11_TEXTURE2D_DESC desc; + src_texture->GetDesc(&desc); + + const auto width = desc.Width; + const auto height = desc.Height; + + bool is_exact_size; + EnsureStagingTexture(width, height, is_exact_size); + + auto device_context = graphics_context_->d3d_device_context(); + auto staging_texture = staging_texture_.get(); + + if (is_exact_size) { + device_context->CopyResource(staging_texture, src_texture.get()); + } + else { + D3D11_BOX client_box; + client_box.top = 0; + client_box.left = 0; + client_box.right = width; + client_box.bottom = height; + client_box.front = 0; + client_box.back = 1; + device_context->CopySubresourceRegion(staging_texture, 0, 0, 0, 0, + src_texture.get(), 0, &client_box); + } + + D3D11_MAPPED_SUBRESOURCE mappedResource; + if (!SUCCEEDED(device_context->Map(staging_texture, 0, D3D11_MAP_READ, 0, + &mappedResource))) { + return; + } + + { + const std::lock_guard lock(buffer_mutex_); + if (!pixel_buffer_ || pixel_buffer_->width != width || + pixel_buffer_->height != height) { + if (!pixel_buffer_) { + pixel_buffer_ = std::make_unique(); + pixel_buffer_->release_context = &buffer_mutex_; + // Gets invoked after the FlutterDesktopPixelBuffer's + // backing buffer has been uploaded. + pixel_buffer_->release_callback = [](void* opaque) + { + auto mutex = reinterpret_cast(opaque); + // Gets locked just before |CopyPixelBuffer| returns. + mutex->unlock(); + }; + } + pixel_buffer_->width = width; + pixel_buffer_->height = height; + const auto size = width * height * 4; + backing_pixel_buffer_.reset(new uint8_t[size]); + pixel_buffer_->buffer = backing_pixel_buffer_.get(); + } + + const auto src_pitch_in_pixels = mappedResource.RowPitch / 4; + RGBA_to_BGRA(reinterpret_cast(backing_pixel_buffer_.get()), + static_cast(mappedResource.pData), height, + src_pitch_in_pixels, width); + } + + device_context->Unmap(staging_texture, 0); + } + + void TextureBridgeFallback::EnsureStagingTexture(uint32_t width, + uint32_t height, + bool& is_exact_size) + { + // Only recreate an existing texture if it's too small. + if (!staging_texture_ || staging_texture_size_.width < width || + staging_texture_size_.height < height) { + D3D11_TEXTURE2D_DESC dstDesc = {}; + dstDesc.ArraySize = 1; + dstDesc.MipLevels = 1; + dstDesc.BindFlags = 0; + dstDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + dstDesc.Format = static_cast(kPixelFormat); + dstDesc.Width = width; + dstDesc.Height = height; + dstDesc.MiscFlags = 0; + dstDesc.SampleDesc.Count = 1; + dstDesc.SampleDesc.Quality = 0; + dstDesc.Usage = D3D11_USAGE_STAGING; + + staging_texture_ = nullptr; + if (!SUCCEEDED(graphics_context_->d3d_device()->CreateTexture2D( + &dstDesc, nullptr, staging_texture_.put()))) { + std::cerr << "Creating dst texture failed" << std::endl; + return; + } + + staging_texture_size_ = { width, height }; + } + + is_exact_size = staging_texture_size_.width == width && + staging_texture_size_.height == height; + } + + const FlutterDesktopPixelBuffer* TextureBridgeFallback::CopyPixelBuffer( + size_t width, size_t height) + { + const std::lock_guard lock(mutex_); + + if (!is_running_) { + return nullptr; + } + + if (last_frame_) { + ProcessFrame(last_frame_); + } + + auto buffer = pixel_buffer_.get(); + // Only lock the mutex if the buffer is not null + // (to ensure the release callback gets called) + if (buffer) { + // Gets unlocked in the FlutterDesktopPixelBuffer's release callback. + buffer_mutex_.lock(); + } + return buffer; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_fallback.h b/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_fallback.h new file mode 100644 index 00000000..ad07b77c --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_fallback.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include + +#include "texture_bridge.h" + +namespace flutter_inappwebview_plugin +{ + class TextureBridgeFallback : public TextureBridge { + public: + TextureBridgeFallback(GraphicsContext* graphics_context, + ABI::Windows::UI::Composition::IVisual* visual); + ~TextureBridgeFallback() override; + + const FlutterDesktopPixelBuffer* CopyPixelBuffer(size_t width, size_t height); + + private: + Size staging_texture_size_ = { 0, 0 }; + winrt::com_ptr staging_texture_{ nullptr }; + std::mutex buffer_mutex_; + std::unique_ptr backing_pixel_buffer_; + std::unique_ptr pixel_buffer_; + + void ProcessFrame(winrt::com_ptr src_texture); + void EnsureStagingTexture(uint32_t width, uint32_t height, + bool& is_exact_size); + }; +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_gpu.cc b/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_gpu.cc new file mode 100644 index 00000000..c3bdee26 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_gpu.cc @@ -0,0 +1,108 @@ +#include "texture_bridge_gpu.h" + +#include + +#include "util/direct3d11.interop.h" + +namespace flutter_inappwebview_plugin +{ + TextureBridgeGpu::TextureBridgeGpu( + GraphicsContext* graphics_context, + ABI::Windows::UI::Composition::IVisual* visual) + : TextureBridge(graphics_context, visual) + { + surface_descriptor_.struct_size = sizeof(FlutterDesktopGpuSurfaceDescriptor); + surface_descriptor_.format = + kFlutterDesktopPixelFormatNone; // no format required for DXGI surfaces + } + + void TextureBridgeGpu::ProcessFrame( + winrt::com_ptr src_texture) + { + D3D11_TEXTURE2D_DESC desc; + src_texture->GetDesc(&desc); + + const auto width = desc.Width; + const auto height = desc.Height; + + EnsureSurface(width, height); + + auto device_context = graphics_context_->d3d_device_context(); + + device_context->CopyResource(surface_.get(), src_texture.get()); + device_context->Flush(); + } + + void TextureBridgeGpu::EnsureSurface(uint32_t width, uint32_t height) + { + if (!surface_ || surface_size_.width != width || + surface_size_.height != height) { + D3D11_TEXTURE2D_DESC dstDesc = {}; + dstDesc.ArraySize = 1; + dstDesc.MipLevels = 1; + dstDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + dstDesc.CPUAccessFlags = 0; + dstDesc.Format = static_cast(kPixelFormat); + dstDesc.Width = width; + dstDesc.Height = height; + dstDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; + dstDesc.SampleDesc.Count = 1; + dstDesc.SampleDesc.Quality = 0; + dstDesc.Usage = D3D11_USAGE_DEFAULT; + + surface_ = nullptr; + if (!SUCCEEDED(graphics_context_->d3d_device()->CreateTexture2D( + &dstDesc, nullptr, surface_.put()))) { + std::cerr << "Creating intermediate texture failed" << std::endl; + return; + } + + HANDLE shared_handle; + surface_.try_as(dxgi_surface_); + assert(dxgi_surface_); + dxgi_surface_->GetSharedHandle(&shared_handle); + + surface_descriptor_.handle = shared_handle; + surface_descriptor_.width = surface_descriptor_.visible_width = width; + surface_descriptor_.height = surface_descriptor_.visible_height = height; + surface_descriptor_.release_context = surface_.get(); + surface_descriptor_.release_callback = [](void* release_context) + { + auto texture = reinterpret_cast(release_context); + texture->Release(); + }; + + surface_size_ = { width, height }; + } + } + + const FlutterDesktopGpuSurfaceDescriptor* + TextureBridgeGpu::GetSurfaceDescriptor(size_t width, size_t height) + { + const std::lock_guard lock(mutex_); + + if (!is_running_) { + return nullptr; + } + + if (last_frame_) { + ProcessFrame(last_frame_); + } + + if (surface_) { + // Gets released in the SurfaceDescriptor's release callback. + surface_->AddRef(); + } + + return &surface_descriptor_; + } + + void TextureBridgeGpu::StopInternal() + { + TextureBridge::StopInternal(); + + // For some reason, the destination surface needs to be recreated upon + // resuming. Force |EnsureSurface| to create a new one by resetting it here. + surface_ = nullptr; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_gpu.h b/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_gpu.h new file mode 100644 index 00000000..8b29a321 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/texture_bridge_gpu.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "texture_bridge.h" + +namespace flutter_inappwebview_plugin +{ + class TextureBridgeGpu : public TextureBridge { + public: + TextureBridgeGpu(GraphicsContext* graphics_context, + ABI::Windows::UI::Composition::IVisual* visual); + + const FlutterDesktopGpuSurfaceDescriptor* GetSurfaceDescriptor(size_t width, + size_t height); + + protected: + void StopInternal() override; + + private: + FlutterDesktopGpuSurfaceDescriptor surface_descriptor_ = {}; + Size surface_size_ = { 0, 0 }; + winrt::com_ptr surface_{ nullptr }; + winrt::com_ptr dxgi_surface_; + + void ProcessFrame(winrt::com_ptr src_texture); + void EnsureSurface(uint32_t width, uint32_t height); + }; +} diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/composition.desktop.interop.h b/flutter_inappwebview_windows/windows/custom_platform_view/util/composition.desktop.interop.h new file mode 100644 index 00000000..9b9eb0d3 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/composition.desktop.interop.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include + +namespace util { + + winrt::com_ptr TryCreateDesktopWindowTarget( + const winrt::com_ptr& + compositor, + HWND window) + { + namespace abi = ABI::Windows::UI::Composition::Desktop; + auto interop = compositor.try_as(); + + winrt::com_ptr target; + interop->CreateDesktopWindowTarget(window, true, target.put()); + return target; + } + +} // namespace util diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/cpuinfo.cc b/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/cpuinfo.cc new file mode 100644 index 00000000..acee3437 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/cpuinfo.cc @@ -0,0 +1,80 @@ +#include "cpuinfo.h" + +#include "detail/cpuinfo_impl.h" + +#if defined(_MSC_VER) && (defined(__x86_64__) || defined(_M_X64)) +#include "detail/init_msvc_x86.h" +#else +#include "detail/init_unknown.hpp" +#endif + +namespace cpuid { + +cpuinfo::cpuinfo() : impl_(new impl) { init_cpuinfo(*impl_); } + +cpuinfo::~cpuinfo() {} + +// x86 member functions +bool cpuinfo::has_fpu() const { return impl_->m_has_fpu; } + +bool cpuinfo::has_mmx() const { return impl_->m_has_mmx; } + +bool cpuinfo::has_sse() const { return impl_->m_has_sse; } + +bool cpuinfo::has_sse2() const { return impl_->m_has_sse2; } + +bool cpuinfo::has_sse3() const { return impl_->m_has_sse3; } + +bool cpuinfo::has_ssse3() const { return impl_->m_has_ssse3; } + +bool cpuinfo::has_sse4_1() const { return impl_->m_has_sse4_1; } + +bool cpuinfo::has_sse4_2() const { return impl_->m_has_sse4_2; } + +bool cpuinfo::has_pclmulqdq() const { return impl_->m_has_pclmulqdq; } + +bool cpuinfo::has_avx() const { return impl_->m_has_avx; } + +bool cpuinfo::has_avx2() const { return impl_->m_has_avx2; } + +bool cpuinfo::has_avx512_f() const { return impl_->m_has_avx512_f; } + +bool cpuinfo::has_avx512_dq() const { return impl_->m_has_avx512_dq; } + +bool cpuinfo::has_avx512_ifma() const { return impl_->m_has_avx512_ifma; } + +bool cpuinfo::has_avx512_pf() const { return impl_->m_has_avx512_pf; } + +bool cpuinfo::has_avx512_er() const { return impl_->m_has_avx512_er; } + +bool cpuinfo::has_avx512_cd() const { return impl_->m_has_avx512_cd; } + +bool cpuinfo::has_avx512_bw() const { return impl_->m_has_avx512_bw; } + +bool cpuinfo::has_avx512_vl() const { return impl_->m_has_avx512_vl; } + +bool cpuinfo::has_avx512_vbmi() const { return impl_->m_has_avx512_vbmi; } + +bool cpuinfo::has_avx512_vbmi2() const { return impl_->m_has_avx512_vbmi2; } + +bool cpuinfo::has_avx512_vnni() const { return impl_->m_has_avx512_vnni; } + +bool cpuinfo::has_avx512_bitalg() const { return impl_->m_has_avx512_bitalg; } + +bool cpuinfo::has_avx512_vpopcntdq() const { + return impl_->m_has_avx512_vpopcntdq; +} + +bool cpuinfo::has_avx512_4vnniw() const { return impl_->m_has_avx512_4vnniw; } + +bool cpuinfo::has_avx512_4fmaps() const { return impl_->m_has_avx512_4fmaps; } + +bool cpuinfo::has_avx512_vp2intersect() const { + return impl_->m_has_avx512_vp2intersect; +} + +bool cpuinfo::has_f16c() const { return impl_->m_has_f16c; } + +// ARM member functions +bool cpuinfo::has_neon() const { return impl_->m_has_neon; } +} // namespace cpuid diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/cpuinfo.h b/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/cpuinfo.h new file mode 100644 index 00000000..0cee3598 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/cpuinfo.h @@ -0,0 +1,105 @@ +#pragma once + +#include + +namespace cpuid { + +class cpuinfo { + public: + struct impl; + + cpuinfo(); + ~cpuinfo(); + + // Has X87 FPU + bool has_fpu() const; + + // Return true if the CPU supports MMX + bool has_mmx() const; + + // Return true if the CPU supports SSE + bool has_sse() const; + + // Return true if the CPU supports SSE2 + bool has_sse2() const; + + // Return true if the CPU supports SSE3 + bool has_sse3() const; + + // Return true if the CPU supports SSSE3 + bool has_ssse3() const; + + // Return true if the CPU supports SSE 4.1 + bool has_sse4_1() const; + + // Return true if the CPU supports SSE 4.2 + bool has_sse4_2() const; + + // Return true if the CPU supports pclmulqdq + bool has_pclmulqdq() const; + + // Return true if the CPU supports AVX + bool has_avx() const; + + // Return true if the CPU supports AVX2 + bool has_avx2() const; + + // Return true if the CPU supports AVX512F + bool has_avx512_f() const; + + // Return true if the CPU supports AVX512DQ + bool has_avx512_dq() const; + + // Return true if the CPU supports AVX512_IFMA + bool has_avx512_ifma() const; + + // Return true if the CPU supports AVX512PF + bool has_avx512_pf() const; + + // Return true if the CPU supports AVX512ER + bool has_avx512_er() const; + + // Return true if the CPU supports AVX512CD + bool has_avx512_cd() const; + + // Return true if the CPU supports AVX512BW + bool has_avx512_bw() const; + + // Return true if the CPU supports AVX512VL + bool has_avx512_vl() const; + + // Return true if the CPU supports AVX512_VBMI + bool has_avx512_vbmi() const; + + // Return true if the CPU supports AVX512_VBMI2 + bool has_avx512_vbmi2() const; + + // Return true if the CPU supports AVX512_VNNI + bool has_avx512_vnni() const; + + // Return true if the CPU supports AVX512_BITALG + bool has_avx512_bitalg() const; + + // Return true if the CPU supports AVX512_VPOPCNTDQ + bool has_avx512_vpopcntdq() const; + + // Return true if the CPU supports AVX512_4VNNIW + bool has_avx512_4vnniw() const; + + // Return true if the CPU supports AVX512_4FMAPS + bool has_avx512_4fmaps() const; + + // Return true if the CPU supports AVX512_VP2INTERSECT + bool has_avx512_vp2intersect() const; + + // Return true if the CPU supports F16C + bool has_f16c() const; + + // Return true if the CPU supports NEON + bool has_neon() const; + + private: + // Private implementation + std::unique_ptr impl_; +}; +} // namespace cpuid diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/cpuinfo_impl.h b/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/cpuinfo_impl.h new file mode 100644 index 00000000..1d2ee69f --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/cpuinfo_impl.h @@ -0,0 +1,69 @@ +#pragma once + +#include "../cpuinfo.h" + +namespace cpuid { + +struct cpuinfo::impl { + impl() + : m_has_fpu(false), + m_has_mmx(false), + m_has_sse(false), + m_has_sse2(false), + m_has_sse3(false), + m_has_ssse3(false), + m_has_sse4_1(false), + m_has_sse4_2(false), + m_has_pclmulqdq(false), + m_has_avx(false), + m_has_avx2(false), + m_has_avx512_f(false), + m_has_avx512_dq(false), + m_has_avx512_ifma(false), + m_has_avx512_pf(false), + m_has_avx512_er(false), + m_has_avx512_cd(false), + m_has_avx512_bw(false), + m_has_avx512_vl(false), + m_has_avx512_vbmi(false), + m_has_avx512_vbmi2(false), + m_has_avx512_vnni(false), + m_has_avx512_bitalg(false), + m_has_avx512_vpopcntdq(false), + m_has_avx512_4vnniw(false), + m_has_avx512_4fmaps(false), + m_has_avx512_vp2intersect(false), + m_has_f16c(false), + m_has_neon(false) {} + + bool m_has_fpu; + bool m_has_mmx; + bool m_has_sse; + bool m_has_sse2; + bool m_has_sse3; + bool m_has_ssse3; + bool m_has_sse4_1; + bool m_has_sse4_2; + bool m_has_pclmulqdq; + bool m_has_avx; + bool m_has_avx2; + bool m_has_avx512_f; + bool m_has_avx512_dq; + bool m_has_avx512_ifma; + bool m_has_avx512_pf; + bool m_has_avx512_er; + bool m_has_avx512_cd; + bool m_has_avx512_bw; + bool m_has_avx512_vl; + bool m_has_avx512_vbmi; + bool m_has_avx512_vbmi2; + bool m_has_avx512_vnni; + bool m_has_avx512_bitalg; + bool m_has_avx512_vpopcntdq; + bool m_has_avx512_4vnniw; + bool m_has_avx512_4fmaps; + bool m_has_avx512_vp2intersect; + bool m_has_f16c; + bool m_has_neon; +}; +} // namespace cpuid diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/extract_x86_flags.h b/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/extract_x86_flags.h new file mode 100644 index 00000000..5bea5866 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/extract_x86_flags.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include "cpuinfo_impl.h" + +namespace cpuid { + +void extract_x86_flags(cpuinfo::impl& info, uint32_t ecx, uint32_t edx) { + info.m_has_fpu = (edx & (1 << 0)) != 0; + info.m_has_mmx = (edx & (1 << 23)) != 0; + info.m_has_sse = (edx & (1 << 25)) != 0; + info.m_has_sse2 = (edx & (1 << 26)) != 0; + info.m_has_sse3 = (ecx & (1 << 0)) != 0; + info.m_has_ssse3 = (ecx & (1 << 9)) != 0; + info.m_has_sse4_1 = (ecx & (1 << 19)) != 0; + info.m_has_sse4_2 = (ecx & (1 << 20)) != 0; + info.m_has_pclmulqdq = (ecx & (1 << 1)) != 0; + info.m_has_avx = (ecx & (1 << 28)) != 0; + info.m_has_f16c = (ecx & (1 << 29)) != 0; +} + +void extract_x86_extended_flags(cpuinfo::impl& info, uint32_t ebx, uint32_t ecx, + uint32_t edx) { + info.m_has_avx2 = (ebx & (1 << 5)) != 0; + info.m_has_avx512_f = (ebx & (1 << 16)) != 0; + info.m_has_avx512_dq = (ebx & (1 << 17)) != 0; + info.m_has_avx512_ifma = (ebx & (1 << 21)) != 0; + info.m_has_avx512_pf = (ebx & (1 << 26)) != 0; + info.m_has_avx512_er = (ebx & (1 << 27)) != 0; + info.m_has_avx512_cd = (ebx & (1 << 28)) != 0; + info.m_has_avx512_bw = (ebx & (1 << 30)) != 0; + info.m_has_avx512_vl = (ebx & (1 << 31)) != 0; + info.m_has_avx512_vbmi = (ecx & (1 << 1)) != 0; + info.m_has_avx512_vbmi2 = (ecx & (1 << 6)) != 0; + info.m_has_avx512_vnni = (ecx & (1 << 11)) != 0; + info.m_has_avx512_bitalg = (ecx & (1 << 12)) != 0; + info.m_has_avx512_vpopcntdq = (ecx & (1 << 14)) != 0; + info.m_has_avx512_4vnniw = (edx & (1 << 2)) != 0; + info.m_has_avx512_4fmaps = (edx & (1 << 3)) != 0; + info.m_has_avx512_vp2intersect = (edx & (1 << 8)) != 0; +} +} // namespace cpuid diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/init_msvc_x86.h b/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/init_msvc_x86.h new file mode 100644 index 00000000..697270b1 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/init_msvc_x86.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include "cpuinfo_impl.h" +#include "extract_x86_flags.h" + +namespace cpuid { + +void init_cpuinfo(cpuinfo::impl& info) { + int registers[4]; + + // The register information per input can be extracted from here: + // http://en.wikipedia.org/wiki/CPUID + // + // CPUID should be called with EAX=0 first, as this will return the + // maximum supported EAX input value for future calls + __cpuid(registers, 0); + uint32_t maximum_eax = registers[0]; + + // Set registers for basic flag extraction, eax=1 + // All CPUs should support index=1 + if (maximum_eax >= 1U) { + __cpuid(registers, 1); + extract_x86_flags(info, registers[2], registers[3]); + } + + // Set registers for extended flags extraction, eax=7 and ecx=0 + // This operation is not supported on older CPUs, so it should be skipped + // to avoid incorrect results + if (maximum_eax >= 7U) { + __cpuidex(registers, 7, 0); + extract_x86_extended_flags(info, registers[1], registers[2], registers[3]); + } +} +} // namespace cpuid diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/init_unknown.h b/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/init_unknown.h new file mode 100644 index 00000000..3b52ef2b --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/cpuid/detail/init_unknown.h @@ -0,0 +1,9 @@ + +#pragma once + +#include "cpuinfo_impl.h" + +namespace cpuid { + +void init_cpuinfo(cpuinfo::impl& info) { (void)info; } +} // namespace cpuid diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/d3dutil.h b/flutter_inappwebview_windows/windows/custom_platform_view/util/d3dutil.h new file mode 100644 index 00000000..a32bc157 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/d3dutil.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +inline auto CreateD3DDevice(D3D_DRIVER_TYPE const type, + winrt::com_ptr& device) { + WINRT_ASSERT(!device); + + UINT flags = + D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_VIDEO_SUPPORT; + + //#ifdef _DEBUG + // flags |= D3D11_CREATE_DEVICE_DEBUG; + //#endif + + return D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0, + D3D11_SDK_VERSION, device.put(), nullptr, nullptr); +} + +inline auto CreateD3DDevice() { + winrt::com_ptr device; + HRESULT hr = CreateD3DDevice(D3D_DRIVER_TYPE_HARDWARE, device); + + if (DXGI_ERROR_UNSUPPORTED == hr) { + CreateD3DDevice(D3D_DRIVER_TYPE_WARP, device); + } + + return device; +} diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/direct3d11.interop.cc b/flutter_inappwebview_windows/windows/custom_platform_view/util/direct3d11.interop.cc new file mode 100644 index 00000000..96a74ba9 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/direct3d11.interop.cc @@ -0,0 +1,47 @@ +#include "direct3d11.interop.h" + +namespace flutter_inappwebview_plugin +{ + + namespace { + + typedef HRESULT(WINAPI* CreateDirect3D11DeviceFromDXGIDeviceFn)(IDXGIDevice*, + LPVOID*); + + struct D3DFuncs { + CreateDirect3D11DeviceFromDXGIDeviceFn CreateDirect3D11DeviceFromDXGIDevice = + nullptr; + + D3DFuncs() + { + auto handle = GetModuleHandle(L"d3d11.dll"); + if (!handle) { + return; + } + + CreateDirect3D11DeviceFromDXGIDevice = + reinterpret_cast( + GetProcAddress(handle, "CreateDirect3D11DeviceFromDXGIDevice")); + } + + static const D3DFuncs& instance() + { + static D3DFuncs funcs; + return funcs; + } + }; + + } // namespace + + HRESULT CreateDirect3D11DeviceFromDXGIDevice(IDXGIDevice* dxgiDevice, + IInspectable** graphicsDevice) + { + auto ptr = D3DFuncs::instance().CreateDirect3D11DeviceFromDXGIDevice; + if (ptr) { + return ptr(dxgiDevice, reinterpret_cast(graphicsDevice)); + } + + return E_NOTIMPL; + } + +} // namespace util diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/direct3d11.interop.h b/flutter_inappwebview_windows/windows/custom_platform_view/util/direct3d11.interop.h new file mode 100644 index 00000000..4434815d --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/direct3d11.interop.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include + +#include "dxgi.h" + +namespace Windows { + namespace Graphics { + namespace DirectX { + namespace Direct3D11 { + struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")) + IDirect3DDxgiInterfaceAccess : ::IUnknown { + virtual HRESULT __stdcall GetInterface(GUID const& id, void** object) = 0; + }; + + } // namespace Direct3D11 + } // namespace DirectX + } // namespace Graphics +} // namespace Windows + +namespace flutter_inappwebview_plugin +{ + + HRESULT CreateDirect3D11DeviceFromDXGIDevice(IDXGIDevice* dxgiDevice, + IInspectable** graphicsDevice); + + template + auto GetDXGIInterfaceFromObject( + winrt::Windows::Foundation::IInspectable const& object) + { + auto access = object.as< + Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>(); + winrt::com_ptr result; + winrt::check_hresult( + access->GetInterface(winrt::guid_of(), result.put_void())); + return result; + } + + template + auto TryGetDXGIInterfaceFromObject(const winrt::com_ptr& object) + { + auto access = object.try_as< + Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>(); + winrt::com_ptr result; + access->GetInterface(winrt::guid_of(), result.put_void()); + return result; + } + +} // namespace util diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/rohelper.cc b/flutter_inappwebview_windows/windows/custom_platform_view/util/rohelper.cc new file mode 100644 index 00000000..6fa6ab0b --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/rohelper.cc @@ -0,0 +1,244 @@ +// Based on ANGLE's RoHelper (CompositorNativeWindow11.{cpp,h}) +// - https://github.com/google/angle/blob/main/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.h +// - https://github.com/google/angle/blob/main/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.cpp +// - https://gist.github.com/clarkezone/43e984fb9bdcd2cfcd9a4f41c208a02f +// +// Copyright 2018 The ANGLE Project Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of TransGaming Inc., Google Inc., 3DLabs Inc. +// Ltd., nor the names of their contributors may be used to endorse +// or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include "rohelper.h" + +#include +#include + +namespace rx { +template +bool AssignProcAddress(HMODULE comBaseModule, const char* name, T*& outProc) { + outProc = reinterpret_cast(GetProcAddress(comBaseModule, name)); + return *outProc != nullptr; +} + +RoHelper::RoHelper(RO_INIT_TYPE init_type) + : mFpWindowsCreateStringReference(nullptr), + mFpGetActivationFactory(nullptr), + mFpWindowsCompareStringOrdinal(nullptr), + mFpCreateDispatcherQueueController(nullptr), + mFpWindowsDeleteString(nullptr), + mFpRoInitialize(nullptr), + mFpRoUninitialize(nullptr), + mWinRtAvailable(false), + mComBaseModule(nullptr), + mCoreMessagingModule(nullptr) { +#ifdef WINUWP + mFpWindowsCreateStringReference = &::WindowsCreateStringReference; + mFpRoInitialize = &::RoInitialize; + mFpRoUninitialize = &::RoUninitialize; + mFpWindowsDeleteString = &::WindowsDeleteString; + mFpGetActivationFactory = &::RoGetActivationFactory; + mFpWindowsCompareStringOrdinal = &::WindowsCompareStringOrdinal; + mFpCreateDispatcherQueueController = &::CreateDispatcherQueueController; + mWinRtAvailable = true; +#else + + mComBaseModule = LoadLibraryA("ComBase.dll"); + + if (mComBaseModule == nullptr) { + return; + } + + if (!AssignProcAddress(mComBaseModule, "WindowsCreateStringReference", + mFpWindowsCreateStringReference)) { + return; + } + + if (!AssignProcAddress(mComBaseModule, "RoGetActivationFactory", + mFpGetActivationFactory)) { + return; + } + + if (!AssignProcAddress(mComBaseModule, "WindowsCompareStringOrdinal", + mFpWindowsCompareStringOrdinal)) { + return; + } + + if (!AssignProcAddress(mComBaseModule, "WindowsDeleteString", + mFpWindowsDeleteString)) { + return; + } + + if (!AssignProcAddress(mComBaseModule, "RoInitialize", mFpRoInitialize)) { + return; + } + + if (!AssignProcAddress(mComBaseModule, "RoUninitialize", mFpRoUninitialize)) { + return; + } + + mCoreMessagingModule = LoadLibraryA("coremessaging.dll"); + + if (mCoreMessagingModule == nullptr) { + return; + } + + if (!AssignProcAddress(mCoreMessagingModule, + "CreateDispatcherQueueController", + mFpCreateDispatcherQueueController)) { + return; + } + + auto result = RoInitialize(init_type); + + if (SUCCEEDED(result) || result == S_FALSE || result == RPC_E_CHANGED_MODE) { + mWinRtAvailable = true; + } +#endif +} + +RoHelper::~RoHelper() { +#ifndef WINUWP + if (mWinRtAvailable) { + RoUninitialize(); + } + + if (mCoreMessagingModule != nullptr) { + FreeLibrary(mCoreMessagingModule); + mCoreMessagingModule = nullptr; + } + + if (mComBaseModule != nullptr) { + FreeLibrary(mComBaseModule); + mComBaseModule = nullptr; + } +#endif +} + +bool RoHelper::WinRtAvailable() const { return mWinRtAvailable; } + +bool RoHelper::SupportedWindowsRelease() { + if (!mWinRtAvailable) { + return false; + } + + HSTRING className, contractName; + HSTRING_HEADER classNameHeader, contractNameHeader; + boolean isSupported = false; + + HRESULT hr = GetStringReference( + RuntimeClass_Windows_Foundation_Metadata_ApiInformation, &className, + &classNameHeader); + + if (FAILED(hr)) { + return !!isSupported; + } + + Microsoft::WRL::ComPtr< + ABI::Windows::Foundation::Metadata::IApiInformationStatics> + api; + + hr = GetActivationFactory( + className, + __uuidof(ABI::Windows::Foundation::Metadata::IApiInformationStatics), + &api); + + if (FAILED(hr)) { + return !!isSupported; + } + + hr = GetStringReference(L"Windows.Foundation.UniversalApiContract", + &contractName, &contractNameHeader); + if (FAILED(hr)) { + return !!isSupported; + } + + api->IsApiContractPresentByMajor(contractName, 6, &isSupported); + + return !!isSupported; +} + +HRESULT RoHelper::GetStringReference(PCWSTR source, HSTRING* act, + HSTRING_HEADER* header) { + if (!mWinRtAvailable) { + return E_FAIL; + } + + const wchar_t* str = static_cast(source); + + unsigned int length; + HRESULT hr = SizeTToUInt32(::wcslen(str), &length); + if (FAILED(hr)) { + return hr; + } + + return mFpWindowsCreateStringReference(source, length, header, act); +} + +HRESULT RoHelper::GetActivationFactory(const HSTRING act, + const IID& interfaceId, void** fac) { + if (!mWinRtAvailable) { + return E_FAIL; + } + auto hr = mFpGetActivationFactory(act, interfaceId, fac); + return hr; +} + +HRESULT RoHelper::WindowsCompareStringOrdinal(HSTRING one, HSTRING two, + int* result) { + if (!mWinRtAvailable) { + return E_FAIL; + } + return mFpWindowsCompareStringOrdinal(one, two, result); +} + +HRESULT RoHelper::CreateDispatcherQueueController( + DispatcherQueueOptions options, + ABI::Windows::System::IDispatcherQueueController** + dispatcherQueueController) { + if (!mWinRtAvailable) { + return E_FAIL; + } + return mFpCreateDispatcherQueueController(options, dispatcherQueueController); +} + +HRESULT RoHelper::WindowsDeleteString(HSTRING one) { + if (!mWinRtAvailable) { + return E_FAIL; + } + return mFpWindowsDeleteString(one); +} + +HRESULT RoHelper::RoInitialize(RO_INIT_TYPE type) { + return mFpRoInitialize(type); +} + +void RoHelper::RoUninitialize() { mFpRoUninitialize(); } +} // namespace rx diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/rohelper.h b/flutter_inappwebview_windows/windows/custom_platform_view/util/rohelper.h new file mode 100644 index 00000000..12e7e6cc --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/rohelper.h @@ -0,0 +1,97 @@ +// Based on ANGLE's RoHelper (CompositorNativeWindow11.{cpp,h}) +// - https://github.com/google/angle/blob/main/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.h +// - https://github.com/google/angle/blob/main/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.cpp +// - https://gist.github.com/clarkezone/43e984fb9bdcd2cfcd9a4f41c208a02f +// +// Copyright 2018 The ANGLE Project Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of TransGaming Inc., Google Inc., 3DLabs Inc. +// Ltd., nor the names of their contributors may be used to endorse +// or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include +#include + +namespace rx { +class RoHelper { + public: + RoHelper(RO_INIT_TYPE init_type); + ~RoHelper(); + bool WinRtAvailable() const; + bool SupportedWindowsRelease(); + HRESULT GetStringReference(PCWSTR source, HSTRING* act, + HSTRING_HEADER* header); + HRESULT GetActivationFactory(const HSTRING act, const IID& interfaceId, + void** fac); + HRESULT WindowsCompareStringOrdinal(HSTRING one, HSTRING two, int* result); + HRESULT CreateDispatcherQueueController( + DispatcherQueueOptions options, + ABI::Windows::System::IDispatcherQueueController** + dispatcherQueueController); + HRESULT WindowsDeleteString(HSTRING one); + HRESULT RoInitialize(RO_INIT_TYPE type); + void RoUninitialize(); + + private: + using WindowsCreateStringReference_ = HRESULT __stdcall(PCWSTR, UINT32, + HSTRING_HEADER*, + HSTRING*); + + using GetActivationFactory_ = HRESULT __stdcall(HSTRING, REFIID, void**); + + using WindowsCompareStringOrginal_ = HRESULT __stdcall(HSTRING, HSTRING, + int*); + + using WindowsDeleteString_ = HRESULT __stdcall(HSTRING); + + using CreateDispatcherQueueController_ = + HRESULT __stdcall(DispatcherQueueOptions, + ABI::Windows::System::IDispatcherQueueController**); + + using RoInitialize_ = HRESULT __stdcall(RO_INIT_TYPE); + using RoUninitialize_ = void __stdcall(); + + WindowsCreateStringReference_* mFpWindowsCreateStringReference; + GetActivationFactory_* mFpGetActivationFactory; + WindowsCompareStringOrginal_* mFpWindowsCompareStringOrdinal; + CreateDispatcherQueueController_* mFpCreateDispatcherQueueController; + WindowsDeleteString_* mFpWindowsDeleteString; + RoInitialize_* mFpRoInitialize; + RoUninitialize_* mFpRoUninitialize; + + bool mWinRtAvailable; + + HMODULE mComBaseModule; + HMODULE mCoreMessagingModule; +}; +} // namespace rx diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/string_converter.cc b/flutter_inappwebview_windows/windows/custom_platform_view/util/string_converter.cc new file mode 100644 index 00000000..7b2d8861 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/string_converter.cc @@ -0,0 +1,54 @@ +#include "string_converter.h" + +#include + +namespace util { +std::string Utf8FromUtf16(std::wstring_view utf16_string) { + if (utf16_string.empty()) { + return std::string(); + } + + auto src_length = static_cast(utf16_string.size()); + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(), + src_length, nullptr, 0, nullptr, nullptr); + + std::string utf8_string; + if (target_length <= 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(), src_length, + utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} + +std::wstring Utf16FromUtf8(std::string_view utf8_string) { + if (utf8_string.empty()) { + return std::wstring(); + } + + auto src_length = static_cast(utf8_string.size()); + int target_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + src_length, nullptr, 0); + + std::wstring utf16_string; + if (target_length <= 0 || target_length > utf16_string.max_size()) { + return utf16_string; + } + utf16_string.resize(target_length); + int converted_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + src_length, utf16_string.data(), target_length); + if (converted_length == 0) { + return std::wstring(); + } + return utf16_string; +} + +} // namespace util diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/string_converter.h b/flutter_inappwebview_windows/windows/custom_platform_view/util/string_converter.h new file mode 100644 index 00000000..0a71dbe0 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/string_converter.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace util { +std::string Utf8FromUtf16(std::wstring_view utf16_string); +std::wstring Utf16FromUtf8(std::string_view utf8_string); +} // namespace util diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/util/swizzle.h b/flutter_inappwebview_windows/windows/custom_platform_view/util/swizzle.h new file mode 100644 index 00000000..67f5f7c6 --- /dev/null +++ b/flutter_inappwebview_windows/windows/custom_platform_view/util/swizzle.h @@ -0,0 +1,192 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* + * see skia/src/opts/SkSwizzler_opts.h + */ + +#pragma once + +#include "cpuid/cpuinfo.h" + +/** + * SK_CPU_SSE_LEVEL + * + * If defined, SK_CPU_SSE_LEVEL should be set to the highest supported level. + * On non-intel CPU this should be undefined. + */ +#define SK_CPU_SSE_LEVEL_SSE1 10 +#define SK_CPU_SSE_LEVEL_SSE2 20 +#define SK_CPU_SSE_LEVEL_SSE3 30 +#define SK_CPU_SSE_LEVEL_SSSE3 31 +#define SK_CPU_SSE_LEVEL_SSE41 41 +#define SK_CPU_SSE_LEVEL_SSE42 42 +#define SK_CPU_SSE_LEVEL_AVX 51 +#define SK_CPU_SSE_LEVEL_AVX2 52 +#define SK_CPU_SSE_LEVEL_SKX 60 + +// Are we in GCC/Clang? +#ifndef SK_CPU_SSE_LEVEL +// These checks must be done in descending order to ensure we set the highest +// available SSE level. +#if defined(__AVX512F__) && defined(__AVX512DQ__) && defined(__AVX512CD__) && \ + defined(__AVX512BW__) && defined(__AVX512VL__) +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SKX +#elif defined(__AVX2__) +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX2 +#elif defined(__AVX__) +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX +#elif defined(__SSE4_2__) +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE42 +#elif defined(__SSE4_1__) +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE41 +#elif defined(__SSSE3__) +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSSE3 +#elif defined(__SSE3__) +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE3 +#elif defined(__SSE2__) +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE2 +#endif +#endif + +// Are we in VisualStudio? +#ifndef SK_CPU_SSE_LEVEL +// These checks must be done in descending order to ensure we set the highest +// available SSE level. 64-bit intel guarantees at least SSE2 support. +#if defined(__AVX512F__) && defined(__AVX512DQ__) && defined(__AVX512CD__) && \ + defined(__AVX512BW__) && defined(__AVX512VL__) +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SKX +#elif defined(__AVX2__) +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX2 +#elif defined(__AVX__) +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_AVX +#elif defined(_M_X64) || defined(_M_AMD64) +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE2 +#elif defined(_M_IX86_FP) +#if _M_IX86_FP >= 2 +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE2 +#elif _M_IX86_FP == 1 +#define SK_CPU_SSE_LEVEL SK_CPU_SSE_LEVEL_SSE1 +#endif +#endif +#endif + +inline void RGBA_to_BGRA_portable(uint32_t* dst, const uint32_t* src, + int height, int src_stride, int dst_stride) { + auto width = std::min(src_stride, dst_stride); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + uint8_t a = (src[x] >> 24) & 0xFF, b = (src[x] >> 16) & 0xFF, + g = (src[x] >> 8) & 0xFF, r = (src[x] >> 0) & 0xFF; + dst[x] = (uint32_t)a << 24 | (uint32_t)r << 16 | (uint32_t)g << 8 | + (uint32_t)b << 0; + } + + src += src_stride; + dst += dst_stride; + } +} + +#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SKX + +inline void RGBA_to_BGRA_SKX(uint32_t* dst, const uint32_t* src, int height, + int src_stride, int dst_stride) { + const uint8_t mask[64] = {2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, + 13, 12, 15, 2, 1, 0, 3, 6, 5, 4, 7, 10, 9, + 8, 11, 14, 13, 12, 15, 2, 1, 0, 3, 6, 5, 4, + 7, 10, 9, 8, 11, 14, 13, 12, 15, 2, 1, 0, 3, + 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15}; + const __m512i swapRB = _mm512_loadu_si512(mask); + + auto width = std::min(src_stride, dst_stride); + + for (int y = 0; y < height; y++) { + auto cw = width; + auto rptr = src; + auto dptr = dst; + while (cw >= 16) { + __m512i rgba = _mm512_loadu_si512((const __m512i*)rptr); + __m512i bgra = _mm512_shuffle_epi8(rgba, swapRB); + _mm512_storeu_si512((__m512i*)dptr, bgra); + + rptr += 16; + dptr += 16; + cw -= 16; + } + + for (auto x = 0; x < cw; x++) { + uint8_t a = (rptr[x] >> 24) & 0xFF, b = (rptr[x] >> 16) & 0xFF, + g = (rptr[x] >> 8) & 0xFF, r = (rptr[x] >> 0) & 0xFF; + dptr[x] = (uint32_t)a << 24 | (uint32_t)r << 16 | (uint32_t)g << 8 | + (uint32_t)b << 0; + } + + src += src_stride; + dst += dst_stride; + } +} + +#endif + +#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX2 + +inline void RGBA_to_BGRA_AVX2(uint32_t* dst, const uint32_t* src, int height, + int src_stride, int dst_stride) { + const __m256i swapRB = + _mm256_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15, 2, + 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15); + + auto width = std::min(src_stride, dst_stride); + + for (int y = 0; y < height; y++) { + auto cw = width; + auto rptr = src; + auto dptr = dst; + while (cw >= 8) { + __m256i rgba = _mm256_loadu_si256((const __m256i*)rptr); + __m256i bgra = _mm256_shuffle_epi8(rgba, swapRB); + _mm256_storeu_si256((__m256i*)dptr, bgra); + + rptr += 8; + dptr += 8; + cw -= 8; + } + + for (auto x = 0; x < cw; x++) { + uint8_t a = (rptr[x] >> 24) & 0xFF, b = (rptr[x] >> 16) & 0xFF, + g = (rptr[x] >> 8) & 0xFF, r = (rptr[x] >> 0) & 0xFF; + dptr[x] = (uint32_t)a << 24 | (uint32_t)r << 16 | (uint32_t)g << 8 | + (uint32_t)b << 0; + } + + src += src_stride; + dst += dst_stride; + } +} + +#endif + +inline void RGBA_to_BGRA(uint32_t* dst, const uint32_t* src, int height, + int src_stride, int dst_stride) { + static cpuid::cpuinfo info; + +#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SKX + if (info.has_avx512_f() && info.has_avx512_dq() && info.has_avx512_cd() && + info.has_avx512_bw() && info.has_avx512_vl()) { + return RGBA_to_BGRA_SKX(dst, src, height, src_stride, dst_stride); + } +#endif + +#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX2 + if (info.has_avx2()) { + return RGBA_to_BGRA_AVX2(dst, src, height, src_stride, dst_stride); + } +#endif + + RGBA_to_BGRA_portable(dst, src, height, src_stride, dst_stride); +} diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp index 74eb2287..dfbb46e1 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp @@ -4,6 +4,11 @@ #include #include "in_app_browser/in_app_browser_manager.h" +#include "in_app_webview/in_app_webview_manager.h" + +#pragma comment(lib, "Shlwapi.lib") +#pragma comment(lib, "dxgi.lib") +#pragma comment(lib, "d3d11.lib") namespace flutter_inappwebview_plugin { @@ -18,6 +23,7 @@ namespace flutter_inappwebview_plugin FlutterInappwebviewWindowsPlugin::FlutterInappwebviewWindowsPlugin(flutter::PluginRegistrarWindows* registrar) : registrar(registrar) { + inAppWebViewManager = std::make_unique(this); inAppBrowserManager = std::make_unique(this); } diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h index ffa0a576..9e882ea7 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h @@ -6,11 +6,13 @@ namespace flutter_inappwebview_plugin { + class InAppWebViewManager; class InAppBrowserManager; class FlutterInappwebviewWindowsPlugin : public flutter::Plugin { public: flutter::PluginRegistrarWindows* registrar; + std::unique_ptr inAppWebViewManager; std::unique_ptr inAppBrowserManager; static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index 219ca230..577db80c 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -26,7 +26,7 @@ namespace flutter_inappwebview_plugin m_hWnd = CreateWindowEx( 0, // Optional window styles. - InAppBrowser::CLASS_NAME, // Window class + wndClass.lpszClassName, // Window class L"", // Window text WS_OVERLAPPEDWINDOW, // Window style @@ -35,25 +35,35 @@ namespace flutter_inappwebview_plugin NULL, // Parent window NULL, // Menu - m_hInstance,// Instance handle + wndClass.hInstance,// Instance handle this // Additional application data ); - ShowWindow(m_hWnd, SW_SHOW); + ShowWindow(m_hWnd, settings->hidden ? SW_HIDE : SW_SHOW); InAppWebViewCreationParams webViewParams = { id, params.initialWebViewSettings }; - webView = std::make_unique(plugin, webViewParams, m_hWnd, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id, [this]() -> void + InAppWebView::createInAppWebViewEnv(m_hWnd, false, + [this, webViewParams](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) -> void { - if (channelDelegate) { - channelDelegate->onBrowserCreated(); - } + if (webViewEnv && webViewController) { + webView = std::make_unique(this, this->plugin, webViewParams, m_hWnd, std::move(webViewEnv), std::move(webViewController), nullptr); + webView->initChannel(std::nullopt, InAppBrowser::METHOD_CHANNEL_NAME_PREFIX + id); - if (initialUrlRequest.has_value()) { - webView->loadUrl(initialUrlRequest.value()); + if (channelDelegate) { + channelDelegate->onBrowserCreated(); + } + + if (initialUrlRequest.has_value()) { + webView->loadUrl(initialUrlRequest.value()); + } + } + else { + std::cerr << "Cannot create the InAppWebView instance!" << std::endl; + close(); } }); @@ -148,6 +158,35 @@ namespace flutter_inappwebview_plugin // }).Get()); } + void InAppBrowser::close() const + { + DestroyWindow(m_hWnd); + } + + 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 + { + return !IsWindowVisible(m_hWnd); + } + + void InAppBrowser::didChangeTitle(const std::optional& title) const + { + if (title.has_value()) { + SetWindowText(m_hWnd, ansi_to_wide(title.value()).c_str()); + } + } + LRESULT CALLBACK InAppBrowser::WndProc( HWND window, UINT message, diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h index 9e91cf14..7c6899a3 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h @@ -43,6 +43,13 @@ namespace flutter_inappwebview_plugin InAppBrowser(const FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params); ~InAppBrowser(); + void close() const; + void show() const; + void hide() const; + bool isHidden() const; + + void didChangeTitle(const std::optional& title) const; + private: const HINSTANCE m_hInstance; HWND m_hWnd; diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp index b07181f4..9086497f 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -18,7 +18,7 @@ namespace flutter_inappwebview_plugin { if (method_call.method_name().compare("open") == 0) { auto* arguments = std::get_if(method_call.arguments()); - open(arguments); + createInAppWebView(arguments); result->Success(flutter::EncodableValue(true)); } else { @@ -26,7 +26,7 @@ namespace flutter_inappwebview_plugin } } - void InAppBrowserManager::open(const flutter::EncodableMap* arguments) + void InAppBrowserManager::createInAppWebView(const flutter::EncodableMap* arguments) { auto id = get_fl_map_value(*arguments, "id"); auto urlRequestMap = get_optional_fl_map_value(*arguments, "urlRequest"); diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h index 31180fee..cf77e008 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h @@ -27,7 +27,7 @@ namespace flutter_inappwebview_plugin const flutter::MethodCall& method_call, std::unique_ptr> result); - void open(const flutter::EncodableMap* arguments); + void createInAppWebView(const flutter::EncodableMap* arguments); }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_MANAGER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index 5f1f33dd..bb66d8f9 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -1,77 +1,135 @@ -#pragma comment(lib, "Shlwapi.lib") - #include #include #include #include +#include "../custom_platform_view/util/composition.desktop.interop.h" #include "../types/web_resource_error.h" #include "../types/web_resource_request.h" #include "../utils/strconv.h" #include "../utils/util.h" #include "in_app_webview.h" +#include "in_app_webview_manager.h" namespace flutter_inappwebview_plugin { using namespace Microsoft::WRL; - InAppWebView::InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, const std::function completionHandler) - : plugin(plugin), id(params.id), settings(params.initialSettings), channelDelegate(std::make_unique(this, plugin->registrar->messenger())) + InAppWebView::InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, wil::com_ptr webViewEnv, + wil::com_ptr webViewController, + wil::com_ptr webViewCompositionController) + : plugin(plugin), id(params.id), webViewEnv(std::move(webViewEnv)), webViewController(std::move(webViewController)), webViewCompositionController(std::move(webViewCompositionController)), + settings(params.initialSettings) { - createWebView(parentWindow, completionHandler); + this->webViewController->get_CoreWebView2(webView.put()); + + if (this->webViewCompositionController) { + if (!createSurface(parentWindow, plugin->inAppWebViewManager->compositor())) { + std::cerr << "Cannot create InAppWebView surface\n"; + } + registerSurfaceEventHandlers(); + } + else { + // Resize WebView to fit the bounds of the parent window + RECT bounds; + GetClientRect(parentWindow, &bounds); + this->webViewController->put_Bounds(bounds); + } + + wil::com_ptr webView2Settings; + if (SUCCEEDED(webView->get_Settings(&webView2Settings))) { + webView2Settings->put_IsScriptEnabled(settings->javaScriptEnabled); + webView2Settings->put_IsZoomControlEnabled(settings->supportZoom); + + wil::com_ptr webView2Settings2; + if (SUCCEEDED(webView2Settings->QueryInterface(IID_PPV_ARGS(&webView2Settings2)))) { + if (!settings->userAgent.empty()) { + webView2Settings2->put_UserAgent(ansi_to_wide(settings->userAgent).c_str()); + } + } + } + + registerEventHandlers(); } - InAppWebView::InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, const std::string& channelName, const std::function completionHandler) - : plugin(plugin), id(params.id), settings(params.initialSettings), channelDelegate(std::make_unique(this, plugin->registrar->messenger(), channelName)) + InAppWebView::InAppWebView(InAppBrowser* inAppBrowser, const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, wil::com_ptr webViewEnv, + wil::com_ptr webViewController, + wil::com_ptr webViewCompositionController) + : InAppWebView(plugin, params, parentWindow, std::move(webViewEnv), std::move(webViewController), std::move(webViewCompositionController)) { - createWebView(parentWindow, completionHandler); + this->inAppBrowser = inAppBrowser; } - void InAppWebView::createWebView(const HWND parentWindow, const std::function completionHandler) + void InAppWebView::createInAppWebViewEnv(const HWND parentWindow, const bool willBeSurface, std::function webViewEnv, + wil::com_ptr webViewController, + wil::com_ptr webViewCompositionController)> completionHandler) { CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, Callback( - [parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT + [parentWindow, completionHandler, willBeSurface](HRESULT result, wil::com_ptr env) -> HRESULT { - webViewEnv = env; - // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window HWND - env->CreateCoreWebView2Controller(parentWindow, Callback( - [parentWindow, completionHandler, this](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT - { - if (controller != nullptr) { - webViewController = controller; - webViewController->get_CoreWebView2(webView.put()); - } + if (FAILED(result) || !env) { + completionHandler(nullptr, nullptr, nullptr); + debugLog(getErrorMessage(result)); + return E_FAIL; + } - wil::com_ptr webView2Settings; - if (SUCCEEDED(webView->get_Settings(&webView2Settings))) { - webView2Settings->put_IsScriptEnabled(settings->javaScriptEnabled); - webView2Settings->put_IsZoomControlEnabled(settings->supportZoom); - webView2Settings->put_IsStatusBarEnabled(true); + wil::com_ptr webViewEnv3; + if (willBeSurface && SUCCEEDED(env->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) { + webViewEnv3->CreateCoreWebView2CompositionController(parentWindow, Callback( + [completionHandler, env](HRESULT result, wil::com_ptr compositionController) -> HRESULT + { + wil::com_ptr webViewController = compositionController.try_query(); - wil::com_ptr webView2Settings2; - if (SUCCEEDED(webView2Settings->QueryInterface(IID_PPV_ARGS(&webView2Settings2)))) { - if (!settings->userAgent.empty()) { - webView2Settings2->put_UserAgent(ansi_to_wide(settings->userAgent).c_str()); - } + if (FAILED(result) || !webViewController) { + completionHandler(nullptr, nullptr, nullptr); + debugLog(getErrorMessage(result)); + return E_FAIL; } + + ICoreWebView2Controller3* webViewController3; + HRESULT hr = webViewController->QueryInterface(IID_PPV_ARGS(&webViewController3)); + if (SUCCEEDED(hr)) { + webViewController3->put_BoundsMode(COREWEBVIEW2_BOUNDS_MODE_USE_RAW_PIXELS); + webViewController3->put_ShouldDetectMonitorScaleChanges(FALSE); + webViewController3->put_RasterizationScale(1.0); + } + else { + debugLog(getErrorMessage(hr)); + } + + completionHandler(std::move(env), std::move(webViewController), std::move(compositionController)); + return S_OK; } + ).Get()); + } + else { + env->CreateCoreWebView2Controller(parentWindow, Callback( + [completionHandler, env](HRESULT result, wil::com_ptr controller) -> HRESULT + { + if (FAILED(result) || !controller) { + completionHandler(nullptr, nullptr, nullptr); + debugLog(getErrorMessage(result)); + return E_FAIL; + } - // Resize WebView to fit the bounds of the parent window - RECT bounds; - GetClientRect(parentWindow, &bounds); - webViewController->put_Bounds(bounds); - - registerEventHandlers(); - - completionHandler(); - - return S_OK; - }).Get()); + completionHandler(std::move(env), std::move(controller), nullptr); + return S_OK; + }).Get()); + } return S_OK; }).Get()); } + void InAppWebView::initChannel(const std::optional> viewId, const std::optional channelName) + { + if (viewId.has_value()) { + id = viewId.value(); + } + channelDelegate = channelName.has_value() ? std::make_unique(this, plugin->registrar->messenger(), channelName.value()) : + std::make_unique(this, plugin->registrar->messenger()); + } + void InAppWebView::registerEventHandlers() { if (!webView) { @@ -124,17 +182,17 @@ namespace flutter_inappwebview_plugin UINT64 navigationId; if (SUCCEEDED(args->get_NavigationId(&navigationId))) { - navigationActions.insert({ navigationId, navigationAction }); + navigationActions.insert({ navigationId, navigationAction }); callShouldOverrideUrlLoading_ = } - if (callShouldOverrideUrlLoading && requestMethod == nullptr) { + if (callShouldOverrideUrlLoading_ && requestMethod == nullptr) { // for some reason, we can't cancel and load an URL with other HTTP methods than GET, // so ignore the shouldOverrideUrlLoading event. auto callback = std::make_unique(); callback->nonNullSuccess = [this, urlRequest](const NavigationActionPolicy actionPolicy) { - callShouldOverrideUrlLoading = false; + callShouldOverrideUrlLoading_ = false; if (actionPolicy == allow) { loadUrl(*urlRequest); } @@ -142,7 +200,7 @@ namespace flutter_inappwebview_plugin }; auto defaultBehaviour = [this, urlRequest](const std::optional actionPolicy) { - callShouldOverrideUrlLoading = false; + callShouldOverrideUrlLoading_ = false; loadUrl(*urlRequest); }; callback->defaultBehaviour = defaultBehaviour; @@ -155,7 +213,7 @@ namespace flutter_inappwebview_plugin args->put_Cancel(true); } else { - callShouldOverrideUrlLoading = true; + callShouldOverrideUrlLoading_ = true; channelDelegate->onLoadStart(url); args->put_Cancel(false); } @@ -207,14 +265,53 @@ namespace flutter_inappwebview_plugin return S_OK; } ).Get(), nullptr); + + webView->add_DocumentTitleChanged(Callback( + [this](ICoreWebView2* sender, IUnknown* args) + { + if (channelDelegate) { + wil::unique_cotaskmem_string title; + sender->get_DocumentTitle(&title); + channelDelegate->onTitleChanged(title.is_valid() ? wide_to_ansi(title.get()) : std::optional{}); + } + return S_OK; + } + ).Get(), nullptr); + } + + void InAppWebView::registerSurfaceEventHandlers() + { + if (!webViewCompositionController) { + return; + } + + webViewCompositionController->add_CursorChanged( + Callback( + [this](ICoreWebView2CompositionController* sender, + IUnknown* args) -> HRESULT + { + HCURSOR cursor; + if (cursorChangedCallback_ && + sender->get_Cursor(&cursor) == S_OK) { + cursorChangedCallback_(cursor); + } + return S_OK; + }) + .Get(), nullptr); } std::optional InAppWebView::getUrl() const { - LPWSTR uri = nullptr; + LPWSTR uri; return SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(uri) : std::optional{}; } + std::optional InAppWebView::getTitle() const + { + LPWSTR title; + return SUCCEEDED(webView->get_DocumentTitle(&title)) ? wide_to_utf8(title) : std::optional{}; + } + void InAppWebView::loadUrl(const URLRequest& urlRequest) const { if (!webView || !urlRequest.url.has_value()) { @@ -261,6 +358,338 @@ namespace flutter_inappwebview_plugin } } + void InAppWebView::reload() const + { + webView->Reload(); + } + + void InAppWebView::goBack() const + { + webView->GoBack(); + } + + void InAppWebView::goForward() const + { + webView->GoForward(); + } + + void InAppWebView::evaluateJavascript(const std::string& source, std::function completionHanlder) const + { + webView->ExecuteScript(ansi_to_wide(source).c_str(), + Callback( + [completionHanlder = std::move(completionHanlder)](HRESULT error, PCWSTR result) -> HRESULT + { + if (error != S_OK) { + debugLog(getErrorMessage(error)); + } + completionHanlder(wide_to_ansi(result)); + return S_OK; + }).Get()); + } + + // flutter_view + void InAppWebView::setSurfaceSize(size_t width, size_t height, float scale_factor) + { + if (!webViewController) { + return; + } + + if (surface_ && width > 0 && height > 0) { + scaleFactor_ = scale_factor; + auto scaled_width = width * scale_factor; + auto scaled_height = height * scale_factor; + + RECT bounds; + bounds.left = 0; + bounds.top = 0; + bounds.right = static_cast(scaled_width); + bounds.bottom = static_cast(scaled_height); + + surface_->put_Size({ scaled_width, scaled_height }); + + wil::com_ptr webViewController3; + if (SUCCEEDED(webViewController->QueryInterface(IID_PPV_ARGS(&webViewController3)))) { + webViewController3->put_RasterizationScale(scale_factor); + } + + if (webViewController->put_Bounds(bounds) != S_OK) { + std::cerr << "Setting webview bounds failed." << std::endl; + } + + if (surfaceSizeChangedCallback_) { + surfaceSizeChangedCallback_(width, height); + } + } + } + + void InAppWebView::setCursorPos(double x, double y) + { + if (!webViewCompositionController) { + return; + } + + POINT point; + point.x = static_cast(x * scaleFactor_); + point.y = static_cast(y * scaleFactor_); + lastCursorPos_ = point; + + // https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.774.44 + webViewCompositionController->SendMouseInput( + COREWEBVIEW2_MOUSE_EVENT_KIND::COREWEBVIEW2_MOUSE_EVENT_KIND_MOVE, + virtualKeys_.state(), 0, point); + } + + void InAppWebView::setPointerUpdate(int32_t pointer, + InAppWebViewPointerEventKind eventKind, double x, + double y, double size, double pressure) + { + if (!webViewEnv || !webViewCompositionController) { + return; + } + + COREWEBVIEW2_POINTER_EVENT_KIND event = + COREWEBVIEW2_POINTER_EVENT_KIND_UPDATE; + UINT32 pointerFlags = POINTER_FLAG_NONE; + switch (eventKind) { + case InAppWebViewPointerEventKind::Activate: + event = COREWEBVIEW2_POINTER_EVENT_KIND_ACTIVATE; + break; + case InAppWebViewPointerEventKind::Down: + event = COREWEBVIEW2_POINTER_EVENT_KIND_DOWN; + pointerFlags = + POINTER_FLAG_DOWN | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT; + break; + case InAppWebViewPointerEventKind::Enter: + event = COREWEBVIEW2_POINTER_EVENT_KIND_ENTER; + break; + case InAppWebViewPointerEventKind::Leave: + event = COREWEBVIEW2_POINTER_EVENT_KIND_LEAVE; + break; + case InAppWebViewPointerEventKind::Up: + event = COREWEBVIEW2_POINTER_EVENT_KIND_UP; + pointerFlags = POINTER_FLAG_UP; + break; + case InAppWebViewPointerEventKind::Update: + event = COREWEBVIEW2_POINTER_EVENT_KIND_UPDATE; + pointerFlags = + POINTER_FLAG_UPDATE | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT; + break; + } + + POINT point; + point.x = static_cast(x * scaleFactor_); + point.y = static_cast(y * scaleFactor_); + + RECT rect; + rect.left = point.x - 2; + rect.right = point.x + 2; + rect.top = point.y - 2; + rect.bottom = point.y + 2; + + wil::com_ptr webViewEnv3; + if (SUCCEEDED(webViewEnv->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) { + wil::com_ptr pInfo; + if (SUCCEEDED(webViewEnv3->CreateCoreWebView2PointerInfo(&pInfo))) { + if (pInfo) { + pInfo->put_PointerId(pointer); + pInfo->put_PointerKind(PT_TOUCH); + pInfo->put_PointerFlags(pointerFlags); + pInfo->put_TouchFlags(TOUCH_FLAG_NONE); + pInfo->put_TouchMask(TOUCH_MASK_CONTACTAREA | TOUCH_MASK_PRESSURE); + pInfo->put_TouchPressure( + std::clamp((UINT32)(pressure == 0.0 ? 1024 : 1024 * pressure), + (UINT32)0, (UINT32)1024)); + pInfo->put_PixelLocationRaw(point); + pInfo->put_TouchContactRaw(rect); + webViewCompositionController->SendPointerInput(event, pInfo.get()); + } + } + } + } + + void InAppWebView::setPointerButtonState(InAppWebViewPointerButton button, bool is_down) + { + if (!webViewCompositionController) { + return; + } + + COREWEBVIEW2_MOUSE_EVENT_KIND kind; + switch (button) { + case InAppWebViewPointerButton::Primary: + virtualKeys_.setIsLeftButtonDown(is_down); + kind = is_down ? COREWEBVIEW2_MOUSE_EVENT_KIND_LEFT_BUTTON_DOWN + : COREWEBVIEW2_MOUSE_EVENT_KIND_LEFT_BUTTON_UP; + break; + case InAppWebViewPointerButton::Secondary: + virtualKeys_.setIsRightButtonDown(is_down); + kind = is_down ? COREWEBVIEW2_MOUSE_EVENT_KIND_RIGHT_BUTTON_DOWN + : COREWEBVIEW2_MOUSE_EVENT_KIND_RIGHT_BUTTON_UP; + break; + case InAppWebViewPointerButton::Tertiary: + virtualKeys_.setIsMiddleButtonDown(is_down); + kind = is_down ? COREWEBVIEW2_MOUSE_EVENT_KIND_MIDDLE_BUTTON_DOWN + : COREWEBVIEW2_MOUSE_EVENT_KIND_MIDDLE_BUTTON_UP; + break; + default: + kind = static_cast(0); + } + + webViewCompositionController->SendMouseInput(kind, virtualKeys_.state(), 0, + lastCursorPos_); + } + + void InAppWebView::sendScroll(double delta, bool horizontal) + { + if (!webViewCompositionController) { + return; + } + + // delta * 6 gives me a multiple of WHEEL_DELTA (120) + constexpr auto kScrollMultiplier = 6; + + auto offset = static_cast(delta * kScrollMultiplier); + + /* + // For some reason, + // setting the point other than (x: 0, y: 0) + // will not make the scroll work. + // Unfortunately, this will break the scroll event + // for nested HTML scrollable elements. + POINT point; + point.x = 0; + point.y = 0; + + + if (horizontal) { + webViewCompositionController->SendMouseInput( + COREWEBVIEW2_MOUSE_EVENT_KIND_HORIZONTAL_WHEEL, virtual_keys_.state(), + offset, point); + } + else { + webViewCompositionController->SendMouseInput(COREWEBVIEW2_MOUSE_EVENT_KIND_WHEEL, + virtual_keys_.state(), offset, + point); + } + */ + + // Workaround for scroll events + auto workaroundScrollJS = "(function(horizontal, offset, x, y) { \ + function elemCanScrollY(elem) { \ + if (elem.scrollTop > 0) { \ + return elem; \ + } else { \ + elem.scrollTop++; \ + const top = elem.scrollTop; \ + top && (elem.scrollTop = 0); \ + if (top > 0) { \ + return elem; \ + } else { \ + return elemCanScrollY(elem.parentElement); \ + } \ + } \ + } \ + function elemCanScrollX(elem) { \ + if (elem.scrollLeft > 0) { \ + return elem; \ + } else { \ + elem.scrollLeft++; \ + const left = elem.scrollLeft; \ + left && (elem.scrollLeft = 0); \ + if (left > 0) { \ + return elem; \ + } else { \ + return elemCanScrollX(elem.parentElement); \ + } \ + } \ + } \ + const elem = document.elementFromPoint(x, y); \ + const elem2 = horizontal ? elemCanScrollX(elem) : elemCanScrollY(elem); \ + const handled = elem2 != null && elem2 != document.documentElement && elem2 != document.body; \ + if (handled) { \ + elem2.scrollBy({left: horizontal ? offset : 0, top: horizontal ? 0 : offset}); \ + } \ + return handled; \ +})(" + std::to_string(horizontal) + ", " + std::to_string(offset) + ", " + std::to_string(lastCursorPos_.x) + ", " + std::to_string(lastCursorPos_.y) + ");"; + + webView->ExecuteScript(ansi_to_wide(workaroundScrollJS).c_str(), Callback( + [this, horizontal, offset](HRESULT error, PCWSTR result) -> HRESULT + { + if (webViewCompositionController && (error != S_OK || wide_to_ansi(result).compare("false") == 0)) { + // try to use native mouse wheel handler + + POINT point; + point.x = 0; + point.y = 0; + + if (horizontal) { + webViewCompositionController->SendMouseInput( + COREWEBVIEW2_MOUSE_EVENT_KIND_HORIZONTAL_WHEEL, virtualKeys_.state(), + offset, point); + } + else { + webViewCompositionController->SendMouseInput(COREWEBVIEW2_MOUSE_EVENT_KIND_WHEEL, + virtualKeys_.state(), offset, + point); + } + } + + return S_OK; + }).Get()); + } + + void InAppWebView::setScrollDelta(double delta_x, double delta_y) + { + if (!webViewCompositionController) { + return; + } + + if (delta_x != 0.0) { + sendScroll(delta_x, true); + } + if (delta_y != 0.0) { + sendScroll(delta_y, false); + } + } + + bool InAppWebView::createSurface(const HWND parentWindow, + winrt::com_ptr compositor) + { + if (!webViewCompositionController || !webViewController) { + return false; + } + + winrt::com_ptr root; + if (FAILED(compositor->CreateContainerVisual(root.put()))) { + return false; + } + surface_ = root.try_as(); + assert(surface_); + + // initial size. doesn't matter as we resize the surface anyway. + surface_->put_Size({ 1280, 720 }); + surface_->put_IsVisible(true); + + winrt::com_ptr webview_visual; + compositor->CreateContainerVisual( + reinterpret_cast( + webview_visual.put())); + + auto webview_visual2 = + webview_visual.try_as(); + if (webview_visual2) { + webview_visual2->put_RelativeSizeAdjustment({ 1.0f, 1.0f }); + } + + winrt::com_ptr children; + root->get_Children(children.put()); + children->InsertAtTop(webview_visual.get()); + webViewCompositionController->put_RootVisualTarget(webview_visual2.get()); + + webViewController->put_IsVisible(true); + + return true; + } + bool InAppWebView::isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus) { return webErrorStatus >= COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_COMMON_NAME_IS_INCORRECT && webErrorStatus <= COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID; @@ -276,6 +705,7 @@ namespace flutter_inappwebview_plugin webViewController->Close(); } navigationActions.clear(); + inAppBrowser = nullptr; plugin = nullptr; } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index d8fa450e..61bbca31 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include "../flutter_inappwebview_windows_plugin.h" #include "../types/navigation_action.h" @@ -13,10 +16,59 @@ namespace flutter_inappwebview_plugin { + class InAppBrowser; + using namespace Microsoft::WRL; + // custom_platform_view + enum class InAppWebViewPointerButton { None, Primary, Secondary, Tertiary }; + enum class InAppWebViewPointerEventKind { Activate, Down, Enter, Leave, Up, Update }; + typedef std::function + SurfaceSizeChangedCallback; + typedef std::function CursorChangedCallback; + struct VirtualKeyState { + public: + inline void setIsLeftButtonDown(bool is_down) + { + set(COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS:: + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_LEFT_BUTTON, + is_down); + } + + inline void setIsRightButtonDown(bool is_down) + { + set(COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS:: + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_RIGHT_BUTTON, + is_down); + } + + inline void setIsMiddleButtonDown(bool is_down) + { + set(COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS:: + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_MIDDLE_BUTTON, + is_down); + } + + inline COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS state() const { return state_; } + + private: + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS state_ = + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS:: + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_NONE; + + inline void set(COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS key, bool flag) + { + if (flag) { + state_ |= key; + } + else { + state_ &= ~key; + } + } + }; + struct InAppWebViewCreationParams { - const std::variant id; + const std::variant id; const std::shared_ptr initialSettings; }; @@ -26,26 +78,75 @@ namespace flutter_inappwebview_plugin static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_"; const FlutterInappwebviewWindowsPlugin* plugin; - const std::variant id; + std::variant id; wil::com_ptr webViewEnv; wil::com_ptr webViewController; + wil::com_ptr webViewCompositionController; wil::com_ptr webView; - const std::unique_ptr channelDelegate; + std::unique_ptr channelDelegate; std::map> navigationActions = {}; const std::shared_ptr settings; + InAppBrowser* inAppBrowser = nullptr; - InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, const std::function completionHandler); - InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, const std::string& channelName, const std::function completionHandler); + InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, + wil::com_ptr webViewEnv, + wil::com_ptr webViewController, + wil::com_ptr webViewCompositionController); + InAppWebView(InAppBrowser* inAppBrowser, const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, + wil::com_ptr webViewEnv, + wil::com_ptr webViewController, + wil::com_ptr webViewCompositionController); ~InAppWebView(); + static void createInAppWebViewEnv(const HWND parentWindow, const bool willBeSurface, std::function webViewEnv, + wil::com_ptr webViewController, + wil::com_ptr webViewCompositionController)> completionHandler); + + // custom_platform_view + ABI::Windows::UI::Composition::IVisual* const surface() + { + return surface_.get(); + } + void setSurfaceSize(size_t width, size_t height, float scale_factor); + void setCursorPos(double x, double y); + void setPointerUpdate(int32_t pointer, InAppWebViewPointerEventKind eventKind, + double x, double y, double size, double pressure); + void setPointerButtonState(InAppWebViewPointerButton button, bool isDown); + void sendScroll(double offset, bool horizontal); + void setScrollDelta(double delta_x, double delta_y); + void onSurfaceSizeChanged(SurfaceSizeChangedCallback callback) + { + surfaceSizeChangedCallback_ = std::move(callback); + } + void onCursorChanged(CursorChangedCallback callback) + { + cursorChangedCallback_ = std::move(callback); + } + bool createSurface(const HWND parentWindow, + winrt::com_ptr compositor); + + void initChannel(const std::optional> viewId, const std::optional channelName); std::optional getUrl() const; + std::optional getTitle() const; void loadUrl(const URLRequest& urlRequest) const; + void reload() const; + void goBack() const; + void goForward() const; + void evaluateJavascript(const std::string& source, std::function completionHanlder) const; static bool isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus); private: - bool callShouldOverrideUrlLoading = true; - void createWebView(const HWND parentWindow, const std::function completionHandler); + // custom_platform_view + winrt::com_ptr surface_; + SurfaceSizeChangedCallback surfaceSizeChangedCallback_; + CursorChangedCallback cursorChangedCallback_; + float scaleFactor_ = 1.0; + POINT lastCursorPos_ = { 0, 0 }; + VirtualKeyState virtualKeys_; + + bool callShouldOverrideUrlLoading_ = true; void InAppWebView::registerEventHandlers(); + void InAppWebView::registerSurfaceEventHandlers(); }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp new file mode 100644 index 00000000..408724b3 --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include + +#include "../in_app_webview/in_app_webview_settings.h" +#include "../types/url_request.h" +#include "../utils/flutter.h" +#include "in_app_webview_manager.h" + +namespace flutter_inappwebview_plugin +{ + InAppWebViewManager::InAppWebViewManager(const FlutterInappwebviewWindowsPlugin* plugin) + : plugin(plugin), + ChannelDelegate(plugin->registrar->messenger(), InAppWebViewManager::METHOD_CHANNEL_NAME), + rohelper_(std::make_unique(RO_INIT_SINGLETHREADED)) + { + if (rohelper_->WinRtAvailable()) { + DispatcherQueueOptions options{ sizeof(DispatcherQueueOptions), + DQTYPE_THREAD_CURRENT, DQTAT_COM_STA }; + + if (FAILED(rohelper_->CreateDispatcherQueueController( + options, dispatcher_queue_controller_.put()))) { + std::cerr << "Creating DispatcherQueueController failed." << std::endl; + return; + } + + if (!isGraphicsCaptureSessionSupported()) { + std::cerr << "Windows::Graphics::Capture::GraphicsCaptureSession is not " + "supported." + << std::endl; + return; + } + + graphics_context_ = std::make_unique(rohelper_.get()); + compositor_ = graphics_context_->CreateCompositor(); + valid_ = graphics_context_->IsValid(); + } + + windowClass_.lpszClassName = CustomPlatformView::CLASS_NAME; + windowClass_.lpfnWndProc = &DefWindowProc; + + RegisterClass(&windowClass_); + } + + void InAppWebViewManager::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + auto* arguments = std::get_if(method_call.arguments()); + + if (method_call.method_name().compare("createInAppWebView") == 0) { + if (isSupported()) { + createInAppWebView(arguments, std::move(result)); + } + else { + result->Error("0", "Creating an InAppWebView instance is not supported! Graphics Context is not valid!"); + } + } + else if (method_call.method_name().compare("dispose") == 0) { + auto id = get_fl_map_value(*arguments, "id"); + if (map_contains(webViews, (uint64_t)id)) { + webViews.erase(id); + } + result->Success(); + } + else { + result->NotImplemented(); + } + } + + void InAppWebViewManager::createInAppWebView(const flutter::EncodableMap* arguments, std::unique_ptr> result) + { + auto result_ = std::shared_ptr>(std::move(result)); + + auto settingsMap = get_fl_map_value(*arguments, "initialSettings"); + auto urlRequestMap = get_optional_fl_map_value(*arguments, "initialUrlRequest"); + + auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, CW_DEFAULT, + CW_DEFAULT, 0, 0, HWND_MESSAGE, nullptr, + windowClass_.hInstance, nullptr); + + InAppWebView::createInAppWebViewEnv(hwnd, true, + [=](wil::com_ptr webViewEnv, + wil::com_ptr webViewController, + wil::com_ptr webViewCompositionController) + { + if (webViewEnv && webViewController && webViewCompositionController) { + auto initialSettings = std::make_unique(settingsMap); + + InAppWebViewCreationParams params = { + "", + std::move(initialSettings), + }; + + auto inAppWebView = std::make_unique(plugin, params, hwnd, + std::move(webViewEnv), std::move(webViewController), std::move(webViewCompositionController) + ); + + std::optional urlRequest = urlRequestMap.has_value() ? std::make_optional(urlRequestMap.value()) : std::optional{}; + if (urlRequest.has_value()) { + inAppWebView->loadUrl(urlRequest.value()); + } + + auto customPlatformView = std::make_unique(plugin->registrar->messenger(), + plugin->registrar->texture_registrar(), + graphics_context(), + hwnd, + std::move(inAppWebView)); + + auto textureId = customPlatformView->texture_id(); + + customPlatformView->view->initChannel(textureId, std::nullopt); + + webViews.insert({ textureId, std::move(customPlatformView) }); + + result_->Success(flutter::EncodableValue(textureId)); + } + else { + result_->Error("0", "Cannot create the InAppWebView instance!"); + } + } + ); + } + + bool InAppWebViewManager::isGraphicsCaptureSessionSupported() + { + HSTRING className; + HSTRING_HEADER classNameHeader; + + if (FAILED(rohelper_->GetStringReference( + RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureSession, + &className, &classNameHeader))) { + return false; + } + + ABI::Windows::Graphics::Capture::IGraphicsCaptureSessionStatics* + capture_session_statics; + if (FAILED(rohelper_->GetActivationFactory( + className, + __uuidof( + ABI::Windows::Graphics::Capture::IGraphicsCaptureSessionStatics), + (void**)&capture_session_statics))) { + return false; + } + + boolean is_supported = false; + if (FAILED(capture_session_statics->IsSupported(&is_supported))) { + return false; + } + + return !!is_supported; + } + + InAppWebViewManager::~InAppWebViewManager() + { + debugLog("dealloc InAppWebViewManager"); + webViews.clear(); + UnregisterClass(windowClass_.lpszClassName, nullptr); + plugin = nullptr; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.h new file mode 100644 index 00000000..8630ceeb --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.h @@ -0,0 +1,58 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_MANAGER_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_MANAGER_H_ + +#include +#include +#include +#include +#include +#include + +#include "../custom_platform_view/custom_platform_view.h" +#include "../custom_platform_view/graphics_context.h" +#include "../custom_platform_view/util/rohelper.h" +#include "../flutter_inappwebview_windows_plugin.h" +#include "../types/channel_delegate.h" +#include "windows.ui.composition.h" + +namespace flutter_inappwebview_plugin +{ + class InAppWebViewManager : public ChannelDelegate + { + public: + static inline const std::string METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_inappwebview"; + + const FlutterInappwebviewWindowsPlugin* plugin; + std::map> webViews; + + bool isSupported() const { return valid_; } + bool isGraphicsCaptureSessionSupported(); + GraphicsContext* graphics_context() const + { + return graphics_context_.get(); + }; + rx::RoHelper* rohelper() const { return rohelper_.get(); } + winrt::com_ptr compositor() const + { + return compositor_; + } + + InAppWebViewManager(const FlutterInappwebviewWindowsPlugin* plugin); + ~InAppWebViewManager(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + void createInAppWebView(const flutter::EncodableMap* arguments, std::unique_ptr> result); + private: + std::unique_ptr rohelper_; + winrt::com_ptr + dispatcher_queue_controller_; + std::unique_ptr graphics_context_; + winrt::com_ptr compositor_; + WNDCLASS windowClass_ = {}; + bool valid_ = false; + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_MANAGER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index c6da13e6..6d267177 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -1,3 +1,4 @@ +#include "../in_app_browser/in_app_browser.h" #include "../types/base_callback_result.h" #include "../utils/flutter.h" #include "../utils/strconv.h" @@ -34,15 +35,53 @@ namespace flutter_inappwebview_plugin return; } + auto& arguments = std::get(*method_call.arguments()); + if (method_call.method_name().compare("getUrl") == 0) { result->Success(make_fl_value(webView->getUrl())); } + else if (method_call.method_name().compare("getTitle") == 0) { + result->Success(make_fl_value(webView->getUrl())); + } else if (method_call.method_name().compare("loadUrl") == 0) { - auto& arguments = std::get(*method_call.arguments()); auto urlRequest = std::make_unique(get_fl_map_value(arguments, "urlRequest")); webView->loadUrl(*urlRequest); result->Success(make_fl_value(true)); } + else if (method_call.method_name().compare("reload") == 0) { + webView->reload(); + result->Success(make_fl_value(true)); + } + else if (method_call.method_name().compare("goBack") == 0) { + webView->goBack(); + result->Success(make_fl_value(true)); + } + else if (method_call.method_name().compare("goForward") == 0) { + webView->goForward(); + result->Success(make_fl_value(true)); + } + else if (method_call.method_name().compare("evaluateJavascript") == 0) { + auto result_ = std::shared_ptr>(std::move(result)); + + auto source = get_fl_map_value(arguments, "source"); + webView->evaluateJavascript(source, [result_ = std::move(result_)](const std::string& value) + { + result_->Success(make_fl_value(value)); + }); + } + // for inAppBrowser + else if (webView->inAppBrowser && method_call.method_name().compare("show") == 0) { + webView->inAppBrowser->show(); + result->Success(make_fl_value(true)); + } + else if (webView->inAppBrowser && method_call.method_name().compare("hide") == 0) { + webView->inAppBrowser->hide(); + result->Success(make_fl_value(true)); + } + else if (webView->inAppBrowser && method_call.method_name().compare("close") == 0) { + webView->inAppBrowser->close(); + result->Success(make_fl_value(true)); + } else { result->NotImplemented(); } @@ -108,6 +147,22 @@ namespace flutter_inappwebview_plugin channel->InvokeMethod("onReceivedHttpError", std::move(arguments)); } + void WebViewChannelDelegate::onTitleChanged(const std::optional& title) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap{ + {"title", make_fl_value(title)} + }); + channel->InvokeMethod("onTitleChanged", std::move(arguments)); + + if (webView && webView->inAppBrowser) { + webView->inAppBrowser->didChangeTitle(title); + } + } + WebViewChannelDelegate::~WebViewChannelDelegate() { debugLog("dealloc WebViewChannelDelegate"); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h index 7f950d66..f511a3c6 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h @@ -39,8 +39,9 @@ namespace flutter_inappwebview_plugin void onLoadStart(const std::optional& url) const; void onLoadStop(const std::optional& url) const; void shouldOverrideUrlLoading(std::shared_ptr navigationAction, std::unique_ptr callback) const; - void WebViewChannelDelegate::onReceivedError(std::shared_ptr request, std::shared_ptr error) const; - void WebViewChannelDelegate::onReceivedHttpError(std::shared_ptr request, std::shared_ptr error) const; + void onReceivedError(std::shared_ptr request, std::shared_ptr error) const; + void onReceivedHttpError(std::shared_ptr request, std::shared_ptr error) const; + void onTitleChanged(const std::optional& title) const; }; } diff --git a/flutter_inappwebview_windows/windows/utils/util.h b/flutter_inappwebview_windows/windows/utils/util.h index 2513ddd2..88822691 100644 --- a/flutter_inappwebview_windows/windows/utils/util.h +++ b/flutter_inappwebview_windows/windows/utils/util.h @@ -2,6 +2,7 @@ #define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ #include +#include #include #include #include @@ -48,13 +49,19 @@ namespace flutter_inappwebview_plugin #endif } + static inline std::string getErrorMessage(const HRESULT& error) + { + _com_error err(error); + return wide_to_ansi(err.ErrorMessage()); + } + template static inline std::optional make_pointer_optional(const T* value) { return value == nullptr ? std::nullopt : std::make_optional(*value); } - static inline std::string variant_to_string(const std::variant& var) + static inline std::string variant_to_string(const std::variant& var) { return std::visit([](auto&& arg) { From a8e960c4d002d4d9b568ba2fc3e9dc59d914fb56 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 Jan 2024 01:23:08 +0100 Subject: [PATCH 07/21] windows: added c++ nlohmann.json dependency, added getCopyBackForwardList implementation --- .../platform_inappwebview_controller.dart | 1 + .../windows/CMakeLists.txt | 7 +++ .../windows/in_app_webview/in_app_webview.cpp | 60 +++++++++++++++++-- .../windows/in_app_webview/in_app_webview.h | 6 +- .../webview_channel_delegate.cpp | 23 ++++--- .../windows/types/web_history.cpp | 22 +++++++ .../windows/types/web_history.h | 26 ++++++++ .../windows/types/web_history_item.cpp | 29 +++++++++ .../windows/types/web_history_item.h | 30 ++++++++++ .../windows/utils/util.h | 33 ++++++++++ 10 files changed, 221 insertions(+), 16 deletions(-) create mode 100644 flutter_inappwebview_windows/windows/types/web_history.cpp create mode 100644 flutter_inappwebview_windows/windows/types/web_history.h create mode 100644 flutter_inappwebview_windows/windows/types/web_history_item.cpp create mode 100644 flutter_inappwebview_windows/windows/types/web_history_item.h diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart index a8f2d9ba..9bde75a2 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart @@ -783,6 +783,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ([Official API - WebView.copyBackForwardList](https://developer.android.com/reference/android/webkit/WebView#copyBackForwardList())) ///- iOS ([Official API - WKWebView.backForwardList](https://developer.apple.com/documentation/webkit/wkwebview/1414977-backforwardlist)) ///- MacOS ([Official API - WKWebView.backForwardList](https://developer.apple.com/documentation/webkit/wkwebview/1414977-backforwardlist)) + ///- Windows ///{@endtemplate} Future getCopyBackForwardList() { throw UnimplementedError( diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt index e662976f..4ccf72af 100644 --- a/flutter_inappwebview_windows/windows/CMakeLists.txt +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -6,6 +6,7 @@ cmake_minimum_required(VERSION 3.14) set(WIL_VERSION "1.0.231216.1") set(WEBVIEW_VERSION "1.0.2210.55") +set(NLOHMANN_JSON "3.11.2") message(VERBOSE "CMake system version is ${CMAKE_SYSTEM_VERSION} (using SDK ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION})") @@ -31,6 +32,7 @@ add_custom_command( TARGET ${PROJECT_NAME}_DEPENDENCIES_DOWNLOAD PRE_BUILD COMMAND ${NUGET} install Microsoft.Windows.ImplementationLibrary -Version ${WIL_VERSION} -ExcludeVersion -OutputDirectory ${CMAKE_BINARY_DIR}/packages COMMAND ${NUGET} install Microsoft.Web.WebView2 -Version ${WEBVIEW_VERSION} -ExcludeVersion -OutputDirectory ${CMAKE_BINARY_DIR}/packages + COMMAND ${NUGET} install nlohmann.json -Version ${NLOHMANN_JSON} -ExcludeVersion -OutputDirectory ${CMAKE_BINARY_DIR}/packages DEPENDS ${NUGET} ) @@ -54,6 +56,10 @@ list(APPEND PLUGIN_SOURCES "types/web_resource_request.h" "types/web_resource_response.cpp" "types/web_resource_response.h" + "types/web_history.cpp" + "types/web_history.h" + "types/web_history_item.cpp" + "types/web_history_item.h" "custom_platform_view/custom_platform_view.cc" "custom_platform_view/custom_platform_view.h" "custom_platform_view/texture_bridge.cc" @@ -123,6 +129,7 @@ apply_standard_settings(${PLUGIN_NAME}) target_link_libraries(${PLUGIN_NAME} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Web.WebView2/build/native/Microsoft.Web.WebView2.targets) target_link_libraries(${PLUGIN_NAME} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.ImplementationLibrary/build/native/Microsoft.Windows.ImplementationLibrary.targets) +target_link_libraries(${PLUGIN_NAME} PRIVATE ${CMAKE_BINARY_DIR}/packages/nlohmann.json/build/native/nlohmann.json.targets) # Symbols are hidden by default to reduce the chance of accidental conflicts # between plugins. This should not be removed; any symbols that should be diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index bb66d8f9..85f1077f 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -21,11 +22,15 @@ namespace flutter_inappwebview_plugin : plugin(plugin), id(params.id), webViewEnv(std::move(webViewEnv)), webViewController(std::move(webViewController)), webViewCompositionController(std::move(webViewCompositionController)), settings(params.initialSettings) { - this->webViewController->get_CoreWebView2(webView.put()); + auto hrWebView2 = this->webViewController->get_CoreWebView2(webView.put()); + if (FAILED(hrWebView2)) { + std::cerr << "Cannot create CoreWebView2." << std::endl; + debugLog(getErrorMessage(hrWebView2)); + } if (this->webViewCompositionController) { if (!createSurface(parentWindow, plugin->inAppWebViewManager->compositor())) { - std::cerr << "Cannot create InAppWebView surface\n"; + std::cerr << "Cannot create InAppWebView surface." << std::endl; } registerSurfaceEventHandlers(); } @@ -37,7 +42,8 @@ namespace flutter_inappwebview_plugin } wil::com_ptr webView2Settings; - if (SUCCEEDED(webView->get_Settings(&webView2Settings))) { + auto hrWebView2Settings = webView->get_Settings(&webView2Settings); + if (SUCCEEDED(hrWebView2Settings)) { webView2Settings->put_IsScriptEnabled(settings->javaScriptEnabled); webView2Settings->put_IsZoomControlEnabled(settings->supportZoom); @@ -48,6 +54,9 @@ namespace flutter_inappwebview_plugin } } } + else { + debugLog(getErrorMessage(hrWebView2Settings)); + } registerEventHandlers(); } @@ -182,7 +191,7 @@ namespace flutter_inappwebview_plugin UINT64 navigationId; if (SUCCEEDED(args->get_NavigationId(&navigationId))) { - navigationActions.insert({ navigationId, navigationAction }); callShouldOverrideUrlLoading_ = + navigationActions.insert({ navigationId, navigationAction }); } if (callShouldOverrideUrlLoading_ && requestMethod == nullptr) { @@ -363,16 +372,55 @@ namespace flutter_inappwebview_plugin webView->Reload(); } - void InAppWebView::goBack() const + void InAppWebView::goBack() { + callShouldOverrideUrlLoading_ = false; webView->GoBack(); } - void InAppWebView::goForward() const + void InAppWebView::goForward() { + callShouldOverrideUrlLoading_ = false; webView->GoForward(); } + void InAppWebView::getCopyBackForwardList(const std::function)> completionHandler) const + { + webView->CallDevToolsProtocolMethod(L"Page.getNavigationHistory", L"{}", Callback( + [completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + if (errorCode == S_OK) { + auto historyJson = nlohmann::json::parse(wide_to_ansi(returnObjectAsJson)); + + int64_t currentIndex = historyJson.at("currentIndex").is_number_unsigned() ? historyJson.at("currentIndex").get() : 0; + auto entries = historyJson.at("entries").is_array() ? historyJson.at("entries").get>() : std::vector{}; + + std::vector> webHistoryItems; + webHistoryItems.reserve(entries.size()); + int64_t i = 0; + for (auto const& entry : entries) { + int64_t offset = i - currentIndex; + webHistoryItems.push_back(std::make_shared( + i, + offset, + entry.at("userTypedURL").is_string() ? entry.at("userTypedURL").get() : std::optional{}, + entry.at("title").is_string() ? entry.at("title").get() : std::optional{}, + entry.at("url").is_string() ? entry.at("url").get() : std::optional{} + )); + i++; + } + + completionHandler(std::make_unique(currentIndex, webHistoryItems)); + } + else { + debugLog(getErrorMessage(errorCode)); + } + + return S_OK; + } + ).Get()); + } + void InAppWebView::evaluateJavascript(const std::string& source, std::function completionHanlder) const { webView->ExecuteScript(ansi_to_wide(source).c_str(), diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index 61bbca31..0012d1df 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -11,6 +11,7 @@ #include "../flutter_inappwebview_windows_plugin.h" #include "../types/navigation_action.h" #include "../types/url_request.h" +#include "../types/web_history.h" #include "in_app_webview_settings.h" #include "webview_channel_delegate.h" @@ -130,9 +131,10 @@ namespace flutter_inappwebview_plugin std::optional getTitle() const; void loadUrl(const URLRequest& urlRequest) const; void reload() const; - void goBack() const; - void goForward() const; + void goBack(); + void goForward(); void evaluateJavascript(const std::string& source, std::function completionHanlder) const; + void getCopyBackForwardList(const std::function)> completionHandler) const; static bool isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus); private: diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index 6d267177..066a66aa 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -46,19 +46,19 @@ namespace flutter_inappwebview_plugin else if (method_call.method_name().compare("loadUrl") == 0) { auto urlRequest = std::make_unique(get_fl_map_value(arguments, "urlRequest")); webView->loadUrl(*urlRequest); - result->Success(make_fl_value(true)); + result->Success(true); } else if (method_call.method_name().compare("reload") == 0) { webView->reload(); - result->Success(make_fl_value(true)); + result->Success(true); } else if (method_call.method_name().compare("goBack") == 0) { webView->goBack(); - result->Success(make_fl_value(true)); + result->Success(true); } else if (method_call.method_name().compare("goForward") == 0) { webView->goForward(); - result->Success(make_fl_value(true)); + result->Success(true); } else if (method_call.method_name().compare("evaluateJavascript") == 0) { auto result_ = std::shared_ptr>(std::move(result)); @@ -66,21 +66,28 @@ namespace flutter_inappwebview_plugin auto source = get_fl_map_value(arguments, "source"); webView->evaluateJavascript(source, [result_ = std::move(result_)](const std::string& value) { - result_->Success(make_fl_value(value)); + result_->Success(value); + }); + } + else if (method_call.method_name().compare("getCopyBackForwardList") == 0) { + auto result_ = std::shared_ptr>(std::move(result)); + webView->getCopyBackForwardList([result_ = std::move(result_)](const std::unique_ptr value) + { + result_->Success(value->toEncodableMap()); }); } // for inAppBrowser else if (webView->inAppBrowser && method_call.method_name().compare("show") == 0) { webView->inAppBrowser->show(); - result->Success(make_fl_value(true)); + result->Success(true); } else if (webView->inAppBrowser && method_call.method_name().compare("hide") == 0) { webView->inAppBrowser->hide(); - result->Success(make_fl_value(true)); + result->Success(true); } else if (webView->inAppBrowser && method_call.method_name().compare("close") == 0) { webView->inAppBrowser->close(); - result->Success(make_fl_value(true)); + result->Success(true); } else { result->NotImplemented(); diff --git a/flutter_inappwebview_windows/windows/types/web_history.cpp b/flutter_inappwebview_windows/windows/types/web_history.cpp new file mode 100644 index 00000000..b7cb9dee --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/web_history.cpp @@ -0,0 +1,22 @@ +#include "../utils/util.h" +#include "web_history.h" + +namespace flutter_inappwebview_plugin +{ + WebHistory::WebHistory(const std::optional currentIndex, const std::optional>>& list) + : currentIndex(currentIndex), list(list) + {} + + WebHistory::WebHistory(const flutter::EncodableMap& map) + : currentIndex(get_optional_fl_map_value(map, "currentIndex")), + list(functional_map(get_optional_fl_map_value(map, "list"), [](const flutter::EncodableValue& m) { return std::make_shared(std::get(m)); })) + {} + + flutter::EncodableMap WebHistory::toEncodableMap() const + { + return flutter::EncodableMap{ + {"currentIndex", make_fl_value(currentIndex)}, + {"list", make_fl_value(functional_map(list, [](const std::shared_ptr& item) { return item->toEncodableMap(); }))} + }; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_history.h b/flutter_inappwebview_windows/windows/types/web_history.h new file mode 100644 index 00000000..95c41bad --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/web_history.h @@ -0,0 +1,26 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_HISTORY_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_HISTORY_H_ + +#include +#include + +#include "../utils/flutter.h" +#include "web_history_item.h" + +namespace flutter_inappwebview_plugin +{ + class WebHistory + { + public: + const std::optional currentIndex; + const std::optional>> list; + + WebHistory(const std::optional currentIndex, const std::optional>>& list); + WebHistory(const flutter::EncodableMap& map); + ~WebHistory() = default; + + flutter::EncodableMap toEncodableMap() const; + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_HISTORY_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_history_item.cpp b/flutter_inappwebview_windows/windows/types/web_history_item.cpp new file mode 100644 index 00000000..bc222022 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/web_history_item.cpp @@ -0,0 +1,29 @@ +#include "web_history_item.h" + +namespace flutter_inappwebview_plugin +{ + WebHistoryItem::WebHistoryItem(const std::optional& index, const std::optional& offset, + const std::optional& originalUrl, const std::optional& title, + const std::optional& url) + : index(index), offset(offset), originalUrl(originalUrl), title(title), url(url) + {} + + WebHistoryItem::WebHistoryItem(const flutter::EncodableMap& map) + : index(get_optional_fl_map_value(map, "index")), + offset(get_optional_fl_map_value(map, "offset")), + originalUrl(get_optional_fl_map_value(map, "originalUrl")), + title(get_optional_fl_map_value(map, "title")), + url(get_optional_fl_map_value(map, "url")) + {} + + flutter::EncodableMap WebHistoryItem::toEncodableMap() const + { + return flutter::EncodableMap{ + {"index", make_fl_value(index)}, + {"offset", make_fl_value(offset)}, + {"originalUrl", make_fl_value(originalUrl)}, + {"title", make_fl_value(title)}, + {"url", make_fl_value(url)} + }; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_history_item.h b/flutter_inappwebview_windows/windows/types/web_history_item.h new file mode 100644 index 00000000..8e63c1b7 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/web_history_item.h @@ -0,0 +1,30 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_HISTORY_ITEM_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_HISTORY_ITEM_H_ + +#include +#include + +#include "../utils/flutter.h" + +namespace flutter_inappwebview_plugin +{ + class WebHistoryItem + { + public: + const std::optional index; + const std::optional offset; + const std::optional originalUrl; + const std::optional title; + const std::optional url; + + WebHistoryItem(const std::optional& index, const std::optional& offset, + const std::optional& originalUrl, const std::optional& title, + const std::optional& url); + WebHistoryItem(const flutter::EncodableMap& map); + ~WebHistoryItem() = default; + + flutter::EncodableMap toEncodableMap() const; + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEB_HISTORY_ITEM_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/util.h b/flutter_inappwebview_windows/windows/utils/util.h index 88822691..b4820d5c 100644 --- a/flutter_inappwebview_windows/windows/utils/util.h +++ b/flutter_inappwebview_windows/windows/utils/util.h @@ -87,6 +87,39 @@ namespace flutter_inappwebview_plugin auto itr = map.find(key); return itr != map.end() ? itr->second : nullptr; } + + template + static inline auto functional_map(Iterator begin, Iterator end, Func&& func) -> + std::vector()))> + { + using value_type = decltype(func(std::declval())); + + std::vector out_vector; + out_vector.reserve(std::distance(begin, end)); + + std::transform(begin, end, std::back_inserter(out_vector), + std::forward(func)); + + return out_vector; + } + + template + static inline auto functional_map(const T& iterable, Func&& func) -> + std::vector()))> + { + return functional_map(std::begin(iterable), std::end(iterable), + std::forward(func)); + } + + template + static inline auto functional_map(const std::optional& iterable, Func&& func) -> + std::vector()))> + { + if (!iterable.has_value()) { + return {}; + } + return functional_map(iterable.value(), std::forward(func)); + } } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ \ No newline at end of file From 9a5dae03ba3ab40ca5d9798a948213393c55a864 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 Jan 2024 01:55:55 +0100 Subject: [PATCH 08/21] windows: implemented some other methods and events, initial implementation work for UserScripts and JavaScript handlers --- .../platform_in_app_browser.dart | 1 + .../platform_inappwebview_controller.dart | 8 + .../src/in_app_webview/platform_webview.dart | 7 + .../lib/src/types/navigation_action.dart | 8 +- .../lib/src/types/navigation_action.g.dart | 4 +- .../lib/src/types/navigation_type.dart | 108 +++++- .../lib/src/types/navigation_type.g.dart | 146 ++++++-- .../lib/src/types/web_history_item.dart | 8 +- .../lib/src/types/web_history_item.g.dart | 17 +- .../windows/CMakeLists.txt | 11 + .../custom_platform_view.cc | 1 + .../windows/in_app_browser/in_app_browser.cpp | 3 +- .../in_app_browser_channel_delegate.cpp | 2 +- .../in_app_browser/in_app_browser_manager.cpp | 1 + .../in_app_browser_settings.cpp | 1 + .../windows/in_app_webview/in_app_webview.cpp | 330 +++++++++++++----- .../windows/in_app_webview/in_app_webview.h | 15 + .../in_app_webview/in_app_webview_manager.cpp | 1 + .../in_app_webview_settings.cpp | 1 + .../user_content_controller.cpp | 121 +++++++ .../in_app_webview/user_content_controller.h | 45 +++ .../webview_channel_delegate.cpp | 43 ++- .../in_app_webview/webview_channel_delegate.h | 1 + .../javascript_bridge_js.cpp | 26 ++ .../plugin_scripts_js/javascript_bridge_js.h | 17 + .../windows/types/navigation_action.cpp | 10 +- .../windows/types/navigation_action.h | 11 +- .../windows/types/plugin_script.cpp | 14 + .../windows/types/plugin_script.h | 21 ++ .../windows/types/user_script.cpp | 15 + .../windows/types/user_script.h | 33 ++ .../windows/types/web_history_item.cpp | 8 +- .../windows/types/web_history_item.h | 3 +- .../windows/utils/flutter.h | 2 + .../windows/utils/log.h | 67 ++++ .../windows/utils/map.h | 71 ++++ .../windows/utils/util.h | 94 ----- .../windows/utils/vector.h | 41 +++ 38 files changed, 1084 insertions(+), 232 deletions(-) create mode 100644 flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.cpp create mode 100644 flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.h create mode 100644 flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.cpp create mode 100644 flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.h create mode 100644 flutter_inappwebview_windows/windows/types/plugin_script.cpp create mode 100644 flutter_inappwebview_windows/windows/types/plugin_script.h create mode 100644 flutter_inappwebview_windows/windows/types/user_script.cpp create mode 100644 flutter_inappwebview_windows/windows/types/user_script.h create mode 100644 flutter_inappwebview_windows/windows/utils/log.h create mode 100644 flutter_inappwebview_windows/windows/utils/map.h create mode 100644 flutter_inappwebview_windows/windows/utils/vector.h diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart index acf5da2d..6574d899 100755 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart @@ -899,6 +899,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ([Official API - WebViewClient.doUpdateVisitedHistory](https://developer.android.com/reference/android/webkit/WebViewClient#doUpdateVisitedHistory(android.webkit.WebView,%20java.lang.String,%20boolean))) ///- iOS ///- MacOS + ///- Windows ([Official API - IWebView2WebView.add_HistoryChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_historychanged)) void onUpdateVisitedHistory(WebUri? url, bool? isReload) {} ///Use [onPrintRequest] instead diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart index 9bde75a2..1276f2b6 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart @@ -377,6 +377,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ([Official API - WebView.canGoBack](https://developer.android.com/reference/android/webkit/WebView#canGoBack())) ///- iOS ([Official API - WKWebView.canGoBack](https://developer.apple.com/documentation/webkit/wkwebview/1414966-cangoback)) ///- MacOS ([Official API - WKWebView.canGoBack](https://developer.apple.com/documentation/webkit/wkwebview/1414966-cangoback)) + ///- Windows ([Official API - IWebView2WebView.get_CanGoBack](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_cangoback)) ///{@endtemplate} Future canGoBack() { throw UnimplementedError( @@ -407,6 +408,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ([Official API - WebView.canGoForward](https://developer.android.com/reference/android/webkit/WebView#canGoForward())) ///- iOS ([Official API - WKWebView.canGoForward](https://developer.apple.com/documentation/webkit/wkwebview/1414962-cangoforward)) ///- MacOS ([Official API - WKWebView.canGoForward](https://developer.apple.com/documentation/webkit/wkwebview/1414962-cangoforward)) + ///- Windows ([Official API - IWebView2WebView.get_CanGoForward](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_cangoforward)) ///{@endtemplate} Future canGoForward() { throw UnimplementedError( @@ -423,6 +425,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.go](https://developer.apple.com/documentation/webkit/wkwebview/1414991-go)) ///- MacOS ([Official API - WKWebView.go](https://developer.apple.com/documentation/webkit/wkwebview/1414991-go)) ///- Web ([Official API - History.go](https://developer.mozilla.org/en-US/docs/Web/API/History/go)) + ///- Windows ///{@endtemplate} Future goBackOrForward({required int steps}) { throw UnimplementedError( @@ -436,6 +439,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ([Official API - WebView.canGoBackOrForward](https://developer.android.com/reference/android/webkit/WebView#canGoBackOrForward(int))) ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future canGoBackOrForward({required int steps}) { throw UnimplementedError( @@ -452,6 +456,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ///- MacOS ///- Web + ///- Windows ///{@endtemplate} Future goTo({required WebHistoryItem historyItem}) { throw UnimplementedError('goTo is not implemented on the current platform'); @@ -465,6 +470,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ///- MacOS ///- Web + ///- Windows ///{@endtemplate} Future isLoading() { throw UnimplementedError( @@ -481,6 +487,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.stopLoading](https://developer.apple.com/documentation/webkit/wkwebview/1414981-stoploading)) ///- MacOS ([Official API - WKWebView.stopLoading](https://developer.apple.com/documentation/webkit/wkwebview/1414981-stoploading)) ///- Web ([Official API - Window.stop](https://developer.mozilla.org/en-US/docs/Web/API/Window/stop)) + ///- Windows ([Official API - IWebView2WebView.Stop](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#stop)) ///{@endtemplate} Future stopLoading() { throw UnimplementedError( @@ -558,6 +565,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ///- MacOS ///- Web + ///- Windows ///{@endtemplate} Future injectJavascriptFileFromAsset( {required String assetFilePath}) { diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_webview.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_webview.dart index a4d4890d..4949d48b 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_webview.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_webview.dart @@ -38,6 +38,7 @@ class PlatformWebViewCreationParams { ///- iOS ///- MacOS ///- Web + ///- Windows ///{@endtemplate} final void Function(T controller)? onWebViewCreated; @@ -54,6 +55,7 @@ class PlatformWebViewCreationParams { ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview)) ///- Web + ///- Windows ([Official API - IWebView2WebView.add_NavigationStarting](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationstarting)) ///{@endtemplate} final void Function(T controller, WebUri? url)? onLoadStart; @@ -68,6 +70,7 @@ class PlatformWebViewCreationParams { ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) ///- Web ([Official API - Window.onload](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event)) + ///- Windows ([Official API - IWebView2WebView.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) ///{@endtemplate} final void Function(T controller, WebUri? url)? onLoadStop; @@ -83,6 +86,7 @@ class PlatformWebViewCreationParams { ///- Android native WebView ([Official API - WebViewClient.onReceivedError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceError))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) + ///- Windows ([Official API - IWebView2WebView.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) ///{@endtemplate} final void Function( T controller, WebResourceRequest request, WebResourceError error)? @@ -107,6 +111,7 @@ class PlatformWebViewCreationParams { ///- Android native WebView ([Official API - WebViewClient.onReceivedHttpError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) + ///- Windows ([Official API - IWebView2WebView.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) ///{@endtemplate} final void Function(T controller, WebResourceRequest request, WebResourceResponse errorResponse)? onReceivedHttpError; @@ -499,6 +504,7 @@ class PlatformWebViewCreationParams { ///- iOS ///- MacOS ///- Web + ///- Windows ([Official API - IWebView2WebView.add_HistoryChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_historychanged)) ///{@endtemplate} final void Function(T controller, WebUri? url, bool? isReload)? onUpdateVisitedHistory; @@ -593,6 +599,7 @@ class PlatformWebViewCreationParams { ///- iOS ///- MacOS ///- Web + ///- Windows ([Official API - IWebView2WebView.add_DocumentTitleChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_documenttitlechanged)) ///{@endtemplate} final void Function(T controller, String? title)? onTitleChanged; diff --git a/flutter_inappwebview_platform_interface/lib/src/types/navigation_action.dart b/flutter_inappwebview_platform_interface/lib/src/types/navigation_action.dart index d2495be5..1d12aa35 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/navigation_action.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/navigation_action.dart @@ -54,7 +54,8 @@ class NavigationAction_ { available: "21", apiName: "WebResourceRequest.isRedirect", apiUrl: - "https://developer.android.com/reference/android/webkit/WebResourceRequest#isRedirect()") + "https://developer.android.com/reference/android/webkit/WebResourceRequest#isRedirect()"), + WindowsPlatform() ]) bool? isRedirect; @@ -62,7 +63,7 @@ class NavigationAction_ { @Deprecated("Use navigationType instead") IOSWKNavigationType_? iosWKNavigationType; - ///The type of action triggering the navigation.ì + ///The type of action triggering the navigation. @SupportedPlatforms(platforms: [ IOSPlatform( apiName: "WKNavigationAction.navigationType", @@ -71,7 +72,8 @@ class NavigationAction_ { MacOSPlatform( apiName: "WKNavigationAction.navigationType", apiUrl: - "https://developer.apple.com/documentation/webkit/wknavigationaction/1401914-navigationtype") + "https://developer.apple.com/documentation/webkit/wknavigationaction/1401914-navigationtype"), + WindowsPlatform() ]) NavigationType_? navigationType; diff --git a/flutter_inappwebview_platform_interface/lib/src/types/navigation_action.g.dart b/flutter_inappwebview_platform_interface/lib/src/types/navigation_action.g.dart index 9b24689e..64593a9f 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/navigation_action.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/navigation_action.g.dart @@ -52,13 +52,15 @@ class NavigationAction { /// ///**Officially Supported Platforms/Implementations**: ///- Android native WebView 21+ ([Official API - WebResourceRequest.isRedirect](https://developer.android.com/reference/android/webkit/WebResourceRequest#isRedirect())) + ///- Windows bool? isRedirect; - ///The type of action triggering the navigation.ì + ///The type of action triggering the navigation. /// ///**Officially Supported Platforms/Implementations**: ///- iOS ([Official API - WKNavigationAction.navigationType](https://developer.apple.com/documentation/webkit/wknavigationaction/1401914-navigationtype)) ///- MacOS ([Official API - WKNavigationAction.navigationType](https://developer.apple.com/documentation/webkit/wknavigationaction/1401914-navigationtype)) + ///- Windows NavigationType? navigationType; ///The URL request object associated with the navigation action. diff --git a/flutter_inappwebview_platform_interface/lib/src/types/navigation_type.dart b/flutter_inappwebview_platform_interface/lib/src/types/navigation_type.dart index 72c07c8c..297046fa 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/navigation_type.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/navigation_type.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; import '../in_app_webview/platform_webview.dart'; part 'navigation_type.g.dart'; @@ -6,26 +7,119 @@ part 'navigation_type.g.dart'; @ExchangeableEnum() class NavigationType_ { // ignore: unused_field - final int _value; + final String _value; + // ignore: unused_field + final int? _nativeValue = null; + const NavigationType_._internal(this._value); ///A link with an href attribute was activated by the user. - static const LINK_ACTIVATED = const NavigationType_._internal(0); + @EnumSupportedPlatforms(platforms: [ + EnumIOSPlatform( + apiName: 'WKNavigationType.linkActivated', + apiUrl: + 'https://developer.apple.com/documentation/webkit/wknavigationtype/linkactivated', + value: 0), + EnumMacOSPlatform( + apiName: 'WKNavigationType.linkActivated', + apiUrl: + 'https://developer.apple.com/documentation/webkit/wknavigationtype/linkactivated', + value: 0), + EnumWindowsPlatform( + value: 0 + ), + ]) + static const LINK_ACTIVATED = const NavigationType_._internal('LINK_ACTIVATED'); ///A form was submitted. - static const FORM_SUBMITTED = const NavigationType_._internal(1); + @EnumSupportedPlatforms(platforms: [ + EnumIOSPlatform( + apiName: 'WKNavigationType.formSubmitted', + apiUrl: + 'https://developer.apple.com/documentation/webkit/wknavigationtype/formsubmitted', + value: 1), + EnumMacOSPlatform( + apiName: 'WKNavigationType.formSubmitted', + apiUrl: + 'https://developer.apple.com/documentation/webkit/wknavigationtype/formsubmitted', + value: 1), + ]) + static const FORM_SUBMITTED = const NavigationType_._internal('FORM_SUBMITTED'); ///An item from the back-forward list was requested. - static const BACK_FORWARD = const NavigationType_._internal(2); + @EnumSupportedPlatforms(platforms: [ + EnumIOSPlatform( + apiName: 'WKNavigationType.formSubmitted', + apiUrl: + 'https://developer.apple.com/documentation/webkit/wknavigationtype/formsubmitted', + value: 2), + EnumMacOSPlatform( + apiName: 'WKNavigationType.formSubmitted', + apiUrl: + 'https://developer.apple.com/documentation/webkit/wknavigationtype/formsubmitted', + value: 2), + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_NAVIGATION_KIND_BACK_OR_FORWARD', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_navigation_kind', + value: 1 + ), + ]) + static const BACK_FORWARD = const NavigationType_._internal('BACK_FORWARD'); ///The webpage was reloaded. - static const RELOAD = const NavigationType_._internal(3); + @EnumSupportedPlatforms(platforms: [ + EnumIOSPlatform( + apiName: 'WKNavigationType.reload', + apiUrl: + 'https://developer.apple.com/documentation/webkit/wknavigationtype/reload', + value: 3), + EnumMacOSPlatform( + apiName: 'WKNavigationType.reload', + apiUrl: + 'https://developer.apple.com/documentation/webkit/wknavigationtype/reload', + value: 3), + EnumWindowsPlatform( + apiName: 'COREWEBVIEW2_NAVIGATION_KIND_RELOAD', + apiUrl: + 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_navigation_kind', + value: 2 + ), + ]) + static const RELOAD = const NavigationType_._internal('RELOAD'); ///A form was resubmitted (for example by going back, going forward, or reloading). - static const FORM_RESUBMITTED = const NavigationType_._internal(4); + @EnumSupportedPlatforms(platforms: [ + EnumIOSPlatform( + apiName: 'WKNavigationType.formSubmitted', + apiUrl: + 'https://developer.apple.com/documentation/webkit/wknavigationtype/formresubmitted', + value: 4), + EnumMacOSPlatform( + apiName: 'WKNavigationType.formSubmitted', + apiUrl: + 'https://developer.apple.com/documentation/webkit/wknavigationtype/formresubmitted', + value: 4), + ]) + static const FORM_RESUBMITTED = const NavigationType_._internal('FORM_RESUBMITTED'); ///Navigation is taking place for some other reason. - static const OTHER = const NavigationType_._internal(-1); + @EnumSupportedPlatforms(platforms: [ + EnumIOSPlatform( + apiName: 'WKNavigationType.other', + apiUrl: + 'https://developer.apple.com/documentation/webkit/wknavigationtype/other', + value: -1), + EnumMacOSPlatform( + apiName: 'WKNavigationType.other', + apiUrl: + 'https://developer.apple.com/documentation/webkit/wknavigationtype/other', + value: -1), + EnumWindowsPlatform( + value: 3 + ), + ]) + static const OTHER = const NavigationType_._internal('OTHER'); } ///Class that represents the type of action triggering a navigation on iOS for the [PlatformWebViewCreationParams.shouldOverrideUrlLoading] event. diff --git a/flutter_inappwebview_platform_interface/lib/src/types/navigation_type.g.dart b/flutter_inappwebview_platform_interface/lib/src/types/navigation_type.g.dart index bdf1b525..7615cd66 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/navigation_type.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/navigation_type.g.dart @@ -8,31 +8,131 @@ part of 'navigation_type.dart'; ///Class that represents the type of action triggering a navigation for the [PlatformWebViewCreationParams.shouldOverrideUrlLoading] event. class NavigationType { - final int _value; - final int _nativeValue; + final String _value; + final int? _nativeValue; const NavigationType._internal(this._value, this._nativeValue); // ignore: unused_element factory NavigationType._internalMultiPlatform( - int value, Function nativeValue) => + String value, Function nativeValue) => NavigationType._internal(value, nativeValue()); ///An item from the back-forward list was requested. - static const BACK_FORWARD = NavigationType._internal(2, 2); + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - WKNavigationType.formSubmitted](https://developer.apple.com/documentation/webkit/wknavigationtype/formsubmitted)) + ///- MacOS ([Official API - WKNavigationType.formSubmitted](https://developer.apple.com/documentation/webkit/wknavigationtype/formsubmitted)) + ///- Windows ([Official API - COREWEBVIEW2_NAVIGATION_KIND_BACK_OR_FORWARD](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_navigation_kind)) + static final BACK_FORWARD = + NavigationType._internalMultiPlatform('BACK_FORWARD', () { + switch (defaultTargetPlatform) { + case TargetPlatform.iOS: + return 2; + case TargetPlatform.macOS: + return 2; + case TargetPlatform.windows: + return 1; + default: + break; + } + return null; + }); ///A form was resubmitted (for example by going back, going forward, or reloading). - static const FORM_RESUBMITTED = NavigationType._internal(4, 4); + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - WKNavigationType.formSubmitted](https://developer.apple.com/documentation/webkit/wknavigationtype/formresubmitted)) + ///- MacOS ([Official API - WKNavigationType.formSubmitted](https://developer.apple.com/documentation/webkit/wknavigationtype/formresubmitted)) + static final FORM_RESUBMITTED = + NavigationType._internalMultiPlatform('FORM_RESUBMITTED', () { + switch (defaultTargetPlatform) { + case TargetPlatform.iOS: + return 4; + case TargetPlatform.macOS: + return 4; + default: + break; + } + return null; + }); ///A form was submitted. - static const FORM_SUBMITTED = NavigationType._internal(1, 1); + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - WKNavigationType.formSubmitted](https://developer.apple.com/documentation/webkit/wknavigationtype/formsubmitted)) + ///- MacOS ([Official API - WKNavigationType.formSubmitted](https://developer.apple.com/documentation/webkit/wknavigationtype/formsubmitted)) + static final FORM_SUBMITTED = + NavigationType._internalMultiPlatform('FORM_SUBMITTED', () { + switch (defaultTargetPlatform) { + case TargetPlatform.iOS: + return 1; + case TargetPlatform.macOS: + return 1; + default: + break; + } + return null; + }); ///A link with an href attribute was activated by the user. - static const LINK_ACTIVATED = NavigationType._internal(0, 0); + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - WKNavigationType.linkActivated](https://developer.apple.com/documentation/webkit/wknavigationtype/linkactivated)) + ///- MacOS ([Official API - WKNavigationType.linkActivated](https://developer.apple.com/documentation/webkit/wknavigationtype/linkactivated)) + ///- Windows + static final LINK_ACTIVATED = + NavigationType._internalMultiPlatform('LINK_ACTIVATED', () { + switch (defaultTargetPlatform) { + case TargetPlatform.iOS: + return 0; + case TargetPlatform.macOS: + return 0; + case TargetPlatform.windows: + return 0; + default: + break; + } + return null; + }); ///Navigation is taking place for some other reason. - static const OTHER = NavigationType._internal(-1, -1); + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - WKNavigationType.other](https://developer.apple.com/documentation/webkit/wknavigationtype/other)) + ///- MacOS ([Official API - WKNavigationType.other](https://developer.apple.com/documentation/webkit/wknavigationtype/other)) + ///- Windows + static final OTHER = NavigationType._internalMultiPlatform('OTHER', () { + switch (defaultTargetPlatform) { + case TargetPlatform.iOS: + return -1; + case TargetPlatform.macOS: + return -1; + case TargetPlatform.windows: + return 3; + default: + break; + } + return null; + }); ///The webpage was reloaded. - static const RELOAD = NavigationType._internal(3, 3); + /// + ///**Officially Supported Platforms/Implementations**: + ///- iOS ([Official API - WKNavigationType.reload](https://developer.apple.com/documentation/webkit/wknavigationtype/reload)) + ///- MacOS ([Official API - WKNavigationType.reload](https://developer.apple.com/documentation/webkit/wknavigationtype/reload)) + ///- Windows ([Official API - COREWEBVIEW2_NAVIGATION_KIND_RELOAD](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#corewebview2_navigation_kind)) + static final RELOAD = NavigationType._internalMultiPlatform('RELOAD', () { + switch (defaultTargetPlatform) { + case TargetPlatform.iOS: + return 3; + case TargetPlatform.macOS: + return 3; + case TargetPlatform.windows: + return 2; + default: + break; + } + return null; + }); ///Set of all values of [NavigationType]. static final Set values = [ @@ -44,8 +144,8 @@ class NavigationType { NavigationType.RELOAD, ].toSet(); - ///Gets a possible [NavigationType] instance from [int] value. - static NavigationType? fromValue(int? value) { + ///Gets a possible [NavigationType] instance from [String] value. + static NavigationType? fromValue(String? value) { if (value != null) { try { return NavigationType.values @@ -70,11 +170,11 @@ class NavigationType { return null; } - ///Gets [int] value. - int toValue() => _value; + ///Gets [String] value. + String toValue() => _value; - ///Gets [int] native value. - int toNativeValue() => _nativeValue; + ///Gets [int?] native value. + int? toNativeValue() => _nativeValue; @override int get hashCode => _value.hashCode; @@ -84,21 +184,7 @@ class NavigationType { @override String toString() { - switch (_value) { - case 2: - return 'BACK_FORWARD'; - case 4: - return 'FORM_RESUBMITTED'; - case 1: - return 'FORM_SUBMITTED'; - case 0: - return 'LINK_ACTIVATED'; - case -1: - return 'OTHER'; - case 3: - return 'RELOAD'; - } - return _value.toString(); + return _value; } } diff --git a/flutter_inappwebview_platform_interface/lib/src/types/web_history_item.dart b/flutter_inappwebview_platform_interface/lib/src/types/web_history_item.dart index 84fc615f..973f85d3 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/web_history_item.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/web_history_item.dart @@ -24,6 +24,12 @@ class WebHistoryItem_ { ///Position offset respect to the currentIndex of the back-forward [WebHistory.list]. int? offset; + ///Unique id of the navigation history entry. + @SupportedPlatforms(platforms: [ + WindowsPlatform() + ]) + int? entryId; + WebHistoryItem_( - {this.originalUrl, this.title, this.url, this.index, this.offset}); + {this.originalUrl, this.title, this.url, this.index, this.offset, this.entryId}); } diff --git a/flutter_inappwebview_platform_interface/lib/src/types/web_history_item.g.dart b/flutter_inappwebview_platform_interface/lib/src/types/web_history_item.g.dart index 790e503d..bcffea28 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/web_history_item.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/web_history_item.g.dart @@ -9,6 +9,12 @@ part of 'web_history_item.dart'; ///A convenience class for accessing fields in an entry in the back/forward list of a `WebView`. ///Each [WebHistoryItem] is a snapshot of the requested history item. class WebHistoryItem { + ///Unique id of the navigation history entry. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows + int? entryId; + ///0-based position index in the back-forward [WebHistory.list]. int? index; @@ -24,7 +30,12 @@ class WebHistoryItem { ///Url of this history item. WebUri? url; WebHistoryItem( - {this.index, this.offset, this.originalUrl, this.title, this.url}); + {this.entryId, + this.index, + this.offset, + this.originalUrl, + this.title, + this.url}); ///Gets a possible [WebHistoryItem] instance from a [Map] value. static WebHistoryItem? fromMap(Map? map) { @@ -32,6 +43,7 @@ class WebHistoryItem { return null; } final instance = WebHistoryItem( + entryId: map['entryId'], index: map['index'], offset: map['offset'], originalUrl: @@ -45,6 +57,7 @@ class WebHistoryItem { ///Converts instance to a map. Map toMap() { return { + "entryId": entryId, "index": index, "offset": offset, "originalUrl": originalUrl?.toString(), @@ -60,6 +73,6 @@ class WebHistoryItem { @override String toString() { - return 'WebHistoryItem{index: $index, offset: $offset, originalUrl: $originalUrl, title: $title, url: $url}'; + return 'WebHistoryItem{entryId: $entryId, index: $index, offset: $offset, originalUrl: $originalUrl, title: $title, url: $url}'; } } diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt index 4ccf72af..998a399b 100644 --- a/flutter_inappwebview_windows/windows/CMakeLists.txt +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -40,7 +40,10 @@ add_custom_command( list(APPEND PLUGIN_SOURCES "flutter_inappwebview_windows_plugin.cpp" "flutter_inappwebview_windows_plugin.h" + "utils/log.h" "utils/strconv.h" + "utils/map.h" + "utils/vector.h" "utils/util.h" "utils/flutter.h" "types/channel_delegate.cpp" @@ -60,6 +63,10 @@ list(APPEND PLUGIN_SOURCES "types/web_history.h" "types/web_history_item.cpp" "types/web_history_item.h" + "types/user_script.cpp" + "types/user_script.h" + "types/plugin_script.cpp" + "types/plugin_script.h" "custom_platform_view/custom_platform_view.cc" "custom_platform_view/custom_platform_view.h" "custom_platform_view/texture_bridge.cc" @@ -73,6 +80,10 @@ list(APPEND PLUGIN_SOURCES "custom_platform_view/util/string_converter.cc" "custom_platform_view/util/string_converter.h" "custom_platform_view/util/swizzle.h" + "plugin_scripts_js/javascript_bridge_js.cpp" + "plugin_scripts_js/javascript_bridge_js.h" + "in_app_webview/user_content_controller.cpp" + "in_app_webview/user_content_controller.h" "in_app_webview/in_app_webview_settings.cpp" "in_app_webview/in_app_webview_settings.h" "in_app_webview/in_app_webview.cpp" diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.cc b/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.cc index e8de9cc4..34579e12 100644 --- a/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.cc +++ b/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.cc @@ -1,3 +1,4 @@ +#include "../utils/log.h" #include "custom_platform_view.h" #include diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index 577db80c..824602a1 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -1,6 +1,7 @@ #include -#include "../utils/util.h" +#include "../utils/log.h" +#include "../utils/strconv.h" #include "in_app_browser.h" #include "in_app_browser_manager.h" diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp index d3bc5961..34aa966e 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_channel_delegate.cpp @@ -1,4 +1,4 @@ -#include "../utils/util.h" +#include "../utils/log.h" #include "in_app_browser.h" #include "in_app_browser_channel_delegate.h" diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp index 9086497f..efdd422a 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -4,6 +4,7 @@ #include "../in_app_webview/in_app_webview_settings.h" #include "../types/url_request.h" #include "../utils/flutter.h" +#include "../utils/log.h" #include "in_app_browser_manager.h" #include "in_app_browser_settings.h" diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.cpp index 4b5d9594..80f1fa70 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.cpp @@ -1,4 +1,5 @@ #include "../utils/flutter.h" +#include "../utils/log.h" #include "in_app_browser_settings.h" namespace flutter_inappwebview_plugin diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index 85f1077f..2d601f4d 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -5,10 +5,11 @@ #include #include "../custom_platform_view/util/composition.desktop.interop.h" +#include "../plugin_scripts_js/javascript_bridge_js.h" #include "../types/web_resource_error.h" #include "../types/web_resource_request.h" +#include "../utils/log.h" #include "../utils/strconv.h" -#include "../utils/util.h" #include "in_app_webview.h" #include "in_app_webview_manager.h" @@ -19,13 +20,12 @@ namespace flutter_inappwebview_plugin InAppWebView::InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) - : plugin(plugin), id(params.id), webViewEnv(std::move(webViewEnv)), webViewController(std::move(webViewController)), webViewCompositionController(std::move(webViewCompositionController)), - settings(params.initialSettings) + : plugin(plugin), id(params.id), + webViewEnv(std::move(webViewEnv)), webViewController(std::move(webViewController)), webViewCompositionController(std::move(webViewCompositionController)), + settings(params.initialSettings), userContentController(std::make_unique(this)) { - auto hrWebView2 = this->webViewController->get_CoreWebView2(webView.put()); - if (FAILED(hrWebView2)) { + if (failedAndLog(this->webViewController->get_CoreWebView2(webView.put()))) { std::cerr << "Cannot create CoreWebView2." << std::endl; - debugLog(getErrorMessage(hrWebView2)); } if (this->webViewCompositionController) { @@ -41,24 +41,7 @@ namespace flutter_inappwebview_plugin this->webViewController->put_Bounds(bounds); } - wil::com_ptr webView2Settings; - auto hrWebView2Settings = webView->get_Settings(&webView2Settings); - if (SUCCEEDED(hrWebView2Settings)) { - webView2Settings->put_IsScriptEnabled(settings->javaScriptEnabled); - webView2Settings->put_IsZoomControlEnabled(settings->supportZoom); - - wil::com_ptr webView2Settings2; - if (SUCCEEDED(webView2Settings->QueryInterface(IID_PPV_ARGS(&webView2Settings2)))) { - if (!settings->userAgent.empty()) { - webView2Settings2->put_UserAgent(ansi_to_wide(settings->userAgent).c_str()); - } - } - } - else { - debugLog(getErrorMessage(hrWebView2Settings)); - } - - registerEventHandlers(); + prepare(); } InAppWebView::InAppWebView(InAppBrowser* inAppBrowser, const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, wil::com_ptr webViewEnv, @@ -73,61 +56,54 @@ namespace flutter_inappwebview_plugin wil::com_ptr webViewController, wil::com_ptr webViewCompositionController)> completionHandler) { - CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, + failedLog(CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, Callback( [parentWindow, completionHandler, willBeSurface](HRESULT result, wil::com_ptr env) -> HRESULT { - if (FAILED(result) || !env) { + if (failedAndLog(result) || !env) { completionHandler(nullptr, nullptr, nullptr); - debugLog(getErrorMessage(result)); return E_FAIL; } wil::com_ptr webViewEnv3; - if (willBeSurface && SUCCEEDED(env->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) { - webViewEnv3->CreateCoreWebView2CompositionController(parentWindow, Callback( + if (willBeSurface && succeededOrLog(env->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) { + failedLog(webViewEnv3->CreateCoreWebView2CompositionController(parentWindow, Callback( [completionHandler, env](HRESULT result, wil::com_ptr compositionController) -> HRESULT { wil::com_ptr webViewController = compositionController.try_query(); - if (FAILED(result) || !webViewController) { + if (failedAndLog(result) || !webViewController) { completionHandler(nullptr, nullptr, nullptr); - debugLog(getErrorMessage(result)); return E_FAIL; } ICoreWebView2Controller3* webViewController3; - HRESULT hr = webViewController->QueryInterface(IID_PPV_ARGS(&webViewController3)); - if (SUCCEEDED(hr)) { + 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); } - else { - debugLog(getErrorMessage(hr)); - } completionHandler(std::move(env), std::move(webViewController), std::move(compositionController)); return S_OK; } - ).Get()); + ).Get())); } else { - env->CreateCoreWebView2Controller(parentWindow, Callback( + failedLog(env->CreateCoreWebView2Controller(parentWindow, Callback( [completionHandler, env](HRESULT result, wil::com_ptr controller) -> HRESULT { - if (FAILED(result) || !controller) { + if (failedAndLog(result) || !controller) { completionHandler(nullptr, nullptr, nullptr); - debugLog(getErrorMessage(result)); return E_FAIL; } completionHandler(std::move(env), std::move(controller), nullptr); return S_OK; - }).Get()); + }).Get())); } return S_OK; - }).Get()); + }).Get())); } void InAppWebView::initChannel(const std::optional> viewId, const std::optional channelName) @@ -139,16 +115,43 @@ namespace flutter_inappwebview_plugin std::make_unique(this, plugin->registrar->messenger()); } + void InAppWebView::prepare() + { + if (!webView) { + return; + } + + wil::com_ptr webView2Settings; + auto hrWebView2Settings = webView->get_Settings(&webView2Settings); + if (succeededOrLog(hrWebView2Settings)) { + webView2Settings->put_IsScriptEnabled(settings->javaScriptEnabled); + webView2Settings->put_IsZoomControlEnabled(settings->supportZoom); + + wil::com_ptr webView2Settings2; + if (succeededOrLog(webView2Settings->QueryInterface(IID_PPV_ARGS(&webView2Settings2)))) { + if (!settings->userAgent.empty()) { + webView2Settings2->put_UserAgent(ansi_to_wide(settings->userAgent).c_str()); + } + } + } + + userContentController->addPluginScript(std::move(createJavaScriptBridgePluginScript())); + + registerEventHandlers(); + } + void InAppWebView::registerEventHandlers() { if (!webView) { return; } - webView->add_NavigationStarting( + failedLog(webView->add_NavigationStarting( Callback( [this](ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args) { + isLoading_ = true; + if (!channelDelegate) { args->put_Cancel(false); return S_OK; @@ -183,12 +186,52 @@ namespace flutter_inappwebview_plugin std::optional method = requestMethod ? wide_to_utf8(requestMethod.get()) : std::optional{}; + BOOL isUserInitiated; + if (FAILED(args->get_IsUserInitiated(&isUserInitiated))) { + isUserInitiated = FALSE; + } + + BOOL isRedirect; + if (FAILED(args->get_IsRedirected(&isRedirect))) { + isRedirect = FALSE; + } + + std::optional navigationType = std::nullopt; + wil::com_ptr args3; + if (SUCCEEDED(args->QueryInterface(IID_PPV_ARGS(&args3)))) { + COREWEBVIEW2_NAVIGATION_KIND navigationKind; + if (SUCCEEDED(args3->get_NavigationKind(&navigationKind))) { + switch (navigationKind) { + case COREWEBVIEW2_NAVIGATION_KIND_RELOAD: + navigationType = NavigationActionType::reload; + break; + case COREWEBVIEW2_NAVIGATION_KIND_BACK_OR_FORWARD: + navigationType = NavigationActionType::backForward; + break; + case COREWEBVIEW2_NAVIGATION_KIND_NEW_DOCUMENT: + if (isUserInitiated && !isRedirect) { + navigationType = NavigationActionType::linkActivated; + } + else { + navigationType = NavigationActionType::other; + } + break; + default: + navigationType = NavigationActionType::other; + } + } + } + auto urlRequest = std::make_shared(url, method, headers, std::nullopt); auto navigationAction = std::make_shared( urlRequest, - true + true, + isRedirect, + navigationType ); + lastNavigationAction_ = navigationAction; + UINT64 navigationId; if (SUCCEEDED(args->get_NavigationId(&navigationId))) { navigationActions.insert({ navigationId, navigationAction }); @@ -202,7 +245,7 @@ namespace flutter_inappwebview_plugin callback->nonNullSuccess = [this, urlRequest](const NavigationActionPolicy actionPolicy) { callShouldOverrideUrlLoading_ = false; - if (actionPolicy == allow) { + if (actionPolicy == NavigationActionPolicy::allow) { loadUrl(*urlRequest); } return false; @@ -229,12 +272,14 @@ namespace flutter_inappwebview_plugin return S_OK; } - ).Get(), nullptr); + ).Get(), nullptr)); - webView->add_NavigationCompleted( + failedLog(webView->add_NavigationCompleted( Callback( [this](ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args) { + isLoading_ = false; + std::shared_ptr navigationAction; UINT64 navigationId; if (SUCCEEDED(args->get_NavigationId(&navigationId))) { @@ -251,8 +296,8 @@ namespace flutter_inappwebview_plugin args->get_IsSuccess(&isSuccess); if (channelDelegate) { - LPWSTR uri = nullptr; - std::optional url = SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(std::wstring(uri)) : std::optional{}; + wil::unique_cotaskmem_string uri; + std::optional url = SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(uri.get()) : std::optional{}; if (isSuccess) { channelDelegate->onLoadStop(url); } @@ -273,9 +318,9 @@ namespace flutter_inappwebview_plugin return S_OK; } - ).Get(), nullptr); + ).Get(), nullptr)); - webView->add_DocumentTitleChanged(Callback( + failedLog(webView->add_DocumentTitleChanged(Callback( [this](ICoreWebView2* sender, IUnknown* args) { if (channelDelegate) { @@ -285,7 +330,36 @@ namespace flutter_inappwebview_plugin } return S_OK; } - ).Get(), nullptr); + ).Get(), nullptr)); + + failedLog(webView->add_HistoryChanged(Callback( + [this](ICoreWebView2* sender, IUnknown* args) + { + if (channelDelegate) { + std::optional isReload = std::nullopt; + if (lastNavigationAction_ && lastNavigationAction_->navigationType.has_value()) { + isReload = lastNavigationAction_->navigationType.value() == NavigationActionType::reload; + } + channelDelegate->onUpdateVisitedHistory(getUrl(), isReload); + } + return S_OK; + } + ).Get(), nullptr)); + + failedLog(webView->add_WebMessageReceived(Callback( + [this](ICoreWebView2* sender, ICoreWebView2WebMessageReceivedEventArgs* args) + { + wil::unique_cotaskmem_string uri; + args->get_Source(&uri); + + wil::unique_cotaskmem_string json; + if (succeededOrLog(args->get_WebMessageAsJson(&json))) { + debugLog(json.get()); + } + + return S_OK; + } + ).Get(), nullptr)); } void InAppWebView::registerSurfaceEventHandlers() @@ -294,7 +368,7 @@ namespace flutter_inappwebview_plugin return; } - webViewCompositionController->add_CursorChanged( + failedLog(webViewCompositionController->add_CursorChanged( Callback( [this](ICoreWebView2CompositionController* sender, IUnknown* args) -> HRESULT @@ -306,19 +380,19 @@ namespace flutter_inappwebview_plugin } return S_OK; }) - .Get(), nullptr); + .Get(), nullptr)); } std::optional InAppWebView::getUrl() const { - LPWSTR uri; - return SUCCEEDED(webView->get_Source(&uri)) ? wide_to_utf8(uri) : std::optional{}; + wil::unique_cotaskmem_string uri; + return webView && succeededOrLog(webView->get_Source(&uri)) ? wide_to_utf8(uri.get()) : std::optional{}; } std::optional InAppWebView::getTitle() const { - LPWSTR title; - return SUCCEEDED(webView->get_DocumentTitle(&title)) ? wide_to_utf8(title) : std::optional{}; + wil::unique_cotaskmem_string title; + return webView && succeededOrLog(webView->get_DocumentTitle(&title)) ? wide_to_utf8(title.get()) : std::optional{}; } void InAppWebView::loadUrl(const URLRequest& urlRequest) const @@ -341,59 +415,147 @@ namespace flutter_inappwebview_plugin postDataStream = SHCreateMemStream( reinterpret_cast(postData.data()), static_cast(postData.length())); } - webViewEnv2->CreateWebResourceRequest( + if (succeededOrLog(webViewEnv2->CreateWebResourceRequest( url.c_str(), method.c_str(), postDataStream.get(), L"", &webResourceRequest - ); - wil::com_ptr requestHeaders; - if (SUCCEEDED(webResourceRequest->get_Headers(&requestHeaders))) { - if (method.compare(L"GET") != 0) { - requestHeaders->SetHeader(L"Flutter-InAppWebView-Request-Method", method.c_str()); - } - if (urlRequest.headers.has_value()) { - auto& headers = urlRequest.headers.value(); - for (auto const& [key, val] : headers) { - requestHeaders->SetHeader(ansi_to_wide(key).c_str(), ansi_to_wide(val).c_str()); + ))) { + wil::com_ptr requestHeaders; + if (SUCCEEDED(webResourceRequest->get_Headers(&requestHeaders))) { + if (method.compare(L"GET") != 0) { + requestHeaders->SetHeader(L"Flutter-InAppWebView-Request-Method", method.c_str()); + } + if (urlRequest.headers.has_value()) { + auto& headers = urlRequest.headers.value(); + for (auto const& [key, val] : headers) { + requestHeaders->SetHeader(ansi_to_wide(key).c_str(), ansi_to_wide(val).c_str()); + } } } + failedLog(webView2->NavigateWithWebResourceRequest(webResourceRequest.get())); } - webView2->NavigateWithWebResourceRequest(webResourceRequest.get()); - } - else { - webView->Navigate(url.c_str()); + return; } + failedLog(webView->Navigate(url.c_str())); } void InAppWebView::reload() const { - webView->Reload(); + if (!webView) { + return; + } + + failedLog(webView->Reload()); } void InAppWebView::goBack() { + if (!webView) { + return; + } + callShouldOverrideUrlLoading_ = false; - webView->GoBack(); + failedLog(webView->GoBack()); + } + + bool InAppWebView::canGoBack() const + { + BOOL canGoBack_; + return webView && succeededOrLog(webView->get_CanGoBack(&canGoBack_)) ? canGoBack_ : false; } void InAppWebView::goForward() { + if (!webView) { + return; + } + callShouldOverrideUrlLoading_ = false; - webView->GoForward(); + failedLog(webView->GoForward()); + } + + bool InAppWebView::canGoForward() const + { + BOOL canGoForward_; + return webView && succeededOrLog(webView->get_CanGoForward(&canGoForward_)) ? canGoForward_ : false; + } + + void InAppWebView::goBackOrForward(const int& steps) + { + getCopyBackForwardList( + [this, steps](std::unique_ptr webHistory) + { + if (webHistory && webHistory->currentIndex.has_value() && webHistory->list.has_value()) { + auto currentIndex = webHistory->currentIndex.value(); + auto items = &webHistory->list.value(); + auto nextIndex = currentIndex + steps; + int64_t size = items->size(); + if (nextIndex >= 0 && nextIndex < size) { + auto entryId = items->at(nextIndex)->entryId; + std::cout << "entryId: " + std::to_string(entryId.value()) << "\n"; + if (entryId.has_value()) { + auto oldCallShouldOverrideUrlLoading_ = callShouldOverrideUrlLoading_; + callShouldOverrideUrlLoading_ = false; + if (failedAndLog(webView->CallDevToolsProtocolMethod(L"Page.navigateToHistoryEntry", ansi_to_wide("{\"entryId\": " + std::to_string(entryId.value()) + "}").c_str(), Callback( + [this](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + failedLog(errorCode); + return S_OK; + } + ).Get()))) { + callShouldOverrideUrlLoading_ = oldCallShouldOverrideUrlLoading_; + } + } + } + } + } + ); + } + + void InAppWebView::canGoBackOrForward(const int& steps, std::function completionHandler) const + { + getCopyBackForwardList( + [steps, completionHandler](std::unique_ptr webHistory) + { + auto canGoBackOrForward_ = false; + if (webHistory && webHistory->currentIndex.has_value() && webHistory->list.has_value()) { + auto currentIndex = webHistory->currentIndex.value(); + auto items = &webHistory->list.value(); + auto nextIndex = currentIndex + steps; + int64_t size = items->size(); + canGoBackOrForward_ = nextIndex >= 0 && nextIndex < size; + } + completionHandler(canGoBackOrForward_); + } + ); + } + + void InAppWebView::stopLoading() const + { + if (!webView) { + return; + } + + failedLog(webView->Stop()); } void InAppWebView::getCopyBackForwardList(const std::function)> completionHandler) const { - webView->CallDevToolsProtocolMethod(L"Page.getNavigationHistory", L"{}", Callback( + if (!webView) { + completionHandler(std::make_unique(std::nullopt, std::nullopt)); + return; + } + + failedLog(webView->CallDevToolsProtocolMethod(L"Page.getNavigationHistory", L"{}", Callback( [completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) { if (errorCode == S_OK) { auto historyJson = nlohmann::json::parse(wide_to_ansi(returnObjectAsJson)); int64_t currentIndex = historyJson.at("currentIndex").is_number_unsigned() ? historyJson.at("currentIndex").get() : 0; - auto entries = historyJson.at("entries").is_array() ? historyJson.at("entries").get>() : std::vector{}; + std::vector entries = historyJson.at("entries").is_array() ? historyJson.at("entries").get>() : std::vector{}; std::vector> webHistoryItems; webHistoryItems.reserve(entries.size()); @@ -401,6 +563,7 @@ namespace flutter_inappwebview_plugin for (auto const& entry : entries) { int64_t offset = i - currentIndex; webHistoryItems.push_back(std::make_shared( + entry.at("id").is_number_integer() ? entry.at("id").get() : std::optional{}, i, offset, entry.at("userTypedURL").is_string() ? entry.at("userTypedURL").get() : std::optional{}, @@ -413,26 +576,27 @@ namespace flutter_inappwebview_plugin completionHandler(std::make_unique(currentIndex, webHistoryItems)); } else { - debugLog(getErrorMessage(errorCode)); + debugLog(errorCode); + completionHandler(std::make_unique(std::nullopt, std::nullopt)); } return S_OK; } - ).Get()); + ).Get())); } void InAppWebView::evaluateJavascript(const std::string& source, std::function completionHanlder) const { - webView->ExecuteScript(ansi_to_wide(source).c_str(), + failedLog(webView->ExecuteScript(ansi_to_wide(source).c_str(), Callback( [completionHanlder = std::move(completionHanlder)](HRESULT error, PCWSTR result) -> HRESULT { if (error != S_OK) { - debugLog(getErrorMessage(error)); + debugLog(error); } completionHanlder(wide_to_ansi(result)); return S_OK; - }).Get()); + }).Get())); } // flutter_view diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index 0012d1df..fb981452 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -13,6 +13,7 @@ #include "../types/url_request.h" #include "../types/web_history.h" #include "in_app_webview_settings.h" +#include "user_content_controller.h" #include "webview_channel_delegate.h" namespace flutter_inappwebview_plugin @@ -88,6 +89,7 @@ namespace flutter_inappwebview_plugin std::map> navigationActions = {}; const std::shared_ptr settings; InAppBrowser* inAppBrowser = nullptr; + std::unique_ptr userContentController; InAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, wil::com_ptr webViewEnv, @@ -127,12 +129,22 @@ namespace flutter_inappwebview_plugin winrt::com_ptr compositor); void initChannel(const std::optional> viewId, const std::optional channelName); + void prepare(); std::optional getUrl() const; std::optional getTitle() const; void loadUrl(const URLRequest& urlRequest) const; void reload() const; void goBack(); + bool canGoBack() const; void goForward(); + bool canGoForward() const; + void goBackOrForward(const int& steps); + void canGoBackOrForward(const int& steps, std::function completionHandler) const; + bool isLoading() const + { + return isLoading_; + } + void stopLoading() const; void evaluateJavascript(const std::string& source, std::function completionHanlder) const; void getCopyBackForwardList(const std::function)> completionHandler) const; @@ -147,6 +159,9 @@ namespace flutter_inappwebview_plugin VirtualKeyState virtualKeys_; bool callShouldOverrideUrlLoading_ = true; + std::shared_ptr lastNavigationAction_; + bool isLoading_ = false; + void InAppWebView::registerEventHandlers(); void InAppWebView::registerSurfaceEventHandlers(); }; diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp index 408724b3..10f2a0cd 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp @@ -7,6 +7,7 @@ #include "../in_app_webview/in_app_webview_settings.h" #include "../types/url_request.h" #include "../utils/flutter.h" +#include "../utils/log.h" #include "in_app_webview_manager.h" namespace flutter_inappwebview_plugin diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.cpp index c66561dc..df3d538e 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.cpp @@ -1,4 +1,5 @@ #include "../utils/flutter.h" +#include "../utils/log.h" #include "in_app_webview_settings.h" namespace flutter_inappwebview_plugin diff --git a/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.cpp b/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.cpp new file mode 100644 index 00000000..eed27040 --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.cpp @@ -0,0 +1,121 @@ +#include + +#include "../utils/log.h" +#include "../utils/strconv.h" +#include "../utils/vector.h" +#include "in_app_webview.h" +#include "user_content_controller.h" + +namespace flutter_inappwebview_plugin +{ + using namespace Microsoft::WRL; + + UserContentController::UserContentController(InAppWebView* webView) + : webView_(webView) + {} + + std::vector> UserContentController::getUserOnlyScriptsAt(const UserScriptInjectionTime& injectionTime) const + { + return userOnlyScripts_.at(injectionTime); + } + + void UserContentController::addUserOnlyScript(std::shared_ptr userScript) + { + if (!userScript) { + return; + } + + userOnlyScripts_.at(userScript->injectionTime).push_back(std::move(userScript)); + } + + void UserContentController::addUserOnlyScripts(std::vector> userScripts) + { + for (auto& userScript : userScripts) { + addUserOnlyScript(std::move(userScript)); + } + } + + void UserContentController::removeUserOnlyScript(std::shared_ptr userScript) + { + if (!userScript) { + return; + } + + vector_remove_erase_el(userOnlyScripts_.at(userScript->injectionTime), std::move(userScript)); + } + + void UserContentController::removeUserOnlyScriptAt(const int64_t& index, const UserScriptInjectionTime& injectionTime) + { + auto& vec = userOnlyScripts_.at(injectionTime); + int64_t size = vec.size(); + if (index >= size) { + return; + } + + vec.erase(vec.begin() + index); + } + + void UserContentController::removeAllUserOnlyScripts() + { + userOnlyScripts_.at(UserScriptInjectionTime::atDocumentStart).clear(); + userOnlyScripts_.at(UserScriptInjectionTime::atDocumentEnd).clear(); + } + + std::vector> UserContentController::getPluginScriptsAt(const UserScriptInjectionTime& injectionTime) const + { + return pluginScripts_.at(injectionTime); + } + + void UserContentController::addPluginScript(std::shared_ptr pluginScript) + { + if (!pluginScript || !webView_) { + return; + } + + if (pluginScript->injectionTime == UserScriptInjectionTime::atDocumentStart) { + failedLog(webView_->webView->AddScriptToExecuteOnDocumentCreated(ansi_to_wide(pluginScript->source).c_str(), + Callback( + [pluginScript](HRESULT error, PCWSTR id) -> HRESULT + { + if (succeededOrLog(error)) { + pluginScript->id = id; + } + return S_OK; + }).Get())); + } + + pluginScripts_.at(pluginScript->injectionTime).push_back(std::move(pluginScript)); + } + + void UserContentController::addPluginScripts(std::vector> pluginScripts) + { + for (auto& pluginScript : pluginScripts) { + addPluginScript(std::move(pluginScript)); + } + } + + void UserContentController::removePluginScript(std::shared_ptr pluginScript) + { + if (!pluginScript || !webView_) { + return; + } + + failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(pluginScript->id.c_str())); + + vector_remove_erase_el(pluginScripts_.at(pluginScript->injectionTime), std::move(pluginScript)); + } + + void UserContentController::removeAllPluginScripts() + { + pluginScripts_.at(UserScriptInjectionTime::atDocumentStart).clear(); + pluginScripts_.at(UserScriptInjectionTime::atDocumentEnd).clear(); + } + + UserContentController::~UserContentController() + { + debugLog("dealloc UserContentController"); + removeAllUserOnlyScripts(); + removeAllPluginScripts(); + webView_ = nullptr; + } +} diff --git a/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.h b/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.h new file mode 100644 index 00000000..1fee5cfa --- /dev/null +++ b/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.h @@ -0,0 +1,45 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_USER_CONTENT_CONTROLLER_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_USER_CONTENT_CONTROLLER_H_ + +#include +#include + +#include "../types/plugin_script.h" +#include "../types/user_script.h" + +namespace flutter_inappwebview_plugin +{ + class InAppWebView; + + class UserContentController + { + public: + UserContentController(InAppWebView* webView); + ~UserContentController(); + + std::vector> getUserOnlyScriptsAt(const UserScriptInjectionTime& injectionTime) const; + void addUserOnlyScript(std::shared_ptr userScript); + void addUserOnlyScripts(std::vector> userScripts); + void removeUserOnlyScript(std::shared_ptr userScript); + void removeUserOnlyScriptAt(const int64_t& index, const UserScriptInjectionTime& injectionTime); + void removeAllUserOnlyScripts(); + std::vector> getPluginScriptsAt(const UserScriptInjectionTime& injectionTime) const; + void addPluginScript(std::shared_ptr pluginScript); + void addPluginScripts(std::vector> pluginScripts); + void removePluginScript(std::shared_ptr pluginScript); + void removeAllPluginScripts(); + private: + InAppWebView* webView_; + + std::map>> pluginScripts_ = { + {UserScriptInjectionTime::atDocumentStart, {}}, + {UserScriptInjectionTime::atDocumentEnd, {}} + }; + + std::map>> userOnlyScripts_ = { + {UserScriptInjectionTime::atDocumentStart, {}}, + {UserScriptInjectionTime::atDocumentEnd, {}} + }; + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_USER_CONTENT_CONTROLLER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index 066a66aa..1b634773 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -1,6 +1,7 @@ #include "../in_app_browser/in_app_browser.h" #include "../types/base_callback_result.h" #include "../utils/flutter.h" +#include "../utils/log.h" #include "../utils/strconv.h" #include "in_app_webview.h" #include "webview_channel_delegate.h" @@ -20,7 +21,7 @@ namespace flutter_inappwebview_plugin decodeResult = [](const flutter::EncodableValue* value) { if (value->IsNull()) { - return cancel; + return NavigationActionPolicy::cancel; } auto navigationPolicy = std::get(*value); return static_cast(navigationPolicy); @@ -56,10 +57,37 @@ namespace flutter_inappwebview_plugin webView->goBack(); result->Success(true); } + else if (method_call.method_name().compare("canGoBack") == 0) { + result->Success(webView->canGoBack()); + } else if (method_call.method_name().compare("goForward") == 0) { webView->goForward(); result->Success(true); } + else if (method_call.method_name().compare("canGoForward") == 0) { + result->Success(webView->canGoForward()); + } + else if (method_call.method_name().compare("goBackOrForward") == 0) { + auto steps = get_fl_map_value(arguments, "steps"); + webView->goBackOrForward(steps); + result->Success(true); + } + else if (method_call.method_name().compare("canGoBackOrForward") == 0) { + auto result_ = std::shared_ptr>(std::move(result)); + + auto steps = get_fl_map_value(arguments, "steps"); + webView->canGoBackOrForward(steps, [result_ = std::move(result_)](const bool& value) + { + result_->Success(value); + }); + } + else if (method_call.method_name().compare("isLoading") == 0) { + result->Success(webView->isLoading()); + } + else if (method_call.method_name().compare("stopLoading") == 0) { + webView->stopLoading(); + result->Success(true); + } else if (method_call.method_name().compare("evaluateJavascript") == 0) { auto result_ = std::shared_ptr>(std::move(result)); @@ -170,6 +198,19 @@ namespace flutter_inappwebview_plugin } } + void WebViewChannelDelegate::onUpdateVisitedHistory(const std::optional& url, const std::optional& isReload) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap{ + {"url", make_fl_value(url)}, + {"isReload", make_fl_value(isReload)} + }); + channel->InvokeMethod("onUpdateVisitedHistory", std::move(arguments)); + } + WebViewChannelDelegate::~WebViewChannelDelegate() { debugLog("dealloc WebViewChannelDelegate"); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h index f511a3c6..a3d8905d 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h @@ -42,6 +42,7 @@ namespace flutter_inappwebview_plugin void onReceivedError(std::shared_ptr request, std::shared_ptr error) const; void onReceivedHttpError(std::shared_ptr request, std::shared_ptr error) const; void onTitleChanged(const std::optional& title) const; + void onUpdateVisitedHistory(const std::optional& url, const std::optional& isReload) const; }; } diff --git a/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.cpp b/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.cpp new file mode 100644 index 00000000..ba2c3066 --- /dev/null +++ b/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.cpp @@ -0,0 +1,26 @@ +#include + +#include "javascript_bridge_js.h" + +namespace flutter_inappwebview_plugin +{ + const std::string JAVASCRIPT_BRIDGE_JS_SOURCE = "window." + JAVASCRIPT_BRIDGE_NAME + " = {}; \ + window." + JAVASCRIPT_BRIDGE_NAME + ".callHandler = function() { \ + var _callHandlerID = setTimeout(function() {}); \ + window.chrome.webview.postMessage({ 'internalHandlerName': 'callHandler', 'handlerName': arguments[0], '_callHandlerID' : _callHandlerID, 'args' : JSON.stringify(Array.prototype.slice.call(arguments, 1)) }); \ + return new Promise(function(resolve, reject) { \ + window." + JAVASCRIPT_BRIDGE_NAME + "[_callHandlerID] = { resolve: resolve, reject : reject };\ + });\ + };"; + + std::unique_ptr createJavaScriptBridgePluginScript() + { + const std::set allowedOriginRules = { "*" }; + return std::make_unique( + JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT_GROUP_NAME, + JAVASCRIPT_BRIDGE_JS_SOURCE, + UserScriptInjectionTime::atDocumentStart, + allowedOriginRules + ); + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.h b/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.h new file mode 100644 index 00000000..5366bc6e --- /dev/null +++ b/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.h @@ -0,0 +1,17 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_JAVASCRIPT_BRIDGE_JS_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_JAVASCRIPT_BRIDGE_JS_H_ + +#include +#include + +#include "../types/plugin_script.h" + +namespace flutter_inappwebview_plugin +{ + const std::string JAVASCRIPT_BRIDGE_NAME = "flutter_inappwebview"; + const std::string JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT"; + + std::unique_ptr createJavaScriptBridgePluginScript(); +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_JAVASCRIPT_BRIDGE_JS_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/navigation_action.cpp b/flutter_inappwebview_windows/windows/types/navigation_action.cpp index 4e499490..543553e7 100644 --- a/flutter_inappwebview_windows/windows/types/navigation_action.cpp +++ b/flutter_inappwebview_windows/windows/types/navigation_action.cpp @@ -3,15 +3,19 @@ namespace flutter_inappwebview_plugin { - NavigationAction::NavigationAction(std::shared_ptr request, const bool& isForMainFrame) - : request(std::move(request)), isForMainFrame(isForMainFrame) + NavigationAction::NavigationAction(std::shared_ptr request, const bool& isForMainFrame, + const std::optional& isRedirect, const std::optional& navigationType) + : request(std::move(request)), isForMainFrame(isForMainFrame), + isRedirect(isRedirect), navigationType(navigationType) {} flutter::EncodableMap NavigationAction::toEncodableMap() const { return flutter::EncodableMap{ {"request", request->toEncodableMap()}, - {"isForMainFrame", make_fl_value(isForMainFrame)} + {"isForMainFrame", isForMainFrame}, + {"isRedirect", make_fl_value(isRedirect)}, + {"navigationType", make_fl_value(navigationType)} }; } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/navigation_action.h b/flutter_inappwebview_windows/windows/types/navigation_action.h index ae100e63..886640be 100644 --- a/flutter_inappwebview_windows/windows/types/navigation_action.h +++ b/flutter_inappwebview_windows/windows/types/navigation_action.h @@ -8,13 +8,22 @@ namespace flutter_inappwebview_plugin { + enum NavigationActionType { + linkActivated = 0, + backForward, + reload, + other + }; + class NavigationAction { public: const std::shared_ptr request; const bool isForMainFrame; + const std::optional isRedirect; + const std::optional navigationType; - NavigationAction(std::shared_ptr request, const bool& isForMainFrame); + NavigationAction(std::shared_ptr request, const bool& isForMainFrame, const std::optional& isRedirect, const std::optional& navigationType); ~NavigationAction() = default; flutter::EncodableMap toEncodableMap() const; diff --git a/flutter_inappwebview_windows/windows/types/plugin_script.cpp b/flutter_inappwebview_windows/windows/types/plugin_script.cpp new file mode 100644 index 00000000..adc7d3f8 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/plugin_script.cpp @@ -0,0 +1,14 @@ +#include "plugin_script.h" + +namespace flutter_inappwebview_plugin +{ + PluginScript::PluginScript( + const std::optional& groupName, + const std::string& source, + const UserScriptInjectionTime& injectionTime, + const std::set& allowedOriginRules + ) : UserScript(groupName, source, injectionTime, allowedOriginRules) + {} + + PluginScript::~PluginScript() {} +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/plugin_script.h b/flutter_inappwebview_windows/windows/types/plugin_script.h new file mode 100644 index 00000000..b2fde408 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/plugin_script.h @@ -0,0 +1,21 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_SCRIPT_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_SCRIPT_H_ + +#include "user_script.h" + +namespace flutter_inappwebview_plugin +{ + class PluginScript : public UserScript + { + public: + + PluginScript( + const std::optional& groupName, + const std::string& source, + const UserScriptInjectionTime& injectionTime, + const std::set& allowedOriginRules + ); + ~PluginScript(); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_SCRIPT_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/user_script.cpp b/flutter_inappwebview_windows/windows/types/user_script.cpp new file mode 100644 index 00000000..3e5f39db --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/user_script.cpp @@ -0,0 +1,15 @@ +#include "user_script.h" + +namespace flutter_inappwebview_plugin +{ + UserScript::UserScript( + const std::optional& groupName, + const std::string& source, + const UserScriptInjectionTime& injectionTime, + const std::set& allowedOriginRules + ) : groupName(groupName), source(source), injectionTime(injectionTime), allowedOriginRules(allowedOriginRules) + {} + + UserScript::~UserScript() + {} +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/user_script.h b/flutter_inappwebview_windows/windows/types/user_script.h new file mode 100644 index 00000000..e6c79166 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/user_script.h @@ -0,0 +1,33 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_USER_SCRIPT_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_USER_SCRIPT_H_ + +#include +#include +#include + +namespace flutter_inappwebview_plugin +{ + enum UserScriptInjectionTime { + atDocumentStart, + atDocumentEnd + }; + + class UserScript + { + public: + std::wstring id; + const std::optional groupName; + const std::string source; + const UserScriptInjectionTime injectionTime; + const std::set allowedOriginRules; + + UserScript( + const std::optional& groupName, + const std::string& source, + const UserScriptInjectionTime& injectionTime, + const std::set& allowedOriginRules + ); + ~UserScript(); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_USER_SCRIPT_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_history_item.cpp b/flutter_inappwebview_windows/windows/types/web_history_item.cpp index bc222022..11350eeb 100644 --- a/flutter_inappwebview_windows/windows/types/web_history_item.cpp +++ b/flutter_inappwebview_windows/windows/types/web_history_item.cpp @@ -2,14 +2,15 @@ namespace flutter_inappwebview_plugin { - WebHistoryItem::WebHistoryItem(const std::optional& index, const std::optional& offset, + WebHistoryItem::WebHistoryItem(const std::optional& entryId, const std::optional& index, const std::optional& offset, const std::optional& originalUrl, const std::optional& title, const std::optional& url) - : index(index), offset(offset), originalUrl(originalUrl), title(title), url(url) + : entryId(entryId), index(index), offset(offset), originalUrl(originalUrl), title(title), url(url) {} WebHistoryItem::WebHistoryItem(const flutter::EncodableMap& map) - : index(get_optional_fl_map_value(map, "index")), + : entryId(get_optional_fl_map_value(map, "entryId")), + index(get_optional_fl_map_value(map, "index")), offset(get_optional_fl_map_value(map, "offset")), originalUrl(get_optional_fl_map_value(map, "originalUrl")), title(get_optional_fl_map_value(map, "title")), @@ -19,6 +20,7 @@ namespace flutter_inappwebview_plugin flutter::EncodableMap WebHistoryItem::toEncodableMap() const { return flutter::EncodableMap{ + {"entryId", make_fl_value(entryId)}, {"index", make_fl_value(index)}, {"offset", make_fl_value(offset)}, {"originalUrl", make_fl_value(originalUrl)}, diff --git a/flutter_inappwebview_windows/windows/types/web_history_item.h b/flutter_inappwebview_windows/windows/types/web_history_item.h index 8e63c1b7..e58181e3 100644 --- a/flutter_inappwebview_windows/windows/types/web_history_item.h +++ b/flutter_inappwebview_windows/windows/types/web_history_item.h @@ -11,13 +11,14 @@ namespace flutter_inappwebview_plugin class WebHistoryItem { public: + const std::optional entryId; const std::optional index; const std::optional offset; const std::optional originalUrl; const std::optional title; const std::optional url; - WebHistoryItem(const std::optional& index, const std::optional& offset, + WebHistoryItem(const std::optional& entryId, const std::optional& index, const std::optional& offset, const std::optional& originalUrl, const std::optional& title, const std::optional& url); WebHistoryItem(const flutter::EncodableMap& map); diff --git a/flutter_inappwebview_windows/windows/utils/flutter.h b/flutter_inappwebview_windows/windows/utils/flutter.h index 517a0075..6447b866 100644 --- a/flutter_inappwebview_windows/windows/utils/flutter.h +++ b/flutter_inappwebview_windows/windows/utils/flutter.h @@ -3,7 +3,9 @@ #include +#include "map.h" #include "util.h" +#include "vector.h" namespace flutter_inappwebview_plugin { diff --git a/flutter_inappwebview_windows/windows/utils/log.h b/flutter_inappwebview_windows/windows/utils/log.h new file mode 100644 index 00000000..c410164a --- /dev/null +++ b/flutter_inappwebview_windows/windows/utils/log.h @@ -0,0 +1,67 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_UTIL_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_UTIL_H_ + +#include +#include +#include + +#include "strconv.h" + +namespace flutter_inappwebview_plugin +{ + static inline void debugLog(const std::string& msg, const bool& isError = false) + { +#ifndef NDEBUG + if (isError) { + std::cerr << msg << std::endl; + } + else { + std::cout << msg << std::endl; + } + OutputDebugString(ansi_to_wide(msg + "\n").c_str()); +#endif + } + + static inline void debugLog(const std::wstring& msg, const bool& isError = false) + { + debugLog(wide_to_ansi(msg), isError); + } + + static inline std::string getHRMessage(const HRESULT& error) + { + return wide_to_ansi(_com_error(error).ErrorMessage()); + } + + static inline void debugLog(const HRESULT& hr) + { + auto isError = hr != S_OK; + debugLog((isError ? "Error: " : "Message: ") + getHRMessage(hr), isError); + } + + static inline bool succeededOrLog(const HRESULT& hr) + { + if (SUCCEEDED(hr)) { + return true; + } + debugLog(hr); + return false; + } + + static inline bool failedAndLog(const HRESULT& hr) + { + if (FAILED(hr)) { + debugLog(hr); + return true; + } + return false; + } + + static inline void failedLog(const HRESULT& hr) + { + if (FAILED(hr)) { + debugLog(hr); + } + } +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_UTIL_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/map.h b/flutter_inappwebview_windows/windows/utils/map.h new file mode 100644 index 00000000..3a432d9f --- /dev/null +++ b/flutter_inappwebview_windows/windows/utils/map.h @@ -0,0 +1,71 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_MAP_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_MAP_H_ + +#include +#include +#include +#include +#include + +namespace flutter_inappwebview_plugin +{ + template + struct is_mappish_impl : std::false_type { }; + + template + struct is_mappish_impl()[std::declval()])>> + : std::true_type { }; + + template + struct is_mappish : is_mappish_impl::type { }; + + template + static inline bool map_contains(const std::map& map, const K& key) + { + return map.find(key) != map.end(); + } + + template + static inline T map_at_or_null(const std::map& map, const K& key) + { + auto itr = map.find(key); + return itr != map.end() ? itr->second : nullptr; + } + + template + static inline auto functional_map(Iterator begin, Iterator end, Func&& func) -> + std::vector()))> + { + using value_type = decltype(func(std::declval())); + + std::vector out_vector; + out_vector.reserve(std::distance(begin, end)); + + std::transform(begin, end, std::back_inserter(out_vector), + std::forward(func)); + + return out_vector; + } + + template + static inline auto functional_map(const T& iterable, Func&& func) -> + std::vector()))> + { + return functional_map(std::begin(iterable), std::end(iterable), + std::forward(func)); + } + + template + static inline auto functional_map(const std::optional& iterable, Func&& func) -> + std::vector()))> + { + if (!iterable.has_value()) { + return {}; + } + return functional_map(iterable.value(), std::forward(func)); + } +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_MAP_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/util.h b/flutter_inappwebview_windows/windows/utils/util.h index b4820d5c..982e404a 100644 --- a/flutter_inappwebview_windows/windows/utils/util.h +++ b/flutter_inappwebview_windows/windows/utils/util.h @@ -1,60 +1,12 @@ #ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ #define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ -#include -#include -#include -#include #include #include #include -#include "strconv.h" - namespace flutter_inappwebview_plugin { - template - struct is_vector_impl : std::false_type { }; - - template - struct is_mappish_impl : std::false_type { }; - - template - struct is_vector_impl>::value> - > : std::true_type { }; - - template - struct is_vector_impl::value_type>::iterator>::value> - > : std::true_type { }; - - template - struct is_mappish_impl()[std::declval()])>> - : std::true_type { }; - - template - struct is_mappish : is_mappish_impl::type { }; - - template - struct is_vector : is_vector_impl::type { }; - - static inline void debugLog(const std::string& msg) - { -#ifndef NDEBUG - std::cout << msg << std::endl; - OutputDebugString(ansi_to_wide(msg + "\n").c_str()); -#endif - } - - static inline std::string getErrorMessage(const HRESULT& error) - { - _com_error err(error); - return wide_to_ansi(err.ErrorMessage()); - } - template static inline std::optional make_pointer_optional(const T* value) { @@ -74,52 +26,6 @@ namespace flutter_inappwebview_plugin static_assert(always_false_v, "non-exhaustive visitor!"); }, var); } - - template - static inline bool map_contains(const std::map& map, const K& key) - { - return map.find(key) != map.end(); - } - - template - static inline T map_at_or_null(const std::map& map, const K& key) - { - auto itr = map.find(key); - return itr != map.end() ? itr->second : nullptr; - } - - template - static inline auto functional_map(Iterator begin, Iterator end, Func&& func) -> - std::vector()))> - { - using value_type = decltype(func(std::declval())); - - std::vector out_vector; - out_vector.reserve(std::distance(begin, end)); - - std::transform(begin, end, std::back_inserter(out_vector), - std::forward(func)); - - return out_vector; - } - - template - static inline auto functional_map(const T& iterable, Func&& func) -> - std::vector()))> - { - return functional_map(std::begin(iterable), std::end(iterable), - std::forward(func)); - } - - template - static inline auto functional_map(const std::optional& iterable, Func&& func) -> - std::vector()))> - { - if (!iterable.has_value()) { - return {}; - } - return functional_map(iterable.value(), std::forward(func)); - } } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/vector.h b/flutter_inappwebview_windows/windows/utils/vector.h new file mode 100644 index 00000000..5ba8558b --- /dev/null +++ b/flutter_inappwebview_windows/windows/utils/vector.h @@ -0,0 +1,41 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_VECTOR_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_VECTOR_H_ + +#include +#include +#include +#include +#include + +namespace flutter_inappwebview_plugin +{ + template + struct is_vector_impl : std::false_type { }; + + template + struct is_vector_impl>::value> + > : std::true_type { }; + + template + struct is_vector_impl::value_type>::iterator>::value> + > : std::true_type { }; + + template + struct is_vector : is_vector_impl::type { }; + + template + static inline void vector_remove_el(std::vector& vec, const T& el) + { + std::remove(vec.begin(), vec.end(), el); + } + + template + static inline void vector_remove_erase_el(std::vector& vec, const T& el) + { + vec.erase(std::remove(vec.begin(), vec.end(), el), vec.end()); + } +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_VECTOR_H_ \ No newline at end of file From a4c044a0f174f82c15843b29476cbfb06cb51f67 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 Jan 2024 18:52:00 +0100 Subject: [PATCH 09/21] windows: completed initial javascript handler communication and user scripts support, added support for loadFile and loadData --- .../platform_in_app_browser.dart | 12 +- .../platform_inappwebview_controller.dart | 29 ++- .../src/in_app_webview/platform_webview.dart | 15 +- .../windows/CMakeLists.txt | 2 + .../windows/in_app_browser/in_app_browser.h | 4 +- .../in_app_browser/in_app_browser_manager.cpp | 2 +- .../windows/in_app_webview/in_app_webview.cpp | 173 ++++++++++++++++-- .../windows/in_app_webview/in_app_webview.h | 10 +- .../in_app_webview/in_app_webview_manager.cpp | 10 +- .../user_content_controller.cpp | 165 +++++++++++++++-- .../in_app_webview/user_content_controller.h | 17 ++ .../webview_channel_delegate.cpp | 89 +++++++-- .../in_app_webview/webview_channel_delegate.h | 9 +- .../javascript_bridge_js.cpp | 13 +- .../plugin_scripts_js/javascript_bridge_js.h | 14 ++ .../plugin_scripts_js/plugin_scripts_util.h | 11 ++ .../windows/types/plugin_script.cpp | 2 +- .../windows/types/plugin_script.h | 2 +- .../windows/types/user_script.cpp | 10 +- .../windows/types/user_script.h | 12 +- .../windows/types/web_history.cpp | 2 +- .../windows/utils/flutter.h | 6 +- .../windows/utils/log.h | 23 ++- .../windows/utils/map.h | 40 +--- .../windows/utils/string.h | 112 ++++++++++++ .../windows/utils/vector.h | 68 ++++++- 26 files changed, 711 insertions(+), 141 deletions(-) create mode 100644 flutter_inappwebview_windows/windows/plugin_scripts_js/plugin_scripts_util.h create mode 100644 flutter_inappwebview_windows/windows/utils/string.h diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart index 6574d899..1af48a02 100755 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart @@ -518,7 +518,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ([Official API - WebViewClient.onPageStarted](https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview)) - ///- Windows ([Official API - IWebView2WebView.add_NavigationStarting](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationstarting)) + ///- Windows ([Official API - ICoreWebView2.add_NavigationStarting](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationstarting)) void onLoadStart(WebUri? url) {} ///Event fired when the [PlatformInAppBrowser] finishes loading an [url]. @@ -527,7 +527,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ([Official API - WebViewClient.onPageFinished](https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) - ///- Windows ([Official API - IWebView2WebView.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) + ///- Windows ([Official API - ICoreWebView2.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) void onLoadStop(WebUri? url) {} ///Use [onReceivedError] instead. @@ -540,7 +540,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ([Official API - WebViewClient.onReceivedError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceError))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) - ///- Windows ([Official API - IWebView2WebView.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) + ///- Windows ([Official API - ICoreWebView2.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) void onReceivedError(WebResourceRequest request, WebResourceError error) {} ///Use [onReceivedHttpError] instead. @@ -559,7 +559,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ([Official API - WebViewClient.onReceivedHttpError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) - ///- Windows ([Official API - IWebView2WebView.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) + ///- Windows ([Official API - ICoreWebView2.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) void onReceivedHttpError( WebResourceRequest request, WebResourceResponse errorResponse) {} @@ -899,7 +899,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ([Official API - WebViewClient.doUpdateVisitedHistory](https://developer.android.com/reference/android/webkit/WebViewClient#doUpdateVisitedHistory(android.webkit.WebView,%20java.lang.String,%20boolean))) ///- iOS ///- MacOS - ///- Windows ([Official API - IWebView2WebView.add_HistoryChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_historychanged)) + ///- Windows ([Official API - ICoreWebView2.add_HistoryChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_historychanged)) void onUpdateVisitedHistory(WebUri? url, bool? isReload) {} ///Use [onPrintRequest] instead @@ -969,7 +969,7 @@ abstract class PlatformInAppBrowserEvents { ///- Android native WebView ([Official API - WebChromeClient.onReceivedTitle](https://developer.android.com/reference/android/webkit/WebChromeClient#onReceivedTitle(android.webkit.WebView,%20java.lang.String))) ///- iOS ///- MacOS - ///- Windows ([Official API - IWebView2WebView.add_DocumentTitleChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_documenttitlechanged)) + ///- Windows ([Official API - ICoreWebView2.add_DocumentTitleChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_documenttitlechanged)) void onTitleChanged(String? title) {} ///Event fired to respond to the results of an over-scroll operation. diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart index 1276f2b6..c38542ec 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart @@ -134,7 +134,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.url](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url)) ///- MacOS ([Official API - WKWebView.url](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url)) ///- Web - ///- Windows ([Official API - IWebView2WebView.get_Source](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_source)) + ///- Windows ([Official API - ICoreWebView2.get_Source](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_source)) ///{@endtemplate} Future getUrl() { throw UnimplementedError( @@ -151,7 +151,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.title](https://developer.apple.com/documentation/webkit/wkwebview/1415015-title)) ///- MacOS ([Official API - WKWebView.title](https://developer.apple.com/documentation/webkit/wkwebview/1415015-title)) ///- Web - ///- Windows ([Official API - IWebView2WebView.get_DocumentTitle](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_documenttitle)) + ///- Windows ([Official API - ICoreWebView2.get_DocumentTitle](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_documenttitle)) ///{@endtemplate} Future getTitle() { throw UnimplementedError( @@ -274,11 +274,14 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///Specify a directory to give WebView permission to read additional files in the specified directory. ///**NOTE**: available only on iOS and MacOS. /// + ///**NOTE for Windows**: only the [data] parameter is used. + /// ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebView.loadDataWithBaseURL](https://developer.android.com/reference/android/webkit/WebView#loadDataWithBaseURL(java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String))) ///- iOS ([Official API - WKWebView.loadHTMLString](https://developer.apple.com/documentation/webkit/wkwebview/1415004-loadhtmlstring) or [Official API - WKWebView.load](https://developer.apple.com/documentation/webkit/wkwebview/1415011-load)) ///- MacOS ([Official API - WKWebView.loadHTMLString](https://developer.apple.com/documentation/webkit/wkwebview/1415004-loadhtmlstring) or [Official API - WKWebView.load](https://developer.apple.com/documentation/webkit/wkwebview/1415011-load)) ///- Web + ///- Windows ([Official API - ICoreWebView2.NavigateToString](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#navigatetostring)) ///{@endtemplate} Future loadData( {required String data, @@ -330,6 +333,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.load](https://developer.apple.com/documentation/webkit/wkwebview/1414954-load)) ///- MacOS ([Official API - WKWebView.load](https://developer.apple.com/documentation/webkit/wkwebview/1414954-load)) ///- Web + ///- Windows ([Official API - ICoreWebView2.Navigate](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#navigate)) ///{@endtemplate} Future loadFile({required String assetFilePath}) { throw UnimplementedError( @@ -346,7 +350,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.reload](https://developer.apple.com/documentation/webkit/wkwebview/1414969-reload)) ///- MacOS ([Official API - WKWebView.reload](https://developer.apple.com/documentation/webkit/wkwebview/1414969-reload)) ///- Web ([Official API - Location.reload](https://developer.mozilla.org/en-US/docs/Web/API/Location/reload)) - ///- Windows ([Official API - IWebView2WebView.Reload](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#reload)) + ///- Windows ([Official API - ICoreWebView2.Reload](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#reload)) ///{@endtemplate} Future reload() { throw UnimplementedError( @@ -363,7 +367,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.goBack](https://developer.apple.com/documentation/webkit/wkwebview/1414952-goback)) ///- MacOS ([Official API - WKWebView.goBack](https://developer.apple.com/documentation/webkit/wkwebview/1414952-goback)) ///- Web ([Official API - History.back](https://developer.mozilla.org/en-US/docs/Web/API/History/back)) - ///- Windows ([Official API - IWebView2WebView.GoBack](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#goback)) + ///- Windows ([Official API - ICoreWebView2.GoBack](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#goback)) ///{@endtemplate} Future goBack() { throw UnimplementedError( @@ -377,7 +381,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ([Official API - WebView.canGoBack](https://developer.android.com/reference/android/webkit/WebView#canGoBack())) ///- iOS ([Official API - WKWebView.canGoBack](https://developer.apple.com/documentation/webkit/wkwebview/1414966-cangoback)) ///- MacOS ([Official API - WKWebView.canGoBack](https://developer.apple.com/documentation/webkit/wkwebview/1414966-cangoback)) - ///- Windows ([Official API - IWebView2WebView.get_CanGoBack](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_cangoback)) + ///- Windows ([Official API - ICoreWebView2.get_CanGoBack](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_cangoback)) ///{@endtemplate} Future canGoBack() { throw UnimplementedError( @@ -394,7 +398,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.goForward](https://developer.apple.com/documentation/webkit/wkwebview/1414993-goforward)) ///- MacOS ([Official API - WKWebView.goForward](https://developer.apple.com/documentation/webkit/wkwebview/1414993-goforward)) ///- Web ([Official API - History.forward](https://developer.mozilla.org/en-US/docs/Web/API/History/forward)) - ///- Windows ([Official API - IWebView2WebView.GoForward](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#goforward)) + ///- Windows ([Official API - ICoreWebView2.GoForward](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#goforward)) ///{@endtemplate} Future goForward() { throw UnimplementedError( @@ -408,7 +412,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ([Official API - WebView.canGoForward](https://developer.android.com/reference/android/webkit/WebView#canGoForward())) ///- iOS ([Official API - WKWebView.canGoForward](https://developer.apple.com/documentation/webkit/wkwebview/1414962-cangoforward)) ///- MacOS ([Official API - WKWebView.canGoForward](https://developer.apple.com/documentation/webkit/wkwebview/1414962-cangoforward)) - ///- Windows ([Official API - IWebView2WebView.get_CanGoForward](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_cangoforward)) + ///- Windows ([Official API - ICoreWebView2.get_CanGoForward](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#get_cangoforward)) ///{@endtemplate} Future canGoForward() { throw UnimplementedError( @@ -487,7 +491,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.stopLoading](https://developer.apple.com/documentation/webkit/wkwebview/1414981-stoploading)) ///- MacOS ([Official API - WKWebView.stopLoading](https://developer.apple.com/documentation/webkit/wkwebview/1414981-stoploading)) ///- Web ([Official API - Window.stop](https://developer.mozilla.org/en-US/docs/Web/API/Window/stop)) - ///- Windows ([Official API - IWebView2WebView.Stop](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#stop)) + ///- Windows ([Official API - ICoreWebView2.Stop](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#stop)) ///{@endtemplate} Future stopLoading() { throw UnimplementedError( @@ -517,7 +521,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.evaluateJavascript](https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript)) ///- MacOS ([Official API - WKWebView.evaluateJavascript](https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript)) ///- Web ([Official API - Window.eval](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval?retiredLocale=it)) - ///- Windows ([Official API - IWebView2WebView.ExecuteScript](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#executescript)) + ///- Windows ([Official API - ICoreWebView2.ExecuteScript](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#executescript)) ///{@endtemplate} Future evaluateJavascript( {required String source, ContentWorld? contentWorld}) { @@ -1189,6 +1193,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ///- iOS ([Official API - WKUserContentController.addUserScript](https://developer.apple.com/documentation/webkit/wkusercontentcontroller/1537448-adduserscript)) ///- MacOS ([Official API - WKUserContentController.addUserScript](https://developer.apple.com/documentation/webkit/wkusercontentcontroller/1537448-adduserscript)) + ///- Windows ///{@endtemplate} Future addUserScript({required UserScript userScript}) { throw UnimplementedError( @@ -1206,6 +1211,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future addUserScripts({required List userScripts}) { throw UnimplementedError( @@ -1225,6 +1231,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future removeUserScript({required UserScript userScript}) { throw UnimplementedError( @@ -1243,6 +1250,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future removeUserScriptsByGroupName({required String groupName}) { throw UnimplementedError( @@ -1261,6 +1269,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future removeUserScripts({required List userScripts}) { throw UnimplementedError( @@ -1278,6 +1287,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ///- iOS ([Official API - WKUserContentController.removeAllUserScripts](https://developer.apple.com/documentation/webkit/wkusercontentcontroller/1536540-removealluserscripts)) ///- MacOS ([Official API - WKUserContentController.removeAllUserScripts](https://developer.apple.com/documentation/webkit/wkusercontentcontroller/1536540-removealluserscripts)) + ///- Windows ///{@endtemplate} Future removeAllUserScripts() { throw UnimplementedError( @@ -1291,6 +1301,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} bool hasUserScript({required UserScript userScript}) { throw UnimplementedError( diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_webview.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_webview.dart index 4949d48b..ff250815 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_webview.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_webview.dart @@ -55,7 +55,7 @@ class PlatformWebViewCreationParams { ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview)) ///- Web - ///- Windows ([Official API - IWebView2WebView.add_NavigationStarting](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationstarting)) + ///- Windows ([Official API - ICoreWebView2.add_NavigationStarting](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationstarting)) ///{@endtemplate} final void Function(T controller, WebUri? url)? onLoadStart; @@ -70,7 +70,7 @@ class PlatformWebViewCreationParams { ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) ///- Web ([Official API - Window.onload](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event)) - ///- Windows ([Official API - IWebView2WebView.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) + ///- Windows ([Official API - ICoreWebView2.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) ///{@endtemplate} final void Function(T controller, WebUri? url)? onLoadStop; @@ -86,7 +86,7 @@ class PlatformWebViewCreationParams { ///- Android native WebView ([Official API - WebViewClient.onReceivedError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceError))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) - ///- Windows ([Official API - IWebView2WebView.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) + ///- Windows ([Official API - ICoreWebView2.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) ///{@endtemplate} final void Function( T controller, WebResourceRequest request, WebResourceError error)? @@ -111,7 +111,7 @@ class PlatformWebViewCreationParams { ///- Android native WebView ([Official API - WebViewClient.onReceivedHttpError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) ///- MacOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) - ///- Windows ([Official API - IWebView2WebView.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) + ///- Windows ([Official API - ICoreWebView2.add_NavigationCompleted](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/iwebview2webview?view=webview2-0.8.355#add_navigationcompleted)) ///{@endtemplate} final void Function(T controller, WebResourceRequest request, WebResourceResponse errorResponse)? onReceivedHttpError; @@ -504,7 +504,7 @@ class PlatformWebViewCreationParams { ///- iOS ///- MacOS ///- Web - ///- Windows ([Official API - IWebView2WebView.add_HistoryChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_historychanged)) + ///- Windows ([Official API - ICoreWebView2.add_HistoryChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_historychanged)) ///{@endtemplate} final void Function(T controller, WebUri? url, bool? isReload)? onUpdateVisitedHistory; @@ -599,7 +599,7 @@ class PlatformWebViewCreationParams { ///- iOS ///- MacOS ///- Web - ///- Windows ([Official API - IWebView2WebView.add_DocumentTitleChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_documenttitlechanged)) + ///- Windows ([Official API - ICoreWebView2.add_DocumentTitleChanged](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#add_documenttitlechanged)) ///{@endtemplate} final void Function(T controller, String? title)? onTitleChanged; @@ -1059,6 +1059,7 @@ class PlatformWebViewCreationParams { ///- iOS ///- MacOS ///- Web + ///- Windows ///{@endtemplate} final URLRequest? initialUrlRequest; @@ -1070,6 +1071,7 @@ class PlatformWebViewCreationParams { ///- iOS ///- MacOS ///- Web + ///- Windows ///{@endtemplate} final String? initialFile; @@ -1081,6 +1083,7 @@ class PlatformWebViewCreationParams { ///- iOS ///- MacOS ///- Web + ///- Windows ///{@endtemplate} final InAppWebViewInitialData? initialData; diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt index 998a399b..7df66194 100644 --- a/flutter_inappwebview_windows/windows/CMakeLists.txt +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -44,6 +44,7 @@ list(APPEND PLUGIN_SOURCES "utils/strconv.h" "utils/map.h" "utils/vector.h" + "utils/string.h" "utils/util.h" "utils/flutter.h" "types/channel_delegate.cpp" @@ -80,6 +81,7 @@ list(APPEND PLUGIN_SOURCES "custom_platform_view/util/string_converter.cc" "custom_platform_view/util/string_converter.h" "custom_platform_view/util/swizzle.h" + "plugin_scripts_js/plugin_scripts_util.h" "plugin_scripts_js/javascript_bridge_js.cpp" "plugin_scripts_js/javascript_bridge_js.h" "in_app_webview/user_content_controller.cpp" diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h index 7c6899a3..fe2e6988 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h @@ -18,7 +18,7 @@ namespace flutter_inappwebview_plugin struct InAppBrowserCreationParams { const std::string id; - const std::optional urlRequest; + const std::optional> urlRequest; const std::shared_ptr initialSettings; const std::shared_ptr initialWebViewSettings; }; @@ -35,7 +35,7 @@ namespace flutter_inappwebview_plugin const FlutterInappwebviewWindowsPlugin* plugin; const std::string id; - const std::optional initialUrlRequest; + const std::optional> initialUrlRequest; std::unique_ptr webView; std::unique_ptr channelDelegate; const std::shared_ptr settings; diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp index efdd422a..0459326d 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -31,7 +31,7 @@ namespace flutter_inappwebview_plugin { auto id = get_fl_map_value(*arguments, "id"); auto urlRequestMap = get_optional_fl_map_value(*arguments, "urlRequest"); - std::optional urlRequest = urlRequestMap.has_value() ? std::make_optional(urlRequestMap.value()) : std::optional{}; + std::optional> urlRequest = urlRequestMap.has_value() ? std::make_shared(urlRequestMap.value()) : std::optional>{}; auto settingsMap = get_fl_map_value(*arguments, "settings"); auto initialSettings = std::make_unique(settingsMap); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index 2d601f4d..118eadd2 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -9,7 +10,9 @@ #include "../types/web_resource_error.h" #include "../types/web_resource_request.h" #include "../utils/log.h" +#include "../utils/map.h" #include "../utils/strconv.h" +#include "../utils/string.h" #include "in_app_webview.h" #include "in_app_webview_manager.h" @@ -246,14 +249,14 @@ namespace flutter_inappwebview_plugin { callShouldOverrideUrlLoading_ = false; if (actionPolicy == NavigationActionPolicy::allow) { - loadUrl(*urlRequest); + loadUrl(urlRequest); } return false; }; - auto defaultBehaviour = [this, urlRequest](const std::optional actionPolicy) + auto defaultBehaviour = [this, urlRequest](const std::optional actionPolicy) { callShouldOverrideUrlLoading_ = false; - loadUrl(*urlRequest); + loadUrl(urlRequest); }; callback->defaultBehaviour = defaultBehaviour; callback->error = [defaultBehaviour](const std::string& error_code, const std::string& error_message, const flutter::EncodableValue* error_details) @@ -280,6 +283,11 @@ namespace flutter_inappwebview_plugin { isLoading_ = false; + if (userContentController) { + evaluateJavascript(userContentController->generateWrappedCodeForDocumentEnd(), nullptr); + } + evaluateJavascript(PLATFORM_READY_JS_SOURCE, nullptr); + std::shared_ptr navigationAction; UINT64 navigationId; if (SUCCEEDED(args->get_NavigationId(&navigationId))) { @@ -349,12 +357,61 @@ namespace flutter_inappwebview_plugin failedLog(webView->add_WebMessageReceived(Callback( [this](ICoreWebView2* sender, ICoreWebView2WebMessageReceivedEventArgs* args) { - wil::unique_cotaskmem_string uri; - args->get_Source(&uri); + if (!channelDelegate) { + return S_OK; + } + + /* + wil::unique_cotaskmem_string url; + args->get_Source(&url); + + wil::com_ptr uri; + failedLog(CreateUri( + url.get(), // NULL terminated URI + Uri_CREATE_ALLOW_RELATIVE, // Flags to control behavior + 0, // Reserved must be 0 + &uri)); + */ wil::unique_cotaskmem_string json; if (succeededOrLog(args->get_WebMessageAsJson(&json))) { - debugLog(json.get()); + auto message = nlohmann::json::parse(wide_to_ansi(json.get())); + + if (message.is_object() && message.contains("name") && message.at("name").is_string() && message.contains("body") && message.at("body").is_object()) { + auto name = message.at("name").get(); + auto body = message.at("body").get(); + + if (name.compare("callHandler") == 0 && body.contains("handlerName") && body.at("handlerName").is_string()) { + auto handlerName = body.at("handlerName").get(); + auto callHandlerID = body.at("_callHandlerID").is_number_integer() ? body.at("_callHandlerID").get() : 0; + std::string handlerArgs = body.at("args").is_string() ? body.at("args").get() : ""; + + auto callback = std::make_unique(); + callback->defaultBehaviour = [this, callHandlerID](const std::optional response) + { + std::string json = "null"; + if (response.has_value() && !response.value()->IsNull()) { + json = std::get(*(response.value())); + } + + evaluateJavascript("if (window." + JAVASCRIPT_BRIDGE_NAME + "[" + std::to_string(callHandlerID) + "] != null) { \ + window." + JAVASCRIPT_BRIDGE_NAME + "[" + std::to_string(callHandlerID) + "].resolve(" + json + "); \ + delete window." + JAVASCRIPT_BRIDGE_NAME + "[" + std::to_string(callHandlerID) + "]; \ + }", nullptr); + }; + callback->error = [this, callHandlerID](const std::string& error_code, const std::string& error_message, const flutter::EncodableValue* error_details) + { + auto errorMessage = error_code + ", " + error_message; + debugLog(errorMessage); + + evaluateJavascript("if (window." + JAVASCRIPT_BRIDGE_NAME + "[" + std::to_string(callHandlerID) + "] != null) { \ + window." + JAVASCRIPT_BRIDGE_NAME + "[" + std::to_string(callHandlerID) + "].reject(new Error('" + replace_all_copy(errorMessage, "\'", "\\'") + "')); \ + delete window." + JAVASCRIPT_BRIDGE_NAME + "[" + std::to_string(callHandlerID) + "]; \ + }", nullptr); + }; + channelDelegate->onCallJsHandler(handlerName, handlerArgs, std::move(callback)); + } + } } return S_OK; @@ -395,23 +452,23 @@ namespace flutter_inappwebview_plugin return webView && succeededOrLog(webView->get_DocumentTitle(&title)) ? wide_to_utf8(title.get()) : std::optional{}; } - void InAppWebView::loadUrl(const URLRequest& urlRequest) const + void InAppWebView::loadUrl(const std::shared_ptr urlRequest) const { - if (!webView || !urlRequest.url.has_value()) { + if (!webView || !urlRequest->url.has_value()) { return; } - std::wstring url = ansi_to_wide(urlRequest.url.value()); + std::wstring url = ansi_to_wide(urlRequest->url.value()); wil::com_ptr webViewEnv2; wil::com_ptr webView2; if (SUCCEEDED(webViewEnv->QueryInterface(IID_PPV_ARGS(&webViewEnv2))) && SUCCEEDED(webView->QueryInterface(IID_PPV_ARGS(&webView2)))) { wil::com_ptr webResourceRequest; - std::wstring method = urlRequest.method.has_value() ? ansi_to_wide(urlRequest.method.value()) : L"GET"; + std::wstring method = urlRequest->method.has_value() ? ansi_to_wide(urlRequest->method.value()) : L"GET"; wil::com_ptr postDataStream = nullptr; - if (urlRequest.body.has_value()) { - auto postData = std::string(urlRequest.body->begin(), urlRequest.body->end()); + if (urlRequest->body.has_value()) { + auto postData = std::string(urlRequest->body->begin(), urlRequest->body->end()); postDataStream = SHCreateMemStream( reinterpret_cast(postData.data()), static_cast(postData.length())); } @@ -427,8 +484,8 @@ namespace flutter_inappwebview_plugin if (method.compare(L"GET") != 0) { requestHeaders->SetHeader(L"Flutter-InAppWebView-Request-Method", method.c_str()); } - if (urlRequest.headers.has_value()) { - auto& headers = urlRequest.headers.value(); + if (urlRequest->headers.has_value()) { + auto& headers = urlRequest->headers.value(); for (auto const& [key, val] : headers) { requestHeaders->SetHeader(ansi_to_wide(key).c_str(), ansi_to_wide(val).c_str()); } @@ -441,6 +498,37 @@ namespace flutter_inappwebview_plugin failedLog(webView->Navigate(url.c_str())); } + + void InAppWebView::loadFile(const std::string& assetFilePath) const + { + if (!webView) { + return; + } + + WCHAR* buf = new WCHAR[32768]; + GetModuleFileName(NULL, buf, 32768); + std::filesystem::path exeAbsPath = std::wstring(buf); + delete[] buf; + + std::filesystem::path flutterAssetPath("data/flutter_assets/" + assetFilePath); + auto absAssetFilePath = exeAbsPath.parent_path() / flutterAssetPath; + + if (!std::filesystem::exists(absAssetFilePath)) { + debugLog(absAssetFilePath.native() + L" asset file cannot be found!"); + return; + } + failedLog(webView->Navigate(absAssetFilePath.c_str())); + } + + void InAppWebView::loadData(const std::string& data) const + { + if (!webView) { + return; + } + + failedLog(webView->NavigateToString(ansi_to_wide(data).c_str())); + } + void InAppWebView::reload() const { if (!webView) { @@ -527,7 +615,10 @@ namespace flutter_inappwebview_plugin int64_t size = items->size(); canGoBackOrForward_ = nextIndex >= 0 && nextIndex < size; } - completionHandler(canGoBackOrForward_); + + if (completionHandler) { + completionHandler(canGoBackOrForward_); + } } ); } @@ -544,13 +635,19 @@ namespace flutter_inappwebview_plugin void InAppWebView::getCopyBackForwardList(const std::function)> completionHandler) const { if (!webView) { - completionHandler(std::make_unique(std::nullopt, std::nullopt)); + if (completionHandler) { + completionHandler(std::make_unique(std::nullopt, std::nullopt)); + } return; } failedLog(webView->CallDevToolsProtocolMethod(L"Page.getNavigationHistory", L"{}", Callback( [completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) { + if (!completionHandler) { + return S_OK; + } + if (errorCode == S_OK) { auto historyJson = nlohmann::json::parse(wide_to_ansi(returnObjectAsJson)); @@ -585,7 +682,7 @@ namespace flutter_inappwebview_plugin ).Get())); } - void InAppWebView::evaluateJavascript(const std::string& source, std::function completionHanlder) const + void InAppWebView::evaluateJavascript(const std::string& source, const std::function completionHanlder) const { failedLog(webView->ExecuteScript(ansi_to_wide(source).c_str(), Callback( @@ -594,11 +691,51 @@ namespace flutter_inappwebview_plugin if (error != S_OK) { debugLog(error); } - completionHanlder(wide_to_ansi(result)); + + if (completionHanlder) { + completionHanlder(wide_to_ansi(result)); + } + return S_OK; }).Get())); } + void InAppWebView::addUserScript(const std::shared_ptr userScript) const + { + if (!userContentController) { + return; + } + + userContentController->addUserOnlyScript(userScript); + } + + void InAppWebView::removeUserScript(const int64_t index, const std::shared_ptr userScript) const + { + if (!userContentController) { + return; + } + + userContentController->removeUserOnlyScriptAt(index, userScript->injectionTime); + } + + void InAppWebView::removeUserScriptsByGroupName(const std::string& groupName) const + { + if (!userContentController) { + return; + } + + userContentController->removeUserOnlyScriptsByGroupName(groupName); + } + + void InAppWebView::removeAllUserScripts() const + { + if (!userContentController) { + return; + } + + userContentController->removeAllUserOnlyScripts(); + } + // flutter_view void InAppWebView::setSurfaceSize(size_t width, size_t height, float scale_factor) { diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index fb981452..6816590a 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -132,7 +132,9 @@ namespace flutter_inappwebview_plugin void prepare(); std::optional getUrl() const; std::optional getTitle() const; - void loadUrl(const URLRequest& urlRequest) const; + void loadUrl(const std::shared_ptr urlRequest) const; + void loadFile(const std::string& assetFilePath) const; + void loadData(const std::string& data) const; void reload() const; void goBack(); bool canGoBack() const; @@ -145,8 +147,12 @@ namespace flutter_inappwebview_plugin return isLoading_; } void stopLoading() const; - void evaluateJavascript(const std::string& source, std::function completionHanlder) const; + void evaluateJavascript(const std::string& source, const std::function completionHanlder) const; void getCopyBackForwardList(const std::function)> completionHandler) const; + void addUserScript(const std::shared_ptr userScript) const; + void removeUserScript(const int64_t index, const std::shared_ptr userScript) const; + void removeUserScriptsByGroupName(const std::string& groupName) const; + void removeAllUserScripts() const; static bool isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus); private: diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp index 10f2a0cd..761e1ff7 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp @@ -76,6 +76,8 @@ namespace flutter_inappwebview_plugin auto settingsMap = get_fl_map_value(*arguments, "initialSettings"); auto urlRequestMap = get_optional_fl_map_value(*arguments, "initialUrlRequest"); + auto initialFile = get_optional_fl_map_value(*arguments, "initialFile"); + auto initialDataMap = get_optional_fl_map_value(*arguments, "initialData"); auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, CW_DEFAULT, CW_DEFAULT, 0, 0, HWND_MESSAGE, nullptr, @@ -98,10 +100,16 @@ namespace flutter_inappwebview_plugin std::move(webViewEnv), std::move(webViewController), std::move(webViewCompositionController) ); - std::optional urlRequest = urlRequestMap.has_value() ? std::make_optional(urlRequestMap.value()) : std::optional{}; + std::optional> urlRequest = urlRequestMap.has_value() ? std::make_shared(urlRequestMap.value()) : std::optional>{}; if (urlRequest.has_value()) { inAppWebView->loadUrl(urlRequest.value()); } + else if (initialFile.has_value()) { + inAppWebView->loadFile(initialFile.value()); + } + else if (initialDataMap.has_value()) { + inAppWebView->loadData(get_fl_map_value(initialDataMap.value(), "data")); + } auto customPlatformView = std::make_unique(plugin->registrar->messenger(), plugin->registrar->texture_registrar(), diff --git a/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.cpp b/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.cpp index eed27040..9f51ed82 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.cpp @@ -1,7 +1,7 @@ #include #include "../utils/log.h" -#include "../utils/strconv.h" +#include "../utils/string.h" #include "../utils/vector.h" #include "in_app_webview.h" #include "user_content_controller.h" @@ -25,6 +25,18 @@ namespace flutter_inappwebview_plugin return; } + if (userScript->injectionTime == UserScriptInjectionTime::atDocumentStart && webView_) { + failedLog(webView_->webView->AddScriptToExecuteOnDocumentCreated(ansi_to_wide(userScript->source).c_str(), + Callback( + [userScript](HRESULT error, PCWSTR id) -> HRESULT + { + if (succeededOrLog(error)) { + userScript->id = id; + } + return S_OK; + }).Get())); + } + userOnlyScripts_.at(userScript->injectionTime).push_back(std::move(userScript)); } @@ -41,7 +53,11 @@ namespace flutter_inappwebview_plugin return; } - vector_remove_erase_el(userOnlyScripts_.at(userScript->injectionTime), std::move(userScript)); + if (webView_) { + failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(userScript->id.c_str())); + } + + vector_remove_erase(userOnlyScripts_.at(userScript->injectionTime), std::move(userScript)); } void UserContentController::removeUserOnlyScriptAt(const int64_t& index, const UserScriptInjectionTime& injectionTime) @@ -52,13 +68,49 @@ namespace flutter_inappwebview_plugin return; } - vec.erase(vec.begin() + index); + auto& userScript = vec.at(index); + if (userScript) { + if (webView_) { + failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(userScript->id.c_str())); + } + vec.erase(vec.begin() + index); + } } void UserContentController::removeAllUserOnlyScripts() { - userOnlyScripts_.at(UserScriptInjectionTime::atDocumentStart).clear(); - userOnlyScripts_.at(UserScriptInjectionTime::atDocumentEnd).clear(); + auto& userScriptsAtStart = userOnlyScripts_.at(UserScriptInjectionTime::atDocumentStart); + auto& userScriptsAtEnd = userOnlyScripts_.at(UserScriptInjectionTime::atDocumentEnd); + + if (webView_) { + for (auto& userScript : userScriptsAtStart) { + failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(userScript->id.c_str())); + } + for (auto& userScript : userScriptsAtEnd) { + failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(userScript->id.c_str())); + } + } + + userScriptsAtStart.clear(); + userScriptsAtEnd.clear(); + } + + void UserContentController::removeUserOnlyScriptsByGroupName(const std::string& groupName) + { + std::vector> userScriptsAtStart = userOnlyScripts_.at(UserScriptInjectionTime::atDocumentStart); + std::vector> userScriptsAtEnd = userOnlyScripts_.at(UserScriptInjectionTime::atDocumentEnd); + + for (auto& userScript : userScriptsAtStart) { + if (string_equals(groupName, userScript->groupName)) { + removeUserOnlyScript(userScript); + } + } + + for (auto& userScript : userScriptsAtEnd) { + if (string_equals(groupName, userScript->groupName)) { + removeUserOnlyScript(userScript); + } + } } std::vector> UserContentController::getPluginScriptsAt(const UserScriptInjectionTime& injectionTime) const @@ -66,13 +118,28 @@ namespace flutter_inappwebview_plugin return pluginScripts_.at(injectionTime); } + bool UserContentController::containsUserOnlyScript(std::shared_ptr userScript) const + { + if (!userScript) { + return false; + } + + return vector_contains(userOnlyScripts_.at(userScript->injectionTime), std::move(userScript)); + } + + bool UserContentController::containsUserOnlyScriptByGroupName(const std::string& groupName) const + { + return vector_contains_if(userOnlyScripts_.at(UserScriptInjectionTime::atDocumentStart), [groupName](const std::shared_ptr userScript) { return string_equals(groupName, userScript->groupName); }) || + vector_contains_if(userOnlyScripts_.at(UserScriptInjectionTime::atDocumentEnd), [groupName](const std::shared_ptr userScript) { return string_equals(groupName, userScript->groupName); }); + } + void UserContentController::addPluginScript(std::shared_ptr pluginScript) { - if (!pluginScript || !webView_) { + if (!pluginScript) { return; } - if (pluginScript->injectionTime == UserScriptInjectionTime::atDocumentStart) { + if (pluginScript->injectionTime == UserScriptInjectionTime::atDocumentStart && webView_) { failedLog(webView_->webView->AddScriptToExecuteOnDocumentCreated(ansi_to_wide(pluginScript->source).c_str(), Callback( [pluginScript](HRESULT error, PCWSTR id) -> HRESULT @@ -96,19 +163,93 @@ namespace flutter_inappwebview_plugin void UserContentController::removePluginScript(std::shared_ptr pluginScript) { - if (!pluginScript || !webView_) { + if (!pluginScript) { return; } - failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(pluginScript->id.c_str())); + if (webView_) { + failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(pluginScript->id.c_str())); + } - vector_remove_erase_el(pluginScripts_.at(pluginScript->injectionTime), std::move(pluginScript)); + vector_remove_erase(pluginScripts_.at(pluginScript->injectionTime), std::move(pluginScript)); } void UserContentController::removeAllPluginScripts() { - pluginScripts_.at(UserScriptInjectionTime::atDocumentStart).clear(); - pluginScripts_.at(UserScriptInjectionTime::atDocumentEnd).clear(); + auto& pluginScriptsAtStart = pluginScripts_.at(UserScriptInjectionTime::atDocumentStart); + auto& pluginScriptsAtEnd = pluginScripts_.at(UserScriptInjectionTime::atDocumentEnd); + + if (webView_) { + for (auto& pluginScript : pluginScriptsAtStart) { + failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(pluginScript->id.c_str())); + } + for (auto& pluginScript : pluginScriptsAtEnd) { + failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(pluginScript->id.c_str())); + } + } + + pluginScriptsAtStart.clear(); + pluginScriptsAtEnd.clear(); + } + + bool UserContentController::containsPluginScript(std::shared_ptr pluginScript) const + { + if (!pluginScript) { + return false; + } + + return vector_contains(pluginScripts_.at(pluginScript->injectionTime), std::move(pluginScript)); + } + + bool UserContentController::containsPluginScriptByGroupName(const std::string& groupName) const + { + return vector_contains_if(pluginScripts_.at(UserScriptInjectionTime::atDocumentStart), [groupName](const std::shared_ptr pluginScript) { return string_equals(groupName, pluginScript->groupName); }) || + vector_contains_if(pluginScripts_.at(UserScriptInjectionTime::atDocumentEnd), [groupName](const std::shared_ptr pluginScript) { return string_equals(groupName, pluginScript->groupName); }); + } + + void UserContentController::removePluginScriptsByGroupName(const std::string& groupName) + { + std::vector> pluginScriptsAtStart = pluginScripts_.at(UserScriptInjectionTime::atDocumentStart); + std::vector> pluginScriptsAtEnd = pluginScripts_.at(UserScriptInjectionTime::atDocumentEnd); + + for (auto& pluginScript : pluginScriptsAtStart) { + if (string_equals(groupName, pluginScript->groupName)) { + removePluginScript(pluginScript); + } + } + + for (auto& pluginScript : pluginScriptsAtEnd) { + if (string_equals(groupName, pluginScript->groupName)) { + removePluginScript(pluginScript); + } + } + } + + std::string UserContentController::generatePluginScriptsCodeAt(const UserScriptInjectionTime& injectionTime) const + { + std::string code; + std::vector> pluginScripts = pluginScripts_.at(injectionTime); + for (auto& pluginScript : pluginScripts) { + code += ";" + pluginScript->source; + } + return code; + } + + std::string UserContentController::generateUserOnlyScriptsCodeAt(const UserScriptInjectionTime& injectionTime) const + { + std::string code; + std::vector> userScripts = userOnlyScripts_.at(injectionTime); + for (auto& userScript : userScripts) { + code += ";" + userScript->source; + } + return code; + } + + std::string UserContentController::generateWrappedCodeForDocumentEnd() const + { + std::string code = generatePluginScriptsCodeAt(UserScriptInjectionTime::atDocumentEnd); + code += generateUserOnlyScriptsCodeAt(UserScriptInjectionTime::atDocumentEnd); + return replace_all_copy(USER_SCRIPTS_AT_DOCUMENT_END_WRAPPER_JS_SOURCE, VAR_PLACEHOLDER_VALUE, code); } UserContentController::~UserContentController() diff --git a/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.h b/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.h index 1fee5cfa..c051544a 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.h @@ -4,6 +4,8 @@ #include #include +#include "../plugin_scripts_js/javascript_bridge_js.h" +#include "../plugin_scripts_js/plugin_scripts_util.h" #include "../types/plugin_script.h" #include "../types/user_script.h" @@ -11,6 +13,12 @@ namespace flutter_inappwebview_plugin { class InAppWebView; + const std::string USER_SCRIPTS_AT_DOCUMENT_END_WRAPPER_JS_SOURCE = + "if (window." + JAVASCRIPT_BRIDGE_NAME + " != null && (window." + JAVASCRIPT_BRIDGE_NAME + "._userScriptsAtDocumentEndLoaded == null || !window." + JAVASCRIPT_BRIDGE_NAME + "._userScriptsAtDocumentEndLoaded)) { \ + window." + JAVASCRIPT_BRIDGE_NAME + "._userScriptsAtDocumentEndLoaded = true; \ + " + VAR_PLACEHOLDER_VALUE + " \ + }"; + class UserContentController { public: @@ -23,11 +31,20 @@ namespace flutter_inappwebview_plugin void removeUserOnlyScript(std::shared_ptr userScript); void removeUserOnlyScriptAt(const int64_t& index, const UserScriptInjectionTime& injectionTime); void removeAllUserOnlyScripts(); + bool containsUserOnlyScript(std::shared_ptr userScript) const; + bool containsUserOnlyScriptByGroupName(const std::string& groupName) const; + void removeUserOnlyScriptsByGroupName(const std::string& groupName); std::vector> getPluginScriptsAt(const UserScriptInjectionTime& injectionTime) const; void addPluginScript(std::shared_ptr pluginScript); void addPluginScripts(std::vector> pluginScripts); void removePluginScript(std::shared_ptr pluginScript); void removeAllPluginScripts(); + bool containsPluginScript(std::shared_ptr pluginScript) const; + bool containsPluginScriptByGroupName(const std::string& groupName) const; + void removePluginScriptsByGroupName(const std::string& groupName); + std::string generatePluginScriptsCodeAt(const UserScriptInjectionTime& injectionTime) const; + std::string generateUserOnlyScriptsCodeAt(const UserScriptInjectionTime& injectionTime) const; + std::string generateWrappedCodeForDocumentEnd() const; private: InAppWebView* webView_; diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index 1b634773..e1884e1f 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -3,6 +3,7 @@ #include "../utils/flutter.h" #include "../utils/log.h" #include "../utils/strconv.h" +#include "../utils/string.h" #include "in_app_webview.h" #include "webview_channel_delegate.h" @@ -28,6 +29,14 @@ namespace flutter_inappwebview_plugin }; } + WebViewChannelDelegate::CallJsHandlerCallback::CallJsHandlerCallback() + { + decodeResult = [](const flutter::EncodableValue* value) + { + return value; + }; + } + void WebViewChannelDelegate::HandleMethodCall(const flutter::MethodCall& method_call, std::unique_ptr> result) { @@ -37,42 +46,53 @@ namespace flutter_inappwebview_plugin } auto& arguments = std::get(*method_call.arguments()); + auto& methodName = method_call.method_name(); - if (method_call.method_name().compare("getUrl") == 0) { + if (string_equals(methodName, "getUrl")) { result->Success(make_fl_value(webView->getUrl())); } - else if (method_call.method_name().compare("getTitle") == 0) { + else if (string_equals(methodName, "getTitle")) { result->Success(make_fl_value(webView->getUrl())); } - else if (method_call.method_name().compare("loadUrl") == 0) { + else if (string_equals(methodName, "loadUrl")) { auto urlRequest = std::make_unique(get_fl_map_value(arguments, "urlRequest")); - webView->loadUrl(*urlRequest); + webView->loadUrl(std::move(urlRequest)); result->Success(true); } - else if (method_call.method_name().compare("reload") == 0) { + else if (string_equals(methodName, "loadFile")) { + auto assetFilePath = get_fl_map_value(arguments, "assetFilePath"); + webView->loadFile(assetFilePath); + result->Success(true); + } + else if (string_equals(methodName, "loadData")) { + auto data = get_fl_map_value(arguments, "data"); + webView->loadData(data); + result->Success(true); + } + else if (string_equals(methodName, "reload")) { webView->reload(); result->Success(true); } - else if (method_call.method_name().compare("goBack") == 0) { + else if (string_equals(methodName, "goBack")) { webView->goBack(); result->Success(true); } - else if (method_call.method_name().compare("canGoBack") == 0) { + else if (string_equals(methodName, "canGoBack")) { result->Success(webView->canGoBack()); } - else if (method_call.method_name().compare("goForward") == 0) { + else if (string_equals(methodName, "goForward")) { webView->goForward(); result->Success(true); } - else if (method_call.method_name().compare("canGoForward") == 0) { + else if (string_equals(methodName, "canGoForward")) { result->Success(webView->canGoForward()); } - else if (method_call.method_name().compare("goBackOrForward") == 0) { + else if (string_equals(methodName, "goBackOrForward")) { auto steps = get_fl_map_value(arguments, "steps"); webView->goBackOrForward(steps); result->Success(true); } - else if (method_call.method_name().compare("canGoBackOrForward") == 0) { + else if (string_equals(methodName, "canGoBackOrForward")) { auto result_ = std::shared_ptr>(std::move(result)); auto steps = get_fl_map_value(arguments, "steps"); @@ -81,14 +101,14 @@ namespace flutter_inappwebview_plugin result_->Success(value); }); } - else if (method_call.method_name().compare("isLoading") == 0) { + else if (string_equals(methodName, "isLoading")) { result->Success(webView->isLoading()); } - else if (method_call.method_name().compare("stopLoading") == 0) { + else if (string_equals(methodName, "stopLoading")) { webView->stopLoading(); result->Success(true); } - else if (method_call.method_name().compare("evaluateJavascript") == 0) { + else if (string_equals(methodName, "evaluateJavascript")) { auto result_ = std::shared_ptr>(std::move(result)); auto source = get_fl_map_value(arguments, "source"); @@ -97,23 +117,43 @@ namespace flutter_inappwebview_plugin result_->Success(value); }); } - else if (method_call.method_name().compare("getCopyBackForwardList") == 0) { + else if (string_equals(methodName, "getCopyBackForwardList")) { auto result_ = std::shared_ptr>(std::move(result)); webView->getCopyBackForwardList([result_ = std::move(result_)](const std::unique_ptr value) { result_->Success(value->toEncodableMap()); }); } + else if (string_equals(methodName, "addUserScript")) { + auto userScript = std::make_unique(get_fl_map_value(arguments, "userScript")); + webView->addUserScript(std::move(userScript)); + result->Success(true); + } + else if (string_equals(methodName, "removeUserScript")) { + auto index = get_fl_map_value(arguments, "index"); + auto userScript = std::make_unique(get_fl_map_value(arguments, "userScript")); + webView->removeUserScript(index, std::move(userScript)); + result->Success(true); + } + else if (string_equals(methodName, "removeUserScriptsByGroupName")) { + auto groupName = get_fl_map_value(arguments, "groupName"); + webView->removeUserScriptsByGroupName(groupName); + result->Success(true); + } + else if (string_equals(methodName, "removeAllUserScripts")) { + webView->removeAllUserScripts(); + result->Success(true); + } // for inAppBrowser - else if (webView->inAppBrowser && method_call.method_name().compare("show") == 0) { + else if (webView->inAppBrowser && string_equals(methodName, "show")) { webView->inAppBrowser->show(); result->Success(true); } - else if (webView->inAppBrowser && method_call.method_name().compare("hide") == 0) { + else if (webView->inAppBrowser && string_equals(methodName, "hide")) { webView->inAppBrowser->hide(); result->Success(true); } - else if (webView->inAppBrowser && method_call.method_name().compare("close") == 0) { + else if (webView->inAppBrowser && string_equals(methodName, "close")) { webView->inAppBrowser->close(); result->Success(true); } @@ -211,6 +251,19 @@ namespace flutter_inappwebview_plugin channel->InvokeMethod("onUpdateVisitedHistory", std::move(arguments)); } + void WebViewChannelDelegate::onCallJsHandler(const std::string& handlerName, const std::string& args, std::unique_ptr callback) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap{ + {"handlerName", handlerName}, + {"args", args} + }); + channel->InvokeMethod("onCallJsHandler", std::move(arguments), std::move(callback)); + } + WebViewChannelDelegate::~WebViewChannelDelegate() { debugLog("dealloc WebViewChannelDelegate"); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h index a3d8905d..f3141b4e 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h @@ -22,12 +22,18 @@ namespace flutter_inappwebview_plugin public: InAppWebView* webView; - class ShouldOverrideUrlLoadingCallback : public BaseCallbackResult { + class ShouldOverrideUrlLoadingCallback : public BaseCallbackResult { public: ShouldOverrideUrlLoadingCallback(); ~ShouldOverrideUrlLoadingCallback() = default; }; + class CallJsHandlerCallback : public BaseCallbackResult { + public: + CallJsHandlerCallback(); + ~CallJsHandlerCallback() = default; + }; + WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger); WebViewChannelDelegate(InAppWebView* webView, flutter::BinaryMessenger* messenger, const std::string& name); ~WebViewChannelDelegate(); @@ -43,6 +49,7 @@ namespace flutter_inappwebview_plugin void onReceivedHttpError(std::shared_ptr request, std::shared_ptr error) const; void onTitleChanged(const std::optional& title) const; void onUpdateVisitedHistory(const std::optional& url, const std::optional& isReload) const; + void onCallJsHandler(const std::string& handlerName, const std::string& args, std::unique_ptr callback) const; }; } diff --git a/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.cpp b/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.cpp index ba2c3066..a87baaf9 100644 --- a/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.cpp +++ b/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.cpp @@ -1,21 +1,12 @@ -#include +#include #include "javascript_bridge_js.h" namespace flutter_inappwebview_plugin { - const std::string JAVASCRIPT_BRIDGE_JS_SOURCE = "window." + JAVASCRIPT_BRIDGE_NAME + " = {}; \ - window." + JAVASCRIPT_BRIDGE_NAME + ".callHandler = function() { \ - var _callHandlerID = setTimeout(function() {}); \ - window.chrome.webview.postMessage({ 'internalHandlerName': 'callHandler', 'handlerName': arguments[0], '_callHandlerID' : _callHandlerID, 'args' : JSON.stringify(Array.prototype.slice.call(arguments, 1)) }); \ - return new Promise(function(resolve, reject) { \ - window." + JAVASCRIPT_BRIDGE_NAME + "[_callHandlerID] = { resolve: resolve, reject : reject };\ - });\ - };"; - std::unique_ptr createJavaScriptBridgePluginScript() { - const std::set allowedOriginRules = { "*" }; + const std::vector allowedOriginRules = { "*" }; return std::make_unique( JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT_GROUP_NAME, JAVASCRIPT_BRIDGE_JS_SOURCE, diff --git a/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.h b/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.h index 5366bc6e..a10c832c 100644 --- a/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.h +++ b/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.h @@ -9,7 +9,21 @@ namespace flutter_inappwebview_plugin { const std::string JAVASCRIPT_BRIDGE_NAME = "flutter_inappwebview"; + const std::string JAVASCRIPT_BRIDGE_JS_SOURCE = "window." + JAVASCRIPT_BRIDGE_NAME + " = {}; \ + window." + JAVASCRIPT_BRIDGE_NAME + ".callHandler = function() { \ + var _callHandlerID = setTimeout(function() {}); \ + window.chrome.webview.postMessage({ 'name': 'callHandler', 'body': {'handlerName': arguments[0], '_callHandlerID' : _callHandlerID, 'args' : JSON.stringify(Array.prototype.slice.call(arguments, 1))} }); \ + return new Promise(function(resolve, reject) { \ + window." + JAVASCRIPT_BRIDGE_NAME + "[_callHandlerID] = { resolve: resolve, reject : reject };\ + });\ + };"; const std::string JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT_GROUP_NAME = "IN_APP_WEBVIEW_JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT"; + const std::string PLATFORM_READY_JS_SOURCE = "(function() { \ + if ((window.top == null || window.top === window) && window." + JAVASCRIPT_BRIDGE_NAME + " != null && window." + JAVASCRIPT_BRIDGE_NAME + "._platformReady == null) { \ + window.dispatchEvent(new Event('flutterInAppWebViewPlatformReady')); \ + window." + JAVASCRIPT_BRIDGE_NAME + "._platformReady = true; \ + } \ + })();"; std::unique_ptr createJavaScriptBridgePluginScript(); } diff --git a/flutter_inappwebview_windows/windows/plugin_scripts_js/plugin_scripts_util.h b/flutter_inappwebview_windows/windows/plugin_scripts_js/plugin_scripts_util.h new file mode 100644 index 00000000..0c29cde5 --- /dev/null +++ b/flutter_inappwebview_windows/windows/plugin_scripts_js/plugin_scripts_util.h @@ -0,0 +1,11 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_SCRIPTS_UTIL_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_SCRIPTS_UTIL_H_ + +#include + +namespace flutter_inappwebview_plugin +{ + const std::string VAR_PLACEHOLDER_VALUE = "$IN_APP_WEBVIEW_PLACEHOLDER_VALUE"; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_SCRIPTS_UTIL_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/plugin_script.cpp b/flutter_inappwebview_windows/windows/types/plugin_script.cpp index adc7d3f8..d9f9c73a 100644 --- a/flutter_inappwebview_windows/windows/types/plugin_script.cpp +++ b/flutter_inappwebview_windows/windows/types/plugin_script.cpp @@ -6,7 +6,7 @@ namespace flutter_inappwebview_plugin const std::optional& groupName, const std::string& source, const UserScriptInjectionTime& injectionTime, - const std::set& allowedOriginRules + const std::vector& allowedOriginRules ) : UserScript(groupName, source, injectionTime, allowedOriginRules) {} diff --git a/flutter_inappwebview_windows/windows/types/plugin_script.h b/flutter_inappwebview_windows/windows/types/plugin_script.h index b2fde408..1e633c88 100644 --- a/flutter_inappwebview_windows/windows/types/plugin_script.h +++ b/flutter_inappwebview_windows/windows/types/plugin_script.h @@ -13,7 +13,7 @@ namespace flutter_inappwebview_plugin const std::optional& groupName, const std::string& source, const UserScriptInjectionTime& injectionTime, - const std::set& allowedOriginRules + const std::vector& allowedOriginRules ); ~PluginScript(); }; diff --git a/flutter_inappwebview_windows/windows/types/user_script.cpp b/flutter_inappwebview_windows/windows/types/user_script.cpp index 3e5f39db..4befa0f4 100644 --- a/flutter_inappwebview_windows/windows/types/user_script.cpp +++ b/flutter_inappwebview_windows/windows/types/user_script.cpp @@ -1,3 +1,4 @@ +#include "../utils/map.h" #include "user_script.h" namespace flutter_inappwebview_plugin @@ -6,10 +7,17 @@ namespace flutter_inappwebview_plugin const std::optional& groupName, const std::string& source, const UserScriptInjectionTime& injectionTime, - const std::set& allowedOriginRules + const std::vector& allowedOriginRules ) : groupName(groupName), source(source), injectionTime(injectionTime), allowedOriginRules(allowedOriginRules) {} + UserScript::UserScript(const flutter::EncodableMap& map) + : groupName(get_optional_fl_map_value(map, "groupName")), + source(get_fl_map_value(map, "source")), + injectionTime(static_cast(get_fl_map_value(map, "injectionTime"))), + allowedOriginRules(functional_map(get_fl_map_value(map, "allowedOriginRules"), [](const flutter::EncodableValue& m) { return std::get(m); })) + {} + UserScript::~UserScript() {} } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/user_script.h b/flutter_inappwebview_windows/windows/types/user_script.h index e6c79166..4c742cb8 100644 --- a/flutter_inappwebview_windows/windows/types/user_script.h +++ b/flutter_inappwebview_windows/windows/types/user_script.h @@ -1,14 +1,17 @@ #ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_USER_SCRIPT_H_ #define FLUTTER_INAPPWEBVIEW_PLUGIN_USER_SCRIPT_H_ +#include #include -#include #include +#include + +#include "../utils/flutter.h" namespace flutter_inappwebview_plugin { enum UserScriptInjectionTime { - atDocumentStart, + atDocumentStart = 0, atDocumentEnd }; @@ -19,14 +22,15 @@ namespace flutter_inappwebview_plugin const std::optional groupName; const std::string source; const UserScriptInjectionTime injectionTime; - const std::set allowedOriginRules; + const std::vector allowedOriginRules; UserScript( const std::optional& groupName, const std::string& source, const UserScriptInjectionTime& injectionTime, - const std::set& allowedOriginRules + const std::vector& allowedOriginRules ); + UserScript(const flutter::EncodableMap& map); ~UserScript(); }; } diff --git a/flutter_inappwebview_windows/windows/types/web_history.cpp b/flutter_inappwebview_windows/windows/types/web_history.cpp index b7cb9dee..aa89d9a2 100644 --- a/flutter_inappwebview_windows/windows/types/web_history.cpp +++ b/flutter_inappwebview_windows/windows/types/web_history.cpp @@ -1,4 +1,4 @@ -#include "../utils/util.h" +#include "../utils/vector.h" #include "web_history.h" namespace flutter_inappwebview_plugin diff --git a/flutter_inappwebview_windows/windows/utils/flutter.h b/flutter_inappwebview_windows/windows/utils/flutter.h index 6447b866..e8ed6444 100644 --- a/flutter_inappwebview_windows/windows/utils/flutter.h +++ b/flutter_inappwebview_windows/windows/utils/flutter.h @@ -1,5 +1,5 @@ -#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_FLUTTER_UTIL_H_ -#define FLUTTER_INAPPWEBVIEW_PLUGIN_FLUTTER_UTIL_H_ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_FLUTTER_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_FLUTTER_H_ #include @@ -148,4 +148,4 @@ namespace flutter_inappwebview_plugin } } -#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_FLUTTER_UTIL_H_ \ No newline at end of file +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_FLUTTER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/log.h b/flutter_inappwebview_windows/windows/utils/log.h index c410164a..e87cdd0c 100644 --- a/flutter_inappwebview_windows/windows/utils/log.h +++ b/flutter_inappwebview_windows/windows/utils/log.h @@ -4,12 +4,14 @@ #include #include #include +#include #include "strconv.h" namespace flutter_inappwebview_plugin { - static inline void debugLog(const std::string& msg, const bool& isError = false) + template + static inline void debugLog(const std::basic_string& msg, const bool& isError = false) { #ifndef NDEBUG if (isError) { @@ -22,11 +24,30 @@ namespace flutter_inappwebview_plugin #endif } + static inline void debugLog(const char* msg, const bool& isError = false) + { + debugLog(std::string(msg), isError); + } + static inline void debugLog(const std::wstring& msg, const bool& isError = false) { debugLog(wide_to_ansi(msg), isError); } + static inline void debugLog(const bool& value, const bool& isError = false) + { + debugLog(value ? "true" : "false", isError); + } + + template< + typename T, + typename = typename std::enable_if::value, T>::type + > + static inline void debugLog(const T& value, const bool& isError = false) + { + debugLog(std::to_string(value), isError); + } + static inline std::string getHRMessage(const HRESULT& error) { return wide_to_ansi(_com_error(error).ErrorMessage()); diff --git a/flutter_inappwebview_windows/windows/utils/map.h b/flutter_inappwebview_windows/windows/utils/map.h index 3a432d9f..c46d4a17 100644 --- a/flutter_inappwebview_windows/windows/utils/map.h +++ b/flutter_inappwebview_windows/windows/utils/map.h @@ -1,11 +1,10 @@ -#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_MAP_H_ -#define FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_MAP_H_ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_MAP_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_MAP_H_ #include #include #include #include -#include namespace flutter_inappwebview_plugin { @@ -33,39 +32,6 @@ namespace flutter_inappwebview_plugin auto itr = map.find(key); return itr != map.end() ? itr->second : nullptr; } - - template - static inline auto functional_map(Iterator begin, Iterator end, Func&& func) -> - std::vector()))> - { - using value_type = decltype(func(std::declval())); - - std::vector out_vector; - out_vector.reserve(std::distance(begin, end)); - - std::transform(begin, end, std::back_inserter(out_vector), - std::forward(func)); - - return out_vector; - } - - template - static inline auto functional_map(const T& iterable, Func&& func) -> - std::vector()))> - { - return functional_map(std::begin(iterable), std::end(iterable), - std::forward(func)); - } - - template - static inline auto functional_map(const std::optional& iterable, Func&& func) -> - std::vector()))> - { - if (!iterable.has_value()) { - return {}; - } - return functional_map(iterable.value(), std::forward(func)); - } } -#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_MAP_H_ \ No newline at end of file +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_MAP_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/string.h b/flutter_inappwebview_windows/windows/utils/string.h new file mode 100644 index 00000000..9fcf7e7d --- /dev/null +++ b/flutter_inappwebview_windows/windows/utils/string.h @@ -0,0 +1,112 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_ + +#include +#include + +#include "strconv.h" + +namespace flutter_inappwebview_plugin +{ + template + struct is_string + : std::false_type + {}; + + // Partial specialization - parameters used to qualify the specialization + template + struct is_string> + : std::true_type + {}; + + template + using is_basic_string = is_string>; + + template + static inline bool string_equals(const std::basic_string& s1, const std::basic_string& s2) + { + return s1.compare(s2) == 0; + } + + template + static inline bool string_equals(const std::basic_string& s1, const char* s2) + { + return s1.compare(s2) == 0; + } + + template + static inline bool string_equals(const char* s1, const std::basic_string& s2) + { + return s2.compare(s1) == 0; + } + + static inline bool string_equals(const std::string& s1, const std::wstring& s2) + { + return string_equals(s1, wide_to_ansi(s2)); + } + + static inline bool string_equals(const std::wstring& s1, const std::string& s2) + { + return string_equals(wide_to_ansi(s1), s2); + } + + template + static inline bool string_equals(const std::optional>& s1, const std::basic_string& s2) + { + return s1.has_value() ? string_equals(s1.value(), s2) : false; + } + + template + static inline bool string_equals(const std::basic_string& s1, const std::optional>& s2) + { + return s2.has_value() ? string_equals(s1, s2.value()) : false; + } + + template + static inline bool string_equals(const std::optional>& s1, const std::optional>& s2) + { + return s1.has_value() && s2.has_value() ? string_equals(s1.value(), s2.value()) : true; + } + + static inline void replace_all(std::string& source, const std::string& from, const std::string& to) + { + std::string newString; + newString.reserve(source.length()); // avoids a few memory allocations + + std::string::size_type lastPos = 0; + std::string::size_type findPos; + + while (std::string::npos != (findPos = source.find(from, lastPos))) { + newString.append(source, lastPos, findPos - lastPos); + newString += to; + lastPos = findPos + from.length(); + } + + // Care for the rest after last occurrence + newString += source.substr(lastPos); + + source.swap(newString); + } + + static inline std::string replace_all_copy(const std::string& source, const std::string& from, const std::string& to) + { + std::string newString; + newString.reserve(source.length()); // avoids a few memory allocations + + std::string::size_type lastPos = 0; + std::string::size_type findPos; + + while (std::string::npos != (findPos = source.find(from, lastPos))) { + newString.append(source, lastPos, findPos - lastPos); + newString += to; + lastPos = findPos + from.length(); + } + + // Care for the rest after last occurrence + newString += source.substr(lastPos); + + return newString; + } +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/vector.h b/flutter_inappwebview_windows/windows/utils/vector.h index 5ba8558b..4a70e8a4 100644 --- a/flutter_inappwebview_windows/windows/utils/vector.h +++ b/flutter_inappwebview_windows/windows/utils/vector.h @@ -1,7 +1,8 @@ -#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_VECTOR_H_ -#define FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_VECTOR_H_ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_VECTOR_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_VECTOR_H_ #include +#include #include #include #include @@ -26,16 +27,73 @@ namespace flutter_inappwebview_plugin struct is_vector : is_vector_impl::type { }; template - static inline void vector_remove_el(std::vector& vec, const T& el) + static inline void vector_remove(std::vector& vec, const T& el) { std::remove(vec.begin(), vec.end(), el); } + template + static inline void vector_remove_if(std::vector& vec, UnaryPredicate&& predicate) + { + std::remove_if(vec.begin(), vec.end(), std::forward(predicate)); + } + template - static inline void vector_remove_erase_el(std::vector& vec, const T& el) + static inline void vector_remove_erase(std::vector& vec, const T& el) { vec.erase(std::remove(vec.begin(), vec.end(), el), vec.end()); } + + template + static inline void vector_remove_erase_if(std::vector& vec, UnaryPredicate&& predicate) + { + vec.erase(std::remove_if(vec.begin(), vec.end(), std::forward(predicate)), vec.end()); + } + + template + static inline bool vector_contains(const std::vector& vec, const T& value) + { + return std::find(vec.begin(), vec.end(), value) != vec.end(); + } + + template + static inline bool vector_contains_if(const std::vector& vec, UnaryPredicate&& predicate) + { + return std::find_if(vec.begin(), vec.end(), std::forward(predicate)) != vec.end(); + } + + template + static inline auto functional_map(Iterator begin, Iterator end, Func&& func) -> + std::vector()))> + { + using value_type = decltype(func(std::declval())); + + std::vector out_vector; + out_vector.reserve(std::distance(begin, end)); + + std::transform(begin, end, std::back_inserter(out_vector), + std::forward(func)); + + return out_vector; + } + + template + static inline auto functional_map(const T& iterable, Func&& func) -> + std::vector()))> + { + return functional_map(std::begin(iterable), std::end(iterable), + std::forward(func)); + } + + template + static inline auto functional_map(const std::optional& iterable, Func&& func) -> + std::vector()))> + { + if (!iterable.has_value()) { + return {}; + } + return functional_map(iterable.value(), std::forward(func)); + } } -#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_VECTOR_H_ \ No newline at end of file +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_VECTOR_H_ \ No newline at end of file From b27036e59bacda31d76048af442c97ccd721c94c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 Jan 2024 20:16:53 +0100 Subject: [PATCH 10/21] windows: updated webview and browser creation params, added support for openWithSystemBrowser --- .../platform_in_app_browser.dart | 27 +++++++++++++ .../src/in_app_webview/platform_webview.dart | 1 + .../src/in_app_webview/in_app_webview.dart | 2 - .../windows/in_app_browser/in_app_browser.cpp | 28 +++++++------ .../windows/in_app_browser/in_app_browser.h | 4 +- .../in_app_browser/in_app_browser_manager.cpp | 39 ++++++++++++++++--- .../in_app_browser/in_app_browser_manager.h | 2 +- .../windows/in_app_webview/in_app_webview.cpp | 8 +++- .../windows/in_app_webview/in_app_webview.h | 3 +- .../in_app_webview/in_app_webview_manager.cpp | 17 ++++++-- .../in_app_webview/in_app_webview_manager.h | 2 +- .../windows/utils/flutter.h | 32 ++++++++------- 12 files changed, 123 insertions(+), 42 deletions(-) diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart index 1af48a02..b2567865 100755 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart @@ -83,29 +83,53 @@ abstract class PlatformInAppBrowser extends PlatformInterface ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.contextMenu} ///Context menu used by the browser. It should be set before opening the browser. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS ///{@endtemplate} ContextMenu? get contextMenu => params.contextMenu; ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.pullToRefreshController} ///Represents the pull-to-refresh feature controller. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS ///{@endtemplate} PlatformPullToRefreshController? get pullToRefreshController => params.pullToRefreshController; ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.findInteractionController} ///Represents the find interaction feature controller. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS ///{@endtemplate} PlatformFindInteractionController? get findInteractionController => params.findInteractionController; ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.initialUserScripts} ///Initial list of user scripts to be loaded at start or end of a page loading. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///- Windows ///{@endtemplate} UnmodifiableListView? get initialUserScripts => params.initialUserScripts; ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser.windowId} ///The window id of a [CreateWindowAction.windowId]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS ///{@endtemplate} int? get windowId => params.windowId; @@ -227,6 +251,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future openFile( {required String assetFilePath, @@ -254,6 +279,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future openData( {required String data, @@ -276,6 +302,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} Future openWithSystemBrowser({required WebUri url}) { throw UnimplementedError( diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_webview.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_webview.dart index ff250815..d0c21f0f 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_webview.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_webview.dart @@ -1124,6 +1124,7 @@ class PlatformWebViewCreationParams { ///- Android native WebView ///- iOS ///- MacOS + ///- Windows ///{@endtemplate} final UnmodifiableListView? initialUserScripts; diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart index 09fac537..1a35ae3b 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart @@ -315,9 +315,7 @@ class WindowsInAppWebViewWidget extends PlatformInAppWebViewWidget { : null, 'initialUserScripts': params.initialUserScripts?.map((e) => e.toMap()).toList() ?? [], - 'pullToRefreshSettings': pullToRefreshSettings, 'keepAliveId': params.keepAlive?.id, - 'preventGestureDelay': params.preventGestureDelay }, ); } diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index 824602a1..c64b1293 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -11,7 +11,6 @@ namespace flutter_inappwebview_plugin : plugin(plugin), m_hInstance(GetModuleHandle(nullptr)), id(params.id), - initialUrlRequest(params.urlRequest), settings(params.initialSettings), channelDelegate(std::make_unique(id, plugin->registrar->messenger())) { @@ -26,29 +25,30 @@ namespace flutter_inappwebview_plugin RegisterClass(&wndClass); m_hWnd = CreateWindowEx( - 0, // Optional window styles. + 0, // Optional window styles. wndClass.lpszClassName, // Window class - L"", // Window text + L"", // Window text WS_OVERLAPPEDWINDOW, // Window style // Size and position CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - NULL, // Parent window - NULL, // Menu - wndClass.hInstance,// Instance handle - this // Additional application data + NULL, // Parent window + NULL, // Menu + wndClass.hInstance, // Instance handle + this // Additional application data ); ShowWindow(m_hWnd, settings->hidden ? SW_HIDE : SW_SHOW); InAppWebViewCreationParams webViewParams = { id, - params.initialWebViewSettings + params.initialWebViewSettings, + params.initialUserScripts }; InAppWebView::createInAppWebViewEnv(m_hWnd, false, - [this, webViewParams](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) -> void + [this, params, webViewParams](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) -> void { if (webViewEnv && webViewController) { webView = std::make_unique(this, this->plugin, webViewParams, m_hWnd, std::move(webViewEnv), std::move(webViewController), nullptr); @@ -58,8 +58,14 @@ namespace flutter_inappwebview_plugin channelDelegate->onBrowserCreated(); } - if (initialUrlRequest.has_value()) { - webView->loadUrl(initialUrlRequest.value()); + if (params.urlRequest.has_value()) { + webView->loadUrl(params.urlRequest.value()); + } + else if (params.assetFilePath.has_value()) { + webView->loadFile(params.assetFilePath.value()); + } + else if (params.data.has_value()) { + webView->loadData(params.data.value()); } } else { diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h index fe2e6988..9d419746 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h @@ -19,8 +19,11 @@ namespace flutter_inappwebview_plugin { const std::string id; const std::optional> urlRequest; + const std::optional assetFilePath; + const std::optional data; const std::shared_ptr initialSettings; const std::shared_ptr initialWebViewSettings; + const std::optional>> initialUserScripts; }; class InAppBrowser { @@ -35,7 +38,6 @@ namespace flutter_inappwebview_plugin const FlutterInappwebviewWindowsPlugin* plugin; const std::string id; - const std::optional> initialUrlRequest; std::unique_ptr webView; std::unique_ptr channelDelegate; const std::shared_ptr settings; diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp index 0459326d..b52d5bc0 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -1,10 +1,14 @@ #include #include +#include #include "../in_app_webview/in_app_webview_settings.h" #include "../types/url_request.h" +#include "../types/user_script.h" #include "../utils/flutter.h" #include "../utils/log.h" +#include "../utils/string.h" +#include "../utils/vector.h" #include "in_app_browser_manager.h" #include "in_app_browser_settings.h" @@ -17,31 +21,56 @@ namespace flutter_inappwebview_plugin void InAppBrowserManager::HandleMethodCall(const flutter::MethodCall& method_call, std::unique_ptr> result) { - if (method_call.method_name().compare("open") == 0) { - auto* arguments = std::get_if(method_call.arguments()); - createInAppWebView(arguments); + auto* arguments = std::get_if(method_call.arguments()); + auto& methodName = method_call.method_name(); + + if (string_equals(methodName, "open")) { + createInAppBrowser(arguments); result->Success(flutter::EncodableValue(true)); } + else if (string_equals(methodName, "openWithSystemBrowser")) { + auto url = get_fl_map_value(*arguments, "url"); + + int status = static_cast(reinterpret_cast( + ShellExecute(nullptr, TEXT("open"), ansi_to_wide(url).c_str(), + nullptr, nullptr, SW_SHOWNORMAL))); + + // Anything >32 indicates success. + // https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutea#return-value + if (status <= 32) { + std::cerr << "Failed to open " << url << ": ShellExecute error code " << status << std::endl; + } + } else { result->NotImplemented(); } } - void InAppBrowserManager::createInAppWebView(const flutter::EncodableMap* arguments) + void InAppBrowserManager::createInAppBrowser(const flutter::EncodableMap* arguments) { auto id = get_fl_map_value(*arguments, "id"); auto urlRequestMap = get_optional_fl_map_value(*arguments, "urlRequest"); + auto assetFilePath = get_optional_fl_map_value(*arguments, "assetFilePath"); + auto data = get_optional_fl_map_value(*arguments, "data"); + auto initialUserScriptList = get_optional_fl_map_value(*arguments, "initialUserScripts"); + std::optional> urlRequest = urlRequestMap.has_value() ? std::make_shared(urlRequestMap.value()) : std::optional>{}; auto settingsMap = get_fl_map_value(*arguments, "settings"); auto initialSettings = std::make_unique(settingsMap); auto initialWebViewSettings = std::make_unique(settingsMap); + std::optional>> initialUserScripts = initialUserScriptList.has_value() ? + functional_map(initialUserScriptList.value(), [](const flutter::EncodableValue& map) { return std::make_shared(std::get(map)); }) : + std::optional>>{}; InAppBrowserCreationParams params = { id, urlRequest, + assetFilePath, + data, std::move(initialSettings), - std::move(initialWebViewSettings) + std::move(initialWebViewSettings), + initialUserScripts }; auto inAppBrowser = std::make_unique(plugin, params); diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h index cf77e008..c8b53a27 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.h @@ -27,7 +27,7 @@ namespace flutter_inappwebview_plugin const flutter::MethodCall& method_call, std::unique_ptr> result); - void createInAppWebView(const flutter::EncodableMap* arguments); + void createInAppBrowser(const flutter::EncodableMap* arguments); }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_MANAGER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index 118eadd2..c0ee89fe 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -44,7 +44,7 @@ namespace flutter_inappwebview_plugin this->webViewController->put_Bounds(bounds); } - prepare(); + prepare(params); } InAppWebView::InAppWebView(InAppBrowser* inAppBrowser, const FlutterInappwebviewWindowsPlugin* plugin, const InAppWebViewCreationParams& params, const HWND parentWindow, wil::com_ptr webViewEnv, @@ -118,7 +118,7 @@ namespace flutter_inappwebview_plugin std::make_unique(this, plugin->registrar->messenger()); } - void InAppWebView::prepare() + void InAppWebView::prepare(const InAppWebViewCreationParams& params) { if (!webView) { return; @@ -140,6 +140,10 @@ namespace flutter_inappwebview_plugin userContentController->addPluginScript(std::move(createJavaScriptBridgePluginScript())); + if (params.initialUserScripts.has_value()) { + userContentController->addUserOnlyScripts(params.initialUserScripts.value()); + } + registerEventHandlers(); } diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index 6816590a..ec87f6fa 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -72,6 +72,7 @@ namespace flutter_inappwebview_plugin struct InAppWebViewCreationParams { const std::variant id; const std::shared_ptr initialSettings; + const std::optional>> initialUserScripts; }; class InAppWebView @@ -129,7 +130,7 @@ namespace flutter_inappwebview_plugin winrt::com_ptr compositor); void initChannel(const std::optional> viewId, const std::optional channelName); - void prepare(); + void prepare(const InAppWebViewCreationParams& params); std::optional getUrl() const; std::optional getTitle() const; void loadUrl(const std::shared_ptr urlRequest) const; diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp index 761e1ff7..44cbe303 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp @@ -6,8 +6,11 @@ #include "../in_app_webview/in_app_webview_settings.h" #include "../types/url_request.h" +#include "../types/user_script.h" #include "../utils/flutter.h" #include "../utils/log.h" +#include "../utils/string.h" +#include "../utils/vector.h" #include "in_app_webview_manager.h" namespace flutter_inappwebview_plugin @@ -49,16 +52,17 @@ namespace flutter_inappwebview_plugin std::unique_ptr> result) { auto* arguments = std::get_if(method_call.arguments()); + auto& methodName = method_call.method_name(); - if (method_call.method_name().compare("createInAppWebView") == 0) { + if (string_equals(methodName, "createInAppWebView")) { if (isSupported()) { - createInAppWebView(arguments, std::move(result)); + createInAppBrowser(arguments, std::move(result)); } else { result->Error("0", "Creating an InAppWebView instance is not supported! Graphics Context is not valid!"); } } - else if (method_call.method_name().compare("dispose") == 0) { + else if (string_equals(methodName, "dispose")) { auto id = get_fl_map_value(*arguments, "id"); if (map_contains(webViews, (uint64_t)id)) { webViews.erase(id); @@ -70,7 +74,7 @@ namespace flutter_inappwebview_plugin } } - void InAppWebViewManager::createInAppWebView(const flutter::EncodableMap* arguments, std::unique_ptr> result) + void InAppWebViewManager::createInAppBrowser(const flutter::EncodableMap* arguments, std::unique_ptr> result) { auto result_ = std::shared_ptr>(std::move(result)); @@ -78,6 +82,7 @@ namespace flutter_inappwebview_plugin auto urlRequestMap = get_optional_fl_map_value(*arguments, "initialUrlRequest"); auto initialFile = get_optional_fl_map_value(*arguments, "initialFile"); auto initialDataMap = get_optional_fl_map_value(*arguments, "initialData"); + auto initialUserScriptList = get_optional_fl_map_value(*arguments, "initialUserScripts"); auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, CW_DEFAULT, CW_DEFAULT, 0, 0, HWND_MESSAGE, nullptr, @@ -90,10 +95,14 @@ namespace flutter_inappwebview_plugin { if (webViewEnv && webViewController && webViewCompositionController) { auto initialSettings = std::make_unique(settingsMap); + std::optional>> initialUserScripts = initialUserScriptList.has_value() ? + functional_map(initialUserScriptList.value(), [](const flutter::EncodableValue& map) { return std::make_shared(std::get(map)); }) : + std::optional>>{}; InAppWebViewCreationParams params = { "", std::move(initialSettings), + initialUserScripts }; auto inAppWebView = std::make_unique(plugin, params, hwnd, diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.h index 8630ceeb..5d89e9cc 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.h @@ -44,7 +44,7 @@ namespace flutter_inappwebview_plugin const flutter::MethodCall& method_call, std::unique_ptr> result); - void createInAppWebView(const flutter::EncodableMap* arguments, std::unique_ptr> result); + void createInAppBrowser(const flutter::EncodableMap* arguments, std::unique_ptr> result); private: std::unique_ptr rohelper_; winrt::com_ptr diff --git a/flutter_inappwebview_windows/windows/utils/flutter.h b/flutter_inappwebview_windows/windows/utils/flutter.h index e8ed6444..6bd271b6 100644 --- a/flutter_inappwebview_windows/windows/utils/flutter.h +++ b/flutter_inappwebview_windows/windows/utils/flutter.h @@ -81,32 +81,36 @@ namespace flutter_inappwebview_plugin } template - static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* string) + static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* key) { - return std::get(map.at(make_fl_value(string))); + return std::get(map.at(make_fl_value(key))); } template::value && !is_vector::value) || std::is_same::value || std::is_same::value), int>::type* = nullptr> - static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) + static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* key) { - return make_pointer_optional(std::get_if(&map.at(make_fl_value(string)))); + auto fl_key = make_fl_value(key); + if (map_contains(map, fl_key)) { + return make_pointer_optional(std::get_if(&map.at(fl_key))); + } + return std::nullopt; } template - static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* string, const T& defaultValue) + static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* key, const T& defaultValue) { - auto optional = get_optional_fl_map_value(map, string); + auto optional = get_optional_fl_map_value(map, key); return !optional.has_value() ? defaultValue : optional.value(); } template::value && !std::is_same::value)>::type* = nullptr> - static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) + static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* key) { using K = typename T::key_type; using V = typename T::mapped_type; - auto flMap = std::get_if(&map.at(make_fl_value(string))); + auto flMap = std::get_if(&map.at(make_fl_value(key))); if (flMap) { T mapValue = {}; for (auto itr = flMap->begin(); itr != flMap->end(); itr++) { @@ -118,18 +122,18 @@ namespace flutter_inappwebview_plugin } template - static inline std::map get_fl_map_value(const flutter::EncodableMap& map, const char* string, const std::map& defaultValue) + static inline std::map get_fl_map_value(const flutter::EncodableMap& map, const char* key, const std::map& defaultValue) { - auto optional = get_optional_fl_map_value>(map, string); + auto optional = get_optional_fl_map_value>(map, key); return !optional.has_value() ? defaultValue : optional.value(); } template::value && !std::is_same::value), bool>::type* = nullptr> - static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string) + static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* key) { using V = typename T::value_type; - auto flList = std::get_if(&map.at(make_fl_value(string))); + auto flList = std::get_if(&map.at(make_fl_value(key))); if (flList) { T vecValue(flList->size()); for (auto itr = flList->begin(); itr != flList->end(); itr++) { @@ -141,9 +145,9 @@ namespace flutter_inappwebview_plugin } template - static inline std::vector get_fl_map_value(const flutter::EncodableMap& map, const char* string, const std::vector& defaultValue) + static inline std::vector get_fl_map_value(const flutter::EncodableMap& map, const char* key, const std::vector& defaultValue) { - auto optional = get_optional_fl_map_value>(map, string); + auto optional = get_optional_fl_map_value>(map, key); return !optional.has_value() ? defaultValue : optional.value(); } } From ed6283d3b35a2337aa3a1e2082dd4109a6a83aa3 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 20 Jan 2024 22:13:24 +0100 Subject: [PATCH 11/21] windows: added content world support for user scripts and javascript evaluation --- .../windows/CMakeLists.txt | 2 + .../windows/in_app_browser/in_app_browser.cpp | 2 +- .../in_app_browser/in_app_browser_manager.cpp | 2 +- .../windows/in_app_webview/in_app_webview.cpp | 165 ++++++++--- .../windows/in_app_webview/in_app_webview.h | 15 +- .../user_content_controller.cpp | 271 ++++++++++++++---- .../in_app_webview/user_content_controller.h | 30 +- .../webview_channel_delegate.cpp | 20 +- .../in_app_webview/webview_channel_delegate.h | 1 + .../javascript_bridge_js.cpp | 4 +- .../plugin_scripts_js/plugin_scripts_util.h | 3 +- .../windows/types/content_world.cpp | 60 ++++ .../windows/types/content_world.h | 33 +++ .../windows/types/plugin_script.cpp | 20 +- .../windows/types/plugin_script.h | 14 +- .../windows/types/user_script.cpp | 8 +- .../windows/types/user_script.h | 7 +- .../windows/utils/log.h | 54 ++-- .../windows/utils/string.h | 33 ++- 19 files changed, 613 insertions(+), 131 deletions(-) create mode 100644 flutter_inappwebview_windows/windows/types/content_world.cpp create mode 100644 flutter_inappwebview_windows/windows/types/content_world.h diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt index 7df66194..908110bb 100644 --- a/flutter_inappwebview_windows/windows/CMakeLists.txt +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -64,6 +64,8 @@ list(APPEND PLUGIN_SOURCES "types/web_history.h" "types/web_history_item.cpp" "types/web_history_item.h" + "types/content_world.cpp" + "types/content_world.h" "types/user_script.cpp" "types/user_script.h" "types/plugin_script.cpp" diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index c64b1293..c87d05a0 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -190,7 +190,7 @@ namespace flutter_inappwebview_plugin void InAppBrowser::didChangeTitle(const std::optional& title) const { if (title.has_value()) { - SetWindowText(m_hWnd, ansi_to_wide(title.value()).c_str()); + SetWindowText(m_hWnd, utf8_to_wide(title.value()).c_str()); } } diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp index b52d5bc0..96bd4fb8 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -32,7 +32,7 @@ namespace flutter_inappwebview_plugin auto url = get_fl_map_value(*arguments, "url"); int status = static_cast(reinterpret_cast( - ShellExecute(nullptr, TEXT("open"), ansi_to_wide(url).c_str(), + ShellExecute(nullptr, TEXT("open"), utf8_to_wide(url).c_str(), nullptr, nullptr, SW_SHOWNORMAL))); // Anything >32 indicates success. diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index c0ee89fe..e494c6a8 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -133,11 +133,40 @@ namespace flutter_inappwebview_plugin wil::com_ptr webView2Settings2; if (succeededOrLog(webView2Settings->QueryInterface(IID_PPV_ARGS(&webView2Settings2)))) { if (!settings->userAgent.empty()) { - webView2Settings2->put_UserAgent(ansi_to_wide(settings->userAgent).c_str()); + webView2Settings2->put_UserAgent(utf8_to_wide(settings->userAgent).c_str()); } } } + // required to make Runtime events work + failedLog(webView->CallDevToolsProtocolMethod(L"Runtime.enable", L"{}", Callback( + [this](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + failedLog(errorCode); + return S_OK; + } + ).Get())); + + // required to make Page events work and to add User Scripts + failedLog(webView->CallDevToolsProtocolMethod(L"Page.enable", L"{}", Callback( + [this](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + failedLog(errorCode); + return S_OK; + } + ).Get())); + + failedLog(webView->CallDevToolsProtocolMethod(L"Page.getFrameTree", L"{}", Callback( + [this](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + if (succeededOrLog(errorCode)) { + auto treeJson = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson)); + pageFrameId_ = treeJson["frameTree"]["frame"]["id"].get(); + } + return S_OK; + } + ).Get())); + userContentController->addPluginScript(std::move(createJavaScriptBridgePluginScript())); if (params.initialUserScripts.has_value()) { @@ -241,7 +270,7 @@ namespace flutter_inappwebview_plugin UINT64 navigationId; if (SUCCEEDED(args->get_NavigationId(&navigationId))) { - navigationActions.insert({ navigationId, navigationAction }); + navigationActions_.insert({ navigationId, navigationAction }); } if (callShouldOverrideUrlLoading_ && requestMethod == nullptr) { @@ -287,17 +316,14 @@ namespace flutter_inappwebview_plugin { isLoading_ = false; - if (userContentController) { - evaluateJavascript(userContentController->generateWrappedCodeForDocumentEnd(), nullptr); - } - evaluateJavascript(PLATFORM_READY_JS_SOURCE, nullptr); + evaluateJavascript(PLATFORM_READY_JS_SOURCE, ContentWorld::page(), nullptr); std::shared_ptr navigationAction; UINT64 navigationId; if (SUCCEEDED(args->get_NavigationId(&navigationId))) { - navigationAction = map_at_or_null(navigationActions, navigationId); + navigationAction = map_at_or_null(navigationActions_, navigationId); if (navigationAction) { - navigationActions.erase(navigationId); + navigationActions_.erase(navigationId); } } @@ -338,7 +364,7 @@ namespace flutter_inappwebview_plugin if (channelDelegate) { wil::unique_cotaskmem_string title; sender->get_DocumentTitle(&title); - channelDelegate->onTitleChanged(title.is_valid() ? wide_to_ansi(title.get()) : std::optional{}); + channelDelegate->onTitleChanged(title.is_valid() ? wide_to_utf8(title.get()) : std::optional{}); } return S_OK; } @@ -379,7 +405,7 @@ namespace flutter_inappwebview_plugin wil::unique_cotaskmem_string json; if (succeededOrLog(args->get_WebMessageAsJson(&json))) { - auto message = nlohmann::json::parse(wide_to_ansi(json.get())); + auto message = nlohmann::json::parse(wide_to_utf8(json.get())); if (message.is_object() && message.contains("name") && message.at("name").is_string() && message.contains("body") && message.at("body").is_object()) { auto name = message.at("name").get(); @@ -401,7 +427,7 @@ namespace flutter_inappwebview_plugin evaluateJavascript("if (window." + JAVASCRIPT_BRIDGE_NAME + "[" + std::to_string(callHandlerID) + "] != null) { \ window." + JAVASCRIPT_BRIDGE_NAME + "[" + std::to_string(callHandlerID) + "].resolve(" + json + "); \ delete window." + JAVASCRIPT_BRIDGE_NAME + "[" + std::to_string(callHandlerID) + "]; \ - }", nullptr); + }", ContentWorld::page(), nullptr); }; callback->error = [this, callHandlerID](const std::string& error_code, const std::string& error_message, const flutter::EncodableValue* error_details) { @@ -411,7 +437,7 @@ namespace flutter_inappwebview_plugin evaluateJavascript("if (window." + JAVASCRIPT_BRIDGE_NAME + "[" + std::to_string(callHandlerID) + "] != null) { \ window." + JAVASCRIPT_BRIDGE_NAME + "[" + std::to_string(callHandlerID) + "].reject(new Error('" + replace_all_copy(errorMessage, "\'", "\\'") + "')); \ delete window." + JAVASCRIPT_BRIDGE_NAME + "[" + std::to_string(callHandlerID) + "]; \ - }", nullptr); + }", ContentWorld::page(), nullptr); }; channelDelegate->onCallJsHandler(handlerName, handlerArgs, std::move(callback)); } @@ -421,6 +447,55 @@ namespace flutter_inappwebview_plugin return S_OK; } ).Get(), nullptr)); + + wil::com_ptr consoleMessageReceiver; + if (succeededOrLog(webView->GetDevToolsProtocolEventReceiver(L"Runtime.consoleAPICalled", &consoleMessageReceiver))) { + failedLog(consoleMessageReceiver->add_DevToolsProtocolEventReceived( + Callback( + [this]( + ICoreWebView2* sender, + ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) -> HRESULT + { + + if (!channelDelegate) { + return S_OK; + } + + wil::unique_cotaskmem_string json; + if (succeededOrLog(args->get_ParameterObjectAsJson(&json))) { + auto consoleMessageJson = nlohmann::json::parse(wide_to_utf8(json.get())); + + auto level = consoleMessageJson.at("type").get(); + int64_t messageLevel = 1; + if (string_equals(level, "log")) { + messageLevel = 1; + } + else if (string_equals(level, "debug")) { + messageLevel = 0; + } + else if (string_equals(level, "error")) { + messageLevel = 3; + } + else if (string_equals(level, "info")) { + messageLevel = 1; + } + else if (string_equals(level, "warn")) { + messageLevel = 2; + } + + auto consoleArgs = consoleMessageJson.at("args").get>(); + auto message = join(functional_map(consoleArgs, [](const nlohmann::json& json) { return json.contains("value") ? json.at("value").dump() : (json.contains("description") ? json.at("description").dump() : json.dump()); }), std::string{ " " }); + channelDelegate->onConsoleMessage(message, messageLevel); + } + + return S_OK; + }) + .Get(), nullptr)); + } + + if (userContentController) { + userContentController->registerEventHandlers(); + } } void InAppWebView::registerSurfaceEventHandlers() @@ -462,13 +537,13 @@ namespace flutter_inappwebview_plugin return; } - std::wstring url = ansi_to_wide(urlRequest->url.value()); + std::wstring url = utf8_to_wide(urlRequest->url.value()); wil::com_ptr webViewEnv2; wil::com_ptr webView2; if (SUCCEEDED(webViewEnv->QueryInterface(IID_PPV_ARGS(&webViewEnv2))) && SUCCEEDED(webView->QueryInterface(IID_PPV_ARGS(&webView2)))) { wil::com_ptr webResourceRequest; - std::wstring method = urlRequest->method.has_value() ? ansi_to_wide(urlRequest->method.value()) : L"GET"; + std::wstring method = urlRequest->method.has_value() ? utf8_to_wide(urlRequest->method.value()) : L"GET"; wil::com_ptr postDataStream = nullptr; if (urlRequest->body.has_value()) { @@ -491,7 +566,7 @@ namespace flutter_inappwebview_plugin if (urlRequest->headers.has_value()) { auto& headers = urlRequest->headers.value(); for (auto const& [key, val] : headers) { - requestHeaders->SetHeader(ansi_to_wide(key).c_str(), ansi_to_wide(val).c_str()); + requestHeaders->SetHeader(utf8_to_wide(key).c_str(), utf8_to_wide(val).c_str()); } } } @@ -530,7 +605,7 @@ namespace flutter_inappwebview_plugin return; } - failedLog(webView->NavigateToString(ansi_to_wide(data).c_str())); + failedLog(webView->NavigateToString(utf8_to_wide(data).c_str())); } void InAppWebView::reload() const @@ -590,7 +665,7 @@ namespace flutter_inappwebview_plugin if (entryId.has_value()) { auto oldCallShouldOverrideUrlLoading_ = callShouldOverrideUrlLoading_; callShouldOverrideUrlLoading_ = false; - if (failedAndLog(webView->CallDevToolsProtocolMethod(L"Page.navigateToHistoryEntry", ansi_to_wide("{\"entryId\": " + std::to_string(entryId.value()) + "}").c_str(), Callback( + if (failedAndLog(webView->CallDevToolsProtocolMethod(L"Page.navigateToHistoryEntry", utf8_to_wide("{\"entryId\": " + std::to_string(entryId.value()) + "}").c_str(), Callback( [this](HRESULT errorCode, LPCWSTR returnObjectAsJson) { failedLog(errorCode); @@ -653,7 +728,7 @@ namespace flutter_inappwebview_plugin } if (errorCode == S_OK) { - auto historyJson = nlohmann::json::parse(wide_to_ansi(returnObjectAsJson)); + auto historyJson = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson)); int64_t currentIndex = historyJson.at("currentIndex").is_number_unsigned() ? historyJson.at("currentIndex").get() : 0; std::vector entries = historyJson.at("entries").is_array() ? historyJson.at("entries").get>() : std::vector{}; @@ -686,22 +761,44 @@ namespace flutter_inappwebview_plugin ).Get())); } - void InAppWebView::evaluateJavascript(const std::string& source, const std::function completionHanlder) const + void InAppWebView::evaluateJavascript(const std::string& source, const std::shared_ptr contentWorld, const std::function completionHandler) const { - failedLog(webView->ExecuteScript(ansi_to_wide(source).c_str(), - Callback( - [completionHanlder = std::move(completionHanlder)](HRESULT error, PCWSTR result) -> HRESULT - { - if (error != S_OK) { - debugLog(error); - } + if (!webView || !userContentController) { + if (completionHandler) { + completionHandler("null"); + } + return; + } - if (completionHanlder) { - completionHanlder(wide_to_ansi(result)); - } + userContentController->createContentWorld(contentWorld, + [=](const int& contextId) + { + nlohmann::json parameters = { + {"expression", source} + }; - return S_OK; - }).Get())); + if (contextId >= 0) { + parameters["contextId"] = contextId; + } + + auto hr = webView->CallDevToolsProtocolMethod(L"Runtime.evaluate", utf8_to_wide(parameters.dump()).c_str(), Callback( + [this, completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + std::string result = "null"; + if (succeededOrLog(errorCode)) { + result = wide_to_utf8(returnObjectAsJson); + } + if (completionHandler) { + completionHandler(result); + } + return S_OK; + } + ).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler("null"); + } + }); } void InAppWebView::addUserScript(const std::shared_ptr userScript) const @@ -964,10 +1061,10 @@ namespace flutter_inappwebview_plugin return handled; \ })(" + std::to_string(horizontal) + ", " + std::to_string(offset) + ", " + std::to_string(lastCursorPos_.x) + ", " + std::to_string(lastCursorPos_.y) + ");"; - webView->ExecuteScript(ansi_to_wide(workaroundScrollJS).c_str(), Callback( + webView->ExecuteScript(utf8_to_wide(workaroundScrollJS).c_str(), Callback( [this, horizontal, offset](HRESULT error, PCWSTR result) -> HRESULT { - if (webViewCompositionController && (error != S_OK || wide_to_ansi(result).compare("false") == 0)) { + if (webViewCompositionController && (error != S_OK || wide_to_utf8(result).compare("false") == 0)) { // try to use native mouse wheel handler POINT point; @@ -1057,7 +1154,7 @@ namespace flutter_inappwebview_plugin if (webViewController) { webViewController->Close(); } - navigationActions.clear(); + navigationActions_.clear(); inAppBrowser = nullptr; plugin = nullptr; } diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index ec87f6fa..c1645cbb 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -9,6 +9,7 @@ #include #include "../flutter_inappwebview_windows_plugin.h" +#include "../types/content_world.h" #include "../types/navigation_action.h" #include "../types/url_request.h" #include "../types/web_history.h" @@ -87,7 +88,6 @@ namespace flutter_inappwebview_plugin wil::com_ptr webViewCompositionController; wil::com_ptr webView; std::unique_ptr channelDelegate; - std::map> navigationActions = {}; const std::shared_ptr settings; InAppBrowser* inAppBrowser = nullptr; std::unique_ptr userContentController; @@ -148,13 +148,18 @@ namespace flutter_inappwebview_plugin return isLoading_; } void stopLoading() const; - void evaluateJavascript(const std::string& source, const std::function completionHanlder) const; + void evaluateJavascript(const std::string& source, const std::shared_ptr contentWorld, const std::function completionHandler) const; void getCopyBackForwardList(const std::function)> completionHandler) const; void addUserScript(const std::shared_ptr userScript) const; void removeUserScript(const int64_t index, const std::shared_ptr userScript) const; void removeUserScriptsByGroupName(const std::string& groupName) const; void removeAllUserScripts() const; + std::string pageFrameId() const + { + return pageFrameId_; + } + static bool isSslError(const COREWEBVIEW2_WEB_ERROR_STATUS& webErrorStatus); private: // custom_platform_view @@ -166,11 +171,13 @@ namespace flutter_inappwebview_plugin VirtualKeyState virtualKeys_; bool callShouldOverrideUrlLoading_ = true; + std::map> navigationActions_ = {}; std::shared_ptr lastNavigationAction_; bool isLoading_ = false; + std::string pageFrameId_; - void InAppWebView::registerEventHandlers(); - void InAppWebView::registerSurfaceEventHandlers(); + void registerEventHandlers(); + void registerSurfaceEventHandlers(); }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.cpp b/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.cpp index 9f51ed82..9cbe2761 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.cpp @@ -1,3 +1,4 @@ +#include #include #include "../utils/log.h" @@ -14,6 +15,67 @@ namespace flutter_inappwebview_plugin : webView_(webView) {} + void UserContentController::registerEventHandlers() + { + if (!webView_ || !(webView_->webView)) { + return; + } + + wil::com_ptr executionContextCreated; + if (succeededOrLog(webView_->webView->GetDevToolsProtocolEventReceiver(L"Runtime.executionContextCreated", &executionContextCreated))) { + auto hr = executionContextCreated->add_DevToolsProtocolEventReceived( + Callback( + [this]( + ICoreWebView2* sender, + ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) -> HRESULT + { + wil::unique_cotaskmem_string json; + if (succeededOrLog(args->get_ParameterObjectAsJson(&json))) { + nlohmann::json context = nlohmann::json::parse(wide_to_utf8(json.get()))["context"]; + auto id = context["id"].get(); + auto name = context["name"].get(); + nlohmann::json auxData = context["auxData"]; + auto isDefault = auxData["isDefault"].get(); + auto frameId = auxData["frameId"].get(); + if (string_equals(webView_->pageFrameId(), frameId)) { + if (isDefault) { + contentWorlds_.insert_or_assign(ContentWorld::page()->name, id); + } + else { + contentWorlds_.insert_or_assign(name, id); + addPluginScriptsIfRequired(std::make_shared(name)); + } + } + } + + return S_OK; + }) + .Get(), nullptr); + + failedLog(hr); + } + + /* + wil::com_ptr executionContextDestroyed; + if (succeededOrLog(webView_->webView->GetDevToolsProtocolEventReceiver(L"Runtime.executionContextDestroyed ", &executionContextDestroyed))) { + failedLog(executionContextDestroyed->add_DevToolsProtocolEventReceived( + Callback( + [this]( + ICoreWebView2* sender, + ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) -> HRESULT + { + wil::unique_cotaskmem_string json; + if (succeededOrLog(args->get_ParameterObjectAsJson(&json))) { + debugLog("executionContextDestroyed: " + wide_to_utf8(json.get())); + } + + return S_OK; + }) + .Get(), nullptr)); + } + */ + } + std::vector> UserContentController::getUserOnlyScriptsAt(const UserScriptInjectionTime& injectionTime) const { return userOnlyScripts_.at(injectionTime); @@ -25,18 +87,8 @@ namespace flutter_inappwebview_plugin return; } - if (userScript->injectionTime == UserScriptInjectionTime::atDocumentStart && webView_) { - failedLog(webView_->webView->AddScriptToExecuteOnDocumentCreated(ansi_to_wide(userScript->source).c_str(), - Callback( - [userScript](HRESULT error, PCWSTR id) -> HRESULT - { - if (succeededOrLog(error)) { - userScript->id = id; - } - return S_OK; - }).Get())); - } - + addPluginScriptsIfRequired(userScript->contentWorld); + addScriptToWebView(userScript, nullptr); userOnlyScripts_.at(userScript->injectionTime).push_back(std::move(userScript)); } @@ -54,7 +106,7 @@ namespace flutter_inappwebview_plugin } if (webView_) { - failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(userScript->id.c_str())); + removeScriptFromWebView(userScript, nullptr); } vector_remove_erase(userOnlyScripts_.at(userScript->injectionTime), std::move(userScript)); @@ -70,9 +122,7 @@ namespace flutter_inappwebview_plugin auto& userScript = vec.at(index); if (userScript) { - if (webView_) { - failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(userScript->id.c_str())); - } + removeScriptFromWebView(userScript, nullptr); vec.erase(vec.begin() + index); } } @@ -84,10 +134,10 @@ namespace flutter_inappwebview_plugin if (webView_) { for (auto& userScript : userScriptsAtStart) { - failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(userScript->id.c_str())); + removeScriptFromWebView(userScript, nullptr); } for (auto& userScript : userScriptsAtEnd) { - failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(userScript->id.c_str())); + removeScriptFromWebView(userScript, nullptr); } } @@ -139,18 +189,7 @@ namespace flutter_inappwebview_plugin return; } - if (pluginScript->injectionTime == UserScriptInjectionTime::atDocumentStart && webView_) { - failedLog(webView_->webView->AddScriptToExecuteOnDocumentCreated(ansi_to_wide(pluginScript->source).c_str(), - Callback( - [pluginScript](HRESULT error, PCWSTR id) -> HRESULT - { - if (succeededOrLog(error)) { - pluginScript->id = id; - } - return S_OK; - }).Get())); - } - + addScriptToWebView(pluginScript, nullptr); pluginScripts_.at(pluginScript->injectionTime).push_back(std::move(pluginScript)); } @@ -168,7 +207,7 @@ namespace flutter_inappwebview_plugin } if (webView_) { - failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(pluginScript->id.c_str())); + removeScriptFromWebView(pluginScript, nullptr); } vector_remove_erase(pluginScripts_.at(pluginScript->injectionTime), std::move(pluginScript)); @@ -181,10 +220,10 @@ namespace flutter_inappwebview_plugin if (webView_) { for (auto& pluginScript : pluginScriptsAtStart) { - failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(pluginScript->id.c_str())); + removeScriptFromWebView(pluginScript, nullptr); } for (auto& pluginScript : pluginScriptsAtEnd) { - failedLog(webView_->webView->RemoveScriptToExecuteOnDocumentCreated(pluginScript->id.c_str())); + removeScriptFromWebView(pluginScript, nullptr); } } @@ -198,7 +237,18 @@ namespace flutter_inappwebview_plugin return false; } - return vector_contains(pluginScripts_.at(pluginScript->injectionTime), std::move(pluginScript)); + auto injectionTime = pluginScript->injectionTime; + return vector_contains(pluginScripts_.at(injectionTime), std::move(pluginScript)); + } + + + bool UserContentController::containsPluginScript(std::shared_ptr pluginScript, const std::shared_ptr contentWorld) const + { + if (!pluginScript || !map_contains(pluginScriptsInContentWorlds_, contentWorld->name)) { + return false; + } + + return vector_contains(pluginScriptsInContentWorlds_.at(contentWorld->name), std::move(pluginScript)); } bool UserContentController::containsPluginScriptByGroupName(const std::string& groupName) const @@ -225,31 +275,150 @@ namespace flutter_inappwebview_plugin } } - std::string UserContentController::generatePluginScriptsCodeAt(const UserScriptInjectionTime& injectionTime) const + + std::vector> UserContentController::getPluginScriptsRequiredInAllContentWorlds() const { - std::string code; - std::vector> pluginScripts = pluginScripts_.at(injectionTime); - for (auto& pluginScript : pluginScripts) { - code += ";" + pluginScript->source; + std::vector> res; + + std::vector> pluginScriptsAtStart = pluginScripts_.at(UserScriptInjectionTime::atDocumentStart); + std::vector> pluginScriptsAtEnd = pluginScripts_.at(UserScriptInjectionTime::atDocumentEnd); + + for (auto& pluginScript : pluginScriptsAtStart) { + if (!pluginScript->contentWorld && pluginScript->isRequiredInAllContentWorlds()) { + res.push_back(pluginScript); + } } - return code; + + for (auto& pluginScript : pluginScriptsAtEnd) { + if (!pluginScript->contentWorld && pluginScript->isRequiredInAllContentWorlds()) { + res.push_back(pluginScript); + } + } + + return res; } - std::string UserContentController::generateUserOnlyScriptsCodeAt(const UserScriptInjectionTime& injectionTime) const + void UserContentController::createContentWorld(const std::shared_ptr contentWorld, const std::function completionHandler) { - std::string code; - std::vector> userScripts = userOnlyScripts_.at(injectionTime); - for (auto& userScript : userScripts) { - code += ";" + userScript->source; + if (!webView_ || !(webView_->webView) || ContentWorld::isPage(contentWorld)) { + if (completionHandler) { + completionHandler(-1); + } + return; + } + + auto& worldName = contentWorld->name; + if (!map_contains(contentWorlds_, worldName)) { + nlohmann::json parameters = { + {"frameId", webView_->pageFrameId()}, + {"worldName", worldName} + }; + auto hr = webView_->webView->CallDevToolsProtocolMethod(L"Page.createIsolatedWorld", utf8_to_wide(parameters.dump()).c_str(), Callback( + [this, completionHandler, worldName](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + if (succeededOrLog(errorCode) && completionHandler) { + auto id = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson))["executionContextId"].get(); + addPluginScriptsIfRequired(std::make_shared(worldName)); + completionHandler(id); + } + return S_OK; + } + ).Get()); + if (failedAndLog(hr) && completionHandler) { + completionHandler(-1); + } + } + else if (completionHandler) { + completionHandler(contentWorlds_.at(worldName)); } - return code; } - std::string UserContentController::generateWrappedCodeForDocumentEnd() const + void UserContentController::addScriptToWebView(std::shared_ptr userScript, const std::function completionHandler) const { - std::string code = generatePluginScriptsCodeAt(UserScriptInjectionTime::atDocumentEnd); - code += generateUserOnlyScriptsCodeAt(UserScriptInjectionTime::atDocumentEnd); - return replace_all_copy(USER_SCRIPTS_AT_DOCUMENT_END_WRAPPER_JS_SOURCE, VAR_PLACEHOLDER_VALUE, code); + if (!webView_ || !(webView_->webView)) { + if (completionHandler) { + completionHandler(userScript->id); + } + } + + std::string source = userScript->source; + if (userScript->injectionTime == UserScriptInjectionTime::atDocumentEnd) { + source = replace_all_copy(USER_SCRIPTS_AT_DOCUMENT_END_WRAPPER_JS_SOURCE, VAR_PLACEHOLDER_VALUE, source); + std::ostringstream address; + address << std::addressof(userScript); + replace_all(source, VAR_PLACEHOLDER_MEMORY_ADDRESS_VALUE, address.str()); + } + + nlohmann::json parameters = { + {"source", source} + }; + + if (userScript->contentWorld && !ContentWorld::isPage(userScript->contentWorld)) { + parameters["worldName"] = userScript->contentWorld->name; + } + + auto hr = webView_->webView->CallDevToolsProtocolMethod(L"Page.addScriptToEvaluateOnNewDocument", utf8_to_wide(parameters.dump()).c_str(), Callback( + [userScript, completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + if (succeededOrLog(errorCode)) { + nlohmann::json json = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson)); + userScript->id = json["identifier"].get(); + } + if (completionHandler) { + completionHandler(userScript->id); + } + return S_OK; + } + ).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler(userScript->id); + } + } + + void UserContentController::removeScriptFromWebView(std::shared_ptr userScript, const std::function completionHandler) const + { + if (!webView_ || !(webView_->webView)) { + if (completionHandler) { + completionHandler(); + } + return; + } + + nlohmann::json parameters = { + {"identifier", userScript->id} + }; + + auto hr = webView_->webView->CallDevToolsProtocolMethod(L"Page.removeScriptToEvaluateOnNewDocument", utf8_to_wide(parameters.dump()).c_str(), Callback( + [userScript, completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + failedLog(errorCode); + if (completionHandler) { + completionHandler(); + } + return S_OK; + } + ).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler(); + } + } + + void UserContentController::addPluginScriptsIfRequired(const std::shared_ptr contentWorld) + { + if (contentWorld && !ContentWorld::isPage(contentWorld)) { + std::vector> pluginScriptsRequiredInAllContentWorlds = getPluginScriptsRequiredInAllContentWorlds(); + for (auto& pluginScript : pluginScriptsRequiredInAllContentWorlds) { + if (!containsPluginScript(pluginScript, contentWorld)) { + if (!map_contains(pluginScriptsInContentWorlds_, contentWorld->name)) { + pluginScriptsInContentWorlds_.insert({ contentWorld->name, {} }); + } + pluginScriptsInContentWorlds_.at(contentWorld->name).push_back(pluginScript); + addPluginScript(pluginScript->copyAndSet(contentWorld)); + } + } + } } UserContentController::~UserContentController() @@ -257,6 +426,8 @@ namespace flutter_inappwebview_plugin debugLog("dealloc UserContentController"); removeAllUserOnlyScripts(); removeAllPluginScripts(); + contentWorlds_.clear(); + pluginScriptsInContentWorlds_.clear(); webView_ = nullptr; } } diff --git a/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.h b/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.h index c051544a..a6592913 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/user_content_controller.h @@ -1,11 +1,13 @@ #ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_USER_CONTENT_CONTROLLER_H_ #define FLUTTER_INAPPWEBVIEW_PLUGIN_USER_CONTENT_CONTROLLER_H_ +#include #include #include #include "../plugin_scripts_js/javascript_bridge_js.h" #include "../plugin_scripts_js/plugin_scripts_util.h" +#include "../types/content_world.h" #include "../types/plugin_script.h" #include "../types/user_script.h" @@ -13,11 +15,12 @@ namespace flutter_inappwebview_plugin { class InAppWebView; - const std::string USER_SCRIPTS_AT_DOCUMENT_END_WRAPPER_JS_SOURCE = - "if (window." + JAVASCRIPT_BRIDGE_NAME + " != null && (window." + JAVASCRIPT_BRIDGE_NAME + "._userScriptsAtDocumentEndLoaded == null || !window." + JAVASCRIPT_BRIDGE_NAME + "._userScriptsAtDocumentEndLoaded)) { \ - window." + JAVASCRIPT_BRIDGE_NAME + "._userScriptsAtDocumentEndLoaded = true; \ + const std::string USER_SCRIPTS_AT_DOCUMENT_END_WRAPPER_JS_SOURCE = "window.addEventListener('load', () => { \ + if (window." + JAVASCRIPT_BRIDGE_NAME + " != null && (window." + JAVASCRIPT_BRIDGE_NAME + "._userScript" + VAR_PLACEHOLDER_MEMORY_ADDRESS_VALUE + "AtDocumentEndLoaded == null || !window." + JAVASCRIPT_BRIDGE_NAME + "._userScript" + VAR_PLACEHOLDER_MEMORY_ADDRESS_VALUE + "AtDocumentEndLoaded)) { \ + window." + JAVASCRIPT_BRIDGE_NAME + "._userScript" + VAR_PLACEHOLDER_MEMORY_ADDRESS_VALUE + "AtDocumentEndLoaded = true; \ " + VAR_PLACEHOLDER_VALUE + " \ - }"; + } \ + });"; class UserContentController { @@ -34,20 +37,28 @@ namespace flutter_inappwebview_plugin bool containsUserOnlyScript(std::shared_ptr userScript) const; bool containsUserOnlyScriptByGroupName(const std::string& groupName) const; void removeUserOnlyScriptsByGroupName(const std::string& groupName); + std::vector> getPluginScriptsAt(const UserScriptInjectionTime& injectionTime) const; void addPluginScript(std::shared_ptr pluginScript); void addPluginScripts(std::vector> pluginScripts); void removePluginScript(std::shared_ptr pluginScript); void removeAllPluginScripts(); bool containsPluginScript(std::shared_ptr pluginScript) const; + bool containsPluginScript(std::shared_ptr pluginScript, const std::shared_ptr contentWorld) const; bool containsPluginScriptByGroupName(const std::string& groupName) const; void removePluginScriptsByGroupName(const std::string& groupName); - std::string generatePluginScriptsCodeAt(const UserScriptInjectionTime& injectionTime) const; - std::string generateUserOnlyScriptsCodeAt(const UserScriptInjectionTime& injectionTime) const; - std::string generateWrappedCodeForDocumentEnd() const; + std::vector> getPluginScriptsRequiredInAllContentWorlds() const; + + void registerEventHandlers(); + void createContentWorld(const std::shared_ptr contentWorld, const std::function completionHandler); private: InAppWebView* webView_; + // used to track Content World names -> Execution Context ID + std::map contentWorlds_; + // used only to track plugin script to inject inside new Content Worlds + std::map>> pluginScriptsInContentWorlds_; + std::map>> pluginScripts_ = { {UserScriptInjectionTime::atDocumentStart, {}}, {UserScriptInjectionTime::atDocumentEnd, {}} @@ -57,6 +68,11 @@ namespace flutter_inappwebview_plugin {UserScriptInjectionTime::atDocumentStart, {}}, {UserScriptInjectionTime::atDocumentEnd, {}} }; + + void addScriptToWebView(std::shared_ptr userScript, const std::function completionHandler) const; + void removeScriptFromWebView(std::shared_ptr userScript, const std::function completionHandler) const; + + void addPluginScriptsIfRequired(const std::shared_ptr contentWorld); }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_USER_CONTENT_CONTROLLER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index e1884e1f..f7ace600 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -1,5 +1,6 @@ #include "../in_app_browser/in_app_browser.h" #include "../types/base_callback_result.h" +#include "../types/content_world.h" #include "../utils/flutter.h" #include "../utils/log.h" #include "../utils/strconv.h" @@ -112,7 +113,9 @@ namespace flutter_inappwebview_plugin auto result_ = std::shared_ptr>(std::move(result)); auto source = get_fl_map_value(arguments, "source"); - webView->evaluateJavascript(source, [result_ = std::move(result_)](const std::string& value) + auto contentWorldMap = get_optional_fl_map_value(arguments, "contentWorld"); + std::shared_ptr contentWorld = contentWorldMap.has_value() ? std::make_shared(contentWorldMap.value()) : ContentWorld::page(); + webView->evaluateJavascript(source, std::move(contentWorld), [result_ = std::move(result_)](const std::string& value) { result_->Success(value); }); @@ -130,7 +133,7 @@ namespace flutter_inappwebview_plugin result->Success(true); } else if (string_equals(methodName, "removeUserScript")) { - auto index = get_fl_map_value(arguments, "index"); + auto index = get_fl_map_value(arguments, "index"); auto userScript = std::make_unique(get_fl_map_value(arguments, "userScript")); webView->removeUserScript(index, std::move(userScript)); result->Success(true); @@ -264,6 +267,19 @@ namespace flutter_inappwebview_plugin channel->InvokeMethod("onCallJsHandler", std::move(arguments), std::move(callback)); } + void WebViewChannelDelegate::onConsoleMessage(const std::string& message, const int64_t& messageLevel) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap{ + {"message", message}, + {"messageLevel", messageLevel} + }); + channel->InvokeMethod("onConsoleMessage", std::move(arguments)); + } + WebViewChannelDelegate::~WebViewChannelDelegate() { debugLog("dealloc WebViewChannelDelegate"); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h index f3141b4e..7774b375 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h @@ -50,6 +50,7 @@ namespace flutter_inappwebview_plugin void onTitleChanged(const std::optional& title) const; void onUpdateVisitedHistory(const std::optional& url, const std::optional& isReload) const; void onCallJsHandler(const std::string& handlerName, const std::string& args, std::unique_ptr callback) const; + void onConsoleMessage(const std::string& message, const int64_t& messageLevel) const; }; } diff --git a/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.cpp b/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.cpp index a87baaf9..dcb743bf 100644 --- a/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.cpp +++ b/flutter_inappwebview_windows/windows/plugin_scripts_js/javascript_bridge_js.cpp @@ -11,7 +11,9 @@ namespace flutter_inappwebview_plugin JAVASCRIPT_BRIDGE_JS_PLUGIN_SCRIPT_GROUP_NAME, JAVASCRIPT_BRIDGE_JS_SOURCE, UserScriptInjectionTime::atDocumentStart, - allowedOriginRules + allowedOriginRules, + nullptr, + true ); } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/plugin_scripts_js/plugin_scripts_util.h b/flutter_inappwebview_windows/windows/plugin_scripts_js/plugin_scripts_util.h index 0c29cde5..1492989e 100644 --- a/flutter_inappwebview_windows/windows/plugin_scripts_js/plugin_scripts_util.h +++ b/flutter_inappwebview_windows/windows/plugin_scripts_js/plugin_scripts_util.h @@ -5,7 +5,8 @@ namespace flutter_inappwebview_plugin { - const std::string VAR_PLACEHOLDER_VALUE = "$IN_APP_WEBVIEW_PLACEHOLDER_VALUE"; + const std::string VAR_PLACEHOLDER_VALUE = "$IN_APP_WEBVIEW_PLACEHOLDER_VALUE"; + const std::string VAR_PLACEHOLDER_MEMORY_ADDRESS_VALUE = "$IN_APP_WEBVIEW_PLACEHOLDER_MEMORY_ADDRESS_VALUE"; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_SCRIPTS_UTIL_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/content_world.cpp b/flutter_inappwebview_windows/windows/types/content_world.cpp new file mode 100644 index 00000000..a95e773c --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/content_world.cpp @@ -0,0 +1,60 @@ +#include "content_world.h" + +namespace flutter_inappwebview_plugin +{ + namespace { + const std::shared_ptr ContentWorldPage = std::make_shared("page"); + const std::shared_ptr ContentWorldDefaultClient = std::make_shared("defaultClient"); + } + + ContentWorld::ContentWorld(const std::string& name) + : name(name) + {} + + ContentWorld::ContentWorld(const flutter::EncodableMap& map) + : name(get_fl_map_value(map, "name")) + {} + + bool ContentWorld::isSame(const ContentWorld& contentWorld) const + { + return name == contentWorld.name; + } + + bool ContentWorld::isSame(const std::shared_ptr contentWorld) const + { + return contentWorld && name == contentWorld->name; + } + + const std::shared_ptr ContentWorld::page() + { + return ContentWorldPage; + } + + const std::shared_ptr ContentWorld::defaultClient() + { + return ContentWorldDefaultClient; + } + + bool ContentWorld::isPage(const ContentWorld& contentWorld) + { + return contentWorld.isSame(*ContentWorld::page()); + } + + bool ContentWorld::isPage(const std::shared_ptr contentWorld) + { + return ContentWorld::page()->isSame(contentWorld); + } + + bool ContentWorld::isDefaultClient(const ContentWorld& contentWorld) + { + return contentWorld.isSame(*ContentWorld::defaultClient()); + } + + bool ContentWorld::isDefaultClient(const std::shared_ptr contentWorld) + { + return ContentWorld::defaultClient()->isSame(contentWorld); + } + + ContentWorld::~ContentWorld() + {} +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/content_world.h b/flutter_inappwebview_windows/windows/types/content_world.h new file mode 100644 index 00000000..46622289 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/content_world.h @@ -0,0 +1,33 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_CONTENT_WORLD_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_CONTENT_WORLD_H_ + +#include +#include + +#include "../utils/flutter.h" + +namespace flutter_inappwebview_plugin +{ + class ContentWorld + { + public: + + const std::string name; + + ContentWorld(const std::string& name); + ContentWorld(const flutter::EncodableMap& map); + ~ContentWorld(); + + bool isSame(const ContentWorld& contentWorld) const; + bool isSame(const std::shared_ptr contentWorld) const; + + const static std::shared_ptr page(); + const static std::shared_ptr defaultClient(); + + static bool isPage(const ContentWorld& contentWorld); + static bool isPage(const std::shared_ptr contentWorld); + static bool isDefaultClient(const ContentWorld& contentWorld); + static bool isDefaultClient(const std::shared_ptr contentWorld); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_CONTENT_WORLD_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/plugin_script.cpp b/flutter_inappwebview_windows/windows/types/plugin_script.cpp index d9f9c73a..cc37467a 100644 --- a/flutter_inappwebview_windows/windows/types/plugin_script.cpp +++ b/flutter_inappwebview_windows/windows/types/plugin_script.cpp @@ -6,9 +6,25 @@ namespace flutter_inappwebview_plugin const std::optional& groupName, const std::string& source, const UserScriptInjectionTime& injectionTime, - const std::vector& allowedOriginRules - ) : UserScript(groupName, source, injectionTime, allowedOriginRules) + const std::vector& allowedOriginRules, + std::shared_ptr contentWorld, + const bool& requiredInAllContentWorlds + ) : UserScript(groupName, source, injectionTime, allowedOriginRules, std::move(contentWorld)), + requiredInAllContentWorlds_(requiredInAllContentWorlds) {} + + std::shared_ptr PluginScript::copyAndSet(const std::shared_ptr cw) const + { + return std::make_unique( + this->groupName, + this->source, + this->injectionTime, + this->allowedOriginRules, + cw, + this->requiredInAllContentWorlds_ + ); + } + PluginScript::~PluginScript() {} } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/plugin_script.h b/flutter_inappwebview_windows/windows/types/plugin_script.h index 1e633c88..e2fae03f 100644 --- a/flutter_inappwebview_windows/windows/types/plugin_script.h +++ b/flutter_inappwebview_windows/windows/types/plugin_script.h @@ -13,9 +13,21 @@ namespace flutter_inappwebview_plugin const std::optional& groupName, const std::string& source, const UserScriptInjectionTime& injectionTime, - const std::vector& allowedOriginRules + const std::vector& allowedOriginRules, + std::shared_ptr contentWorld, + const bool& requiredInAllContentWorlds ); ~PluginScript(); + + bool isRequiredInAllContentWorlds() const + { + return requiredInAllContentWorlds_; + } + + std::shared_ptr copyAndSet(const std::shared_ptr cw) const; + + private: + bool requiredInAllContentWorlds_; }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_SCRIPT_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/user_script.cpp b/flutter_inappwebview_windows/windows/types/user_script.cpp index 4befa0f4..6a783fe0 100644 --- a/flutter_inappwebview_windows/windows/types/user_script.cpp +++ b/flutter_inappwebview_windows/windows/types/user_script.cpp @@ -7,15 +7,17 @@ namespace flutter_inappwebview_plugin const std::optional& groupName, const std::string& source, const UserScriptInjectionTime& injectionTime, - const std::vector& allowedOriginRules - ) : groupName(groupName), source(source), injectionTime(injectionTime), allowedOriginRules(allowedOriginRules) + const std::vector& allowedOriginRules, + std::shared_ptr contentWorld + ) : groupName(groupName), source(source), injectionTime(injectionTime), allowedOriginRules(allowedOriginRules), contentWorld(std::move(contentWorld)) {} UserScript::UserScript(const flutter::EncodableMap& map) : groupName(get_optional_fl_map_value(map, "groupName")), source(get_fl_map_value(map, "source")), injectionTime(static_cast(get_fl_map_value(map, "injectionTime"))), - allowedOriginRules(functional_map(get_fl_map_value(map, "allowedOriginRules"), [](const flutter::EncodableValue& m) { return std::get(m); })) + allowedOriginRules(functional_map(get_fl_map_value(map, "allowedOriginRules"), [](const flutter::EncodableValue& m) { return std::get(m); })), + contentWorld(std::make_shared(get_fl_map_value(map, "contentWorld"))) {} UserScript::~UserScript() diff --git a/flutter_inappwebview_windows/windows/types/user_script.h b/flutter_inappwebview_windows/windows/types/user_script.h index 4c742cb8..473661f0 100644 --- a/flutter_inappwebview_windows/windows/types/user_script.h +++ b/flutter_inappwebview_windows/windows/types/user_script.h @@ -7,6 +7,7 @@ #include #include "../utils/flutter.h" +#include "content_world.h" namespace flutter_inappwebview_plugin { @@ -18,17 +19,19 @@ namespace flutter_inappwebview_plugin class UserScript { public: - std::wstring id; + std::string id; const std::optional groupName; const std::string source; const UserScriptInjectionTime injectionTime; const std::vector allowedOriginRules; + const std::shared_ptr contentWorld; UserScript( const std::optional& groupName, const std::string& source, const UserScriptInjectionTime& injectionTime, - const std::vector& allowedOriginRules + const std::vector& allowedOriginRules, + std::shared_ptr contentWorld ); UserScript(const flutter::EncodableMap& map); ~UserScript(); diff --git a/flutter_inappwebview_windows/windows/utils/log.h b/flutter_inappwebview_windows/windows/utils/log.h index e87cdd0c..d1eb3a36 100644 --- a/flutter_inappwebview_windows/windows/utils/log.h +++ b/flutter_inappwebview_windows/windows/utils/log.h @@ -7,82 +7,96 @@ #include #include "strconv.h" +#include "string.h" namespace flutter_inappwebview_plugin { template - static inline void debugLog(const std::basic_string& msg, const bool& isError = false) + static inline void debugLog(const std::basic_string& msg, const bool& isError = false, const std::string& filename = "", const int& line = 0) { #ifndef NDEBUG + std::basic_string debugMsg = msg; + if (!filename.empty() && line > 0) { + auto filenameSplit = split(filename, std::string{ "\\flutter_inappwebview_windows\\" }); + std::string reduceFilenamePath = filenameSplit.size() > 0 ? "flutter_inappwebview_windows\\" + filenameSplit.back() : filename; + debugMsg = reduceFilenamePath + "(" + std::to_string(line) + "): " + debugMsg; + } if (isError) { - std::cerr << msg << std::endl; + std::cerr << debugMsg << std::endl; } else { - std::cout << msg << std::endl; + std::cout << debugMsg << std::endl; } - OutputDebugString(ansi_to_wide(msg + "\n").c_str()); + OutputDebugString(utf8_to_wide("\n" + debugMsg + "\n").c_str()); #endif } - static inline void debugLog(const char* msg, const bool& isError = false) + static inline void debugLog(const char* msg, const bool& isError = false, const std::string& filename = "", const int& line = 0) { - debugLog(std::string(msg), isError); + debugLog(std::string(msg), isError, filename, line); } - static inline void debugLog(const std::wstring& msg, const bool& isError = false) + static inline void debugLog(const std::wstring& msg, const bool& isError = false, const std::string& filename = "", const int& line = 0) { - debugLog(wide_to_ansi(msg), isError); + debugLog(wide_to_utf8(msg), isError, filename, line); } - static inline void debugLog(const bool& value, const bool& isError = false) + static inline void debugLog(const bool& value, const bool& isError = false, const std::string& filename = "", const int& line = 0) { - debugLog(value ? "true" : "false", isError); + debugLog(value ? "true" : "false", isError, filename, line); } template< typename T, typename = typename std::enable_if::value, T>::type > - static inline void debugLog(const T& value, const bool& isError = false) + static inline void debugLog(const T& value, const bool& isError = false, const std::string& filename = "", const int& line = 0) { debugLog(std::to_string(value), isError); } static inline std::string getHRMessage(const HRESULT& error) { - return wide_to_ansi(_com_error(error).ErrorMessage()); + return wide_to_utf8(_com_error(error).ErrorMessage()); } - static inline void debugLog(const HRESULT& hr) + static inline void debugLog(const HRESULT& hr, const std::string& filename = "", const int& line = 0) { auto isError = hr != S_OK; - debugLog((isError ? "Error: " : "Message: ") + getHRMessage(hr), isError); + debugLog((isError ? "Error: " : "Message: ") + getHRMessage(hr), isError, filename, line); } - static inline bool succeededOrLog(const HRESULT& hr) + static inline bool succeededOrLog(const HRESULT& hr, const std::string& filename = "", const int& line = 0) { if (SUCCEEDED(hr)) { return true; } - debugLog(hr); + debugLog(hr, filename, line); return false; } - static inline bool failedAndLog(const HRESULT& hr) + static inline bool failedAndLog(const HRESULT& hr, const std::string& filename = "", const int& line = 0) { if (FAILED(hr)) { - debugLog(hr); + debugLog(hr, filename, line); return true; } return false; } - static inline void failedLog(const HRESULT& hr) + static inline void failedLog(const HRESULT& hr, const std::string& filename = "", const int& line = 0) { if (FAILED(hr)) { - debugLog(hr); + debugLog(hr, filename, line); } } } +#ifndef NDEBUG +#define debugLog(value) debugLog(value, false, __FILE__, __LINE__) +#define succeededOrLog(value) succeededOrLog(value, __FILE__, __LINE__) +#define failedAndLog(value) failedAndLog(value, __FILE__, __LINE__) +#define failedLog(value) failedLog(value, __FILE__, __LINE__) +#endif + #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_LOG_UTIL_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/string.h b/flutter_inappwebview_windows/windows/utils/string.h index 9fcf7e7d..cce03750 100644 --- a/flutter_inappwebview_windows/windows/utils/string.h +++ b/flutter_inappwebview_windows/windows/utils/string.h @@ -1,8 +1,10 @@ #ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_ #define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_ +#include #include #include +#include #include "strconv.h" @@ -42,12 +44,12 @@ namespace flutter_inappwebview_plugin static inline bool string_equals(const std::string& s1, const std::wstring& s2) { - return string_equals(s1, wide_to_ansi(s2)); + return string_equals(s1, wide_to_utf8(s2)); } static inline bool string_equals(const std::wstring& s1, const std::string& s2) { - return string_equals(wide_to_ansi(s1), s2); + return string_equals(wide_to_utf8(s1), s2); } template @@ -107,6 +109,33 @@ namespace flutter_inappwebview_plugin return newString; } + + template + static inline std::basic_string join(const std::vector>& vec, const std::basic_string& delim) + { + return vec.empty() ? std::basic_string{ "" } : /* leave early if there are no items in the list */ + std::accumulate( /* otherwise, accumulate */ + ++vec.begin(), vec.end(), /* the range 2nd to after-last */ + *vec.begin(), /* and start accumulating with the first item */ + [delim](auto& a, auto& b) { return a + delim + b; }); + } + + template + static inline std::vector> split(const std::basic_string& s, std::basic_string delimiter) + { + size_t pos_start = 0, pos_end, delim_len = delimiter.length(); + std::basic_string token; + std::vector> res; + + while ((pos_end = s.find(delimiter, pos_start)) != std::basic_string::npos) { + token = s.substr(pos_start, pos_end - pos_start); + pos_start = pos_end + delim_len; + res.push_back(token); + } + + res.push_back(s.substr(pos_start)); + return res; + } } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_ \ No newline at end of file From e4bfd6831392fe60cbccde1f488a4dbf287de3d2 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 22 Jan 2024 16:11:43 +0100 Subject: [PATCH 12/21] windows: fixed custom platform view context menu and window position --- .../in_app_webview/custom_platform_view.dart | 32 ++++- .../custom_platform_view.cc | 15 ++- .../windows/in_app_webview/in_app_webview.cpp | 117 ++++++------------ .../windows/in_app_webview/in_app_webview.h | 1 + .../in_app_webview/in_app_webview_manager.cpp | 13 +- .../in_app_webview/in_app_webview_manager.h | 2 +- 6 files changed, 92 insertions(+), 88 deletions(-) diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/custom_platform_view.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/custom_platform_view.dart index 1f41ab12..8a302954 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/custom_platform_view.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/custom_platform_view.dart @@ -220,6 +220,16 @@ class CustomPlatformViewController return _methodChannel .invokeMethod('setSize', [size.width, size.height, scaleFactor]); } + + /// Sets the surface size to the provided [size]. + Future _setPosition(Offset position, double scaleFactor) async { + if (_isDisposed) { + return; + } + assert(value.isInitialized); + return _methodChannel + .invokeMethod('setPosition', [position.dx, position.dy, scaleFactor]); + } } class CustomPlatformView extends StatefulWidget { @@ -273,8 +283,11 @@ class _CustomPlatformViewState extends State { }, arguments: widget.creationParams); - // Report initial surface size - WidgetsBinding.instance.addPostFrameCallback((_) => _reportSurfaceSize()); + // Report initial surface size and widget position + WidgetsBinding.instance.addPostFrameCallback((_) { + _reportSurfaceSize(); + _reportWidgetPosition(); + }); _cursorSubscription = _controller._cursor.listen((cursor) { setState(() { @@ -301,6 +314,7 @@ class _CustomPlatformViewState extends State { return NotificationListener( onNotification: (notification) { _reportSurfaceSize(); + _reportWidgetPosition(); return true; }, child: SizeChangedLayoutNotifier( @@ -316,6 +330,9 @@ class _CustomPlatformViewState extends State { _controller._setCursorPos(ev.localPosition); }, onPointerDown: (ev) { + _reportSurfaceSize(); + _reportWidgetPosition(); + if (!_focusNode.hasFocus) { _focusNode.requestFocus(); Future.delayed(const Duration(milliseconds: 50), () { @@ -401,6 +418,17 @@ class _CustomPlatformViewState extends State { await _controller.ready; unawaited(_controller._setSize( box.size, widget.scaleFactor ?? window.devicePixelRatio)); + + } + } + + void _reportWidgetPosition() async { + final box = _key.currentContext?.findRenderObject() as RenderBox?; + if (box != null) { + await _controller.ready; + final position = box.localToGlobal(Offset.zero); + unawaited(_controller._setPosition( + position, widget.scaleFactor ?? window.devicePixelRatio)); } } diff --git a/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.cc b/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.cc index 34579e12..c955f87d 100644 --- a/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.cc +++ b/flutter_inappwebview_windows/windows/custom_platform_view/custom_platform_view.cc @@ -15,6 +15,7 @@ namespace flutter_inappwebview_plugin constexpr auto kErrorInvalidArgs = "invalidArguments"; constexpr auto kMethodSetSize = "setSize"; + constexpr auto kMethodSetPosition = "setPosition"; constexpr auto kMethodSetCursorPos = "setCursorPos"; constexpr auto kMethodSetPointerUpdate = "setPointerUpdate"; constexpr auto kMethodSetPointerButton = "setPointerButton"; @@ -299,8 +300,20 @@ namespace flutter_inappwebview_plugin } return result->Error(kErrorInvalidArgs); } + else if (method_name.compare(kMethodSetPosition) == 0) { + auto position = GetPointAndScaleFactorFromArgs(method_call.arguments()); + if (position && view) { + const auto [x, y, scale_factor] = position.value(); - if (method_name.compare(kMethodSetFpsLimit) == 0) { + view->setPosition(static_cast(x), + static_cast(y), + static_cast(scale_factor)); + + return result->Success(); + } + return result->Error(kErrorInvalidArgs); + } + else if (method_name.compare(kMethodSetFpsLimit) == 0) { if (const auto value = std::get_if(method_call.arguments())) { texture_bridge_->SetFpsLimit(*value == 0 ? std::nullopt : std::make_optional(*value)); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index e494c6a8..034b573d 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -14,6 +14,7 @@ #include "../utils/strconv.h" #include "../utils/string.h" #include "in_app_webview.h" + #include "in_app_webview_manager.h" namespace flutter_inappwebview_plugin @@ -872,6 +873,38 @@ namespace flutter_inappwebview_plugin } } + + void InAppWebView::setPosition(size_t x, size_t y, float scale_factor) + { + if (!webViewController || !plugin || !plugin->registrar) { + return; + } + + if (x >= 0 && y >= 0) { + scaleFactor_ = scale_factor; + auto scaled_x = static_cast(x * scale_factor); + auto scaled_y = static_cast(y * scale_factor); + + auto titleBarHeight = ((GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME)) * scale_factor) + GetSystemMetrics(SM_CXPADDEDBORDER); + auto borderWidth = (GetSystemMetrics(SM_CXBORDER) + GetSystemMetrics(SM_CXPADDEDBORDER)) * scale_factor; + + RECT flutterWindowRect; + HWND flutterWindowHWnd = plugin->registrar->GetView()->GetNativeWindow(); + GetWindowRect(flutterWindowHWnd, &flutterWindowRect); + + HWND webViewHWnd; + if (succeededOrLog(webViewController->get_ParentWindow(&webViewHWnd))) { + ::SetWindowPos(webViewHWnd, + nullptr, + static_cast(flutterWindowRect.left + scaled_x - borderWidth), + static_cast(flutterWindowRect.top + scaled_y - titleBarHeight), + 0, 0, + SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); + } + } + } + + void InAppWebView::setCursorPos(double x, double y) { if (!webViewCompositionController) { @@ -999,92 +1032,16 @@ namespace flutter_inappwebview_plugin auto offset = static_cast(delta * kScrollMultiplier); - /* - // For some reason, - // setting the point other than (x: 0, y: 0) - // will not make the scroll work. - // Unfortunately, this will break the scroll event - // for nested HTML scrollable elements. - POINT point; - point.x = 0; - point.y = 0; - - if (horizontal) { webViewCompositionController->SendMouseInput( - COREWEBVIEW2_MOUSE_EVENT_KIND_HORIZONTAL_WHEEL, virtual_keys_.state(), - offset, point); + COREWEBVIEW2_MOUSE_EVENT_KIND_HORIZONTAL_WHEEL, virtualKeys_.state(), + offset, lastCursorPos_); } else { webViewCompositionController->SendMouseInput(COREWEBVIEW2_MOUSE_EVENT_KIND_WHEEL, - virtual_keys_.state(), offset, - point); + virtualKeys_.state(), offset, + lastCursorPos_); } - */ - - // Workaround for scroll events - auto workaroundScrollJS = "(function(horizontal, offset, x, y) { \ - function elemCanScrollY(elem) { \ - if (elem.scrollTop > 0) { \ - return elem; \ - } else { \ - elem.scrollTop++; \ - const top = elem.scrollTop; \ - top && (elem.scrollTop = 0); \ - if (top > 0) { \ - return elem; \ - } else { \ - return elemCanScrollY(elem.parentElement); \ - } \ - } \ - } \ - function elemCanScrollX(elem) { \ - if (elem.scrollLeft > 0) { \ - return elem; \ - } else { \ - elem.scrollLeft++; \ - const left = elem.scrollLeft; \ - left && (elem.scrollLeft = 0); \ - if (left > 0) { \ - return elem; \ - } else { \ - return elemCanScrollX(elem.parentElement); \ - } \ - } \ - } \ - const elem = document.elementFromPoint(x, y); \ - const elem2 = horizontal ? elemCanScrollX(elem) : elemCanScrollY(elem); \ - const handled = elem2 != null && elem2 != document.documentElement && elem2 != document.body; \ - if (handled) { \ - elem2.scrollBy({left: horizontal ? offset : 0, top: horizontal ? 0 : offset}); \ - } \ - return handled; \ -})(" + std::to_string(horizontal) + ", " + std::to_string(offset) + ", " + std::to_string(lastCursorPos_.x) + ", " + std::to_string(lastCursorPos_.y) + ");"; - - webView->ExecuteScript(utf8_to_wide(workaroundScrollJS).c_str(), Callback( - [this, horizontal, offset](HRESULT error, PCWSTR result) -> HRESULT - { - if (webViewCompositionController && (error != S_OK || wide_to_utf8(result).compare("false") == 0)) { - // try to use native mouse wheel handler - - POINT point; - point.x = 0; - point.y = 0; - - if (horizontal) { - webViewCompositionController->SendMouseInput( - COREWEBVIEW2_MOUSE_EVENT_KIND_HORIZONTAL_WHEEL, virtualKeys_.state(), - offset, point); - } - else { - webViewCompositionController->SendMouseInput(COREWEBVIEW2_MOUSE_EVENT_KIND_WHEEL, - virtualKeys_.state(), offset, - point); - } - } - - return S_OK; - }).Get()); } void InAppWebView::setScrollDelta(double delta_x, double delta_y) diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index c1645cbb..d5daa5e4 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -112,6 +112,7 @@ namespace flutter_inappwebview_plugin return surface_.get(); } void setSurfaceSize(size_t width, size_t height, float scale_factor); + void setPosition(size_t x, size_t y, float scale_factor); void setCursorPos(double x, double y); void setPointerUpdate(int32_t pointer, InAppWebViewPointerEventKind eventKind, double x, double y, double size, double pressure); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp index 44cbe303..c1ef879c 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp @@ -56,7 +56,7 @@ namespace flutter_inappwebview_plugin if (string_equals(methodName, "createInAppWebView")) { if (isSupported()) { - createInAppBrowser(arguments, std::move(result)); + createInAppWebView(arguments, std::move(result)); } else { result->Error("0", "Creating an InAppWebView instance is not supported! Graphics Context is not valid!"); @@ -74,7 +74,7 @@ namespace flutter_inappwebview_plugin } } - void InAppWebViewManager::createInAppBrowser(const flutter::EncodableMap* arguments, std::unique_ptr> result) + void InAppWebViewManager::createInAppWebView(const flutter::EncodableMap* arguments, std::unique_ptr> result) { auto result_ = std::shared_ptr>(std::move(result)); @@ -84,8 +84,13 @@ namespace flutter_inappwebview_plugin auto initialDataMap = get_optional_fl_map_value(*arguments, "initialData"); auto initialUserScriptList = get_optional_fl_map_value(*arguments, "initialUserScripts"); - auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, CW_DEFAULT, - CW_DEFAULT, 0, 0, HWND_MESSAGE, nullptr, + RECT bounds; + GetClientRect(plugin->registrar->GetView()->GetNativeWindow(), &bounds); + + auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, 0, + 0, bounds.right - bounds.left, bounds.bottom - bounds.top, + plugin->registrar->GetView()->GetNativeWindow(), // HWND_MESSAGE, + nullptr, windowClass_.hInstance, nullptr); InAppWebView::createInAppWebViewEnv(hwnd, true, diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.h index 5d89e9cc..8630ceeb 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.h @@ -44,7 +44,7 @@ namespace flutter_inappwebview_plugin const flutter::MethodCall& method_call, std::unique_ptr> result); - void createInAppBrowser(const flutter::EncodableMap* arguments, std::unique_ptr> result); + void createInAppWebView(const flutter::EncodableMap* arguments, std::unique_ptr> result); private: std::unique_ptr rohelper_; winrt::com_ptr From ab869e9703828e297d62709954a11a686e3bdb07 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 Jan 2024 16:01:09 +0100 Subject: [PATCH 13/21] windows: fixed javascript evaluation, added callAsyncJavaScript implementation, call DestroyWindow(parentWindow) on InAppWebView dealloc --- .../platform_inappwebview_controller.dart | 3 +- .../in_app_webview_controller.dart | 3 +- .../windows/in_app_browser/in_app_browser.cpp | 90 ------------------ .../windows/in_app_webview/in_app_webview.cpp | 95 ++++++++++++++++++- .../windows/in_app_webview/in_app_webview.h | 6 ++ .../in_app_webview/in_app_webview_manager.cpp | 2 +- .../webview_channel_delegate.cpp | 12 +++ .../plugin_scripts_js/plugin_scripts_util.h | 3 + .../windows/utils/log.h | 3 +- .../windows/utils/string.h | 6 ++ 10 files changed, 126 insertions(+), 97 deletions(-) diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart index c38542ec..eb01d7c5 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart @@ -521,7 +521,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- iOS ([Official API - WKWebView.evaluateJavascript](https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript)) ///- MacOS ([Official API - WKWebView.evaluateJavascript](https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript)) ///- Web ([Official API - Window.eval](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval?retiredLocale=it)) - ///- Windows ([Official API - ICoreWebView2.ExecuteScript](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#executescript)) + ///- Windows ///{@endtemplate} Future evaluateJavascript( {required String source, ContentWorld? contentWorld}) { @@ -1342,6 +1342,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ///- iOS ([Official API - WKWebView.callAsyncJavaScript](https://developer.apple.com/documentation/webkit/wkwebview/3656441-callasyncjavascript)) ///- MacOS ([Official API - WKWebView.callAsyncJavaScript](https://developer.apple.com/documentation/webkit/wkwebview/3656441-callasyncjavascript)) + ///- Windows ///{@endtemplate} Future callAsyncJavaScript( {required String functionBody, diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart index 2878505e..020d109b 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart @@ -2428,12 +2428,13 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController ContentWorld? contentWorld}) async { Map args = {}; args.putIfAbsent('functionBody', () => functionBody); - args.putIfAbsent('arguments', () => arguments); + args.putIfAbsent('arguments', () => jsonEncode(arguments)); args.putIfAbsent('contentWorld', () => contentWorld?.toMap()); var data = await channel?.invokeMethod('callAsyncJavaScript', args); if (data == null) { return null; } + data = json.decode(data); return CallAsyncJavaScriptResult( value: data["value"], error: data["error"]); } diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index c87d05a0..a7ab72e9 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -73,96 +73,6 @@ namespace flutter_inappwebview_plugin close(); } }); - - // <-- WebView2 sample code starts here --> - // Step 3 - Create a single WebView within the parent window - // Locate the browser and set up the environment for WebView - //CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, - // Callback( - // [hWnd = m_hWnd, inAppBrowser = this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT { - - // // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd - // env->CreateCoreWebView2Controller(hWnd, Callback( - // [hWnd, inAppBrowser](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT { - // if (controller != nullptr) { - // inAppBrowser->webviewController = controller; - // inAppBrowser->webviewController->get_CoreWebView2(&inAppBrowser->webview); - // } - - // // Add a few settings for the webview - // // The demo step is redundant since the values are the default settings - // wil::com_ptr settings; - // inAppBrowser->webview->get_Settings(&settings); - // settings->put_IsScriptEnabled(TRUE); - // settings->put_AreDefaultScriptDialogsEnabled(TRUE); - // settings->put_IsWebMessageEnabled(TRUE); - - // // Resize WebView to fit the bounds of the parent window - // RECT bounds; - // GetClientRect(hWnd, &bounds); - // inAppBrowser->webviewController->put_Bounds(bounds); - - // auto url = inAppBrowser->initialUrlRequest.value().url.value(); - // std::wstring stemp = ansi_to_wide(url); - - // // Schedule an async task to navigate to Bing - // inAppBrowser->webview->Navigate(stemp.c_str()); - - // // - // // Step 4 - Navigation events - // // register an ICoreWebView2NavigationStartingEventHandler to cancel any non-https navigation - // EventRegistrationToken token; - // inAppBrowser->webview->add_NavigationStarting(Callback( - // [](ICoreWebView2* webview, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT { - // wil::unique_cotaskmem_string uri; - // args->get_Uri(&uri); - // std::wstring source(uri.get()); - // if (source.substr(0, 5) != L"https") { - // args->put_Cancel(true); - // } - // return S_OK; - // }).Get(), &token); - // // - - // // - // // Step 5 - Scripting - // // Schedule an async task to add initialization script that freezes the Object object - // inAppBrowser->webview->AddScriptToExecuteOnDocumentCreated(L"Object.freeze(Object);", nullptr); - // // Schedule an async task to get the document URL - // inAppBrowser->webview->ExecuteScript(L"window.document.URL;", Callback( - // [](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT { - // LPCWSTR URL = resultObjectAsJson; - // OutputDebugStringW(URL); - // //doSomethingWithURL(URL); - // return S_OK; - // }).Get()); - // // - - // // - // // Step 6 - Communication between host and web content - // // Set an event handler for the host to return received message back to the web content - // inAppBrowser->webview->add_WebMessageReceived(Callback( - // [](ICoreWebView2* webview, ICoreWebView2WebMessageReceivedEventArgs* args) -> HRESULT { - // wil::unique_cotaskmem_string message; - // args->TryGetWebMessageAsString(&message); - // // processMessage(&message); - // webview->PostWebMessageAsString(message.get()); - // return S_OK; - // }).Get(), &token); - - // // Schedule an async task to add initialization script that - // // 1) Add an listener to print message from the host - // // 2) Post document URL to the host - // inAppBrowser->webview->AddScriptToExecuteOnDocumentCreated( - // L"window.chrome.webview.addEventListener(\'message\', event => alert(event.data));" \ - // L"window.chrome.webview.postMessage(window.document.URL);", - // nullptr); - // // - - // return S_OK; - // }).Get()); - // return S_OK; - // }).Get()); } void InAppBrowser::close() const diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index 034b573d..e2edd175 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -785,12 +785,94 @@ namespace flutter_inappwebview_plugin auto hr = webView->CallDevToolsProtocolMethod(L"Runtime.evaluate", utf8_to_wide(parameters.dump()).c_str(), Callback( [this, completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) { - std::string result = "null"; + nlohmann::json result; if (succeededOrLog(errorCode)) { - result = wide_to_utf8(returnObjectAsJson); + nlohmann::json json = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson)); + result = json["result"].contains("value") ? json["result"]["value"] : nlohmann::json{}; + if (json.contains("exceptionDetails")) { + nlohmann::json exceptionDetails = json["exceptionDetails"]; + auto errorMessage = exceptionDetails.contains("exception") && exceptionDetails["exception"].contains("value") + ? exceptionDetails["exception"]["value"].dump() : + (result["value"].is_null() ? exceptionDetails["text"].get() : result["value"].dump()); + result = nlohmann::json{}; + debugLog(exceptionDetails.dump()); + if (channelDelegate) { + channelDelegate->onConsoleMessage(errorMessage, 3); + } + } } if (completionHandler) { - completionHandler(result); + completionHandler(result.dump()); + } + return S_OK; + } + ).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler("null"); + } + }); + } + + void InAppWebView::callAsyncJavaScript(const std::string& functionBody, const std::string& argumentsAsJson, const std::shared_ptr contentWorld, const std::function completionHandler) const + { + if (!webView || !userContentController) { + if (completionHandler) { + completionHandler("null"); + } + return; + } + + userContentController->createContentWorld(contentWorld, + [=](const int& contextId) + { + std::vector functionArgumentNamesList; + std::vector functionArgumentValuesList; + + auto jsonVal = nlohmann::json::parse(argumentsAsJson); + for (auto const& [key, val] : jsonVal.items()) { + functionArgumentNamesList.push_back(key); + functionArgumentValuesList.push_back(val.dump()); + } + + auto source = replace_all_copy(CALL_ASYNC_JAVASCRIPT_WRAPPER_JS, VAR_FUNCTION_ARGUMENT_NAMES, join(functionArgumentNamesList, ", ")); + replace_all(source, VAR_FUNCTION_ARGUMENT_VALUES, join(functionArgumentValuesList, ", ")); + replace_all(source, VAR_FUNCTION_BODY, functionBody); + + nlohmann::json parameters = { + {"expression", source}, + {"awaitPromise", true} + }; + + if (contextId >= 0) { + parameters["contextId"] = contextId; + } + + auto hr = webView->CallDevToolsProtocolMethod(L"Runtime.evaluate", utf8_to_wide(parameters.dump()).c_str(), Callback( + [this, completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + nlohmann::json result = { + {"value", nlohmann::json{}}, + {"error", nlohmann::json{}} + }; + if (succeededOrLog(errorCode)) { + nlohmann::json json = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson)); + result["value"] = json["result"].contains("value") ? json["result"]["value"] : nlohmann::json{}; + if (json.contains("exceptionDetails")) { + nlohmann::json exceptionDetails = json["exceptionDetails"]; + auto errorMessage = exceptionDetails.contains("exception") && exceptionDetails["exception"].contains("value") + ? exceptionDetails["exception"]["value"].dump() : + (result["value"].is_null() ? exceptionDetails["text"].get() : result["value"].dump()); + result["value"] = nlohmann::json{}; + result["error"] = errorMessage; + debugLog(exceptionDetails.dump()); + if (channelDelegate) { + channelDelegate->onConsoleMessage(errorMessage, 3); + } + } + } + if (completionHandler) { + completionHandler(result.dump()); } return S_OK; } @@ -1105,6 +1187,8 @@ namespace flutter_inappwebview_plugin InAppWebView::~InAppWebView() { debugLog("dealloc InAppWebView"); + HWND parentWindow; + webViewController->get_ParentWindow(&parentWindow); if (webView) { webView->Stop(); } @@ -1114,5 +1198,10 @@ namespace flutter_inappwebview_plugin navigationActions_.clear(); inAppBrowser = nullptr; plugin = nullptr; + if (webViewCompositionController) { + // if it's an InAppWebView, + // then destroy the Window created with it + DestroyWindow(parentWindow); + } } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index d5daa5e4..b5915c2f 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -9,6 +9,7 @@ #include #include "../flutter_inappwebview_windows_plugin.h" +#include "../plugin_scripts_js/plugin_scripts_util.h" #include "../types/content_world.h" #include "../types/navigation_action.h" #include "../types/url_request.h" @@ -70,6 +71,10 @@ namespace flutter_inappwebview_plugin } }; + const std::string CALL_ASYNC_JAVASCRIPT_WRAPPER_JS = "(async function(" + VAR_FUNCTION_ARGUMENT_NAMES + ") { \ + " + VAR_FUNCTION_BODY + " \ + })(" + VAR_FUNCTION_ARGUMENT_VALUES + ");"; + struct InAppWebViewCreationParams { const std::variant id; const std::shared_ptr initialSettings; @@ -150,6 +155,7 @@ namespace flutter_inappwebview_plugin } void stopLoading() const; void evaluateJavascript(const std::string& source, const std::shared_ptr contentWorld, const std::function completionHandler) const; + void callAsyncJavaScript(const std::string& functionBody, const std::string& argumentsAsJson, const std::shared_ptr contentWorld, const std::function completionHandler) const; void getCopyBackForwardList(const std::function)> completionHandler) const; void addUserScript(const std::shared_ptr userScript) const; void removeUserScript(const int64_t index, const std::shared_ptr userScript) const; diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp index c1ef879c..41e3e965 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp @@ -89,7 +89,7 @@ namespace flutter_inappwebview_plugin auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, 0, 0, bounds.right - bounds.left, bounds.bottom - bounds.top, - plugin->registrar->GetView()->GetNativeWindow(), // HWND_MESSAGE, + plugin->registrar->GetView()->GetNativeWindow(), nullptr, windowClass_.hInstance, nullptr); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index f7ace600..db7e2875 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -120,6 +120,18 @@ namespace flutter_inappwebview_plugin result_->Success(value); }); } + else if (string_equals(methodName, "callAsyncJavaScript")) { + auto result_ = std::shared_ptr>(std::move(result)); + + auto functionBody = get_fl_map_value(arguments, "functionBody"); + auto argumentsAsJson = get_fl_map_value(arguments, "arguments"); + auto contentWorldMap = get_optional_fl_map_value(arguments, "contentWorld"); + std::shared_ptr contentWorld = contentWorldMap.has_value() ? std::make_shared(contentWorldMap.value()) : ContentWorld::page(); + webView->callAsyncJavaScript(functionBody, argumentsAsJson, std::move(contentWorld), [result_ = std::move(result_)](const std::string& value) + { + result_->Success(value); + }); + } else if (string_equals(methodName, "getCopyBackForwardList")) { auto result_ = std::shared_ptr>(std::move(result)); webView->getCopyBackForwardList([result_ = std::move(result_)](const std::unique_ptr value) diff --git a/flutter_inappwebview_windows/windows/plugin_scripts_js/plugin_scripts_util.h b/flutter_inappwebview_windows/windows/plugin_scripts_js/plugin_scripts_util.h index 1492989e..239783c3 100644 --- a/flutter_inappwebview_windows/windows/plugin_scripts_js/plugin_scripts_util.h +++ b/flutter_inappwebview_windows/windows/plugin_scripts_js/plugin_scripts_util.h @@ -7,6 +7,9 @@ namespace flutter_inappwebview_plugin { const std::string VAR_PLACEHOLDER_VALUE = "$IN_APP_WEBVIEW_PLACEHOLDER_VALUE"; const std::string VAR_PLACEHOLDER_MEMORY_ADDRESS_VALUE = "$IN_APP_WEBVIEW_PLACEHOLDER_MEMORY_ADDRESS_VALUE"; + const std::string VAR_FUNCTION_ARGUMENT_NAMES = "$IN_APP_WEBVIEW_FUNCTION_ARGUMENT_NAMES"; + const std::string VAR_FUNCTION_ARGUMENT_VALUES = "$IN_APP_WEBVIEW_FUNCTION_ARGUMENT_VALUES"; + const std::string VAR_FUNCTION_BODY = "$IN_APP_WEBVIEW_FUNCTION_BODY"; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_SCRIPTS_UTIL_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/utils/log.h b/flutter_inappwebview_windows/windows/utils/log.h index d1eb3a36..e61dcb61 100644 --- a/flutter_inappwebview_windows/windows/utils/log.h +++ b/flutter_inappwebview_windows/windows/utils/log.h @@ -63,7 +63,8 @@ namespace flutter_inappwebview_plugin static inline void debugLog(const HRESULT& hr, const std::string& filename = "", const int& line = 0) { auto isError = hr != S_OK; - debugLog((isError ? "Error: " : "Message: ") + getHRMessage(hr), isError, filename, line); + auto errorCode = std::to_string(hr); + debugLog((isError ? "Error " + errorCode + ": " : "Message: ") + getHRMessage(hr), isError, filename, line); } static inline bool succeededOrLog(const HRESULT& hr, const std::string& filename = "", const int& line = 0) diff --git a/flutter_inappwebview_windows/windows/utils/string.h b/flutter_inappwebview_windows/windows/utils/string.h index cce03750..27b64731 100644 --- a/flutter_inappwebview_windows/windows/utils/string.h +++ b/flutter_inappwebview_windows/windows/utils/string.h @@ -120,6 +120,12 @@ namespace flutter_inappwebview_plugin [delim](auto& a, auto& b) { return a + delim + b; }); } + template + static inline std::basic_string join(const std::vector>& vec, const char* delim) + { + return join(vec, std::basic_string{ delim }); + } + template static inline std::vector> split(const std::basic_string& s, std::basic_string delimiter) { From 7721deb062d0c4f2e7b82b276ace5b85f4bfc9a6 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 Jan 2024 02:11:28 +0100 Subject: [PATCH 14/21] windows: added headless inappwebview initial implementation --- .../headless_in_app_webview.dart | 22 +-- .../lib/src/inappwebview_platform.dart | 12 ++ .../windows/CMakeLists.txt | 8 ++ .../flutter_inappwebview_windows_plugin.cpp | 3 +- .../flutter_inappwebview_windows_plugin.h | 2 + .../headless_in_app_webview.cpp | 59 ++++++++ .../headless_in_app_webview.h | 35 +++++ .../headless_in_app_webview_manager.cpp | 136 ++++++++++++++++++ .../headless_in_app_webview_manager.h | 36 +++++ .../headless_webview_channel_delegate.cpp | 56 ++++++++ .../headless_webview_channel_delegate.h | 27 ++++ .../windows/in_app_webview/in_app_webview.cpp | 25 ++-- .../webview_channel_delegate.cpp | 2 +- .../windows/types/size_2d.cpp | 21 +++ .../windows/types/size_2d.h | 25 ++++ 15 files changed, 446 insertions(+), 23 deletions(-) create mode 100644 flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.cpp create mode 100644 flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.h create mode 100644 flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp create mode 100644 flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.h create mode 100644 flutter_inappwebview_windows/windows/headless_in_app_webview/headless_webview_channel_delegate.cpp create mode 100644 flutter_inappwebview_windows/windows/headless_in_app_webview/headless_webview_channel_delegate.h create mode 100644 flutter_inappwebview_windows/windows/types/size_2d.cpp create mode 100644 flutter_inappwebview_windows/windows/types/size_2d.h diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart index 7354b20b..843b144a 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart @@ -274,7 +274,7 @@ class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView dynamic _controllerFromPlatform; - WindowsHeadlessInAppWebViewCreationParams get _macosParams => + WindowsHeadlessInAppWebViewCreationParams get _windowsParams => params as WindowsHeadlessInAppWebViewCreationParams; _init() { @@ -284,7 +284,7 @@ class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView _controllerFromPlatform = params.controllerFromPlatform?.call(_webViewController!) ?? _webViewController!; - _macosParams.findInteractionController?.init(id); + _windowsParams.findInteractionController?.init(id); channel = MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview_$id'); handler = _handleMethod; @@ -320,8 +320,8 @@ class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView initialSettings.toMap(); Map pullToRefreshSettings = - _macosParams.pullToRefreshController?.params.settings.toMap() ?? - _macosParams.pullToRefreshController?.params.options.toMap() ?? + _windowsParams.pullToRefreshController?.params.settings.toMap() ?? + _windowsParams.pullToRefreshController?.params.options.toMap() ?? PullToRefreshSettings(enabled: false).toMap(); Map args = {}; @@ -341,8 +341,14 @@ class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView 'pullToRefreshSettings': pullToRefreshSettings, 'initialSize': params.initialSize.toMap() }); - await _sharedChannel.invokeMethod('run', args); - _running = true; + try { + await _sharedChannel.invokeMethod('run', args); + _running = true; + } catch (e) { + _running = false; + _started = false; + throw e; + } } void _inferInitialSettings(InAppWebViewSettings settings) { @@ -420,8 +426,8 @@ class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView _webViewController?.dispose(); _webViewController = null; _controllerFromPlatform = null; - _macosParams.pullToRefreshController?.dispose(); - _macosParams.findInteractionController?.dispose(); + _windowsParams.pullToRefreshController?.dispose(); + _windowsParams.findInteractionController?.dispose(); } } diff --git a/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart b/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart index a91ade71..3cbfaa80 100644 --- a/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart +++ b/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart @@ -3,6 +3,7 @@ import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_pla 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'; /// Implementation of [InAppWebViewPlatform] using the WebKit API. class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { @@ -61,4 +62,15 @@ class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { WindowsInAppBrowser createPlatformInAppBrowserStatic() { return WindowsInAppBrowser.static(); } + + /// Creates a new [WindowsHeadlessInAppWebView]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [HeadlessInAppWebView] in `flutter_inappwebview` instead. + @override + WindowsHeadlessInAppWebView createPlatformHeadlessInAppWebView( + PlatformHeadlessInAppWebViewCreationParams params, + ) { + return WindowsHeadlessInAppWebView(params); + } } diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt index 908110bb..2d8801db 100644 --- a/flutter_inappwebview_windows/windows/CMakeLists.txt +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -70,6 +70,8 @@ list(APPEND PLUGIN_SOURCES "types/user_script.h" "types/plugin_script.cpp" "types/plugin_script.h" + "types/size_2d.cpp" + "types/size_2d.h" "custom_platform_view/custom_platform_view.cc" "custom_platform_view/custom_platform_view.h" "custom_platform_view/texture_bridge.cc" @@ -96,6 +98,12 @@ list(APPEND PLUGIN_SOURCES "in_app_webview/in_app_webview_manager.h" "in_app_webview/webview_channel_delegate.cpp" "in_app_webview/webview_channel_delegate.h" + "headless_in_app_webview/headless_in_app_webview.cpp" + "headless_in_app_webview/headless_in_app_webview.h" + "headless_in_app_webview/headless_in_app_webview_manager.cpp" + "headless_in_app_webview/headless_in_app_webview_manager.h" + "headless_in_app_webview/headless_webview_channel_delegate.cpp" + "headless_in_app_webview/headless_webview_channel_delegate.h" "in_app_browser/in_app_browser_settings.cpp" "in_app_browser/in_app_browser_settings.h" "in_app_browser/in_app_browser_manager.cpp" diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp index dfbb46e1..57355649 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp @@ -1,8 +1,8 @@ #include "flutter_inappwebview_windows_plugin.h" -#include #include +#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" @@ -25,6 +25,7 @@ namespace flutter_inappwebview_plugin { inAppWebViewManager = std::make_unique(this); inAppBrowserManager = std::make_unique(this); + headlessInAppWebViewManager = std::make_unique(this); } FlutterInappwebviewWindowsPlugin::~FlutterInappwebviewWindowsPlugin() diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h index 9e882ea7..64566fa0 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h @@ -8,12 +8,14 @@ namespace flutter_inappwebview_plugin { class InAppWebViewManager; class InAppBrowserManager; + class HeadlessInAppWebViewManager; class FlutterInappwebviewWindowsPlugin : public flutter::Plugin { public: flutter::PluginRegistrarWindows* registrar; std::unique_ptr inAppWebViewManager; std::unique_ptr inAppBrowserManager; + std::unique_ptr headlessInAppWebViewManager; static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); diff --git a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.cpp b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.cpp new file mode 100644 index 00000000..bf1db080 --- /dev/null +++ b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.cpp @@ -0,0 +1,59 @@ +#include "../utils/log.h" +#include "headless_in_app_webview.h" + +namespace flutter_inappwebview_plugin +{ + using namespace Microsoft::WRL; + + HeadlessInAppWebView::HeadlessInAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const HeadlessInAppWebViewCreationParams& params, const HWND parentWindow, std::unique_ptr webView) + : plugin(plugin), id(params.id), + webView(std::move(webView)), + channelDelegate(std::make_unique(this, plugin->registrar->messenger())) + { + prepare(params); + ShowWindow(parentWindow, SW_HIDE); + } + + void HeadlessInAppWebView::prepare(const HeadlessInAppWebViewCreationParams& params) + { + if (!webView) { + return; + } + webView->webViewController->put_IsVisible(false); + } + + void HeadlessInAppWebView::setSize(const std::shared_ptr size) const + { + if (!webView) { + return; + } + RECT rect = { + 0, 0, (LONG)size->width, (LONG)size->height + }; + webView->webViewController->put_Bounds(rect); + } + + std::shared_ptr HeadlessInAppWebView::getSize() const + { + if (!webView) { + return std::make_shared(-1.0, -1.0); + } + RECT rect; + webView->webViewController->get_Bounds(&rect); + auto width = rect.right - rect.left; + auto height = rect.bottom - rect.top; + return std::make_shared((double)width, (double)height); + } + + HeadlessInAppWebView::~HeadlessInAppWebView() + { + debugLog("dealloc HeadlessInAppWebView"); + if (webView && webView->webViewController) { + HWND parentWindow; + if (succeededOrLog(webView->webViewController->get_ParentWindow(&parentWindow))) { + DestroyWindow(parentWindow); + } + } + webView = nullptr; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.h b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.h new file mode 100644 index 00000000..0e419602 --- /dev/null +++ b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.h @@ -0,0 +1,35 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_HEADLESS_IN_APP_WEBVIEW_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_HEADLESS_IN_APP_WEBVIEW_H_ + +#include "../flutter_inappwebview_windows_plugin.h" +#include "../in_app_webview/in_app_webview.h" +#include "../types/size_2d.h" +#include "headless_webview_channel_delegate.h" + +namespace flutter_inappwebview_plugin +{ + struct HeadlessInAppWebViewCreationParams { + const std::string id; + const std::shared_ptr initialSize; + }; + + class HeadlessInAppWebView + { + public: + static inline const wchar_t* CLASS_NAME = L"HeadlessInAppWebView"; + static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_headless_inappwebview_"; + + const FlutterInappwebviewWindowsPlugin* plugin; + std::string id; + std::unique_ptr webView; + std::unique_ptr channelDelegate; + + HeadlessInAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const HeadlessInAppWebViewCreationParams& params, const HWND parentWindow, std::unique_ptr webView); + ~HeadlessInAppWebView(); + + void prepare(const HeadlessInAppWebViewCreationParams& params); + void setSize(const std::shared_ptr size) const; + std::shared_ptr getSize() const; + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_HEADLESS_IN_APP_WEBVIEW_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp new file mode 100644 index 00000000..a8803bae --- /dev/null +++ b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include + +#include "../in_app_webview/in_app_webview_settings.h" +#include "../types/size_2d.h" +#include "../types/url_request.h" +#include "../types/user_script.h" +#include "../utils/flutter.h" +#include "../utils/log.h" +#include "../utils/string.h" +#include "../utils/vector.h" +#include "headless_in_app_webview_manager.h" + +namespace flutter_inappwebview_plugin +{ + HeadlessInAppWebViewManager::HeadlessInAppWebViewManager(const FlutterInappwebviewWindowsPlugin* plugin) + : plugin(plugin), + ChannelDelegate(plugin->registrar->messenger(), HeadlessInAppWebViewManager::METHOD_CHANNEL_NAME) + { + windowClass_.lpszClassName = HeadlessInAppWebView::CLASS_NAME; + windowClass_.lpfnWndProc = &DefWindowProc; + + RegisterClass(&windowClass_); + } + + void HeadlessInAppWebViewManager::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + auto* arguments = std::get_if(method_call.arguments()); + auto& methodName = method_call.method_name(); + + if (string_equals(methodName, "run")) { + run(arguments, std::move(result)); + } + else { + result->NotImplemented(); + } + } + + void HeadlessInAppWebViewManager::run(const flutter::EncodableMap* arguments, std::unique_ptr> result) + { + auto result_ = std::shared_ptr>(std::move(result)); + + auto id = get_fl_map_value(*arguments, "id"); + auto params = get_fl_map_value(*arguments, "params"); + + auto initialSize = std::make_shared(get_fl_map_value(params, "initialSize")); + + auto settingsMap = get_fl_map_value(params, "initialSettings"); + auto urlRequestMap = get_optional_fl_map_value(params, "initialUrlRequest"); + auto initialFile = get_optional_fl_map_value(params, "initialFile"); + auto initialDataMap = get_optional_fl_map_value(params, "initialData"); + auto initialUserScriptList = get_optional_fl_map_value(params, "initialUserScripts"); + + RECT bounds; + GetClientRect(plugin->registrar->GetView()->GetNativeWindow(), &bounds); + + auto initialWidth = initialSize->width >= 0 ? initialSize->width : bounds.right - bounds.left; + auto initialHeight = initialSize->height >= 0 ? initialSize->height : bounds.bottom - bounds.top; + + auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, 0, + 0, (int)initialWidth, (int)initialHeight, + plugin->registrar->GetView()->GetNativeWindow(), + nullptr, + windowClass_.hInstance, nullptr); + + InAppWebView::createInAppWebViewEnv(hwnd, false, + [=](wil::com_ptr webViewEnv, + wil::com_ptr webViewController, + wil::com_ptr webViewCompositionController) + { + if (webViewEnv && webViewController) { + auto initialSettings = std::make_unique(settingsMap); + std::optional>> initialUserScripts = initialUserScriptList.has_value() ? + functional_map(initialUserScriptList.value(), [](const flutter::EncodableValue& map) { return std::make_shared(std::get(map)); }) : + std::optional>>{}; + + InAppWebViewCreationParams params = { + id, + std::move(initialSettings), + initialUserScripts + }; + + auto inAppWebView = std::make_unique(plugin, params, hwnd, + std::move(webViewEnv), std::move(webViewController), nullptr + ); + + HeadlessInAppWebViewCreationParams headlessParams = { + id, + std::move(initialSize) + }; + + auto headlessInAppWebView = std::make_unique(plugin, + headlessParams, + hwnd, + std::move(inAppWebView)); + + headlessInAppWebView->webView->initChannel(std::nullopt, std::nullopt); + + if (headlessInAppWebView->channelDelegate) { + headlessInAppWebView->channelDelegate->onWebViewCreated(); + } + + std::optional> urlRequest = urlRequestMap.has_value() ? std::make_shared(urlRequestMap.value()) : std::optional>{}; + if (urlRequest.has_value()) { + headlessInAppWebView->webView->loadUrl(urlRequest.value()); + } + else if (initialFile.has_value()) { + headlessInAppWebView->webView->loadFile(initialFile.value()); + } + else if (initialDataMap.has_value()) { + headlessInAppWebView->webView->loadData(get_fl_map_value(initialDataMap.value(), "data")); + } + + webViews.insert({ id, std::move(headlessInAppWebView) }); + + result_->Success(true); + } + else { + result_->Error("0", "Cannot create the HeadlessInAppWebView instance!"); + } + } + ); + } + + HeadlessInAppWebViewManager::~HeadlessInAppWebViewManager() + { + debugLog("dealloc HeadlessInAppWebViewManager"); + webViews.clear(); + UnregisterClass(windowClass_.lpszClassName, nullptr); + plugin = nullptr; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.h b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.h new file mode 100644 index 00000000..1e52c51f --- /dev/null +++ b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.h @@ -0,0 +1,36 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_HEADLESS_IN_APP_WEBVIEW_MANAGER_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_HEADLESS_IN_APP_WEBVIEW_MANAGER_H_ + +#include +#include +#include +#include +#include + +#include "../flutter_inappwebview_windows_plugin.h" +#include "../types/channel_delegate.h" +#include "headless_in_app_webview.h" + +namespace flutter_inappwebview_plugin +{ + class HeadlessInAppWebViewManager : public ChannelDelegate + { + public: + static inline const std::string METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_headless_inappwebview"; + + const FlutterInappwebviewWindowsPlugin* plugin; + std::map> webViews; + + HeadlessInAppWebViewManager(const FlutterInappwebviewWindowsPlugin* plugin); + ~HeadlessInAppWebViewManager(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + void run(const flutter::EncodableMap* arguments, std::unique_ptr> result); + private: + WNDCLASS windowClass_ = {}; + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_HEADLESS_IN_APP_WEBVIEW_MANAGER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_webview_channel_delegate.cpp new file mode 100644 index 00000000..5aadab56 --- /dev/null +++ b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_webview_channel_delegate.cpp @@ -0,0 +1,56 @@ +#include "../utils/flutter.h" +#include "../utils/log.h" +#include "../utils/strconv.h" +#include "../utils/string.h" +#include "headless_in_app_webview.h" +#include "headless_webview_channel_delegate.h" + +#include "headless_in_app_webview_manager.h" + +namespace flutter_inappwebview_plugin +{ + HeadlessWebViewChannelDelegate::HeadlessWebViewChannelDelegate(HeadlessInAppWebView* webView, flutter::BinaryMessenger* messenger) + : webView(webView), ChannelDelegate(messenger, HeadlessInAppWebView::METHOD_CHANNEL_NAME_PREFIX + variant_to_string(webView->id)) + {} + + void HeadlessWebViewChannelDelegate::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + if (!webView) { + result->Success(); + return; + } + + // auto& arguments = std::get(*method_call.arguments()); + auto& methodName = method_call.method_name(); + + if (string_equals(methodName, "dispose")) { + if (webView->plugin && webView->plugin->headlessInAppWebViewManager) { + std::map>& webViews = webView->plugin->headlessInAppWebViewManager->webViews; + auto& id = webView->id; + if (map_contains(webViews, id)) { + webViews.erase(id); + } + } + result->Success(); + } + else { + result->NotImplemented(); + } + } + + void HeadlessWebViewChannelDelegate::onWebViewCreated() const + { + if (!channel) { + return; + } + + channel->InvokeMethod("onWebViewCreated", nullptr); + } + + HeadlessWebViewChannelDelegate::~HeadlessWebViewChannelDelegate() + { + debugLog("dealloc HeadlessWebViewChannelDelegate"); + webView = nullptr; + } +} diff --git a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_webview_channel_delegate.h b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_webview_channel_delegate.h new file mode 100644 index 00000000..15d3a8ed --- /dev/null +++ b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_webview_channel_delegate.h @@ -0,0 +1,27 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_HEADLESS_WEBVIEW_CHANNEL_DELEGATE_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_HEADLESS_WEBVIEW_CHANNEL_DELEGATE_H_ + +#include "../types/channel_delegate.h" +#include + +namespace flutter_inappwebview_plugin +{ + class HeadlessInAppWebView; + + class HeadlessWebViewChannelDelegate : public ChannelDelegate + { + public: + HeadlessInAppWebView* webView; + + HeadlessWebViewChannelDelegate(HeadlessInAppWebView* webView, flutter::BinaryMessenger* messenger); + ~HeadlessWebViewChannelDelegate(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + void onWebViewCreated() const; + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_HEADLESS_WEBVIEW_CHANNEL_DELEGATE_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index e2edd175..8f1f9658 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -274,7 +274,7 @@ namespace flutter_inappwebview_plugin navigationActions_.insert({ navigationId, navigationAction }); } - if (callShouldOverrideUrlLoading_ && requestMethod == nullptr) { + if (settings->useShouldOverrideUrlLoading && callShouldOverrideUrlLoading_ && requestMethod == nullptr) { // for some reason, we can't cancel and load an URL with other HTTP methods than GET, // so ignore the shouldOverrideUrlLoading event. @@ -1187,21 +1187,20 @@ namespace flutter_inappwebview_plugin InAppWebView::~InAppWebView() { debugLog("dealloc InAppWebView"); - HWND parentWindow; - webViewController->get_ParentWindow(&parentWindow); - if (webView) { - webView->Stop(); - } - if (webViewController) { - webViewController->Close(); - } - navigationActions_.clear(); - inAppBrowser = nullptr; - plugin = nullptr; - if (webViewCompositionController) { + HWND parentWindow = nullptr; + if (webViewCompositionController && succeededOrLog(webViewController->get_ParentWindow(&parentWindow))) { // if it's an InAppWebView, // then destroy the Window created with it DestroyWindow(parentWindow); } + if (webView) { + failedLog(webView->Stop()); + } + if (webViewController) { + failedLog(webViewController->Close()); + } + navigationActions_.clear(); + inAppBrowser = nullptr; + plugin = nullptr; } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index db7e2875..a2648dc7 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -22,7 +22,7 @@ namespace flutter_inappwebview_plugin { decodeResult = [](const flutter::EncodableValue* value) { - if (value->IsNull()) { + if (!value || value->IsNull()) { return NavigationActionPolicy::cancel; } auto navigationPolicy = std::get(*value); diff --git a/flutter_inappwebview_windows/windows/types/size_2d.cpp b/flutter_inappwebview_windows/windows/types/size_2d.cpp new file mode 100644 index 00000000..e1239026 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/size_2d.cpp @@ -0,0 +1,21 @@ +#include "size_2d.h" + +namespace flutter_inappwebview_plugin +{ + Size2D::Size2D(const double& width, const double& height) + : width(width), height(height) + {} + + Size2D::Size2D(const flutter::EncodableMap& map) + : width(get_fl_map_value(map, "width")), + height(get_fl_map_value(map, "height")) + {} + + flutter::EncodableMap Size2D::toEncodableMap() const + { + return flutter::EncodableMap{ + {"width", width}, + {"height", height} + }; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/size_2d.h b/flutter_inappwebview_windows/windows/types/size_2d.h new file mode 100644 index 00000000..01162a95 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/size_2d.h @@ -0,0 +1,25 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_SIZE_2D_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_SIZE_2D_H_ + +#include +#include + +#include "../utils/flutter.h" + +namespace flutter_inappwebview_plugin +{ + class Size2D + { + public: + const double width; + const double height; + + Size2D(const double& width, const double& height); + Size2D(const flutter::EncodableMap& map); + ~Size2D() = default; + + flutter::EncodableMap toEncodableMap() const; + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_SIZE_2D_H_ \ No newline at end of file From ed979283318c470e01c96d1da0191633cc506740 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 27 Jan 2024 18:41:10 +0100 Subject: [PATCH 15/21] windows: fixed dealloc webviews logic, implemented takeScreenshot, added WebViewEnvironment and WebViewEnvironmentSettings classes --- .../lib/src/exchangeable_enum_generator.dart | 13 +- .../lib/in_app_webiew_example.screen.dart | 1 + flutter_inappwebview/example/lib/main.dart | 8 + .../Flutter/GeneratedPluginRegistrant.swift | 2 - flutter_inappwebview/example/pubspec.yaml | 16 +- .../src/in_app_webview/in_app_webview.dart | 3 + flutter_inappwebview/lib/src/main.dart | 1 + .../lib/src/webview_environment/main.dart | 1 + .../webview_environment.dart | 35 ++ flutter_inappwebview/pubspec.yaml | 11 +- .../platform_inappwebview_controller.dart | 1 + .../platform_inappwebview_widget.dart | 8 + .../lib/src/inappwebview_platform.dart | 21 ++ .../lib/src/main.dart | 1 + .../lib/src/types/compress_format.dart | 32 +- .../lib/src/types/compress_format.g.dart | 26 +- .../src/types/screenshot_configuration.dart | 31 +- .../src/types/screenshot_configuration.g.dart | 29 +- .../lib/src/webview_environment/main.dart | 2 + .../platform_webview_environment.dart | 106 ++++++ .../webview_environment_settings.dart | 25 ++ .../webview_environment_settings.g.dart | 70 ++++ .../pubspec.yaml | 2 +- .../example/pubspec.lock | 7 +- .../lib/src/cookie_manager.dart | 333 ++++-------------- .../find_interaction_controller.dart | 10 +- .../src/in_app_browser/in_app_browser.dart | 4 +- .../headless_in_app_webview.dart | 4 +- .../src/in_app_webview/in_app_webview.dart | 15 +- .../in_app_webview_controller.dart | 11 +- .../lib/src/inappwebview_platform.dart | 34 ++ .../lib/src/main.dart | 1 + .../src/web_message/web_message_channel.dart | 20 +- .../lib/src/web_message/web_message_port.dart | 6 +- .../lib/src/webview_environment/main.dart | 1 + .../webview_environment.dart | 102 ++++++ flutter_inappwebview_windows/pubspec.yaml | 3 +- .../windows/CMakeLists.txt | 14 + .../windows/cookie_manager.cpp | 120 +++++++ .../windows/cookie_manager.h | 36 ++ .../flutter_inappwebview_windows_plugin.cpp | 4 + .../flutter_inappwebview_windows_plugin.h | 4 + .../headless_in_app_webview.cpp | 10 +- .../headless_in_app_webview_manager.cpp | 13 +- .../windows/in_app_browser/in_app_browser.cpp | 14 +- .../windows/in_app_browser/in_app_browser.h | 1 + .../in_app_browser/in_app_browser_manager.cpp | 4 +- .../windows/in_app_webview/in_app_webview.cpp | 193 ++++++---- .../windows/in_app_webview/in_app_webview.h | 14 +- .../in_app_webview/in_app_webview_manager.cpp | 15 +- .../webview_channel_delegate.cpp | 10 + .../windows/types/rect.cpp | 25 ++ .../windows/types/rect.h | 27 ++ .../types/screenshot_configuration.cpp | 48 +++ .../windows/types/screenshot_configuration.h | 38 ++ .../windows/types/web_resource_error.cpp | 2 +- .../windows/types/web_resource_error.h | 4 +- .../windows/types/web_resource_response.cpp | 2 +- .../windows/types/web_resource_response.h | 4 +- .../windows/utils/flutter.h | 50 ++- .../windows/utils/string.h | 34 ++ .../webview_environment.cpp | 59 ++++ .../webview_environment/webview_environment.h | 30 ++ .../webview_environment_channel_delegate.cpp | 47 +++ .../webview_environment_channel_delegate.h | 25 ++ .../webview_environment_manager.cpp | 63 ++++ .../webview_environment_manager.h | 33 ++ .../webview_environment_settings.cpp | 27 ++ .../webview_environment_settings.h | 29 ++ 69 files changed, 1547 insertions(+), 448 deletions(-) create mode 100644 flutter_inappwebview/lib/src/webview_environment/main.dart create mode 100644 flutter_inappwebview/lib/src/webview_environment/webview_environment.dart create mode 100644 flutter_inappwebview_platform_interface/lib/src/webview_environment/main.dart create mode 100644 flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart create mode 100644 flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.dart create mode 100644 flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.g.dart create mode 100644 flutter_inappwebview_windows/lib/src/webview_environment/main.dart create mode 100644 flutter_inappwebview_windows/lib/src/webview_environment/webview_environment.dart create mode 100644 flutter_inappwebview_windows/windows/cookie_manager.cpp create mode 100644 flutter_inappwebview_windows/windows/cookie_manager.h create mode 100644 flutter_inappwebview_windows/windows/types/rect.cpp create mode 100644 flutter_inappwebview_windows/windows/types/rect.h create mode 100644 flutter_inappwebview_windows/windows/types/screenshot_configuration.cpp create mode 100644 flutter_inappwebview_windows/windows/types/screenshot_configuration.h create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment.cpp create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment.h create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.cpp create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.h create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.cpp create mode 100644 flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.h diff --git a/dev_packages/generators/lib/src/exchangeable_enum_generator.dart b/dev_packages/generators/lib/src/exchangeable_enum_generator.dart index c362e681..c7802cfa 100644 --- a/dev_packages/generators/lib/src/exchangeable_enum_generator.dart +++ b/dev_packages/generators/lib/src/exchangeable_enum_generator.dart @@ -140,6 +140,7 @@ class ExchangeableEnumGenerator []; var hasWebSupport = false; var webSupportValue = null; + var allPlatformsWithoutValue = true; if (platforms.isNotEmpty) { for (var platform in platforms) { final targetPlatformName = @@ -150,6 +151,9 @@ class ExchangeableEnumGenerator ? platformValueField.toIntValue() ?? "'${platformValueField.toStringValue()}'" : null; + if (allPlatformsWithoutValue && platformValue != null) { + allPlatformsWithoutValue = false; + } if (targetPlatformName == "web") { hasWebSupport = true; webSupportValue = platformValue; @@ -170,8 +174,13 @@ class ExchangeableEnumGenerator nativeValueBody += "return $defaultValue;"; nativeValueBody += "}"; - classBuffer.writeln( - "static final $fieldName = $extClassName._internalMultiPlatform($constantValue, $nativeValueBody);"); + if (!allPlatformsWithoutValue) { + classBuffer.writeln( + "static final $fieldName = $extClassName._internalMultiPlatform($constantValue, $nativeValueBody);"); + } else { + classBuffer.writeln( + "static const $fieldName = $extClassName._internal($constantValue, ${defaultValue ?? constantValue});"); + } } else { classBuffer.writeln( "static const $fieldName = $extClassName._internal($constantValue, $constantValue);"); diff --git a/flutter_inappwebview/example/lib/in_app_webiew_example.screen.dart b/flutter_inappwebview/example/lib/in_app_webiew_example.screen.dart index 79dc016f..0cd746dd 100755 --- a/flutter_inappwebview/example/lib/in_app_webiew_example.screen.dart +++ b/flutter_inappwebview/example/lib/in_app_webiew_example.screen.dart @@ -115,6 +115,7 @@ class _InAppWebViewExampleScreenState extends State { children: [ InAppWebView( key: webViewKey, + webViewEnvironment: webViewEnvironment, initialUrlRequest: URLRequest(url: WebUri('https://flutter.dev')), // initialUrlRequest: diff --git a/flutter_inappwebview/example/lib/main.dart b/flutter_inappwebview/example/lib/main.dart index d94bc1b5..881061dd 100755 --- a/flutter_inappwebview/example/lib/main.dart +++ b/flutter_inappwebview/example/lib/main.dart @@ -15,6 +15,7 @@ import 'package:pointer_interceptor/pointer_interceptor.dart'; // import 'package:permission_handler/permission_handler.dart'; final localhostServer = InAppLocalhostServer(documentRoot: 'assets'); +WebViewEnvironment? webViewEnvironment; Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -22,6 +23,13 @@ Future main() async { // await Permission.microphone.request(); // await Permission.storage.request(); + if (!kIsWeb && defaultTargetPlatform == TargetPlatform.windows) { + webViewEnvironment = await WebViewEnvironment.create(settings: + WebViewEnvironmentSettings( + userDataFolder: 'custom_path' + )); + } + if (!kIsWeb && defaultTargetPlatform == TargetPlatform.android) { await InAppWebViewController.setWebContentsDebuggingEnabled(kDebugMode); } diff --git a/flutter_inappwebview/example/macos/Flutter/GeneratedPluginRegistrant.swift b/flutter_inappwebview/example/macos/Flutter/GeneratedPluginRegistrant.swift index 959e5aa0..a1cdfd0c 100644 --- a/flutter_inappwebview/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/flutter_inappwebview/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,12 +5,10 @@ import FlutterMacOS import Foundation -import flutter_inappwebview_macos import path_provider_foundation import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/flutter_inappwebview/example/pubspec.yaml b/flutter_inappwebview/example/pubspec.yaml index 59aa16aa..dffe2a82 100755 --- a/flutter_inappwebview/example/pubspec.yaml +++ b/flutter_inappwebview/example/pubspec.yaml @@ -35,14 +35,14 @@ dependencies: dependency_overrides: flutter_inappwebview_platform_interface: path: ../../flutter_inappwebview_platform_interface - flutter_inappwebview_android: - path: ../../flutter_inappwebview_android - flutter_inappwebview_ios: - path: ../../flutter_inappwebview_ios - flutter_inappwebview_macos: - path: ../../flutter_inappwebview_macos - flutter_inappwebview_web: - path: ../../flutter_inappwebview_web + #flutter_inappwebview_android: + # path: ../../flutter_inappwebview_android + #flutter_inappwebview_ios: + # path: ../../flutter_inappwebview_ios + #flutter_inappwebview_macos: + # path: ../../flutter_inappwebview_macos + #flutter_inappwebview_web: + # path: ../../flutter_inappwebview_web flutter_inappwebview_windows: path: ../../flutter_inappwebview_windows diff --git a/flutter_inappwebview/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview/lib/src/in_app_webview/in_app_webview.dart index e2490bfc..b885aa3c 100755 --- a/flutter_inappwebview/lib/src/in_app_webview/in_app_webview.dart +++ b/flutter_inappwebview/lib/src/in_app_webview/in_app_webview.dart @@ -9,6 +9,7 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import '../webview_environment/webview_environment.dart'; import 'headless_in_app_webview.dart'; import 'in_app_webview_controller.dart'; import '../find_interaction/find_interaction_controller.dart'; @@ -42,6 +43,7 @@ class InAppWebView extends StatefulWidget { InAppWebViewKeepAlive? keepAlive, bool? preventGestureDelay, TextDirection? layoutDirection, + WebViewEnvironment? webViewEnvironment, @Deprecated('Use onGeolocationPermissionsHidePrompt instead') void Function(InAppWebViewController controller)? androidOnGeolocationPermissionsHidePrompt, @@ -310,6 +312,7 @@ class InAppWebView extends StatefulWidget { findInteractionController: findInteractionController?.platform, contextMenu: contextMenu, layoutDirection: layoutDirection, + webViewEnvironment: webViewEnvironment?.platform, onWebViewCreated: onWebViewCreated != null ? (controller) => onWebViewCreated.call(controller) : null, diff --git a/flutter_inappwebview/lib/src/main.dart b/flutter_inappwebview/lib/src/main.dart index 2b968fee..64e28fb3 100644 --- a/flutter_inappwebview/lib/src/main.dart +++ b/flutter_inappwebview/lib/src/main.dart @@ -15,3 +15,4 @@ export 'webview_asset_loader.dart'; export 'tracing_controller.dart'; export 'process_global_config.dart'; export 'in_app_localhost_server.dart'; +export 'webview_environment/main.dart'; diff --git a/flutter_inappwebview/lib/src/webview_environment/main.dart b/flutter_inappwebview/lib/src/webview_environment/main.dart new file mode 100644 index 00000000..ec25d6bb --- /dev/null +++ b/flutter_inappwebview/lib/src/webview_environment/main.dart @@ -0,0 +1 @@ +export 'webview_environment.dart'; \ No newline at end of file diff --git a/flutter_inappwebview/lib/src/webview_environment/webview_environment.dart b/flutter_inappwebview/lib/src/webview_environment/webview_environment.dart new file mode 100644 index 00000000..4b89d135 --- /dev/null +++ b/flutter_inappwebview/lib/src/webview_environment/webview_environment.dart @@ -0,0 +1,35 @@ +import 'dart:core'; + +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment} +class WebViewEnvironment { + /// Constructs a [WebViewEnvironment]. + /// + /// See [WebViewEnvironment.fromPlatformCreationParams] for setting parameters for + /// a specific platform. + WebViewEnvironment.fromPlatformCreationParams({ + required PlatformWebViewEnvironmentCreationParams params, + }) : this.fromPlatform(platform: PlatformWebViewEnvironment(params)); + + /// Constructs a [WebViewEnvironment] from a specific platform implementation. + WebViewEnvironment.fromPlatform({required this.platform}); + + /// Implementation of [PlatformWebViewEnvironment] for the current platform. + final PlatformWebViewEnvironment platform; + + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.id} + String get id => platform.id; + + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.settings} + WebViewEnvironmentSettings? get settings => platform.settings; + + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.create} + static Future create( + {WebViewEnvironmentSettings? settings}) async { + return WebViewEnvironment.fromPlatform(platform: await PlatformWebViewEnvironment.static().create(settings: settings)); + } + + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.dispose} + Future dispose() => platform.dispose(); +} \ No newline at end of file diff --git a/flutter_inappwebview/pubspec.yaml b/flutter_inappwebview/pubspec.yaml index ef480d4a..03b3d522 100755 --- a/flutter_inappwebview/pubspec.yaml +++ b/flutter_inappwebview/pubspec.yaml @@ -18,11 +18,12 @@ environment: dependencies: flutter: sdk: flutter - flutter_inappwebview_platform_interface: ^1.0.10 - flutter_inappwebview_android: ^1.0.12 - flutter_inappwebview_ios: ^1.0.13 - flutter_inappwebview_macos: ^1.0.11 - flutter_inappwebview_web: ^1.0.8 + flutter_inappwebview_platform_interface: #^1.0.10 + path: ../flutter_inappwebview_platform_interface + #flutter_inappwebview_android: ^1.0.12 + #flutter_inappwebview_ios: ^1.0.13 + #flutter_inappwebview_macos: ^1.0.11 + #flutter_inappwebview_web: ^1.0.8 dev_dependencies: flutter_test: diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart index eb01d7c5..077f9180 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart @@ -750,6 +750,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///- Android native WebView ///- iOS ([Official API - WKWebView.takeSnapshot](https://developer.apple.com/documentation/webkit/wkwebview/2873260-takesnapshot)) ///- MacOS ([Official API - WKWebView.takeSnapshot](https://developer.apple.com/documentation/webkit/wkwebview/2873260-takesnapshot)) + ///- Windows ///{@endtemplate} Future takeScreenshot( {ScreenshotConfiguration? screenshotConfiguration}) { diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart index c79090a6..0f3aa670 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart @@ -5,6 +5,7 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import '../inappwebview_platform.dart'; import '../types/disposable.dart'; +import '../webview_environment/platform_webview_environment.dart'; import 'in_app_webview_keep_alive.dart'; import 'platform_webview.dart'; import 'platform_headless_in_app_webview.dart'; @@ -24,6 +25,7 @@ class PlatformInAppWebViewWidgetCreationParams this.headlessWebView, this.keepAlive, this.preventGestureDelay, + this.webViewEnvironment, super.controllerFromPlatform, super.windowId, super.onWebViewCreated, @@ -181,6 +183,12 @@ class PlatformInAppWebViewWidgetCreationParams ///**Officially Supported Platforms/Implementations**: ///- iOS final bool? preventGestureDelay; + + ///Used create the [PlatformInAppWebViewWidget] using the specified environment. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows + final PlatformWebViewEnvironment? webViewEnvironment; } /// Interface for a platform implementation of a web view widget. diff --git a/flutter_inappwebview_platform_interface/lib/src/inappwebview_platform.dart b/flutter_inappwebview_platform_interface/lib/src/inappwebview_platform.dart index a216e5b9..683ffbdb 100644 --- a/flutter_inappwebview_platform_interface/lib/src/inappwebview_platform.dart +++ b/flutter_inappwebview_platform_interface/lib/src/inappwebview_platform.dart @@ -24,6 +24,7 @@ import 'platform_tracing_controller.dart'; import 'platform_webview_asset_loader.dart'; import 'platform_webview_feature.dart'; import 'in_app_localhost_server.dart'; +import 'webview_environment/platform_webview_environment.dart'; /// Interface for a platform implementation of a WebView. abstract class InAppWebViewPlatform extends PlatformInterface { @@ -430,4 +431,24 @@ abstract class InAppWebViewPlatform extends PlatformInterface { throw UnimplementedError( 'createPlatformChromeSafariBrowserStatic is not implemented on the current platform.'); } + + /// Creates a new [PlatformWebViewEnvironment]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewEnvironment] in `flutter_inappwebview` instead. + PlatformWebViewEnvironment createPlatformWebViewEnvironment( + PlatformWebViewEnvironmentCreationParams params, + ) { + throw UnimplementedError( + 'createPlatformWebViewEnvironment is not implemented on the current platform.'); + } + + /// Creates a new empty [PlatformWebViewEnvironment] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewEnvironment] in `flutter_inappwebview` instead. + PlatformWebViewEnvironment createPlatformWebViewEnvironmentStatic() { + throw UnimplementedError( + 'createPlatformWebViewEnvironmentStatic is not implemented on the current platform.'); + } } diff --git a/flutter_inappwebview_platform_interface/lib/src/main.dart b/flutter_inappwebview_platform_interface/lib/src/main.dart index 5b8d6cb7..bf9626ae 100644 --- a/flutter_inappwebview_platform_interface/lib/src/main.dart +++ b/flutter_inappwebview_platform_interface/lib/src/main.dart @@ -1,5 +1,6 @@ export 'inappwebview_platform.dart'; export 'types/main.dart'; +export 'webview_environment/main.dart'; export 'in_app_webview/main.dart'; export 'in_app_browser/main.dart'; export 'chrome_safari_browser/main.dart'; diff --git a/flutter_inappwebview_platform_interface/lib/src/types/compress_format.dart b/flutter_inappwebview_platform_interface/lib/src/types/compress_format.dart index f7967e24..23a59412 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/compress_format.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/compress_format.dart @@ -11,36 +11,48 @@ class CompressFormat_ { ///Compress to the `PNG` format. ///PNG is lossless, so `quality` is ignored. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform(), + EnumIOSPlatform(), + EnumMacOSPlatform(), + EnumWindowsPlatform(), + ]) static const PNG = const CompressFormat_._internal("PNG"); ///Compress to the `JPEG` format. ///Quality of `0` means compress for the smallest size. ///`100` means compress for max visual quality. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform(), + EnumIOSPlatform(), + EnumMacOSPlatform(), + EnumWindowsPlatform(), + ]) static const JPEG = const CompressFormat_._internal("JPEG"); ///Compress to the `WEBP` lossy format. ///Quality of `0` means compress for the smallest size. ///`100` means compress for max visual quality. - /// - ///**NOTE**: available only on Android. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform(), + EnumWindowsPlatform(), + ]) static const WEBP = const CompressFormat_._internal("WEBP"); ///Compress to the `WEBP` lossy format. ///Quality of `0` means compress for the smallest size. ///`100` means compress for max visual quality. - /// - ///**NOTE**: available only on Android. - /// - ///**NOTE for Android**: available on Android 30+. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform(available: '30'), + ]) static const WEBP_LOSSY = const CompressFormat_._internal("WEBP_LOSSY"); ///Compress to the `WEBP` lossless format. ///Quality refers to how much effort to put into compression. ///A value of `0` means to compress quickly, resulting in a relatively large file size. ///`100` means to spend more time compressing, resulting in a smaller file. - /// - ///**NOTE**: available only on Android. - /// - ///**NOTE for Android**: available on Android 30+. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform(available: '30'), + ]) static const WEBP_LOSSLESS = const CompressFormat_._internal("WEBP_LOSSLESS"); } diff --git a/flutter_inappwebview_platform_interface/lib/src/types/compress_format.g.dart b/flutter_inappwebview_platform_interface/lib/src/types/compress_format.g.dart index bad4b339..befe838f 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/compress_format.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/compress_format.g.dart @@ -19,17 +19,31 @@ class CompressFormat { ///Compress to the `JPEG` format. ///Quality of `0` means compress for the smallest size. ///`100` means compress for max visual quality. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///- Windows static const JPEG = CompressFormat._internal('JPEG', 'JPEG'); ///Compress to the `PNG` format. ///PNG is lossless, so `quality` is ignored. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///- Windows static const PNG = CompressFormat._internal('PNG', 'PNG'); ///Compress to the `WEBP` lossy format. ///Quality of `0` means compress for the smallest size. ///`100` means compress for max visual quality. /// - ///**NOTE**: available only on Android. + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- Windows static const WEBP = CompressFormat._internal('WEBP', 'WEBP'); ///Compress to the `WEBP` lossless format. @@ -37,9 +51,8 @@ class CompressFormat { ///A value of `0` means to compress quickly, resulting in a relatively large file size. ///`100` means to spend more time compressing, resulting in a smaller file. /// - ///**NOTE**: available only on Android. - /// - ///**NOTE for Android**: available on Android 30+. + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView 30+ static const WEBP_LOSSLESS = CompressFormat._internal('WEBP_LOSSLESS', 'WEBP_LOSSLESS'); @@ -47,9 +60,8 @@ class CompressFormat { ///Quality of `0` means compress for the smallest size. ///`100` means compress for max visual quality. /// - ///**NOTE**: available only on Android. - /// - ///**NOTE for Android**: available on Android 30+. + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView 30+ static const WEBP_LOSSY = CompressFormat._internal('WEBP_LOSSY', 'WEBP_LOSSY'); diff --git a/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.dart b/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.dart index ce033a58..8d364fb0 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.dart @@ -12,6 +12,12 @@ class ScreenshotConfiguration_ { ///The portion of your web view to capture, specified as a rectangle in the view’s coordinate system. ///The default value of this property is `null`, which captures everything in the view’s bounds rectangle. ///If you specify a custom rectangle, it must lie within the bounds rectangle of the `WebView` object. + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform(), + WindowsPlatform() + ]) InAppWebViewRect_? rect; ///The width of the captured image, in points. @@ -19,14 +25,31 @@ class ScreenshotConfiguration_ { ///The web view maintains the aspect ratio of the captured content, but scales it to match the width you specify. /// ///The default value of this property is `null`, which returns an image whose size matches the original size of the captured rectangle. + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform() + ]) double? snapshotWidth; ///The compression format of the captured image. ///The default value is [CompressFormat.PNG]. + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform(), + WindowsPlatform() + ]) CompressFormat_ compressFormat; ///Hint to the compressor, `0-100`. The value is interpreted differently depending on the [CompressFormat]. ///[CompressFormat.PNG] is lossless, so this value is ignored. + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform(), + WindowsPlatform() + ]) int quality; ///Use [afterScreenUpdates] instead. @@ -36,10 +59,10 @@ class ScreenshotConfiguration_ { ///A Boolean value that indicates whether to take the snapshot after incorporating any pending screen updates. ///The default value of this property is `true`, which causes the web view to incorporate any recent changes to the view’s content and then generate the snapshot. ///If you change the value to `false`, the `WebView` takes the snapshot immediately, and before incorporating any new changes. - /// - ///**NOTE**: available only on iOS. - /// - ///**NOTE for iOS**: Available from iOS 13.0+. + @SupportedPlatforms(platforms: [ + IOSPlatform(available: '13.0'), + MacOSPlatform(available: '10.15'), + ]) bool afterScreenUpdates; @ExchangeableObjectConstructor() diff --git a/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.g.dart b/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.g.dart index 1a22c107..460c7890 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/screenshot_configuration.g.dart @@ -12,13 +12,19 @@ class ScreenshotConfiguration { ///The default value of this property is `true`, which causes the web view to incorporate any recent changes to the view’s content and then generate the snapshot. ///If you change the value to `false`, the `WebView` takes the snapshot immediately, and before incorporating any new changes. /// - ///**NOTE**: available only on iOS. - /// - ///**NOTE for iOS**: Available from iOS 13.0+. + ///**Officially Supported Platforms/Implementations**: + ///- iOS 13.0+ + ///- MacOS 10.15+ bool afterScreenUpdates; ///The compression format of the captured image. ///The default value is [CompressFormat.PNG]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///- Windows CompressFormat compressFormat; ///Use [afterScreenUpdates] instead. @@ -27,11 +33,23 @@ class ScreenshotConfiguration { ///Hint to the compressor, `0-100`. The value is interpreted differently depending on the [CompressFormat]. ///[CompressFormat.PNG] is lossless, so this value is ignored. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///- Windows int quality; ///The portion of your web view to capture, specified as a rectangle in the view’s coordinate system. ///The default value of this property is `null`, which captures everything in the view’s bounds rectangle. ///If you specify a custom rectangle, it must lie within the bounds rectangle of the `WebView` object. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS + ///- Windows InAppWebViewRect? rect; ///The width of the captured image, in points. @@ -39,6 +57,11 @@ class ScreenshotConfiguration { ///The web view maintains the aspect ratio of the captured content, but scales it to match the width you specify. /// ///The default value of this property is `null`, which returns an image whose size matches the original size of the captured rectangle. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView + ///- iOS + ///- MacOS double? snapshotWidth; ScreenshotConfiguration( {this.rect, diff --git a/flutter_inappwebview_platform_interface/lib/src/webview_environment/main.dart b/flutter_inappwebview_platform_interface/lib/src/webview_environment/main.dart new file mode 100644 index 00000000..09b2d039 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/webview_environment/main.dart @@ -0,0 +1,2 @@ +export 'platform_webview_environment.dart'; +export 'webview_environment_settings.dart' show WebViewEnvironmentSettings; \ No newline at end of file diff --git a/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart b/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart new file mode 100644 index 00000000..9bbe5bb5 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart @@ -0,0 +1,106 @@ +import 'package:flutter/foundation.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import '../debug_logging_settings.dart'; +import '../inappwebview_platform.dart'; +import '../types/disposable.dart'; +import 'webview_environment_settings.dart'; + +/// Object specifying creation parameters for creating a [PlatformWebViewEnvironment]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +@immutable +class PlatformWebViewEnvironmentCreationParams { + /// Used by the platform implementation to create a new [PlatformWebViewEnvironment]. + const PlatformWebViewEnvironmentCreationParams({this.settings}); + + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.settings} + final WebViewEnvironmentSettings? settings; +} + +///Controls a WebView Environment used by WebView instances. +/// +///**Officially Supported Platforms/Implementations**: +///- Windows +abstract class PlatformWebViewEnvironment extends PlatformInterface + implements Disposable { + ///Debug settings used by [PlatformWebViewEnvironment]. + static DebugLoggingSettings debugLoggingSettings = DebugLoggingSettings( + maxLogMessageLength: 1000 + ); + + /// Creates a new [PlatformInAppWebViewController] + factory PlatformWebViewEnvironment( + PlatformWebViewEnvironmentCreationParams params) { + assert( + InAppWebViewPlatform.instance != null, + 'A platform implementation for `flutter_inappwebview` has not been set. Please ' + 'ensure that an implementation of `InAppWebViewPlatform` has been set to ' + '`InAppWebViewPlatform.instance` before use. For unit testing, ' + '`InAppWebViewPlatform.instance` can be set with your own test implementation.', + ); + final PlatformWebViewEnvironment webViewEnvironment = + InAppWebViewPlatform.instance! + .createPlatformWebViewEnvironment(params); + PlatformInterface.verify(webViewEnvironment, _token); + return webViewEnvironment; + } + + /// Creates a new [PlatformWebViewEnvironment] to access static methods. + factory PlatformWebViewEnvironment.static() { + assert( + InAppWebViewPlatform.instance != null, + 'A platform implementation for `flutter_inappwebview` has not been set. Please ' + 'ensure that an implementation of `InAppWebViewPlatform` has been set to ' + '`InAppWebViewPlatform.instance` before use. For unit testing, ' + '`InAppWebViewPlatform.instance` can be set with your own test implementation.', + ); + final PlatformWebViewEnvironment webViewEnvironment = + InAppWebViewPlatform.instance! + .createPlatformWebViewEnvironmentStatic(); + PlatformInterface.verify(webViewEnvironment, _token); + return webViewEnvironment; + } + + /// Used by the platform implementation to create a new [PlatformWebViewEnvironment]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformWebViewEnvironment.implementation(this.params) + : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformWebViewEnvironment]. + final PlatformWebViewEnvironmentCreationParams params; + + ///{@template flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.id} + /// WebView Environment ID. + ///{@endtemplate} + String get id => + throw UnimplementedError('id is not implemented on the current platform'); + + ///{@template flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.settings} + /// WebView Environment settings. + ///{@endtemplate} + WebViewEnvironmentSettings? get settings => params.settings; + + ///{@template flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.create} + ///Initializes the [PlatformWebViewEnvironment] using [settings]. + ///{@endtemplate} + Future create( + {WebViewEnvironmentSettings? settings}) { + throw UnimplementedError( + 'create is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.dispose} + ///Disposes the WebView Environment reference. + ///{@endtemplate} + Future dispose() { + throw UnimplementedError( + 'dispose is not implemented on the current platform'); + } +} \ No newline at end of file diff --git a/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.dart b/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.dart new file mode 100644 index 00000000..6c14a6d4 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.dart @@ -0,0 +1,25 @@ +import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; + +import 'platform_webview_environment.dart'; + +part 'webview_environment_settings.g.dart'; + +///This class represents all the [PlatformWebViewEnvironment] settings available. +@ExchangeableObject(copyMethod: true) +class WebViewEnvironmentSettings_ { + final String? browserExecutableFolder; + final String? userDataFolder; + final String? additionalBrowserArguments; + final bool? allowSingleSignOnUsingOSPrimaryAccount; + final String? language; + final String? targetCompatibleBrowserVersion; + + WebViewEnvironmentSettings_({ + this.browserExecutableFolder, + this.userDataFolder, + this.additionalBrowserArguments, + this.allowSingleSignOnUsingOSPrimaryAccount, + this.language, + this.targetCompatibleBrowserVersion + }); +} \ No newline at end of file diff --git a/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.g.dart b/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.g.dart new file mode 100644 index 00000000..c89b9f53 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.g.dart @@ -0,0 +1,70 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'webview_environment_settings.dart'; + +// ************************************************************************** +// ExchangeableObjectGenerator +// ************************************************************************** + +///This class represents all the [PlatformWebViewEnvironment] settings available. +class WebViewEnvironmentSettings { + final String? additionalBrowserArguments; + final bool? allowSingleSignOnUsingOSPrimaryAccount; + final String? browserExecutableFolder; + final String? language; + final String? targetCompatibleBrowserVersion; + final String? userDataFolder; + WebViewEnvironmentSettings( + {this.additionalBrowserArguments, + this.allowSingleSignOnUsingOSPrimaryAccount, + this.browserExecutableFolder, + this.language, + this.targetCompatibleBrowserVersion, + this.userDataFolder}); + + ///Gets a possible [WebViewEnvironmentSettings] instance from a [Map] value. + static WebViewEnvironmentSettings? fromMap(Map? map) { + if (map == null) { + return null; + } + final instance = WebViewEnvironmentSettings( + additionalBrowserArguments: map['additionalBrowserArguments'], + allowSingleSignOnUsingOSPrimaryAccount: + map['allowSingleSignOnUsingOSPrimaryAccount'], + browserExecutableFolder: map['browserExecutableFolder'], + language: map['language'], + targetCompatibleBrowserVersion: map['targetCompatibleBrowserVersion'], + userDataFolder: map['userDataFolder'], + ); + return instance; + } + + ///Converts instance to a map. + Map toMap() { + return { + "additionalBrowserArguments": additionalBrowserArguments, + "allowSingleSignOnUsingOSPrimaryAccount": + allowSingleSignOnUsingOSPrimaryAccount, + "browserExecutableFolder": browserExecutableFolder, + "language": language, + "targetCompatibleBrowserVersion": targetCompatibleBrowserVersion, + "userDataFolder": userDataFolder, + }; + } + + ///Converts instance to a map. + Map toJson() { + return toMap(); + } + + ///Returns a copy of WebViewEnvironmentSettings. + WebViewEnvironmentSettings copy() { + return WebViewEnvironmentSettings.fromMap(toMap()) ?? + WebViewEnvironmentSettings(); + } + + @override + String toString() { + return 'WebViewEnvironmentSettings{additionalBrowserArguments: $additionalBrowserArguments, allowSingleSignOnUsingOSPrimaryAccount: $allowSingleSignOnUsingOSPrimaryAccount, browserExecutableFolder: $browserExecutableFolder, language: $language, targetCompatibleBrowserVersion: $targetCompatibleBrowserVersion, userDataFolder: $userDataFolder}'; + } +} diff --git a/flutter_inappwebview_platform_interface/pubspec.yaml b/flutter_inappwebview_platform_interface/pubspec.yaml index bbb0f0a9..a3d59800 100644 --- a/flutter_inappwebview_platform_interface/pubspec.yaml +++ b/flutter_inappwebview_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_inappwebview_platform_interface description: A common platform interface for the flutter_inappwebview plugin. -version: 1.0.10 +version: 1.0.11 homepage: https://inappwebview.dev/ repository: https://github.com/pichillilorenzo/flutter_inappwebview/tree/master/flutter_inappwebview_platform_interface issue_tracker: https://github.com/pichillilorenzo/flutter_inappwebview/issues diff --git a/flutter_inappwebview_windows/example/pubspec.lock b/flutter_inappwebview_windows/example/pubspec.lock index d24b5eee..847582b6 100644 --- a/flutter_inappwebview_windows/example/pubspec.lock +++ b/flutter_inappwebview_windows/example/pubspec.lock @@ -86,10 +86,9 @@ packages: flutter_inappwebview_platform_interface: dependency: transitive description: - name: flutter_inappwebview_platform_interface - sha256: "545fd4c25a07d2775f7d5af05a979b2cac4fbf79393b0a7f5d33ba39ba4f6187" - url: "https://pub.dev" - source: hosted + path: "../../flutter_inappwebview_platform_interface" + relative: true + source: path version: "1.0.10" flutter_inappwebview_windows: dependency: "direct main" diff --git a/flutter_inappwebview_windows/lib/src/cookie_manager.dart b/flutter_inappwebview_windows/lib/src/cookie_manager.dart index a95dc409..81903ca7 100644 --- a/flutter_inappwebview_windows/lib/src/cookie_manager.dart +++ b/flutter_inappwebview_windows/lib/src/cookie_manager.dart @@ -5,56 +5,54 @@ import 'package:flutter/services.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; -import 'in_app_webview/headless_in_app_webview.dart'; -import 'platform_util.dart'; - -/// Object specifying creation parameters for creating a [MacOSCookieManager]. +/// Object specifying creation parameters for creating a [WindowsCookieManager]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformCookieManagerCreationParams] for /// more information. @immutable -class MacOSCookieManagerCreationParams +class WindowsCookieManagerCreationParams extends PlatformCookieManagerCreationParams { - /// Creates a new [MacOSCookieManagerCreationParams] instance. - const MacOSCookieManagerCreationParams( - // This parameter prevents breaking changes later. - // ignore: avoid_unused_constructor_parameters - PlatformCookieManagerCreationParams params, - ) : super(); + /// Creates a new [WindowsCookieManagerCreationParams] instance. + const WindowsCookieManagerCreationParams( + // This parameter prevents breaking changes later. + // ignore: avoid_unused_constructor_parameters + PlatformCookieManagerCreationParams params, + ) : super(); - /// Creates a [MacOSCookieManagerCreationParams] instance based on [PlatformCookieManagerCreationParams]. - factory MacOSCookieManagerCreationParams.fromPlatformCookieManagerCreationParams( + /// Creates a [WindowsCookieManagerCreationParams] instance based on [PlatformCookieManagerCreationParams]. + factory WindowsCookieManagerCreationParams.fromPlatformCookieManagerCreationParams( PlatformCookieManagerCreationParams params) { - return MacOSCookieManagerCreationParams(params); + return WindowsCookieManagerCreationParams(params); } } ///{@macro flutter_inappwebview_platform_interface.PlatformCookieManager} -class MacOSCookieManager extends PlatformCookieManager with ChannelController { - /// Creates a new [MacOSCookieManager]. - MacOSCookieManager(PlatformCookieManagerCreationParams params) +class WindowsCookieManager extends PlatformCookieManager + with ChannelController { + /// Creates a new [WindowsCookieManager]. + WindowsCookieManager(PlatformCookieManagerCreationParams params) : super.implementation( - params is MacOSCookieManagerCreationParams - ? params - : MacOSCookieManagerCreationParams - .fromPlatformCookieManagerCreationParams(params), - ) { + params is WindowsCookieManagerCreationParams + ? params + : WindowsCookieManagerCreationParams + .fromPlatformCookieManagerCreationParams(params), + ) { channel = const MethodChannel( 'com.pichillilorenzo/flutter_inappwebview_cookiemanager'); handler = handleMethod; initMethodCallHandler(); } - static MacOSCookieManager? _instance; + static WindowsCookieManager? _instance; - ///Gets the [MacOSCookieManager] shared instance. - static MacOSCookieManager instance() { + ///Gets the [WindowsCookieManager] shared instance. + static WindowsCookieManager instance() { return (_instance != null) ? _instance! : _init(); } - static MacOSCookieManager _init() { - _instance = MacOSCookieManager(MacOSCookieManagerCreationParams( + static WindowsCookieManager _init() { + _instance = WindowsCookieManager(WindowsCookieManagerCreationParams( const PlatformCookieManagerCreationParams())); return _instance!; } @@ -64,40 +62,23 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { @override Future setCookie( {required WebUri url, - required String name, - required String value, - String path = "/", - String? domain, - int? expiresDate, - int? maxAge, - bool? isSecure, - bool? isHttpOnly, - HTTPCookieSameSitePolicy? sameSite, - @Deprecated("Use webViewController instead") - PlatformInAppWebViewController? iosBelow11WebViewController, - PlatformInAppWebViewController? webViewController}) async { - webViewController = webViewController ?? iosBelow11WebViewController; - + required String name, + required String value, + String path = "/", + String? domain, + int? expiresDate, + int? maxAge, + bool? isSecure, + bool? isHttpOnly, + HTTPCookieSameSitePolicy? sameSite, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { assert(url.toString().isNotEmpty); assert(name.isNotEmpty); assert(value.isNotEmpty); assert(path.isNotEmpty); - if (await _shouldUseJavascript()) { - await _setCookieWithJavaScript( - url: url, - name: name, - value: value, - domain: domain, - path: path, - expiresDate: expiresDate, - maxAge: maxAge, - isSecure: isSecure, - sameSite: sameSite, - webViewController: webViewController); - return true; - } - Map args = {}; args.putIfAbsent('url', () => url.toString()); args.putIfAbsent('name', () => name); @@ -113,72 +94,14 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { return await channel?.invokeMethod('setCookie', args) ?? false; } - Future _setCookieWithJavaScript( - {required WebUri url, - required String name, - required String value, - String path = "/", - String? domain, - int? expiresDate, - int? maxAge, - bool? isSecure, - HTTPCookieSameSitePolicy? sameSite, - PlatformInAppWebViewController? webViewController}) async { - var cookieValue = name + "=" + value + "; Path=" + path; - - if (domain != null) cookieValue += "; Domain=" + domain; - - if (expiresDate != null) - cookieValue += "; Expires=" + await _getCookieExpirationDate(expiresDate); - - if (maxAge != null) cookieValue += "; Max-Age=" + maxAge.toString(); - - if (isSecure != null && isSecure) cookieValue += "; Secure"; - - if (sameSite != null) - cookieValue += "; SameSite=" + sameSite.toNativeValue(); - - cookieValue += ";"; - - if (webViewController != null) { - final javaScriptEnabled = - (await webViewController.getSettings())?.javaScriptEnabled ?? false; - if (javaScriptEnabled) { - await webViewController.evaluateJavascript( - source: 'document.cookie="$cookieValue"'); - return; - } - } - - final setCookieCompleter = Completer(); - final headlessWebView = - WindowsHeadlessInAppWebView(WindowsHeadlessInAppWebViewCreationParams( - initialUrlRequest: URLRequest(url: url), - onLoadStop: (controller, url) async { - await controller.evaluateJavascript( - source: 'document.cookie="$cookieValue"'); - setCookieCompleter.complete(); - })); - await headlessWebView.run(); - await setCookieCompleter.future; - await headlessWebView.dispose(); - } - @override Future> getCookies( {required WebUri url, - @Deprecated("Use webViewController instead") - PlatformInAppWebViewController? iosBelow11WebViewController, - PlatformInAppWebViewController? webViewController}) async { + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { assert(url.toString().isNotEmpty); - webViewController = webViewController ?? iosBelow11WebViewController; - - if (await _shouldUseJavascript()) { - return await _getCookiesWithJavaScript( - url: url, webViewController: webViewController); - } - List cookies = []; Map args = {}; @@ -195,7 +118,7 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { isSessionOnly: cookieMap["isSessionOnly"], domain: cookieMap["domain"], sameSite: - HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), + HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), isSecure: cookieMap["isSecure"], isHttpOnly: cookieMap["isHttpOnly"], path: cookieMap["path"])); @@ -203,84 +126,16 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { return cookies; } - Future> _getCookiesWithJavaScript( - {required WebUri url, - PlatformInAppWebViewController? webViewController}) async { - assert(url.toString().isNotEmpty); - - List cookies = []; - - if (webViewController != null) { - final javaScriptEnabled = - (await webViewController.getSettings())?.javaScriptEnabled ?? false; - if (javaScriptEnabled) { - List documentCookies = (await webViewController - .evaluateJavascript(source: 'document.cookie') as String) - .split(';') - .map((documentCookie) => documentCookie.trim()) - .toList(); - documentCookies.forEach((documentCookie) { - List cookie = documentCookie.split('='); - if (cookie.length > 1) { - cookies.add(Cookie( - name: cookie[0], - value: cookie[1], - )); - } - }); - return cookies; - } - } - - final pageLoaded = Completer(); - final headlessWebView = - WindowsHeadlessInAppWebView(WindowsHeadlessInAppWebViewCreationParams( - initialUrlRequest: URLRequest(url: url), - onLoadStop: (controller, url) async { - pageLoaded.complete(); - }, - )); - await headlessWebView.run(); - await pageLoaded.future; - - List documentCookies = (await headlessWebView.webViewController! - .evaluateJavascript(source: 'document.cookie') as String) - .split(';') - .map((documentCookie) => documentCookie.trim()) - .toList(); - documentCookies.forEach((documentCookie) { - List cookie = documentCookie.split('='); - if (cookie.length > 1) { - cookies.add(Cookie( - name: cookie[0], - value: cookie[1], - )); - } - }); - await headlessWebView.dispose(); - return cookies; - } - @override Future getCookie( {required WebUri url, - required String name, - @Deprecated("Use webViewController instead") - PlatformInAppWebViewController? iosBelow11WebViewController, - PlatformInAppWebViewController? webViewController}) async { + required String name, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { assert(url.toString().isNotEmpty); assert(name.isNotEmpty); - webViewController = webViewController ?? iosBelow11WebViewController; - - if (await _shouldUseJavascript()) { - List cookies = await _getCookiesWithJavaScript( - url: url, webViewController: webViewController); - return cookies - .cast() - .firstWhere((cookie) => cookie!.name == name, orElse: () => null); - } - Map args = {}; args.putIfAbsent('url', () => url.toString()); List cookies = @@ -307,29 +162,15 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { @override Future deleteCookie( {required WebUri url, - required String name, - String path = "/", - String? domain, - @Deprecated("Use webViewController instead") - PlatformInAppWebViewController? iosBelow11WebViewController, - PlatformInAppWebViewController? webViewController}) async { + required String name, + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { assert(url.toString().isNotEmpty); assert(name.isNotEmpty); - webViewController = webViewController ?? iosBelow11WebViewController; - - if (await _shouldUseJavascript()) { - await _setCookieWithJavaScript( - url: url, - name: name, - value: "", - path: path, - domain: domain, - maxAge: -1, - webViewController: webViewController); - return true; - } - Map args = {}; args.putIfAbsent('url', () => url.toString()); args.putIfAbsent('name', () => name); @@ -341,31 +182,13 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { @override Future deleteCookies( {required WebUri url, - String path = "/", - String? domain, - @Deprecated("Use webViewController instead") - PlatformInAppWebViewController? iosBelow11WebViewController, - PlatformInAppWebViewController? webViewController}) async { + String path = "/", + String? domain, + @Deprecated("Use webViewController instead") + PlatformInAppWebViewController? iosBelow11WebViewController, + PlatformInAppWebViewController? webViewController}) async { assert(url.toString().isNotEmpty); - webViewController = webViewController ?? iosBelow11WebViewController; - - if (await _shouldUseJavascript()) { - List cookies = await _getCookiesWithJavaScript( - url: url, webViewController: webViewController); - for (var i = 0; i < cookies.length; i++) { - await _setCookieWithJavaScript( - url: url, - name: cookies[i].name, - value: "", - path: path, - domain: domain, - maxAge: -1, - webViewController: webViewController); - } - return true; - } - Map args = {}; args.putIfAbsent('url', () => url.toString()); args.putIfAbsent('domain', () => domain); @@ -380,46 +203,10 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { } @override - Future> getAllCookies() async { - List cookies = []; - + Future removeSessionCookies() async { Map args = {}; - List cookieListMap = - await channel?.invokeMethod('getAllCookies', args) ?? []; - cookieListMap = cookieListMap.cast>(); - - cookieListMap.forEach((cookieMap) { - cookies.add(Cookie( - name: cookieMap["name"], - value: cookieMap["value"], - expiresDate: cookieMap["expiresDate"], - isSessionOnly: cookieMap["isSessionOnly"], - domain: cookieMap["domain"], - sameSite: - HTTPCookieSameSitePolicy.fromNativeValue(cookieMap["sameSite"]), - isSecure: cookieMap["isSecure"], - isHttpOnly: cookieMap["isHttpOnly"], - path: cookieMap["path"])); - }); - return cookies; - } - - Future _getCookieExpirationDate(int expiresDate) async { - var platformUtil = PlatformUtil.instance(); - var dateTime = DateTime.fromMillisecondsSinceEpoch(expiresDate).toUtc(); - return !kIsWeb - ? await platformUtil.formatDate( - date: dateTime, - format: 'EEE, dd MMM yyyy hh:mm:ss z', - locale: 'en_US', - timezone: 'GMT') - : await platformUtil.getWebCookieExpirationDate(date: dateTime); - } - - Future _shouldUseJavascript() async { - final platformUtil = PlatformUtil.instance(); - final systemVersion = await platformUtil.getSystemVersion(); - return systemVersion.compareTo("11") == -1; + return await channel?.invokeMethod('removeSessionCookies', args) ?? + false; } @override @@ -428,6 +215,6 @@ class MacOSCookieManager extends PlatformCookieManager with ChannelController { } } -extension InternalCookieManager on MacOSCookieManager { +extension InternalCookieManager on WindowsCookieManager { get handleMethod => _handleMethod; } diff --git a/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart b/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart index ca291cbc..9835059a 100644 --- a/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart +++ b/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart @@ -2,7 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; -/// Object specifying creation parameters for creating a [MacOSFindInteractionController]. +/// Object specifying creation parameters for creating a [WindowsFindInteractionController]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformFindInteractionControllerCreationParams] for @@ -25,10 +25,10 @@ class MacOSFindInteractionControllerCreationParams } ///{@macro flutter_inappwebview_platform_interface.PlatformFindInteractionController} -class MacOSFindInteractionController extends PlatformFindInteractionController +class WindowsFindInteractionController extends PlatformFindInteractionController with ChannelController { - /// Constructs a [MacOSFindInteractionController]. - MacOSFindInteractionController( + /// Constructs a [WindowsFindInteractionController]. + WindowsFindInteractionController( PlatformFindInteractionControllerCreationParams params) : super.implementation( params is MacOSFindInteractionControllerCreationParams @@ -114,7 +114,7 @@ class MacOSFindInteractionController extends PlatformFindInteractionController } } -extension InternalFindInteractionController on MacOSFindInteractionController { +extension InternalFindInteractionController on WindowsFindInteractionController { void init(dynamic id) { channel = MethodChannel( 'com.pichillilorenzo/flutter_inappwebview_find_interaction_$id'); diff --git a/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart b/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart index e37a3068..4c1f892d 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart @@ -32,13 +32,13 @@ class WindowsInAppBrowserCreationParams contextMenu: params.contextMenu, pullToRefreshController: params.pullToRefreshController, findInteractionController: - params.findInteractionController as MacOSFindInteractionController?, + params.findInteractionController as WindowsFindInteractionController?, initialUserScripts: params.initialUserScripts, windowId: params.windowId); } @override - final MacOSFindInteractionController? findInteractionController; + final WindowsFindInteractionController? findInteractionController; } ///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser} diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart index 843b144a..0f6299a7 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart @@ -238,10 +238,10 @@ class WindowsHeadlessInAppWebViewCreationParams initialUserScripts: params.initialUserScripts, pullToRefreshController: params.pullToRefreshController, findInteractionController: params.findInteractionController - as MacOSFindInteractionController?); + as WindowsFindInteractionController?); @override - final MacOSFindInteractionController? findInteractionController; + final WindowsFindInteractionController? findInteractionController; } ///{@macro flutter_inappwebview_platform_interface.PlatformHeadlessInAppWebView} diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart index 1a35ae3b..df3d744a 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart @@ -23,6 +23,7 @@ class WindowsInAppWebViewWidgetCreationParams super.keepAlive, super.preventGestureDelay, super.windowId, + super.webViewEnvironment, super.onWebViewCreated, super.onLoadStart, super.onLoadStop, @@ -145,6 +146,7 @@ class WindowsInAppWebViewWidgetCreationParams keepAlive: params.keepAlive, preventGestureDelay: params.preventGestureDelay, windowId: params.windowId, + webViewEnvironment: params.webViewEnvironment, onWebViewCreated: params.onWebViewCreated, onLoadStart: params.onLoadStart, onLoadStop: params.onLoadStop, @@ -248,10 +250,10 @@ class WindowsInAppWebViewWidgetCreationParams initialUserScripts: params.initialUserScripts, pullToRefreshController: params.pullToRefreshController, findInteractionController: params.findInteractionController - as MacOSFindInteractionController?); + as WindowsFindInteractionController?); @override - final MacOSFindInteractionController? findInteractionController; + final WindowsFindInteractionController? findInteractionController; } ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} @@ -267,12 +269,12 @@ class WindowsInAppWebViewWidget extends PlatformInAppWebViewWidget { .fromPlatformInAppWebViewWidgetCreationParams(params), ); - WindowsInAppWebViewWidgetCreationParams get _macosParams => + WindowsInAppWebViewWidgetCreationParams get _windowsParams => params as WindowsInAppWebViewWidgetCreationParams; WindowsInAppWebViewController? _controller; - WindowsHeadlessInAppWebView? get _macosHeadlessInAppWebView => + WindowsHeadlessInAppWebView? get _windowsHeadlessInAppWebView => params.headlessWebView as WindowsHeadlessInAppWebView?; @override @@ -316,6 +318,7 @@ class WindowsInAppWebViewWidget extends PlatformInAppWebViewWidget { 'initialUserScripts': params.initialUserScripts?.map((e) => e.toMap()).toList() ?? [], 'keepAliveId': params.keepAlive?.id, + 'webViewEnvironmentId': params.webViewEnvironment?.id, }, ); } @@ -326,11 +329,11 @@ class WindowsInAppWebViewWidget extends PlatformInAppWebViewWidget { viewId = params.headlessWebView?.id; } viewId = params.keepAlive?.id ?? viewId ?? id; - _macosHeadlessInAppWebView?.internalDispose(); + _windowsHeadlessInAppWebView?.internalDispose(); _controller = WindowsInAppWebViewController( PlatformInAppWebViewControllerCreationParams( id: viewId, webviewParams: params)); - _macosParams.findInteractionController?.init(viewId); + _windowsParams.findInteractionController?.init(viewId); debugLog( className: runtimeType.toString(), id: viewId?.toString(), diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart index 020d109b..b6373a8b 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart @@ -74,7 +74,7 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController }; Set _webMessageListenerObjNames = Set(); Map _injectedScriptsFromURL = {}; - Set _webMessageChannels = Set(); + Set _webMessageChannels = Set(); Set _webMessageListeners = Set(); // static map that contains the properties to be saved and restored for keep alive feature @@ -187,7 +187,7 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController _userScripts = props.userScripts; _webMessageListenerObjNames = props.webMessageListenerObjNames; _webMessageChannels = - props.webMessageChannels as Set; + props.webMessageChannels as Set; _webMessageListeners = props.webMessageListeners as Set; } @@ -2007,7 +2007,8 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController Map args = {}; args.putIfAbsent( 'screenshotConfiguration', () => screenshotConfiguration?.toMap()); - return await channel?.invokeMethod('takeScreenshot', args); + final base64 = await channel?.invokeMethod('takeScreenshot', args); + return base64 != null ? base64Decode(base64) : null; } @override @@ -2460,12 +2461,12 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController } @override - Future createWebMessageChannel() async { + Future createWebMessageChannel() async { Map args = {}; Map? result = (await channel?.invokeMethod('createWebMessageChannel', args)) ?.cast(); - final webMessageChannel = MacOSWebMessageChannel.static().fromMap(result); + final webMessageChannel = WindowsWebMessageChannel.static().fromMap(result); if (webMessageChannel != null) { _webMessageChannels.add(webMessageChannel); } diff --git a/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart b/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart index 3cbfaa80..93ffb9be 100644 --- a/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart +++ b/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart @@ -1,9 +1,11 @@ import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'cookie_manager.dart'; import 'in_app_browser/in_app_browser.dart'; import 'in_app_webview/in_app_webview.dart'; import 'in_app_webview/in_app_webview_controller.dart'; import 'in_app_webview/headless_in_app_webview.dart'; +import 'webview_environment/webview_environment.dart'; /// Implementation of [InAppWebViewPlatform] using the WebKit API. class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { @@ -12,6 +14,18 @@ class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { InAppWebViewPlatform.instance = WindowsInAppWebViewPlatform(); } + /// Creates a new [WindowsCookieManager]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [CookieManager] in `flutter_inappwebview` instead. + @override + WindowsCookieManager createPlatformCookieManager( + PlatformCookieManagerCreationParams params, + ) { + return WindowsCookieManager(params); + } + + /// Creates a new [WindowsInAppWebViewController]. /// /// This function should only be called by the app-facing package. @@ -73,4 +87,24 @@ class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { ) { return WindowsHeadlessInAppWebView(params); } + + /// Creates a new [WindowsWebViewEnvironment]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewEnvironment] in `flutter_inappwebview` instead. + @override + WindowsWebViewEnvironment createPlatformWebViewEnvironment( + PlatformWebViewEnvironmentCreationParams params, + ) { + return WindowsWebViewEnvironment(params); + } + + /// Creates a new empty [WindowsWebViewEnvironment] to access static methods. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewEnvironment] in `flutter_inappwebview` instead. + @override + WindowsWebViewEnvironment createPlatformWebViewEnvironmentStatic() { + return WindowsWebViewEnvironment.static(); + } } diff --git a/flutter_inappwebview_windows/lib/src/main.dart b/flutter_inappwebview_windows/lib/src/main.dart index f5fe68f1..4fce8f8c 100644 --- a/flutter_inappwebview_windows/lib/src/main.dart +++ b/flutter_inappwebview_windows/lib/src/main.dart @@ -8,3 +8,4 @@ export 'http_auth_credentials_database.dart' export 'web_message/main.dart'; export 'print_job/main.dart'; export 'find_interaction/main.dart'; +export 'webview_environment/main.dart'; diff --git a/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart b/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart index aedbc5d1..2077c759 100644 --- a/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart +++ b/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart @@ -3,7 +3,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; import 'web_message_port.dart'; -/// Object specifying creation parameters for creating a [MacOSWebMessageChannel]. +/// Object specifying creation parameters for creating a [WindowsWebMessageChannel]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformWebMessageChannelCreationParams] for @@ -31,10 +31,10 @@ class MacOSWebMessageChannelCreationParams } ///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageChannel} -class MacOSWebMessageChannel extends PlatformWebMessageChannel +class WindowsWebMessageChannel extends PlatformWebMessageChannel with ChannelController { - /// Constructs a [MacOSWebMessageChannel]. - MacOSWebMessageChannel(PlatformWebMessageChannelCreationParams params) + /// Constructs a [WindowsWebMessageChannel]. + WindowsWebMessageChannel(PlatformWebMessageChannelCreationParams params) : super.implementation( params is MacOSWebMessageChannelCreationParams ? params @@ -47,7 +47,7 @@ class MacOSWebMessageChannel extends PlatformWebMessageChannel initMethodCallHandler(); } - static final MacOSWebMessageChannel _staticValue = MacOSWebMessageChannel( + static final WindowsWebMessageChannel _staticValue = WindowsWebMessageChannel( MacOSWebMessageChannelCreationParams( id: '', port1: @@ -56,7 +56,7 @@ class MacOSWebMessageChannel extends PlatformWebMessageChannel MacOSWebMessagePortCreationParams(index: 1)))); /// Provide static access. - factory MacOSWebMessageChannel.static() { + factory WindowsWebMessageChannel.static() { return _staticValue; } @@ -64,11 +64,11 @@ class MacOSWebMessageChannel extends PlatformWebMessageChannel MacOSWebMessagePort get _macosPort2 => port2 as MacOSWebMessagePort; - static MacOSWebMessageChannel? _fromMap(Map? map) { + static WindowsWebMessageChannel? _fromMap(Map? map) { if (map == null) { return null; } - var webMessageChannel = MacOSWebMessageChannel( + var webMessageChannel = WindowsWebMessageChannel( MacOSWebMessageChannelCreationParams( id: map["id"], port1: MacOSWebMessagePort( @@ -100,7 +100,7 @@ class MacOSWebMessageChannel extends PlatformWebMessageChannel } @override - MacOSWebMessageChannel? fromMap(Map? map) { + WindowsWebMessageChannel? fromMap(Map? map) { return _fromMap(map); } @@ -115,6 +115,6 @@ class MacOSWebMessageChannel extends PlatformWebMessageChannel } } -extension InternalWebMessageChannel on MacOSWebMessageChannel { +extension InternalWebMessageChannel on WindowsWebMessageChannel { MethodChannel? get internalChannel => channel; } diff --git a/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart b/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart index c0682298..be3ecece 100644 --- a/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart +++ b/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart @@ -31,7 +31,7 @@ class MacOSWebMessagePortCreationParams ///{@macro flutter_inappwebview_platform_interface.PlatformWebMessagePort} class MacOSWebMessagePort extends PlatformWebMessagePort { WebMessageCallback? _onMessage; - late MacOSWebMessageChannel _webMessageChannel; + late WindowsWebMessageChannel _webMessageChannel; /// Constructs a [MacOSWebMessagePort]. MacOSWebMessagePort(PlatformWebMessagePortCreationParams params) @@ -89,7 +89,7 @@ extension InternalWebMessagePort on MacOSWebMessagePort { WebMessageCallback? get onMessage => _onMessage; void set onMessage(WebMessageCallback? value) => _onMessage = value; - MacOSWebMessageChannel get webMessageChannel => _webMessageChannel; - void set webMessageChannel(MacOSWebMessageChannel value) => + WindowsWebMessageChannel get webMessageChannel => _webMessageChannel; + void set webMessageChannel(WindowsWebMessageChannel value) => _webMessageChannel = value; } diff --git a/flutter_inappwebview_windows/lib/src/webview_environment/main.dart b/flutter_inappwebview_windows/lib/src/webview_environment/main.dart new file mode 100644 index 00000000..64d0520b --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/webview_environment/main.dart @@ -0,0 +1 @@ +export 'webview_environment.dart' hide InternalWindowsWebViewEnvironment; \ No newline at end of file diff --git a/flutter_inappwebview_windows/lib/src/webview_environment/webview_environment.dart b/flutter_inappwebview_windows/lib/src/webview_environment/webview_environment.dart new file mode 100644 index 00000000..b15a8963 --- /dev/null +++ b/flutter_inappwebview_windows/lib/src/webview_environment/webview_environment.dart @@ -0,0 +1,102 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; + +/// Object specifying creation parameters for creating a [WindowsWebViewEnvironment]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +@immutable +class WindowsWebViewEnvironmentCreationParams extends PlatformWebViewEnvironmentCreationParams { + /// Creates a new [WindowsInAppWebViewControllerCreationParams] instance. + const WindowsWebViewEnvironmentCreationParams({super.settings}); + + /// Creates a [WindowsInAppWebViewControllerCreationParams] instance based on [PlatformInAppWebViewControllerCreationParams]. + factory WindowsWebViewEnvironmentCreationParams.fromPlatformWebViewEnvironmentCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebViewEnvironmentCreationParams params) { + return WindowsWebViewEnvironmentCreationParams( + settings: params.settings + ); + } +} + +///Controls a WebView Environment used by WebView instances. +/// +///**Officially Supported Platforms/Implementations**: +///- Windows +class WindowsWebViewEnvironment extends PlatformWebViewEnvironment + with ChannelController { + static final MethodChannel _staticChannel = MethodChannel('com.pichillilorenzo/flutter_webview_environment'); + + @override + final String id = IdGenerator.generate(); + + WindowsWebViewEnvironment( + PlatformWebViewEnvironmentCreationParams params) + : super.implementation(params is WindowsWebViewEnvironmentCreationParams + ? params + : WindowsWebViewEnvironmentCreationParams + .fromPlatformWebViewEnvironmentCreationParams(params)); + + static final WindowsWebViewEnvironment _staticValue = + WindowsWebViewEnvironment( + WindowsWebViewEnvironmentCreationParams()); + + factory WindowsWebViewEnvironment.static() { + return _staticValue; + } + + _debugLog(String method, dynamic args) { + debugLog( + className: this.runtimeType.toString(), + id: id, + debugLoggingSettings: + PlatformWebViewEnvironment.debugLoggingSettings, + method: method, + args: args); + } + + Future _handleMethod(MethodCall call) async { + if (PlatformWebViewEnvironment.debugLoggingSettings.enabled) { + _debugLog(call.method, call.arguments); + } + + switch (call.method) { + default: + throw UnimplementedError("Unimplemented ${call.method} method"); + } + return null; + } + + @override + Future create( + {WebViewEnvironmentSettings? settings}) async { + final env = WindowsWebViewEnvironment( + WindowsWebViewEnvironmentCreationParams(settings: settings) + ); + + Map args = {}; + args.putIfAbsent('id', () => env.id); + args.putIfAbsent('settings', () => env.settings?.toMap()); + await _staticChannel.invokeMethod( + 'create', args); + + env.channel = MethodChannel('com.pichillilorenzo/flutter_webview_environment_$id'); + env.handler = env.handleMethod; + env.initMethodCallHandler(); + return env; + } + + @override + Future dispose() async { + Map args = {}; + await channel?.invokeMethod('dispose', args); + disposeChannel(); + } +} + +extension InternalWindowsWebViewEnvironment on WindowsWebViewEnvironment { + get handleMethod => _handleMethod; +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/pubspec.yaml b/flutter_inappwebview_windows/pubspec.yaml index 354848d1..6741c0bb 100644 --- a/flutter_inappwebview_windows/pubspec.yaml +++ b/flutter_inappwebview_windows/pubspec.yaml @@ -18,7 +18,8 @@ environment: dependencies: flutter: sdk: flutter - flutter_inappwebview_platform_interface: ^1.0.10 + flutter_inappwebview_platform_interface: #^1.0.10 + path: ../flutter_inappwebview_platform_interface dev_dependencies: flutter_test: diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt index 2d8801db..aaac0851 100644 --- a/flutter_inappwebview_windows/windows/CMakeLists.txt +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -72,6 +72,10 @@ list(APPEND PLUGIN_SOURCES "types/plugin_script.h" "types/size_2d.cpp" "types/size_2d.h" + "types/rect.cpp" + "types/rect.h" + "types/screenshot_configuration.cpp" + "types/screenshot_configuration.h" "custom_platform_view/custom_platform_view.cc" "custom_platform_view/custom_platform_view.h" "custom_platform_view/texture_bridge.cc" @@ -88,6 +92,14 @@ list(APPEND PLUGIN_SOURCES "plugin_scripts_js/plugin_scripts_util.h" "plugin_scripts_js/javascript_bridge_js.cpp" "plugin_scripts_js/javascript_bridge_js.h" + "webview_environment/webview_environment_settings.cpp" + "webview_environment/webview_environment_settings.h" + "webview_environment/webview_environment.cpp" + "webview_environment/webview_environment.h" + "webview_environment/webview_environment_manager.cpp" + "webview_environment/webview_environment_manager.h" + "webview_environment/webview_environment_channel_delegate.cpp" + "webview_environment/webview_environment_channel_delegate.h" "in_app_webview/user_content_controller.cpp" "in_app_webview/user_content_controller.h" "in_app_webview/in_app_webview_settings.cpp" @@ -112,6 +124,8 @@ list(APPEND PLUGIN_SOURCES "in_app_browser/in_app_browser.h" "in_app_browser/in_app_browser_channel_delegate.cpp" "in_app_browser/in_app_browser_channel_delegate.h" + "cookie_manager.cpp" + "cookie_manager.h" ) # Define the plugin library target. Its name must not be changed (see comment diff --git a/flutter_inappwebview_windows/windows/cookie_manager.cpp b/flutter_inappwebview_windows/windows/cookie_manager.cpp new file mode 100644 index 00000000..d962755d --- /dev/null +++ b/flutter_inappwebview_windows/windows/cookie_manager.cpp @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include + +#include "cookie_manager.h" +#include "in_app_webview/in_app_webview.h" +#include "utils/log.h" + +namespace flutter_inappwebview_plugin +{ + using namespace Microsoft::WRL; + + CookieManager::CookieManager(const FlutterInappwebviewWindowsPlugin* plugin) + : ChannelDelegate(plugin->registrar->messenger(), CookieManager::METHOD_CHANNEL_NAME_PREFIX) + { + windowClass_.lpszClassName = CookieManager::CLASS_NAME; + windowClass_.lpfnWndProc = &DefWindowProc; + + RegisterClass(&windowClass_); + + auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, 0, + 0, 0, 0, + plugin->registrar->GetView()->GetNativeWindow(), + nullptr, + windowClass_.hInstance, nullptr); + + CreateInAppWebViewEnvParams webViewEnvParams = { + hwnd, + false + }; + + InAppWebView::createInAppWebViewEnv(webViewEnvParams, nullptr, + [=](wil::com_ptr webViewEnv, + wil::com_ptr webViewController, + wil::com_ptr webViewCompositionController) + { + if (webViewEnv && webViewController) { + webViewEnv_ = std::move(webViewEnv); + webViewController_ = std::move(webViewController); + webViewController_->get_CoreWebView2(&webView_); + webViewController_->put_IsVisible(false); + } + }); + } + + void CookieManager::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + auto& arguments = std::get(*method_call.arguments()); + auto& methodName = method_call.method_name(); + + if (string_equals(methodName, "setCookie")) { + if (!webView_) { + result->Success(false); + return; + } + + auto url = get_fl_map_value(arguments, "url"); + auto name = get_fl_map_value(arguments, "name"); + auto value = get_fl_map_value(arguments, "value"); + auto path = get_fl_map_value(arguments, "path"); + auto domain = get_optional_fl_map_value(arguments, "domain"); + auto expiresDate = get_optional_fl_map_value(arguments, "expiresDate"); + auto maxAge = get_optional_fl_map_value(arguments, "maxAge"); + auto isSecure = get_optional_fl_map_value(arguments, "isSecure"); + auto isHttpOnly = get_optional_fl_map_value(arguments, "isHttpOnly"); + auto sameSite = get_optional_fl_map_value(arguments, "sameSite"); + + nlohmann::json parameters = { + {"url", url}, + {"name", name}, + {"value", value}, + {"path", path} + }; + if (domain.has_value()) { + parameters["domain"] = domain.value(); + } + if (expiresDate.has_value()) { + parameters["expires"] = expiresDate.value() / 1000; + } + debugLog(maxAge.value()); + if (maxAge.has_value()) { + // time(NULL) represents the current unix timestamp in seconds + parameters["expires"] = time(NULL) + maxAge.value(); + } + if (isSecure.has_value()) { + parameters["secure"] = isSecure.value(); + } + if (isHttpOnly.has_value()) { + parameters["httpOnly"] = isHttpOnly.value(); + } + if (sameSite.has_value()) { + parameters["sameSite"] = sameSite.value(); + } + + auto result_ = std::shared_ptr>(std::move(result)); + auto hr = webView_->CallDevToolsProtocolMethod(L"Network.setCookie", utf8_to_wide(parameters.dump()).c_str(), Callback( + [this, result_](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + result_->Success(succeededOrLog(errorCode)); + return S_OK; + } + ).Get()); + + if (failedAndLog(hr)) { + result_->Success(false); + } + } + else { + result->NotImplemented(); + } + } + + CookieManager::~CookieManager() + { + debugLog("dealloc CookieManager"); + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/cookie_manager.h b/flutter_inappwebview_windows/windows/cookie_manager.h new file mode 100644 index 00000000..6d3d3daa --- /dev/null +++ b/flutter_inappwebview_windows/windows/cookie_manager.h @@ -0,0 +1,36 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_COOKIE_MANAGER_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_COOKIE_MANAGER_H_ + +#include +#include +#include +#include + +#include "flutter_inappwebview_windows_plugin.h" +#include "types/channel_delegate.h" + +namespace flutter_inappwebview_plugin +{ + class CookieManager : public ChannelDelegate + { + public: + static inline const wchar_t* CLASS_NAME = L"CookieManager"; + static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_inappwebview_cookiemanager"; + + const FlutterInappwebviewWindowsPlugin* plugin; + + CookieManager(const FlutterInappwebviewWindowsPlugin* plugin); + ~CookieManager(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + private: + wil::com_ptr webViewEnv_; + wil::com_ptr webViewController_; + wil::com_ptr webView_; + WNDCLASS windowClass_ = {}; + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_COOKIE_MANAGER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp index 57355649..a616a89b 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp @@ -2,9 +2,11 @@ #include +#include "cookie_manager.h" #include "headless_in_app_webview/headless_in_app_webview_manager.h" #include "in_app_browser/in_app_browser_manager.h" #include "in_app_webview/in_app_webview_manager.h" +#include "webview_environment/webview_environment_manager.h" #pragma comment(lib, "Shlwapi.lib") #pragma comment(lib, "dxgi.lib") @@ -23,9 +25,11 @@ namespace flutter_inappwebview_plugin FlutterInappwebviewWindowsPlugin::FlutterInappwebviewWindowsPlugin(flutter::PluginRegistrarWindows* registrar) : registrar(registrar) { + webViewEnvironmentManager = std::make_unique(this); inAppWebViewManager = std::make_unique(this); inAppBrowserManager = std::make_unique(this); headlessInAppWebViewManager = std::make_unique(this); + cookieManager = std::make_unique(this); } FlutterInappwebviewWindowsPlugin::~FlutterInappwebviewWindowsPlugin() diff --git a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h index 64566fa0..9d0bd403 100644 --- a/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h +++ b/flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h @@ -6,16 +6,20 @@ namespace flutter_inappwebview_plugin { + class WebViewEnvironmentManager; class InAppWebViewManager; class InAppBrowserManager; class HeadlessInAppWebViewManager; + class CookieManager; class FlutterInappwebviewWindowsPlugin : public flutter::Plugin { public: flutter::PluginRegistrarWindows* registrar; + std::unique_ptr webViewEnvironmentManager; std::unique_ptr inAppWebViewManager; std::unique_ptr inAppBrowserManager; std::unique_ptr headlessInAppWebViewManager; + std::unique_ptr cookieManager; static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); diff --git a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.cpp b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.cpp index bf1db080..fc2ff2c1 100644 --- a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview.cpp @@ -19,7 +19,6 @@ namespace flutter_inappwebview_plugin if (!webView) { return; } - webView->webViewController->put_IsVisible(false); } void HeadlessInAppWebView::setSize(const std::shared_ptr size) const @@ -48,12 +47,13 @@ namespace flutter_inappwebview_plugin HeadlessInAppWebView::~HeadlessInAppWebView() { debugLog("dealloc HeadlessInAppWebView"); + HWND parentWindow = nullptr; if (webView && webView->webViewController) { - HWND parentWindow; - if (succeededOrLog(webView->webViewController->get_ParentWindow(&parentWindow))) { - DestroyWindow(parentWindow); - } + webView->webViewController->get_ParentWindow(&parentWindow); } webView = nullptr; + if (parentWindow) { + DestroyWindow(parentWindow); + } } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp index a8803bae..9c6e3d9e 100644 --- a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp +++ b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp @@ -10,8 +10,10 @@ #include "../types/user_script.h" #include "../utils/flutter.h" #include "../utils/log.h" +#include "../utils/map.h" #include "../utils/string.h" #include "../utils/vector.h" +#include "../webview_environment/webview_environment_manager.h" #include "headless_in_app_webview_manager.h" namespace flutter_inappwebview_plugin @@ -54,6 +56,7 @@ namespace flutter_inappwebview_plugin auto initialFile = get_optional_fl_map_value(params, "initialFile"); auto initialDataMap = get_optional_fl_map_value(params, "initialData"); auto initialUserScriptList = get_optional_fl_map_value(params, "initialUserScripts"); + auto webViewEnvironmentId = get_optional_fl_map_value(*arguments, "webViewEnvironmentId"); RECT bounds; GetClientRect(plugin->registrar->GetView()->GetNativeWindow(), &bounds); @@ -67,7 +70,15 @@ namespace flutter_inappwebview_plugin nullptr, windowClass_.hInstance, nullptr); - InAppWebView::createInAppWebViewEnv(hwnd, false, + CreateInAppWebViewEnvParams webViewEnvParams = { + hwnd, + false + }; + + auto webViewEnvironment = webViewEnvironmentId.has_value() && map_contains(plugin->webViewEnvironmentManager->webViewEnvironments, webViewEnvironmentId.value()) + ? plugin->webViewEnvironmentManager->webViewEnvironments.at(webViewEnvironmentId.value()).get() : nullptr; + + InAppWebView::createInAppWebViewEnv(webViewEnvParams, webViewEnvironment, [=](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index a7ab72e9..baa749a5 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -2,6 +2,7 @@ #include "../utils/log.h" #include "../utils/strconv.h" +#include "../webview_environment/webview_environment_manager.h" #include "in_app_browser.h" #include "in_app_browser_manager.h" @@ -41,13 +42,22 @@ namespace flutter_inappwebview_plugin ShowWindow(m_hWnd, settings->hidden ? SW_HIDE : SW_SHOW); + CreateInAppWebViewEnvParams webViewEnvParams = { + m_hWnd, + false + }; + + InAppWebViewCreationParams webViewParams = { id, params.initialWebViewSettings, params.initialUserScripts }; - InAppWebView::createInAppWebViewEnv(m_hWnd, false, + auto webViewEnvironment = params.webViewEnvironmentId.has_value() && map_contains(plugin->webViewEnvironmentManager->webViewEnvironments, params.webViewEnvironmentId.value()) + ? plugin->webViewEnvironmentManager->webViewEnvironments.at(params.webViewEnvironmentId.value()).get() : nullptr; + + InAppWebView::createInAppWebViewEnv(webViewEnvParams, webViewEnvironment, [this, params, webViewParams](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) -> void { if (webViewEnv && webViewController) { @@ -83,13 +93,11 @@ namespace flutter_inappwebview_plugin void InAppBrowser::show() const { ShowWindow(m_hWnd, SW_SHOW); - webView->webViewController->put_IsVisible(true); } void InAppBrowser::hide() const { ShowWindow(m_hWnd, SW_HIDE); - webView->webViewController->put_IsVisible(false); } bool InAppBrowser::isHidden() const diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h index 9d419746..f0a0d677 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h @@ -24,6 +24,7 @@ namespace flutter_inappwebview_plugin const std::shared_ptr initialSettings; const std::shared_ptr initialWebViewSettings; const std::optional>> initialUserScripts; + const std::optional webViewEnvironmentId; }; class InAppBrowser { diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp index 96bd4fb8..b414c806 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -53,6 +53,7 @@ namespace flutter_inappwebview_plugin auto assetFilePath = get_optional_fl_map_value(*arguments, "assetFilePath"); auto data = get_optional_fl_map_value(*arguments, "data"); auto initialUserScriptList = get_optional_fl_map_value(*arguments, "initialUserScripts"); + auto webViewEnvironmentId = get_optional_fl_map_value(*arguments, "webViewEnvironmentId"); std::optional> urlRequest = urlRequestMap.has_value() ? std::make_shared(urlRequestMap.value()) : std::optional>{}; @@ -70,7 +71,8 @@ namespace flutter_inappwebview_plugin data, std::move(initialSettings), std::move(initialWebViewSettings), - initialUserScripts + initialUserScripts, + webViewEnvironmentId }; auto inAppBrowser = std::make_unique(plugin, params); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index 8f1f9658..fc69518a 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -15,6 +15,7 @@ #include "../utils/string.h" #include "in_app_webview.h" +#include ".plugin_symlinks/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h" #include "in_app_webview_manager.h" namespace flutter_inappwebview_plugin @@ -39,6 +40,7 @@ namespace flutter_inappwebview_plugin registerSurfaceEventHandlers(); } else { + this->webViewController->put_IsVisible(true); // Resize WebView to fit the bounds of the parent window RECT bounds; GetClientRect(parentWindow, &bounds); @@ -56,58 +58,70 @@ namespace flutter_inappwebview_plugin this->inAppBrowser = inAppBrowser; } - void InAppWebView::createInAppWebViewEnv(const HWND parentWindow, const bool willBeSurface, std::function webViewEnv, + void InAppWebView::createInAppWebViewEnv(const CreateInAppWebViewEnvParams& params, const WebViewEnvironment* webViewEnvironment, std::function webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController)> completionHandler) { - failedLog(CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, - Callback( - [parentWindow, completionHandler, willBeSurface](HRESULT result, wil::com_ptr env) -> HRESULT - { - if (failedAndLog(result) || !env) { - completionHandler(nullptr, nullptr, nullptr); - return E_FAIL; - } + auto callback = [params, completionHandler](HRESULT result, wil::com_ptr env) -> HRESULT + { + if (failedAndLog(result) || !env) { + completionHandler(nullptr, nullptr, nullptr); + return E_FAIL; + } - wil::com_ptr webViewEnv3; - if (willBeSurface && succeededOrLog(env->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) { - failedLog(webViewEnv3->CreateCoreWebView2CompositionController(parentWindow, Callback( - [completionHandler, env](HRESULT result, wil::com_ptr compositionController) -> HRESULT - { - wil::com_ptr webViewController = compositionController.try_query(); + wil::com_ptr webViewEnv3; + if (params.willBeSurface && succeededOrLog(env->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) { + failedLog(webViewEnv3->CreateCoreWebView2CompositionController(params.parentWindow, Callback( + [completionHandler, env](HRESULT result, wil::com_ptr compositionController) -> HRESULT + { + wil::com_ptr webViewController = compositionController.try_query(); - if (failedAndLog(result) || !webViewController) { - completionHandler(nullptr, nullptr, nullptr); - return E_FAIL; - } - - ICoreWebView2Controller3* webViewController3; - if (succeededOrLog(webViewController->QueryInterface(IID_PPV_ARGS(&webViewController3)))) { - webViewController3->put_BoundsMode(COREWEBVIEW2_BOUNDS_MODE_USE_RAW_PIXELS); - webViewController3->put_ShouldDetectMonitorScaleChanges(FALSE); - webViewController3->put_RasterizationScale(1.0); - } - - completionHandler(std::move(env), std::move(webViewController), std::move(compositionController)); - return S_OK; + if (failedAndLog(result) || !webViewController) { + completionHandler(nullptr, nullptr, nullptr); + return E_FAIL; } - ).Get())); - } - else { - failedLog(env->CreateCoreWebView2Controller(parentWindow, Callback( - [completionHandler, env](HRESULT result, wil::com_ptr controller) -> HRESULT - { - if (failedAndLog(result) || !controller) { - completionHandler(nullptr, nullptr, nullptr); - return E_FAIL; - } - completionHandler(std::move(env), std::move(controller), nullptr); - return S_OK; - }).Get())); - } - return S_OK; - }).Get())); + ICoreWebView2Controller3* webViewController3; + if (succeededOrLog(webViewController->QueryInterface(IID_PPV_ARGS(&webViewController3)))) { + webViewController3->put_BoundsMode(COREWEBVIEW2_BOUNDS_MODE_USE_RAW_PIXELS); + webViewController3->put_ShouldDetectMonitorScaleChanges(FALSE); + webViewController3->put_RasterizationScale(1.0); + } + + completionHandler(std::move(env), std::move(webViewController), std::move(compositionController)); + return S_OK; + } + ).Get())); + } + else { + failedLog(env->CreateCoreWebView2Controller(params.parentWindow, Callback( + [completionHandler, env](HRESULT result, wil::com_ptr controller) -> HRESULT + { + if (failedAndLog(result) || !controller) { + completionHandler(nullptr, nullptr, nullptr); + return E_FAIL; + } + + completionHandler(std::move(env), std::move(controller), nullptr); + return S_OK; + }).Get())); + } + return S_OK; + }; + + HRESULT hr; + if (webViewEnvironment && webViewEnvironment->env) { + hr = callback(S_OK, webViewEnvironment->env); + } + else { + hr = CreateCoreWebView2EnvironmentWithOptions( + nullptr, nullptr, nullptr, + Callback(callback).Get()); + } + + if (failedAndLog(hr)) { + completionHandler(nullptr, nullptr, nullptr); + } } void InAppWebView::initChannel(const std::optional> viewId, const std::optional channelName) @@ -168,10 +182,11 @@ namespace flutter_inappwebview_plugin } ).Get())); - userContentController->addPluginScript(std::move(createJavaScriptBridgePluginScript())); - - if (params.initialUserScripts.has_value()) { - userContentController->addUserOnlyScripts(params.initialUserScripts.value()); + if (userContentController) { + userContentController->addPluginScript(std::move(createJavaScriptBridgePluginScript())); + if (params.initialUserScripts.has_value()) { + userContentController->addUserOnlyScripts(params.initialUserScripts.value()); + } } registerEventHandlers(); @@ -392,18 +407,6 @@ namespace flutter_inappwebview_plugin return S_OK; } - /* - wil::unique_cotaskmem_string url; - args->get_Source(&url); - - wil::com_ptr uri; - failedLog(CreateUri( - url.get(), // NULL terminated URI - Uri_CREATE_ALLOW_RELATIVE, // Flags to control behavior - 0, // Reserved must be 0 - &uri)); - */ - wil::unique_cotaskmem_string json; if (succeededOrLog(args->get_WebMessageAsJson(&json))) { auto message = nlohmann::json::parse(wide_to_utf8(json.get())); @@ -650,7 +653,7 @@ namespace flutter_inappwebview_plugin return webView && succeededOrLog(webView->get_CanGoForward(&canGoForward_)) ? canGoForward_ : false; } - void InAppWebView::goBackOrForward(const int& steps) + void InAppWebView::goBackOrForward(const int64_t& steps) { getCopyBackForwardList( [this, steps](std::unique_ptr webHistory) @@ -682,7 +685,7 @@ namespace flutter_inappwebview_plugin ); } - void InAppWebView::canGoBackOrForward(const int& steps, std::function completionHandler) const + void InAppWebView::canGoBackOrForward(const int64_t& steps, std::function completionHandler) const { getCopyBackForwardList( [steps, completionHandler](std::unique_ptr webHistory) @@ -920,6 +923,55 @@ namespace flutter_inappwebview_plugin userContentController->removeAllUserOnlyScripts(); } + void InAppWebView::takeScreenshot(const std::optional> screenshotConfiguration, const std::function)> completionHandler) const + { + if (!webView) { + if (completionHandler) { + completionHandler(std::nullopt); + } + return; + } + + nlohmann::json parameters = { + {"captureBeyondViewport", true} + }; + if (screenshotConfiguration.has_value()) { + auto& scp = screenshotConfiguration.value(); + parameters["format"] = to_lowercase_copy(CompressFormatToString(scp->compressFormat)); + if (scp->compressFormat == CompressFormat::jpeg) { + parameters["quality"] = scp->quality; + } + if (scp->rect.has_value()) { + auto& rect = scp->rect.value(); + parameters["clip"] = { + {"x", rect->x}, + {"y", rect->y}, + {"width", rect->width}, + {"height", rect->height}, + {"scale", scaleFactor_} + }; + } + } + + auto hr = webView->CallDevToolsProtocolMethod(L"Page.captureScreenshot", utf8_to_wide(parameters.dump()).c_str(), Callback( + [this, completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + std::optional result = std::nullopt; + if (succeededOrLog(errorCode)) { + nlohmann::json json = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson)); + result = json["data"].get(); + } + if (completionHandler) { + completionHandler(result); + } + return S_OK; + } + ).Get()); + if (failedAndLog(hr) && completionHandler) { + completionHandler(std::nullopt); + } + } + // flutter_view void InAppWebView::setSurfaceSize(size_t width, size_t height, float scale_factor) { @@ -1187,15 +1239,16 @@ namespace flutter_inappwebview_plugin InAppWebView::~InAppWebView() { debugLog("dealloc InAppWebView"); - HWND parentWindow = nullptr; - if (webViewCompositionController && succeededOrLog(webViewController->get_ParentWindow(&parentWindow))) { - // if it's an InAppWebView, - // then destroy the Window created with it - DestroyWindow(parentWindow); - } + userContentController = nullptr; if (webView) { failedLog(webView->Stop()); } + HWND parentWindow = nullptr; + if (webViewCompositionController && webViewController && succeededOrLog(webViewController->get_ParentWindow(&parentWindow))) { + // if it's an InAppWebView (so webViewCompositionController will be not a nullptr!), + // then destroy the Window created with it + DestroyWindow(parentWindow); + } if (webViewController) { failedLog(webViewController->Close()); } @@ -1203,4 +1256,4 @@ namespace flutter_inappwebview_plugin inAppBrowser = nullptr; plugin = nullptr; } -} \ No newline at end of file +} diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index b5915c2f..ddf21439 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -14,6 +14,8 @@ #include "../types/navigation_action.h" #include "../types/url_request.h" #include "../types/web_history.h" +#include "../webview_environment/webview_environment.h" +#include ".plugin_symlinks/flutter_inappwebview_windows/windows/types/screenshot_configuration.h" #include "in_app_webview_settings.h" #include "user_content_controller.h" #include "webview_channel_delegate.h" @@ -81,6 +83,11 @@ namespace flutter_inappwebview_plugin const std::optional>> initialUserScripts; }; + struct CreateInAppWebViewEnvParams { + const HWND parentWindow; + const bool willBeSurface; + }; + class InAppWebView { public: @@ -107,7 +114,7 @@ namespace flutter_inappwebview_plugin wil::com_ptr webViewCompositionController); ~InAppWebView(); - static void createInAppWebViewEnv(const HWND parentWindow, const bool willBeSurface, std::function webViewEnv, + static void createInAppWebViewEnv(const CreateInAppWebViewEnvParams& params, const WebViewEnvironment* webViewEnvironment, std::function webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController)> completionHandler); @@ -147,8 +154,8 @@ namespace flutter_inappwebview_plugin bool canGoBack() const; void goForward(); bool canGoForward() const; - void goBackOrForward(const int& steps); - void canGoBackOrForward(const int& steps, std::function completionHandler) const; + void goBackOrForward(const int64_t& steps); + void canGoBackOrForward(const int64_t& steps, std::function completionHandler) const; bool isLoading() const { return isLoading_; @@ -161,6 +168,7 @@ namespace flutter_inappwebview_plugin void removeUserScript(const int64_t index, const std::shared_ptr userScript) const; void removeUserScriptsByGroupName(const std::string& groupName) const; void removeAllUserScripts() const; + void takeScreenshot(const std::optional> screenshotConfiguration, const std::function)> completionHandler) const; std::string pageFrameId() const { diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp index 41e3e965..6f4296dc 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp @@ -13,6 +13,8 @@ #include "../utils/vector.h" #include "in_app_webview_manager.h" +#include ".plugin_symlinks/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h" + namespace flutter_inappwebview_plugin { InAppWebViewManager::InAppWebViewManager(const FlutterInappwebviewWindowsPlugin* plugin) @@ -83,6 +85,7 @@ namespace flutter_inappwebview_plugin auto initialFile = get_optional_fl_map_value(*arguments, "initialFile"); auto initialDataMap = get_optional_fl_map_value(*arguments, "initialData"); auto initialUserScriptList = get_optional_fl_map_value(*arguments, "initialUserScripts"); + auto webViewEnvironmentId = get_optional_fl_map_value(*arguments, "webViewEnvironmentId"); RECT bounds; GetClientRect(plugin->registrar->GetView()->GetNativeWindow(), &bounds); @@ -93,7 +96,15 @@ namespace flutter_inappwebview_plugin nullptr, windowClass_.hInstance, nullptr); - InAppWebView::createInAppWebViewEnv(hwnd, true, + CreateInAppWebViewEnvParams webViewEnvParams = { + hwnd, + true + }; + + auto webViewEnvironment = webViewEnvironmentId.has_value() && map_contains(plugin->webViewEnvironmentManager->webViewEnvironments, webViewEnvironmentId.value()) + ? plugin->webViewEnvironmentManager->webViewEnvironments.at(webViewEnvironmentId.value()).get() : nullptr; + + InAppWebView::createInAppWebViewEnv(webViewEnvParams, webViewEnvironment, [=](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) @@ -182,4 +193,4 @@ namespace flutter_inappwebview_plugin UnregisterClass(windowClass_.lpszClassName, nullptr); plugin = nullptr; } -} \ No newline at end of file +} diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index a2648dc7..7f4d4ab3 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -159,6 +159,16 @@ namespace flutter_inappwebview_plugin webView->removeAllUserScripts(); result->Success(true); } + else if (string_equals(methodName, "takeScreenshot")) { + auto result_ = std::shared_ptr>(std::move(result)); + auto screenshotConfigurationMap = get_optional_fl_map_value(arguments, "screenshotConfiguration"); + std::optional> screenshotConfiguration = + screenshotConfigurationMap.has_value() ? std::make_unique(screenshotConfigurationMap.value()) : std::optional>{}; + webView->takeScreenshot(std::move(screenshotConfiguration), [result_ = std::move(result_)](const std::optional data) + { + result_->Success(make_fl_value(data)); + }); + } // for inAppBrowser else if (webView->inAppBrowser && string_equals(methodName, "show")) { webView->inAppBrowser->show(); diff --git a/flutter_inappwebview_windows/windows/types/rect.cpp b/flutter_inappwebview_windows/windows/types/rect.cpp new file mode 100644 index 00000000..a0a41ab0 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/rect.cpp @@ -0,0 +1,25 @@ +#include "rect.h" + +namespace flutter_inappwebview_plugin +{ + Rect::Rect(const double& x, const double& y, const double& width, const double& height) + : x(x), y(y), width(width), height(height) + {} + + Rect::Rect(const flutter::EncodableMap& map) + : x(get_fl_map_value(map, "x")), + y(get_fl_map_value(map, "y")), + width(get_fl_map_value(map, "width")), + height(get_fl_map_value(map, "height")) + {} + + flutter::EncodableMap Rect::toEncodableMap() const + { + return flutter::EncodableMap{ + {"x", x}, + {"y", y}, + {"width", width}, + {"height", height} + }; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/rect.h b/flutter_inappwebview_windows/windows/types/rect.h new file mode 100644 index 00000000..2472d084 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/rect.h @@ -0,0 +1,27 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_RECT_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_RECT_H_ + +#include +#include + +#include "../utils/flutter.h" + +namespace flutter_inappwebview_plugin +{ + class Rect + { + public: + const double x; + const double y; + const double width; + const double height; + + Rect(const double& x, const double& y, const double& width, const double& height); + Rect(const flutter::EncodableMap& map); + ~Rect() = default; + + flutter::EncodableMap toEncodableMap() const; + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_RECT_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/screenshot_configuration.cpp b/flutter_inappwebview_windows/windows/types/screenshot_configuration.cpp new file mode 100644 index 00000000..5e0f027e --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/screenshot_configuration.cpp @@ -0,0 +1,48 @@ +#include "../utils/flutter.h" +#include "../utils/map.h" +#include "screenshot_configuration.h" + +namespace flutter_inappwebview_plugin +{ + CompressFormat CompressFormatFromString(const std::string& compressFormat) + { + if (string_equals(compressFormat, "PNG")) { + return CompressFormat::png; + } + else if (string_equals(compressFormat, "JPEG")) { + return CompressFormat::jpeg; + } + else if (string_equals(compressFormat, "WEBP")) { + return CompressFormat::webp; + } + return CompressFormat::png; + } + + std::string CompressFormatToString(const CompressFormat& compressFormat) + { + switch (compressFormat) { + case CompressFormat::jpeg: + return "JPEG"; + case CompressFormat::webp: + return "WEBP"; + case CompressFormat::png: + default: + return "PNG"; + } + } + + ScreenshotConfiguration::ScreenshotConfiguration( + const CompressFormat& compressFormat, + const int64_t& quality, + const std::optional> rect + ) : compressFormat(compressFormat), quality(quality), rect(rect) + {} + + ScreenshotConfiguration::ScreenshotConfiguration(const flutter::EncodableMap& map) + : compressFormat(CompressFormatFromString(get_fl_map_value(map, "compressFormat"))), + quality(get_fl_map_value(map, "quality")), + rect(fl_map_contains_not_null(map, "rect") ? std::make_shared(get_fl_map_value(map, "rect")) : std::optional>{}) + {} + + ScreenshotConfiguration::~ScreenshotConfiguration() {} +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/screenshot_configuration.h b/flutter_inappwebview_windows/windows/types/screenshot_configuration.h new file mode 100644 index 00000000..f05ee7f0 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/screenshot_configuration.h @@ -0,0 +1,38 @@ +#ifndef FLUTTER_INAPPWEBVIEW_SCREENSHOT_CONFIGURATION_H_ +#define FLUTTER_INAPPWEBVIEW_SCREENSHOT_CONFIGURATION_H_ + +#include +#include +#include + +#include "../types/rect.h" +#include "../utils/string.h" + +namespace flutter_inappwebview_plugin +{ + enum CompressFormat { + png, + jpeg, + webp + }; + + CompressFormat CompressFormatFromString(const std::string& compressFormat); + std::string CompressFormatToString(const CompressFormat& compressFormat); + + class ScreenshotConfiguration + { + public: + const CompressFormat compressFormat; + const int64_t quality; + const std::optional> rect; + + ScreenshotConfiguration( + const CompressFormat& compressFormat, + const int64_t& quality, + const std::optional> rect + ); + ScreenshotConfiguration(const flutter::EncodableMap& map); + ~ScreenshotConfiguration(); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_SCREENSHOT_CONFIGURATION_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/types/web_resource_error.cpp b/flutter_inappwebview_windows/windows/types/web_resource_error.cpp index f3fb3a36..5059a3dc 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_error.cpp +++ b/flutter_inappwebview_windows/windows/types/web_resource_error.cpp @@ -3,7 +3,7 @@ namespace flutter_inappwebview_plugin { - WebResourceError::WebResourceError(const std::string& description, const int type) + WebResourceError::WebResourceError(const std::string& description, const int64_t type) : description(description), type(type) {} diff --git a/flutter_inappwebview_windows/windows/types/web_resource_error.h b/flutter_inappwebview_windows/windows/types/web_resource_error.h index b1d2a55b..f5a0a5aa 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_error.h +++ b/flutter_inappwebview_windows/windows/types/web_resource_error.h @@ -33,9 +33,9 @@ namespace flutter_inappwebview_plugin { public: const std::string description; - const int type; + const int64_t type; - WebResourceError(const std::string& description, const int type); + WebResourceError(const std::string& description, const int64_t type); WebResourceError(const flutter::EncodableMap& map); ~WebResourceError() = default; diff --git a/flutter_inappwebview_windows/windows/types/web_resource_response.cpp b/flutter_inappwebview_windows/windows/types/web_resource_response.cpp index 5f76dc9c..2e84cd9e 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_response.cpp +++ b/flutter_inappwebview_windows/windows/types/web_resource_response.cpp @@ -3,7 +3,7 @@ namespace flutter_inappwebview_plugin { - WebResourceResponse::WebResourceResponse(const std::optional& statusCode) + WebResourceResponse::WebResourceResponse(const std::optional& statusCode) : statusCode(statusCode) {} diff --git a/flutter_inappwebview_windows/windows/types/web_resource_response.h b/flutter_inappwebview_windows/windows/types/web_resource_response.h index 123080aa..c428e0ad 100644 --- a/flutter_inappwebview_windows/windows/types/web_resource_response.h +++ b/flutter_inappwebview_windows/windows/types/web_resource_response.h @@ -9,9 +9,9 @@ namespace flutter_inappwebview_plugin class WebResourceResponse { public: - const std::optional statusCode; + const std::optional statusCode; - WebResourceResponse(const std::optional& statusCode); + WebResourceResponse(const std::optional& statusCode); WebResourceResponse(const flutter::EncodableMap& map); ~WebResourceResponse() = default; diff --git a/flutter_inappwebview_windows/windows/utils/flutter.h b/flutter_inappwebview_windows/windows/utils/flutter.h index 6bd271b6..45c1390c 100644 --- a/flutter_inappwebview_windows/windows/utils/flutter.h +++ b/flutter_inappwebview_windows/windows/utils/flutter.h @@ -80,23 +80,65 @@ namespace flutter_inappwebview_plugin return encodableMap; } - template + static inline bool fl_map_contains(const flutter::EncodableMap& map, const char* key) + { + return map_contains(map, make_fl_value(key)); + } + + static inline bool fl_map_contains_not_null(const flutter::EncodableMap& map, const char* key) + { + return fl_map_contains(map, key) && !map.at(make_fl_value(key)).IsNull(); + } + + template::value && !std::is_same::value), bool>::type* = nullptr> static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* key) { return std::get(map.at(make_fl_value(key))); } - template::value && !is_vector::value) || + template::value, bool>::type* = nullptr> + static inline int64_t get_fl_map_value(const flutter::EncodableMap& map, const char* key) + { + return map.at(make_fl_value(key)).LongValue(); + } + + template::value, bool>::type* = nullptr> + static inline int64_t get_fl_map_value(const flutter::EncodableMap& map, const char* key) + { + return map.at(make_fl_value(key)).LongValue(); + } + + template::value && !is_vector::value && !std::is_same::value && !std::is_same::value) || std::is_same::value || std::is_same::value), int>::type* = nullptr> static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* key) { - auto fl_key = make_fl_value(key); - if (map_contains(map, fl_key)) { + if (fl_map_contains_not_null(map, key)) { + auto fl_key = make_fl_value(key); return make_pointer_optional(std::get_if(&map.at(fl_key))); } return std::nullopt; } + template::value, bool>::type* = nullptr> + static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* key) + { + if (fl_map_contains_not_null(map, key)) { + auto fl_key = make_fl_value(key); + return std::make_optional(map.at(fl_key).LongValue()); + } + return std::nullopt; + } + + template::value, bool>::type* = nullptr> + static inline std::optional get_optional_fl_map_value(const flutter::EncodableMap& map, const char* key) + { + if (fl_map_contains_not_null(map, key)) { + auto fl_key = make_fl_value(key); + return std::make_optional(map.at(fl_key).LongValue()); + } + return std::nullopt; + } + template static inline T get_fl_map_value(const flutter::EncodableMap& map, const char* key, const T& defaultValue) { diff --git a/flutter_inappwebview_windows/windows/utils/string.h b/flutter_inappwebview_windows/windows/utils/string.h index 27b64731..ee912db8 100644 --- a/flutter_inappwebview_windows/windows/utils/string.h +++ b/flutter_inappwebview_windows/windows/utils/string.h @@ -1,6 +1,7 @@ #ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_ #define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_ +#include #include #include #include @@ -142,6 +143,39 @@ namespace flutter_inappwebview_plugin res.push_back(s.substr(pos_start)); return res; } + + template + void to_lowercase(const std::basic_string& s) + { + std::transform(s.begin(), s.end(), s.begin(), + [](const T v) { return static_cast(std::tolower(v)); }); + } + + template + std::basic_string to_lowercase_copy(const std::basic_string& s) + { + std::basic_string s2 = s; + std::transform(s2.begin(), s2.end(), s2.begin(), + [](const T v) { return static_cast(std::tolower(v)); }); + return s2; + } + + template + void to_uppercase(const std::basic_string& s) + { + std::transform(s.begin(), s.end(), s.begin(), + [](const T v) { return static_cast(std::toupper(v)); }); + return s2; + } + + template + std::basic_string to_uppercase_copy(const std::basic_string& s) + { + std::basic_string s2 = s; + std::transform(s2.begin(), s2.end(), s2.begin(), + [](const T v) { return static_cast(std::toupper(v)); }); + return s2; + } } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment.cpp b/flutter_inappwebview_windows/windows/webview_environment/webview_environment.cpp new file mode 100644 index 00000000..575f0d33 --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment.cpp @@ -0,0 +1,59 @@ +#include +#include + +#include "../utils/log.h" +#include "webview_environment.h" + +namespace flutter_inappwebview_plugin +{ + using namespace Microsoft::WRL; + + WebViewEnvironment::WebViewEnvironment(const FlutterInappwebviewWindowsPlugin* plugin, const std::string& id) + : plugin(plugin), id(id), + channelDelegate(std::make_unique(this, plugin->registrar->messenger())) + {} + + void WebViewEnvironment::create(const std::unique_ptr settings, const std::function completionHandler) + { + auto options = Make(); + if (settings) { + if (settings->additionalBrowserArguments.has_value()) { + options->put_AdditionalBrowserArguments(utf8_to_wide(settings->additionalBrowserArguments.value()).c_str()); + } + if (settings->allowSingleSignOnUsingOSPrimaryAccount.has_value()) { + options->put_AllowSingleSignOnUsingOSPrimaryAccount(settings->allowSingleSignOnUsingOSPrimaryAccount.value()); + } + if (settings->language.has_value()) { + options->put_Language(utf8_to_wide(settings->language.value()).c_str()); + } + if (settings->targetCompatibleBrowserVersion.has_value()) { + options->put_TargetCompatibleBrowserVersion(utf8_to_wide(settings->targetCompatibleBrowserVersion.value()).c_str()); + } + } + + auto hr = CreateCoreWebView2EnvironmentWithOptions( + settings && settings->browserExecutableFolder.has_value() ? utf8_to_wide(settings->browserExecutableFolder.value()).c_str() : nullptr, + settings && settings->userDataFolder.has_value() ? utf8_to_wide(settings->userDataFolder.value()).c_str() : nullptr, + options.Get(), + Callback( + [this, completionHandler](HRESULT result, wil::com_ptr environment) -> HRESULT + { + if (succeededOrLog(result)) { + env = std::move(environment); + } + if (completionHandler) { + completionHandler(result); + } + return S_OK; + }).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler(hr); + } + } + + WebViewEnvironment::~WebViewEnvironment() + { + debugLog("dealloc WebViewEnvironment"); + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment.h b/flutter_inappwebview_windows/windows/webview_environment/webview_environment.h new file mode 100644 index 00000000..849b48ba --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment.h @@ -0,0 +1,30 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_H_ + +#include +#include +#include + +#include "../flutter_inappwebview_windows_plugin.h" +#include "webview_environment_channel_delegate.h" +#include "webview_environment_settings.h" + +namespace flutter_inappwebview_plugin +{ + class WebViewEnvironment + { + public: + static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_webview_environment_"; + + const FlutterInappwebviewWindowsPlugin* plugin; + std::string id; + wil::com_ptr env; + std::unique_ptr channelDelegate; + + WebViewEnvironment(const FlutterInappwebviewWindowsPlugin* plugin, const std::string& id); + ~WebViewEnvironment(); + + void create(const std::unique_ptr settings, const std::function completionHandler); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.cpp b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.cpp new file mode 100644 index 00000000..d5b9e4d2 --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.cpp @@ -0,0 +1,47 @@ +#include "../utils/flutter.h" +#include "../utils/log.h" +#include "../utils/strconv.h" +#include "../utils/string.h" +#include "webview_environment.h" +#include "webview_environment_channel_delegate.h" + +#include "webview_environment_manager.h" + +namespace flutter_inappwebview_plugin +{ + WebViewEnvironmentChannelDelegate::WebViewEnvironmentChannelDelegate(WebViewEnvironment* webViewEnv, flutter::BinaryMessenger* messenger) + : webViewEnvironment(webViewEnv), ChannelDelegate(messenger, WebViewEnvironment::METHOD_CHANNEL_NAME_PREFIX + webViewEnv->id) + {} + + void WebViewEnvironmentChannelDelegate::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + if (!webViewEnvironment) { + result->Success(); + return; + } + + // auto& arguments = std::get(*method_call.arguments()); + auto& methodName = method_call.method_name(); + + if (string_equals(methodName, "dispose")) { + if (webViewEnvironment->plugin && webViewEnvironment->plugin->webViewEnvironmentManager) { + std::map>& webViewEnvironments = webViewEnvironment->plugin->webViewEnvironmentManager->webViewEnvironments; + auto& id = webViewEnvironment->id; + if (map_contains(webViewEnvironments, id)) { + webViewEnvironments.erase(id); + } + } + result->Success(); + } + else { + result->NotImplemented(); + } + } + + WebViewEnvironmentChannelDelegate::~WebViewEnvironmentChannelDelegate() + { + debugLog("dealloc WebViewEnvironmentChannelDelegate"); + webViewEnvironment = nullptr; + } +} diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.h b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.h new file mode 100644 index 00000000..b98e67ab --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_channel_delegate.h @@ -0,0 +1,25 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CHANNEL_DELEGATE_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CHANNEL_DELEGATE_H_ + +#include "../types/channel_delegate.h" +#include + +namespace flutter_inappwebview_plugin +{ + class WebViewEnvironment; + + class WebViewEnvironmentChannelDelegate : public ChannelDelegate + { + public: + WebViewEnvironment* webViewEnvironment; + + WebViewEnvironmentChannelDelegate(WebViewEnvironment* webViewEnv, flutter::BinaryMessenger* messenger); + ~WebViewEnvironmentChannelDelegate(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CHANNEL_DELEGATE_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp new file mode 100644 index 00000000..b0b492b4 --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp @@ -0,0 +1,63 @@ +#include +#include +#include + +#include "../in_app_webview/in_app_webview_settings.h" +#include "../utils/flutter.h" +#include "../utils/log.h" +#include "../utils/string.h" +#include "../utils/vector.h" +#include "webview_environment_manager.h" + +namespace flutter_inappwebview_plugin +{ + WebViewEnvironmentManager::WebViewEnvironmentManager(const FlutterInappwebviewWindowsPlugin* plugin) + : plugin(plugin), + ChannelDelegate(plugin->registrar->messenger(), WebViewEnvironmentManager::METHOD_CHANNEL_NAME) + {} + + void WebViewEnvironmentManager::HandleMethodCall(const flutter::MethodCall& method_call, + std::unique_ptr> result) + { + auto* arguments = std::get_if(method_call.arguments()); + auto& methodName = method_call.method_name(); + + if (string_equals(methodName, "create")) { + auto id = get_fl_map_value(*arguments, "id"); + auto settingsMap = get_optional_fl_map_value(*arguments, "settings"); + auto settings = settingsMap.has_value() ? std::make_unique(settingsMap.value()) : nullptr; + createWebViewEnvironment(id, std::move(settings), std::move(result)); + } + else { + result->NotImplemented(); + } + } + + void WebViewEnvironmentManager::createWebViewEnvironment(const std::string& id, std::unique_ptr settings, std::unique_ptr> result) + { + auto result_ = std::shared_ptr>(std::move(result)); + + auto webViewEnvironment = std::make_unique(plugin, id); + webViewEnvironment->create(std::move(settings), + [this, id, result_](HRESULT errorCode) + { + if (succeededOrLog(errorCode)) { + result_->Success(true); + } + else { + result_->Error("0", "Cannot create WebViewEnvironment: " + getHRMessage(errorCode)); + if (map_contains(webViewEnvironments, id)) { + webViewEnvironments.erase(id); + } + } + }); + webViewEnvironments.insert({ id, std::move(webViewEnvironment) }); + } + + WebViewEnvironmentManager::~WebViewEnvironmentManager() + { + debugLog("dealloc WebViewEnvironmentManager"); + webViewEnvironments.clear(); + plugin = nullptr; + } +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h new file mode 100644 index 00000000..59c25b50 --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h @@ -0,0 +1,33 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_MANAGER_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_MANAGER_H_ + +#include +#include +#include +#include + +#include "../flutter_inappwebview_windows_plugin.h" +#include "../types/channel_delegate.h" +#include "webview_environment.h" + +namespace flutter_inappwebview_plugin +{ + class WebViewEnvironmentManager : public ChannelDelegate + { + public: + static inline const std::string METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_webview_environment"; + + const FlutterInappwebviewWindowsPlugin* plugin; + std::map> webViewEnvironments; + + WebViewEnvironmentManager(const FlutterInappwebviewWindowsPlugin* plugin); + ~WebViewEnvironmentManager(); + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + void createWebViewEnvironment(const std::string& id, std::unique_ptr settings, std::unique_ptr> result); + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_MANAGER_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.cpp b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.cpp new file mode 100644 index 00000000..24091a7d --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.cpp @@ -0,0 +1,27 @@ +#include "../utils/flutter.h" +#include "webview_environment_settings.h" + +namespace flutter_inappwebview_plugin +{ + WebViewEnvironmentSettings::WebViewEnvironmentSettings(const flutter::EncodableMap& map) + : browserExecutableFolder(get_optional_fl_map_value(map, "browserExecutableFolder")), + userDataFolder(get_optional_fl_map_value(map, "userDataFolder")), + additionalBrowserArguments(get_optional_fl_map_value(map, "additionalBrowserArguments")), + allowSingleSignOnUsingOSPrimaryAccount(get_optional_fl_map_value(map, "allowSingleSignOnUsingOSPrimaryAccount")), + language(get_optional_fl_map_value(map, "language")), + targetCompatibleBrowserVersion(get_optional_fl_map_value(map, "targetCompatibleBrowserVersion")) + {} + + flutter::EncodableMap WebViewEnvironmentSettings::toEncodableMap() const + { + return flutter::EncodableMap{ + {"browserExecutableFolder", make_fl_value(browserExecutableFolder)}, + {"userDataFolder", make_fl_value(userDataFolder)}, + {"additionalBrowserArguments", make_fl_value(additionalBrowserArguments)}, + {"allowSingleSignOnUsingOSPrimaryAccount", make_fl_value(allowSingleSignOnUsingOSPrimaryAccount)}, + {"language", make_fl_value(language)}, + {"targetCompatibleBrowserVersion", make_fl_value(targetCompatibleBrowserVersion)} + }; + } + +} \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.h b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.h new file mode 100644 index 00000000..12d1388c --- /dev/null +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_settings.h @@ -0,0 +1,29 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CREATION_PARAMS_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CREATION_PARAMS_H_ + +#include +#include +#include + +#include "../flutter_inappwebview_windows_plugin.h" + +namespace flutter_inappwebview_plugin +{ + class WebViewEnvironmentSettings + { + public: + const std::optional browserExecutableFolder; + const std::optional userDataFolder; + const std::optional additionalBrowserArguments; + const std::optional allowSingleSignOnUsingOSPrimaryAccount; + const std::optional language; + const std::optional targetCompatibleBrowserVersion; + + WebViewEnvironmentSettings() = default; + WebViewEnvironmentSettings(const flutter::EncodableMap& map); + ~WebViewEnvironmentSettings() = default; + + flutter::EncodableMap toEncodableMap() const; + }; +} +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_CREATION_PARAMS_H_ \ No newline at end of file From c452082219137a12eb5ddd7a531576ad6cf4e476 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 27 Jan 2024 18:53:27 +0100 Subject: [PATCH 16/21] windows: fixed some c++ include --- .../windows/in_app_webview/in_app_webview.cpp | 3 +-- .../windows/in_app_webview/in_app_webview.h | 2 +- .../windows/in_app_webview/in_app_webview_manager.cpp | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index fc69518a..530dc7a5 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -13,9 +13,8 @@ #include "../utils/map.h" #include "../utils/strconv.h" #include "../utils/string.h" +#include "../webview_environment/webview_environment_manager.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 diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index ddf21439..dfc0082d 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -15,7 +15,7 @@ #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 "../types/screenshot_configuration.h" #include "in_app_webview_settings.h" #include "user_content_controller.h" #include "webview_channel_delegate.h" diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp index 6f4296dc..f3ebce7e 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp @@ -12,8 +12,7 @@ #include "../utils/string.h" #include "../utils/vector.h" #include "in_app_webview_manager.h" - -#include ".plugin_symlinks/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h" +#include "../webview_environment/webview_environment_manager.h" namespace flutter_inappwebview_plugin { From db9ff8c4e6c80b6bb4fc9d240c22e36b0669e33c Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 27 Jan 2024 21:36:37 +0100 Subject: [PATCH 17/21] windows: added WebViewEnvironment support for headlessinappwebview and inappbrowser --- .../lib/headless_in_app_webview.screen.dart | 1 + .../lib/in_app_browser_example.screen.dart | 3 ++- .../Flutter/GeneratedPluginRegistrant.swift | 2 ++ flutter_inappwebview/example/pubspec.yaml | 16 ++++++++-------- .../lib/src/in_app_browser/in_app_browser.dart | 8 ++++++-- .../in_app_webview/headless_in_app_webview.dart | 3 +++ flutter_inappwebview/pubspec.yaml | 11 +++++------ .../in_app_browser/platform_in_app_browser.dart | 11 ++++++++++- .../platform_headless_in_app_webview.dart | 8 ++++++++ .../lib/src/in_app_browser/in_app_browser.dart | 11 +++++++++-- .../in_app_webview/headless_in_app_webview.dart | 9 ++++++++- .../lib/src/in_app_webview/in_app_webview.dart | 8 ++++++-- .../windows/cookie_manager.cpp | 8 +------- .../headless_in_app_webview_manager.cpp | 9 ++------- .../windows/in_app_browser/in_app_browser.cpp | 8 +------- .../in_app_browser/in_app_browser_manager.cpp | 2 +- .../windows/in_app_webview/in_app_webview.cpp | 10 +++++----- .../windows/in_app_webview/in_app_webview.h | 9 ++------- .../in_app_webview/in_app_webview_manager.cpp | 11 +++-------- 19 files changed, 83 insertions(+), 65 deletions(-) diff --git a/flutter_inappwebview/example/lib/headless_in_app_webview.screen.dart b/flutter_inappwebview/example/lib/headless_in_app_webview.screen.dart index 87e3dc20..5bfa5db1 100755 --- a/flutter_inappwebview/example/lib/headless_in_app_webview.screen.dart +++ b/flutter_inappwebview/example/lib/headless_in_app_webview.screen.dart @@ -24,6 +24,7 @@ class _HeadlessInAppWebViewExampleScreenState : WebUri("http://localhost:${Uri.base.port}/page.html"); headlessWebView = HeadlessInAppWebView( + webViewEnvironment: webViewEnvironment, initialUrlRequest: URLRequest(url: url), initialSettings: InAppWebViewSettings( isInspectable: kDebugMode, diff --git a/flutter_inappwebview/example/lib/in_app_browser_example.screen.dart b/flutter_inappwebview/example/lib/in_app_browser_example.screen.dart index 15f2bd7b..e063f85c 100755 --- a/flutter_inappwebview/example/lib/in_app_browser_example.screen.dart +++ b/flutter_inappwebview/example/lib/in_app_browser_example.screen.dart @@ -17,7 +17,8 @@ class MyInAppBrowser extends InAppBrowser { : super( windowId: windowId, initialUserScripts: initialUserScripts, - pullToRefreshController: pullToRefreshController); + pullToRefreshController: pullToRefreshController, + webViewEnvironment: webViewEnvironment,); @override Future onBrowserCreated() async { diff --git a/flutter_inappwebview/example/macos/Flutter/GeneratedPluginRegistrant.swift b/flutter_inappwebview/example/macos/Flutter/GeneratedPluginRegistrant.swift index a1cdfd0c..959e5aa0 100644 --- a/flutter_inappwebview/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/flutter_inappwebview/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,10 +5,12 @@ import FlutterMacOS import Foundation +import flutter_inappwebview_macos import path_provider_foundation import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/flutter_inappwebview/example/pubspec.yaml b/flutter_inappwebview/example/pubspec.yaml index dffe2a82..59aa16aa 100755 --- a/flutter_inappwebview/example/pubspec.yaml +++ b/flutter_inappwebview/example/pubspec.yaml @@ -35,14 +35,14 @@ dependencies: dependency_overrides: flutter_inappwebview_platform_interface: path: ../../flutter_inappwebview_platform_interface - #flutter_inappwebview_android: - # path: ../../flutter_inappwebview_android - #flutter_inappwebview_ios: - # path: ../../flutter_inappwebview_ios - #flutter_inappwebview_macos: - # path: ../../flutter_inappwebview_macos - #flutter_inappwebview_web: - # path: ../../flutter_inappwebview_web + flutter_inappwebview_android: + path: ../../flutter_inappwebview_android + flutter_inappwebview_ios: + path: ../../flutter_inappwebview_ios + flutter_inappwebview_macos: + path: ../../flutter_inappwebview_macos + flutter_inappwebview_web: + path: ../../flutter_inappwebview_web flutter_inappwebview_windows: path: ../../flutter_inappwebview_windows diff --git a/flutter_inappwebview/lib/src/in_app_browser/in_app_browser.dart b/flutter_inappwebview/lib/src/in_app_browser/in_app_browser.dart index 3393cafd..b17a68a8 100755 --- a/flutter_inappwebview/lib/src/in_app_browser/in_app_browser.dart +++ b/flutter_inappwebview/lib/src/in_app_browser/in_app_browser.dart @@ -11,6 +11,7 @@ import '../find_interaction/find_interaction_controller.dart'; import '../pull_to_refresh/main.dart'; import '../in_app_webview/in_app_webview_controller.dart'; +import '../webview_environment/webview_environment.dart'; ///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser} class InAppBrowser implements PlatformInAppBrowserEvents { @@ -22,14 +23,17 @@ class InAppBrowser implements PlatformInAppBrowserEvents { PullToRefreshController? pullToRefreshController, FindInteractionController? findInteractionController, UnmodifiableListView? initialUserScripts, - int? windowId}) + int? windowId, + WebViewEnvironment? webViewEnvironment, + }) : this.fromPlatformCreationParams( PlatformInAppBrowserCreationParams( contextMenu: contextMenu, pullToRefreshController: pullToRefreshController?.platform, findInteractionController: findInteractionController?.platform, initialUserScripts: initialUserScripts, - windowId: windowId), + windowId: windowId, + webViewEnvironment: webViewEnvironment?.platform,), ); /// Constructs a [InAppBrowser] from creation params for a specific diff --git a/flutter_inappwebview/lib/src/in_app_webview/headless_in_app_webview.dart b/flutter_inappwebview/lib/src/in_app_webview/headless_in_app_webview.dart index 5f714d67..f7c21662 100644 --- a/flutter_inappwebview/lib/src/in_app_webview/headless_in_app_webview.dart +++ b/flutter_inappwebview/lib/src/in_app_webview/headless_in_app_webview.dart @@ -5,6 +5,7 @@ import 'dart:ui'; import 'package:flutter/services.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; import '../find_interaction/find_interaction_controller.dart'; +import '../webview_environment/webview_environment.dart'; import 'in_app_webview_controller.dart'; import '../pull_to_refresh/pull_to_refresh_controller.dart'; @@ -42,6 +43,7 @@ class HeadlessInAppWebView { HeadlessInAppWebView? headlessWebView, InAppWebViewKeepAlive? keepAlive, bool? preventGestureDelay, + WebViewEnvironment? webViewEnvironment, @Deprecated('Use onGeolocationPermissionsHidePrompt instead') void Function(InAppWebViewController controller)? androidOnGeolocationPermissionsHidePrompt, @@ -307,6 +309,7 @@ class HeadlessInAppWebView { pullToRefreshController: pullToRefreshController?.platform, findInteractionController: findInteractionController?.platform, contextMenu: contextMenu, + webViewEnvironment: webViewEnvironment?.platform, onWebViewCreated: onWebViewCreated != null ? (controller) => onWebViewCreated.call(controller) : null, diff --git a/flutter_inappwebview/pubspec.yaml b/flutter_inappwebview/pubspec.yaml index 03b3d522..ef480d4a 100755 --- a/flutter_inappwebview/pubspec.yaml +++ b/flutter_inappwebview/pubspec.yaml @@ -18,12 +18,11 @@ environment: dependencies: flutter: sdk: flutter - 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 + 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 dev_dependencies: flutter_test: diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart index b2567865..c1ebeefe 100755 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart @@ -19,6 +19,7 @@ import '../in_app_webview/in_app_webview_settings.dart'; import '../print_job/main.dart'; import '../web_uri.dart'; +import '../webview_environment/platform_webview_environment.dart'; import 'in_app_browser_menu_item.dart'; import 'in_app_browser_settings.dart'; import '../debug_logging_settings.dart'; @@ -36,7 +37,9 @@ class PlatformInAppBrowserCreationParams { this.pullToRefreshController, this.findInteractionController, this.initialUserScripts, - this.windowId}); + this.windowId, + this.webViewEnvironment, + }); ///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser.contextMenu} final ContextMenu? contextMenu; @@ -52,6 +55,12 @@ class PlatformInAppBrowserCreationParams { ///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser.windowId} final int? windowId; + + ///Used create the [PlatformInAppBrowser] using the specified environment. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows + final PlatformWebViewEnvironment? webViewEnvironment; } ///{@template flutter_inappwebview_platform_interface.PlatformInAppBrowser} diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_headless_in_app_webview.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_headless_in_app_webview.dart index b953d3b4..9fd5c7ab 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_headless_in_app_webview.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_headless_in_app_webview.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; 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 'platform_inappwebview_controller.dart'; import 'platform_webview.dart'; @@ -17,6 +18,7 @@ class PlatformHeadlessInAppWebViewCreationParams /// Used by the platform implementation to create a new [PlatformHeadlessInAppWebView]. const PlatformHeadlessInAppWebViewCreationParams( {this.initialSize = const Size(-1, -1), + this.webViewEnvironment, super.controllerFromPlatform, super.windowId, super.onWebViewCreated, @@ -141,6 +143,12 @@ class PlatformHeadlessInAppWebViewCreationParams ///- Web ///- MacOS final Size initialSize; + + ///Used create the [PlatformHeadlessInAppWebView] using the specified environment. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows + final PlatformWebViewEnvironment? webViewEnvironment; } ///{@template flutter_inappwebview_platform_interface.PlatformHeadlessInAppWebView} diff --git a/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart b/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart index 4c1f892d..7920c613 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_browser/in_app_browser.dart @@ -7,6 +7,7 @@ import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_pla import '../find_interaction/find_interaction_controller.dart'; import '../in_app_webview/in_app_webview_controller.dart'; +import '../webview_environment/webview_environment.dart'; /// Object specifying creation parameters for creating a [WindowsInAppBrowser]. /// @@ -21,7 +22,8 @@ class WindowsInAppBrowserCreationParams super.pullToRefreshController, this.findInteractionController, super.initialUserScripts, - super.windowId}); + super.windowId, + this.webViewEnvironment}); /// Creates a [WindowsInAppBrowserCreationParams] instance based on [PlatformInAppBrowserCreationParams]. factory WindowsInAppBrowserCreationParams.fromPlatformInAppBrowserCreationParams( @@ -34,11 +36,15 @@ class WindowsInAppBrowserCreationParams findInteractionController: params.findInteractionController as WindowsFindInteractionController?, initialUserScripts: params.initialUserScripts, - windowId: params.windowId); + windowId: params.windowId, + webViewEnvironment: params.webViewEnvironment as WindowsWebViewEnvironment?); } @override final WindowsFindInteractionController? findInteractionController; + + @override + final WindowsWebViewEnvironment? webViewEnvironment; } ///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser} @@ -168,6 +174,7 @@ class WindowsInAppBrowser extends PlatformInAppBrowser with ChannelController { () => initialUserScripts?.map((e) => e.toMap()).toList() ?? []); args.putIfAbsent('pullToRefreshSettings', () => pullToRefreshSettings); args.putIfAbsent('menuItems', () => menuItemList); + args.putIfAbsent('webViewEnvironmentId', () => _windowsParams.webViewEnvironment?.id); return args; } diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart index 0f6299a7..1bede448 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; import '../find_interaction/find_interaction_controller.dart'; +import '../webview_environment/webview_environment.dart'; import 'in_app_webview_controller.dart'; /// Object specifying creation parameters for creating a [WindowsHeadlessInAppWebView]. @@ -18,6 +19,7 @@ class WindowsHeadlessInAppWebViewCreationParams WindowsHeadlessInAppWebViewCreationParams( {super.controllerFromPlatform, super.initialSize, + this.webViewEnvironment, super.windowId, super.onWebViewCreated, super.onLoadStart, @@ -133,6 +135,7 @@ class WindowsHeadlessInAppWebViewCreationParams PlatformHeadlessInAppWebViewCreationParams params) : this( controllerFromPlatform: params.controllerFromPlatform, + webViewEnvironment: params.webViewEnvironment as WindowsWebViewEnvironment?, initialSize: params.initialSize, windowId: params.windowId, onWebViewCreated: params.onWebViewCreated, @@ -242,6 +245,9 @@ class WindowsHeadlessInAppWebViewCreationParams @override final WindowsFindInteractionController? findInteractionController; + + @override + final WindowsWebViewEnvironment? webViewEnvironment; } ///{@macro flutter_inappwebview_platform_interface.PlatformHeadlessInAppWebView} @@ -339,7 +345,8 @@ class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView params.initialUserScripts?.map((e) => e.toMap()).toList() ?? [], 'pullToRefreshSettings': pullToRefreshSettings, - 'initialSize': params.initialSize.toMap() + 'initialSize': params.initialSize.toMap(), + 'webViewEnvironmentId': params.webViewEnvironment?.id, }); try { await _sharedChannel.invokeMethod('run', args); diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart index df3d744a..61f44880 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.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 '../find_interaction/find_interaction_controller.dart'; @@ -23,7 +24,7 @@ class WindowsInAppWebViewWidgetCreationParams super.keepAlive, super.preventGestureDelay, super.windowId, - super.webViewEnvironment, + this.webViewEnvironment, super.onWebViewCreated, super.onLoadStart, super.onLoadStop, @@ -146,7 +147,7 @@ class WindowsInAppWebViewWidgetCreationParams keepAlive: params.keepAlive, preventGestureDelay: params.preventGestureDelay, windowId: params.windowId, - webViewEnvironment: params.webViewEnvironment, + webViewEnvironment: params.webViewEnvironment as WindowsWebViewEnvironment?, onWebViewCreated: params.onWebViewCreated, onLoadStart: params.onLoadStart, onLoadStop: params.onLoadStop, @@ -254,6 +255,9 @@ class WindowsInAppWebViewWidgetCreationParams @override final WindowsFindInteractionController? findInteractionController; + + @override + final WindowsWebViewEnvironment? webViewEnvironment; } ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewWidget} diff --git a/flutter_inappwebview_windows/windows/cookie_manager.cpp b/flutter_inappwebview_windows/windows/cookie_manager.cpp index d962755d..23500931 100644 --- a/flutter_inappwebview_windows/windows/cookie_manager.cpp +++ b/flutter_inappwebview_windows/windows/cookie_manager.cpp @@ -26,12 +26,7 @@ namespace flutter_inappwebview_plugin nullptr, windowClass_.hInstance, nullptr); - CreateInAppWebViewEnvParams webViewEnvParams = { - hwnd, - false - }; - - InAppWebView::createInAppWebViewEnv(webViewEnvParams, nullptr, + InAppWebView::createInAppWebViewEnv(hwnd, false, nullptr, [=](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) @@ -80,7 +75,6 @@ namespace flutter_inappwebview_plugin 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(); diff --git a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp index 9c6e3d9e..ff235315 100644 --- a/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp +++ b/flutter_inappwebview_windows/windows/headless_in_app_webview/headless_in_app_webview_manager.cpp @@ -56,7 +56,7 @@ namespace flutter_inappwebview_plugin auto initialFile = get_optional_fl_map_value(params, "initialFile"); auto initialDataMap = get_optional_fl_map_value(params, "initialData"); auto initialUserScriptList = get_optional_fl_map_value(params, "initialUserScripts"); - auto webViewEnvironmentId = get_optional_fl_map_value(*arguments, "webViewEnvironmentId"); + auto webViewEnvironmentId = get_optional_fl_map_value(params, "webViewEnvironmentId"); RECT bounds; GetClientRect(plugin->registrar->GetView()->GetNativeWindow(), &bounds); @@ -70,15 +70,10 @@ namespace flutter_inappwebview_plugin nullptr, windowClass_.hInstance, nullptr); - 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, + InAppWebView::createInAppWebViewEnv(hwnd, false, webViewEnvironment, [=](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index baa749a5..682c4aa5 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -42,12 +42,6 @@ namespace flutter_inappwebview_plugin ShowWindow(m_hWnd, settings->hidden ? SW_HIDE : SW_SHOW); - CreateInAppWebViewEnvParams webViewEnvParams = { - m_hWnd, - false - }; - - InAppWebViewCreationParams webViewParams = { id, params.initialWebViewSettings, @@ -57,7 +51,7 @@ namespace flutter_inappwebview_plugin 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, + InAppWebView::createInAppWebViewEnv(m_hWnd, false, webViewEnvironment, [this, params, webViewParams](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) -> void { if (webViewEnv && webViewController) { diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp index b414c806..46130067 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_manager.cpp @@ -26,7 +26,7 @@ namespace flutter_inappwebview_plugin if (string_equals(methodName, "open")) { createInAppBrowser(arguments); - result->Success(flutter::EncodableValue(true)); + result->Success(true); } else if (string_equals(methodName, "openWithSystemBrowser")) { auto url = get_fl_map_value(*arguments, "url"); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index 530dc7a5..9540048b 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -57,11 +57,11 @@ namespace flutter_inappwebview_plugin this->inAppBrowser = inAppBrowser; } - void InAppWebView::createInAppWebViewEnv(const CreateInAppWebViewEnvParams& params, const WebViewEnvironment* webViewEnvironment, std::function webViewEnv, + void InAppWebView::createInAppWebViewEnv(const HWND parentWindow, const bool& willBeSurface, const WebViewEnvironment* webViewEnvironment, std::function webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController)> completionHandler) { - auto callback = [params, completionHandler](HRESULT result, wil::com_ptr env) -> HRESULT + auto callback = [parentWindow, willBeSurface, completionHandler](HRESULT result, wil::com_ptr env) -> HRESULT { if (failedAndLog(result) || !env) { completionHandler(nullptr, nullptr, nullptr); @@ -69,8 +69,8 @@ namespace flutter_inappwebview_plugin } wil::com_ptr webViewEnv3; - if (params.willBeSurface && succeededOrLog(env->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) { - failedLog(webViewEnv3->CreateCoreWebView2CompositionController(params.parentWindow, Callback( + if (willBeSurface && succeededOrLog(env->QueryInterface(IID_PPV_ARGS(&webViewEnv3)))) { + failedLog(webViewEnv3->CreateCoreWebView2CompositionController(parentWindow, Callback( [completionHandler, env](HRESULT result, wil::com_ptr compositionController) -> HRESULT { wil::com_ptr webViewController = compositionController.try_query(); @@ -93,7 +93,7 @@ namespace flutter_inappwebview_plugin ).Get())); } else { - failedLog(env->CreateCoreWebView2Controller(params.parentWindow, Callback( + failedLog(env->CreateCoreWebView2Controller(parentWindow, Callback( [completionHandler, env](HRESULT result, wil::com_ptr controller) -> HRESULT { if (failedAndLog(result) || !controller) { diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index dfc0082d..3caf70ca 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -12,10 +12,10 @@ #include "../plugin_scripts_js/plugin_scripts_util.h" #include "../types/content_world.h" #include "../types/navigation_action.h" +#include "../types/screenshot_configuration.h" #include "../types/url_request.h" #include "../types/web_history.h" #include "../webview_environment/webview_environment.h" -#include "../types/screenshot_configuration.h" #include "in_app_webview_settings.h" #include "user_content_controller.h" #include "webview_channel_delegate.h" @@ -83,11 +83,6 @@ namespace flutter_inappwebview_plugin const std::optional>> initialUserScripts; }; - struct CreateInAppWebViewEnvParams { - const HWND parentWindow; - const bool willBeSurface; - }; - class InAppWebView { public: @@ -114,7 +109,7 @@ namespace flutter_inappwebview_plugin wil::com_ptr webViewCompositionController); ~InAppWebView(); - static void createInAppWebViewEnv(const CreateInAppWebViewEnvParams& params, const WebViewEnvironment* webViewEnvironment, std::function webViewEnv, + static void createInAppWebViewEnv(const HWND parentWindow, const bool& willBeSurface, const WebViewEnvironment* webViewEnvironment, std::function webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController)> completionHandler); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp index f3ebce7e..173e85a6 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_manager.cpp @@ -11,8 +11,8 @@ #include "../utils/log.h" #include "../utils/string.h" #include "../utils/vector.h" -#include "in_app_webview_manager.h" #include "../webview_environment/webview_environment_manager.h" +#include "in_app_webview_manager.h" namespace flutter_inappwebview_plugin { @@ -95,15 +95,10 @@ namespace flutter_inappwebview_plugin nullptr, windowClass_.hInstance, nullptr); - 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, + InAppWebView::createInAppWebViewEnv(hwnd, true, webViewEnvironment, [=](wil::com_ptr webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController) @@ -147,7 +142,7 @@ namespace flutter_inappwebview_plugin webViews.insert({ textureId, std::move(customPlatformView) }); - result_->Success(flutter::EncodableValue(textureId)); + result_->Success(textureId); } else { result_->Error("0", "Cannot create the InAppWebView instance!"); From 7da9cb264366466eaac2cf15aa3e552efa7300c9 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 28 Jan 2024 00:41:48 +0100 Subject: [PATCH 18/21] windows: updated webview environment docs, updated cookie manager to support webview environment --- .../lib/src/cookie_manager.dart | 19 +- .../platform_in_app_browser.dart | 2 +- .../platform_headless_in_app_webview.dart | 2 +- .../platform_inappwebview_widget.dart | 2 +- .../lib/src/platform_cookie_manager.dart | 9 +- .../platform_webview_environment.dart | 8 +- .../webview_environment_settings.dart | 66 +++++++ .../webview_environment_settings.g.dart | 70 +++++++ .../lib/src/cookie_manager.dart | 38 +++- .../windows/cookie_manager.cpp | 174 ++++++++++-------- .../windows/cookie_manager.h | 12 +- .../windows/in_app_webview/in_app_webview.cpp | 6 +- .../windows/in_app_webview/in_app_webview.h | 2 +- .../webview_environment.cpp | 43 ++++- .../webview_environment/webview_environment.h | 20 +- .../webview_environment_manager.cpp | 41 ++++- .../webview_environment_manager.h | 10 + 17 files changed, 407 insertions(+), 117 deletions(-) diff --git a/flutter_inappwebview/lib/src/cookie_manager.dart b/flutter_inappwebview/lib/src/cookie_manager.dart index 19afa277..11247bfe 100755 --- a/flutter_inappwebview/lib/src/cookie_manager.dart +++ b/flutter_inappwebview/lib/src/cookie_manager.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; import 'in_app_webview/in_app_webview_controller.dart'; +import 'webview_environment/webview_environment.dart'; ///{@macro flutter_inappwebview_platform_interface.PlatformCookieManager} class CookieManager { @@ -31,12 +32,22 @@ class CookieManager { static CookieManager? _instance; + WebViewEnvironment? _webViewEnvironment; + ///Gets the [CookieManager] shared instance. - static CookieManager instance() { - if (_instance == null) { - _instance = CookieManager(); + /// + ///[webViewEnvironment] (Supported only on Windows) - Used to create the [CookieManager] using the specified environment. + static CookieManager instance({WebViewEnvironment? webViewEnvironment}) { + if (webViewEnvironment == null) { + if (_instance == null) { + _instance = CookieManager(); + } + return _instance!; + } else { + return CookieManager.fromPlatformCreationParams( + PlatformCookieManagerCreationParams(webViewEnvironment: webViewEnvironment.platform) + ); } - return _instance!; } ///{@macro flutter_inappwebview_platform_interface.PlatformCookieManager.setCookie} diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart index c1ebeefe..45c29bb6 100755 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/platform_in_app_browser.dart @@ -56,7 +56,7 @@ class PlatformInAppBrowserCreationParams { ///{@macro flutter_inappwebview_platform_interface.PlatformInAppBrowser.windowId} final int? windowId; - ///Used create the [PlatformInAppBrowser] using the specified environment. + ///Used to create the [PlatformInAppBrowser] using the specified environment. /// ///**Officially Supported Platforms/Implementations**: ///- Windows diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_headless_in_app_webview.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_headless_in_app_webview.dart index 9fd5c7ab..99ea3b4f 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_headless_in_app_webview.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_headless_in_app_webview.dart @@ -144,7 +144,7 @@ class PlatformHeadlessInAppWebViewCreationParams ///- MacOS final Size initialSize; - ///Used create the [PlatformHeadlessInAppWebView] using the specified environment. + ///Used to create the [PlatformHeadlessInAppWebView] using the specified environment. /// ///**Officially Supported Platforms/Implementations**: ///- Windows diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart index 0f3aa670..e8194a92 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_widget.dart @@ -184,7 +184,7 @@ class PlatformInAppWebViewWidgetCreationParams ///- iOS final bool? preventGestureDelay; - ///Used create the [PlatformInAppWebViewWidget] using the specified environment. + ///Used to create the [PlatformInAppWebViewWidget] using the specified environment. /// ///**Officially Supported Platforms/Implementations**: ///- Windows diff --git a/flutter_inappwebview_platform_interface/lib/src/platform_cookie_manager.dart b/flutter_inappwebview_platform_interface/lib/src/platform_cookie_manager.dart index a3e98c5e..81bbb37a 100755 --- a/flutter_inappwebview_platform_interface/lib/src/platform_cookie_manager.dart +++ b/flutter_inappwebview_platform_interface/lib/src/platform_cookie_manager.dart @@ -8,6 +8,7 @@ import 'types/main.dart'; import 'web_uri.dart'; import 'inappwebview_platform.dart'; import 'in_app_webview/platform_headless_in_app_webview.dart'; +import 'webview_environment/platform_webview_environment.dart'; /// Object specifying creation parameters for creating a [PlatformCookieManager]. /// @@ -16,7 +17,13 @@ import 'in_app_webview/platform_headless_in_app_webview.dart'; @immutable class PlatformCookieManagerCreationParams { /// Used by the platform implementation to create a new [PlatformCookieManager]. - const PlatformCookieManagerCreationParams(); + const PlatformCookieManagerCreationParams({this.webViewEnvironment}); + + ///Used to create the [PlatformCookieManager] using the specified environment. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows + final PlatformWebViewEnvironment? webViewEnvironment; } ///{@template flutter_inappwebview_platform_interface.PlatformCookieManager} diff --git a/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart b/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart index 9bbe5bb5..d5c156c9 100644 --- a/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart +++ b/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart @@ -88,7 +88,13 @@ abstract class PlatformWebViewEnvironment extends PlatformInterface WebViewEnvironmentSettings? get settings => params.settings; ///{@template flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.create} - ///Initializes the [PlatformWebViewEnvironment] using [settings]. + ///Creates the [PlatformWebViewEnvironment] using [settings]. + /// + ///Check https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#createcorewebview2environmentwithoptions + ///for more info. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - CreateCoreWebView2EnvironmentWithOptions](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#createcorewebview2environmentwithoptions)) ///{@endtemplate} Future create( {WebViewEnvironmentSettings? settings}) { diff --git a/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.dart b/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.dart index 6c14a6d4..0a7ee483 100644 --- a/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.dart +++ b/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.dart @@ -5,13 +5,79 @@ import 'platform_webview_environment.dart'; part 'webview_environment_settings.g.dart'; ///This class represents all the [PlatformWebViewEnvironment] settings available. +/// +///The [browserExecutableFolder], [userDataFolder] and [additionalBrowserArguments] +///may be overridden by values either specified in environment variables or in the registry. +@SupportedPlatforms(platforms: [ + WindowsPlatform() +]) @ExchangeableObject(copyMethod: true) class WebViewEnvironmentSettings_ { + ///Use [browserExecutableFolder] to specify whether WebView2 controls use a fixed + ///or installed version of the WebView2 Runtime that exists on a user machine. + ///To use a fixed version of the WebView2 Runtime, pass the folder path that contains + ///the fixed version of the WebView2 Runtime to [browserExecutableFolder]. + ///BrowserExecutableFolder supports both relative (to the application's executable) and absolute files paths. + ///To create WebView2 controls that use the installed version of the WebView2 Runtime that exists on user machines, + ///pass a `null` or empty string to [browserExecutableFolder]. + ///In this scenario, the API tries to find a compatible version of the WebView2 Runtime + ///that is installed on the user machine (first at the machine level, and then per user) using the selected channel preference. + ///The path of fixed version of the WebView2 Runtime should not contain `\Edge\Application\`. + ///When such a path is used, the API fails with `HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)`. + /// + ///The default channel search order is the WebView2 Runtime, Beta, Dev, and Canary. + ///When an override `WEBVIEW2_RELEASE_CHANNEL_PREFERENCE` environment variable or + ///applicable `releaseChannelPreference` registry value is set to `1`, the channel search order is reversed. + @SupportedPlatforms(platforms: [ + WindowsPlatform(apiName: 'CreateCoreWebView2EnvironmentWithOptions.browserExecutableFolder', apiUrl: 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#createcorewebview2environmentwithoptions') + ]) final String? browserExecutableFolder; + + ///You may specify the [userDataFolder] to change the default user data folder location for WebView2. + ///The path is either an absolute file path or a relative file path that is interpreted as relative + ///to the compiled code for the current process. + ///For UWP apps, the default user data folder is the app data folder for the package. + ///For non-UWP apps, the default user data (`{Executable File Name}.WebView2`) folder + ///is created in the same directory next to the compiled code for the app. + ///WebView2 creation fails if the compiled code is running in a directory in which the + ///process does not have permission to create a new directory. + ///The app is responsible to clean up the associated user data folder when it is done. + /// + ///**NOTE**: As a browser process may be shared among WebViews, + ///WebView creation fails with `HRESULT_FROM_WIN32(ERROR_INVALID_STATE)` if the specified + ///options does not match the options of the WebViews that are currently + ///running in the shared browser process. + @SupportedPlatforms(platforms: [ + WindowsPlatform(apiName: 'CreateCoreWebView2EnvironmentWithOptions.userDataFolder', apiUrl: 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#createcorewebview2environmentwithoptions') + ]) final String? userDataFolder; + + ///If there are multiple switches, there should be a space in between them. + ///The one exception is if multiple features are being enabled/disabled for a single switch, + ///in which case the features should be comma-seperated. + ///Example: `"--disable-features=feature1,feature2 --some-other-switch --do-something"` + @SupportedPlatforms(platforms: [ + WindowsPlatform(apiName: 'ICoreWebView2EnvironmentOptions.put_AdditionalBrowserArguments', apiUrl: 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environmentoptions?view=webview2-1.0.2210.55#put_additionalbrowserarguments') + ]) final String? additionalBrowserArguments; + + ///This property is used to enable single sign on with Azure Active Directory (AAD) + ///and personal Microsoft Account (MSA) resources inside WebView. + @SupportedPlatforms(platforms: [ + WindowsPlatform(apiName: 'ICoreWebView2EnvironmentOptions.put_AllowSingleSignOnUsingOSPrimaryAccount', apiUrl: 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environmentoptions?view=webview2-1.0.2210.55#put_allowsinglesignonusingosprimaryaccount') + ]) final bool? allowSingleSignOnUsingOSPrimaryAccount; + + ///The default display language for WebView. + @SupportedPlatforms(platforms: [ + WindowsPlatform(apiName: 'ICoreWebView2EnvironmentOptions.put_Language', apiUrl: 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environmentoptions?view=webview2-1.0.2210.55#put_language') + ]) final String? language; + + ///Specifies the version of the WebView2 Runtime binaries required to be compatible with your app. + @SupportedPlatforms(platforms: [ + WindowsPlatform(apiName: 'ICoreWebView2EnvironmentOptions.put_TargetCompatibleBrowserVersion', apiUrl: 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environmentoptions?view=webview2-1.0.2210.55#put_targetcompatiblebrowserversion') + ]) final String? targetCompatibleBrowserVersion; WebViewEnvironmentSettings_({ diff --git a/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.g.dart b/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.g.dart index c89b9f53..eb62b39f 100644 --- a/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/webview_environment/webview_environment_settings.g.dart @@ -7,13 +7,83 @@ part of 'webview_environment_settings.dart'; // ************************************************************************** ///This class represents all the [PlatformWebViewEnvironment] settings available. +/// +///The [browserExecutableFolder], [userDataFolder] and [additionalBrowserArguments] +///may be overridden by values either specified in environment variables or in the registry. +/// +///**Officially Supported Platforms/Implementations**: +///- Windows class WebViewEnvironmentSettings { + ///If there are multiple switches, there should be a space in between them. + ///The one exception is if multiple features are being enabled/disabled for a single switch, + ///in which case the features should be comma-seperated. + ///Example: `"--disable-features=feature1,feature2 --some-other-switch --do-something"` + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - ICoreWebView2EnvironmentOptions.put_AdditionalBrowserArguments](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environmentoptions?view=webview2-1.0.2210.55#put_additionalbrowserarguments)) final String? additionalBrowserArguments; + + ///This property is used to enable single sign on with Azure Active Directory (AAD) + ///and personal Microsoft Account (MSA) resources inside WebView. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - ICoreWebView2EnvironmentOptions.put_AllowSingleSignOnUsingOSPrimaryAccount](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environmentoptions?view=webview2-1.0.2210.55#put_allowsinglesignonusingosprimaryaccount)) final bool? allowSingleSignOnUsingOSPrimaryAccount; + + ///Use [browserExecutableFolder] to specify whether WebView2 controls use a fixed + ///or installed version of the WebView2 Runtime that exists on a user machine. + ///To use a fixed version of the WebView2 Runtime, pass the folder path that contains + ///the fixed version of the WebView2 Runtime to [browserExecutableFolder]. + ///BrowserExecutableFolder supports both relative (to the application's executable) and absolute files paths. + ///To create WebView2 controls that use the installed version of the WebView2 Runtime that exists on user machines, + ///pass a `null` or empty string to [browserExecutableFolder]. + ///In this scenario, the API tries to find a compatible version of the WebView2 Runtime + ///that is installed on the user machine (first at the machine level, and then per user) using the selected channel preference. + ///The path of fixed version of the WebView2 Runtime should not contain `\Edge\Application\`. + ///When such a path is used, the API fails with `HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)`. + /// + ///The default channel search order is the WebView2 Runtime, Beta, Dev, and Canary. + ///When an override `WEBVIEW2_RELEASE_CHANNEL_PREFERENCE` environment variable or + ///applicable `releaseChannelPreference` registry value is set to `1`, the channel search order is reversed. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - CreateCoreWebView2EnvironmentWithOptions.browserExecutableFolder](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#createcorewebview2environmentwithoptions)) final String? browserExecutableFolder; + + ///The default display language for WebView. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - ICoreWebView2EnvironmentOptions.put_Language](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environmentoptions?view=webview2-1.0.2210.55#put_language)) final String? language; + + ///Specifies the version of the WebView2 Runtime binaries required to be compatible with your app. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - ICoreWebView2EnvironmentOptions.put_TargetCompatibleBrowserVersion](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2environmentoptions?view=webview2-1.0.2210.55#put_targetcompatiblebrowserversion)) final String? targetCompatibleBrowserVersion; + + ///You may specify the [userDataFolder] to change the default user data folder location for WebView2. + ///The path is either an absolute file path or a relative file path that is interpreted as relative + ///to the compiled code for the current process. + ///For UWP apps, the default user data folder is the app data folder for the package. + ///For non-UWP apps, the default user data (`{Executable File Name}.WebView2`) folder + ///is created in the same directory next to the compiled code for the app. + ///WebView2 creation fails if the compiled code is running in a directory in which the + ///process does not have permission to create a new directory. + ///The app is responsible to clean up the associated user data folder when it is done. + /// + ///**NOTE**: As a browser process may be shared among WebViews, + ///WebView creation fails with `HRESULT_FROM_WIN32(ERROR_INVALID_STATE)` if the specified + ///options does not match the options of the WebViews that are currently + ///running in the shared browser process. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - CreateCoreWebView2EnvironmentWithOptions.userDataFolder](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#createcorewebview2environmentwithoptions)) final String? userDataFolder; + + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows WebViewEnvironmentSettings( {this.additionalBrowserArguments, this.allowSingleSignOnUsingOSPrimaryAccount, diff --git a/flutter_inappwebview_windows/lib/src/cookie_manager.dart b/flutter_inappwebview_windows/lib/src/cookie_manager.dart index 81903ca7..10882604 100644 --- a/flutter_inappwebview_windows/lib/src/cookie_manager.dart +++ b/flutter_inappwebview_windows/lib/src/cookie_manager.dart @@ -5,6 +5,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import 'webview_environment/webview_environment.dart'; + /// Object specifying creation parameters for creating a [WindowsCookieManager]. /// /// When adding additional fields make sure they can be null or have a default @@ -15,16 +17,19 @@ class WindowsCookieManagerCreationParams extends PlatformCookieManagerCreationParams { /// Creates a new [WindowsCookieManagerCreationParams] instance. const WindowsCookieManagerCreationParams( - // This parameter prevents breaking changes later. - // ignore: avoid_unused_constructor_parameters - PlatformCookieManagerCreationParams params, - ) : super(); + {this.webViewEnvironment}); /// Creates a [WindowsCookieManagerCreationParams] instance based on [PlatformCookieManagerCreationParams]. factory WindowsCookieManagerCreationParams.fromPlatformCookieManagerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters PlatformCookieManagerCreationParams params) { - return WindowsCookieManagerCreationParams(params); + return WindowsCookieManagerCreationParams( + webViewEnvironment: params.webViewEnvironment as WindowsWebViewEnvironment?); } + + @override + final WindowsWebViewEnvironment? webViewEnvironment; } ///{@macro flutter_inappwebview_platform_interface.PlatformCookieManager} @@ -47,13 +52,21 @@ class WindowsCookieManager extends PlatformCookieManager static WindowsCookieManager? _instance; ///Gets the [WindowsCookieManager] shared instance. - static WindowsCookieManager instance() { - return (_instance != null) ? _instance! : _init(); + static WindowsCookieManager instance({WindowsWebViewEnvironment? webViewEnvironment}) { + if (webViewEnvironment == null) { + if (_instance == null) { + _instance = _init(); + } + return _instance!; + } else { + return WindowsCookieManager( + WindowsCookieManagerCreationParams(webViewEnvironment: webViewEnvironment) + ); + } } static WindowsCookieManager _init() { - _instance = WindowsCookieManager(WindowsCookieManagerCreationParams( - const PlatformCookieManagerCreationParams())); + _instance = WindowsCookieManager(WindowsCookieManagerCreationParams()); return _instance!; } @@ -90,6 +103,7 @@ class WindowsCookieManager extends PlatformCookieManager args.putIfAbsent('isSecure', () => isSecure); args.putIfAbsent('isHttpOnly', () => isHttpOnly); args.putIfAbsent('sameSite', () => sameSite?.toNativeValue()); + args.putIfAbsent('webViewEnvironmentId', () => params.webViewEnvironment?.id); return await channel?.invokeMethod('setCookie', args) ?? false; } @@ -106,6 +120,7 @@ class WindowsCookieManager extends PlatformCookieManager Map args = {}; args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('webViewEnvironmentId', () => params.webViewEnvironment?.id); List cookieListMap = await channel?.invokeMethod('getCookies', args) ?? []; cookieListMap = cookieListMap.cast>(); @@ -138,6 +153,7 @@ class WindowsCookieManager extends PlatformCookieManager Map args = {}; args.putIfAbsent('url', () => url.toString()); + args.putIfAbsent('webViewEnvironmentId', () => params.webViewEnvironment?.id); List cookies = await channel?.invokeMethod('getCookies', args) ?? []; cookies = cookies.cast>(); @@ -176,6 +192,7 @@ class WindowsCookieManager extends PlatformCookieManager args.putIfAbsent('name', () => name); args.putIfAbsent('domain', () => domain); args.putIfAbsent('path', () => path); + args.putIfAbsent('webViewEnvironmentId', () => params.webViewEnvironment?.id); return await channel?.invokeMethod('deleteCookie', args) ?? false; } @@ -193,18 +210,21 @@ class WindowsCookieManager extends PlatformCookieManager args.putIfAbsent('url', () => url.toString()); args.putIfAbsent('domain', () => domain); args.putIfAbsent('path', () => path); + args.putIfAbsent('webViewEnvironmentId', () => params.webViewEnvironment?.id); return await channel?.invokeMethod('deleteCookies', args) ?? false; } @override Future deleteAllCookies() async { Map args = {}; + args.putIfAbsent('webViewEnvironmentId', () => params.webViewEnvironment?.id); return await channel?.invokeMethod('deleteAllCookies', args) ?? false; } @override Future removeSessionCookies() async { Map args = {}; + args.putIfAbsent('webViewEnvironmentId', () => params.webViewEnvironment?.id); return await channel?.invokeMethod('removeSessionCookies', args) ?? false; } diff --git a/flutter_inappwebview_windows/windows/cookie_manager.cpp b/flutter_inappwebview_windows/windows/cookie_manager.cpp index 23500931..9ab62069 100644 --- a/flutter_inappwebview_windows/windows/cookie_manager.cpp +++ b/flutter_inappwebview_windows/windows/cookie_manager.cpp @@ -1,11 +1,11 @@ +#include #include #include -#include #include #include #include "cookie_manager.h" -#include "in_app_webview/in_app_webview.h" +#include "utils/flutter.h" #include "utils/log.h" namespace flutter_inappwebview_plugin @@ -13,32 +13,8 @@ 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); - - InAppWebView::createInAppWebViewEnv(hwnd, false, nullptr, - [=](wil::com_ptr webViewEnv, - wil::com_ptr webViewController, - wil::com_ptr webViewCompositionController) - { - if (webViewEnv && webViewController) { - webViewEnv_ = std::move(webViewEnv); - webViewController_ = std::move(webViewController); - webViewController_->get_CoreWebView2(&webView_); - webViewController_->put_IsVisible(false); - } - }); - } + : plugin(plugin), ChannelDelegate(plugin->registrar->messenger(), CookieManager::METHOD_CHANNEL_NAME_PREFIX) + {} void CookieManager::HandleMethodCall(const flutter::MethodCall& method_call, std::unique_ptr> result) @@ -46,69 +22,105 @@ namespace flutter_inappwebview_plugin auto& arguments = std::get(*method_call.arguments()); auto& methodName = method_call.method_name(); - if (string_equals(methodName, "setCookie")) { - if (!webView_) { - result->Success(false); - return; - } + auto webViewEnvironmentId = get_optional_fl_map_value(arguments, "webViewEnvironmentId"); - auto url = get_fl_map_value(arguments, "url"); - auto name = get_fl_map_value(arguments, "name"); - auto value = get_fl_map_value(arguments, "value"); - auto path = get_fl_map_value(arguments, "path"); - auto domain = get_optional_fl_map_value(arguments, "domain"); - auto expiresDate = get_optional_fl_map_value(arguments, "expiresDate"); - auto maxAge = get_optional_fl_map_value(arguments, "maxAge"); - auto isSecure = get_optional_fl_map_value(arguments, "isSecure"); - auto isHttpOnly = get_optional_fl_map_value(arguments, "isHttpOnly"); - auto sameSite = get_optional_fl_map_value(arguments, "sameSite"); + auto webViewEnvironment = webViewEnvironmentId.has_value() && map_contains(plugin->webViewEnvironmentManager->webViewEnvironments, webViewEnvironmentId.value()) + ? plugin->webViewEnvironmentManager->webViewEnvironments.at(webViewEnvironmentId.value()).get() : nullptr; - 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; - } - if (maxAge.has_value()) { - // time(NULL) represents the current unix timestamp in seconds - parameters["expires"] = time(NULL) + maxAge.value(); - } - if (isSecure.has_value()) { - parameters["secure"] = isSecure.value(); - } - if (isHttpOnly.has_value()) { - parameters["httpOnly"] = isHttpOnly.value(); - } - if (sameSite.has_value()) { - parameters["sameSite"] = sameSite.value(); - } - - auto result_ = std::shared_ptr>(std::move(result)); - auto hr = webView_->CallDevToolsProtocolMethod(L"Network.setCookie", utf8_to_wide(parameters.dump()).c_str(), Callback( - [this, result_](HRESULT errorCode, LPCWSTR returnObjectAsJson) - { - result_->Success(succeededOrLog(errorCode)); - return S_OK; + auto result_ = std::shared_ptr>(std::move(result)); + auto callback = [this, result_, methodName, arguments](WebViewEnvironment* webViewEnvironment) + { + if (!webViewEnvironment) { + result_->Error("0", "Cannot obtain the WebViewEnvironment!"); + return; } - ).Get()); - if (failedAndLog(hr)) { - result_->Success(false); - } + if (string_equals(methodName, "setCookie")) { + setCookie(webViewEnvironment, arguments, [result_](const bool& created) + { + result_->Success(created); + }); + } + else { + result_->NotImplemented(); + } + }; + + if (webViewEnvironment) { + callback(webViewEnvironment); } else { - result->NotImplemented(); + plugin->webViewEnvironmentManager->createOrGetDefaultWebViewEnvironment([callback](WebViewEnvironment* webViewEnvironment) + { + callback(webViewEnvironment); + }); + } + } + + void CookieManager::setCookie(WebViewEnvironment* webViewEnvironment, const flutter::EncodableMap& map, std::function completionHandler) const + { + if (!plugin || !plugin->webViewEnvironmentManager) { + if (completionHandler) { + completionHandler(false); + } + return; + } + + auto url = get_fl_map_value(map, "url"); + auto name = get_fl_map_value(map, "name"); + auto value = get_fl_map_value(map, "value"); + auto path = get_fl_map_value(map, "path"); + auto domain = get_optional_fl_map_value(map, "domain"); + auto expiresDate = get_optional_fl_map_value(map, "expiresDate"); + auto maxAge = get_optional_fl_map_value(map, "maxAge"); + auto isSecure = get_optional_fl_map_value(map, "isSecure"); + auto isHttpOnly = get_optional_fl_map_value(map, "isHttpOnly"); + auto sameSite = get_optional_fl_map_value(map, "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; + } + 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 hr = webViewEnvironment->getWebView()->CallDevToolsProtocolMethod(L"Network.setCookie", utf8_to_wide(parameters.dump()).c_str(), Callback( + [completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + if (completionHandler) { + completionHandler(succeededOrLog(errorCode)); + } + return S_OK; + } + ).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler(false); } } CookieManager::~CookieManager() { debugLog("dealloc CookieManager"); + plugin = nullptr; } -} \ No newline at end of file +} diff --git a/flutter_inappwebview_windows/windows/cookie_manager.h b/flutter_inappwebview_windows/windows/cookie_manager.h index 6d3d3daa..b6f596d7 100644 --- a/flutter_inappwebview_windows/windows/cookie_manager.h +++ b/flutter_inappwebview_windows/windows/cookie_manager.h @@ -3,18 +3,17 @@ #include #include -#include -#include +#include #include "flutter_inappwebview_windows_plugin.h" #include "types/channel_delegate.h" +#include "webview_environment/webview_environment_manager.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; @@ -25,11 +24,8 @@ namespace flutter_inappwebview_plugin void HandleMethodCall( const flutter::MethodCall& method_call, std::unique_ptr> result); - private: - wil::com_ptr webViewEnv_; - wil::com_ptr webViewController_; - wil::com_ptr webView_; - WNDCLASS windowClass_ = {}; + + void setCookie(WebViewEnvironment* webViewEnvironment, const flutter::EncodableMap& map, std::function completionHandler) const; }; } diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index 9540048b..f85e6be9 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -57,7 +57,7 @@ namespace flutter_inappwebview_plugin this->inAppBrowser = inAppBrowser; } - void InAppWebView::createInAppWebViewEnv(const HWND parentWindow, const bool& willBeSurface, const WebViewEnvironment* webViewEnvironment, std::function webViewEnv, + void InAppWebView::createInAppWebViewEnv(const HWND parentWindow, const bool& willBeSurface, WebViewEnvironment* webViewEnvironment, std::function webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController)> completionHandler) { @@ -109,8 +109,8 @@ namespace flutter_inappwebview_plugin }; HRESULT hr; - if (webViewEnvironment && webViewEnvironment->env) { - hr = callback(S_OK, webViewEnvironment->env); + if (webViewEnvironment && webViewEnvironment->getEnvironment()) { + hr = callback(S_OK, webViewEnvironment->getEnvironment()); } else { hr = CreateCoreWebView2EnvironmentWithOptions( diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index 3caf70ca..95dfa6a4 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -109,7 +109,7 @@ namespace flutter_inappwebview_plugin wil::com_ptr webViewCompositionController); ~InAppWebView(); - static void createInAppWebViewEnv(const HWND parentWindow, const bool& willBeSurface, const WebViewEnvironment* webViewEnvironment, std::function webViewEnv, + static void createInAppWebViewEnv(const HWND parentWindow, const bool& willBeSurface, WebViewEnvironment* webViewEnvironment, std::function webViewEnv, wil::com_ptr webViewController, wil::com_ptr webViewCompositionController)> completionHandler); diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment.cpp b/flutter_inappwebview_windows/windows/webview_environment/webview_environment.cpp index 575f0d33..e8ae3933 100644 --- a/flutter_inappwebview_windows/windows/webview_environment/webview_environment.cpp +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment.cpp @@ -4,6 +4,8 @@ #include "../utils/log.h" #include "webview_environment.h" +#include "webview_environment_manager.h" + namespace flutter_inappwebview_plugin { using namespace Microsoft::WRL; @@ -15,6 +17,21 @@ namespace flutter_inappwebview_plugin void WebViewEnvironment::create(const std::unique_ptr settings, const std::function completionHandler) { + if (!plugin) { + if (completionHandler) { + completionHandler(E_FAIL); + } + return; + } + + auto hwnd = plugin->webViewEnvironmentManager->getHWND(); + if (!hwnd) { + if (completionHandler) { + completionHandler(E_FAIL); + } + return; + } + auto options = Make(); if (settings) { if (settings->additionalBrowserArguments.has_value()) { @@ -36,12 +53,30 @@ namespace flutter_inappwebview_plugin settings && settings->userDataFolder.has_value() ? utf8_to_wide(settings->userDataFolder.value()).c_str() : nullptr, options.Get(), Callback( - [this, completionHandler](HRESULT result, wil::com_ptr environment) -> HRESULT + [this, hwnd, completionHandler](HRESULT result, wil::com_ptr environment) -> HRESULT { if (succeededOrLog(result)) { - env = std::move(environment); + environment_ = std::move(environment); + + auto hr = environment_->CreateCoreWebView2Controller(hwnd, Callback( + [this, completionHandler](HRESULT result, wil::com_ptr controller) -> HRESULT + { + if (succeededOrLog(result)) { + webViewController_ = std::move(controller); + webViewController_->get_CoreWebView2(&webView_); + webViewController_->put_IsVisible(false); + } + if (completionHandler) { + completionHandler(result); + } + return S_OK; + }).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler(hr); + } } - if (completionHandler) { + else if (completionHandler) { completionHandler(result); } return S_OK; @@ -56,4 +91,4 @@ namespace flutter_inappwebview_plugin { debugLog("dealloc WebViewEnvironment"); } -} \ No newline at end of file +} diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment.h b/flutter_inappwebview_windows/windows/webview_environment/webview_environment.h index 849b48ba..615442d4 100644 --- a/flutter_inappwebview_windows/windows/webview_environment/webview_environment.h +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment.h @@ -14,17 +14,35 @@ namespace flutter_inappwebview_plugin class WebViewEnvironment { public: + static inline const wchar_t* CLASS_NAME = L"WebViewEnvironment"; static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_webview_environment_"; const FlutterInappwebviewWindowsPlugin* plugin; std::string id; - wil::com_ptr env; + std::unique_ptr channelDelegate; WebViewEnvironment(const FlutterInappwebviewWindowsPlugin* plugin, const std::string& id); ~WebViewEnvironment(); void create(const std::unique_ptr settings, const std::function completionHandler); + wil::com_ptr getEnvironment() + { + return environment_; + } + wil::com_ptr getWebViewController() + { + return webViewController_; + } + wil::com_ptr getWebView() + { + return webView_; + } + private: + wil::com_ptr environment_; + wil::com_ptr webViewController_; + wil::com_ptr webView_; + WNDCLASS windowClass_ = {}; }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp index b0b492b4..81e07fd8 100644 --- a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp @@ -14,7 +14,18 @@ namespace flutter_inappwebview_plugin WebViewEnvironmentManager::WebViewEnvironmentManager(const FlutterInappwebviewWindowsPlugin* plugin) : plugin(plugin), ChannelDelegate(plugin->registrar->messenger(), WebViewEnvironmentManager::METHOD_CHANNEL_NAME) - {} + { + windowClass_.lpszClassName = WebViewEnvironmentManager::CLASS_NAME; + windowClass_.lpfnWndProc = &DefWindowProc; + + RegisterClass(&windowClass_); + + hwnd_ = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, 0, + 0, 0, 0, + plugin->registrar->GetView()->GetNativeWindow(), + nullptr, + windowClass_.hInstance, nullptr); + } void WebViewEnvironmentManager::HandleMethodCall(const flutter::MethodCall& method_call, std::unique_ptr> result) @@ -54,10 +65,38 @@ namespace flutter_inappwebview_plugin webViewEnvironments.insert({ id, std::move(webViewEnvironment) }); } + void WebViewEnvironmentManager::createOrGetDefaultWebViewEnvironment(const std::function completionHandler) + { + if (defaultEnvironment_) { + if (completionHandler) { + completionHandler(defaultEnvironment_.get()); + } + return; + } + + defaultEnvironment_ = std::make_unique(plugin, "-1"); + defaultEnvironment_->create(nullptr, [this, completionHandler](HRESULT errorCode) + { + if (succeededOrLog(errorCode)) { + if (completionHandler) { + completionHandler(defaultEnvironment_.get()); + } + } + else if (completionHandler) { + defaultEnvironment_ = nullptr; + completionHandler(nullptr); + } + }); + } + WebViewEnvironmentManager::~WebViewEnvironmentManager() { debugLog("dealloc WebViewEnvironmentManager"); webViewEnvironments.clear(); plugin = nullptr; + defaultEnvironment_ = nullptr; + if (hwnd_) { + DestroyWindow(hwnd_); + } } } \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h index 59c25b50..a8bc82fa 100644 --- a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h @@ -15,6 +15,7 @@ namespace flutter_inappwebview_plugin class WebViewEnvironmentManager : public ChannelDelegate { public: + static inline const wchar_t* CLASS_NAME = L"WebViewEnvironmentManager"; static inline const std::string METHOD_CHANNEL_NAME = "com.pichillilorenzo/flutter_webview_environment"; const FlutterInappwebviewWindowsPlugin* plugin; @@ -28,6 +29,15 @@ namespace flutter_inappwebview_plugin std::unique_ptr> result); void createWebViewEnvironment(const std::string& id, std::unique_ptr settings, std::unique_ptr> result); + void createOrGetDefaultWebViewEnvironment(const std::function completionHandler); + HWND getHWND() + { + return hwnd_; + } + private: + std::unique_ptr defaultEnvironment_; + WNDCLASS windowClass_ = {}; + HWND hwnd_ = nullptr; }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_WEBVIEW_ENVIRONMENT_MANAGER_H_ \ No newline at end of file From ade2edfb7d5ba83f0c1e290d8c7446d970b966db Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 28 Jan 2024 16:24:54 +0100 Subject: [PATCH 19/21] windows: completed CookieManager implementation, added WebViewEnvironment getAvailableVersion and compareBrowserVersions methods --- flutter_inappwebview/example/lib/main.dart | 3 + .../webview_environment.dart | 8 + .../lib/src/inappwebview_platform.dart | 6 +- .../lib/src/inappwebview_platform.dart | 6 +- .../lib/src/inappwebview_platform.dart | 6 +- .../lib/src/platform_cookie_manager.dart | 7 + .../lib/src/types/cookie.dart | 30 ++- .../lib/src/types/cookie.g.dart | 9 + .../src/web_storage/platform_web_storage.dart | 1 + .../platform_webview_environment.dart | 35 +++ .../lib/src/cookie_manager.dart | 8 - .../find_interaction_controller.dart | 16 +- .../src/http_auth_credentials_database.dart | 36 +-- .../in_app_webview_controller.dart | 24 +- .../lib/src/inappwebview_platform.dart | 34 +++ .../src/print_job/print_job_controller.dart | 24 +- .../src/web_message/web_message_channel.dart | 38 +-- .../src/web_message/web_message_listener.dart | 34 +-- .../lib/src/web_message/web_message_port.dart | 26 +- .../lib/src/web_storage/web_storage.dart | 96 ++++---- .../src/web_storage/web_storage_manager.dart | 34 +-- .../webview_environment.dart | 19 ++ .../windows/CMakeLists.txt | 1 + .../windows/cookie_manager.cpp | 225 +++++++++++++++++- .../windows/cookie_manager.h | 8 +- .../windows/types/callbacks_complete.h | 39 +++ .../webview_environment_manager.cpp | 27 +++ .../webview_environment_manager.h | 3 + 28 files changed, 611 insertions(+), 192 deletions(-) create mode 100644 flutter_inappwebview_windows/windows/types/callbacks_complete.h diff --git a/flutter_inappwebview/example/lib/main.dart b/flutter_inappwebview/example/lib/main.dart index 881061dd..ab93bf9d 100755 --- a/flutter_inappwebview/example/lib/main.dart +++ b/flutter_inappwebview/example/lib/main.dart @@ -24,6 +24,9 @@ Future main() async { // await Permission.storage.request(); if (!kIsWeb && defaultTargetPlatform == TargetPlatform.windows) { + final availableVersion = await WebViewEnvironment.getAvailableVersion(); + assert(availableVersion != null, 'Failed to find an installed WebView2 runtime or non-stable Microsoft Edge installation.'); + webViewEnvironment = await WebViewEnvironment.create(settings: WebViewEnvironmentSettings( userDataFolder: 'custom_path' diff --git a/flutter_inappwebview/lib/src/webview_environment/webview_environment.dart b/flutter_inappwebview/lib/src/webview_environment/webview_environment.dart index 4b89d135..b355df71 100644 --- a/flutter_inappwebview/lib/src/webview_environment/webview_environment.dart +++ b/flutter_inappwebview/lib/src/webview_environment/webview_environment.dart @@ -30,6 +30,14 @@ class WebViewEnvironment { return WebViewEnvironment.fromPlatform(platform: await PlatformWebViewEnvironment.static().create(settings: settings)); } + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.getAvailableVersion} + static Future getAvailableVersion( + {String? browserExecutableFolder}) => PlatformWebViewEnvironment.static().getAvailableVersion(browserExecutableFolder: browserExecutableFolder); + + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.getAvailableVersion} + static Future compareBrowserVersions( + {required String version1, required String version2}) => PlatformWebViewEnvironment.static().compareBrowserVersions(version1: version1, version2: version2); + ///{@macro flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.dispose} Future dispose() => platform.dispose(); } \ No newline at end of file diff --git a/flutter_inappwebview_android/lib/src/inappwebview_platform.dart b/flutter_inappwebview_android/lib/src/inappwebview_platform.dart index 65e1b426..aa5c4207 100644 --- a/flutter_inappwebview_android/lib/src/inappwebview_platform.dart +++ b/flutter_inappwebview_android/lib/src/inappwebview_platform.dart @@ -155,7 +155,7 @@ class AndroidInAppWebViewPlatform extends InAppWebViewPlatform { /// Creates a new [AndroidWebStorage]. /// /// This function should only be called by the app-facing package. - /// Look at using [AndroidWebStorage] in `flutter_inappwebview` instead. + /// Look at using [WebStorage] in `flutter_inappwebview` instead. @override AndroidWebStorage createPlatformWebStorage( PlatformWebStorageCreationParams params, @@ -166,7 +166,7 @@ class AndroidInAppWebViewPlatform extends InAppWebViewPlatform { /// Creates a new [AndroidLocalStorage]. /// /// This function should only be called by the app-facing package. - /// Look at using [AndroidLocalStorage] in `flutter_inappwebview` instead. + /// Look at using [LocalStorage] in `flutter_inappwebview` instead. @override AndroidLocalStorage createPlatformLocalStorage( PlatformLocalStorageCreationParams params, @@ -177,7 +177,7 @@ class AndroidInAppWebViewPlatform extends InAppWebViewPlatform { /// Creates a new [AndroidSessionStorage]. /// /// This function should only be called by the app-facing package. - /// Look at using [PlatformSessionStorage] in `flutter_inappwebview` instead. + /// Look at using [SessionStorage] in `flutter_inappwebview` instead. @override AndroidSessionStorage createPlatformSessionStorage( PlatformSessionStorageCreationParams params, diff --git a/flutter_inappwebview_ios/lib/src/inappwebview_platform.dart b/flutter_inappwebview_ios/lib/src/inappwebview_platform.dart index 504f1f1a..fc27a56d 100644 --- a/flutter_inappwebview_ios/lib/src/inappwebview_platform.dart +++ b/flutter_inappwebview_ios/lib/src/inappwebview_platform.dart @@ -150,7 +150,7 @@ class IOSInAppWebViewPlatform extends InAppWebViewPlatform { /// Creates a new [IOSWebStorage]. /// /// This function should only be called by the app-facing package. - /// Look at using [IOSWebStorage] in `flutter_inappwebview` instead. + /// Look at using [WebStorage] in `flutter_inappwebview` instead. @override IOSWebStorage createPlatformWebStorage( PlatformWebStorageCreationParams params, @@ -161,7 +161,7 @@ class IOSInAppWebViewPlatform extends InAppWebViewPlatform { /// Creates a new [IOSLocalStorage]. /// /// This function should only be called by the app-facing package. - /// Look at using [IOSLocalStorage] in `flutter_inappwebview` instead. + /// Look at using [LocalStorage] in `flutter_inappwebview` instead. @override IOSLocalStorage createPlatformLocalStorage( PlatformLocalStorageCreationParams params, @@ -172,7 +172,7 @@ class IOSInAppWebViewPlatform extends InAppWebViewPlatform { /// Creates a new [IOSSessionStorage]. /// /// This function should only be called by the app-facing package. - /// Look at using [PlatformSessionStorage] in `flutter_inappwebview` instead. + /// Look at using [SessionStorage] in `flutter_inappwebview` instead. @override IOSSessionStorage createPlatformSessionStorage( PlatformSessionStorageCreationParams params, diff --git a/flutter_inappwebview_macos/lib/src/inappwebview_platform.dart b/flutter_inappwebview_macos/lib/src/inappwebview_platform.dart index 3e13e4f5..98f1e04a 100644 --- a/flutter_inappwebview_macos/lib/src/inappwebview_platform.dart +++ b/flutter_inappwebview_macos/lib/src/inappwebview_platform.dart @@ -138,7 +138,7 @@ class MacOSInAppWebViewPlatform extends InAppWebViewPlatform { /// Creates a new [MacOSWebStorage]. /// /// This function should only be called by the app-facing package. - /// Look at using [MacOSWebStorage] in `flutter_inappwebview` instead. + /// Look at using [WebStorage] in `flutter_inappwebview` instead. @override MacOSWebStorage createPlatformWebStorage( PlatformWebStorageCreationParams params, @@ -149,7 +149,7 @@ class MacOSInAppWebViewPlatform extends InAppWebViewPlatform { /// Creates a new [MacOSLocalStorage]. /// /// This function should only be called by the app-facing package. - /// Look at using [MacOSLocalStorage] in `flutter_inappwebview` instead. + /// Look at using [LocalStorage] in `flutter_inappwebview` instead. @override MacOSLocalStorage createPlatformLocalStorage( PlatformLocalStorageCreationParams params, @@ -160,7 +160,7 @@ class MacOSInAppWebViewPlatform extends InAppWebViewPlatform { /// Creates a new [MacOSSessionStorage]. /// /// This function should only be called by the app-facing package. - /// Look at using [PlatformSessionStorage] in `flutter_inappwebview` instead. + /// Look at using [SessionStorage] in `flutter_inappwebview` instead. @override MacOSSessionStorage createPlatformSessionStorage( PlatformSessionStorageCreationParams params, diff --git a/flutter_inappwebview_platform_interface/lib/src/platform_cookie_manager.dart b/flutter_inappwebview_platform_interface/lib/src/platform_cookie_manager.dart index 81bbb37a..700cc487 100755 --- a/flutter_inappwebview_platform_interface/lib/src/platform_cookie_manager.dart +++ b/flutter_inappwebview_platform_interface/lib/src/platform_cookie_manager.dart @@ -40,6 +40,7 @@ class PlatformCookieManagerCreationParams { ///- iOS ///- MacOS ///- Web +///- Windows ///{@endtemplate} abstract class PlatformCookieManager extends PlatformInterface { /// Creates a new [PlatformCookieManager] @@ -94,6 +95,7 @@ abstract class PlatformCookieManager extends PlatformInterface { ///- iOS ([Official API - WKHTTPCookieStore.setCookie](https://developer.apple.com/documentation/webkit/wkhttpcookiestore/2882007-setcookie)) ///- MacOS ([Official API - WKHTTPCookieStore.setCookie](https://developer.apple.com/documentation/webkit/wkhttpcookiestore/2882007-setcookie)) ///- Web + ///- Windows ///{@endtemplate} Future setCookie( {required WebUri url, @@ -133,6 +135,7 @@ abstract class PlatformCookieManager extends PlatformInterface { ///- iOS ([Official API - WKHTTPCookieStore.getAllCookies](https://developer.apple.com/documentation/webkit/wkhttpcookiestore/2882005-getallcookies)) ///- MacOS ([Official API - WKHTTPCookieStore.getAllCookies](https://developer.apple.com/documentation/webkit/wkhttpcookiestore/2882005-getallcookies)) ///- Web + ///- Windows ///{@endtemplate} Future> getCookies( {required WebUri url, @@ -163,6 +166,7 @@ abstract class PlatformCookieManager extends PlatformInterface { ///- iOS ///- MacOS ///- Web + ///- Windows ///{@endtemplate} Future getCookie( {required WebUri url, @@ -198,6 +202,7 @@ abstract class PlatformCookieManager extends PlatformInterface { ///- iOS ([Official API - WKHTTPCookieStore.delete](https://developer.apple.com/documentation/webkit/wkhttpcookiestore/2882009-delete) ///- MacOS ([Official API - WKHTTPCookieStore.delete](https://developer.apple.com/documentation/webkit/wkhttpcookiestore/2882009-delete) ///- Web + ///- Windows ///{@endtemplate} Future deleteCookie( {required WebUri url, @@ -235,6 +240,7 @@ abstract class PlatformCookieManager extends PlatformInterface { ///- iOS ///- MacOS ///- Web + ///- Windows ///{@endtemplate} Future deleteCookies( {required WebUri url, @@ -261,6 +267,7 @@ abstract class PlatformCookieManager extends PlatformInterface { ///- Android native WebView ([Official API - CookieManager.removeAllCookies](https://developer.android.com/reference/android/webkit/CookieManager#removeAllCookies(android.webkit.ValueCallback%3Cjava.lang.Boolean%3E))) ///- iOS ([Official API - WKWebsiteDataStore.removeData](https://developer.apple.com/documentation/webkit/wkwebsitedatastore/1532938-removedata)) ///- MacOS ([Official API - WKWebsiteDataStore.removeData](https://developer.apple.com/documentation/webkit/wkwebsitedatastore/1532938-removedata)) + ///- Windows ///{@endtemplate} Future deleteAllCookies() { throw UnimplementedError( diff --git a/flutter_inappwebview_platform_interface/lib/src/types/cookie.dart b/flutter_inappwebview_platform_interface/lib/src/types/cookie.dart index d0edfb4b..da4f3803 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/cookie.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/cookie.dart @@ -14,7 +14,8 @@ class Cookie_ { IOSPlatform(), MacOSPlatform(), AndroidPlatform(), - WebPlatform() + WebPlatform(), + WindowsPlatform() ]) String name; @@ -23,7 +24,8 @@ class Cookie_ { IOSPlatform(), MacOSPlatform(), AndroidPlatform(), - WebPlatform() + WebPlatform(), + WindowsPlatform() ]) dynamic value; @@ -33,12 +35,17 @@ class Cookie_ { MacOSPlatform(), AndroidPlatform( note: - "available on Android only if [WebViewFeature.GET_COOKIE_INFO] feature is supported.") + "available on Android only if [WebViewFeature.GET_COOKIE_INFO] feature is supported."), + WindowsPlatform() ]) int? expiresDate; ///Indicates if the cookie is a session only cookie. - @SupportedPlatforms(platforms: [IOSPlatform(), MacOSPlatform()]) + @SupportedPlatforms(platforms: [ + IOSPlatform(), + MacOSPlatform(), + WindowsPlatform() + ]) bool? isSessionOnly; ///The cookie domain. @@ -47,7 +54,8 @@ class Cookie_ { MacOSPlatform(), AndroidPlatform( note: - "available on Android only if [WebViewFeature.GET_COOKIE_INFO] feature is supported.") + "available on Android only if [WebViewFeature.GET_COOKIE_INFO] feature is supported."), + WindowsPlatform() ]) String? domain; @@ -57,7 +65,8 @@ class Cookie_ { MacOSPlatform(), AndroidPlatform( note: - "available on Android only if [WebViewFeature.GET_COOKIE_INFO] feature is supported.") + "available on Android only if [WebViewFeature.GET_COOKIE_INFO] feature is supported."), + WindowsPlatform() ]) HTTPCookieSameSitePolicy_? sameSite; @@ -67,7 +76,8 @@ class Cookie_ { MacOSPlatform(), AndroidPlatform( note: - "available on Android only if [WebViewFeature.GET_COOKIE_INFO] feature is supported.") + "available on Android only if [WebViewFeature.GET_COOKIE_INFO] feature is supported."), + WindowsPlatform() ]) bool? isSecure; @@ -77,7 +87,8 @@ class Cookie_ { MacOSPlatform(), AndroidPlatform( note: - "available on Android only if [WebViewFeature.GET_COOKIE_INFO] feature is supported.") + "available on Android only if [WebViewFeature.GET_COOKIE_INFO] feature is supported."), + WindowsPlatform() ]) bool? isHttpOnly; @@ -87,7 +98,8 @@ class Cookie_ { MacOSPlatform(), AndroidPlatform( note: - "available on Android only if [WebViewFeature.GET_COOKIE_INFO] feature is supported.") + "available on Android only if [WebViewFeature.GET_COOKIE_INFO] feature is supported."), + WindowsPlatform() ]) String? path; diff --git a/flutter_inappwebview_platform_interface/lib/src/types/cookie.g.dart b/flutter_inappwebview_platform_interface/lib/src/types/cookie.g.dart index 8b5e268d..ada5df95 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/cookie.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/cookie.g.dart @@ -16,6 +16,7 @@ class Cookie { ///- iOS ///- MacOS ///- Android native WebView + ///- Windows String? domain; ///The cookie expiration date in milliseconds. @@ -26,6 +27,7 @@ class Cookie { ///- iOS ///- MacOS ///- Android native WebView + ///- Windows int? expiresDate; ///Indicates if the cookie is a http only cookie. @@ -36,6 +38,7 @@ class Cookie { ///- iOS ///- MacOS ///- Android native WebView + ///- Windows bool? isHttpOnly; ///Indicates if the cookie is secure or not. @@ -46,6 +49,7 @@ class Cookie { ///- iOS ///- MacOS ///- Android native WebView + ///- Windows bool? isSecure; ///Indicates if the cookie is a session only cookie. @@ -53,6 +57,7 @@ class Cookie { ///**Officially Supported Platforms/Implementations**: ///- iOS ///- MacOS + ///- Windows bool? isSessionOnly; ///The cookie name. @@ -62,6 +67,7 @@ class Cookie { ///- MacOS ///- Android native WebView ///- Web but iframe requires same origin + ///- Windows String name; ///The cookie path. @@ -72,6 +78,7 @@ class Cookie { ///- iOS ///- MacOS ///- Android native WebView + ///- Windows String? path; ///The cookie same site policy. @@ -82,6 +89,7 @@ class Cookie { ///- iOS ///- MacOS ///- Android native WebView + ///- Windows HTTPCookieSameSitePolicy? sameSite; ///The cookie value. @@ -91,6 +99,7 @@ class Cookie { ///- MacOS ///- Android native WebView ///- Web but iframe requires same origin + ///- Windows dynamic value; Cookie( {this.domain, diff --git a/flutter_inappwebview_platform_interface/lib/src/web_storage/platform_web_storage.dart b/flutter_inappwebview_platform_interface/lib/src/web_storage/platform_web_storage.dart index 84ecd089..c47eb753 100644 --- a/flutter_inappwebview_platform_interface/lib/src/web_storage/platform_web_storage.dart +++ b/flutter_inappwebview_platform_interface/lib/src/web_storage/platform_web_storage.dart @@ -33,6 +33,7 @@ class PlatformWebStorageCreationParams { ///- iOS ///- MacOS ///- Web +///- Windows ///{@endtemplate} abstract class PlatformWebStorage extends PlatformInterface implements Disposable { diff --git a/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart b/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart index d5c156c9..0aea7510 100644 --- a/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart +++ b/flutter_inappwebview_platform_interface/lib/src/webview_environment/platform_webview_environment.dart @@ -20,6 +20,7 @@ class PlatformWebViewEnvironmentCreationParams { } ///Controls a WebView Environment used by WebView instances. +///Use [dispose] when not needed anymore to release references. /// ///**Officially Supported Platforms/Implementations**: ///- Windows @@ -102,6 +103,40 @@ abstract class PlatformWebViewEnvironment extends PlatformInterface 'create is not implemented on the current platform'); } + ///{@template flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.getAvailableVersion} + ///Get the browser version info including channel name if it is not the WebView2 Runtime. + /// + ///Channel names are Beta, Dev, and Canary. + ///If an override exists for the browserExecutableFolder or the channel preference, the override is used. + ///If an override is not specified, then the parameter value passed to [getAvailableVersion] is used. + ///Returns `null` if it fails to find an installed WebView2 runtime or non-stable Microsoft Edge installation. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - GetAvailableCoreWebView2BrowserVersionString](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#comparebrowserversions)) + ///{@endtemplate} + Future getAvailableVersion( + {String? browserExecutableFolder}) { + throw UnimplementedError( + 'getAvailableVersion is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.compareBrowserVersions} + ///This method is for anyone want to compare version correctly to determine which version is newer, older or same. + /// + ///Use it to determine whether to use webview2 or certain feature based upon version. + ///Sets the value of result to `-1`, `0` or `1` if version1 is less than, equal or greater than version2 respectively. + ///Returns `null` if it fails to parse any of the version strings. + ///Directly use the version info obtained from [getAvailableVersion] with input, channel information is ignored. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - CompareBrowserVersions](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2210.55#comparebrowserversions)) + ///{@endtemplate} + Future compareBrowserVersions( + {required String version1, required String version2}) { + throw UnimplementedError( + 'compareBrowserVersions is not implemented on the current platform'); + } + ///{@template flutter_inappwebview_platform_interface.PlatformWebViewEnvironment.dispose} ///Disposes the WebView Environment reference. ///{@endtemplate} diff --git a/flutter_inappwebview_windows/lib/src/cookie_manager.dart b/flutter_inappwebview_windows/lib/src/cookie_manager.dart index 10882604..30474f7d 100644 --- a/flutter_inappwebview_windows/lib/src/cookie_manager.dart +++ b/flutter_inappwebview_windows/lib/src/cookie_manager.dart @@ -221,14 +221,6 @@ class WindowsCookieManager extends PlatformCookieManager return await channel?.invokeMethod('deleteAllCookies', args) ?? false; } - @override - Future removeSessionCookies() async { - Map args = {}; - args.putIfAbsent('webViewEnvironmentId', () => params.webViewEnvironment?.id); - return await channel?.invokeMethod('removeSessionCookies', args) ?? - false; - } - @override void dispose() { // empty diff --git a/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart b/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart index 9835059a..8868a3d6 100644 --- a/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart +++ b/flutter_inappwebview_windows/lib/src/find_interaction/find_interaction_controller.dart @@ -8,18 +8,18 @@ import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_pla /// value to avoid breaking changes. See [PlatformFindInteractionControllerCreationParams] for /// more information. @immutable -class MacOSFindInteractionControllerCreationParams +class WindowsFindInteractionControllerCreationParams extends PlatformFindInteractionControllerCreationParams { - /// Creates a new [MacOSFindInteractionControllerCreationParams] instance. - const MacOSFindInteractionControllerCreationParams( + /// Creates a new [WindowsFindInteractionControllerCreationParams] instance. + const WindowsFindInteractionControllerCreationParams( {super.onFindResultReceived}); - /// Creates a [MacOSFindInteractionControllerCreationParams] instance based on [PlatformFindInteractionControllerCreationParams]. - factory MacOSFindInteractionControllerCreationParams.fromPlatformFindInteractionControllerCreationParams( + /// Creates a [WindowsFindInteractionControllerCreationParams] instance based on [PlatformFindInteractionControllerCreationParams]. + factory WindowsFindInteractionControllerCreationParams.fromPlatformFindInteractionControllerCreationParams( // Recommended placeholder to prevent being broken by platform interface. // ignore: avoid_unused_constructor_parameters PlatformFindInteractionControllerCreationParams params) { - return MacOSFindInteractionControllerCreationParams( + return WindowsFindInteractionControllerCreationParams( onFindResultReceived: params.onFindResultReceived); } } @@ -31,9 +31,9 @@ class WindowsFindInteractionController extends PlatformFindInteractionController WindowsFindInteractionController( PlatformFindInteractionControllerCreationParams params) : super.implementation( - params is MacOSFindInteractionControllerCreationParams + params is WindowsFindInteractionControllerCreationParams ? params - : MacOSFindInteractionControllerCreationParams + : WindowsFindInteractionControllerCreationParams .fromPlatformFindInteractionControllerCreationParams(params), ); diff --git a/flutter_inappwebview_windows/lib/src/http_auth_credentials_database.dart b/flutter_inappwebview_windows/lib/src/http_auth_credentials_database.dart index 28c9126f..90bc6e7e 100644 --- a/flutter_inappwebview_windows/lib/src/http_auth_credentials_database.dart +++ b/flutter_inappwebview_windows/lib/src/http_auth_credentials_database.dart @@ -3,38 +3,38 @@ 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 [MacOSHttpAuthCredentialDatabase]. +/// Object specifying creation parameters for creating a [WindowsHttpAuthCredentialDatabase]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformHttpAuthCredentialDatabaseCreationParams] for /// more information. @immutable -class MacOSHttpAuthCredentialDatabaseCreationParams +class WindowsHttpAuthCredentialDatabaseCreationParams extends PlatformHttpAuthCredentialDatabaseCreationParams { - /// Creates a new [MacOSHttpAuthCredentialDatabaseCreationParams] instance. - const MacOSHttpAuthCredentialDatabaseCreationParams( + /// Creates a new [WindowsHttpAuthCredentialDatabaseCreationParams] instance. + const WindowsHttpAuthCredentialDatabaseCreationParams( // This parameter prevents breaking changes later. // ignore: avoid_unused_constructor_parameters PlatformHttpAuthCredentialDatabaseCreationParams params, ) : super(); - /// Creates a [MacOSHttpAuthCredentialDatabaseCreationParams] instance based on [PlatformHttpAuthCredentialDatabaseCreationParams]. - factory MacOSHttpAuthCredentialDatabaseCreationParams.fromPlatformHttpAuthCredentialDatabaseCreationParams( + /// Creates a [WindowsHttpAuthCredentialDatabaseCreationParams] instance based on [PlatformHttpAuthCredentialDatabaseCreationParams]. + factory WindowsHttpAuthCredentialDatabaseCreationParams.fromPlatformHttpAuthCredentialDatabaseCreationParams( PlatformHttpAuthCredentialDatabaseCreationParams params) { - return MacOSHttpAuthCredentialDatabaseCreationParams(params); + return WindowsHttpAuthCredentialDatabaseCreationParams(params); } } ///{@macro flutter_inappwebview_platform_interface.PlatformHttpAuthCredentialDatabase} -class MacOSHttpAuthCredentialDatabase extends PlatformHttpAuthCredentialDatabase +class WindowsHttpAuthCredentialDatabase extends PlatformHttpAuthCredentialDatabase with ChannelController { - /// Creates a new [MacOSHttpAuthCredentialDatabase]. - MacOSHttpAuthCredentialDatabase( + /// Creates a new [WindowsHttpAuthCredentialDatabase]. + WindowsHttpAuthCredentialDatabase( PlatformHttpAuthCredentialDatabaseCreationParams params) : super.implementation( - params is MacOSHttpAuthCredentialDatabaseCreationParams + params is WindowsHttpAuthCredentialDatabaseCreationParams ? params - : MacOSHttpAuthCredentialDatabaseCreationParams + : WindowsHttpAuthCredentialDatabaseCreationParams .fromPlatformHttpAuthCredentialDatabaseCreationParams(params), ) { channel = const MethodChannel( @@ -43,16 +43,16 @@ class MacOSHttpAuthCredentialDatabase extends PlatformHttpAuthCredentialDatabase initMethodCallHandler(); } - static MacOSHttpAuthCredentialDatabase? _instance; + static WindowsHttpAuthCredentialDatabase? _instance; ///Gets the database shared instance. - static MacOSHttpAuthCredentialDatabase instance() { + static WindowsHttpAuthCredentialDatabase instance() { return (_instance != null) ? _instance! : _init(); } - static MacOSHttpAuthCredentialDatabase _init() { - _instance = MacOSHttpAuthCredentialDatabase( - MacOSHttpAuthCredentialDatabaseCreationParams( + static WindowsHttpAuthCredentialDatabase _init() { + _instance = WindowsHttpAuthCredentialDatabase( + WindowsHttpAuthCredentialDatabaseCreationParams( const PlatformHttpAuthCredentialDatabaseCreationParams())); return _instance!; } @@ -150,6 +150,6 @@ class MacOSHttpAuthCredentialDatabase extends PlatformHttpAuthCredentialDatabase } extension InternalHttpAuthCredentialDatabase - on MacOSHttpAuthCredentialDatabase { + on WindowsHttpAuthCredentialDatabase { get handleMethod => _handleMethod; } diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart index b6373a8b..66e56d1e 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart @@ -75,7 +75,7 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController Set _webMessageListenerObjNames = Set(); Map _injectedScriptsFromURL = {}; Set _webMessageChannels = Set(); - Set _webMessageListeners = Set(); + Set _webMessageListeners = Set(); // static map that contains the properties to be saved and restored for keep alive feature static final Map @@ -89,7 +89,7 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController dynamic _controllerFromPlatform; @override - late MacOSWebStorage webStorage; + late WindowsWebStorage webStorage; WindowsInAppWebViewController( PlatformInAppWebViewControllerCreationParams params) @@ -161,9 +161,9 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController _controllerFromPlatform = params.webviewParams?.controllerFromPlatform?.call(this) ?? this; - webStorage = MacOSWebStorage(MacOSWebStorageCreationParams( - localStorage: MacOSLocalStorage.defaultStorage(controller: this), - sessionStorage: MacOSSessionStorage.defaultStorage(controller: this))); + webStorage = WindowsWebStorage(WindowsWebStorageCreationParams( + localStorage: WindowsLocalStorage.defaultStorage(controller: this), + sessionStorage: WindowsSessionStorage.defaultStorage(controller: this))); if (params.webviewParams is PlatformInAppWebViewWidgetCreationParams) { final keepAlive = @@ -189,7 +189,7 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController _webMessageChannels = props.webMessageChannels as Set; _webMessageListeners = - props.webMessageListeners as Set; + props.webMessageListeners as Set; } } } @@ -1311,9 +1311,9 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController String? url = call.arguments["url"]; String? printJobId = call.arguments["printJobId"]; WebUri? uri = url != null ? WebUri(url) : null; - MacOSPrintJobController? printJob = printJobId != null - ? MacOSPrintJobController( - MacOSPrintJobControllerCreationParams(id: printJobId)) + WindowsPrintJobController? printJob = printJobId != null + ? WindowsPrintJobController( + WindowsPrintJobControllerCreationParams(id: printJobId)) : null; if (webviewParams != null) { @@ -2139,14 +2139,14 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController } @override - Future printCurrentPage( + Future printCurrentPage( {PrintJobSettings? settings}) async { Map args = {}; args.putIfAbsent("settings", () => settings?.toMap()); String? jobId = await channel?.invokeMethod('printCurrentPage', args); if (jobId != null) { - return MacOSPrintJobController( + return WindowsPrintJobController( PlatformPrintJobControllerCreationParams(id: jobId)); } return null; @@ -2494,7 +2494,7 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController !_webMessageListenerObjNames .contains(webMessageListener.params.jsObjectName), "jsObjectName ${webMessageListener.params.jsObjectName} was already added."); - _webMessageListeners.add(webMessageListener as MacOSWebMessageListener); + _webMessageListeners.add(webMessageListener as WindowsWebMessageListener); _webMessageListenerObjNames.add(webMessageListener.params.jsObjectName); Map args = {}; diff --git a/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart b/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart index 93ffb9be..3dbf4ab7 100644 --- a/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart +++ b/flutter_inappwebview_windows/lib/src/inappwebview_platform.dart @@ -6,6 +6,7 @@ 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'; +import 'web_storage/web_storage.dart'; /// Implementation of [InAppWebViewPlatform] using the WebKit API. class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { @@ -107,4 +108,37 @@ class WindowsInAppWebViewPlatform extends InAppWebViewPlatform { WindowsWebViewEnvironment createPlatformWebViewEnvironmentStatic() { return WindowsWebViewEnvironment.static(); } + + /// Creates a new [WindowsWebStorage]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebStorage] in `flutter_inappwebview` instead. + @override + WindowsWebStorage createPlatformWebStorage( + PlatformWebStorageCreationParams params, + ) { + return WindowsWebStorage(params); + } + + /// Creates a new [WindowsLocalStorage]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [LocalStorage] in `flutter_inappwebview` instead. + @override + WindowsLocalStorage createPlatformLocalStorage( + PlatformLocalStorageCreationParams params, + ) { + return WindowsLocalStorage(params); + } + + /// Creates a new [WindowsSessionStorage]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [SessionStorage] in `flutter_inappwebview` instead. + @override + WindowsSessionStorage createPlatformSessionStorage( + PlatformSessionStorageCreationParams params, + ) { + return WindowsSessionStorage(params); + } } diff --git a/flutter_inappwebview_windows/lib/src/print_job/print_job_controller.dart b/flutter_inappwebview_windows/lib/src/print_job/print_job_controller.dart index be3136d6..a1da365a 100644 --- a/flutter_inappwebview_windows/lib/src/print_job/print_job_controller.dart +++ b/flutter_inappwebview_windows/lib/src/print_job/print_job_controller.dart @@ -2,37 +2,37 @@ 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 [MacOSPrintJobController]. +/// Object specifying creation parameters for creating a [WindowsPrintJobController]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformPrintJobControllerCreationParams] for /// more information. @immutable -class MacOSPrintJobControllerCreationParams +class WindowsPrintJobControllerCreationParams extends PlatformPrintJobControllerCreationParams { - /// Creates a new [MacOSPrintJobControllerCreationParams] instance. - const MacOSPrintJobControllerCreationParams( + /// Creates a new [WindowsPrintJobControllerCreationParams] instance. + const WindowsPrintJobControllerCreationParams( {required super.id, super.onComplete}); - /// Creates a [MacOSPrintJobControllerCreationParams] instance based on [PlatformPrintJobControllerCreationParams]. - factory MacOSPrintJobControllerCreationParams.fromPlatformPrintJobControllerCreationParams( + /// Creates a [WindowsPrintJobControllerCreationParams] instance based on [PlatformPrintJobControllerCreationParams]. + factory WindowsPrintJobControllerCreationParams.fromPlatformPrintJobControllerCreationParams( // Recommended placeholder to prevent being broken by platform interface. // ignore: avoid_unused_constructor_parameters PlatformPrintJobControllerCreationParams params) { - return MacOSPrintJobControllerCreationParams( + return WindowsPrintJobControllerCreationParams( id: params.id, onComplete: params.onComplete); } } ///{@macro flutter_inappwebview_platform_interface.PlatformPrintJobController} -class MacOSPrintJobController extends PlatformPrintJobController +class WindowsPrintJobController extends PlatformPrintJobController with ChannelController { - /// Constructs a [MacOSPrintJobController]. - MacOSPrintJobController(PlatformPrintJobControllerCreationParams params) + /// Constructs a [WindowsPrintJobController]. + WindowsPrintJobController(PlatformPrintJobControllerCreationParams params) : super.implementation( - params is MacOSPrintJobControllerCreationParams + params is WindowsPrintJobControllerCreationParams ? params - : MacOSPrintJobControllerCreationParams + : WindowsPrintJobControllerCreationParams .fromPlatformPrintJobControllerCreationParams(params), ) { onComplete = params.onComplete; diff --git a/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart b/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart index 2077c759..2a47ab16 100644 --- a/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart +++ b/flutter_inappwebview_windows/lib/src/web_message/web_message_channel.dart @@ -9,18 +9,18 @@ import 'web_message_port.dart'; /// value to avoid breaking changes. See [PlatformWebMessageChannelCreationParams] for /// more information. @immutable -class MacOSWebMessageChannelCreationParams +class WindowsWebMessageChannelCreationParams extends PlatformWebMessageChannelCreationParams { - /// Creates a new [MacOSWebMessageChannelCreationParams] instance. - const MacOSWebMessageChannelCreationParams( + /// Creates a new [WindowsWebMessageChannelCreationParams] instance. + const WindowsWebMessageChannelCreationParams( {required super.id, required super.port1, required super.port2}); - /// Creates a [MacOSWebMessageChannelCreationParams] instance based on [PlatformWebMessageChannelCreationParams]. - factory MacOSWebMessageChannelCreationParams.fromPlatformWebMessageChannelCreationParams( + /// Creates a [WindowsWebMessageChannelCreationParams] instance based on [PlatformWebMessageChannelCreationParams]. + factory WindowsWebMessageChannelCreationParams.fromPlatformWebMessageChannelCreationParams( // Recommended placeholder to prevent being broken by platform interface. // ignore: avoid_unused_constructor_parameters PlatformWebMessageChannelCreationParams params) { - return MacOSWebMessageChannelCreationParams( + return WindowsWebMessageChannelCreationParams( id: params.id, port1: params.port1, port2: params.port2); } @@ -36,9 +36,9 @@ class WindowsWebMessageChannel extends PlatformWebMessageChannel /// Constructs a [WindowsWebMessageChannel]. WindowsWebMessageChannel(PlatformWebMessageChannelCreationParams params) : super.implementation( - params is MacOSWebMessageChannelCreationParams + params is WindowsWebMessageChannelCreationParams ? params - : MacOSWebMessageChannelCreationParams + : WindowsWebMessageChannelCreationParams .fromPlatformWebMessageChannelCreationParams(params), ) { channel = MethodChannel( @@ -48,33 +48,33 @@ class WindowsWebMessageChannel extends PlatformWebMessageChannel } static final WindowsWebMessageChannel _staticValue = WindowsWebMessageChannel( - MacOSWebMessageChannelCreationParams( + WindowsWebMessageChannelCreationParams( id: '', port1: - MacOSWebMessagePort(MacOSWebMessagePortCreationParams(index: 0)), - port2: MacOSWebMessagePort( - MacOSWebMessagePortCreationParams(index: 1)))); + WindowsWebMessagePort(WindowsWebMessagePortCreationParams(index: 0)), + port2: WindowsWebMessagePort( + WindowsWebMessagePortCreationParams(index: 1)))); /// Provide static access. factory WindowsWebMessageChannel.static() { return _staticValue; } - MacOSWebMessagePort get _macosPort1 => port1 as MacOSWebMessagePort; + WindowsWebMessagePort get _macosPort1 => port1 as WindowsWebMessagePort; - MacOSWebMessagePort get _macosPort2 => port2 as MacOSWebMessagePort; + WindowsWebMessagePort get _macosPort2 => port2 as WindowsWebMessagePort; static WindowsWebMessageChannel? _fromMap(Map? map) { if (map == null) { return null; } var webMessageChannel = WindowsWebMessageChannel( - MacOSWebMessageChannelCreationParams( + WindowsWebMessageChannelCreationParams( id: map["id"], - port1: MacOSWebMessagePort( - MacOSWebMessagePortCreationParams(index: 0)), - port2: MacOSWebMessagePort( - MacOSWebMessagePortCreationParams(index: 1)))); + port1: WindowsWebMessagePort( + WindowsWebMessagePortCreationParams(index: 0)), + port2: WindowsWebMessagePort( + WindowsWebMessagePortCreationParams(index: 1)))); webMessageChannel._macosPort1.webMessageChannel = webMessageChannel; webMessageChannel._macosPort2.webMessageChannel = webMessageChannel; return webMessageChannel; diff --git a/flutter_inappwebview_windows/lib/src/web_message/web_message_listener.dart b/flutter_inappwebview_windows/lib/src/web_message/web_message_listener.dart index b2791eba..22a9cd76 100644 --- a/flutter_inappwebview_windows/lib/src/web_message/web_message_listener.dart +++ b/flutter_inappwebview_windows/lib/src/web_message/web_message_listener.dart @@ -2,26 +2,26 @@ 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 [MacOSWebMessageListener]. +/// Object specifying creation parameters for creating a [WindowsWebMessageListener]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformWebMessageListenerCreationParams] for /// more information. @immutable -class MacOSWebMessageListenerCreationParams +class WindowsWebMessageListenerCreationParams extends PlatformWebMessageListenerCreationParams { - /// Creates a new [MacOSWebMessageListenerCreationParams] instance. - const MacOSWebMessageListenerCreationParams( + /// Creates a new [WindowsWebMessageListenerCreationParams] instance. + const WindowsWebMessageListenerCreationParams( {required this.allowedOriginRules, required super.jsObjectName, super.onPostMessage}); - /// Creates a [MacOSWebMessageListenerCreationParams] instance based on [PlatformWebMessageListenerCreationParams]. - factory MacOSWebMessageListenerCreationParams.fromPlatformWebMessageListenerCreationParams( + /// Creates a [WindowsWebMessageListenerCreationParams] instance based on [PlatformWebMessageListenerCreationParams]. + factory WindowsWebMessageListenerCreationParams.fromPlatformWebMessageListenerCreationParams( // Recommended placeholder to prevent being broken by platform interface. // ignore: avoid_unused_constructor_parameters PlatformWebMessageListenerCreationParams params) { - return MacOSWebMessageListenerCreationParams( + return WindowsWebMessageListenerCreationParams( allowedOriginRules: params.allowedOriginRules ?? Set.from(["*"]), jsObjectName: params.jsObjectName, onPostMessage: params.onPostMessage); @@ -37,14 +37,14 @@ class MacOSWebMessageListenerCreationParams } ///{@macro flutter_inappwebview_platform_interface.PlatformWebMessageListener} -class MacOSWebMessageListener extends PlatformWebMessageListener +class WindowsWebMessageListener extends PlatformWebMessageListener with ChannelController { - /// Constructs a [MacOSWebMessageListener]. - MacOSWebMessageListener(PlatformWebMessageListenerCreationParams params) + /// Constructs a [WindowsWebMessageListener]. + WindowsWebMessageListener(PlatformWebMessageListenerCreationParams params) : super.implementation( - params is MacOSWebMessageListenerCreationParams + params is WindowsWebMessageListenerCreationParams ? params - : MacOSWebMessageListenerCreationParams + : WindowsWebMessageListenerCreationParams .fromPlatformWebMessageListenerCreationParams(params), ) { assert(!this._macosParams.allowedOriginRules.contains(""), @@ -60,8 +60,8 @@ class MacOSWebMessageListener extends PlatformWebMessageListener MacOSJavaScriptReplyProxy? _replyProxy; - MacOSWebMessageListenerCreationParams get _macosParams => - params as MacOSWebMessageListenerCreationParams; + WindowsWebMessageListenerCreationParams get _macosParams => + params as WindowsWebMessageListenerCreationParams; Future _handleMethod(MethodCall call) async { switch (call.method) { @@ -138,7 +138,7 @@ class MacOSJavaScriptReplyProxyCreationParams ///{@macro flutter_inappwebview_platform_interface.JavaScriptReplyProxy} class MacOSJavaScriptReplyProxy extends PlatformJavaScriptReplyProxy { - /// Constructs a [MacOSWebMessageListener]. + /// Constructs a [WindowsWebMessageListener]. MacOSJavaScriptReplyProxy(PlatformJavaScriptReplyProxyCreationParams params) : super.implementation( params is MacOSJavaScriptReplyProxyCreationParams @@ -147,8 +147,8 @@ class MacOSJavaScriptReplyProxy extends PlatformJavaScriptReplyProxy { .fromPlatformJavaScriptReplyProxyCreationParams(params), ); - MacOSWebMessageListener get _macosWebMessageListener => - params.webMessageListener as MacOSWebMessageListener; + WindowsWebMessageListener get _macosWebMessageListener => + params.webMessageListener as WindowsWebMessageListener; @override Future postMessage(WebMessage message) async { diff --git a/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart b/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart index be3ecece..f75c956e 100644 --- a/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart +++ b/flutter_inappwebview_windows/lib/src/web_message/web_message_port.dart @@ -3,23 +3,23 @@ import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_pla import 'web_message_channel.dart'; -/// Object specifying creation parameters for creating a [MacOSWebMessagePort]. +/// Object specifying creation parameters for creating a [WindowsWebMessagePort]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformWebMessagePortCreationParams] for /// more information. @immutable -class MacOSWebMessagePortCreationParams +class WindowsWebMessagePortCreationParams extends PlatformWebMessagePortCreationParams { - /// Creates a new [MacOSWebMessagePortCreationParams] instance. - const MacOSWebMessagePortCreationParams({required super.index}); + /// Creates a new [WindowsWebMessagePortCreationParams] instance. + const WindowsWebMessagePortCreationParams({required super.index}); - /// Creates a [MacOSWebMessagePortCreationParams] instance based on [PlatformWebMessagePortCreationParams]. - factory MacOSWebMessagePortCreationParams.fromPlatformWebMessagePortCreationParams( + /// Creates a [WindowsWebMessagePortCreationParams] instance based on [PlatformWebMessagePortCreationParams]. + factory WindowsWebMessagePortCreationParams.fromPlatformWebMessagePortCreationParams( // Recommended placeholder to prevent being broken by platform interface. // ignore: avoid_unused_constructor_parameters PlatformWebMessagePortCreationParams params) { - return MacOSWebMessagePortCreationParams(index: params.index); + return WindowsWebMessagePortCreationParams(index: params.index); } @override @@ -29,16 +29,16 @@ class MacOSWebMessagePortCreationParams } ///{@macro flutter_inappwebview_platform_interface.PlatformWebMessagePort} -class MacOSWebMessagePort extends PlatformWebMessagePort { +class WindowsWebMessagePort extends PlatformWebMessagePort { WebMessageCallback? _onMessage; late WindowsWebMessageChannel _webMessageChannel; - /// Constructs a [MacOSWebMessagePort]. - MacOSWebMessagePort(PlatformWebMessagePortCreationParams params) + /// Constructs a [WindowsWebMessagePort]. + WindowsWebMessagePort(PlatformWebMessagePortCreationParams params) : super.implementation( - params is MacOSWebMessagePortCreationParams + params is WindowsWebMessagePortCreationParams ? params - : MacOSWebMessagePortCreationParams + : WindowsWebMessagePortCreationParams .fromPlatformWebMessagePortCreationParams(params), ); @@ -85,7 +85,7 @@ class MacOSWebMessagePort extends PlatformWebMessagePort { } } -extension InternalWebMessagePort on MacOSWebMessagePort { +extension InternalWebMessagePort on WindowsWebMessagePort { WebMessageCallback? get onMessage => _onMessage; void set onMessage(WebMessageCallback? value) => _onMessage = value; diff --git a/flutter_inappwebview_windows/lib/src/web_storage/web_storage.dart b/flutter_inappwebview_windows/lib/src/web_storage/web_storage.dart index e884747b..7fe18b95 100644 --- a/flutter_inappwebview_windows/lib/src/web_storage/web_storage.dart +++ b/flutter_inappwebview_windows/lib/src/web_storage/web_storage.dart @@ -4,35 +4,35 @@ import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_pla import '../in_app_webview/in_app_webview_controller.dart'; -/// Object specifying creation parameters for creating a [MacOSWebStorage]. +/// Object specifying creation parameters for creating a [WindowsWebStorage]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformWebStorageCreationParams] for /// more information. -class MacOSWebStorageCreationParams extends PlatformWebStorageCreationParams { - /// Creates a new [MacOSWebStorageCreationParams] instance. - MacOSWebStorageCreationParams( +class WindowsWebStorageCreationParams extends PlatformWebStorageCreationParams { + /// Creates a new [WindowsWebStorageCreationParams] instance. + WindowsWebStorageCreationParams( {required super.localStorage, required super.sessionStorage}); - /// Creates a [MacOSWebStorageCreationParams] instance based on [PlatformWebStorageCreationParams]. - factory MacOSWebStorageCreationParams.fromPlatformWebStorageCreationParams( + /// Creates a [WindowsWebStorageCreationParams] instance based on [PlatformWebStorageCreationParams]. + factory WindowsWebStorageCreationParams.fromPlatformWebStorageCreationParams( // Recommended placeholder to prevent being broken by platform interface. // ignore: avoid_unused_constructor_parameters PlatformWebStorageCreationParams params) { - return MacOSWebStorageCreationParams( + return WindowsWebStorageCreationParams( localStorage: params.localStorage, sessionStorage: params.sessionStorage); } } ///{@macro flutter_inappwebview_platform_interface.PlatformWebStorage} -class MacOSWebStorage extends PlatformWebStorage { - /// Constructs a [MacOSWebStorage]. - MacOSWebStorage(PlatformWebStorageCreationParams params) +class WindowsWebStorage extends PlatformWebStorage { + /// Constructs a [WindowsWebStorage]. + WindowsWebStorage(PlatformWebStorageCreationParams params) : super.implementation( - params is MacOSWebStorageCreationParams + params is WindowsWebStorageCreationParams ? params - : MacOSWebStorageCreationParams + : WindowsWebStorageCreationParams .fromPlatformWebStorageCreationParams(params), ); @@ -49,28 +49,28 @@ class MacOSWebStorage extends PlatformWebStorage { } } -/// Object specifying creation parameters for creating a [MacOSStorage]. +/// Object specifying creation parameters for creating a [WindowsStorage]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformStorageCreationParams] for /// more information. -class MacOSStorageCreationParams extends PlatformStorageCreationParams { - /// Creates a new [MacOSStorageCreationParams] instance. - MacOSStorageCreationParams( +class WindowsStorageCreationParams extends PlatformStorageCreationParams { + /// Creates a new [WindowsStorageCreationParams] instance. + WindowsStorageCreationParams( {required super.controller, required super.webStorageType}); - /// Creates a [MacOSStorageCreationParams] instance based on [PlatformStorageCreationParams]. - factory MacOSStorageCreationParams.fromPlatformStorageCreationParams( + /// Creates a [WindowsStorageCreationParams] instance based on [PlatformStorageCreationParams]. + factory WindowsStorageCreationParams.fromPlatformStorageCreationParams( // Recommended placeholder to prevent being broken by platform interface. // ignore: avoid_unused_constructor_parameters PlatformStorageCreationParams params) { - return MacOSStorageCreationParams( + return WindowsStorageCreationParams( controller: params.controller, webStorageType: params.webStorageType); } } ///{@macro flutter_inappwebview_platform_interface.PlatformStorage} -abstract class MacOSStorage implements PlatformStorage { +abstract class WindowsStorage implements PlatformStorage { @override WindowsInAppWebViewController? controller; @@ -168,40 +168,40 @@ abstract class MacOSStorage implements PlatformStorage { } } -/// Object specifying creation parameters for creating a [MacOSLocalStorage]. +/// Object specifying creation parameters for creating a [WindowsLocalStorage]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformLocalStorageCreationParams] for /// more information. -class MacOSLocalStorageCreationParams +class WindowsLocalStorageCreationParams extends PlatformLocalStorageCreationParams { - /// Creates a new [MacOSLocalStorageCreationParams] instance. - MacOSLocalStorageCreationParams(super.params); + /// Creates a new [WindowsLocalStorageCreationParams] instance. + WindowsLocalStorageCreationParams(super.params); - /// Creates a [MacOSLocalStorageCreationParams] instance based on [PlatformLocalStorageCreationParams]. - factory MacOSLocalStorageCreationParams.fromPlatformLocalStorageCreationParams( + /// Creates a [WindowsLocalStorageCreationParams] instance based on [PlatformLocalStorageCreationParams]. + factory WindowsLocalStorageCreationParams.fromPlatformLocalStorageCreationParams( // Recommended placeholder to prevent being broken by platform interface. // ignore: avoid_unused_constructor_parameters PlatformLocalStorageCreationParams params) { - return MacOSLocalStorageCreationParams(params); + return WindowsLocalStorageCreationParams(params); } } ///{@macro flutter_inappwebview_platform_interface.PlatformLocalStorage} -class MacOSLocalStorage extends PlatformLocalStorage with MacOSStorage { - /// Constructs a [MacOSLocalStorage]. - MacOSLocalStorage(PlatformLocalStorageCreationParams params) +class WindowsLocalStorage extends PlatformLocalStorage with WindowsStorage { + /// Constructs a [WindowsLocalStorage]. + WindowsLocalStorage(PlatformLocalStorageCreationParams params) : super.implementation( - params is MacOSLocalStorageCreationParams + params is WindowsLocalStorageCreationParams ? params - : MacOSLocalStorageCreationParams + : WindowsLocalStorageCreationParams .fromPlatformLocalStorageCreationParams(params), ); /// Default storage - factory MacOSLocalStorage.defaultStorage( + factory WindowsLocalStorage.defaultStorage( {required PlatformInAppWebViewController? controller}) { - return MacOSLocalStorage(MacOSLocalStorageCreationParams( + return WindowsLocalStorage(WindowsLocalStorageCreationParams( PlatformLocalStorageCreationParams(PlatformStorageCreationParams( controller: controller, webStorageType: WebStorageType.LOCAL_STORAGE)))); @@ -212,40 +212,40 @@ class MacOSLocalStorage extends PlatformLocalStorage with MacOSStorage { params.controller as WindowsInAppWebViewController?; } -/// Object specifying creation parameters for creating a [MacOSSessionStorage]. +/// Object specifying creation parameters for creating a [WindowsSessionStorage]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformSessionStorageCreationParams] for /// more information. -class MacOSSessionStorageCreationParams +class WindowsSessionStorageCreationParams extends PlatformSessionStorageCreationParams { - /// Creates a new [MacOSSessionStorageCreationParams] instance. - MacOSSessionStorageCreationParams(super.params); + /// Creates a new [WindowsSessionStorageCreationParams] instance. + WindowsSessionStorageCreationParams(super.params); - /// Creates a [MacOSSessionStorageCreationParams] instance based on [PlatformSessionStorageCreationParams]. - factory MacOSSessionStorageCreationParams.fromPlatformSessionStorageCreationParams( + /// Creates a [WindowsSessionStorageCreationParams] instance based on [PlatformSessionStorageCreationParams]. + factory WindowsSessionStorageCreationParams.fromPlatformSessionStorageCreationParams( // Recommended placeholder to prevent being broken by platform interface. // ignore: avoid_unused_constructor_parameters PlatformSessionStorageCreationParams params) { - return MacOSSessionStorageCreationParams(params); + return WindowsSessionStorageCreationParams(params); } } ///{@macro flutter_inappwebview_platform_interface.PlatformSessionStorage} -class MacOSSessionStorage extends PlatformSessionStorage with MacOSStorage { - /// Constructs a [MacOSSessionStorage]. - MacOSSessionStorage(PlatformSessionStorageCreationParams params) +class WindowsSessionStorage extends PlatformSessionStorage with WindowsStorage { + /// Constructs a [WindowsSessionStorage]. + WindowsSessionStorage(PlatformSessionStorageCreationParams params) : super.implementation( - params is MacOSSessionStorageCreationParams + params is WindowsSessionStorageCreationParams ? params - : MacOSSessionStorageCreationParams + : WindowsSessionStorageCreationParams .fromPlatformSessionStorageCreationParams(params), ); /// Default storage - factory MacOSSessionStorage.defaultStorage( + factory WindowsSessionStorage.defaultStorage( {required PlatformInAppWebViewController? controller}) { - return MacOSSessionStorage(MacOSSessionStorageCreationParams( + return WindowsSessionStorage(WindowsSessionStorageCreationParams( PlatformSessionStorageCreationParams(PlatformStorageCreationParams( controller: controller, webStorageType: WebStorageType.SESSION_STORAGE)))); diff --git a/flutter_inappwebview_windows/lib/src/web_storage/web_storage_manager.dart b/flutter_inappwebview_windows/lib/src/web_storage/web_storage_manager.dart index b281f0d7..cd9c24e6 100644 --- a/flutter_inappwebview_windows/lib/src/web_storage/web_storage_manager.dart +++ b/flutter_inappwebview_windows/lib/src/web_storage/web_storage_manager.dart @@ -4,37 +4,37 @@ 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 [MacOSWebStorageManager]. +/// Object specifying creation parameters for creating a [WindowsWebStorageManager]. /// /// When adding additional fields make sure they can be null or have a default /// value to avoid breaking changes. See [PlatformWebStorageManagerCreationParams] for /// more information. @immutable -class MacOSWebStorageManagerCreationParams +class WindowsWebStorageManagerCreationParams extends PlatformWebStorageManagerCreationParams { - /// Creates a new [MacOSWebStorageManagerCreationParams] instance. - const MacOSWebStorageManagerCreationParams( + /// Creates a new [WindowsWebStorageManagerCreationParams] instance. + const WindowsWebStorageManagerCreationParams( // This parameter prevents breaking changes later. // ignore: avoid_unused_constructor_parameters PlatformWebStorageManagerCreationParams params, ) : super(); - /// Creates a [MacOSWebStorageManagerCreationParams] instance based on [PlatformWebStorageManagerCreationParams]. - factory MacOSWebStorageManagerCreationParams.fromPlatformWebStorageManagerCreationParams( + /// Creates a [WindowsWebStorageManagerCreationParams] instance based on [PlatformWebStorageManagerCreationParams]. + factory WindowsWebStorageManagerCreationParams.fromPlatformWebStorageManagerCreationParams( PlatformWebStorageManagerCreationParams params) { - return MacOSWebStorageManagerCreationParams(params); + return WindowsWebStorageManagerCreationParams(params); } } ///{@macro flutter_inappwebview_platform_interface.PlatformWebStorageManager} -class MacOSWebStorageManager extends PlatformWebStorageManager +class WindowsWebStorageManager extends PlatformWebStorageManager with ChannelController { - /// Creates a new [MacOSWebStorageManager]. - MacOSWebStorageManager(PlatformWebStorageManagerCreationParams params) + /// Creates a new [WindowsWebStorageManager]. + WindowsWebStorageManager(PlatformWebStorageManagerCreationParams params) : super.implementation( - params is MacOSWebStorageManagerCreationParams + params is WindowsWebStorageManagerCreationParams ? params - : MacOSWebStorageManagerCreationParams + : WindowsWebStorageManagerCreationParams .fromPlatformWebStorageManagerCreationParams(params), ) { channel = const MethodChannel( @@ -43,15 +43,15 @@ class MacOSWebStorageManager extends PlatformWebStorageManager initMethodCallHandler(); } - static MacOSWebStorageManager? _instance; + static WindowsWebStorageManager? _instance; ///Gets the WebStorage manager shared instance. - static MacOSWebStorageManager instance() { + static WindowsWebStorageManager instance() { return (_instance != null) ? _instance! : _init(); } - static MacOSWebStorageManager _init() { - _instance = MacOSWebStorageManager(MacOSWebStorageManagerCreationParams( + static WindowsWebStorageManager _init() { + _instance = WindowsWebStorageManager(WindowsWebStorageManagerCreationParams( const PlatformWebStorageManagerCreationParams())); return _instance!; } @@ -129,6 +129,6 @@ class MacOSWebStorageManager extends PlatformWebStorageManager } } -extension InternalWebStorageManager on MacOSWebStorageManager { +extension InternalWebStorageManager on WindowsWebStorageManager { get handleMethod => _handleMethod; } diff --git a/flutter_inappwebview_windows/lib/src/webview_environment/webview_environment.dart b/flutter_inappwebview_windows/lib/src/webview_environment/webview_environment.dart index b15a8963..c2020b5c 100644 --- a/flutter_inappwebview_windows/lib/src/webview_environment/webview_environment.dart +++ b/flutter_inappwebview_windows/lib/src/webview_environment/webview_environment.dart @@ -89,6 +89,25 @@ class WindowsWebViewEnvironment extends PlatformWebViewEnvironment return env; } + @override + Future getAvailableVersion( + {String? browserExecutableFolder}) async { + Map args = {}; + args.putIfAbsent('browserExecutableFolder', () => browserExecutableFolder); + return await _staticChannel.invokeMethod( + 'getAvailableVersion', args); + } + + @override + Future compareBrowserVersions( + {required String version1, required String version2}) async { + Map args = {}; + args.putIfAbsent('version1', () => version1); + args.putIfAbsent('version2', () => version2); + return await _staticChannel.invokeMethod( + 'compareBrowserVersions', args); + } + @override Future dispose() async { Map args = {}; diff --git a/flutter_inappwebview_windows/windows/CMakeLists.txt b/flutter_inappwebview_windows/windows/CMakeLists.txt index aaac0851..9b6da7af 100644 --- a/flutter_inappwebview_windows/windows/CMakeLists.txt +++ b/flutter_inappwebview_windows/windows/CMakeLists.txt @@ -74,6 +74,7 @@ list(APPEND PLUGIN_SOURCES "types/size_2d.h" "types/rect.cpp" "types/rect.h" + "types/callbacks_complete.h" "types/screenshot_configuration.cpp" "types/screenshot_configuration.h" "custom_platform_view/custom_platform_view.cc" diff --git a/flutter_inappwebview_windows/windows/cookie_manager.cpp b/flutter_inappwebview_windows/windows/cookie_manager.cpp index 9ab62069..2fa0eb5d 100644 --- a/flutter_inappwebview_windows/windows/cookie_manager.cpp +++ b/flutter_inappwebview_windows/windows/cookie_manager.cpp @@ -5,6 +5,7 @@ #include #include "cookie_manager.h" +#include "types/callbacks_complete.h" #include "utils/flutter.h" #include "utils/log.h" @@ -41,6 +42,46 @@ namespace flutter_inappwebview_plugin result_->Success(created); }); } + else if (string_equals(methodName, "getCookie")) { + auto url = get_fl_map_value(arguments, "url"); + auto name = get_fl_map_value(arguments, "name"); + getCookie(webViewEnvironment, url, name, [result_](const flutter::EncodableValue& cookie) + { + result_->Success(cookie); + }); + } + else if (string_equals(methodName, "getCookies")) { + auto url = get_fl_map_value(arguments, "url"); + getCookies(webViewEnvironment, url, [result_](const flutter::EncodableList& cookies) + { + result_->Success(cookies); + }); + } + else if (string_equals(methodName, "deleteCookie")) { + auto url = get_fl_map_value(arguments, "url"); + auto name = get_fl_map_value(arguments, "name"); + auto path = get_fl_map_value(arguments, "path"); + auto domain = get_optional_fl_map_value(arguments, "domain"); + deleteCookie(webViewEnvironment, url, name, path, domain, [result_](const bool& deleted) + { + result_->Success(deleted); + }); + } + else if (string_equals(methodName, "deleteCookies")) { + auto url = get_fl_map_value(arguments, "url"); + auto path = get_fl_map_value(arguments, "path"); + auto domain = get_optional_fl_map_value(arguments, "domain"); + deleteCookies(webViewEnvironment, url, path, domain, [result_](const bool& deleted) + { + result_->Success(deleted); + }); + } + else if (string_equals(methodName, "deleteAllCookies")) { + deleteAllCookies(webViewEnvironment, [result_](const bool& deleted) + { + result_->Success(deleted); + }); + } else { result_->NotImplemented(); } @@ -57,7 +98,7 @@ namespace flutter_inappwebview_plugin } } - void CookieManager::setCookie(WebViewEnvironment* webViewEnvironment, const flutter::EncodableMap& map, std::function completionHandler) const + void CookieManager::setCookie(WebViewEnvironment* webViewEnvironment, const flutter::EncodableMap& map, std::function completionHandler) const { if (!plugin || !plugin->webViewEnvironmentManager) { if (completionHandler) { @@ -118,6 +159,188 @@ namespace flutter_inappwebview_plugin } } + void CookieManager::getCookie(WebViewEnvironment* webViewEnvironment, const std::string& url, const std::string& name, std::function completionHandler) const + { + if (!plugin || !plugin->webViewEnvironmentManager) { + if (completionHandler) { + completionHandler(make_fl_value()); + } + return; + } + + nlohmann::json parameters = { + {"urls", std::vector{url}} + }; + + auto hr = webViewEnvironment->getWebView()->CallDevToolsProtocolMethod(L"Network.getCookies", utf8_to_wide(parameters.dump()).c_str(), Callback( + [completionHandler, name](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + if (succeededOrLog(errorCode)) { + nlohmann::json json = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson)); + auto jsonCookies = json["cookies"].get>(); + for (auto& jsonCookie : jsonCookies) { + auto cookieName = jsonCookie["name"].get(); + if (string_equals(name, cookieName)) { + completionHandler(flutter::EncodableMap{ + {"name", cookieName}, + {"value", jsonCookie["value"].get()}, + {"domain", jsonCookie["domain"].get()}, + {"path", jsonCookie["path"].get()}, + {"expiresDate", jsonCookie["expires"].get()}, + {"isHttpOnly", jsonCookie["httpOnly"].get()}, + {"isSecure", jsonCookie["secure"].get()}, + {"isSessionOnly", jsonCookie["session"].get()}, + {"sameSite", jsonCookie.contains("sameSite") ? jsonCookie["sameSite"].get() : make_fl_value()} + }); + return S_OK; + } + } + } + if (completionHandler) { + completionHandler(make_fl_value()); + } + return S_OK; + } + ).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler(make_fl_value()); + } + } + + void CookieManager::getCookies(WebViewEnvironment* webViewEnvironment, const std::string& url, std::function completionHandler) const + { + if (!plugin || !plugin->webViewEnvironmentManager) { + if (completionHandler) { + completionHandler({}); + } + return; + } + + nlohmann::json parameters = { + {"urls", std::vector{url}} + }; + + auto hr = webViewEnvironment->getWebView()->CallDevToolsProtocolMethod(L"Network.getCookies", utf8_to_wide(parameters.dump()).c_str(), Callback( + [completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + std::vector cookies = {}; + if (succeededOrLog(errorCode)) { + nlohmann::json json = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson)); + auto jsonCookies = json["cookies"].get>(); + for (auto& jsonCookie : jsonCookies) { + cookies.push_back(flutter::EncodableMap{ + {"name", jsonCookie["name"].get()}, + {"value", jsonCookie["value"].get()}, + {"domain", jsonCookie["domain"].get()}, + {"path", jsonCookie["path"].get()}, + {"expiresDate", jsonCookie["expires"].get()}, + {"isHttpOnly", jsonCookie["httpOnly"].get()}, + {"isSecure", jsonCookie["secure"].get()}, + {"isSessionOnly", jsonCookie["session"].get()}, + {"sameSite", jsonCookie.contains("sameSite") ? jsonCookie["sameSite"].get() : make_fl_value()} + }); + } + } + if (completionHandler) { + completionHandler(cookies); + } + return S_OK; + } + ).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler({}); + } + } + + void CookieManager::deleteCookie(WebViewEnvironment* webViewEnvironment, const std::string& url, const std::string& name, const std::string& path, const std::optional& domain, std::function completionHandler) const + { + if (!plugin || !plugin->webViewEnvironmentManager) { + if (completionHandler) { + completionHandler(false); + } + return; + } + + nlohmann::json parameters = { + {"url", url}, + {"name", name}, + {"path", path} + }; + if (domain.has_value()) { + parameters["domain"] = domain.value(); + } + + auto hr = webViewEnvironment->getWebView()->CallDevToolsProtocolMethod(L"Network.deleteCookies", utf8_to_wide(parameters.dump()).c_str(), Callback( + [completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + if (completionHandler) { + completionHandler(succeededOrLog(errorCode)); + } + return S_OK; + } + ).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler(false); + } + } + + void CookieManager::deleteCookies(WebViewEnvironment* webViewEnvironment, const std::string& url, const std::string& path, const std::optional& domain, std::function completionHandler) const + { + if (!plugin || !plugin->webViewEnvironmentManager) { + if (completionHandler) { + completionHandler(false); + } + return; + } + + getCookies(webViewEnvironment, url, [this, webViewEnvironment, url, path, domain, completionHandler](const flutter::EncodableList& cookies) + { + auto callbacksComplete = std::make_shared>( + [completionHandler](const std::vector& values) + { + if (completionHandler) { + completionHandler(true); + } + }); + + for (auto& cookie : cookies) { + auto cookieMap = std::get(cookie); + auto name = get_fl_map_value(cookieMap, "name"); + deleteCookie(webViewEnvironment, url, name, path, domain, [callbacksComplete](const bool& deleted) + { + callbacksComplete->addValue(deleted); + }); + } + }); + } + + void CookieManager::deleteAllCookies(WebViewEnvironment* webViewEnvironment, std::function completionHandler) const + { + if (!plugin || !plugin->webViewEnvironmentManager) { + if (completionHandler) { + completionHandler(false); + } + return; + } + + auto hr = webViewEnvironment->getWebView()->CallDevToolsProtocolMethod(L"Network.clearBrowserCookies", L"{}", Callback( + [completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + if (completionHandler) { + completionHandler(succeededOrLog(errorCode)); + } + return S_OK; + } + ).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler(false); + } + } + CookieManager::~CookieManager() { debugLog("dealloc CookieManager"); diff --git a/flutter_inappwebview_windows/windows/cookie_manager.h b/flutter_inappwebview_windows/windows/cookie_manager.h index b6f596d7..313fd8a2 100644 --- a/flutter_inappwebview_windows/windows/cookie_manager.h +++ b/flutter_inappwebview_windows/windows/cookie_manager.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "flutter_inappwebview_windows_plugin.h" #include "types/channel_delegate.h" @@ -25,7 +26,12 @@ namespace flutter_inappwebview_plugin const flutter::MethodCall& method_call, std::unique_ptr> result); - void setCookie(WebViewEnvironment* webViewEnvironment, const flutter::EncodableMap& map, std::function completionHandler) const; + void setCookie(WebViewEnvironment* webViewEnvironment, const flutter::EncodableMap& map, std::function completionHandler) const; + void getCookie(WebViewEnvironment* webViewEnvironment, const std::string& url, const std::string& name, std::function completionHandler) const; + void getCookies(WebViewEnvironment* webViewEnvironment, const std::string& url, std::function completionHandler) const; + void deleteCookie(WebViewEnvironment* webViewEnvironment, const std::string& url, const std::string& name, const std::string& path, const std::optional& domain, std::function completionHandler) const; + void deleteCookies(WebViewEnvironment* webViewEnvironment, const std::string& url, const std::string& path, const std::optional& domain, std::function completionHandler) const; + void deleteAllCookies(WebViewEnvironment* webViewEnvironment, std::function completionHandler) const; }; } diff --git a/flutter_inappwebview_windows/windows/types/callbacks_complete.h b/flutter_inappwebview_windows/windows/types/callbacks_complete.h new file mode 100644 index 00000000..c2ba5704 --- /dev/null +++ b/flutter_inappwebview_windows/windows/types/callbacks_complete.h @@ -0,0 +1,39 @@ +#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_CALLBACKS_COMPLETE_H_ +#define FLUTTER_INAPPWEBVIEW_PLUGIN_CALLBACKS_COMPLETE_H_ + +#include +#include +#include + +namespace flutter_inappwebview_plugin +{ + template + class CallbacksComplete + { + public: + std::function&)> onComplete; + + CallbacksComplete(const std::function&)> onComplete) + : onComplete(onComplete) + {} + + ~CallbacksComplete() + { + if (onComplete) { + onComplete(values_); + } + } + + void addValue(const T& value) + { + const std::lock_guard lock(mutex_); + values_.push_back(value); + } + + private: + std::vector values_; + std::mutex mutex_; + }; +} + +#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_CALLBACKS_COMPLETE_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp index 81e07fd8..bae69c1a 100644 --- a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.cpp @@ -39,6 +39,15 @@ namespace flutter_inappwebview_plugin auto settings = settingsMap.has_value() ? std::make_unique(settingsMap.value()) : nullptr; createWebViewEnvironment(id, std::move(settings), std::move(result)); } + else if (string_equals(methodName, "getAvailableVersion")) { + auto browserExecutableFolder = get_optional_fl_map_value(*arguments, "browserExecutableFolder"); + result->Success(make_fl_value(getAvailableVersion(browserExecutableFolder))); + } + else if (string_equals(methodName, "compareBrowserVersions")) { + auto version1 = get_fl_map_value(*arguments, "version1"); + auto version2 = get_fl_map_value(*arguments, "version2"); + result->Success(make_fl_value(compareBrowserVersions(version1, version2))); + } else { result->NotImplemented(); } @@ -89,6 +98,24 @@ namespace flutter_inappwebview_plugin }); } + std::optional WebViewEnvironmentManager::getAvailableVersion(std::optional browserExecutableFolder) + { + wil::unique_cotaskmem_string versionInfo; + if (succeededOrLog(GetAvailableCoreWebView2BrowserVersionString(browserExecutableFolder.has_value() ? utf8_to_wide(browserExecutableFolder.value()).c_str() : nullptr, &versionInfo))) { + return wide_to_utf8(versionInfo.get()); + } + return std::nullopt; + } + + std::optional WebViewEnvironmentManager::compareBrowserVersions(std::string version1, std::string version2) + { + int result = 0; + if (succeededOrLog(CompareBrowserVersions(utf8_to_wide(version1).c_str(), utf8_to_wide(version2).c_str(), &result))) { + return result; + } + return std::nullopt; + } + WebViewEnvironmentManager::~WebViewEnvironmentManager() { debugLog("dealloc WebViewEnvironmentManager"); diff --git a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h index a8bc82fa..6bfcafcb 100644 --- a/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h +++ b/flutter_inappwebview_windows/windows/webview_environment/webview_environment_manager.h @@ -34,6 +34,9 @@ namespace flutter_inappwebview_plugin { return hwnd_; } + + static std::optional getAvailableVersion(std::optional browserExecutableFolder); + static std::optional compareBrowserVersions(std::string version1, std::string version2); private: std::unique_ptr defaultEnvironment_; WNDCLASS windowClass_ = {}; From 599e2fd94ffac000c4ad902d063abb64b2a71875 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Jan 2024 04:04:26 +0100 Subject: [PATCH 20/21] windows: added openDevTools, callDevToolsProtocolMethod, addDevToolsProtocolEventListener and removeDevToolsProtocolEventListener methods, added some more inappwebview and inappbrowser basic settings --- .../in_app_webview_controller.dart | 15 ++ .../CHANGELOG.md | 4 + .../in_app_browser_settings.dart | 206 +++++++++--------- .../in_app_webview_keep_alive.dart | 5 +- .../in_app_webview_settings.dart | 44 +++- .../in_app_webview_settings.g.dart | 7 + .../platform_inappwebview_controller.dart | 56 +++++ .../lib/src/types/window_type.dart | 4 +- .../lib/src/types/window_type.g.dart | 6 + .../in_app_webview_controller.dart | 49 ++++- .../windows/in_app_browser/in_app_browser.cpp | 79 ++++++- .../windows/in_app_browser/in_app_browser.h | 10 +- .../in_app_browser_settings.cpp | 50 ++++- .../in_app_browser/in_app_browser_settings.h | 13 +- .../windows/in_app_webview/in_app_webview.cpp | 140 ++++++++++++ .../windows/in_app_webview/in_app_webview.h | 9 +- .../in_app_webview_settings.cpp | 54 +++++ .../in_app_webview/in_app_webview_settings.h | 6 + .../webview_channel_delegate.cpp | 63 ++++++ .../in_app_webview/webview_channel_delegate.h | 1 + .../windows/types/rect.h | 9 + .../windows/types/size_2d.h | 9 + .../windows/utils/util.h | 7 + 23 files changed, 710 insertions(+), 136 deletions(-) diff --git a/flutter_inappwebview/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview/lib/src/in_app_webview/in_app_webview_controller.dart index ee98e38d..bbff4d46 100644 --- a/flutter_inappwebview/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/flutter_inappwebview/lib/src/in_app_webview/in_app_webview_controller.dart @@ -484,6 +484,21 @@ class InAppWebViewController { URLResponse? urlResponse}) => platform.loadSimulatedRequest(urlRequest: urlRequest, data: data); + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.openDevTools} + Future openDevTools() => platform.openDevTools(); + + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.callDevToolsProtocolMethod} + Future callDevToolsProtocolMethod({required String methodName, Map? parameters}) => + platform.callDevToolsProtocolMethod(methodName: methodName, parameters: parameters); + + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.addDevToolsProtocolEventListener} + Future addDevToolsProtocolEventListener({required String eventName, required Function(dynamic data) callback}) => + platform.addDevToolsProtocolEventListener(eventName: eventName, callback: callback); + + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.removeDevToolsProtocolEventListener} + Future removeDevToolsProtocolEventListener({required String eventName}) => + platform.removeDevToolsProtocolEventListener(eventName: eventName); + ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.getIFrameId} Future getIFrameId() => platform.getIFrameId(); diff --git a/flutter_inappwebview_platform_interface/CHANGELOG.md b/flutter_inappwebview_platform_interface/CHANGELOG.md index 6aba9680..1cc18371 100644 --- a/flutter_inappwebview_platform_interface/CHANGELOG.md +++ b/flutter_inappwebview_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.11 + +- Added `PlatformWebViewEnvironment` class + ## 1.0.10 - Merged "Added == operator and hashCode to WebUri" [#1941](https://github.com/pichillilorenzo/flutter_inappwebview/pull/1941) (thanks to [daisukeueta](https://github.com/daisukeueta)) diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_settings.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_settings.dart index 038f8358..b9c287ab 100755 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_settings.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_browser/in_app_browser_settings.dart @@ -98,194 +98,198 @@ class InAppBrowserSettings_ implements BrowserOptions, AndroidOptions, IosOptions { ///Set to `true` to create the browser and load the page, but not show it. Omit or set to `false` to have the browser open and load normally. ///The default value is `false`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- Android native WebView - ///- iOS - ///- MacOS + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform(), + WindowsPlatform() + ]) bool? hidden; ///Set to `true` to hide the toolbar at the top of the WebView. The default value is `false`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- Android native WebView - ///- iOS - ///- MacOS + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform() + ]) bool? hideToolbarTop; ///Set the custom background color of the toolbar at the top. - /// - ///**Officially Supported Platforms/Implementations**: - ///- Android native WebView - ///- iOS - ///- MacOS + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform() + ]) Color_? toolbarTopBackgroundColor; ///Set to `true` to hide the url bar on the toolbar at the top. The default value is `false`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- Android native WebView - ///- iOS - ///- MacOS + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform() + ]) bool? hideUrlBar; ///Set to `true` to hide the progress bar when the WebView is loading a page. The default value is `false`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- Android native WebView - ///- iOS - ///- MacOS + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform() + ]) bool? hideProgressBar; ///Set to `true` to hide the default menu items. The default value is `false`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- Android native WebView - ///- iOS + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + IOSPlatform(), + MacOSPlatform() + ]) bool? hideDefaultMenuItems; ///Set to `true` if you want the title should be displayed. The default value is `false`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- Android native WebView + @SupportedPlatforms(platforms: [ + AndroidPlatform() + ]) bool? hideTitleBar; ///Set the action bar's title. - /// - ///**Officially Supported Platforms/Implementations**: - ///- Android native WebView - ///- MacOS + @SupportedPlatforms(platforms: [ + AndroidPlatform(), + MacOSPlatform(), + WindowsPlatform() + ]) String? toolbarTopFixedTitle; ///Set to `false` to not close the InAppBrowser when the user click on the Android back button and the WebView cannot go back to the history. The default value is `true`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- Android native WebView + @SupportedPlatforms(platforms: [ + AndroidPlatform() + ]) bool? closeOnCannotGoBack; ///Set to `false` to block the InAppBrowser WebView going back when the user click on the Android back button. The default value is `true`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- Android native WebView + @SupportedPlatforms(platforms: [ + AndroidPlatform() + ]) bool? allowGoBackWithBackButton; ///Set to `true` to close the InAppBrowser when the user click on the Android back button. The default value is `false`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- Android native WebView + @SupportedPlatforms(platforms: [ + AndroidPlatform() + ]) bool? shouldCloseOnBackButtonPressed; ///Set to `true` to set the toolbar at the top translucent. The default value is `true`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) bool? toolbarTopTranslucent; ///Set the tint color to apply to the navigation bar background. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) Color_? toolbarTopBarTintColor; ///Set the tint color to apply to the navigation items and bar button items. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) Color_? toolbarTopTintColor; ///Set to `true` to hide the toolbar at the bottom of the WebView. The default value is `false`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) bool? hideToolbarBottom; ///Set the custom background color of the toolbar at the bottom. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) Color_? toolbarBottomBackgroundColor; ///Set the tint color to apply to the bar button items. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) Color_? toolbarBottomTintColor; ///Set to `true` to set the toolbar at the bottom translucent. The default value is `true`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) bool? toolbarBottomTranslucent; ///Set the custom text for the close button. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) String? closeButtonCaption; ///Set the custom color for the close button. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) Color_? closeButtonColor; ///Set to `true` to hide the close button. The default value is `false`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) bool? hideCloseButton; ///Set the custom color for the menu button. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) Color_? menuButtonColor; ///Set the custom modal presentation style when presenting the WebView. The default value is [ModalPresentationStyle.FULL_SCREEN]. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) ModalPresentationStyle_? presentationStyle; ///Set to the custom transition style when presenting the WebView. The default value is [ModalTransitionStyle.COVER_VERTICAL]. - /// - ///**Officially Supported Platforms/Implementations**: - ///- iOS + @SupportedPlatforms(platforms: [ + IOSPlatform() + ]) ModalTransitionStyle_? transitionStyle; ///How the browser window should be added to the main window. ///The default value is [WindowType.WINDOW]. - /// - ///**Officially Supported Platforms/Implementations**: - ///- MacOS + @SupportedPlatforms(platforms: [ + MacOSPlatform(), + WindowsPlatform() + ]) WindowType_? windowType; ///The window’s alpha value. ///The default value is `1.0`. - /// - ///**Officially Supported Platforms/Implementations**: - ///- MacOS + @SupportedPlatforms(platforms: [ + MacOSPlatform(), + WindowsPlatform() + ]) double? windowAlphaValue; ///Flags that describe the window’s current style, such as if it’s resizable or in full-screen mode. - /// - ///**Officially Supported Platforms/Implementations**: - ///- MacOS + @SupportedPlatforms(platforms: [ + MacOSPlatform() + ]) WindowStyleMask_? windowStyleMask; ///The type of separator that the app displays between the title bar and content of a window. - /// - ///**NOTE for MacOS**: available on MacOS 11.0+. - /// - ///**Officially Supported Platforms/Implementations**: - ///- MacOS + @SupportedPlatforms(platforms: [ + MacOSPlatform(available: '11.0') + ]) WindowTitlebarSeparatorStyle_? windowTitlebarSeparatorStyle; ///Sets the origin and size of the window’s frame rectangle according to a given frame rectangle, ///thereby setting its position and size onscreen. - /// - ///**Officially Supported Platforms/Implementations**: - ///- MacOS + @SupportedPlatforms(platforms: [ + MacOSPlatform(), + WindowsPlatform() + ]) InAppWebViewRect_? windowFrame; InAppBrowserSettings_( diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_keep_alive.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_keep_alive.dart index e026022f..81dc56f2 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_keep_alive.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_keep_alive.dart @@ -28,6 +28,7 @@ class InAppWebViewControllerKeepAliveProps { Map injectedScriptsFromURL; Set webMessageChannels = Set(); Set webMessageListeners = Set(); + Map devToolsProtocolEventListenerMap; InAppWebViewControllerKeepAliveProps( {required this.javaScriptHandlersMap, @@ -35,5 +36,7 @@ class InAppWebViewControllerKeepAliveProps { required this.webMessageListenerObjNames, required this.injectedScriptsFromURL, required this.webMessageChannels, - required this.webMessageListeners}); + required this.webMessageListeners, + required this.devToolsProtocolEventListenerMap + }); } diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.dart index a53edcc2..e3b059ad 100755 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.dart @@ -58,7 +58,7 @@ class InAppWebViewSettings_ { ///it will be automatically inferred as `true`, otherwise, the default value is `false`. ///This logic will not be applied for [PlatformInAppBrowser], where you must set the value manually. @SupportedPlatforms( - platforms: [AndroidPlatform(), IOSPlatform(), MacOSPlatform()]) + platforms: [AndroidPlatform(), IOSPlatform(), MacOSPlatform(), WindowsPlatform()]) bool? useShouldOverrideUrlLoading; ///Set to `true` to be able to listen at the [PlatformWebViewCreationParams.onLoadResource] event. @@ -98,7 +98,11 @@ class InAppWebViewSettings_ { MacOSPlatform( apiName: "WKWebView.customUserAgent", apiUrl: - "https://developer.apple.com/documentation/webkit/wkwebview/1414950-customuseragent") + "https://developer.apple.com/documentation/webkit/wkwebview/1414950-customuseragent"), + WindowsPlatform( + apiName: 'ICoreWebView2Settings2.put_UserAgent', + apiUrl: 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2settings2?view=webview2-1.0.2210.55#put_useragent' + ) ]) String? userAgent; @@ -130,7 +134,12 @@ class InAppWebViewSettings_ { apiName: "WKWebpagePreferences.allowsContentJavaScript", apiUrl: "https://developer.apple.com/documentation/webkit/wkwebpagepreferences/3552422-allowscontentjavascript/"), - WebPlatform(requiresSameOrigin: false) + WebPlatform(requiresSameOrigin: false), + WindowsPlatform( + apiName: "ICoreWebView2Settings.put_IsScriptEnabled", + apiUrl: + "https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2settings?view=webview2-1.0.2210.55#put_isscriptenabled" + ) ]) bool? javaScriptEnabled; @@ -307,7 +316,12 @@ because there isn't any way to make the website data store non-persistent for th @SupportedPlatforms(platforms: [ AndroidPlatform(), IOSPlatform(), - MacOSPlatform(available: "12.0") + MacOSPlatform(available: "12.0"), + WindowsPlatform( + available: '1.0.774.44', + apiName: 'ICoreWebView2Controller2.put_DefaultBackgroundColor', + apiUrl: 'https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2controller2?view=webview2-1.0.2210.55#put_defaultbackgroundcolor' + ) ]) bool? transparentBackground; @@ -323,7 +337,13 @@ because there isn't any way to make the website data store non-persistent for th ///Set to `true` to disable context menu. The default value is `false`. @SupportedPlatforms( - platforms: [AndroidPlatform(), IOSPlatform(), WebPlatform()]) + platforms: [AndroidPlatform(), IOSPlatform(), WebPlatform(), + WindowsPlatform( + apiName: "ICoreWebView2Settings.put_AreDefaultContextMenusEnabled", + apiUrl: + "https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2settings?view=webview2-1.0.2210.55#put_aredefaultcontextmenusenabled" + ) + ]) bool? disableContextMenu; ///Set to `false` if the WebView should not support zooming using its on-screen zoom controls and gestures. The default value is `true`. @@ -333,7 +353,12 @@ because there isn't any way to make the website data store non-persistent for th apiUrl: "https://developer.android.com/reference/android/webkit/WebSettings?hl=en#setSupportZoom(boolean)"), IOSPlatform(), - MacOSPlatform() + MacOSPlatform(), + WindowsPlatform( + apiName: "ICoreWebView2Settings.put_IsZoomControlEnabled", + apiUrl: + "https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2settings?view=webview2-1.0.2210.55#put_iszoomcontrolenabled" + ) ]) bool? supportZoom; @@ -1537,7 +1562,12 @@ as it can cause framerate drops on animations in Android 9 and lower (see [Hybri available: "13.3", apiName: "WKWebView.isInspectable", apiUrl: - "https://developer.apple.com/documentation/webkit/wkwebview/4111163-isinspectable") + "https://developer.apple.com/documentation/webkit/wkwebview/4111163-isinspectable"), + WindowsPlatform( + apiName: "ICoreWebView2Settings.put_AreDevToolsEnabled", + apiUrl: + "https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2settings?view=webview2-1.0.2210.55#put_aredevtoolsenabled" + ) ]) bool? isInspectable; diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.g.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.g.dart index 3ae73665..7c30031d 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_settings.g.dart @@ -371,6 +371,7 @@ class InAppWebViewSettings { ///- Android native WebView ///- iOS ///- Web but iframe requires same origin + ///- Windows ([Official API - ICoreWebView2Settings.put_AreDefaultContextMenusEnabled](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2settings?view=webview2-1.0.2210.55#put_aredefaultcontextmenusenabled)) bool? disableContextMenu; ///Sets whether the default Android WebView’s internal error page should be suppressed or displayed for bad navigations. @@ -648,6 +649,7 @@ class InAppWebViewSettings { ///**Officially Supported Platforms/Implementations**: ///- iOS 16.4+ ([Official API - WKWebView.isInspectable](https://developer.apple.com/documentation/webkit/wkwebview/4111163-isinspectable)) ///- MacOS 13.3+ ([Official API - WKWebView.isInspectable](https://developer.apple.com/documentation/webkit/wkwebview/4111163-isinspectable)) + ///- Windows ([Official API - ICoreWebView2Settings.put_AreDevToolsEnabled](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2settings?view=webview2-1.0.2210.55#put_aredevtoolsenabled)) bool? isInspectable; ///A Boolean value that determines whether paging is enabled for the scroll view. @@ -691,6 +693,7 @@ class InAppWebViewSettings { ///- iOS ([Official API - WKWebpagePreferences.allowsContentJavaScript](https://developer.apple.com/documentation/webkit/wkwebpagepreferences/3552422-allowscontentjavascript/)) ///- MacOS ([Official API - WKWebpagePreferences.allowsContentJavaScript](https://developer.apple.com/documentation/webkit/wkwebpagepreferences/3552422-allowscontentjavascript/)) ///- Web + ///- Windows ([Official API - ICoreWebView2Settings.put_IsScriptEnabled](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2settings?view=webview2-1.0.2210.55#put_isscriptenabled)) bool? javaScriptEnabled; ///Sets the underlying layout algorithm. This will cause a re-layout of the WebView. @@ -983,6 +986,7 @@ class InAppWebViewSettings { ///- Android native WebView ([Official API - WebSettings.setSupportZoom](https://developer.android.com/reference/android/webkit/WebSettings?hl=en#setSupportZoom(boolean))) ///- iOS ///- MacOS + ///- Windows ([Official API - ICoreWebView2Settings.put_IsZoomControlEnabled](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2settings?view=webview2-1.0.2210.55#put_iszoomcontrolenabled)) bool? supportZoom; ///Set to `true` if you want the WebView suppresses content rendering until it is fully loaded into memory. The default value is `false`. @@ -1012,6 +1016,7 @@ class InAppWebViewSettings { ///- Android native WebView ///- iOS ///- MacOS 12.0+ + ///- Windows 1.0.774.44+ ([Official API - ICoreWebView2Controller2.put_DefaultBackgroundColor](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2controller2?view=webview2-1.0.2210.55#put_defaultbackgroundcolor)) bool? transparentBackground; ///The color the web view displays behind the active page, visible when the user scrolls beyond the bounds of the page. @@ -1137,6 +1142,7 @@ class InAppWebViewSettings { ///- Android native WebView ///- iOS ///- MacOS + ///- Windows bool? useShouldOverrideUrlLoading; ///Set to `true` if the WebView should enable support for the "viewport" HTML meta tag or should use a wide viewport. @@ -1154,6 +1160,7 @@ class InAppWebViewSettings { ///- Android native WebView ([Official API - WebSettings.setUserAgentString](https://developer.android.com/reference/android/webkit/WebSettings?hl=en#setUserAgentString(java.lang.String))) ///- iOS ([Official API - WKWebView.customUserAgent](https://developer.apple.com/documentation/webkit/wkwebview/1414950-customuseragent)) ///- MacOS ([Official API - WKWebView.customUserAgent](https://developer.apple.com/documentation/webkit/wkwebview/1414950-customuseragent)) + ///- Windows ([Official API - ICoreWebView2Settings2.put_UserAgent](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2settings2?view=webview2-1.0.2210.55#put_useragent)) String? userAgent; ///Define whether the vertical scrollbar should be drawn or not. The default value is `true`. diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart index 077f9180..54e2d51f 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart @@ -2040,6 +2040,62 @@ abstract class PlatformInAppWebViewController extends PlatformInterface 'loadSimulatedRequest is not implemented on the current platform'); } + ///{@template flutter_inappwebview_platform_interface.PlatformInAppWebViewController.openDevTools} + ///Opens the DevTools window for the current document in the WebView. + ///Does nothing if run when the DevTools window is already open. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - ICoreWebView2.OpenDevToolsWindow](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#opendevtoolswindow)) + ///{@endtemplate} + Future openDevTools() { + throw UnimplementedError( + 'openDevTools is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppWebViewController.callDevToolsProtocolMethod} + ///Runs an asynchronous `DevToolsProtocol` method. + /// + ///For more information about available methods, navigate to [DevTools Protocol Viewer](https://chromedevtools.github.io/devtools-protocol/tot). + ///The [methodName] parameter is the full name of the method in the `{domain}.{method}` format. + ///The [parameters] will be a JSON formatted string containing the parameters for the corresponding method. + ///This function throws an error if the [methodName] is unknown or the [parameters] has an error. + ///In the case of such an error, the [parameters] parameter of the + ///handler will include information about the error. + ///Note even though WebView dispatches the CDP messages in the order called, + ///CDP method calls may be processed out of order. + ///If you require CDP methods to run in a particular order, you should wait for + ///the previous method's completed handler to run before calling the next method. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - ICoreWebView2.CallDevToolsProtocolMethod](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.2210.55#calldevtoolsprotocolmethod)) + ///{@endtemplate} + Future callDevToolsProtocolMethod({required String methodName, Map? parameters}) { + throw UnimplementedError( + 'callDevToolsProtocolMethod is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppWebViewController.addDevToolsProtocolEventListener} + ///Subscribe to a `DevToolsProtocol` event. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - ICoreWebView2DevToolsProtocolEventReceiver.add_DevToolsProtocolEventReceived](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2devtoolsprotocoleventreceiver?view=webview2-1.0.2210.55#add_devtoolsprotocoleventreceived)) + ///{@endtemplate} + Future addDevToolsProtocolEventListener({required String eventName, required Function(dynamic data) callback}) { + throw UnimplementedError( + 'addDevToolsProtocolEventListener is not implemented on the current platform'); + } + + ///{@template flutter_inappwebview_platform_interface.PlatformInAppWebViewController.removeDevToolsProtocolEventListener} + ///Remove an event handler previously added with [addDevToolsProtocolEventListener]. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Windows ([Official API - ICoreWebView2DevToolsProtocolEventReceiver.remove_DevToolsProtocolEventReceived](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2devtoolsprotocoleventreceiver?view=webview2-1.0.2210.55#remove_devtoolsprotocoleventreceived)) + ///{@endtemplate} + Future removeDevToolsProtocolEventListener({required String eventName}) { + throw UnimplementedError( + 'removeDevToolsProtocolEventListener is not implemented on the current platform'); + } + ///{@template flutter_inappwebview_platform_interface.PlatformInAppWebViewController.getIFrameId} ///Returns the iframe `id` attribute used on the Web platform. /// diff --git a/flutter_inappwebview_platform_interface/lib/src/types/window_type.dart b/flutter_inappwebview_platform_interface/lib/src/types/window_type.dart index 2d49b9c9..74673224 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/window_type.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/window_type.dart @@ -12,11 +12,11 @@ class WindowType_ { const WindowType_._internal(this._value); ///Adds the new browser window as a separate new window from the main window. - @EnumSupportedPlatforms(platforms: [EnumMacOSPlatform(value: 'WINDOW')]) + @EnumSupportedPlatforms(platforms: [EnumMacOSPlatform(value: 'WINDOW'), EnumWindowsPlatform(value: 'WINDOW')]) static const WINDOW = const WindowType_._internal('WINDOW'); ///Adds the new browser window as a child window of the main window. - @EnumSupportedPlatforms(platforms: [EnumMacOSPlatform(value: 'CHILD')]) + @EnumSupportedPlatforms(platforms: [EnumMacOSPlatform(value: 'CHILD'), EnumWindowsPlatform(value: 'CHILD')]) static const CHILD = const WindowType_._internal('CHILD'); ///Adds the new browser window as a new tab in a tabbed window of the main window. diff --git a/flutter_inappwebview_platform_interface/lib/src/types/window_type.g.dart b/flutter_inappwebview_platform_interface/lib/src/types/window_type.g.dart index b9be001c..78d757c1 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/window_type.g.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/window_type.g.dart @@ -20,10 +20,13 @@ class WindowType { /// ///**Officially Supported Platforms/Implementations**: ///- MacOS + ///- Windows static final CHILD = WindowType._internalMultiPlatform('CHILD', () { switch (defaultTargetPlatform) { case TargetPlatform.macOS: return 'CHILD'; + case TargetPlatform.windows: + return 'CHILD'; default: break; } @@ -48,10 +51,13 @@ class WindowType { /// ///**Officially Supported Platforms/Implementations**: ///- MacOS + ///- Windows static final WINDOW = WindowType._internalMultiPlatform('WINDOW', () { switch (defaultTargetPlatform) { case TargetPlatform.macOS: return 'WINDOW'; + case TargetPlatform.windows: + return 'WINDOW'; default: break; } diff --git a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart index 66e56d1e..37997164 100644 --- a/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/flutter_inappwebview_windows/lib/src/in_app_webview/in_app_webview_controller.dart @@ -76,6 +76,7 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController Map _injectedScriptsFromURL = {}; Set _webMessageChannels = Set(); Set _webMessageListeners = Set(); + Map _devToolsProtocolEventListenerMap = HashMap(); // static map that contains the properties to be saved and restored for keep alive feature static final Map @@ -179,7 +180,9 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController userScripts: _userScripts, webMessageListenerObjNames: _webMessageListenerObjNames, webMessageChannels: _webMessageChannels, - webMessageListeners: _webMessageListeners); + webMessageListeners: _webMessageListeners, + devToolsProtocolEventListenerMap: _devToolsProtocolEventListenerMap + ); } else { // restore controller properties _injectedScriptsFromURL = props.injectedScriptsFromURL; @@ -190,6 +193,7 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController props.webMessageChannels as Set; _webMessageListeners = props.webMessageListeners as Set; + _devToolsProtocolEventListenerMap = props.devToolsProtocolEventListenerMap; } } } @@ -1403,6 +1407,14 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController .onContentSizeChanged(oldContentSize, newContentSize); } break; + case "onDevToolsProtocolEventReceived": + String eventName = call.arguments["eventName"]; + dynamic data = call.arguments["data"] != null ? jsonDecode(call.arguments["data"]) : null; + + if (this._devToolsProtocolEventListenerMap.containsKey(eventName)) { + this._devToolsProtocolEventListenerMap[eventName]!.call(data); + } + break; case "onCallJsHandler": String handlerName = call.arguments["handlerName"]; // decode args to json @@ -2627,6 +2639,40 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController await channel?.invokeMethod('loadSimulatedRequest', args); } + @override + Future openDevTools() async { + Map args = {}; + await channel?.invokeMethod('openDevTools', args); + } + + @override + Future callDevToolsProtocolMethod({required String methodName, Map? parameters}) async { + Map args = {}; + args.putIfAbsent('methodName', () => methodName); + args.putIfAbsent('parametersAsJson', () => parameters != null ? jsonEncode(parameters) : null); + final result = await channel?.invokeMethod('callDevToolsProtocolMethod', args); + if (result != null) { + return jsonDecode(result); + } + return null; + } + + @override + Future addDevToolsProtocolEventListener({required String eventName, required Function(dynamic data) callback}) async { + Map args = {}; + args.putIfAbsent('eventName', () => eventName); + await channel?.invokeMethod('addDevToolsProtocolEventListener', args); + this._devToolsProtocolEventListenerMap[eventName] = callback; + } + + @override + Future removeDevToolsProtocolEventListener({required String eventName}) async { + Map args = {}; + args.putIfAbsent('eventName', () => eventName); + await channel?.invokeMethod('removeDevToolsProtocolEventListener', args); + this._devToolsProtocolEventListenerMap.remove(eventName); + } + @override Future getDefaultUserAgent() async { Map args = {}; @@ -2689,6 +2735,7 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController webMessageListener.dispose(); } _webMessageListeners.clear(); + _devToolsProtocolEventListenerMap.clear(); } } } diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp index 682c4aa5..71f5bd39 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.cpp @@ -21,25 +21,48 @@ namespace flutter_inappwebview_plugin wndClass.hInstance = m_hInstance; wndClass.hIcon = LoadIcon(NULL, IDI_WINLOGO); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpfnWndProc = InAppBrowser::WndProc; RegisterClass(&wndClass); + auto parentWindow = plugin->registrar->GetView()->GetNativeWindow(); + RECT bounds; + GetWindowRect(parentWindow, &bounds); + + auto x = CW_USEDEFAULT; + auto y = CW_USEDEFAULT; + auto width = bounds.right - bounds.left; + auto height = bounds.bottom - bounds.top; + + if (settings->windowFrame) { + x = (int)settings->windowFrame->x; + y = (int)settings->windowFrame->y; + width = (int)settings->windowFrame->width; + height = (int)settings->windowFrame->height; + } + m_hWnd = CreateWindowEx( - 0, // Optional window styles. + WS_EX_LAYERED, // Optional window styles. wndClass.lpszClassName, // Window class - L"", // Window text - WS_OVERLAPPEDWINDOW, // Window style - // Size and position - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + settings->toolbarTopFixedTitle.empty() ? L"" : utf8_to_wide(settings->toolbarTopFixedTitle).c_str(), // Window text - NULL, // Parent window - NULL, // Menu + settings->windowType == InAppBrowserWindowType::window ? WS_OVERLAPPEDWINDOW : (WS_CHILDWINDOW | WS_OVERLAPPEDWINDOW), // Window style + + // Position + x, y, + // Size + width, height, + + settings->windowType == InAppBrowserWindowType::window ? nullptr : parentWindow, // Parent window + nullptr, // Menu wndClass.hInstance, // Instance handle this // Additional application data ); + SetLayeredWindowAttributes(m_hWnd, 0, (BYTE)(255 * settings->windowAlphaValue), LWA_ALPHA); + ShowWindow(m_hWnd, settings->hidden ? SW_HIDE : SW_SHOW); InAppWebViewCreationParams webViewParams = { @@ -99,9 +122,49 @@ namespace flutter_inappwebview_plugin return !IsWindowVisible(m_hWnd); } + void InAppBrowser::setSettings(const std::shared_ptr newSettings, const flutter::EncodableMap& newSettingsMap) + { + if (webView) { + webView->setSettings(std::make_shared(newSettingsMap), newSettingsMap); + } + + if (fl_map_contains_not_null(newSettingsMap, "hidden") && settings->hidden != newSettings->hidden) { + newSettings->hidden ? hide() : show(); + } + + if (fl_map_contains_not_null(newSettingsMap, "toolbarTopFixedTitle") && !string_equals(settings->toolbarTopFixedTitle, newSettings->toolbarTopFixedTitle) && !newSettings->toolbarTopFixedTitle.empty()) { + SetWindowText(m_hWnd, utf8_to_wide(newSettings->toolbarTopFixedTitle).c_str()); + } + + if (fl_map_contains_not_null(newSettingsMap, "windowAlphaValue") && settings->windowAlphaValue != newSettings->windowAlphaValue) { + SetLayeredWindowAttributes(m_hWnd, 0, (BYTE)(255 * newSettings->windowAlphaValue), LWA_ALPHA); + } + + if (fl_map_contains_not_null(newSettingsMap, "windowFrame")) { + auto x = (int)newSettings->windowFrame->x; + auto y = (int)newSettings->windowFrame->y; + auto width = (int)newSettings->windowFrame->width; + auto height = (int)newSettings->windowFrame->height; + MoveWindow(m_hWnd, x, y, width, height, true); + } + + settings = newSettings; + } + + flutter::EncodableValue InAppBrowser::getSettings() const + { + if (!settings || !webView) { + return make_fl_value(); + } + + auto encodableMap = settings->getRealSettings(this); + encodableMap.merge(std::get(webView->getSettings())); + return encodableMap; + } + void InAppBrowser::didChangeTitle(const std::optional& title) const { - if (title.has_value()) { + if (title.has_value() && settings->toolbarTopFixedTitle.empty()) { SetWindowText(m_hWnd, utf8_to_wide(title.value()).c_str()); } } diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h index f0a0d677..3c1181c0 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser.h @@ -3,7 +3,6 @@ #include #include -#include #include #include "../flutter_inappwebview_windows_plugin.h" @@ -41,7 +40,7 @@ namespace flutter_inappwebview_plugin const std::string id; std::unique_ptr webView; std::unique_ptr channelDelegate; - const std::shared_ptr settings; + std::shared_ptr settings; InAppBrowser(const FlutterInappwebviewWindowsPlugin* plugin, const InAppBrowserCreationParams& params); ~InAppBrowser(); @@ -50,9 +49,14 @@ namespace flutter_inappwebview_plugin void show() const; void hide() const; bool isHidden() const; + void setSettings(const std::shared_ptr newSettings, const flutter::EncodableMap& newSettingsMap); + flutter::EncodableValue getSettings() const; void didChangeTitle(const std::optional& title) const; - + HWND getHWND() const + { + return m_hWnd; + } private: const HINSTANCE m_hInstance; HWND m_hWnd; diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.cpp b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.cpp index 80f1fa70..dea7f862 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.cpp +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.cpp @@ -1,29 +1,27 @@ #include "../utils/flutter.h" #include "../utils/log.h" +#include "../utils/string.h" #include "in_app_browser_settings.h" +#include "in_app_browser.h" + namespace flutter_inappwebview_plugin { namespace { InAppBrowserWindowType inAppBrowserWindowTypeFromString(const std::string& s) { - if (s.compare("CHILD") == 0) { - return child; + if (string_equals(s, "CHILD")) { + return InAppBrowserWindowType::child; } - else if (s.compare("TABBED") == 0) { - return tabbed; - } - return window; + return InAppBrowserWindowType::window; } std::string inAppBrowserWindowTypeToString(const InAppBrowserWindowType& t) { switch (t) { - case child: + case InAppBrowserWindowType::child: return "CHILD"; - case tabbed: - return "TABBED"; default: return "WINDOW"; } @@ -35,8 +33,40 @@ namespace flutter_inappwebview_plugin InAppBrowserSettings::InAppBrowserSettings(const flutter::EncodableMap& encodableMap) { hidden = get_fl_map_value(encodableMap, "hidden", hidden); - windowType = inAppBrowserWindowTypeFromString(get_fl_map_value(encodableMap, "windowType", inAppBrowserWindowTypeToString(window))); + windowType = inAppBrowserWindowTypeFromString(get_fl_map_value(encodableMap, "windowType", inAppBrowserWindowTypeToString(InAppBrowserWindowType::window))); + toolbarTopFixedTitle = get_fl_map_value(encodableMap, "toolbarTopFixedTitle", toolbarTopFixedTitle); windowAlphaValue = get_fl_map_value(encodableMap, "windowAlphaValue", windowAlphaValue); + auto windowFrameMap = get_optional_fl_map_value(encodableMap, "windowFrame"); + if (windowFrameMap.has_value()) { + windowFrame = std::make_shared(windowFrameMap.value()); + } + } + + flutter::EncodableMap InAppBrowserSettings::toEncodableMap() const + { + return flutter::EncodableMap{ + {"hidden", hidden}, + {"windowType", inAppBrowserWindowTypeToString(windowType)}, + {"toolbarTopFixedTitle", toolbarTopFixedTitle}, + {"windowAlphaValue", windowAlphaValue}, + {"windowFrame", windowFrame ? windowFrame->toEncodableMap() : make_fl_value()}, + }; + } + + flutter::EncodableMap InAppBrowserSettings::getRealSettings(const InAppBrowser* inAppBrowser) const + { + auto settingsMap = toEncodableMap(); + settingsMap["hidden"] = inAppBrowser->isHidden(); + + BYTE alphaValue = 0; + GetLayeredWindowAttributes(inAppBrowser->getHWND(), nullptr, &alphaValue, nullptr); + settingsMap["windowAlphaValue"] = (double)alphaValue; + + RECT position; + GetWindowRect(inAppBrowser->getHWND(), &position); + settingsMap["windowFrame"] = std::make_unique(position.left, position.top, position.right - position.left, position.bottom - position.top)->toEncodableMap(); + + return settingsMap; } InAppBrowserSettings::~InAppBrowserSettings() diff --git a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.h b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.h index c65bbe14..1243ca2b 100644 --- a/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.h +++ b/flutter_inappwebview_windows/windows/in_app_browser/in_app_browser_settings.h @@ -2,14 +2,18 @@ #define FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_SETTINGS_H_ #include +#include #include +#include "../types/rect.h" + namespace flutter_inappwebview_plugin { + class InAppBrowser; + enum InAppBrowserWindowType { window, - child, - tabbed + child }; class InAppBrowserSettings @@ -17,11 +21,16 @@ namespace flutter_inappwebview_plugin public: bool hidden = false; InAppBrowserWindowType windowType = window; + std::string toolbarTopFixedTitle; double windowAlphaValue = 1.0; + std::shared_ptr windowFrame; InAppBrowserSettings(); InAppBrowserSettings(const flutter::EncodableMap& encodableMap); ~InAppBrowserSettings(); + + flutter::EncodableMap toEncodableMap() const; + flutter::EncodableMap getRealSettings(const InAppBrowser* inAppBrowser) const; }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_SETTINGS_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp index f85e6be9..abfae2b1 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.cpp @@ -143,6 +143,8 @@ namespace flutter_inappwebview_plugin if (succeededOrLog(hrWebView2Settings)) { webView2Settings->put_IsScriptEnabled(settings->javaScriptEnabled); webView2Settings->put_IsZoomControlEnabled(settings->supportZoom); + webView2Settings->put_AreDevToolsEnabled(settings->isInspectable); + webView2Settings->put_AreDefaultContextMenusEnabled(!settings->disableContextMenu); wil::com_ptr webView2Settings2; if (succeededOrLog(webView2Settings->QueryInterface(IID_PPV_ARGS(&webView2Settings2)))) { @@ -152,6 +154,13 @@ namespace flutter_inappwebview_plugin } } + wil::com_ptr webViewController2; + if (succeededOrLog(webViewController->QueryInterface(IID_PPV_ARGS(&webViewController2)))) { + if (!settings->transparentBackground) { + webViewController2->put_DefaultBackgroundColor({ 0, 255, 255, 255 }); + } + } + // required to make Runtime events work failedLog(webView->CallDevToolsProtocolMethod(L"Runtime.enable", L"{}", Callback( [this](HRESULT errorCode, LPCWSTR returnObjectAsJson) @@ -971,6 +980,137 @@ namespace flutter_inappwebview_plugin } } + void InAppWebView::setSettings(const std::shared_ptr newSettings, const flutter::EncodableMap& newSettingsMap) + { + wil::com_ptr webView2Settings; + if (succeededOrLog(webView->get_Settings(&webView2Settings))) { + if (fl_map_contains_not_null(newSettingsMap, "javaScriptEnabled") && settings->javaScriptEnabled != newSettings->javaScriptEnabled) { + webView2Settings->put_IsScriptEnabled(newSettings->javaScriptEnabled); + } + + if (fl_map_contains_not_null(newSettingsMap, "supportZoom") && settings->supportZoom != newSettings->supportZoom) { + webView2Settings->put_IsZoomControlEnabled(newSettings->supportZoom); + } + + if (fl_map_contains_not_null(newSettingsMap, "isInspectable") && settings->isInspectable != newSettings->isInspectable) { + webView2Settings->put_AreDevToolsEnabled(newSettings->isInspectable); + } + + if (fl_map_contains_not_null(newSettingsMap, "disableContextMenu") && settings->disableContextMenu != newSettings->disableContextMenu) { + webView2Settings->put_AreDefaultContextMenusEnabled(!newSettings->disableContextMenu); + } + + wil::com_ptr webView2Settings2; + if (succeededOrLog(webView2Settings->QueryInterface(IID_PPV_ARGS(&webView2Settings2)))) { + if (fl_map_contains_not_null(newSettingsMap, "userAgent") && !string_equals(settings->userAgent, newSettings->userAgent)) { + webView2Settings2->put_UserAgent(utf8_to_wide(newSettings->userAgent).c_str()); + } + } + } + + wil::com_ptr webViewController2; + if (succeededOrLog(webViewController->QueryInterface(IID_PPV_ARGS(&webViewController2)))) { + if (fl_map_contains_not_null(newSettingsMap, "transparentBackground") && settings->transparentBackground != newSettings->transparentBackground) { + BYTE alpha = newSettings->transparentBackground ? 0 : 255; + webViewController2->put_DefaultBackgroundColor({ alpha, 255, 255, 255 }); + } + } + + settings = newSettings; + } + + flutter::EncodableValue InAppWebView::getSettings() const + { + if (!settings || !webView) { + return make_fl_value(); + } + + wil::com_ptr webView2Settings; + if (succeededOrLog(webView->get_Settings(&webView2Settings))) { + return settings->getRealSettings(webView2Settings.get()); + } + return settings->toEncodableMap(); + } + + void InAppWebView::openDevTools() const + { + if (!webView) { + return; + } + + failedLog(webView->OpenDevToolsWindow()); + } + + void InAppWebView::callDevToolsProtocolMethod(const std::string& methodName, const std::optional& parametersAsJson, const std::function&)> completionHandler) const + { + if (!webView) { + if (completionHandler) { + completionHandler(S_OK, std::nullopt); + } + return; + } + + auto hr = webView->CallDevToolsProtocolMethod( + utf8_to_wide(methodName).c_str(), + !parametersAsJson.has_value() || parametersAsJson.value().empty() ? L"{}" : utf8_to_wide(parametersAsJson.value()).c_str(), + Callback( + [completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson) + { + failedLog(errorCode); + if (completionHandler) { + completionHandler(errorCode, wide_to_utf8(returnObjectAsJson)); + } + return S_OK; + } + ).Get()); + + if (failedAndLog(hr) && completionHandler) { + completionHandler(hr, std::nullopt); + } + } + + void InAppWebView::addDevToolsProtocolEventListener(const std::string& eventName) + { + if (map_contains(devToolsProtocolEventListener_, eventName)) { + return; + } + + wil::com_ptr eventReceiver; + if (succeededOrLog(webView->GetDevToolsProtocolEventReceiver(utf8_to_wide(eventName).c_str(), &eventReceiver))) { + EventRegistrationToken token = {}; + auto hr = eventReceiver->add_DevToolsProtocolEventReceived( + Callback( + [this, eventName]( + ICoreWebView2* sender, + ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) -> HRESULT + { + if (!channelDelegate) { + return S_OK; + } + + wil::unique_cotaskmem_string json; + failedLog(args->get_ParameterObjectAsJson(&json)); + channelDelegate->onDevToolsProtocolEventReceived(eventName, wide_to_utf8(json.get())); + + return S_OK; + }) + .Get(), &token); + if (succeededOrLog(hr)) { + devToolsProtocolEventListener_.insert({ eventName, std::make_pair(std::move(eventReceiver), token) }); + } + } + } + + void InAppWebView::removeDevToolsProtocolEventListener(const std::string& eventName) + { + if (map_contains(devToolsProtocolEventListener_, eventName)) { + auto eventReceiver = devToolsProtocolEventListener_.at(eventName).first; + auto token = devToolsProtocolEventListener_.at(eventName).second; + eventReceiver->remove_DevToolsProtocolEventReceived(token); + devToolsProtocolEventListener_.erase(eventName); + } + } + // flutter_view void InAppWebView::setSurfaceSize(size_t width, size_t height, float scale_factor) { diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h index 95dfa6a4..6f92eea9 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview.h @@ -95,7 +95,7 @@ namespace flutter_inappwebview_plugin wil::com_ptr webViewCompositionController; wil::com_ptr webView; std::unique_ptr channelDelegate; - const std::shared_ptr settings; + std::shared_ptr settings; InAppBrowser* inAppBrowser = nullptr; std::unique_ptr userContentController; @@ -164,6 +164,12 @@ namespace flutter_inappwebview_plugin void removeUserScriptsByGroupName(const std::string& groupName) const; void removeAllUserScripts() const; void takeScreenshot(const std::optional> screenshotConfiguration, const std::function)> completionHandler) const; + void setSettings(const std::shared_ptr newSettings, const flutter::EncodableMap& newSettingsMap); + flutter::EncodableValue getSettings() const; + void openDevTools() const; + void callDevToolsProtocolMethod(const std::string& methodName, const std::optional& parametersAsJson, const std::function&)> completionHandler) const; + void addDevToolsProtocolEventListener(const std::string& eventName); + void removeDevToolsProtocolEventListener(const std::string& eventName); std::string pageFrameId() const { @@ -185,6 +191,7 @@ namespace flutter_inappwebview_plugin std::shared_ptr lastNavigationAction_; bool isLoading_ = false; std::string pageFrameId_; + std::map, EventRegistrationToken>> devToolsProtocolEventListener_ = {}; void registerEventHandlers(); void registerSurfaceEventHandlers(); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.cpp b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.cpp index df3d538e..cf63c982 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.cpp @@ -2,8 +2,12 @@ #include "../utils/log.h" #include "in_app_webview_settings.h" +#include + namespace flutter_inappwebview_plugin { + using namespace Microsoft::WRL; + InAppWebViewSettings::InAppWebViewSettings() {}; InAppWebViewSettings::InAppWebViewSettings(const flutter::EncodableMap& encodableMap) @@ -16,6 +20,56 @@ namespace flutter_inappwebview_plugin resourceCustomSchemes = get_fl_map_value(encodableMap, "resourceCustomSchemes", resourceCustomSchemes); transparentBackground = get_fl_map_value(encodableMap, "transparentBackground", transparentBackground); supportZoom = get_fl_map_value(encodableMap, "supportZoom", supportZoom); + isInspectable = get_fl_map_value(encodableMap, "isInspectable", isInspectable); + disableContextMenu = get_fl_map_value(encodableMap, "disableContextMenu", disableContextMenu); + } + + flutter::EncodableMap InAppWebViewSettings::toEncodableMap() const + { + return flutter::EncodableMap{ + {"useShouldOverrideUrlLoading", useShouldOverrideUrlLoading}, + {"useOnLoadResource", useOnLoadResource}, + {"useOnDownloadStart", useOnDownloadStart}, + {"userAgent", userAgent}, + {"javaScriptEnabled", javaScriptEnabled}, + {"resourceCustomSchemes", make_fl_value(resourceCustomSchemes)}, + {"transparentBackground", transparentBackground}, + {"supportZoom", supportZoom}, + {"isInspectable", isInspectable}, + {"disableContextMenu", disableContextMenu}, + }; + } + + flutter::EncodableMap InAppWebViewSettings::getRealSettings(ICoreWebView2Settings* settings) const + { + auto settingsMap = toEncodableMap(); + if (settings) { + BOOL realJavaScriptEnabled; + if (SUCCEEDED(settings->get_IsScriptEnabled(&realJavaScriptEnabled))) { + settingsMap["javaScriptEnabled"] = (bool)realJavaScriptEnabled; + } + BOOL realSupportZoom; + if (SUCCEEDED(settings->get_IsZoomControlEnabled(&realSupportZoom))) { + settingsMap["supportZoom"] = (bool)realSupportZoom; + } + BOOL realIsInspectable; + if (SUCCEEDED(settings->get_AreDevToolsEnabled(&realIsInspectable))) { + settingsMap["isInspectable"] = (bool)realIsInspectable; + } + BOOL areDefaultContextMenusEnabled; + if (SUCCEEDED(settings->get_AreDefaultContextMenusEnabled(&areDefaultContextMenusEnabled))) { + settingsMap["disableContextMenu"] = !(bool)areDefaultContextMenusEnabled; + } + + wil::com_ptr settings2; + if (SUCCEEDED(settings->QueryInterface(IID_PPV_ARGS(&settings2)))) { + wil::unique_cotaskmem_string realUserAgent; + if (SUCCEEDED(settings2->get_UserAgent(&realUserAgent))) { + settingsMap["userAgent"] = wide_to_utf8(realUserAgent.get()); + } + } + } + return settingsMap; } InAppWebViewSettings::~InAppWebViewSettings() diff --git a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.h b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.h index ad74f450..417951df 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/in_app_webview_settings.h @@ -3,6 +3,7 @@ #include #include +#include namespace flutter_inappwebview_plugin { @@ -17,10 +18,15 @@ namespace flutter_inappwebview_plugin std::vector resourceCustomSchemes; bool transparentBackground = false; bool supportZoom = true; + bool isInspectable = true; + bool disableContextMenu = false; InAppWebViewSettings(); InAppWebViewSettings(const flutter::EncodableMap& encodableMap); ~InAppWebViewSettings(); + + flutter::EncodableMap toEncodableMap() const; + flutter::EncodableMap getRealSettings(ICoreWebView2Settings* settings) const; }; } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_WEBVIEW_SETTINGS_H_ \ No newline at end of file diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp index 7f4d4ab3..a3ffc806 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.cpp @@ -169,6 +169,55 @@ namespace flutter_inappwebview_plugin result_->Success(make_fl_value(data)); }); } + else if (string_equals(methodName, "setSettings")) { + if (webView->inAppBrowser) { + auto settingsMap = get_fl_map_value(arguments, "settings"); + auto settings = std::make_unique(settingsMap); + webView->inAppBrowser->setSettings(std::move(settings), settingsMap); + } + else { + auto settingsMap = get_fl_map_value(arguments, "settings"); + auto settings = std::make_unique(settingsMap); + webView->setSettings(std::move(settings), settingsMap); + } + result->Success(true); + } + else if (string_equals(methodName, "getSettings")) { + if (webView->inAppBrowser) { + result->Success(webView->inAppBrowser->getSettings()); + } + else { + result->Success(webView->getSettings()); + } + } + else if (string_equals(methodName, "openDevTools")) { + webView->openDevTools(); + result->Success(true); + } + else if (string_equals(methodName, "callDevToolsProtocolMethod")) { + auto result_ = std::shared_ptr>(std::move(result)); + auto cdpMethodName = get_fl_map_value(arguments, "methodName"); + auto parametersAsJson = get_optional_fl_map_value(arguments, "parametersAsJson"); + webView->callDevToolsProtocolMethod(cdpMethodName, parametersAsJson, [result_ = std::move(result_)](const HRESULT& errorCode, const std::optional& data) + { + if (SUCCEEDED(errorCode)) { + result_->Success(make_fl_value(data)); + } + else { + result_->Error(std::to_string(errorCode), getHRMessage(errorCode)); + } + }); + } + else if (string_equals(methodName, "addDevToolsProtocolEventListener")) { + auto eventName = get_fl_map_value(arguments, "eventName"); + webView->addDevToolsProtocolEventListener(eventName); + result->Success(true); + } + else if (string_equals(methodName, "removeDevToolsProtocolEventListener")) { + auto eventName = get_fl_map_value(arguments, "eventName"); + webView->removeDevToolsProtocolEventListener(eventName); + result->Success(true); + } // for inAppBrowser else if (webView->inAppBrowser && string_equals(methodName, "show")) { webView->inAppBrowser->show(); @@ -302,6 +351,20 @@ namespace flutter_inappwebview_plugin channel->InvokeMethod("onConsoleMessage", std::move(arguments)); } + + void WebViewChannelDelegate::onDevToolsProtocolEventReceived(const std::string& eventName, const std::string& data) const + { + if (!channel) { + return; + } + + auto arguments = std::make_unique(flutter::EncodableMap{ + {"eventName", eventName}, + {"data", data} + }); + channel->InvokeMethod("onDevToolsProtocolEventReceived", std::move(arguments)); + } + WebViewChannelDelegate::~WebViewChannelDelegate() { debugLog("dealloc WebViewChannelDelegate"); diff --git a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h index 7774b375..b27cbb9e 100644 --- a/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h +++ b/flutter_inappwebview_windows/windows/in_app_webview/webview_channel_delegate.h @@ -51,6 +51,7 @@ namespace flutter_inappwebview_plugin void onUpdateVisitedHistory(const std::optional& url, const std::optional& isReload) const; void onCallJsHandler(const std::string& handlerName, const std::string& args, std::unique_ptr callback) const; void onConsoleMessage(const std::string& message, const int64_t& messageLevel) const; + void onDevToolsProtocolEventReceived(const std::string& eventName, const std::string& data) const; }; } diff --git a/flutter_inappwebview_windows/windows/types/rect.h b/flutter_inappwebview_windows/windows/types/rect.h index 2472d084..57c94eeb 100644 --- a/flutter_inappwebview_windows/windows/types/rect.h +++ b/flutter_inappwebview_windows/windows/types/rect.h @@ -20,6 +20,15 @@ namespace flutter_inappwebview_plugin Rect(const flutter::EncodableMap& map); ~Rect() = default; + bool Rect::operator==(const Rect& other) + { + return x == other.x && y == other.y && width == other.width && height == other.height; + } + bool Rect::operator!=(const Rect& other) + { + return !(*this == other); + } + flutter::EncodableMap toEncodableMap() const; }; } diff --git a/flutter_inappwebview_windows/windows/types/size_2d.h b/flutter_inappwebview_windows/windows/types/size_2d.h index 01162a95..e2cc28f6 100644 --- a/flutter_inappwebview_windows/windows/types/size_2d.h +++ b/flutter_inappwebview_windows/windows/types/size_2d.h @@ -18,6 +18,15 @@ namespace flutter_inappwebview_plugin Size2D(const flutter::EncodableMap& map); ~Size2D() = default; + bool Size2D::operator==(const Size2D& other) + { + return width == other.width && height == other.height; + } + bool Size2D::operator!=(const Size2D& other) + { + return !(*this == other); + } + flutter::EncodableMap toEncodableMap() const; }; } diff --git a/flutter_inappwebview_windows/windows/utils/util.h b/flutter_inappwebview_windows/windows/utils/util.h index 982e404a..bbac292d 100644 --- a/flutter_inappwebview_windows/windows/utils/util.h +++ b/flutter_inappwebview_windows/windows/utils/util.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace flutter_inappwebview_plugin { @@ -26,6 +27,12 @@ namespace flutter_inappwebview_plugin static_assert(always_false_v, "non-exhaustive visitor!"); }, var); } + + static inline float get_current_scale_factor(HWND hwnd) + { + auto dpi = GetDpiForWindow(hwnd); + return dpi > 0 ? dpi / 96.0f : 1.0f; + } } #endif //FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_H_ \ No newline at end of file From 7b8ebf49d925488d7e85082a19ff5caf683711a9 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Jan 2024 14:19:54 +0100 Subject: [PATCH 21/21] added InAppWebViewControllerKeepAliveProps constructor parameters default value --- .../in_app_webview/in_app_webview_keep_alive.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_keep_alive.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_keep_alive.dart index 81dc56f2..cb7328de 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_keep_alive.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/in_app_webview_keep_alive.dart @@ -31,12 +31,12 @@ class InAppWebViewControllerKeepAliveProps { Map devToolsProtocolEventListenerMap; InAppWebViewControllerKeepAliveProps( - {required this.javaScriptHandlersMap, - required this.userScripts, - required this.webMessageListenerObjNames, - required this.injectedScriptsFromURL, - required this.webMessageChannels, - required this.webMessageListeners, - required this.devToolsProtocolEventListenerMap + {this.javaScriptHandlersMap = const {}, + this.userScripts = const {}, + this.webMessageListenerObjNames = const {}, + this.injectedScriptsFromURL = const {}, + this.webMessageChannels = const {}, + this.webMessageListeners = const {}, + this.devToolsProtocolEventListenerMap = const {} }); }