diff --git a/packages/blitz-dom/src/document.rs b/packages/blitz-dom/src/document.rs index 4543788bd0..b8f8020611 100644 --- a/packages/blitz-dom/src/document.rs +++ b/packages/blitz-dom/src/document.rs @@ -1068,6 +1068,25 @@ impl BaseDocument { } pub(crate) fn resolve_url(&self, raw: &str) -> url::Url { + #[cfg(windows)] + { + let path = std::path::Path::new(raw); + if path.is_absolute() { + if let Ok(file_url) = url::Url::from_file_path(path) { + return file_url; + } + } + } + #[cfg(not(windows))] + { + let path = std::path::Path::new(raw); + if path.is_absolute() && self.url.scheme() == "file" { + if let Ok(file_url) = url::Url::from_file_path(path) { + return file_url; + } + } + } + self.url.resolve_relative(raw).unwrap_or_else(|| { panic!( "to be able to resolve {raw} with the base_url: {:?}", diff --git a/packages/blitz-dom/src/net.rs b/packages/blitz-dom/src/net.rs index 0d39ca4464..6fe78289ec 100644 --- a/packages/blitz-dom/src/net.rs +++ b/packages/blitz-dom/src/net.rs @@ -1,9 +1,9 @@ use blitz_traits::node_id::NodeId; use selectors::context::QuirksMode; -use std::sync::atomic::Ordering as Ao; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as Ao}; use std::{ io::Cursor, - sync::{Arc, atomic::AtomicUsize, mpsc::Sender}, + sync::{Arc, mpsc::Sender}, }; use style::{ font_face::{FontFaceSourceFormat, FontFaceSourceFormatKeyword, FontStyleRange, Source}, @@ -75,6 +75,7 @@ pub(crate) struct ResourceHandler { tx: Sender, shell_provider: Arc, data: T, + responded: AtomicBool, } impl ResourceHandler { @@ -93,6 +94,7 @@ impl ResourceHandler { tx, shell_provider, data, + responded: AtomicBool::new(false), } } @@ -114,6 +116,9 @@ impl ResourceHandler { } fn respond(&self, resolved_url: String, result: Result) { + if self.responded.swap(true, Ao::SeqCst) { + return; + } let response = ResourceLoadResponse { request_id: self.request_id, node_id: self.node_id, @@ -125,6 +130,21 @@ impl ResourceHandler { } } +impl Drop for ResourceHandler { + fn drop(&mut self) { + if !self.responded.swap(true, Ao::SeqCst) { + let response = ResourceLoadResponse { + request_id: self.request_id, + node_id: self.node_id, + resolved_url: None, + result: Err(String::from("Resource handler dropped without response")), + }; + let _ = self.tx.send(DocumentEvent::ResourceLoad(response)); + self.shell_provider.request_redraw(); + } + } +} + #[allow(unused)] pub struct ResourceLoadResponse { pub request_id: usize, @@ -643,4 +663,42 @@ mod tests { Fq::Oblique(Some(10.0)), ); } + + #[test] + fn dropped_handler_emits_error_response() { + let (tx, rx) = std::sync::mpsc::channel(); + let shell = Arc::new(blitz_traits::shell::DummyShellProvider); + let handler = ResourceHandler::new(tx, 0, None, shell, ()); + let req_id = handler.request_id(); + drop(handler); + + let event = rx.try_recv().expect("drop should emit an event"); + match event { + DocumentEvent::ResourceLoad(res) => { + assert_eq!(res.request_id, req_id); + assert!(res.result.is_err()); + } + _ => panic!("unexpected event"), + } + } + + #[test] + fn responded_handler_does_not_emit_on_drop() { + let (tx, rx) = std::sync::mpsc::channel(); + let shell = Arc::new(blitz_traits::shell::DummyShellProvider); + let handler = ResourceHandler::new(tx, 0, None, shell, ()); + let req_id = handler.request_id(); + handler.respond("test".into(), Ok(Resource::None)); + drop(handler); + + let event = rx.try_recv().expect("respond should emit an event"); + match event { + DocumentEvent::ResourceLoad(res) => { + assert_eq!(res.request_id, req_id); + assert!(res.result.is_ok()); + } + _ => panic!("unexpected event"), + } + assert!(rx.try_recv().is_err(), "should not emit duplicate event on drop"); + } } diff --git a/packages/blitz-net/src/lib.rs b/packages/blitz-net/src/lib.rs index 49bbb4c7a8..377e7b24eb 100644 --- a/packages/blitz-net/src/lib.rs +++ b/packages/blitz-net/src/lib.rs @@ -157,7 +157,11 @@ impl Provider { Ok((request.url.to_string(), Bytes::from(decoded.0))) } "file" => { - let file_content = std::fs::read(request.url.path())?; + let file_path = request + .url + .to_file_path() + .map_err(|()| std::io::Error::new(std::io::ErrorKind::NotFound, "invalid file url"))?; + let file_content = std::fs::read(file_path)?; Ok((request.url.to_string(), Bytes::from(file_content))) } _ => Self::fetch_http(client, request, per_host_limits).await,