Skip to content

Commit 484deb7

Browse files
fix(webview): drop cached controllers before Close
Close can pump the message loop. A resize or the viewport timer must not call into a view that is already being released. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 338737e commit 484deb7

1 file changed

Lines changed: 65 additions & 29 deletions

File tree

‎src/widgets/WebViewWidget_win.cpp‎

Lines changed: 65 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -311,30 +311,31 @@ class WebViewWidgetWin : public WebViewWidget
311311
}
312312
}
313313

314+
// Drop cached COM pointers before Close. Close can pump, and a
315+
// resize or the emulation timer must not call into a dying view.
316+
if (m_emulationTimer)
317+
m_emulationTimer->stop();
318+
m_touchView = nullptr;
319+
m_touchOn = false;
320+
m_metricsView = nullptr;
321+
m_metricsOn = false;
314322
if (m_pages.isEmpty()) {
315-
if (m_controller) {
316-
m_controller->Close();
317-
m_controller->Release();
318-
}
319-
if (m_webView)
320-
m_webView->Release();
323+
ICoreWebView2Controller *controller = m_controller;
324+
ICoreWebView2 *webView = m_webView;
321325
m_controller = nullptr;
322326
m_webView = nullptr;
327+
releaseOwnedView(controller, webView);
323328
} else {
329+
m_controller = nullptr;
330+
m_webView = nullptr;
324331
for (Page &page : m_pages) {
325-
if (page.controller) {
326-
page.controller->Close();
327-
page.controller->Release();
328-
page.controller = nullptr;
329-
}
330-
if (page.webView) {
331-
page.webView->Release();
332-
page.webView = nullptr;
333-
}
332+
ICoreWebView2Controller *controller = page.controller;
333+
ICoreWebView2 *webView = page.webView;
334+
page.controller = nullptr;
335+
page.webView = nullptr;
336+
releaseOwnedView(controller, webView);
334337
}
335338
m_pages.clear();
336-
m_controller = nullptr;
337-
m_webView = nullptr;
338339
}
339340
m_inflightPages.clear();
340341
m_initialControllerPending = false;
@@ -373,6 +374,35 @@ class WebViewWidgetWin : public WebViewWidget
373374
}
374375

375376
protected:
377+
// Null live aliases first. Close pumps the message loop, and a resize or
378+
// the emulation timer must not call into the view being destroyed.
379+
void releaseOwnedView(ICoreWebView2Controller *controller, ICoreWebView2 *webView)
380+
{
381+
if (webView) {
382+
if (webView == m_touchView) {
383+
m_touchView = nullptr;
384+
m_touchOn = false;
385+
}
386+
if (webView == m_metricsView) {
387+
m_metricsView = nullptr;
388+
m_metricsOn = false;
389+
}
390+
if (webView == m_webView) {
391+
m_webView = nullptr;
392+
if (m_emulationTimer)
393+
m_emulationTimer->stop();
394+
}
395+
}
396+
if (controller && controller == m_controller)
397+
m_controller = nullptr;
398+
if (controller) {
399+
controller->Close();
400+
controller->Release();
401+
}
402+
if (webView)
403+
webView->Release();
404+
}
405+
376406
void applyControllerBounds(ICoreWebView2Controller *controller, QWidget *host)
377407
{
378408
if (!controller || !host)
@@ -417,8 +447,9 @@ class WebViewWidgetWin : public WebViewWidget
417447
"AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Mobile/15E148 Safari/604.1\","
418448
"\"platform\":\"iPad\"}");
419449
}
450+
const std::wstring ua = uaJson.toStdWString();
420451
webView->CallDevToolsProtocolMethod(
421-
L"Emulation.setUserAgentOverride", uaJson.toStdWString().c_str(), nullptr);
452+
L"Emulation.setUserAgentOverride", ua.c_str(), nullptr);
422453
}
423454

424455
const QRect r = m_hostWidget
@@ -449,8 +480,9 @@ class WebViewWidgetWin : public WebViewWidget
449480
.arg(w)
450481
.arg(h)
451482
.arg(dpr);
483+
const std::wstring metricsW = metrics.toStdWString();
452484
webView->CallDevToolsProtocolMethod(
453-
L"Emulation.setDeviceMetricsOverride", metrics.toStdWString().c_str(), nullptr);
485+
L"Emulation.setDeviceMetricsOverride", metricsW.c_str(), nullptr);
454486
}
455487

456488
void applyViewport() override
@@ -468,6 +500,8 @@ class WebViewWidgetWin : public WebViewWidget
468500
m_emulationTimer->setSingleShot(true);
469501
m_emulationTimer->setInterval(50);
470502
connect(m_emulationTimer, &QTimer::timeout, this, [this]() {
503+
if (!m_alive->load(std::memory_order_acquire))
504+
return;
471505
applyTouchEmulation(m_webView);
472506
});
473507
}
@@ -1405,12 +1439,13 @@ class WebViewWidgetWin : public WebViewWidget
14051439
m_tabBar->removeTab(index);
14061440
m_switching = false;
14071441
releasePending(takePending(id));
1408-
if (page.controller) {
1409-
page.controller->Close();
1410-
page.controller->Release();
1442+
if (page.controller || page.webView) {
1443+
ICoreWebView2Controller *controller = page.controller;
1444+
ICoreWebView2 *webView = page.webView;
1445+
page.controller = nullptr;
1446+
page.webView = nullptr;
1447+
releaseOwnedView(controller, webView);
14111448
}
1412-
if (page.webView)
1413-
page.webView->Release();
14141449
if (awaitingController && m_inflightPages.contains(id) && page.host) {
14151450
page.host->hide();
14161451
m_deferredHosts.insert(id, page.host);
@@ -1444,12 +1479,13 @@ class WebViewWidgetWin : public WebViewWidget
14441479
if (m_tabBar && index < m_tabBar->count())
14451480
m_tabBar->removeTab(index);
14461481
m_switching = false;
1447-
if (page.controller) {
1448-
page.controller->Close();
1449-
page.controller->Release();
1482+
if (page.controller || page.webView) {
1483+
ICoreWebView2Controller *controller = page.controller;
1484+
ICoreWebView2 *webView = page.webView;
1485+
page.controller = nullptr;
1486+
page.webView = nullptr;
1487+
releaseOwnedView(controller, webView);
14501488
}
1451-
if (page.webView)
1452-
page.webView->Release();
14531489
if (page.host)
14541490
page.host->deleteLater();
14551491
if (m_pages.size() < 2 && m_tabBar)

0 commit comments

Comments
 (0)