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
+19
-2
@@ -1405,12 +1405,25 @@ pub fn collect_entries_with_prev(
|
||||
/// The filesystem stat is desktop-only by `cfg` (not a runtime check): the
|
||||
/// wasm binary contains no `std::fs::metadata` call at all, so a web
|
||||
/// session can never block on — or fail from — a host-filesystem probe.
|
||||
/// Path-based web references resolve through the file-handle loader and
|
||||
/// simply stay `NotFound` when unresolvable.
|
||||
/// Path-based web references resolve through the file-handle loader: a
|
||||
/// source present in that registry reports Loaded here, and only truly
|
||||
/// unregistered ones stay `NotFound`.
|
||||
fn stat_into(entry: &mut ReferenceEntry, raw: &str, base_dir: &Path) {
|
||||
if raw.is_empty() {
|
||||
return;
|
||||
}
|
||||
// Web (and the native tests exercising it): `resolve_path` probes the
|
||||
// filesystem, which the browser does not have — consult the source
|
||||
// registry instead. A registered drawing IS loaded; its byte length
|
||||
// comes free from the in-memory bytes (no read), and there is no
|
||||
// mtime to report.
|
||||
#[cfg(any(target_arch = "wasm32", test))]
|
||||
if let Some(key) = find_registered_source(raw) {
|
||||
entry.size_bytes = registered_bytes(&key).map(|b| b.len() as u64);
|
||||
entry.found_at = Some(key);
|
||||
entry.status = RefStatus::Loaded;
|
||||
return;
|
||||
}
|
||||
let Some(found) = resolve_path(raw, base_dir) else {
|
||||
return;
|
||||
};
|
||||
@@ -2005,6 +2018,8 @@ fn bind_dwg(
|
||||
.find(|b| b.handle == br_handle)
|
||||
.map(|b| b.xref_path.clone())
|
||||
.unwrap_or_default();
|
||||
// Native-only path: `resolve_path` reads the real filesystem, so DWG
|
||||
// bind stays desktop-only (the web has no fs to load a bind source from).
|
||||
let Some(found) = resolve_path(&saved, base_dir) else {
|
||||
return Err(bind_failed(name, "file not found"));
|
||||
};
|
||||
@@ -2181,6 +2196,8 @@ fn collect_bind_images(
|
||||
let mut planned: HashMap<String, String> = HashMap::default();
|
||||
let mut ops: Vec<(Handle, PathBuf, String)> = Vec::new();
|
||||
for (handle, saved) in &defs {
|
||||
// Native-only path: `resolve_path` stats the real filesystem (and
|
||||
// the copy below reads it), so image COLLECT stays desktop-only.
|
||||
let Some(resolved) = resolve_path(saved, source_dir) else {
|
||||
return Err(bind_failed(ref_name, format!("image not found: {saved}")));
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user