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