Compare commits

...
Author SHA1 Message Date
Hanif Koh 49e135c8e3 Restore Plugin HTML After a WebKit Reload
Plugin dialog content is injected with SetPage, so on the WebKit backends a
reload (context menu, keyboard shortcut or location.reload()) re-fetches the
SetPage base URL instead of the injected document, and the plugin UI is gone
for good: load_plugin_content() returned early once m_content_loaded was set.

Re-inject the plugin HTML when a main-frame load after the initial swap is
neither that swap nor a page the plugin linked to. m_own_page_load marks the
load our own SetPage caused, and the URL test recognises the reload: the
injected document and the directory a reload re-fetches both report the base
URL, so a load of any other URL is left alone. The test ignores a fragment the
page navigated to, and undoes the escaping the web view applies to what the
resources path holds.

A load reaching the base URL is not enough on its own, because WebKitGTK reports
a navigation that never committed against the document that stayed and then
finishes that document again: a link to a missing file therefore arrives as a
load of the base URL and reads exactly like a reload. So the re-injection also
requires a navigation to the base URL to have committed, which a reload always
does and a failure never does.

A bootstrap page that cannot be loaded is still not recovered from: WebKitGTK
substitutes a stock error page for it, and that load supersedes the swap
whichever way the swap is ordered around it. The file ships, so this is a
broken-install path; nothing here makes it worse than it already was.

No separate MSW path is needed: wxWebViewEdge ignores the SetPage base URL, so
its documents report about:blank and the test never matches there, and WebView2
reloads NavigateToString content from its own history entry anyway.
2026-09-19 15:45:10 +08:00
2 changed files with 41 additions and 6 deletions
+38 -6
View File
@@ -8,6 +8,7 @@
#include <boost/filesystem.hpp>
#include <wx/event.h>
#include <wx/uri.h>
#include <utility>
@@ -55,6 +56,14 @@ wxString web_base_url()
return wxString("file://") + from_u8(dir) + "/";
}
// Whether a loaded document is the plugin HTML's own base URL. The web view reports the URL it
// parsed, so any fragment the page navigated to is ignored and the escaping it applies to what the
// resources path holds (a space, a non-ASCII character) is undone first.
bool is_content_url(const wxString& url)
{
return wxURI::Unescape(url.BeforeFirst('#')) == web_base_url();
}
} // namespace
PluginWebDialog::PluginWebDialog(wxWindow* parent,
@@ -89,6 +98,7 @@ PluginWebDialog::PluginWebDialog(wxWindow* parent,
// missing/blocked bootstrap resource (e.g. a packaged build) still triggers it.
Bind(wxEVT_WEBVIEW_LOADED, &PluginWebDialog::on_bootstrap_event, this, wv->GetId());
Bind(wxEVT_WEBVIEW_ERROR, &PluginWebDialog::on_bootstrap_event, this, wv->GetId());
Bind(wxEVT_WEBVIEW_NAVIGATED, &PluginWebDialog::on_navigated, this, wv->GetId());
}
Bind(wxEVT_CLOSE_WINDOW, &PluginWebDialog::on_close_window, this);
}
@@ -139,19 +149,41 @@ void PluginWebDialog::destroy_for_plugin(PluginWebDialog* dialog)
void PluginWebDialog::on_bootstrap_event(wxWebViewEvent& event)
{
// The first bootstrap load (or its error) triggers the swap to plugin HTML;
// the resulting plugin-page load is ignored (guarded by m_content_loaded).
load_plugin_content();
const bool loaded = event.GetEventType() == wxEVT_WEBVIEW_LOADED;
// The first bootstrap load (or its error) triggers the swap to plugin HTML.
if (!m_content_loaded)
load_plugin_content();
// WebKit reloads the SetPage base URL rather than the injected page, and the injected document
// reports that same URL, so a load of it that is not the swap we started is a browser reload and
// the plugin HTML has to be put back. A page the plugin linked to reports its own URL and is left
// alone. A navigation that fails is reported against the document that stayed, base URL and all,
// so the reload also has to have committed; nothing commits for a failure. The Edge backend
// ignores the base URL, so nothing matches there, and WebView2 reloads SetPage content from its
// own history entry anyway.
else if (is_content_url(event.GetURL())) {
if (m_own_page_load)
m_own_page_load = false;
else if (loaded && m_content_navigated)
load_plugin_content();
}
if (loaded)
m_content_navigated = false;
event.Skip();
}
void PluginWebDialog::on_navigated(wxWebViewEvent& event)
{
m_content_navigated = is_content_url(event.GetURL());
event.Skip();
}
void PluginWebDialog::load_plugin_content()
{
if (m_content_loaded)
return;
m_content_loaded = true;
if (wxWebView* wv = browser())
if (wxWebView* wv = browser()) {
m_own_page_load = true;
wv->SetPage(wxString::FromUTF8(m_html), web_base_url());
}
}
void PluginWebDialog::on_script_message(const nlohmann::json& payload)
+3
View File
@@ -64,6 +64,7 @@ protected:
private:
void on_bootstrap_event(wxWebViewEvent& event);
void on_navigated(wxWebViewEvent& event);
void load_plugin_content();
void on_close_window(wxCloseEvent& event);
void fire_submit(const nlohmann::json& data);
@@ -72,6 +73,8 @@ private:
std::string m_html;
bool m_content_loaded{false};
bool m_own_page_load{false}; // a SetPage of the plugin HTML is in flight
bool m_content_navigated{false}; // a navigation to the base URL has committed
bool m_open{true};
bool m_close_fired{false};
std::optional<nlohmann::json> m_result;