diff --git a/components/net/image_cache.rs b/components/net/image_cache.rs index 68d14f4c10c..078380bb466 100644 --- a/components/net/image_cache.rs +++ b/components/net/image_cache.rs @@ -31,6 +31,11 @@ use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat}; use crate::resource_thread::CoreResourceThreadPool; +// We bake in rippy.png as a fallback, in case the embedder does not provide +// a rippy resource. this version is 253 bytes large, don't exchange it against +// something in higher resolution. +const FALLBACK_RIPPY: &[u8] = include_bytes!("../../resources/rippy.png"); + // // TODO(gw): Remaining work on image cache: // * Make use of the prefetch support in various parts of the code. @@ -51,7 +56,9 @@ fn decode_bytes_sync(key: LoadKey, bytes: &[u8], cors: CorsStatus) -> DecoderMsg } fn get_placeholder_image(compositor_api: &CrossProcessCompositorApi, data: &[u8]) -> Arc { - let mut image = load_from_memory(data, CorsStatus::Unsafe).unwrap(); + let mut image = load_from_memory(data, CorsStatus::Unsafe) + .or_else(|| load_from_memory(FALLBACK_RIPPY, CorsStatus::Unsafe)) + .expect("load fallback image failed"); set_webrender_image_key(compositor_api, &mut image); Arc::new(image) } diff --git a/components/shared/embedder/Cargo.toml b/components/shared/embedder/Cargo.toml index 1dfe363a93b..d938169d37a 100644 --- a/components/shared/embedder/Cargo.toml +++ b/components/shared/embedder/Cargo.toml @@ -11,6 +11,11 @@ rust-version.workspace = true name = "embedder_traits" path = "lib.rs" +[features] +# bakes default resources into the library. +# This feature is mainly intended for testing purposes. +baked-default-resources = [] + [dependencies] base = { workspace = true } cfg-if = { workspace = true } diff --git a/components/shared/embedder/resources.rs b/components/shared/embedder/resources.rs index a5c34a33f30..29e64bf9629 100644 --- a/components/shared/embedder/resources.rs +++ b/components/shared/embedder/resources.rs @@ -3,30 +3,42 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::path::PathBuf; -use std::sync::{LazyLock, RwLock}; +use std::sync::RwLock; -use cfg_if::cfg_if; +static RES: RwLock>> = RwLock::new(None); -static RES: LazyLock>>> = - LazyLock::new(|| { - cfg_if! { - if #[cfg(any(servo_production, target_env = "ohos"))] { - RwLock::new(None) - } else { - // Static assert that this is really a non-production build, rather - // than a failure of the build script’s production check. - const _: () = assert!(cfg!(servo_do_not_use_in_production)); +#[cfg(feature = "baked-default-resources")] +static INIT_TEST_RESOURCES: std::sync::Once = std::sync::Once::new(); - RwLock::new(Some(resources_for_tests())) - } - } - }); +#[cfg(all(feature = "baked-default-resources", servo_production))] +const _: () = assert!( + false, + "baked-default-resources should not be used in production" +); +/// The Embedder should initialize the ResourceReader early. pub fn set(reader: Box) { *RES.write().unwrap() = Some(reader); } +#[cfg(not(feature = "baked-default-resources"))] pub fn read_bytes(res: Resource) -> Vec { + if let Some(reader) = RES.read().unwrap().as_ref() { + reader.read(res) + } else { + log::error!("Resource reader not set."); + vec![] + } +} + +#[cfg(feature = "baked-default-resources")] +pub fn read_bytes(res: Resource) -> Vec { + INIT_TEST_RESOURCES.call_once(|| { + let mut reader = RES.write().unwrap(); + if reader.is_none() { + *reader = Some(resources_for_tests()) + } + }); RES.read() .unwrap() .as_ref() @@ -42,16 +54,16 @@ pub fn sandbox_access_files() -> Vec { RES.read() .unwrap() .as_ref() - .expect("Resource reader not set.") - .sandbox_access_files() + .map(|reader| reader.sandbox_access_files()) + .unwrap_or_default() } pub fn sandbox_access_files_dirs() -> Vec { RES.read() .unwrap() .as_ref() - .expect("Resource reader not set.") - .sandbox_access_files_dirs() + .map(|reader| reader.sandbox_access_files_dirs()) + .unwrap_or_default() } pub enum Resource { @@ -93,10 +105,10 @@ pub enum Resource { QuirksModeCSS, /// A placeholder image to display if we couldn't get the requested image. /// - /// ## Safety + /// ## Panic /// - /// Servo will crash if this is an invalid image. Check `resources/rippy.png` in Servo codebase to see what - /// a default rippy png should look like. + /// If the resource is not provided, servo will fallback to a baked in default (See resources/rippy.png). + /// However, if the image is provided but invalid, Servo will crash. RippyPNG, /// A CSS file to style the media controls. /// It can be empty but then media controls will not be styled. @@ -145,12 +157,10 @@ pub trait ResourceReaderMethods { fn sandbox_access_files_dirs(&self) -> Vec; } -/// Bake all of our resources into this crate for tests, unless we are `cfg!(servo_production)`. +/// Provides baked in resources for tests. /// -/// Local non-production embedder builds (e.g. servoshell) can still override these with [`set`]. -/// On OpenHarmony we never want to include files, since we ship all the files in the application -/// bundle anyway. -#[cfg(not(any(servo_production, target_env = "ohos")))] +/// Embedder builds (e.g. servoshell) should use [`set`] and ship the resources themselves. +#[cfg(feature = "baked-default-resources")] fn resources_for_tests() -> Box { struct ResourceReader; impl ResourceReaderMethods for ResourceReader { diff --git a/components/shared/net/Cargo.toml b/components/shared/net/Cargo.toml index 2c0a4995ec4..79ea936a688 100644 --- a/components/shared/net/Cargo.toml +++ b/components/shared/net/Cargo.toml @@ -40,3 +40,6 @@ servo_rand = { path = "../../rand" } servo_url = { path = "../../url" } url = { workspace = true } uuid = { workspace = true } + +[dev-dependencies] +embedder_traits = { workspace = true, features = ["baked-default-resources"] }