windows: added content world support for user scripts and javascript evaluation

This commit is contained in:
unknown 2024-01-20 22:13:24 +01:00
parent b27036e59b
commit ed6283d3b3
19 changed files with 613 additions and 131 deletions

View File

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

View File

@ -190,7 +190,7 @@ namespace flutter_inappwebview_plugin
void InAppBrowser::didChangeTitle(const std::optional<std::string>& 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());
}
}

View File

@ -32,7 +32,7 @@ namespace flutter_inappwebview_plugin
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(),
ShellExecute(nullptr, TEXT("open"), utf8_to_wide(url).c_str(),
nullptr, nullptr, SW_SHOWNORMAL)));
// Anything >32 indicates success.

View File

@ -133,11 +133,40 @@ namespace flutter_inappwebview_plugin
wil::com_ptr<ICoreWebView2Settings2> 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<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
[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<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
[this](HRESULT errorCode, LPCWSTR returnObjectAsJson)
{
failedLog(errorCode);
return S_OK;
}
).Get()));
failedLog(webView->CallDevToolsProtocolMethod(L"Page.getFrameTree", L"{}", Callback<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
[this](HRESULT errorCode, LPCWSTR returnObjectAsJson)
{
if (succeededOrLog(errorCode)) {
auto treeJson = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson));
pageFrameId_ = treeJson["frameTree"]["frame"]["id"].get<std::string>();
}
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> 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<std::string>{});
channelDelegate->onTitleChanged(title.is_valid() ? wide_to_utf8(title.get()) : std::optional<std::string>{});
}
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<std::string>();
@ -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<ICoreWebView2DevToolsProtocolEventReceiver> consoleMessageReceiver;
if (succeededOrLog(webView->GetDevToolsProtocolEventReceiver(L"Runtime.consoleAPICalled", &consoleMessageReceiver))) {
failedLog(consoleMessageReceiver->add_DevToolsProtocolEventReceived(
Callback<ICoreWebView2DevToolsProtocolEventReceivedEventHandler>(
[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<std::string>();
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<std::vector<nlohmann::json>>();
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<ICoreWebView2Environment2> webViewEnv2;
wil::com_ptr<ICoreWebView2_2> webView2;
if (SUCCEEDED(webViewEnv->QueryInterface(IID_PPV_ARGS(&webViewEnv2))) && SUCCEEDED(webView->QueryInterface(IID_PPV_ARGS(&webView2)))) {
wil::com_ptr<ICoreWebView2WebResourceRequest> 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<IStream> 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<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
if (failedAndLog(webView->CallDevToolsProtocolMethod(L"Page.navigateToHistoryEntry", utf8_to_wide("{\"entryId\": " + std::to_string(entryId.value()) + "}").c_str(), Callback<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
[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<int64_t>() : 0;
std::vector<nlohmann::json> entries = historyJson.at("entries").is_array() ? historyJson.at("entries").get<std::vector<nlohmann::json>>() : std::vector<nlohmann::json>{};
@ -686,22 +761,44 @@ namespace flutter_inappwebview_plugin
).Get()));
}
void InAppWebView::evaluateJavascript(const std::string& source, const std::function<void(std::string)> completionHanlder) const
void InAppWebView::evaluateJavascript(const std::string& source, const std::shared_ptr<ContentWorld> contentWorld, const std::function<void(std::string)> completionHandler) const
{
failedLog(webView->ExecuteScript(ansi_to_wide(source).c_str(),
Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
[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<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
[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> 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<ICoreWebView2ExecuteScriptCompletedHandler>(
webView->ExecuteScript(utf8_to_wide(workaroundScrollJS).c_str(), Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
[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;
}

View File

@ -9,6 +9,7 @@
#include <winrt/base.h>
#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<ICoreWebView2CompositionController> webViewCompositionController;
wil::com_ptr<ICoreWebView2> webView;
std::unique_ptr<WebViewChannelDelegate> channelDelegate;
std::map<UINT64, std::shared_ptr<NavigationAction>> navigationActions = {};
const std::shared_ptr<InAppWebViewSettings> settings;
InAppBrowser* inAppBrowser = nullptr;
std::unique_ptr<UserContentController> userContentController;
@ -148,13 +148,18 @@ namespace flutter_inappwebview_plugin
return isLoading_;
}
void stopLoading() const;
void evaluateJavascript(const std::string& source, const std::function<void(std::string)> completionHanlder) const;
void evaluateJavascript(const std::string& source, 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;
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<UINT64, std::shared_ptr<NavigationAction>> navigationActions_ = {};
std::shared_ptr<NavigationAction> 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_

View File

@ -1,3 +1,4 @@
#include <nlohmann/json.hpp>
#include <wil/wrl.h>
#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<ICoreWebView2DevToolsProtocolEventReceiver> executionContextCreated;
if (succeededOrLog(webView_->webView->GetDevToolsProtocolEventReceiver(L"Runtime.executionContextCreated", &executionContextCreated))) {
auto hr = executionContextCreated->add_DevToolsProtocolEventReceived(
Callback<ICoreWebView2DevToolsProtocolEventReceivedEventHandler>(
[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<int>();
auto name = context["name"].get<std::string>();
nlohmann::json auxData = context["auxData"];
auto isDefault = auxData["isDefault"].get<bool>();
auto frameId = auxData["frameId"].get<std::string>();
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<ContentWorld>(name));
}
}
}
return S_OK;
})
.Get(), nullptr);
failedLog(hr);
}
/*
wil::com_ptr<ICoreWebView2DevToolsProtocolEventReceiver> executionContextDestroyed;
if (succeededOrLog(webView_->webView->GetDevToolsProtocolEventReceiver(L"Runtime.executionContextDestroyed ", &executionContextDestroyed))) {
failedLog(executionContextDestroyed->add_DevToolsProtocolEventReceived(
Callback<ICoreWebView2DevToolsProtocolEventReceivedEventHandler>(
[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<std::shared_ptr<UserScript>> 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<ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler>(
[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<ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler>(
[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> pluginScript, const std::shared_ptr<ContentWorld> 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<std::shared_ptr<PluginScript>> UserContentController::getPluginScriptsRequiredInAllContentWorlds() const
{
std::string code;
std::vector<std::shared_ptr<PluginScript>> pluginScripts = pluginScripts_.at(injectionTime);
for (auto& pluginScript : pluginScripts) {
code += ";" + pluginScript->source;
std::vector<std::shared_ptr<PluginScript>> res;
std::vector<std::shared_ptr<PluginScript>> pluginScriptsAtStart = pluginScripts_.at(UserScriptInjectionTime::atDocumentStart);
std::vector<std::shared_ptr<PluginScript>> 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> contentWorld, const std::function<void(int)> completionHandler)
{
std::string code;
std::vector<std::shared_ptr<UserScript>> 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<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
[this, completionHandler, worldName](HRESULT errorCode, LPCWSTR returnObjectAsJson)
{
if (succeededOrLog(errorCode) && completionHandler) {
auto id = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson))["executionContextId"].get<int>();
addPluginScriptsIfRequired(std::make_shared<ContentWorld>(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> userScript, const std::function<void(std::string)> 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<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
[userScript, completionHandler](HRESULT errorCode, LPCWSTR returnObjectAsJson)
{
if (succeededOrLog(errorCode)) {
nlohmann::json json = nlohmann::json::parse(wide_to_utf8(returnObjectAsJson));
userScript->id = json["identifier"].get<std::string>();
}
if (completionHandler) {
completionHandler(userScript->id);
}
return S_OK;
}
).Get());
if (failedAndLog(hr) && completionHandler) {
completionHandler(userScript->id);
}
}
void UserContentController::removeScriptFromWebView(std::shared_ptr<UserScript> userScript, const std::function<void()> 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<ICoreWebView2CallDevToolsProtocolMethodCompletedHandler>(
[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> contentWorld)
{
if (contentWorld && !ContentWorld::isPage(contentWorld)) {
std::vector<std::shared_ptr<PluginScript>> 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;
}
}

View File

@ -1,11 +1,13 @@
#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_USER_CONTENT_CONTROLLER_H_
#define FLUTTER_INAPPWEBVIEW_PLUGIN_USER_CONTENT_CONTROLLER_H_
#include <functional>
#include <map>
#include <vector>
#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> userScript) const;
bool containsUserOnlyScriptByGroupName(const std::string& groupName) const;
void removeUserOnlyScriptsByGroupName(const std::string& groupName);
std::vector<std::shared_ptr<PluginScript>> getPluginScriptsAt(const UserScriptInjectionTime& injectionTime) const;
void addPluginScript(std::shared_ptr<PluginScript> pluginScript);
void addPluginScripts(std::vector<std::shared_ptr<PluginScript>> pluginScripts);
void removePluginScript(std::shared_ptr<PluginScript> pluginScript);
void removeAllPluginScripts();
bool containsPluginScript(std::shared_ptr<PluginScript> pluginScript) const;
bool containsPluginScript(std::shared_ptr<PluginScript> pluginScript, const std::shared_ptr<ContentWorld> 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<std::shared_ptr<PluginScript>> getPluginScriptsRequiredInAllContentWorlds() const;
void registerEventHandlers();
void createContentWorld(const std::shared_ptr<ContentWorld> contentWorld, const std::function<void(int)> completionHandler);
private:
InAppWebView* webView_;
// used to track Content World names -> Execution Context ID
std::map<std::string, int> contentWorlds_;
// used only to track plugin script to inject inside new Content Worlds
std::map<std::string, std::vector<std::shared_ptr<PluginScript>>> pluginScriptsInContentWorlds_;
std::map<UserScriptInjectionTime, std::vector<std::shared_ptr<PluginScript>>> pluginScripts_ = {
{UserScriptInjectionTime::atDocumentStart, {}},
{UserScriptInjectionTime::atDocumentEnd, {}}
@ -57,6 +68,11 @@ namespace flutter_inappwebview_plugin
{UserScriptInjectionTime::atDocumentStart, {}},
{UserScriptInjectionTime::atDocumentEnd, {}}
};
void addScriptToWebView(std::shared_ptr<UserScript> userScript, const std::function<void(std::string)> completionHandler) const;
void removeScriptFromWebView(std::shared_ptr<UserScript> userScript, const std::function<void()> completionHandler) const;
void addPluginScriptsIfRequired(const std::shared_ptr<ContentWorld> contentWorld);
};
}
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_USER_CONTENT_CONTROLLER_H_

View File

@ -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<flutter::MethodResult<flutter::EncodableValue>>(std::move(result));
auto source = get_fl_map_value<std::string>(arguments, "source");
webView->evaluateJavascript(source, [result_ = std::move(result_)](const std::string& value)
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->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<int64_t>(arguments, "index");
auto index = get_fl_map_value<int>(arguments, "index");
auto userScript = std::make_unique<UserScript>(get_fl_map_value<flutter::EncodableMap>(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::EncodableValue>(flutter::EncodableMap{
{"message", message},
{"messageLevel", messageLevel}
});
channel->InvokeMethod("onConsoleMessage", std::move(arguments));
}
WebViewChannelDelegate::~WebViewChannelDelegate()
{
debugLog("dealloc WebViewChannelDelegate");

View File

@ -50,6 +50,7 @@ namespace flutter_inappwebview_plugin
void onTitleChanged(const std::optional<std::string>& title) const;
void onUpdateVisitedHistory(const std::optional<std::string>& url, const std::optional<bool>& isReload) const;
void onCallJsHandler(const std::string& handlerName, const std::string& args, std::unique_ptr<CallJsHandlerCallback> callback) const;
void onConsoleMessage(const std::string& message, const int64_t& messageLevel) const;
};
}

View File

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

View File

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

View File

@ -0,0 +1,60 @@
#include "content_world.h"
namespace flutter_inappwebview_plugin
{
namespace {
const std::shared_ptr<ContentWorld> ContentWorldPage = std::make_shared<ContentWorld>("page");
const std::shared_ptr<ContentWorld> ContentWorldDefaultClient = std::make_shared<ContentWorld>("defaultClient");
}
ContentWorld::ContentWorld(const std::string& name)
: name(name)
{}
ContentWorld::ContentWorld(const flutter::EncodableMap& map)
: name(get_fl_map_value<std::string>(map, "name"))
{}
bool ContentWorld::isSame(const ContentWorld& contentWorld) const
{
return name == contentWorld.name;
}
bool ContentWorld::isSame(const std::shared_ptr<ContentWorld> contentWorld) const
{
return contentWorld && name == contentWorld->name;
}
const std::shared_ptr<ContentWorld> ContentWorld::page()
{
return ContentWorldPage;
}
const std::shared_ptr<ContentWorld> ContentWorld::defaultClient()
{
return ContentWorldDefaultClient;
}
bool ContentWorld::isPage(const ContentWorld& contentWorld)
{
return contentWorld.isSame(*ContentWorld::page());
}
bool ContentWorld::isPage(const std::shared_ptr<ContentWorld> 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> contentWorld)
{
return ContentWorld::defaultClient()->isSame(contentWorld);
}
ContentWorld::~ContentWorld()
{}
}

View File

@ -0,0 +1,33 @@
#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_CONTENT_WORLD_H_
#define FLUTTER_INAPPWEBVIEW_PLUGIN_CONTENT_WORLD_H_
#include <flutter/standard_method_codec.h>
#include <string>
#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> contentWorld) const;
const static std::shared_ptr<ContentWorld> page();
const static std::shared_ptr<ContentWorld> defaultClient();
static bool isPage(const ContentWorld& contentWorld);
static bool isPage(const std::shared_ptr<ContentWorld> contentWorld);
static bool isDefaultClient(const ContentWorld& contentWorld);
static bool isDefaultClient(const std::shared_ptr<ContentWorld> contentWorld);
};
}
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_CONTENT_WORLD_H_

View File

@ -6,9 +6,25 @@ namespace flutter_inappwebview_plugin
const std::optional<std::string>& groupName,
const std::string& source,
const UserScriptInjectionTime& injectionTime,
const std::vector<std::string>& allowedOriginRules
) : UserScript(groupName, source, injectionTime, allowedOriginRules)
const std::vector<std::string>& allowedOriginRules,
std::shared_ptr<ContentWorld> contentWorld,
const bool& requiredInAllContentWorlds
) : UserScript(groupName, source, injectionTime, allowedOriginRules, std::move(contentWorld)),
requiredInAllContentWorlds_(requiredInAllContentWorlds)
{}
std::shared_ptr<PluginScript> PluginScript::copyAndSet(const std::shared_ptr<ContentWorld> cw) const
{
return std::make_unique<PluginScript>(
this->groupName,
this->source,
this->injectionTime,
this->allowedOriginRules,
cw,
this->requiredInAllContentWorlds_
);
}
PluginScript::~PluginScript() {}
}

View File

@ -13,9 +13,21 @@ namespace flutter_inappwebview_plugin
const std::optional<std::string>& groupName,
const std::string& source,
const UserScriptInjectionTime& injectionTime,
const std::vector<std::string>& allowedOriginRules
const std::vector<std::string>& allowedOriginRules,
std::shared_ptr<ContentWorld> contentWorld,
const bool& requiredInAllContentWorlds
);
~PluginScript();
bool isRequiredInAllContentWorlds() const
{
return requiredInAllContentWorlds_;
}
std::shared_ptr<PluginScript> copyAndSet(const std::shared_ptr<ContentWorld> cw) const;
private:
bool requiredInAllContentWorlds_;
};
}
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_PLUGIN_SCRIPT_H_

View File

@ -7,15 +7,17 @@ namespace flutter_inappwebview_plugin
const std::optional<std::string>& groupName,
const std::string& source,
const UserScriptInjectionTime& injectionTime,
const std::vector<std::string>& allowedOriginRules
) : groupName(groupName), source(source), injectionTime(injectionTime), allowedOriginRules(allowedOriginRules)
const std::vector<std::string>& allowedOriginRules,
std::shared_ptr<ContentWorld> 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<std::string>(map, "groupName")),
source(get_fl_map_value<std::string>(map, "source")),
injectionTime(static_cast<UserScriptInjectionTime>(get_fl_map_value<int>(map, "injectionTime"))),
allowedOriginRules(functional_map(get_fl_map_value<flutter::EncodableList>(map, "allowedOriginRules"), [](const flutter::EncodableValue& m) { return std::get<std::string>(m); }))
allowedOriginRules(functional_map(get_fl_map_value<flutter::EncodableList>(map, "allowedOriginRules"), [](const flutter::EncodableValue& m) { return std::get<std::string>(m); })),
contentWorld(std::make_shared<ContentWorld>(get_fl_map_value<flutter::EncodableMap>(map, "contentWorld")))
{}
UserScript::~UserScript()

View File

@ -7,6 +7,7 @@
#include <vector>
#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<std::string> groupName;
const std::string source;
const UserScriptInjectionTime injectionTime;
const std::vector<std::string> allowedOriginRules;
const std::shared_ptr<ContentWorld> contentWorld;
UserScript(
const std::optional<std::string>& groupName,
const std::string& source,
const UserScriptInjectionTime& injectionTime,
const std::vector<std::string>& allowedOriginRules
const std::vector<std::string>& allowedOriginRules,
std::shared_ptr<ContentWorld> contentWorld
);
UserScript(const flutter::EncodableMap& map);
~UserScript();

View File

@ -7,82 +7,96 @@
#include <type_traits>
#include "strconv.h"
#include "string.h"
namespace flutter_inappwebview_plugin
{
template <typename T>
static inline void debugLog(const std::basic_string<T>& msg, const bool& isError = false)
static inline void debugLog(const std::basic_string<T>& msg, const bool& isError = false, const std::string& filename = "", const int& line = 0)
{
#ifndef NDEBUG
std::basic_string<T> 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<std::is_arithmetic<T>::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_

View File

@ -1,8 +1,10 @@
#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_
#define FLUTTER_INAPPWEBVIEW_PLUGIN_UTIL_STRING_H_
#include <numeric>
#include <optional>
#include <string>
#include <vector>
#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 <typename T>
@ -107,6 +109,33 @@ namespace flutter_inappwebview_plugin
return newString;
}
template <typename T>
static inline std::basic_string<T> join(const std::vector<std::basic_string<T>>& vec, const std::basic_string<T>& delim)
{
return vec.empty() ? std::basic_string<T>{ "" } : /* 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 <typename T>
static inline std::vector<std::basic_string<T>> split(const std::basic_string<T>& s, std::basic_string<T> delimiter)
{
size_t pos_start = 0, pos_end, delim_len = delimiter.length();
std::basic_string<T> token;
std::vector<std::basic_string<T>> res;
while ((pos_end = s.find(delimiter, pos_start)) != std::basic_string<T>::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_