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