@@ -37,8 +37,15 @@ extern "C" size_t LWE_EXPORT createWebViewInstance(
3737
3838class NavigationRequestResult : public FlMethodResult {
3939 public:
40- NavigationRequestResult (std::string url, WebView* webview)
41- : url_(url), webview_(webview) {}
40+ // |alive| is the WebView's is_alive_ flag. Dart resolves the
41+ // "navigationRequest" method call asynchronously (it round-trips through
42+ // the Dart navigation delegate), so this result's completion can run well
43+ // after the WebView that created it has been disposed; |webview_| would
44+ // then be a dangling pointer. Checking |alive| before dereferencing it
45+ // avoids a use-after-free in that case.
46+ NavigationRequestResult (std::string url, WebView* webview,
47+ std::shared_ptr<bool > alive)
48+ : url_(url), webview_(webview), alive_(std::move(alive)) {}
4249
4350 void SuccessInternal (const flutter::EncodableValue* should_load) override {
4451 if (std::holds_alternative<bool >(*should_load)) {
@@ -60,13 +67,17 @@ class NavigationRequestResult : public FlMethodResult {
6067
6168 private:
6269 void LoadUrl () {
70+ if (!*alive_) {
71+ return ;
72+ }
6373 if (webview_ && webview_->GetWebViewInstance ()) {
6474 webview_->GetWebViewInstance ()->LoadURL (url_);
6575 }
6676 }
6777
6878 std::string url_;
6979 WebView* webview_;
80+ std::shared_ptr<bool > alive_;
7081};
7182
7283template <typename T>
@@ -153,35 +164,45 @@ WebView::WebView(flutter::PluginRegistrar* registrar, int view_id,
153164 flutter::EncodableMap args = {
154165 {flutter::EncodableValue (" url" ), flutter::EncodableValue (url)}};
155166
156- dispatcher_->dispatchTaskOnMainThread ([this , args]() {
157- navigation_delegate_channel_->InvokeMethod (
158- " onPageStarted" , std::make_unique<flutter::EncodableValue>(args));
159- // The lightweight web engine has no dedicated URL-change callback, so
160- // report a URL change whenever a navigation starts.
161- navigation_delegate_channel_->InvokeMethod (
162- " onUrlChange" , std::make_unique<flutter::EncodableValue>(args));
163- });
167+ dispatcher_->dispatchTaskOnMainThread (
168+ [this , args, alive = is_alive_]() {
169+ if (!*alive) return ;
170+ navigation_delegate_channel_->InvokeMethod (
171+ " onPageStarted" ,
172+ std::make_unique<flutter::EncodableValue>(args));
173+ // The lightweight web engine has no dedicated URL-change
174+ // callback, so report a URL change whenever a navigation
175+ // starts.
176+ navigation_delegate_channel_->InvokeMethod (
177+ " onUrlChange" ,
178+ std::make_unique<flutter::EncodableValue>(args));
179+ });
164180 });
165181 webview_instance_->RegisterOnPageLoadedHandler (
166182 [this ](LWE ::WebContainer* container, const std::string& url) {
167183 flutter::EncodableMap args = {
168184 {flutter::EncodableValue (" url" ), flutter::EncodableValue (url)}};
169185
170- dispatcher_->dispatchTaskOnMainThread ([this , args]() {
171- navigation_delegate_channel_->InvokeMethod (
172- " onPageFinished" ,
173- std::make_unique<flutter::EncodableValue>(args));
174- });
186+ dispatcher_->dispatchTaskOnMainThread (
187+ [this , args, alive = is_alive_]() {
188+ if (!*alive) return ;
189+ navigation_delegate_channel_->InvokeMethod (
190+ " onPageFinished" ,
191+ std::make_unique<flutter::EncodableValue>(args));
192+ });
175193 });
176194 webview_instance_->RegisterOnProgressChangedHandler (
177195 [this ](LWE ::WebContainer* container, int progress) {
178196 flutter::EncodableMap args = {{flutter::EncodableValue (" progress" ),
179197 flutter::EncodableValue (progress)}};
180198
181- dispatcher_->dispatchTaskOnMainThread ([this , args]() {
182- navigation_delegate_channel_->InvokeMethod (
183- " onProgress" , std::make_unique<flutter::EncodableValue>(args));
184- });
199+ dispatcher_->dispatchTaskOnMainThread (
200+ [this , args, alive = is_alive_]() {
201+ if (!*alive) return ;
202+ navigation_delegate_channel_->InvokeMethod (
203+ " onProgress" ,
204+ std::make_unique<flutter::EncodableValue>(args));
205+ });
185206 });
186207 webview_instance_->RegisterOnReceivedErrorHandler (
187208 [this ](LWE ::WebContainer* container, LWE ::ResourceError error) {
@@ -193,11 +214,13 @@ WebView::WebView(flutter::PluginRegistrar* registrar, int view_id,
193214 {flutter::EncodableValue (" failingUrl" ),
194215 flutter::EncodableValue (error.GetUrl ())},
195216 };
196- dispatcher_->dispatchTaskOnMainThread ([this , args]() {
197- navigation_delegate_channel_->InvokeMethod (
198- " onWebResourceError" ,
199- std::make_unique<flutter::EncodableValue>(args));
200- });
217+ dispatcher_->dispatchTaskOnMainThread (
218+ [this , args, alive = is_alive_]() {
219+ if (!*alive) return ;
220+ navigation_delegate_channel_->InvokeMethod (
221+ " onWebResourceError" ,
222+ std::make_unique<flutter::EncodableValue>(args));
223+ });
201224 });
202225 webview_instance_->RegisterShouldOverrideUrlLoadingHandler (
203226 [this ](LWE ::WebContainer* view, const std::string& url) -> bool {
@@ -210,13 +233,16 @@ WebView::WebView(flutter::PluginRegistrar* registrar, int view_id,
210233 flutter::EncodableValue (true )},
211234 };
212235
213- dispatcher_->dispatchTaskOnMainThread ([this , args, url]() {
214- auto result = std::make_unique<NavigationRequestResult>(url, this );
215- navigation_delegate_channel_->InvokeMethod (
216- " navigationRequest" ,
217- std::make_unique<flutter::EncodableValue>(args),
218- std::move (result));
219- });
236+ dispatcher_->dispatchTaskOnMainThread (
237+ [this , args, url, alive = is_alive_]() {
238+ if (!*alive) return ;
239+ auto result = std::make_unique<NavigationRequestResult>(
240+ url, this , alive);
241+ navigation_delegate_channel_->InvokeMethod (
242+ " navigationRequest" ,
243+ std::make_unique<flutter::EncodableValue>(args),
244+ std::move (result));
245+ });
220246 return true ;
221247 });
222248}
@@ -235,11 +261,13 @@ void WebView::RegisterJavaScriptChannelName(const std::string& name) {
235261 {flutter::EncodableValue (" message" ), flutter::EncodableValue (message)},
236262 };
237263
238- dispatcher_->dispatchTaskOnMainThread ([this , args]() {
239- webview_channel_->InvokeMethod (
240- " javaScriptChannelMessage" ,
241- std::make_unique<flutter::EncodableValue>(args));
242- });
264+ dispatcher_->dispatchTaskOnMainThread (
265+ [this , args, alive = is_alive_]() {
266+ if (!*alive) return ;
267+ webview_channel_->InvokeMethod (
268+ " javaScriptChannelMessage" ,
269+ std::make_unique<flutter::EncodableValue>(args));
270+ });
243271 return " success" ;
244272 };
245273 webview_instance_->AddJavaScriptInterface (name, " postMessage" , on_message);
@@ -261,6 +289,12 @@ std::string WebView::GetNavigationDelegateChannelName() {
261289}
262290
263291void WebView::Dispose () {
292+ // Flip this before anything else: any dispatcher_ callback already queued
293+ // on the main loop (e.g. a navigation event that fired just before this
294+ // WebView was disposed) checks it and bails out instead of touching a
295+ // WebView that may be fully destroyed by the time it runs.
296+ *is_alive_ = false ;
297+
264298 // Stop the web engine first so its renderer thread stops writing into the
265299 // shared TBM surfaces before anything is torn down.
266300 if (webview_instance_) {
0 commit comments