From 3a48086f5d7800f2536a1acc1071c9ad2853fb1d Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Mon, 20 Jul 2026 12:47:18 +0800 Subject: [PATCH 1/4] fix: plugin descriptor should not reply on changelog for latest version --- src/slic3r/plugin/PluginDescriptor.hpp | 6 +---- .../test_plugin_cloud_metadata.cpp | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/slic3r/plugin/PluginDescriptor.hpp b/src/slic3r/plugin/PluginDescriptor.hpp index 28c411b176..687b14d7c6 100644 --- a/src/slic3r/plugin/PluginDescriptor.hpp +++ b/src/slic3r/plugin/PluginDescriptor.hpp @@ -54,7 +54,7 @@ struct PluginDescriptor std::string description; // Plugin description std::string author; // Plugin author from manifest, if available std::string version; // Selected plugin version - std::string latest_version; // Latest available cloud version fallback when changelog is unavailable. + std::string latest_version; // Authoritative latest available cloud version. std::string installed_version; // Locally installed package version. Preserved across cloud merges, which overwrite `version` with the latest cloud version. Empty when not installed. std::vector display_types; // Display-only "compatibility" labels (cloud: raw service labels; local: from real capabilities). Never used for dispatch. std::string plugin_root; // Installed plugin directory, even when entry_path is invalid or ambiguous. @@ -113,10 +113,6 @@ struct PluginDescriptor bool is_unauthorized() const { return get_update_status() == PluginUpdateStatus::Unauthorized; } std::string latest_available_version() const { - for (const PluginChangelog& entry : changelog) { - if (!entry.version.empty()) - return entry.version; - } if (!latest_version.empty()) return latest_version; return version; diff --git a/tests/slic3rutils/test_plugin_cloud_metadata.cpp b/tests/slic3rutils/test_plugin_cloud_metadata.cpp index 6f438541ff..ec8b61dea9 100644 --- a/tests/slic3rutils/test_plugin_cloud_metadata.cpp +++ b/tests/slic3rutils/test_plugin_cloud_metadata.cpp @@ -52,6 +52,29 @@ print('ok') } // namespace +TEST_CASE("plugin latest version uses the authoritative catalog field", "[PluginDescriptor]") +{ + PluginDescriptor descriptor; + descriptor.version = "1.3.0"; + descriptor.latest_version = "1.3.0"; + PluginChangelog changelog; + changelog.version = "1.2.0"; + descriptor.changelog.push_back(changelog); + + CHECK(descriptor.latest_available_version() == "1.3.0"); +} + +TEST_CASE("plugin latest version falls back to the descriptor version", "[PluginDescriptor]") +{ + PluginDescriptor descriptor; + descriptor.version = "1.1.0"; + PluginChangelog changelog; + changelog.version = "1.0.0"; + descriptor.changelog.push_back(changelog); + + CHECK(descriptor.latest_available_version() == "1.1.0"); +} + // Regression: update_cloud_metadata() replaces a matched entry's descriptor wholesale with the // cloud catalog record (`entry = cloud_entry`). Configuration used to ride on the descriptor, so // that overwrite silently wiped it and plugins fell back to their built-in defaults (found via From ddbbc7f7f2622b35692afbf78a1b12e0b21112c0 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Mon, 20 Jul 2026 12:48:17 +0800 Subject: [PATCH 2/4] fix: only the top level frame should have the orca syles and bridges injected --- src/slic3r/GUI/PluginWebDialog.cpp | 11 ++++++----- src/slic3r/GUI/Widgets/WebViewHostDialog.cpp | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/PluginWebDialog.cpp b/src/slic3r/GUI/PluginWebDialog.cpp index fdaf6c7a23..1808f21ce9 100644 --- a/src/slic3r/GUI/PluginWebDialog.cpp +++ b/src/slic3r/GUI/PluginWebDialog.cpp @@ -48,13 +48,14 @@ std::string plugin_defaults_user_script() return WebViewHostDialog::document_start_injector(css, "orca-plugin-defaults", "beforeend"); } -// Injected into every page at document start (before the plugin's own scripts). -// Defines window.orca as the only host surface the page may use. It references -// window.wx lazily (at call time) so it never races the backend's deferred -// registration of the "wx" message handler. Guarded against double-injection so -// it is harmless if also prepended. +// Injected into the top-level page at document start (before the plugin's own +// scripts). Defines window.orca as the only host surface the page may use. It +// references window.wx lazily (at call time) so it never races the backend's +// deferred registration of the "wx" message handler. Guarded against +// double-injection so it is harmless if also prepended. constexpr char ORCA_BRIDGE_JS[] = R"JS( (function () { + if (window.top !== window.self) return; if (window.orca) return; var handlers = []; function send(kind, data) { diff --git a/src/slic3r/GUI/Widgets/WebViewHostDialog.cpp b/src/slic3r/GUI/Widgets/WebViewHostDialog.cpp index 9f4718160e..5e6026d1cf 100644 --- a/src/slic3r/GUI/Widgets/WebViewHostDialog.cpp +++ b/src/slic3r/GUI/Widgets/WebViewHostDialog.cpp @@ -96,6 +96,9 @@ std::string WebViewHostDialog::document_start_injector(const std::string& markup const std::string literal = nlohmann::json(markup).dump(); std::string s; s += "(function(){"; + // wxWebView's AddUserScript runs in child frames too (including cross-origin + // frames on WebView2). Host theme state belongs only to the top-level page. + s += "if(window.top!==window.self)return;"; s += prelude; s += "var css=" + literal + ";"; s += "function inject(){"; From 2a46bd1384888b7c75ee883ad3ffc8a40bb38357 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Mon, 20 Jul 2026 13:58:57 +0800 Subject: [PATCH 3/4] fix: crash transferring Slicing Pipeline Plugin changes across a printer switch --- src/libslic3r/Preset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index bfe66069d1..0e9dd58593 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -3504,7 +3504,7 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt) { //BBS: add bed_exclude_area - if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails" || opt_key == "wrapping_exclude_area") { + if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails" || opt_key == "wrapping_exclude_area" || opt_key == "slicing_pipeline_plugin") { // Scalar variable, or a vector variable, which is independent from number of extruders, // thus the vector is presented to the user as a single input. diff.emplace_back(opt_key); From 242e100febe433c8044056294568d635c6689fcf Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Mon, 20 Jul 2026 14:47:26 +0800 Subject: [PATCH 4/4] fix: stale .whl cache when .whl changes, added option to delete cache and reload --- src/slic3r/GUI/PluginsDialog.cpp | 40 ++++++++++++++++++++++++++++---- src/slic3r/GUI/PluginsDialog.hpp | 2 +- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/PluginsDialog.cpp b/src/slic3r/GUI/PluginsDialog.cpp index 3011c35ce3..3485f686fa 100644 --- a/src/slic3r/GUI/PluginsDialog.cpp +++ b/src/slic3r/GUI/PluginsDialog.cpp @@ -300,7 +300,12 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item) add_action("open_folder", "Show in folder", has_local); - add_action("reinstall_plugin", "Reinstall"); + if (is_cloud) { + add_action("reinstall_plugin", "Reinstall"); + } else { + add_action("reload_plugin", "Reload"); + add_action("clear_cache_reload_plugin", "Delete cache and reload"); + } return available_actions; } @@ -739,11 +744,13 @@ void PluginsDialog::handle_plugin_menu_action(const std::string& plugin_key, con unsubscribe_cloud_plugin(row_data); } else if (action == "delete_mine_plugin") { delete_mine_local_and_cloud_plugin(plugin_key); + } else if (action == "reload_plugin") { + reload_local_plugin(plugin_key, /*clear_cache=*/false); + } else if (action == "clear_cache_reload_plugin") { + reload_local_plugin(plugin_key, /*clear_cache=*/true); } else if (action == "reinstall_plugin") { if (row_data.is_cloud_plugin()) reinstall_cloud_plugin(row_data); - else - reinstall_local_plugin(plugin_key); } } @@ -1123,7 +1130,7 @@ void PluginsDialog::unsubscribe_cloud_plugin(const PluginDescriptor& plugin) _L("Unsubscribing plugin"), _L("Deleting local files and unsubscribing plugin...")); } -void PluginsDialog::reinstall_local_plugin(const std::string& plugin_key) +void PluginsDialog::reload_local_plugin(const std::string& plugin_key, bool clear_cache) { if (plugin_key.empty()) return; @@ -1132,11 +1139,34 @@ void PluginsDialog::reinstall_local_plugin(const std::string& plugin_key) std::pair reload_result{false, ""}; try { reload_result = run_with_dialog_wait( - [plugin_key, was_loaded]() -> std::pair { + [plugin_key, was_loaded, clear_cache]() -> std::pair { PluginManager& manager = PluginManager::instance(); + + boost::filesystem::path cache_dir; + if (clear_cache) { + PluginDescriptor descriptor; + if (!manager.try_get_plugin_descriptor(plugin_key, descriptor)) + return {false, "Plugin not found."}; + + boost::filesystem::path resolved_root; + std::string resolve_error; + if (!resolve_allowed_plugin_root(descriptor, {get_orca_plugins_dir()}, + "Refusing to clear a plugin cache outside the local plugin directory.", + resolved_root, resolve_error)) + return {false, resolve_error}; + cache_dir = resolved_root / "__whl_extracted__"; + } + if (!manager.unload_plugin(plugin_key)) return {false, "Failed to unload plugin."}; + if (clear_cache) { + boost::system::error_code ec; + boost::filesystem::remove_all(cache_dir, ec); + if (ec) + return {false, "Failed to clear plugin cache: " + ec.message()}; + } + manager.load_plugin(plugin_key, false); std::string error; if (!manager.wait_for_plugin_load(plugin_key, std::chrono::minutes(5), error) || !manager.is_plugin_loaded(plugin_key)) diff --git a/src/slic3r/GUI/PluginsDialog.hpp b/src/slic3r/GUI/PluginsDialog.hpp index de0f44985d..b921801166 100644 --- a/src/slic3r/GUI/PluginsDialog.hpp +++ b/src/slic3r/GUI/PluginsDialog.hpp @@ -96,7 +96,7 @@ private: void open_plugin_folder(const Slic3r::PluginDescriptor& plugin); void delete_local_plugin(const Slic3r::PluginDescriptor& plugin); void unsubscribe_cloud_plugin(const Slic3r::PluginDescriptor& plugin); - void reinstall_local_plugin(const std::string& plugin_key); + void reload_local_plugin(const std::string& plugin_key, bool clear_cache); void reinstall_cloud_plugin(const Slic3r::PluginDescriptor& plugin); void delete_mine_local_and_cloud_plugin(const std::string& plugin_key);