From b2f0c89420a9a20dd98e42b5f20b1bc7799f72fb Mon Sep 17 00:00:00 2001 From: Paul-Anatole CLAUDOT Date: Mon, 10 Nov 2025 21:17:12 +0100 Subject: [PATCH 1/3] fix: remove JS bridge on WKWebview instead of adding --- .../macos/Classes/DesktopWebviewWindowPlugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop_webview_window/macos/Classes/DesktopWebviewWindowPlugin.swift b/packages/desktop_webview_window/macos/Classes/DesktopWebviewWindowPlugin.swift index ba41e3a5..2a273b2c 100644 --- a/packages/desktop_webview_window/macos/Classes/DesktopWebviewWindowPlugin.swift +++ b/packages/desktop_webview_window/macos/Classes/DesktopWebviewWindowPlugin.swift @@ -110,7 +110,7 @@ public class DesktopWebviewWindowPlugin: NSObject, FlutterPlugin { result(FlutterError(code: "0", message: "can not find webview for id: \(viewId)", details: nil)) return } - wc.webViewController.addJavascriptInterface(name: name) + wc.webViewController.removeJavascriptInterface(name: name) result(nil) break case "clearAll": From 9e174aa19235a6365cd7a9b4a10e84fd3f51fc0b Mon Sep 17 00:00:00 2001 From: Paul-Anatole CLAUDOT Date: Mon, 10 Nov 2025 21:20:57 +0100 Subject: [PATCH 2/3] feat: add new removeAllWebMessage to clean all message instead of passing each callback --- .../desktop_webview_window/lib/src/webview.dart | 13 +++++++++---- .../lib/src/webview_impl.dart | 8 +++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/desktop_webview_window/lib/src/webview.dart b/packages/desktop_webview_window/lib/src/webview.dart index 415c4732..7debe733 100644 --- a/packages/desktop_webview_window/lib/src/webview.dart +++ b/packages/desktop_webview_window/lib/src/webview.dart @@ -6,8 +6,8 @@ typedef JavaScriptMessageHandler = void Function(String name, dynamic body); typedef PromptHandler = String Function(String prompt, String defaultText); -typedef OnHistoryChangedCallback = void Function( - bool canGoBack, bool canGoForward); +typedef OnHistoryChangedCallback = + void Function(bool canGoBack, bool canGoForward); /// Callback when WebView start to load a URL. /// [url] is the URL string. @@ -27,7 +27,9 @@ abstract class Webview { /// /// available: macOS (10.10+) void registerJavaScriptMessageHandler( - String name, JavaScriptMessageHandler handler); + String name, + JavaScriptMessageHandler handler, + ); /// available: macOS void unregisterJavaScriptMessageHandler(String name); @@ -83,7 +85,10 @@ abstract class Webview { void addOnWebMessageReceivedCallback(OnWebMessageReceivedCallback callback); void removeOnWebMessageReceivedCallback( - OnWebMessageReceivedCallback callback); + OnWebMessageReceivedCallback callback, + ); + + void removeAllWebMessageReceivedCallback(); /// Close the web view window. void close(); diff --git a/packages/desktop_webview_window/lib/src/webview_impl.dart b/packages/desktop_webview_window/lib/src/webview_impl.dart index 2ef6e706..52a2b2e1 100644 --- a/packages/desktop_webview_window/lib/src/webview_impl.dart +++ b/packages/desktop_webview_window/lib/src/webview_impl.dart @@ -245,10 +245,16 @@ class WebviewImpl extends Webview { @override void removeOnWebMessageReceivedCallback( - OnWebMessageReceivedCallback callback) { + OnWebMessageReceivedCallback callback, + ) { _onWebMessageReceivedCallbacks.remove(callback); } + @override + void removeAllWebMessageReceivedCallback() { + _onWebMessageReceivedCallbacks.clear(); + } + @override void close() { if (_closed) { From d77ba2f578b7fe8807ff2ee296aad2dcf3cf500e Mon Sep 17 00:00:00 2001 From: Paul-Anatole CLAUDOT Date: Mon, 10 Nov 2025 21:21:25 +0100 Subject: [PATCH 3/3] fix: add some safety and memory leaks fixes for windows --- .../windows/web_view.cc | 140 ++++++++++++------ 1 file changed, 94 insertions(+), 46 deletions(-) diff --git a/packages/desktop_webview_window/windows/web_view.cc b/packages/desktop_webview_window/windows/web_view.cc index 5673c825..44269442 100644 --- a/packages/desktop_webview_window/windows/web_view.cc +++ b/packages/desktop_webview_window/windows/web_view.cc @@ -81,27 +81,34 @@ void WebView::OnWebviewControllerCreated() { if (!webview_controller_) { return; } - webview_controller_->get_CoreWebView2(&webview_); - - if (!webview_) { + + HRESULT hr = webview_controller_->get_CoreWebView2(&webview_); + if (FAILED(hr) || !webview_) { std::cerr << "failed to get core webview" << std::endl; return; } - ICoreWebView2Settings *settings; - webview_->get_Settings(&settings); + wil::com_ptr settings; + hr = webview_->get_Settings(&settings); + if (FAILED(hr) || !settings) { + std::cerr << "failed to get settings" << std::endl; + return; + } + settings->put_IsScriptEnabled(true); settings->put_IsZoomControlEnabled(false); settings->put_AreDefaultContextMenusEnabled(false); settings->put_IsStatusBarEnabled(false); settings->put_IsWebMessageEnabled(true); - ICoreWebView2Settings2 *settings2; - auto hr = settings->QueryInterface(IID_PPV_ARGS(&settings2)); - if (SUCCEEDED(hr)) { - LPWSTR user_agent[256]; - settings2->get_UserAgent(user_agent); - default_user_agent_ = std::wstring(*user_agent); + wil::com_ptr settings2; + hr = settings->QueryInterface(IID_PPV_ARGS(&settings2)); + if (SUCCEEDED(hr) && settings2) { + wil::unique_cotaskmem_string user_agent; + hr = settings2->get_UserAgent(&user_agent); + if (SUCCEEDED(hr) && user_agent) { + default_user_agent_ = std::wstring(user_agent.get()); + } } UpdateBounds(); @@ -111,9 +118,11 @@ void WebView::OnWebviewControllerCreated() { Callback( [](ICoreWebView2 *sender, ICoreWebView2NewWindowRequestedEventArgs *args) { - LPWSTR url; - args->get_Uri(&url); - sender->Navigate(url); + wil::unique_cotaskmem_string url; + HRESULT hr = args->get_Uri(&url); + if (SUCCEEDED(hr) && url) { + sender->Navigate(url.get()); + } args->put_Handled(true); return S_OK; }) @@ -158,17 +167,28 @@ void WebView::OnWebviewControllerCreated() { })); if (triggerOnUrlRequestedEvent) { - LPWSTR uri; - args->get_Uri(&uri); + wil::unique_cotaskmem_string uri; + HRESULT hr = args->get_Uri(&uri); + if (FAILED(hr) || !uri) { + args->put_Cancel(true); + return S_OK; + } + + // Capture URI string before async callback + std::wstring uri_string(uri.get()); auto result_handler = std::make_unique>( - [uri, sender, + [uri_string, sender, this](const flutter::EncodableValue *success_value) { - const bool letPass = std::get(*success_value); + bool letPass = false; + if (success_value && + std::holds_alternative(*success_value)) { + letPass = std::get(*success_value); + } if (letPass) { this->setTriggerOnUrlRequestedEvent(false); - sender->Navigate(uri); + sender->Navigate(uri_string.c_str()); } }, nullptr, nullptr); @@ -181,7 +201,7 @@ void WebView::OnWebviewControllerCreated() { flutter::EncodableValue(web_view_id_)}, {flutter::EncodableValue("url"), flutter::EncodableValue( - wide_to_utf8(std::wstring(uri)))}, + wide_to_utf8(uri_string))}, }), std::move(result_handler)); @@ -248,7 +268,8 @@ void WebView::UpdateBounds() { // Resize WebView to fit the bounds of the parent window RECT bounds; GetClientRect(view_window_.get(), &bounds); - webview_controller_->put_Bounds(bounds); + if (webview_controller_) + webview_controller_->put_Bounds(bounds); } void WebView::Navigate(const std::wstring &url) { @@ -268,11 +289,14 @@ void WebView::AddScriptToExecuteOnDocumentCreated( void WebView::SetApplicationNameForUserAgent(const std::wstring &name) { if (webview_) { - ICoreWebView2Settings *settings; - webview_->get_Settings(&settings); - ICoreWebView2Settings2 *settings2; - auto hr = settings->QueryInterface(IID_PPV_ARGS(&settings2)); - if (SUCCEEDED(hr)) { + wil::com_ptr settings; + HRESULT hr = webview_->get_Settings(&settings); + if (FAILED(hr) || !settings) { + return; + } + wil::com_ptr settings2; + hr = settings->QueryInterface(IID_PPV_ARGS(&settings2)); + if (SUCCEEDED(hr) && settings2) { settings2->put_UserAgent((default_user_agent_ + name).c_str()); } } @@ -326,46 +350,66 @@ void WebView::GetAllCookies( Callback( [result = std::move(result)]( HRESULT hr, ICoreWebView2CookieList *cookieList) -> HRESULT { + if (FAILED(hr) || !cookieList) { + result->Error("0", "Failed to get cookies"); + return S_OK; + } + UINT cookieCount; - cookieList->get_Count(&cookieCount); + hr = cookieList->get_Count(&cookieCount); + if (FAILED(hr)) { + result->Error("0", "Failed to get cookie count"); + return S_OK; + } std::vector cookies; for (UINT i = 0; i < cookieCount; ++i) { wil::com_ptr cookie; - cookieList->GetValueAtIndex(i, &cookie); + hr = cookieList->GetValueAtIndex(i, &cookie); + if (FAILED(hr) || !cookie) { + continue; + } - LPWSTR name; - LPWSTR value; - LPWSTR domain; - LPWSTR path; + wil::unique_cotaskmem_string name; + wil::unique_cotaskmem_string value; + wil::unique_cotaskmem_string domain; + wil::unique_cotaskmem_string path; double expires; BOOL isSecure; BOOL isHttpOnly; BOOL isSessionOnly; - cookie->get_Name(&name); - cookie->get_Value(&value); - cookie->get_Domain(&domain); - cookie->get_Path(&path); - cookie->get_Expires(&expires); - cookie->get_IsSecure(&isSecure); - cookie->get_IsHttpOnly(&isHttpOnly); - cookie->get_IsSession(&isSessionOnly); + hr = cookie->get_Name(&name); + if (FAILED(hr)) continue; + hr = cookie->get_Value(&value); + if (FAILED(hr)) continue; + hr = cookie->get_Domain(&domain); + if (FAILED(hr)) continue; + hr = cookie->get_Path(&path); + if (FAILED(hr)) continue; + hr = cookie->get_Expires(&expires); + if (FAILED(hr)) continue; + hr = cookie->get_IsSecure(&isSecure); + if (FAILED(hr)) continue; + hr = cookie->get_IsHttpOnly(&isHttpOnly); + if (FAILED(hr)) continue; + hr = cookie->get_IsSession(&isSessionOnly); + if (FAILED(hr)) continue; std::map cookieMap; cookieMap[flutter::EncodableValue("name")] = flutter::EncodableValue( - webview_window::ConvertLPCWSTRToString(name)); + webview_window::ConvertLPCWSTRToString(name.get())); cookieMap[flutter::EncodableValue("value")] = flutter::EncodableValue( - webview_window::ConvertLPCWSTRToString(value)); + webview_window::ConvertLPCWSTRToString(value.get())); cookieMap[flutter::EncodableValue("domain")] = flutter::EncodableValue( - webview_window::ConvertLPCWSTRToString(domain)); + webview_window::ConvertLPCWSTRToString(domain.get())); cookieMap[flutter::EncodableValue("path")] = flutter::EncodableValue( - webview_window::ConvertLPCWSTRToString(path)); + webview_window::ConvertLPCWSTRToString(path.get())); if (expires >= 0) { cookieMap[flutter::EncodableValue(std::string("expires"))] = @@ -431,8 +475,12 @@ void WebView::ExecuteJavaScript( if (error != S_OK) { completer->Error("0", "Error executing JavaScript"); } else { - completer->Success(flutter::EncodableValue( - wide_to_utf8(std::wstring(result)))); + if (result) { + completer->Success(flutter::EncodableValue( + wide_to_utf8(std::wstring(result)))); + } else { + completer->Success(flutter::EncodableValue("")); + } } return S_OK; })