servo_config/
opts.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
5//! Options are global configuration options that are initialized once and cannot be changed at
6//! runtime.
7
8use std::default::Default;
9use std::path::PathBuf;
10use std::process;
11use std::sync::OnceLock;
12
13use serde::{Deserialize, Serialize};
14
15/// The set of global options supported by Servo. The values for these can be configured during
16/// initialization of Servo and cannot be changed later at runtime.
17#[derive(Clone, Debug, Deserialize, Serialize)]
18pub struct Opts {
19    /// `None` to disable the time profiler or `Some` to enable it with either:
20    ///
21    ///  - an interval in seconds to cause it to produce output on that interval.
22    ///  - a file path to write profiling info to a TSV file upon Servo's termination.
23    pub time_profiling: Option<OutputOptions>,
24
25    /// When the profiler is enabled, this is an optional path to dump a self-contained HTML file
26    /// visualizing the traces as a timeline.
27    pub time_profiler_trace_path: Option<String>,
28
29    /// True to exit on thread failure instead of displaying about:failure.
30    pub hard_fail: bool,
31
32    /// Debug options that are used by developers to control Servo
33    /// behavior for debugging purposes.
34    pub debug: DiagnosticsLogging,
35
36    /// Whether we're running in multiprocess mode.
37    pub multiprocess: bool,
38
39    /// Whether to force using ipc_channel instead of crossbeam_channel in singleprocess mode. Does
40    /// nothing in multiprocess mode.
41    pub force_ipc: bool,
42
43    /// Whether we want background hang monitor enabled or not
44    pub background_hang_monitor: bool,
45
46    /// Whether we're running inside the sandbox.
47    pub sandbox: bool,
48
49    /// Probability of randomly closing a pipeline,
50    /// used for testing the hardening of the constellation.
51    pub random_pipeline_closure_probability: Option<f32>,
52
53    /// The seed for the RNG used to randomly close pipelines,
54    /// used for testing the hardening of the constellation.
55    pub random_pipeline_closure_seed: Option<usize>,
56
57    /// Load shaders from disk.
58    pub shaders_path: Option<PathBuf>,
59
60    /// Directory for a default config directory
61    pub config_dir: Option<PathBuf>,
62
63    /// Path to PEM encoded SSL CA certificate store.
64    pub certificate_path: Option<String>,
65
66    /// Whether or not to completely ignore SSL certificate validation errors.
67    /// TODO: We should see if we can eliminate the need for this by fixing
68    /// <https://github.com/servo/servo/issues/30080>.
69    pub ignore_certificate_errors: bool,
70
71    /// Unminify Javascript.
72    pub unminify_js: bool,
73
74    /// Directory path that was created with "unminify-js"
75    pub local_script_source: Option<String>,
76
77    /// Unminify Css.
78    pub unminify_css: bool,
79}
80
81/// Debug options for Servo.
82#[derive(Clone, Debug, Default, Deserialize, Serialize)]
83pub struct DiagnosticsLogging {
84    /// Print all the debug options supported by Servo to the standard output.
85    pub help: bool,
86
87    /// Print the DOM after each restyle.
88    pub style_tree: bool,
89
90    /// Log the rule tree.
91    pub rule_tree: bool,
92
93    /// Log the fragment tree after each layout.
94    pub flow_tree: bool,
95
96    /// Log the stacking context tree after each layout.
97    pub stacking_context_tree: bool,
98
99    /// Log the scroll tree after each layout.
100    ///
101    /// Displays the hierarchy of scrollable areas and their properties.
102    pub scroll_tree: bool,
103
104    /// Log the display list after each layout.
105    pub display_list: bool,
106
107    /// Log notifications when a relayout occurs.
108    pub relayout_event: bool,
109
110    /// Periodically log on which events script threads spend their processing time.
111    pub profile_script_events: bool,
112
113    /// Log style sharing cache statistics to after each restyle.
114    ///
115    /// Shows hit/miss statistics for the style sharing cache
116    pub style_statistics: bool,
117
118    /// Log garbage collection passes and their durations.
119    pub gc_profile: bool,
120
121    /// Log Progressive Web Metrics.
122    pub progressive_web_metrics: bool,
123}
124
125impl DiagnosticsLogging {
126    /// Create a new DiagnosticsLogging configuration.
127    ///
128    /// In non-production builds, this will automatically read and parse the
129    /// SERVO_DIAGNOSTICS environment variable if it is set.
130    pub fn new() -> Self {
131        let mut config: DiagnosticsLogging = Default::default();
132
133        // Disabled for production builds
134        #[cfg(debug_assertions)]
135        {
136            if let Ok(diagnostics_var) = std::env::var("SERVO_DIAGNOSTICS") {
137                if let Err(error) = config.extend_from_string(&diagnostics_var) {
138                    eprintln!("Could not parse debug logging option: {error}");
139                }
140            }
141        }
142
143        config
144    }
145
146    /// Print available diagnostic logging options and their descriptions.
147    fn print_debug_options_usage(app: &str) {
148        fn print_option(name: &str, description: &str) {
149            println!("\t{:<35} {}", name, description);
150        }
151
152        println!(
153            "Usage: {} debug option,[options,...]\n\twhere options include\n\nOptions:",
154            app
155        );
156        print_option("help", "Show this help message");
157        print_option("style-tree", "Log the style tree after each restyle");
158        print_option("rule-tree", "Log the rule tree");
159        print_option("flow-tree", "Log the fragment tree after each layout");
160        print_option(
161            "stacking-context-tree",
162            "Log the stacking context tree after each layout",
163        );
164        print_option("scroll-tree", "Log the scroll tree after each layout");
165        print_option("display-list", "Log the display list after each layout");
166        print_option("style-stats", "Log style sharing cache statistics");
167        print_option("relayout-event", "Log when relayout occurs");
168        print_option("profile-script-events", "Log script event processing time");
169        print_option("gc-profile", "Log garbage collection statistics");
170        print_option("progressive-web-metrics", "Log Progressive Web Metrics");
171        println!();
172
173        process::exit(0);
174    }
175
176    /// Extend the current configuration with additional options.
177    ///
178    /// Parses the string and merges any enabled options into the current configuration.
179    pub fn extend_from_string(&mut self, option_string: &str) -> Result<(), String> {
180        for option in option_string.split(',') {
181            let option = option.trim();
182            match option {
183                "help" => Self::print_debug_options_usage("servo"),
184                "display-list" => self.display_list = true,
185                "stacking-context-tree" => self.stacking_context_tree = true,
186                "flow-tree" => self.flow_tree = true,
187                "rule-tree" => self.rule_tree = true,
188                "style-tree" => self.style_tree = true,
189                "style-stats" => self.style_statistics = true,
190                "scroll-tree" => self.scroll_tree = true,
191                "gc-profile" => self.gc_profile = true,
192                "profile-script-events" => self.profile_script_events = true,
193                "relayout-event" => self.relayout_event = true,
194                "progressive-web-metrics" => self.progressive_web_metrics = true,
195                "" => {},
196                _ => return Err(format!("Unknown diagnostic option: {option}")),
197            };
198        }
199
200        Ok(())
201    }
202}
203
204/// The destination for the time profiler reports.
205#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
206pub enum OutputOptions {
207    FileName(String),
208    Stdout(f64),
209}
210
211impl Default for Opts {
212    fn default() -> Self {
213        Self {
214            time_profiling: None,
215            time_profiler_trace_path: None,
216            hard_fail: true,
217            multiprocess: false,
218            force_ipc: false,
219            background_hang_monitor: false,
220            random_pipeline_closure_probability: None,
221            random_pipeline_closure_seed: None,
222            sandbox: false,
223            debug: Default::default(),
224            config_dir: None,
225            shaders_path: None,
226            certificate_path: None,
227            ignore_certificate_errors: false,
228            unminify_js: false,
229            local_script_source: None,
230            unminify_css: false,
231        }
232    }
233}
234
235// Make Opts available globally. This saves having to clone and pass
236// opts everywhere it is used, which gets particularly cumbersome
237// when passing through the DOM structures.
238static OPTIONS: OnceLock<Opts> = OnceLock::new();
239
240/// Initialize options.
241///
242/// Should only be called once at process startup.
243/// Must be called before the first call to [`get`].
244pub fn initialize_options(opts: Opts) {
245    OPTIONS.set(opts).expect("Already initialized");
246}
247
248/// Get the servo options
249///
250/// If the servo options have not been initialized by calling [`initialize_options`], then the
251/// options will be initialized to default values. Outside of tests the options should be
252/// explicitly initialized.
253#[inline]
254pub fn get() -> &'static Opts {
255    // In unit-tests using default options reduces boilerplate.
256    // We can't use `cfg(test)` since that only is enabled when this crate
257    // is compiled in test mode.
258    // We rely on the `expect` in `initialize_options` to inform us if refactoring
259    // causes a `get` call to move before `initialize_options`.
260    OPTIONS.get_or_init(Default::default)
261}