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::RwLock;
7
8static RES: RwLock<Option<Box<dyn ResourceReaderMethods + Sync + Send>>> = RwLock::new(None);
9
10#[cfg(feature = "baked-default-resources")]
11static INIT_TEST_RESOURCES: std::sync::Once = std::sync::Once::new();
12
13#[cfg(all(feature = "baked-default-resources", servo_production))]
14const _: () = assert!(
15    false,
16    "baked-default-resources should not be used in production"
17);
18
19/// The Embedder should initialize the ResourceReader early.
20pub fn set(reader: Box<dyn ResourceReaderMethods + Sync + Send>) {
21    *RES.write().unwrap() = Some(reader);
22}
23
24#[cfg(not(feature = "baked-default-resources"))]
25pub fn read_bytes(res: Resource) -> Vec<u8> {
26    if let Some(reader) = RES.read().unwrap().as_ref() {
27        reader.read(res)
28    } else {
29        log::error!("Resource reader not set.");
30        vec![]
31    }
32}
33
34#[cfg(feature = "baked-default-resources")]
35pub fn read_bytes(res: Resource) -> Vec<u8> {
36    INIT_TEST_RESOURCES.call_once(|| {
37        let mut reader = RES.write().unwrap();
38        if reader.is_none() {
39            *reader = Some(resources_for_tests())
40        }
41    });
42    RES.read()
43        .unwrap()
44        .as_ref()
45        .expect("Resource reader not set.")
46        .read(res)
47}
48
49pub fn read_string(res: Resource) -> String {
50    String::from_utf8(read_bytes(res)).unwrap()
51}
52
53pub fn sandbox_access_files() -> Vec<PathBuf> {
54    RES.read()
55        .unwrap()
56        .as_ref()
57        .map(|reader| reader.sandbox_access_files())
58        .unwrap_or_default()
59}
60
61pub fn sandbox_access_files_dirs() -> Vec<PathBuf> {
62    RES.read()
63        .unwrap()
64        .as_ref()
65        .map(|reader| reader.sandbox_access_files_dirs())
66        .unwrap_or_default()
67}
68
69pub enum Resource {
70    /// A list of GATT services that are blocked from being used by web bluetooth.
71    /// The format of the file is a list of UUIDs, one per line, with an optional second word to specify the
72    /// type of blocklist.
73    /// It can be empty but then all GATT services will be allowed.
74    BluetoothBlocklist,
75    /// A list of domain names that are considered public suffixes, typically obtained from <https://publicsuffix.org/list/>.
76    /// The Public Suffix List is a cross-vendor initiative to provide an accurate list of domain name suffixes
77    /// that are under the control of a registry. This is used to prevent cookies from being set for top-level
78    /// domains that are not controlled by the same entity as the website.
79    /// It can be empty but all domain names will be considered not public suffixes.
80    DomainList,
81    /// A preloaded list of HTTP Strict Transport Security. It can be an empty list and
82    /// `HstsList::default()` will be called.
83    HstsPreloadList,
84    /// A HTML page to display when `net_traits::NetworkError::SslValidation` network error is
85    /// reported.
86    /// The page contains placeholder `${reason}` for the error code and `${bytes}` for the certificate bytes,
87    /// and also `${secret}` for the privileged secret.
88    /// It can be empty but then nothing will be displayed when a certificate error occurs.
89    BadCertHTML,
90    /// A HTML page to display when any network error occurs that is not related to SSL validation.
91    /// The message can contain a placeholder `${reason}` for the error code.
92    /// It can be empty but then nothing will be displayed when an internal error occurs.
93    NetErrorHTML,
94    /// A placeholder image to display if we couldn't get the requested image.
95    ///
96    /// ## Panic
97    ///
98    /// If the resource is not provided, servo will fallback to a baked in default (See resources/rippy.png).
99    /// However, if the image is provided but invalid, Servo will crash.
100    RippyPNG,
101    /// A placeholder HTML page to display when the code responsible for rendering a page panics and the original
102    /// page can no longer be displayed.
103    /// The message can contain a placeholder `${details}` for the error details.
104    /// It can be empty but then nothing will be displayed when a crash occurs.
105    CrashHTML,
106    /// A HTML page to display when a directory listing is requested.
107    /// The page contains a js function `setData` that will then be used to build the list of directory.
108    /// It can be empty but then nothing will be displayed when a directory listing is requested.
109    DirectoryListingHTML,
110    /// A HTML page that is used for the about:memory url.
111    AboutMemoryHTML,
112    /// RPC script for the Debugger API on behalf of devtools.
113    DebuggerJS,
114}
115
116impl Resource {
117    pub fn filename(&self) -> &'static str {
118        match self {
119            Resource::BluetoothBlocklist => "gatt_blocklist.txt",
120            Resource::DomainList => "public_domains.txt",
121            Resource::HstsPreloadList => "hsts_preload.fstmap",
122            Resource::BadCertHTML => "badcert.html",
123            Resource::NetErrorHTML => "neterror.html",
124            Resource::RippyPNG => "rippy.png",
125            Resource::CrashHTML => "crash.html",
126            Resource::DirectoryListingHTML => "directory-listing.html",
127            Resource::AboutMemoryHTML => "about-memory.html",
128            Resource::DebuggerJS => "debugger.js",
129        }
130    }
131}
132
133pub trait ResourceReaderMethods {
134    fn read(&self, res: Resource) -> Vec<u8>;
135    fn sandbox_access_files(&self) -> Vec<PathBuf>;
136    fn sandbox_access_files_dirs(&self) -> Vec<PathBuf>;
137}
138
139/// Provides baked in resources for tests.
140///
141/// Embedder builds (e.g. servoshell) should use [`set`] and ship the resources themselves.
142#[cfg(feature = "baked-default-resources")]
143fn resources_for_tests() -> Box<dyn ResourceReaderMethods + Sync + Send> {
144    struct ResourceReader;
145    impl ResourceReaderMethods for ResourceReader {
146        fn sandbox_access_files(&self) -> Vec<PathBuf> {
147            vec![]
148        }
149        fn sandbox_access_files_dirs(&self) -> Vec<PathBuf> {
150            vec![]
151        }
152        fn read(&self, file: Resource) -> Vec<u8> {
153            match file {
154                Resource::BluetoothBlocklist => {
155                    &include_bytes!("../../../resources/gatt_blocklist.txt")[..]
156                },
157                Resource::DomainList => {
158                    &include_bytes!("../../../resources/public_domains.txt")[..]
159                },
160                Resource::HstsPreloadList => {
161                    &include_bytes!("../../../resources/hsts_preload.fstmap")[..]
162                },
163                Resource::BadCertHTML => &include_bytes!("../../../resources/badcert.html")[..],
164                Resource::NetErrorHTML => &include_bytes!("../../../resources/neterror.html")[..],
165                Resource::RippyPNG => &include_bytes!("../../../resources/rippy.png")[..],
166                Resource::CrashHTML => &include_bytes!("../../../resources/crash.html")[..],
167                Resource::DirectoryListingHTML => {
168                    &include_bytes!("../../../resources/directory-listing.html")[..]
169                },
170                Resource::AboutMemoryHTML => {
171                    &include_bytes!("../../../resources/about-memory.html")[..]
172                },
173                Resource::DebuggerJS => &include_bytes!("../../../resources/debugger.js")[..],
174            }
175            .to_owned()
176        }
177    }
178    Box::new(ResourceReader)
179}