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}