From 49e135c8e3e51efa9092867bfbed01ce8faaa7a6 Mon Sep 17 00:00:00 2001 From: Hanif Koh Date: Wed, 16 Sep 2026 14:09:46 +0800 Subject: [PATCH] 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. --- src/slic3r/GUI/PluginWebDialog.cpp | 44 ++++++++++++++++++++++++++---- src/slic3r/GUI/PluginWebDialog.hpp | 3 ++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/PluginWebDialog.cpp b/src/slic3r/GUI/PluginWebDialog.cpp index d89aac7270..ca8e815bbe 100644 --- a/src/slic3r/GUI/PluginWebDialog.cpp +++ b/src/slic3r/GUI/PluginWebDialog.cpp @@ -8,6 +8,7 @@ #include #include +#include #include @@ -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) diff --git a/src/slic3r/GUI/PluginWebDialog.hpp b/src/slic3r/GUI/PluginWebDialog.hpp index 05f77f5148..a62e0b260c 100644 --- a/src/slic3r/GUI/PluginWebDialog.hpp +++ b/src/slic3r/GUI/PluginWebDialog.hpp @@ -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 m_result;