forked from 1b-agent/opencadstudio-upstream
fix: close the adversarial findings on the save/paste wave
Ported fromda950460(#1717). In-place save: - A stale retained handle could receive bytes serialized for a DIFFERENT format (Save As .dwg->.dxf whose re-pick failed wrote DXF bytes through the .dwg handle) or silently save in place while the user was told a copy was downloading. The serialized format now travels to completion (tab-keyed pending map), the write requires a matching handle extension AND a live tab, and a failed re-pick drops the old handle before the download fallback. - Discarding an unsaved tab leaked its handle, and a serialization still in flight could then overwrite the real file the user just declined to save. Discard drops the handle; a tab-gone completion downloads instead of writing. - Overlapping saves had no guard: a wasm in-flight slot refuses a second save with the same localized message, cleared on every exit path. Paste: pasted_image_name built its timestamp from Date::new(0) — a CONSTANT name, so every paste overwrote every earlier paste's image. Real clock, milliseconds, and a collision-suffixed unique name now. Capture: a web capture in a hidden tab, or one with nothing redrawing, could hold the automation slot forever. It fails fast when the page is hidden and gives up after 5 s, on main's frame-parked capture. The XREF manager reports registry-loaded xrefs with their size on the web instead of NotFound. Left out: the always-warm print snapshot (a frames subscription that emits a message on every frame keeps the app redrawing at the display rate), so the browser-menu print wiring from the previous commit goes too — its beforeprint readback printed blank pages on WebGL2. The merge-residue hunks (re-added XREF bind tests, snap endpoint map and 3D-snap gate) belong to code main already has in its own form. (cherry picked from commitda950460)
This commit is contained in:
1 parent
835e8d469f
commit
1c0963b68d
8 files changed
+382
-90
No files matched your search
@@ -198,6 +198,14 @@ impl OpenCADStudio {
|
||||
let Some(idx) = self.tabs.iter().position(|t| t.id == tab_id) else {
|
||||
return Task::batch([close_win, self.continue_tab_close_queue()]);
|
||||
};
|
||||
// …and, on the web, its retained in-place save handle — the
|
||||
// same hook on_tab_close runs: the user declined the save,
|
||||
// so the picked file must never receive a later write, and
|
||||
// the next tab to reuse this id must start with a fresh
|
||||
// pick instead of silently writing over the discarded
|
||||
// drawing's file.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
crate::io::fs_access::drop_handle(tab_id);
|
||||
// Discarded — drop this tab's autosave recovery copy.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let _ = std::fs::remove_file(self.autosave_target(idx));
|
||||
@@ -319,6 +327,21 @@ impl OpenCADStudio {
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
// Same guard the native half runs against active_save_jobs
|
||||
// above: while a web save is between capture and completion,
|
||||
// refuse a second one (the window is seconds — refuse, don't
|
||||
// queue) instead of letting two serializer rounds race for the
|
||||
// same tab's bookkeeping.
|
||||
if let Some(busy_tab) = crate::io::fs_access::web_save_in_flight() {
|
||||
if self.tabs.iter().any(|tab| tab.id == busy_tab) {
|
||||
self.command_line
|
||||
.push_info(crate::t!("Save already running for this drawing.").as_ref());
|
||||
return Task::none();
|
||||
}
|
||||
// Stale marker (its tab closed before settling): clear it
|
||||
// rather than block this save forever.
|
||||
crate::io::fs_access::end_web_save(busy_tab);
|
||||
}
|
||||
match self.pending_close.take() {
|
||||
Some(crate::app::PendingClose::Tab(tab_id)) => {
|
||||
let Some(idx) = self.tabs.iter().position(|t| t.id == tab_id) else {
|
||||
|
||||
Reference in new issue
Block a user