windows: fixed javascript evaluation, added callAsyncJavaScript implementation, call DestroyWindow(parentWindow) on InAppWebView dealloc
This commit is contained in:
parent
e4bfd68313
commit
ab869e9703
|
@ -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<dynamic> 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<CallAsyncJavaScriptResult?> callAsyncJavaScript(
|
||||
{required String functionBody,
|
||||
|
|
|
@ -2428,12 +2428,13 @@ class WindowsInAppWebViewController extends PlatformInAppWebViewController
|
|||
ContentWorld? contentWorld}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
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"]);
|
||||
}
|
||||
|
|
|
@ -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<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
|
||||
// [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<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
|
||||
// [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<ICoreWebView2Settings> 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());
|
||||
|
||||
// // <NavigationEvents>
|
||||
// // Step 4 - Navigation events
|
||||
// // register an ICoreWebView2NavigationStartingEventHandler to cancel any non-https navigation
|
||||
// EventRegistrationToken token;
|
||||
// inAppBrowser->webview->add_NavigationStarting(Callback<ICoreWebView2NavigationStartingEventHandler>(
|
||||
// [](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);
|
||||
// // </NavigationEvents>
|
||||
|
||||
// // <Scripting>
|
||||
// // 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<ICoreWebView2ExecuteScriptCompletedHandler>(
|
||||
// [](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT {
|
||||
// LPCWSTR URL = resultObjectAsJson;
|
||||
// OutputDebugStringW(URL);
|
||||
// //doSomethingWithURL(URL);
|
||||
// return S_OK;
|
||||
// }).Get());
|
||||
// // </Scripting>
|
||||
|
||||
// // <CommunicationHostWeb>
|
||||
// // 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<ICoreWebView2WebMessageReceivedEventHandler>(
|
||||
// [](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);
|
||||
// // </CommunicationHostWeb>
|
||||
|
||||
// return S_OK;
|
||||
// }).Get());
|
||||
// return S_OK;
|
||||
// }).Get());
|
||||
}
|
||||
|
||||
void InAppBrowser::close() const
|
||||
|
|
|
@ -785,12 +785,94 @@ namespace flutter_inappwebview_plugin
|
|||
auto hr = webView->CallDevToolsProtocolMethod(L"Runtime.evaluate", utf8_to_wide(parameters.dump()).c_str(), Callback<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
|
||||
[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<std::string>() : 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> contentWorld, const std::function<void(std::string)> completionHandler) const
|
||||
{
|
||||
if (!webView || !userContentController) {
|
||||
if (completionHandler) {
|
||||
completionHandler("null");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
userContentController->createContentWorld(contentWorld,
|
||||
[=](const int& contextId)
|
||||
{
|
||||
std::vector<std::string> functionArgumentNamesList;
|
||||
std::vector<std::string> 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<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
|
||||
[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<std::string>() : 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
#include <winrt/base.h>
|
||||
|
||||
#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<std::string, int64_t> id;
|
||||
const std::shared_ptr<InAppWebViewSettings> initialSettings;
|
||||
|
@ -150,6 +155,7 @@ namespace flutter_inappwebview_plugin
|
|||
}
|
||||
void stopLoading() const;
|
||||
void evaluateJavascript(const std::string& source, const std::shared_ptr<ContentWorld> contentWorld, const std::function<void(std::string)> completionHandler) const;
|
||||
void callAsyncJavaScript(const std::string& functionBody, const std::string& argumentsAsJson, const std::shared_ptr<ContentWorld> contentWorld, const std::function<void(std::string)> completionHandler) const;
|
||||
void getCopyBackForwardList(const std::function<void(std::unique_ptr<WebHistory>)> completionHandler) const;
|
||||
void addUserScript(const std::shared_ptr<UserScript> userScript) const;
|
||||
void removeUserScript(const int64_t index, const std::shared_ptr<UserScript> userScript) const;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -120,6 +120,18 @@ namespace flutter_inappwebview_plugin
|
|||
result_->Success(value);
|
||||
});
|
||||
}
|
||||
else if (string_equals(methodName, "callAsyncJavaScript")) {
|
||||
auto result_ = std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>(std::move(result));
|
||||
|
||||
auto functionBody = get_fl_map_value<std::string>(arguments, "functionBody");
|
||||
auto argumentsAsJson = get_fl_map_value<std::string>(arguments, "arguments");
|
||||
auto contentWorldMap = get_optional_fl_map_value<flutter::EncodableMap>(arguments, "contentWorld");
|
||||
std::shared_ptr<ContentWorld> contentWorld = contentWorldMap.has_value() ? std::make_shared<ContentWorld>(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<flutter::MethodResult<flutter::EncodableValue>>(std::move(result));
|
||||
webView->getCopyBackForwardList([result_ = std::move(result_)](const std::unique_ptr<WebHistory> value)
|
||||
|
|
|
@ -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_
|
|
@ -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)
|
||||
|
|
|
@ -120,6 +120,12 @@ namespace flutter_inappwebview_plugin
|
|||
[delim](auto& a, auto& b) { return a + delim + b; });
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline std::basic_string<T> join(const std::vector<std::basic_string<T>>& vec, const char* delim)
|
||||
{
|
||||
return join(vec, std::basic_string<T>{ delim });
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline std::vector<std::basic_string<T>> split(const std::basic_string<T>& s, std::basic_string<T> delimiter)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue