embedder_traits/
resources.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::path::PathBuf;
6use std::sync::LazyLock;
7
8#[doc(hidden)]
9pub use inventory as _inventory;
10
11/// A static reference to a ResourceReader
12///
13/// If you need to initialize the resource reader at runtime, use interior mutability.
14///
15/// # Examples
16///
17/// ```
18/// pub(crate) struct ResourceReaderImpl {
19///     resource_dir: OnceLock<PathBuf>,
20/// }
21/// static RESOURCE_READER: ResourceReaderImpl = ResourceReaderImpl {
22///     resource_dir: OnceLock::new(),
23/// };
24///
25/// servo::submit_resource_reader!(&RESOURCE_READER);
26///
27/// /// This can be called during initialization, e.g. after parsing commandline flags.
28/// pub(crate) fn set_resource_dir(resource_dir: PathBuf) {
29///     RESOURCE_READER.resource_dir.set(resource_dir).expect("Already initialized.")
30/// }
31/// impl ResourceReaderMethods for ResourceReaderImpl {
32///  //
33/// }
34/// ```
35pub type ResourceReader = &'static (dyn ResourceReaderMethods + Sync + Send);
36
37/// Register the [`ResourceReader`] implementation.
38///
39/// This should be added at most once in the whole project.
40/// In particular this means you should make sure to disable the (default)
41/// `baked-in-resources` feature of servo if you want to override the default reader.
42///
43/// # Examples
44///
45/// Put `submit_resource_reader` invocations **outside** any function body:
46/// ```
47/// servo_embedder_traits::submit_resource_reader!(my_resource_reader);
48/// ```
49#[macro_export]
50macro_rules! submit_resource_reader {
51    ($resource_reader:expr) => {
52        $crate::resources::_inventory::submit! {
53            $resource_reader as $crate::resources::ResourceReader
54        }
55    };
56}
57
58// The embedder may register a resource reader via `submit_resource_reader!()`
59// Note: A weak symbol would perhaps be preferable, but that isn't available in stable rust yet.
60inventory::collect!(ResourceReader);
61
62static RESOURCE_READER: LazyLock<ResourceReader> = {
63    LazyLock::new(|| {
64        let mut resource_reader_iterator = inventory::iter::<ResourceReader>.into_iter();
65        let Some(resource_reader) = resource_reader_iterator.next() else {
66            panic!("No resource reader registered");
67        };
68        if resource_reader_iterator.next().is_some() {
69            log::error!(
70                "Multiple resource readers registered. Taking the first implementation \
71                (random, non deterministic order). This is a bug! Check usages of \
72                `submit_resource_reader!()`. Perhaps you meant to disable the default resource reader \
73                (selected by depending on the `servo-default-resources` crate) ?"
74            );
75        }
76        *resource_reader
77    })
78};
79
80pub fn read_bytes(res: Resource) -> Vec<u8> {
81    RESOURCE_READER.read(res)
82}
83
84pub fn read_string(res: Resource) -> String {
85    String::from_utf8(read_bytes(res)).unwrap()
86}
87
88pub fn sandbox_access_files() -> Vec<PathBuf> {
89    RESOURCE_READER.sandbox_access_files()
90}
91
92pub fn sandbox_access_files_dirs() -> Vec<PathBuf> {
93    RESOURCE_READER.sandbox_access_files_dirs()
94}
95
96pub enum Resource {
97    /// A list of GATT services that are blocked from being used by web bluetooth.
98    /// The format of the file is a list of UUIDs, one per line, with an optional second word to specify the
99    /// type of blocklist.
100    /// It can be empty but then all GATT services will be allowed.
101    BluetoothBlocklist,
102    /// A list of domain names that are considered public suffixes, typically obtained from <https://publicsuffix.org/list/>.
103    /// The Public Suffix List is a cross-vendor initiative to provide an accurate list of domain name suffixes
104    /// that are under the control of a registry. This is used to prevent cookies from being set for top-level
105    /// domains that are not controlled by the same entity as the website.
106    /// It can be empty but all domain names will be considered not public suffixes.
107    DomainList,
108    /// A preloaded list of HTTP Strict Transport Security. It can be an empty list and
109    /// `HstsList::default()` will be called.
110    HstsPreloadList,
111    /// A HTML page to display when `net_traits::NetworkError::SslValidation` network error is
112    /// reported.
113    /// The page contains placeholder `${reason}` for the error code and `${bytes}` for the certificate bytes,
114    /// and also `${secret}` for the privileged secret.
115    /// It can be empty but then nothing will be displayed when a certificate error occurs.
116    BadCertHTML,
117    /// A HTML page to display when any network error occurs that is not related to SSL validation.
118    /// The message can contain a placeholder `${reason}` for the error code.
119    /// It can be empty but then nothing will be displayed when an internal error occurs.
120    NetErrorHTML,
121    /// A placeholder image to display if we couldn't get the requested image.
122    ///
123    /// ## Panic
124    ///
125    /// If the resource is not provided, servo will fallback to a baked in default (See resources/rippy.png).
126    /// However, if the image is provided but invalid, Servo will crash.
127    BrokenImageIcon,
128    /// A placeholder HTML page to display when the code responsible for rendering a page panics and the original
129    /// page can no longer be displayed.
130    /// The message can contain a placeholder `${details}` for the error details.
131    /// It can be empty but then nothing will be displayed when a crash occurs.
132    CrashHTML,
133    /// A HTML page to display when a directory listing is requested.
134    /// The page contains a js function `setData` that will then be used to build the list of directory.
135    /// It can be empty but then nothing will be displayed when a directory listing is requested.
136    DirectoryListingHTML,
137    /// A HTML page that is used for the about:memory url.
138    AboutMemoryHTML,
139    /// RPC script for the Debugger API on behalf of devtools.
140    DebuggerJS,
141}
142
143impl Resource {
144    pub fn filename(&self) -> &'static str {
145        match self {
146            Resource::BluetoothBlocklist => "gatt_blocklist.txt",
147            Resource::DomainList => "public_domains.txt",
148            Resource::HstsPreloadList => "hsts_preload.fstmap",
149            Resource::BadCertHTML => "badcert.html",
150            Resource::NetErrorHTML => "neterror.html",
151            Resource::BrokenImageIcon => "rippy.png",
152            Resource::CrashHTML => "crash.html",
153            Resource::DirectoryListingHTML => "directory-listing.html",
154            Resource::AboutMemoryHTML => "about-memory.html",
155            Resource::DebuggerJS => "debugger.js",
156        }
157    }
158}
159
160pub trait ResourceReaderMethods {
161    /// Read a named [`Resource`].
162    ///
163    /// The implementation must be functional in all Servo processes.
164    fn read(&self, res: Resource) -> Vec<u8>;
165    /// Files that should remain accessible after sandboxing the content process.
166    ///
167    /// If the resources are shipped as files, then the files should be listed here,
168    /// or the parent directory in [sandbox_access_files_dirs].
169    fn sandbox_access_files(&self) -> Vec<PathBuf>;
170    /// Directories that should remain accessible after sandboxing the content process.
171    ///
172    /// If resources are shipped as files, then the directory containing them be listed
173    /// here to ensure the content process can access the files.
174    fn sandbox_access_files_dirs(&self) -> Vec<PathBuf>;
175}