Skip to main content

stylo_static_prefs/
lib.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::collections::HashMap;
6use std::sync::{LazyLock, RwLock};
7
8/// Returns the default value for a preference exposed to the style crate.
9/// This is what will be used if the embedder has not set the preference.
10#[macro_export]
11macro_rules! default_value {
12    ("dom.select.customizable_select.enabled") => {
13        false
14    };
15    ("dom.viewTransitions.cross-document.enabled") => {
16        false
17    };
18    ("layout.columns.enabled") => {
19        false
20    };
21    ("layout.container-queries.enabled") => {
22        false
23    };
24    ("layout.css.anchor-positioning.enabled") => {
25        false
26    };
27    ("layout.css.appearance-base.enabled") => {
28        false
29    };
30    ("layout.css.at-scope.enabled") => {
31        false
32    };
33    ("layout.css.attr.enabled") => {
34        false
35    };
36    ("layout.css.basic-shape-shape.enabled") => {
37        false
38    };
39    ("layout.css.color-mix-multi-color.enabled") => {
40        true
41    };
42    ("layout.css.content.alt-text.enabled") => {
43        false
44    };
45    ("layout.css.contrast-color.enabled") => {
46        true
47    };
48    ("layout.css.custom-media.enabled") => {
49        false
50    };
51    ("layout.css.fit-content-function.enabled") => {
52        true
53    };
54    ("layout.css.font-palette.enabled") => {
55        false
56    };
57    ("layout.css.font-tech.enabled") => {
58        false
59    };
60    ("layout.css.font-variations.enabled") => {
61        true
62    };
63    ("layout.css.gradient-color-interpolation-method.enabled") => {
64        true
65    };
66    ("layout.css.light-dark.images.enabled") => {
67        false
68    };
69    ("layout.css.margin-rules.enabled") => {
70        false
71    };
72    ("layout.css.marker.restricted") => {
73        true
74    };
75    ("layout.css.motion-path-url.enabled") => {
76        false
77    };
78    ("layout.css.outline-offset.snapping") => {
79        1
80    };
81    ("layout.css.properties-and-values.enabled") => {
82        true
83    };
84    ("layout.css.relative-color-syntax.enabled") => {
85        true
86    };
87    ("layout.css.revert-rule.enabled") => {
88        true
89    };
90    ("layout.css.scroll-driven-animations.enabled") => {
91        false
92    };
93    ("layout.css.scroll-state.enabled") => {
94        false
95    };
96    ("layout.css.starting-style-at-rules.enabled") => {
97        false
98    };
99    ("layout.css.stretch-size-keyword.enabled") => {
100        true
101    };
102    ("layout.css.style-queries.enabled") => {
103        false
104    };
105    ("layout.css.stylo-local-work-queue.in-main-thread") => {
106        32
107    };
108    ("layout.css.stylo-local-work-queue.in-worker") => {
109        0
110    };
111    ("layout.css.stylo-work-unit-size") => {
112        16
113    };
114    ("layout.css.system-ui.enabled") => {
115        true
116    };
117    ("layout.css.webkit-fill-available.all-size-properties.enabled") => {
118        true
119    };
120    ("layout.css.webkit-fill-available.enabled") => {
121        true
122    };
123    ("layout.grid.enabled") => {
124        false
125    };
126    ("layout.threads") => {
127        // Negative means auto, 0 disables the thread-pool (main-thread styling),
128        // other numbers override as specified.
129        -1
130    };
131    ("layout.unimplemented") => {
132        false
133    };
134    ("layout.variable_fonts.enabled") => {
135        false
136    };
137    ("layout.writing-mode.enabled") => {
138        false
139    };
140}
141
142/// Returns the value of a preference exposed to the style crate. If the embedder
143/// has not set a value for it, this returns the default value of the preference.
144#[macro_export]
145macro_rules! pref {
146    ($string:tt) => {
147        $crate::Preference::get($string, $crate::default_value!($string))
148    };
149}
150
151/// Sets a preference to the provided value.
152#[macro_export]
153macro_rules! set_pref {
154    ($string:tt, $($value:expr)+) => {
155        let value = $($value)+;
156        // This comparison ensures that the provided value belongs to the expected type.
157        let _ = $crate::default_value!($string) == value;
158        $crate::Preference::set($string, value)
159    };
160}
161
162static PREFS: LazyLock<Preferences> = LazyLock::new(Preferences::default);
163
164#[derive(Debug, Default)]
165struct Preferences {
166    bool_prefs: RwLock<HashMap<String, bool>>,
167    i32_prefs: RwLock<HashMap<String, i32>>,
168}
169
170impl Preferences {
171    pub fn get_bool(&self, key: &str, default: bool) -> bool {
172        let prefs = self.bool_prefs.read().expect("RwLock is poisoned");
173        *prefs.get(key).unwrap_or(&default)
174    }
175
176    pub fn get_i32(&self, key: &str, default: i32) -> i32 {
177        let prefs = self.i32_prefs.read().expect("RwLock is poisoned");
178        *prefs.get(key).unwrap_or(&default)
179    }
180
181    pub fn set_bool(&self, key: &str, value: bool) {
182        let mut prefs = self.bool_prefs.write().expect("RwLock is poisoned");
183
184        // Avoid cloning the key if it exists.
185        if let Some(pref) = prefs.get_mut(key) {
186            *pref = value;
187        } else {
188            prefs.insert(key.to_owned(), value);
189        }
190    }
191
192    pub fn set_i32(&self, key: &str, value: i32) {
193        let mut prefs = self.i32_prefs.write().expect("RwLock is poisoned");
194
195        // Avoid cloning the key if it exists.
196        if let Some(pref) = prefs.get_mut(key) {
197            *pref = value;
198        } else {
199            prefs.insert(key.to_owned(), value);
200        }
201    }
202}
203
204pub trait Preference: Sized {
205    /// Gets the value of a preference, falling back to the provided default.
206    /// Prefer using [`pref!()`] instead, since it ensures that the preference
207    /// exists and uses the correct default automatically.
208    fn get(key: &str, default: Self) -> Self;
209
210    /// Sets a preference to the provided value. Prefer using [`set_pref!()`]
211    /// instead, since it ensures that the preference exists and the value
212    /// belongs to the expected type.
213    fn set(key: &str, value: Self);
214}
215
216impl Preference for bool {
217    fn get(key: &str, default: Self) -> Self {
218        PREFS.get_bool(key, default)
219    }
220    fn set(key: &str, value: Self) {
221        PREFS.set_bool(key, value)
222    }
223}
224
225impl Preference for i32 {
226    fn get(key: &str, default: Self) -> Self {
227        PREFS.get_i32(key, default)
228    }
229    fn set(key: &str, value: Self) {
230        PREFS.set_i32(key, value)
231    }
232}
233
234#[test]
235fn test() {
236    let prefs = Preferences::default();
237
238    // We get the default value when the pref is not set.
239    assert_eq!(prefs.get_bool("foo", false), false);
240    assert_eq!(prefs.get_bool("foo", true), true);
241    assert_eq!(prefs.get_i32("bar", 0), 0);
242    assert_eq!(prefs.get_i32("bar", 1), 1);
243    assert_eq!(prefs.get_i32("bar", 2), 2);
244
245    // Prefs can be set and retrieved.
246    prefs.set_bool("foo", true);
247    prefs.set_i32("bar", 1);
248    assert_eq!(prefs.get_bool("foo", false), true);
249    assert_eq!(prefs.get_bool("foo", true), true);
250    assert_eq!(prefs.get_i32("bar", 0), 1);
251    assert_eq!(prefs.get_i32("bar", 1), 1);
252    assert_eq!(prefs.get_i32("bar", 2), 1);
253    prefs.set_bool("foo", false);
254    prefs.set_i32("bar", 2);
255    assert_eq!(prefs.get_bool("foo", false), false);
256    assert_eq!(prefs.get_bool("foo", true), false);
257    assert_eq!(prefs.get_i32("bar", 0), 2);
258    assert_eq!(prefs.get_i32("bar", 1), 2);
259    assert_eq!(prefs.get_i32("bar", 2), 2);
260
261    // Each value type currently has an independent namespace.
262    prefs.set_i32("foo", 3);
263    prefs.set_bool("bar", true);
264    assert_eq!(prefs.get_i32("foo", 0), 3);
265    assert_eq!(prefs.get_bool("foo", false), false);
266    assert_eq!(prefs.get_bool("bar", false), true);
267    assert_eq!(prefs.get_i32("bar", 0), 2);
268}