mirror of
https://github.com/servo/servo
synced 2026-04-25 17:15:48 +02:00
This PR considers the following constraints: - Resources must be available when building servo via a published crates.io package (i.e. no `../../../resources/<file>` file references). - Minimal setup when writing tests (`nextest` spawns each test in its own process, so we don't want to explicitly initialize the resource handler for every `#[test]` fn) - Use local resources when developing locally - Support loading the resources from a proper resource directory if the embedder wishes so, including via a custom mechanism, not necessarily as files (File) Resources that are only accessed from servoshell are out of scope of this PR, since it mainly focusses on unblocking publishing `libservo` to crates.io. Baking the resources into the binary by default simplifies the setup a lot. We already supported that before, but only for testing purposes and explicitly not for production builds. Using [`inventory`](https://crates.io/crates/inventory) adds a simple way for the embedder to replace the default baked in resources, while also keeping the test usage of baked in resources simple. rippy.png is also referenced from image_cache - We simply duplicate it, since the image is small, to avoid adding unnecessarily complex solutions like adding a dedicated crate. Testing: Covered by existing tests. [mach try full](https://github.com/jschwe/servo/actions/runs/23811669469) Fixes: Part of #43145 --------- Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
176 lines
7.3 KiB
Rust
176 lines
7.3 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
use std::path::PathBuf;
|
|
use std::sync::LazyLock;
|
|
|
|
#[doc(hidden)]
|
|
pub use inventory as _inventory;
|
|
|
|
/// A static reference to a ResourceReader
|
|
///
|
|
/// If you need to initialize the resource reader at runtime, use interior mutability.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// pub(crate) struct ResourceReaderImpl {
|
|
/// resource_dir: OnceLock<PathBuf>,
|
|
/// }
|
|
/// static RESOURCE_READER: ResourceReaderImpl = ResourceReaderImpl {
|
|
/// resource_dir: OnceLock::new(),
|
|
/// };
|
|
///
|
|
/// servo::submit_resource_reader!(&RESOURCE_READER);
|
|
///
|
|
/// /// This can be called during initialization, e.g. after parsing commandline flags.
|
|
/// pub(crate) fn set_resource_dir(resource_dir: PathBuf) {
|
|
/// RESOURCE_READER.resource_dir.set(resource_dir).expect("Already initialized.")
|
|
/// }
|
|
/// impl ResourceReaderMethods for ResourceReaderImpl {
|
|
/// //
|
|
/// }
|
|
/// ```
|
|
pub type ResourceReader = &'static (dyn ResourceReaderMethods + Sync + Send);
|
|
|
|
/// Register the [`ResourceReader`] implementation.
|
|
///
|
|
/// This should be added at most once in the whole project.
|
|
/// In particular this means you should make sure to disable the (default)
|
|
/// `baked-in-resources` feature of servo if you want to override the default reader.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// Put `submit_resource_reader` invocations **outside** any function body:
|
|
/// ```
|
|
/// servo_embedder_traits::submit_resource_reader!(my_resource_reader);
|
|
/// ```
|
|
#[macro_export]
|
|
macro_rules! submit_resource_reader {
|
|
($resource_reader:expr) => {
|
|
$crate::resources::_inventory::submit! {
|
|
$resource_reader as $crate::resources::ResourceReader
|
|
}
|
|
};
|
|
}
|
|
|
|
// The embedder may register a resource reader via `submit_resource_reader!()`
|
|
// Note: A weak symbol would perhaps be preferable, but that isn't available in stable rust yet.
|
|
inventory::collect!(ResourceReader);
|
|
|
|
static RESOURCE_READER: LazyLock<ResourceReader> = {
|
|
LazyLock::new(|| {
|
|
let mut resource_reader_iterator = inventory::iter::<ResourceReader>.into_iter();
|
|
let Some(resource_reader) = resource_reader_iterator.next() else {
|
|
panic!("No resource reader registered");
|
|
};
|
|
if resource_reader_iterator.next().is_some() {
|
|
log::error!(
|
|
"Multiple resource readers registered. Taking the first implementation \
|
|
(random, non deterministic order). This is a bug! Check usages of \
|
|
`submit_resource_reader!()`. Perhaps you meant to disable the default resource reader \
|
|
(selected by depending on the `servo-default-resources` crate) ?"
|
|
);
|
|
}
|
|
*resource_reader
|
|
})
|
|
};
|
|
|
|
pub fn read_bytes(res: Resource) -> Vec<u8> {
|
|
RESOURCE_READER.read(res)
|
|
}
|
|
|
|
pub fn read_string(res: Resource) -> String {
|
|
String::from_utf8(read_bytes(res)).unwrap()
|
|
}
|
|
|
|
pub fn sandbox_access_files() -> Vec<PathBuf> {
|
|
RESOURCE_READER.sandbox_access_files()
|
|
}
|
|
|
|
pub fn sandbox_access_files_dirs() -> Vec<PathBuf> {
|
|
RESOURCE_READER.sandbox_access_files_dirs()
|
|
}
|
|
|
|
pub enum Resource {
|
|
/// A list of GATT services that are blocked from being used by web bluetooth.
|
|
/// The format of the file is a list of UUIDs, one per line, with an optional second word to specify the
|
|
/// type of blocklist.
|
|
/// It can be empty but then all GATT services will be allowed.
|
|
BluetoothBlocklist,
|
|
/// A list of domain names that are considered public suffixes, typically obtained from <https://publicsuffix.org/list/>.
|
|
/// The Public Suffix List is a cross-vendor initiative to provide an accurate list of domain name suffixes
|
|
/// that are under the control of a registry. This is used to prevent cookies from being set for top-level
|
|
/// domains that are not controlled by the same entity as the website.
|
|
/// It can be empty but all domain names will be considered not public suffixes.
|
|
DomainList,
|
|
/// A preloaded list of HTTP Strict Transport Security. It can be an empty list and
|
|
/// `HstsList::default()` will be called.
|
|
HstsPreloadList,
|
|
/// A HTML page to display when `net_traits::NetworkError::SslValidation` network error is
|
|
/// reported.
|
|
/// The page contains placeholder `${reason}` for the error code and `${bytes}` for the certificate bytes,
|
|
/// and also `${secret}` for the privileged secret.
|
|
/// It can be empty but then nothing will be displayed when a certificate error occurs.
|
|
BadCertHTML,
|
|
/// A HTML page to display when any network error occurs that is not related to SSL validation.
|
|
/// The message can contain a placeholder `${reason}` for the error code.
|
|
/// It can be empty but then nothing will be displayed when an internal error occurs.
|
|
NetErrorHTML,
|
|
/// A placeholder image to display if we couldn't get the requested image.
|
|
///
|
|
/// ## Panic
|
|
///
|
|
/// 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.
|
|
BrokenImageIcon,
|
|
/// A placeholder HTML page to display when the code responsible for rendering a page panics and the original
|
|
/// page can no longer be displayed.
|
|
/// The message can contain a placeholder `${details}` for the error details.
|
|
/// It can be empty but then nothing will be displayed when a crash occurs.
|
|
CrashHTML,
|
|
/// A HTML page to display when a directory listing is requested.
|
|
/// The page contains a js function `setData` that will then be used to build the list of directory.
|
|
/// It can be empty but then nothing will be displayed when a directory listing is requested.
|
|
DirectoryListingHTML,
|
|
/// A HTML page that is used for the about:memory url.
|
|
AboutMemoryHTML,
|
|
/// RPC script for the Debugger API on behalf of devtools.
|
|
DebuggerJS,
|
|
}
|
|
|
|
impl Resource {
|
|
pub fn filename(&self) -> &'static str {
|
|
match self {
|
|
Resource::BluetoothBlocklist => "gatt_blocklist.txt",
|
|
Resource::DomainList => "public_domains.txt",
|
|
Resource::HstsPreloadList => "hsts_preload.fstmap",
|
|
Resource::BadCertHTML => "badcert.html",
|
|
Resource::NetErrorHTML => "neterror.html",
|
|
Resource::BrokenImageIcon => "rippy.png",
|
|
Resource::CrashHTML => "crash.html",
|
|
Resource::DirectoryListingHTML => "directory-listing.html",
|
|
Resource::AboutMemoryHTML => "about-memory.html",
|
|
Resource::DebuggerJS => "debugger.js",
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait ResourceReaderMethods {
|
|
/// Read a named [`Resource`].
|
|
///
|
|
/// The implementation must be functional in all Servo processes.
|
|
fn read(&self, res: Resource) -> Vec<u8>;
|
|
/// Files that should remain accessible after sandboxing the content process.
|
|
///
|
|
/// If the resources are shipped as files, then the files should be listed here,
|
|
/// or the parent directory in [sandbox_access_files_dirs].
|
|
fn sandbox_access_files(&self) -> Vec<PathBuf>;
|
|
/// Directories that should remain accessible after sandboxing the content process.
|
|
///
|
|
/// If resources are shipped as files, then the directory containing them be listed
|
|
/// here to ensure the content process can access the files.
|
|
fn sandbox_access_files_dirs(&self) -> Vec<PathBuf>;
|
|
}
|