windows: updated webview and browser creation params, added support for openWithSystemBrowser

This commit is contained in:
unknown 2024-01-19 20:16:53 +01:00
parent a4c044a0f1
commit b27036e59b
12 changed files with 123 additions and 42 deletions

View File

@ -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<UserScript>? 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<void> openFile(
{required String assetFilePath,
@ -254,6 +279,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface
///- Android native WebView
///- iOS
///- MacOS
///- Windows
///{@endtemplate}
Future<void> openData(
{required String data,
@ -276,6 +302,7 @@ abstract class PlatformInAppBrowser extends PlatformInterface
///- Android native WebView
///- iOS
///- MacOS
///- Windows
///{@endtemplate}
Future<void> openWithSystemBrowser({required WebUri url}) {
throw UnimplementedError(

View File

@ -1124,6 +1124,7 @@ class PlatformWebViewCreationParams<T> {
///- Android native WebView
///- iOS
///- MacOS
///- Windows
///{@endtemplate}
final UnmodifiableListView<UserScript>? initialUserScripts;

View File

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

View File

@ -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<InAppBrowserChannelDelegate>(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<ICoreWebView2Environment> webViewEnv, wil::com_ptr<ICoreWebView2Controller> webViewController, wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController) -> void
[this, params, webViewParams](wil::com_ptr<ICoreWebView2Environment> webViewEnv, wil::com_ptr<ICoreWebView2Controller> webViewController, wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController) -> void
{
if (webViewEnv && webViewController) {
webView = std::make_unique<InAppWebView>(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 {

View File

@ -19,8 +19,11 @@ namespace flutter_inappwebview_plugin
{
const std::string id;
const std::optional<std::shared_ptr<URLRequest>> urlRequest;
const std::optional<std::string> assetFilePath;
const std::optional<std::string> data;
const std::shared_ptr<InAppBrowserSettings> initialSettings;
const std::shared_ptr<InAppWebViewSettings> initialWebViewSettings;
const std::optional<std::vector<std::shared_ptr<UserScript>>> initialUserScripts;
};
class InAppBrowser {
@ -35,7 +38,6 @@ namespace flutter_inappwebview_plugin
const FlutterInappwebviewWindowsPlugin* plugin;
const std::string id;
const std::optional<std::shared_ptr<URLRequest>> initialUrlRequest;
std::unique_ptr<InAppWebView> webView;
std::unique_ptr<InAppBrowserChannelDelegate> channelDelegate;
const std::shared_ptr<InAppBrowserSettings> settings;

View File

@ -1,10 +1,14 @@
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>
#include <windows.h>
#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<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
{
if (method_call.method_name().compare("open") == 0) {
auto* arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
createInAppWebView(arguments);
auto* arguments = std::get_if<flutter::EncodableMap>(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<std::string>(*arguments, "url");
int status = static_cast<int>(reinterpret_cast<INT_PTR>(
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<std::string>(*arguments, "id");
auto urlRequestMap = get_optional_fl_map_value<flutter::EncodableMap>(*arguments, "urlRequest");
auto assetFilePath = get_optional_fl_map_value<std::string>(*arguments, "assetFilePath");
auto data = get_optional_fl_map_value<std::string>(*arguments, "data");
auto initialUserScriptList = get_optional_fl_map_value<flutter::EncodableList>(*arguments, "initialUserScripts");
std::optional<std::shared_ptr<URLRequest>> urlRequest = urlRequestMap.has_value() ? std::make_shared<URLRequest>(urlRequestMap.value()) : std::optional<std::shared_ptr<URLRequest>>{};
auto settingsMap = get_fl_map_value<flutter::EncodableMap>(*arguments, "settings");
auto initialSettings = std::make_unique<InAppBrowserSettings>(settingsMap);
auto initialWebViewSettings = std::make_unique<InAppWebViewSettings>(settingsMap);
std::optional<std::vector<std::shared_ptr<UserScript>>> initialUserScripts = initialUserScriptList.has_value() ?
functional_map(initialUserScriptList.value(), [](const flutter::EncodableValue& map) { return std::make_shared<UserScript>(std::get<flutter::EncodableMap>(map)); }) :
std::optional<std::vector<std::shared_ptr<UserScript>>>{};
InAppBrowserCreationParams params = {
id,
urlRequest,
assetFilePath,
data,
std::move(initialSettings),
std::move(initialWebViewSettings)
std::move(initialWebViewSettings),
initialUserScripts
};
auto inAppBrowser = std::make_unique<InAppBrowser>(plugin, params);

View File

@ -27,7 +27,7 @@ namespace flutter_inappwebview_plugin
const flutter::MethodCall<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
void createInAppWebView(const flutter::EncodableMap* arguments);
void createInAppBrowser(const flutter::EncodableMap* arguments);
};
}
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_IN_APP_BROWSER_MANAGER_H_

View File

@ -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<ICoreWebView2Environment> webViewEnv,
@ -118,7 +118,7 @@ namespace flutter_inappwebview_plugin
std::make_unique<WebViewChannelDelegate>(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();
}

View File

@ -72,6 +72,7 @@ namespace flutter_inappwebview_plugin
struct InAppWebViewCreationParams {
const std::variant<std::string, int64_t> id;
const std::shared_ptr<InAppWebViewSettings> initialSettings;
const std::optional<std::vector<std::shared_ptr<UserScript>>> initialUserScripts;
};
class InAppWebView
@ -129,7 +130,7 @@ namespace flutter_inappwebview_plugin
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositor> compositor);
void initChannel(const std::optional<std::variant<std::string, int64_t>> viewId, const std::optional<std::string> channelName);
void prepare();
void prepare(const InAppWebViewCreationParams& params);
std::optional<std::string> getUrl() const;
std::optional<std::string> getTitle() const;
void loadUrl(const std::shared_ptr<URLRequest> urlRequest) const;

View File

@ -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<flutter::MethodResult<flutter::EncodableValue>> result)
{
auto* arguments = std::get_if<flutter::EncodableMap>(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<int64_t>(*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<flutter::MethodResult<flutter::EncodableValue>> result)
void InAppWebViewManager::createInAppBrowser(const flutter::EncodableMap* arguments, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
{
auto result_ = std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>(std::move(result));
@ -78,6 +82,7 @@ namespace flutter_inappwebview_plugin
auto urlRequestMap = get_optional_fl_map_value<flutter::EncodableMap>(*arguments, "initialUrlRequest");
auto initialFile = get_optional_fl_map_value<std::string>(*arguments, "initialFile");
auto initialDataMap = get_optional_fl_map_value<flutter::EncodableMap>(*arguments, "initialData");
auto initialUserScriptList = get_optional_fl_map_value<flutter::EncodableList>(*arguments, "initialUserScripts");
auto 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<InAppWebViewSettings>(settingsMap);
std::optional<std::vector<std::shared_ptr<UserScript>>> initialUserScripts = initialUserScriptList.has_value() ?
functional_map(initialUserScriptList.value(), [](const flutter::EncodableValue& map) { return std::make_shared<UserScript>(std::get<flutter::EncodableMap>(map)); }) :
std::optional<std::vector<std::shared_ptr<UserScript>>>{};
InAppWebViewCreationParams params = {
"",
std::move(initialSettings),
initialUserScripts
};
auto inAppWebView = std::make_unique<InAppWebView>(plugin, params, hwnd,

View File

@ -44,7 +44,7 @@ namespace flutter_inappwebview_plugin
const flutter::MethodCall<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
void createInAppWebView(const flutter::EncodableMap* arguments, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
void createInAppBrowser(const flutter::EncodableMap* arguments, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
private:
std::unique_ptr<rx::RoHelper> rohelper_;
winrt::com_ptr<ABI::Windows::System::IDispatcherQueueController>

View File

@ -81,32 +81,36 @@ namespace flutter_inappwebview_plugin
}
template<typename T>
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<T>(map.at(make_fl_value(string)));
return std::get<T>(map.at(make_fl_value(key)));
}
template<typename T, typename std::enable_if<((!is_mappish<T>::value && !is_vector<T>::value) ||
std::is_same<T, flutter::EncodableMap>::value || std::is_same<T, flutter::EncodableList>::value), int>::type* = nullptr>
static inline std::optional<T> get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string)
static inline std::optional<T> get_optional_fl_map_value(const flutter::EncodableMap& map, const char* key)
{
return make_pointer_optional<T>(std::get_if<T>(&map.at(make_fl_value(string))));
auto fl_key = make_fl_value(key);
if (map_contains<flutter::EncodableValue, flutter::EncodableValue>(map, fl_key)) {
return make_pointer_optional<T>(std::get_if<T>(&map.at(fl_key)));
}
return std::nullopt;
}
template<typename T>
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<T>(map, string);
auto optional = get_optional_fl_map_value<T>(map, key);
return !optional.has_value() ? defaultValue : optional.value();
}
template<typename T, typename std::enable_if<(is_mappish<T>::value && !std::is_same<T, flutter::EncodableMap>::value)>::type* = nullptr>
static inline std::optional<T> get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string)
static inline std::optional<T> 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<flutter::EncodableMap>(&map.at(make_fl_value(string)));
auto flMap = std::get_if<flutter::EncodableMap>(&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<typename K, typename T>
static inline std::map<K, T> get_fl_map_value(const flutter::EncodableMap& map, const char* string, const std::map<K, T>& defaultValue)
static inline std::map<K, T> get_fl_map_value(const flutter::EncodableMap& map, const char* key, const std::map<K, T>& defaultValue)
{
auto optional = get_optional_fl_map_value<std::map<K, T>>(map, string);
auto optional = get_optional_fl_map_value<std::map<K, T>>(map, key);
return !optional.has_value() ? defaultValue : optional.value();
}
template<typename T, typename std::enable_if<(is_vector<T>::value && !std::is_same<T, flutter::EncodableList>::value), bool>::type* = nullptr>
static inline std::optional<T> get_optional_fl_map_value(const flutter::EncodableMap& map, const char* string)
static inline std::optional<T> get_optional_fl_map_value(const flutter::EncodableMap& map, const char* key)
{
using V = typename T::value_type;
auto flList = std::get_if<flutter::EncodableList>(&map.at(make_fl_value(string)));
auto flList = std::get_if<flutter::EncodableList>(&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<typename T>
static inline std::vector<T> get_fl_map_value(const flutter::EncodableMap& map, const char* string, const std::vector<T>& defaultValue)
static inline std::vector<T> get_fl_map_value(const flutter::EncodableMap& map, const char* key, const std::vector<T>& defaultValue)
{
auto optional = get_optional_fl_map_value<std::vector<T>>(map, string);
auto optional = get_optional_fl_map_value<std::vector<T>>(map, key);
return !optional.has_value() ? defaultValue : optional.value();
}
}