From fb39a00d802def504d04987533052df6ca317594 Mon Sep 17 00:00:00 2001 From: joblonfk001 Date: Fri, 23 Oct 2026 08:54:08 +0800 Subject: [PATCH] fix(web): keep undecodable files out of the picker accept list .avif has no AV1 decoder in any build and .heic/.heif only decode when the codecs' heif feature is enabled, so listing them let web users pick files import would immediately report as failed. accept_list() now mirrors lightcraft_codecs::Format::Heif::can_decode() and never offers .avif. EXTENSIONS itself is unchanged, so files on disk are still recognised and imported-or-reported as before. --- crates/engine/src/import.rs | 57 +++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/import.rs b/crates/engine/src/import.rs index 5ce188531..935013e05 100644 --- a/crates/engine/src/import.rs +++ b/crates/engine/src/import.rs @@ -378,9 +378,25 @@ pub fn is_supported(path: &Path) -> bool { /// spec, but some implementations (e.g. Windows/Edge) match case-sensitively and /// rejected uppercase `.NEF` files, so a lowercase-only list silently hid raws /// such as CR3, NRW, RAF, ORF, RW2, RWL, RAW, PEF and SRW from the picker. +/// +/// The picker only offers files this build can actually import: `.avif` has no +/// AV1 decoder in any build, and `.heic`/`.heif` decode only when the codecs' +/// `heif` feature is enabled ([`lightcraft_codecs::Format::Heif::can_decode`]). +/// [`EXTENSIONS`] itself stays complete — files on disk are still recognised, +/// and import reports them as failed, saying why. pub fn accept_list() -> String { + let heif_decodable = lightcraft_codecs::Format::Heif.can_decode(); let mut out = String::new(); for &ext in EXTENSIONS { + // Skip extensions a web user could pick but this build could not import. + let pickable = match ext { + "avif" => false, + "heic" | "heif" => heif_decodable, + _ => true, + }; + if !pickable { + continue; + } if !out.is_empty() { out.push(','); } @@ -1609,15 +1625,52 @@ mod tests_known; mod prepared_tests { use super::*; + /// Extensions the picker may offer: every one in [`EXTENSIONS`], minus the + /// files this build cannot import (see [`accept_list`]). + fn pickable_extensions() -> Vec<&'static str> { + let heif_decodable = lightcraft_codecs::Format::Heif.can_decode(); + EXTENSIONS + .iter() + .copied() + .filter(|ext| match *ext { + "avif" => false, + "heic" | "heif" => heif_decodable, + _ => true, + }) + .collect() + } + #[test] - fn accept_list_covers_every_extension_in_both_cases() { + fn accept_list_covers_every_pickable_extension_in_both_cases() { let list = accept_list(); - for &ext in EXTENSIONS { + for ext in pickable_extensions() { assert!(list.contains(&format!(".{ext}")), "lowercase .{ext} missing from accept list"); assert!(list.contains(&format!(".{}", ext.to_uppercase())), "uppercase .{} missing from accept list", ext.to_uppercase()); } } + /// The web picker must not offer files this build cannot import: `.avif` has + /// no AV1 decoder in any build, so it is never listed, in either case. + #[test] + fn accept_list_never_offers_avif() { + let list = accept_list(); + assert!(!list.contains(".avif"), "{list}"); + assert!(!list.contains(".AVIF"), "{list}"); + } + + /// `.heic`/`.heif` follow the codecs' `heif` feature: listed (in both cases) + /// only when this build can decode them; otherwise hidden, so a web user + /// cannot pick a file that would fail to import. + #[test] + fn accept_list_offers_heic_heif_only_when_decodable() { + let list = accept_list(); + let listed = lightcraft_codecs::Format::Heif.can_decode(); + for ext in ["heic", "heif"] { + assert_eq!(list.contains(&format!(".{ext}")), listed, "lowercase .{ext}"); + assert_eq!(list.contains(&format!(".{}", ext.to_uppercase())), listed, "uppercase .{ext}"); + } + } + #[test] fn revalidate_add_only_converts_matching_ready_item() { let mut s = Session::new();