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