Implement external message pump support in recommended way

Override OnScheduleMessagePumpWork() to schedule work to be done on the
main thread instead of relying on only doing this in wxEVT_IDLE handler.

Still keep the latter, however, as without it the window doesn't refresh
correctly -- which might indicate some other problem somewhere.

This also allows to remove an ugly loop calling CefDoMessageLoopWork()
10 times before shutting down CEF and, most importantly, avoids a fatal
assert with "Object reference incorrectly held at CefShutdown" error on
exit in debug builds.
This commit is contained in:
Vadim Zeitlin 2023-09-01 02:21:54 +02:00
parent 0fd0496f81
commit 61cb46cbed
2 changed files with 80 additions and 23 deletions

View file

@ -143,11 +143,8 @@ private:
ClientHandler* m_clientHandler;
friend class wxWebViewChromiumModule;
static int ms_activeWebViewCount;
static bool ms_cefInitialized;
static void OnIdle(wxIdleEvent& evt);
static bool InitCEF();
static void ShutdownCEF();

View file

@ -63,7 +63,6 @@ constexpr const char* TRACE_CEF = "cef";
extern WXDLLIMPEXP_DATA_WEBVIEW_CHROMIUM(const char) wxWebViewBackendChromium[] = "wxWebViewChromium";
int wxWebViewChromium::ms_activeWebViewCount = 0;
bool wxWebViewChromium::ms_cefInitialized = false;
wxIMPLEMENT_DYNAMIC_CLASS(wxWebViewChromium, wxWebView);
@ -229,6 +228,73 @@ private:
IMPLEMENT_REFCOUNTING(wxStringVisitor);
};
namespace
{
class wxBrowserProcessHandler : public CefBrowserProcessHandler
{
public:
wxBrowserProcessHandler() = default;
void OnScheduleMessagePumpWork(int64_t delay_ms) override
{
if ( delay_ms > 0 )
{
if ( !m_timer.IsRunning() )
{
wxLogTrace(TRACE_CEF, "schedule work in %lldms", delay_ms);
m_timer.StartOnce(delay_ms);
}
else
{
wxLogTrace(TRACE_CEF, "work already scheduled");
}
}
else
{
if ( wxTheApp )
wxTheApp->CallAfter([]() { CefDoMessageLoopWork(); });
else
wxLogTrace(TRACE_CEF, "can't schedule message pump work");
}
}
private:
class CEFTimer : public wxTimer
{
public:
CEFTimer() = default;
void Notify() override
{
CefDoMessageLoopWork();
}
} m_timer;
IMPLEMENT_REFCOUNTING(wxBrowserProcessHandler);
};
class wxCefApp : public CefApp
{
public:
wxCefApp()
: m_browserProcessHandler(new wxBrowserProcessHandler{})
{
}
CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override
{
return m_browserProcessHandler;
}
private:
CefRefPtr<CefBrowserProcessHandler> m_browserProcessHandler;
IMPLEMENT_REFCOUNTING(wxCefApp);
};
} // anonymous namespace
bool wxWebViewChromium::Create(wxWindow* parent,
wxWindowID id,
const wxString& url,
@ -307,23 +373,20 @@ bool wxWebViewChromium::Create(wxWindow* parent,
this->Bind(wxEVT_SIZE, &wxWebViewChromium::OnSize, this);
// Initalize CEF message loop handling
if (ms_activeWebViewCount == 0)
wxTheApp->Bind(wxEVT_IDLE, &wxWebViewChromium::OnIdle);
ms_activeWebViewCount++;
Bind(wxEVT_IDLE, [](wxIdleEvent&) { CefDoMessageLoopWork(); });
return true;
}
wxWebViewChromium::~wxWebViewChromium()
{
// Delete CEF idle handler when there is no active webview
ms_activeWebViewCount--;
if (ms_activeWebViewCount == 0)
wxTheApp->Unbind(wxEVT_IDLE, &wxWebViewChromium::OnIdle);
if (m_clientHandler)
{
wxLogTrace(TRACE_CEF, "closing browser");
constexpr bool forceClose = true;
m_clientHandler->GetBrowser()->GetHost()->CloseBrowser(forceClose);
m_clientHandler->Release();
m_clientHandler = nullptr;
}
@ -349,6 +412,10 @@ bool wxWebViewChromium::InitCEF()
wxFileName userDataPath(cefDataFolder.GetFullPath(), "UserData");
CefString(&settings.root_cache_path).FromWString(userDataPath.GetFullPath().ToStdWstring());
// Set up CEF for use inside another application, as is the case for us.
settings.multi_threaded_message_loop = false;
settings.external_message_pump = true;
settings.no_sandbox = true;
#ifdef __WXDEBUG__
@ -363,7 +430,9 @@ bool wxWebViewChromium::InitCEF()
wxAppConsole* app = wxApp::GetInstance();
CefMainArgs args(app->argc, app->argv);
#endif
if (CefInitialize(args, settings, nullptr, nullptr))
CefRefPtr<CefApp> cefApp{new wxCefApp{}};
if (CefInitialize(args, settings, cefApp, nullptr))
{
ms_cefInitialized = true;
return true;
@ -379,19 +448,10 @@ void wxWebViewChromium::ShutdownCEF()
{
if (ms_cefInitialized)
{
// Give CEF a chance for some cleanup work
for (int i = 0; i < 10; i++)
CefDoMessageLoopWork();
CefShutdown();
}
}
void wxWebViewChromium::OnIdle(wxIdleEvent& WXUNUSED(event))
{
CefDoMessageLoopWork();
}
void wxWebViewChromium::OnSize(wxSizeEvent& event)
{
#ifdef __WXMSW__