From 8bfd4dc1e2f9c71ff3d1f9964565c43a6ae02278 Mon Sep 17 00:00:00 2001 From: Peter Hall Date: Thu, 14 Feb 2019 12:53:59 +0000 Subject: [PATCH] #8539 Config preferences backend restructure --- Cargo.lock | 12 + components/bluetooth/lib.rs | 4 +- components/canvas/webgl_mode/inprocess.rs | 4 +- components/config/Cargo.toml | 4 +- components/config/lib.rs | 6 +- components/config/opts.rs | 54 +- components/config/pref_util.rs | 282 ++++++++ components/config/prefs.rs | 646 ++++++++++++------ components/config/tests/opts.rs | 69 +- components/config/tests/prefs.rs | 322 +++++++-- components/config_plugins/Cargo.toml | 19 + components/config_plugins/lib.rs | 219 ++++++ components/config_plugins/parse.rs | 149 ++++ components/constellation/constellation.rs | 8 +- components/constellation/pipeline.rs | 8 +- components/layout_thread/lib.rs | 8 +- components/net/filemanager_thread.rs | 12 +- components/net/http_cache.rs | 7 +- components/net/lib.rs | 2 + components/net/tests/filemanager_thread.rs | 7 +- .../dom/bindings/codegen/CodegenRust.py | 5 +- components/script/dom/bindings/guard.rs | 4 +- components/script/dom/create.rs | 4 +- components/script/dom/document.rs | 27 +- components/script/dom/htmlcanvaselement.rs | 4 +- components/script/dom/htmlmediaelement.rs | 9 +- components/script/dom/htmlmetaelement.rs | 8 +- components/script/dom/mouseevent.rs | 8 +- .../script/dom/paintworkletglobalscope.rs | 7 +- components/script/dom/permissions.rs | 8 +- .../script/dom/serviceworkerglobalscope.rs | 7 +- components/script/dom/servoparser/mod.rs | 8 +- components/script/dom/testbinding.rs | 11 +- .../script/dom/webglrenderingcontext.rs | 7 +- components/script/script_runtime.rs | 305 ++++----- components/script/serviceworker_manager.rs | 7 +- components/script/timers.rs | 7 +- components/servo/lib.rs | 8 +- components/style/global_style_data.rs | 4 +- .../style/properties/properties.mako.rs | 4 +- components/style/stylesheets/viewport_rule.rs | 7 +- components/webdriver_server/lib.rs | 37 +- components/webvr/webvr_thread.rs | 7 +- docs/COMMAND_LINE_ARGS.md | 4 +- ports/libsimpleservo/api/src/lib.rs | 13 +- ports/servo/browser.rs | 11 +- ports/servo/glutin_app/window.rs | 5 +- ports/servo/non_android_main.rs | 12 +- resources/prefs.json | 20 +- tests/unit/style/stylesheets.rs | 4 +- tests/unit/style/viewport.rs | 6 +- .../wpt/metadata/css/css-flexbox/__dir__.ini | 12 +- tests/wpt/mozilla/meta/css/__dir__.ini | 2 - 53 files changed, 1751 insertions(+), 683 deletions(-) create mode 100644 components/config/pref_util.rs create mode 100644 components/config_plugins/Cargo.toml create mode 100644 components/config_plugins/lib.rs create mode 100644 components/config_plugins/parse.rs delete mode 100644 tests/wpt/mozilla/meta/css/__dir__.ini diff --git a/Cargo.lock b/Cargo.lock index c0df2ab8bc76..a0546dce36a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3857,13 +3857,25 @@ dependencies = [ "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "servo_config_plugins 0.0.1", "servo_geometry 0.0.1", "servo_url 0.0.1", "std_test_override 0.0.1", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "servo_config_plugins" +version = "0.0.1" +dependencies = [ + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "servo_geometry" version = "0.0.1" diff --git a/components/bluetooth/lib.rs b/components/bluetooth/lib.rs index 8b5919d9708e..81688ef7e041 100644 --- a/components/bluetooth/lib.rs +++ b/components/bluetooth/lib.rs @@ -21,7 +21,7 @@ use device::bluetooth::{BluetoothGATTDescriptor, BluetoothGATTService}; use embedder_traits::{EmbedderMsg, EmbedderProxy}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use servo_config::opts; -use servo_config::prefs::PREFS; +use servo_config::pref; use servo_rand::{self, Rng}; use std::borrow::ToOwned; use std::collections::{HashMap, HashSet}; @@ -65,7 +65,7 @@ pub trait BluetoothThreadFactory { impl BluetoothThreadFactory for IpcSender { fn new(embedder_proxy: EmbedderProxy) -> IpcSender { let (sender, receiver) = ipc::channel().unwrap(); - let adapter = if Some(true) == PREFS.get("dom.bluetooth.enabled").as_boolean() { + let adapter = if pref!(dom.bluetooth.enabled) { BluetoothAdapter::init() } else { BluetoothAdapter::init_mock() diff --git a/components/canvas/webgl_mode/inprocess.rs b/components/canvas/webgl_mode/inprocess.rs index 9c0823c9da71..9166eea55f34 100644 --- a/components/canvas/webgl_mode/inprocess.rs +++ b/components/canvas/webgl_mode/inprocess.rs @@ -11,7 +11,7 @@ use canvas_traits::webgl::{WebGLSender, WebVRCommand, WebVRRenderHandler}; use euclid::Size2D; use fnv::FnvHashMap; use gleam::gl; -use servo_config::prefs::PREFS; +use servo_config::pref; use std::rc::Rc; /// WebGL Threading API entry point that lives in the constellation. @@ -35,7 +35,7 @@ impl WebGLThreads { webrender_api_sender, webvr_compositor.map(|c| WebVRRenderWrapper(c)), ); - let output_handler = if PREFS.is_dom_to_texture_enabled() { + let output_handler = if pref!(dom.webgl.dom_to_texture.enabled) { Some(Box::new(OutputHandler::new( webrender_gl.clone(), channel.clone(), diff --git a/components/config/Cargo.toml b/components/config/Cargo.toml index e6c6cc3f2f9c..885eee5f5d67 100644 --- a/components/config/Cargo.toml +++ b/components/config/Cargo.toml @@ -9,8 +9,6 @@ publish = false [lib] name = "servo_config" path = "lib.rs" -test = false -doctest = false [dependencies] euclid = "0.19" @@ -20,10 +18,12 @@ lazy_static = "1" log = "0.4" num_cpus = "1.1.0" serde = "1.0" +serde_derive = "1.0" serde_json = "1.0" servo_geometry = {path = "../geometry"} servo_url = {path = "../url"} url = "1.2" +servo_config_plugins = { path = "../config_plugins" } [dev-dependencies] env_logger = "0.6" diff --git a/components/config/lib.rs b/components/config/lib.rs index cc9a03acd136..f00c2b853f88 100644 --- a/components/config/lib.rs +++ b/components/config/lib.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +#![feature(core_intrinsics)] #![deny(unsafe_code)] #[macro_use] @@ -11,10 +12,13 @@ extern crate log; #[macro_use] extern crate serde; +pub mod pref_util; +#[macro_use] +pub mod prefs; + pub mod basedir; #[allow(unsafe_code)] pub mod opts; -pub mod prefs; pub fn servo_version() -> String { let cargo_version = env!("CARGO_PKG_VERSION"); diff --git a/components/config/opts.rs b/components/config/opts.rs index 636733105283..8a8a58aeb035 100644 --- a/components/config/opts.rs +++ b/components/config/opts.rs @@ -5,13 +5,12 @@ //! Configuration options for a single run of the servo application. Created //! from command line arguments. -use crate::prefs::{self, PrefValue, PREFS}; +use crate::prefs::{self, PrefValue}; use euclid::TypedSize2D; use getopts::Options; use servo_geometry::DeviceIndependentPixel; use servo_url::ServoUrl; use std::borrow::Cow; -use std::cmp; use std::default::Default; use std::env; use std::fs::{self, File}; @@ -975,17 +974,11 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult { }) .collect(); - let do_not_use_native_titlebar = opt_match.opt_present("b") || - !PREFS - .get("shell.native-titlebar.enabled") - .as_boolean() - .unwrap(); + let do_not_use_native_titlebar = + opt_match.opt_present("b") || !(pref!(shell.native_titlebar.enabled)); - let enable_subpixel_text_antialiasing = !debug_options.disable_subpixel_aa && - PREFS - .get("gfx.subpixel-text-antialiasing.enabled") - .as_boolean() - .unwrap(); + let enable_subpixel_text_antialiasing = + !debug_options.disable_subpixel_aa && pref!(gfx.subpixel_text_antialiasing.enabled); let is_printing_version = opt_match.opt_present("v") || opt_match.opt_present("version"); @@ -1065,15 +1058,7 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult { } if let Some(layout_threads) = layout_threads { - PREFS.set("layout.threads", PrefValue::Number(layout_threads as f64)); - } else if let Some(layout_threads) = PREFS.get("layout.threads").as_string() { - PREFS.set( - "layout.threads", - PrefValue::Number(layout_threads.parse::().unwrap()), - ); - } else if *PREFS.get("layout.threads") == PrefValue::Missing { - let layout_threads = cmp::max(num_cpus::get() * 3 / 4, 1); - PREFS.set("layout.threads", PrefValue::Number(layout_threads as f64)); + set_pref!(layout.threads, layout_threads as i64); } ArgumentParsingResult::ChromeProcess @@ -1104,15 +1089,26 @@ pub fn get() -> RwLockReadGuard<'static, Opts> { pub fn parse_pref_from_command_line(pref: &str) { let split: Vec<&str> = pref.splitn(2, '=').collect(); let pref_name = split[0]; - let value = split.get(1); - match value { - Some(&"false") => PREFS.set(pref_name, PrefValue::Boolean(false)), - Some(&"true") | None => PREFS.set(pref_name, PrefValue::Boolean(true)), - Some(value) => match value.parse::() { - Ok(v) => PREFS.set(pref_name, PrefValue::Number(v)), - Err(_) => PREFS.set(pref_name, PrefValue::String(value.to_string())), + let pref_value = parse_cli_pref_value(split.get(1).cloned()); + prefs::pref_map() + .set(pref_name, pref_value) + .expect(format!("Error setting preference: {}", pref).as_str()); +} + +fn parse_cli_pref_value(input: Option<&str>) -> PrefValue { + match input { + Some("true") | None => PrefValue::Bool(true), + Some("false") => PrefValue::Bool(false), + Some(string) => { + if let Some(int) = string.parse::().ok() { + PrefValue::Int(int) + } else if let Some(float) = string.parse::().ok() { + PrefValue::Float(float) + } else { + PrefValue::from(string) + } }, - }; + } } pub fn parse_url_or_filename(cwd: &Path, input: &str) -> Result { diff --git a/components/config/pref_util.rs b/components/config/pref_util.rs new file mode 100644 index 000000000000..3083e39b9f50 --- /dev/null +++ b/components/config/pref_util.rs @@ -0,0 +1,282 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use serde_json::Value; +use std::collections::HashMap; +use std::fmt; +use std::str::FromStr; +use std::sync::{Arc, RwLock}; + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub enum PrefValue { + Float(f64), + Int(i64), + Str(String), + Bool(bool), + Missing, +} + +impl PrefValue { + pub fn as_str(&self) -> Option<&str> { + if let PrefValue::Str(val) = self { + Some(val) + } else { + None + } + } + + pub fn as_i64(&self) -> Option { + if let PrefValue::Int(val) = self { + Some(*val) + } else { + None + } + } + + pub fn as_f64(&self) -> Option { + if let PrefValue::Float(val) = self { + Some(*val) + } else { + None + } + } + + pub fn as_bool(&self) -> Option { + if let PrefValue::Bool(val) = self { + Some(*val) + } else { + None + } + } + + pub fn is_missing(&self) -> bool { + match self { + PrefValue::Missing => true, + _ => false, + } + } + + pub fn from_json_value(value: &Value) -> Option { + match value { + Value::Bool(b) => Some(PrefValue::Bool(*b)), + Value::Number(n) if n.is_i64() => Some(PrefValue::Int(n.as_i64().unwrap())), + Value::Number(n) if n.is_f64() => Some(PrefValue::Float(n.as_f64().unwrap())), + Value::String(s) => Some(PrefValue::Str(s.to_owned())), + _ => None, + } + } +} + +impl FromStr for PrefValue { + type Err = PrefError; + fn from_str(s: &str) -> Result { + if s == "false" { + Ok(PrefValue::Bool(false)) + } else if s == "true" { + Ok(PrefValue::Bool(true)) + } else if let Ok(float) = s.parse::() { + Ok(PrefValue::Float(float)) + } else if let Ok(integer) = s.parse::() { + Ok(PrefValue::Int(integer)) + } else { + Ok(PrefValue::from(s)) + } + } +} + +macro_rules! impl_pref_from { + ($($t: ty => $variant: path,)*) => { + $( + impl From<$t> for PrefValue { + fn from(other: $t) -> Self { + $variant(other.into()) + } + } + )+ + $( + impl From> for PrefValue { + fn from(other: Option<$t>) -> Self { + other.map(|val| $variant(val.into())).unwrap_or(PrefValue::Missing) + } + } + )+ + } +} + +macro_rules! impl_from_pref { + ($($variant: path => $t: ty,)*) => { + $( + impl From for $t { + #[allow(unsafe_code)] + fn from(other: PrefValue) -> Self { + if let $variant(value) = other { + value.into() + } else { + panic!( + format!("Cannot convert {:?} to {:?}", + other, + unsafe { std::intrinsics::type_name::<$t>() } + ) + ); + } + } + } + )+ + $( + impl From for Option<$t> { + fn from(other: PrefValue) -> Self { + if let PrefValue::Missing = other { + None + } else { + Some(other.into()) + } + } + } + )+ + } +} + +impl_pref_from! { + f64 => PrefValue::Float, + i64 => PrefValue::Int, + String => PrefValue::Str, + &str => PrefValue::Str, + bool => PrefValue::Bool, +} + +impl_from_pref! { + PrefValue::Float => f64, + PrefValue::Int => i64, + PrefValue::Str => String, + PrefValue::Bool => bool, +} + +#[derive(Debug)] +pub enum PrefError { + NoSuchPref(String), + InvalidValue(String), + JsonParseErr(serde_json::error::Error), +} + +impl fmt::Display for PrefError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + PrefError::NoSuchPref(s) | PrefError::InvalidValue(s) => f.write_str(&s), + PrefError::JsonParseErr(e) => e.fmt(f), + } + } +} + +impl std::error::Error for PrefError {} + +pub struct Accessor { + pub getter: Box V + Sync>, + pub setter: Box, +} + +impl Accessor { + pub fn new(getter: G, setter: S) -> Self + where + G: Fn(&P) -> V + Sync + 'static, + S: Fn(&mut P, V) + Sync + 'static, + { + Accessor { + getter: Box::new(getter), + setter: Box::new(setter), + } + } +} + +pub struct Preferences<'m, P> { + user_prefs: Arc>, + default_prefs: P, + accessors: &'m HashMap>, +} + +impl<'m, P: Clone> Preferences<'m, P> { + /// Create a new `Preferences` object. The values provided in `default_prefs` are immutable and + /// can always be restored using `reset` or `reset_all`. + pub fn new(default_prefs: P, accessors: &'m HashMap>) -> Self { + Self { + user_prefs: Arc::new(RwLock::new(default_prefs.clone())), + default_prefs, + accessors, + } + } + + /// Access to the data structure holding the preference values. + pub fn values(&self) -> Arc> { + Arc::clone(&self.user_prefs) + } + + /// Retrieve a preference using its key + pub fn get(&self, key: &str) -> PrefValue { + if let Some(accessor) = self.accessors.get(key) { + let prefs = self.user_prefs.read().unwrap(); + (accessor.getter)(&prefs) + } else { + PrefValue::Missing + } + } + + /// Creates an iterator over all keys and values + pub fn iter<'a>(&'a self) -> impl Iterator + 'a { + let prefs = self.user_prefs.read().unwrap(); + self.accessors + .iter() + .map(move |(k, accessor)| (k.clone(), (accessor.getter)(&prefs))) + } + + /// Creates an iterator over all keys + pub fn keys<'a>(&'a self) -> impl Iterator + 'a { + self.accessors.keys().map(String::as_str) + } + + fn set_inner(&self, key: &str, mut prefs: &mut P, val: V) -> Result<(), PrefError> + where + V: Into, + { + if let Some(accessor) = self.accessors.get(key) { + Ok((accessor.setter)(&mut prefs, val.into())) + } else { + Err(PrefError::NoSuchPref(String::from(key))) + } + } + + /// Set a new value for a preference, using its key. + pub fn set(&self, key: &str, val: V) -> Result<(), PrefError> + where + V: Into, + { + let mut prefs = self.user_prefs.write().unwrap(); + self.set_inner(key, &mut prefs, val) + } + + pub fn set_all(&self, values: M) -> Result<(), PrefError> + where + M: IntoIterator, + { + let mut prefs = self.user_prefs.write().unwrap(); + for (k, v) in values.into_iter() { + self.set_inner(&k, &mut prefs, v)?; + } + Ok(()) + } + + pub fn reset(&self, key: &str) -> Result { + if let Some(accessor) = self.accessors.get(key) { + let mut prefs = self.user_prefs.write().unwrap(); + let old_pref = (accessor.getter)(&prefs); + let default_pref = (accessor.getter)(&self.default_prefs); + (accessor.setter)(&mut prefs, default_pref); + Ok(old_pref) + } else { + Err(PrefError::NoSuchPref(String::from(key))) + } + } + + pub fn reset_all(&self) { + *self.user_prefs.write().unwrap() = self.default_prefs.clone(); + } +} diff --git a/components/config/prefs.rs b/components/config/prefs.rs index 061f8ca3ec39..e8b8b9a1e7ec 100644 --- a/components/config/prefs.rs +++ b/components/config/prefs.rs @@ -7,251 +7,475 @@ use crate::opts; use embedder_traits::resources::{self, Resource}; use serde_json::{self, Value}; use std::borrow::ToOwned; -use std::cmp::max; use std::collections::HashMap; use std::fs::File; use std::io::{stderr, Read, Write}; use std::path::PathBuf; -use std::sync::{Arc, RwLock}; + +use crate::pref_util::Preferences; +pub use crate::pref_util::{PrefError, PrefValue}; +use gen::Prefs; lazy_static! { - pub static ref PREFS: Preferences = { - let defaults = default_prefs(); - if let Ok(prefs) = read_prefs(&resources::read_string(Resource::Preferences)) { - defaults.extend(prefs); - } - defaults + static ref PREFS: Preferences<'static, Prefs> = { + let def_prefs: Prefs = serde_json::from_str(&resources::read_string(Resource::Preferences)) + .expect("Failed to initialize config preferences."); + Preferences::new(def_prefs, &gen::PREF_ACCESSORS) }; } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -pub enum PrefValue { - Boolean(bool), - String(String), - Number(f64), - Missing, -} - -impl PrefValue { - pub fn from_json(data: Value) -> Result { - let value = match data { - Value::Bool(x) => PrefValue::Boolean(x), - Value::String(x) => PrefValue::String(x), - Value::Number(x) => { - if let Some(v) = x.as_f64() { - PrefValue::Number(v) - } else { - return Err(()); - } - }, - _ => return Err(()), - }; - Ok(value) - } - - pub fn as_boolean(&self) -> Option { - match *self { - PrefValue::Boolean(value) => Some(value), - _ => None, - } - } - - pub fn as_string(&self) -> Option<&str> { - match *self { - PrefValue::String(ref value) => Some(&value), - _ => None, - } - } - - pub fn as_i64(&self) -> Option { - match *self { - PrefValue::Number(x) => Some(x as i64), - _ => None, - } - } - - pub fn as_u64(&self) -> Option { - match *self { - PrefValue::Number(x) => Some(x as u64), - _ => None, - } - } +/// A convenience macro for accessing a preference value using its static path. +/// Passing an invalid path is a compile-time error. +#[macro_export] +macro_rules! pref { + ($($segment: ident).+) => {{ + let values = $crate::prefs::pref_map().values(); + let lock = values.read() + .map(|prefs| prefs $(.$segment)+.clone()); + lock.unwrap() + }}; } -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum Pref { - NoDefault(Arc), - WithDefault(Arc, Option>), +/// A convenience macro for updating a preference value using its static path. +/// Passing an invalid path is a compile-time error. +#[macro_export] +macro_rules! set_pref { + ($($segment: ident).+, $value: expr) => {{ + let values = $crate::prefs::pref_map().values(); + let mut lock = values.write().unwrap(); + lock$ (.$segment)+ = $value; + }}; } -impl Pref { - pub fn new(value: PrefValue) -> Pref { - Pref::NoDefault(Arc::new(value)) - } - - fn new_default(value: PrefValue) -> Pref { - Pref::WithDefault(Arc::new(value), None) - } - - fn from_json(data: Value) -> Result { - let value = PrefValue::from_json(data)?; - Ok(Pref::new_default(value)) - } - - pub fn value(&self) -> &Arc { - match *self { - Pref::NoDefault(ref x) => x, - Pref::WithDefault(ref default, ref override_value) => match *override_value { - Some(ref x) => x, - None => default, - }, - } - } - - fn set(&mut self, value: PrefValue) { - // TODO - this should error if we try to override a pref of one type - // with a value of a different type - match *self { - Pref::NoDefault(ref mut pref_value) => *pref_value = Arc::new(value), - Pref::WithDefault(_, ref mut override_value) => *override_value = Some(Arc::new(value)), - } - } +/// Access preferences using their `String` keys. Note that the key may be different from the +/// static path because legacy keys contain hyphens, or because a preference name has been renamed. +/// +/// When retrieving a preference, the value will always be a `PrefValue`. When setting a value, it +/// may be a `PrefValue` or the type that converts into the correct underlying value; one of `bool`, +/// `i64`, `f64` or `String`. +#[inline] +pub fn pref_map() -> &'static Preferences<'static, Prefs> { + &PREFS } -pub fn default_prefs() -> Preferences { - let prefs = Preferences(Arc::new(RwLock::new(HashMap::new()))); - prefs.set( - "layout.threads", - PrefValue::Number(max(num_cpus::get() * 3 / 4, 1) as f64), - ); - prefs -} - -pub fn read_prefs(txt: &str) -> Result, ()> { - let json: Value = serde_json::from_str(txt).or_else(|e| { - println!("Ignoring invalid JSON in preferences: {:?}.", e); - Err(()) - })?; - - let mut prefs = HashMap::new(); - if let Value::Object(obj) = json { - for (name, value) in obj.into_iter() { - match Pref::from_json(value) { - Ok(x) => { - prefs.insert(name, x); - }, - Err(_) => println!( - "Ignoring non-boolean/string/i64 preference value for {:?}", - name - ), - } - } +pub fn add_user_prefs() { + if let Some(path) = user_prefs_path() { + init_user_prefs(path); } - Ok(prefs) } -pub fn add_user_prefs() { - match opts::get().config_dir { - Some(ref config_path) => { - let mut path = PathBuf::from(config_path); - init_user_prefs(&mut path); - }, - None => { - if let Some(mut path) = default_config_dir() { - if path.join("prefs.json").exists() { - init_user_prefs(&mut path); - } - } - }, - } +fn user_prefs_path() -> Option { + opts::get() + .config_dir + .clone() + .or_else(|| default_config_dir()) + .map(|path| path.join("prefs.json")) + .filter(|path| path.exists()) } -fn init_user_prefs(path: &mut PathBuf) { - path.push("prefs.json"); - if let Ok(mut file) = File::open(path) { +fn init_user_prefs(path: PathBuf) { + if let Ok(mut file) = File::open(&path) { let mut txt = String::new(); - file.read_to_string(&mut txt).expect("Can't read use prefs"); - if let Ok(prefs) = read_prefs(&txt) { - PREFS.extend(prefs); + file.read_to_string(&mut txt) + .expect("Can't read user prefs"); + match read_prefs_map(&txt) { + Ok(prefs) => { + if let Err(error) = PREFS.set_all(prefs.into_iter()) { + writeln!(&mut stderr(), "Error setting preference: {:?}", error) + } else { + Ok(()) + } + }, + Err(error) => writeln!(&mut stderr(), "Error parsing prefs.json: {:?}", error), } } else { - writeln!( - &mut stderr(), - "Error opening prefs.json from config directory" - ) - .expect("failed printing to stderr"); + writeln!(&mut stderr(), "Error opening user prefs from {:?}", path) } + .expect("failed printing to stderr"); } -pub struct Preferences(Arc>>); +pub fn read_prefs_map(txt: &str) -> Result, PrefError> { + let prefs: HashMap = + serde_json::from_str(txt).map_err(|e| PrefError::JsonParseErr(e))?; + prefs + .into_iter() + .map(|(k, pref_value)| { + Ok({ + let v = match &pref_value { + Value::Bool(b) => PrefValue::Bool(*b), + Value::Number(n) if n.is_i64() => PrefValue::Int(n.as_i64().unwrap()), + Value::Number(n) if n.is_f64() => PrefValue::Float(n.as_f64().unwrap()), + Value::String(s) => PrefValue::Str(s.to_owned()), + _ => { + return Err(PrefError::InvalidValue(format!( + "Invalid value: {}", + pref_value + ))); + }, + }; + (k.to_owned(), v) + }) + }) + .collect() +} -impl Preferences { - pub fn get(&self, name: &str) -> Arc { - self.0 - .read() - .unwrap() - .get(name) - .map_or(Arc::new(PrefValue::Missing), |x| x.value().clone()) - } +mod gen { + use servo_config_plugins::build_structs; - pub fn cloned(&self) -> HashMap { - self.0.read().unwrap().clone() + // The number of layout threads is calculated if it is not present in `prefs.json`. + fn default_layout_threads() -> i64 { + std::cmp::max(num_cpus::get() * 3 / 4, 1) as i64 } - pub fn set(&self, name: &str, value: PrefValue) { - let mut prefs = self.0.write().unwrap(); - if let Some(pref) = prefs.get_mut(name) { - pref.set(value); - return; - } - prefs.insert(name.to_owned(), Pref::new(value)); + fn black() -> i64 { + 0x000000 } - pub fn reset(&self, name: &str) -> Arc { - let mut prefs = self.0.write().unwrap(); - let result = match prefs.get_mut(name) { - None => return Arc::new(PrefValue::Missing), - Some(&mut Pref::NoDefault(_)) => Arc::new(PrefValue::Missing), - Some(&mut Pref::WithDefault(ref default, ref mut set_value)) => { - *set_value = None; - default.clone() - }, - }; - if *result == PrefValue::Missing { - prefs.remove(name); - } - result + fn white() -> i64 { + 0xFFFFFF } - pub fn reset_all(&self) { - let names = { - self.0 - .read() - .unwrap() - .keys() - .cloned() - .collect::>() - }; - for name in names.iter() { - self.reset(name); + build_structs! { + // type of the accessors + accessor_type = crate::pref_util::Accessor::, + // name of the constant, which will hold a HashMap of preference accessors + gen_accessors = PREF_ACCESSORS, + // tree of structs to generate + gen_types = Prefs { + browser: { + display: { + #[serde(default = "white")] + background_color: i64, + #[serde(default = "black")] + foreground_color: i64, + } + }, + css: { + animations: { + testing: { + #[serde(default)] + enabled: bool, + }, + }, + }, + dom: { + bluetooth: { + enabled: bool, + testing: { + enabled: bool, + } + }, + canvas_text: { + #[serde(rename = "dom.canvas-text.enabled")] + enabled: bool, + }, + composition_event: { + #[serde(rename = "dom.compositionevent.enabled")] + enabled: bool, + }, + custom_elements: { + #[serde(rename = "dom.customelements.enabled")] + enabled: bool, + }, + document: { + dblclick_timeout: i64, + dblclick_dist: i64, + }, + forcetouch: { + enabled: bool, + }, + fullscreen: { + test: bool, + }, + gamepad: { + enabled: bool, + }, + microdata: { + testing: { + enabled: bool, + } + }, + mouse_event: { + which: { + #[serde(rename = "dom.mouseevent.which.enabled")] + enabled: bool, + } + }, + mutation_observer: { + enabled: bool, + }, + offscreen_canvas: { + enabled: bool, + }, + permissions: { + enabled: bool, + testing: { + allowed_in_nonsecure_contexts: bool, + } + }, + serviceworker: { + enabled: bool, + timeout_seconds: i64, + }, + servoparser: { + async_html_tokenizer: { + enabled: bool, + } + }, + svg: { + enabled: bool, + }, + testable_crash: { + enabled: bool, + }, + testbinding: { + enabled: bool, + prefcontrolled: { + #[serde(default)] + enabled: bool, + }, + prefcontrolled2: { + #[serde(default)] + enabled: bool, + }, + preference_value: { + #[serde(default)] + falsy: bool, + #[serde(default)] + quote_string_test: String, + #[serde(default)] + space_string_test: String, + #[serde(default)] + string_empty: String, + #[serde(default)] + string_test: String, + #[serde(default)] + truthy: bool, + }, + }, + testing: { + element: { + activation: { + #[serde(default)] + enabled: bool, + } + }, + html_input_element: { + select_files: { + #[serde(rename = "dom.testing.htmlinputelement.select_files.enabled")] + enabled: bool, + } + }, + }, + testperf: { + #[serde(default)] + enabled: bool, + }, + webgl: { + dom_to_texture: { + enabled: bool, + } + }, + webgl2: { + enabled: bool, + }, + webrtc: { + #[serde(default)] + enabled: bool, + }, + webvr: { + enabled: bool, + event_polling_interval: i64, + test: bool, + }, + webxr: { + #[serde(default)] + enabled: bool, + }, + worklet: { + blockingsleep: { + #[serde(default)] + enabled: bool, + }, + #[serde(default)] + enabled: bool, + testing: { + #[serde(default)] + enabled: bool, + }, + timeout_ms: i64, + }, + }, + gfx: { + subpixel_text_antialiasing: { + #[serde(rename = "gfx.subpixel-text-antialiasing.enabled")] + enabled: bool, + } + }, + js: { + asmjs: { + enabled: bool, + }, + asyncstack: { + enabled: bool, + }, + baseline: { + enabled: bool, + unsafe_eager_compilation: { + enabled: bool, + }, + }, + discard_system_source: { + enabled: bool, + }, + dump_stack_on_debuggee_would_run: { + enabled: bool, + }, + ion: { + enabled: bool, + offthread_compilation: { + enabled: bool, + }, + unsafe_eager_compilation: { + enabled: bool, + }, + }, + mem: { + gc: { + allocation_threshold_mb: i64, + allocation_threshold_factor: i64, + allocation_threshold_avoid_interrupt_factor: i64, + compacting: { + enabled: bool, + }, + decommit_threshold_mb: i64, + dynamic_heap_growth: { + enabled: bool, + }, + dynamic_mark_slice: { + enabled: bool, + }, + empty_chunk_count_max: i64, + empty_chunk_count_min: i64, + high_frequency_heap_growth_max: i64, + high_frequency_heap_growth_min: i64, + high_frequency_high_limit_mb: i64, + high_frequency_low_limit_mb: i64, + high_frequency_time_limit_ms: i64, + incremental: { + enabled: bool, + slice_ms: i64, + }, + low_frequency_heap_growth: i64, + per_compartment: { + enabled: bool, + }, + per_zone: { + enabled: bool, + }, + zeal: { + frequency: i64, + level: i64, + }, + }, + high_water_mark: i64, + max: i64, + }, + native_regex: { + enabled: bool, + }, + offthread_compilation: { + enabled: bool, + }, + parallel_parsing: { + enabled: bool, + }, + shared_memory: { + enabled: bool, + }, + strict: { + debug: { + enabled: bool, + }, + enabled: bool, + }, + throw_on_asmjs_validation_failure: { + enabled: bool, + }, + throw_on_debuggee_would_run: { + enabled: bool, + }, + timers: { + minimum_duration: i64, + }, + wasm: { + baseline: { + enabled: bool, + }, + enabled: bool, + ion: { + enabled: bool, + } + }, + werror: { + enabled: bool, + }, + }, + layout: { + animations: { + test: { + enabled: bool, + } + }, + columns: { + enabled: bool, + }, + #[serde(default = "default_layout_threads")] + threads: i64, + viewport: { + enabled: bool, + }, + writing_mode: { + #[serde(rename = "layout.writing-mode.enabled")] + enabled: bool, + } + }, + media: { + testing: { + enabled: bool, + } + }, + network: { + http_cache: { + #[serde(rename = "network.http-cache.disabled")] + disabled: bool, + }, + mime: { + sniff: bool, + } + }, + session_history: { + #[serde(rename = "session-history.max-length")] + max_length: i64, + }, + shell: { + homepage: String, + keep_screen_on: { + enabled: bool, + }, + #[serde(rename = "shell.native-orientation")] + native_orientation: String, + native_titlebar: { + #[serde(rename = "shell.native-titlebar.enabled")] + enabled: bool, + }, + searchpage: String, + }, + webgl: { + testing: { + context_creation_error: bool, + } + }, } } - - pub fn extend(&self, extension: HashMap) { - self.0.write().unwrap().extend(extension); - } - - pub fn is_webvr_enabled(&self) -> bool { - self.get("dom.webvr.enabled").as_boolean().unwrap_or(false) - } - - pub fn is_dom_to_texture_enabled(&self) -> bool { - self.get("dom.webgl.dom_to_texture.enabled") - .as_boolean() - .unwrap_or(false) - } - - pub fn is_webgl2_enabled(&self) -> bool { - self.get("dom.webgl2.enabled").as_boolean().unwrap_or(false) - } } diff --git a/components/config/tests/opts.rs b/components/config/tests/opts.rs index ce46e07b319f..abb1d0a9dfc9 100644 --- a/components/config/tests/opts.rs +++ b/components/config/tests/opts.rs @@ -2,8 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +#[macro_use] +extern crate servo_config; + use servo_config::opts::{parse_pref_from_command_line, parse_url_or_filename}; -use servo_config::prefs::{PrefValue, PREFS}; +use servo_config::{prefs, prefs::PrefValue}; use std::path::Path; #[cfg(not(target_os = "windows"))] @@ -86,25 +89,53 @@ fn test_argument_parsing_special() { assert_eq!(url.fragment(), None); } +#[test] +fn test_invalid_prefs_from_command_line_panics() { + let err_msg = std::panic::catch_unwind(|| { + parse_pref_from_command_line("doesntexist=true"); + }) + .err() + .and_then(|a| a.downcast_ref::().cloned()) + .expect("Should panic"); + assert!( + err_msg.starts_with("Error setting preference"), + "Message should describe the problem" + ); + assert!( + err_msg.contains("doesntexist"), + "Message should mention the name of the preference" + ); +} + #[test] fn test_parse_pref_from_command_line() { // Test with boolean values. - parse_pref_from_command_line("testtrue=true"); - assert_eq!(*PREFS.get("testtrue"), PrefValue::Boolean(true)); - parse_pref_from_command_line("testfalse=false"); - assert_eq!(*PREFS.get("testfalse"), PrefValue::Boolean(false)); - - // Test with numbers. - parse_pref_from_command_line("testint=42"); - assert_eq!(*PREFS.get("testint"), PrefValue::Number(42 as f64)); - parse_pref_from_command_line("testfloat=4.2"); - assert_eq!(*PREFS.get("testfloat"), PrefValue::Number(4.2)); - - // Test default (string). - parse_pref_from_command_line("teststr=str"); - assert_eq!(*PREFS.get("teststr"), PrefValue::String("str".to_owned())); - - // Test with no value. - parse_pref_from_command_line("testempty"); - assert_eq!(*PREFS.get("testempty"), PrefValue::Boolean(true)); + parse_pref_from_command_line("dom.bluetooth.enabled=true"); + assert_eq!( + prefs::pref_map().get("dom.bluetooth.enabled"), + PrefValue::Bool(true) + ); + assert_eq!(pref!(dom.bluetooth.enabled), true); + + parse_pref_from_command_line("dom.bluetooth.enabled=false"); + assert_eq!( + prefs::pref_map().get("dom.bluetooth.enabled"), + PrefValue::Bool(false) + ); + assert_eq!(pref!(dom.bluetooth.enabled), false); + + // Test with numbers + parse_pref_from_command_line("layout.threads=42"); + assert_eq!(pref!(layout.threads), 42); + + // Test string. + parse_pref_from_command_line("shell.homepage=str"); + assert_eq!(pref!(shell.homepage), "str"); + + // Test with no value (defaults to true). + prefs::pref_map() + .set("dom.bluetooth.enabled", false) + .unwrap(); + parse_pref_from_command_line("dom.bluetooth.enabled"); + assert_eq!(pref!(dom.bluetooth.enabled), true); } diff --git a/components/config/tests/prefs.rs b/components/config/tests/prefs.rs index 2b035b6d46ef..16812b40ac37 100644 --- a/components/config/tests/prefs.rs +++ b/components/config/tests/prefs.rs @@ -2,60 +2,246 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +#[macro_use] +extern crate serde_derive; + use servo_config::basedir; -use servo_config::prefs::{read_prefs, PrefValue, PREFS}; +use servo_config::pref_util::Preferences; +use servo_config::prefs::{read_prefs_map, PrefValue}; +use std::collections::HashMap; +use std::error::Error; use std::fs::{self, File}; use std::io::{Read, Write}; #[test] -fn test_create_pref() { - let json_str = "{\ - \"layout.writing-mode.enabled\": true,\ - \"network.mime.sniff\": false,\ - \"shell.homepage\": \"https://servo.org\"\ - }"; - - let prefs = read_prefs(json_str); +fn test_create_prefs_map() { + let json_str = "{ + \"layout.writing-mode.enabled\": true, + \"network.mime.sniff\": false, + \"shell.homepage\": \"https://servo.org\" + }"; + let prefs = read_prefs_map(json_str); assert!(prefs.is_ok()); let prefs = prefs.unwrap(); - assert_eq!(prefs.len(), 3); } #[test] -fn test_get_set_reset_extend() { - let json_str = "{\ - \"layout.writing-mode.enabled\": true,\ - \"extra.stuff\": false,\ - \"shell.homepage\": \"https://google.com\"\ - }"; +fn test_generated_accessors_get() { + let prefs: gen::TestPrefs = serde_json::from_str(DEF_JSON_STR).unwrap(); + let map: HashMap = gen::TEST_PREF_ACCESSORS + .iter() + .map(move |(key, accessor)| { + let pref_value = (accessor.getter)(&prefs); + (key.clone(), pref_value) + }) + .collect(); - assert_eq!(*PREFS.get("test"), PrefValue::Missing); - PREFS.set("test", PrefValue::String("hi".to_owned())); - assert_eq!(*PREFS.get("test"), PrefValue::String("hi".to_owned())); - assert_eq!( - *PREFS.get("shell.homepage"), - PrefValue::String("https://servo.org".to_owned()) - ); - PREFS.set("shell.homepage", PrefValue::Boolean(true)); - assert_eq!(*PREFS.get("shell.homepage"), PrefValue::Boolean(true)); - PREFS.reset("shell.homepage"); + assert_eq!(&PrefValue::from("hello"), map.get("pref_string").unwrap()); + assert_eq!(&PrefValue::from(23_i64), map.get("pref_i64").unwrap()); + assert_eq!(&PrefValue::from(1.5_f64), map.get("pref_f64").unwrap()); + assert_eq!(&PrefValue::from(true), map.get("pref_bool").unwrap()); assert_eq!( - *PREFS.get("shell.homepage"), - PrefValue::String("https://servo.org".to_owned()) + &PrefValue::from(333_i64), + map.get("group.nested.nested_i64").unwrap() ); + assert_eq!(&PrefValue::from(42_i64), map.get("a.renamed.pref").unwrap()); +} - let extension = read_prefs(json_str).unwrap(); - PREFS.extend(extension); - assert_eq!( - *PREFS.get("shell.homepage"), - PrefValue::String("https://google.com".to_owned()) - ); - assert_eq!( - *PREFS.get("layout.writing-mode.enabled"), - PrefValue::Boolean(true) +#[test] +fn test_generated_accessors_set() { + let mut prefs: gen::TestPrefs = serde_json::from_str(DEF_JSON_STR).unwrap(); + let setters: HashMap = gen::TEST_PREF_ACCESSORS + .iter() + .map(|(key, accessor)| (key.clone(), &accessor.setter)) + .collect(); + + (setters.get("pref_string").unwrap())(&mut prefs, PrefValue::Str(String::from("boo"))); + (setters.get("pref_i64").unwrap())(&mut prefs, PrefValue::Int(-25)); + (setters.get("pref_f64").unwrap())(&mut prefs, PrefValue::Float(-1.9)); + (setters.get("pref_bool").unwrap())(&mut prefs, PrefValue::Bool(false)); + (setters.get("group.nested.nested_i64").unwrap())(&mut prefs, PrefValue::Int(10)); + (setters.get("a.renamed.pref").unwrap())(&mut prefs, PrefValue::Int(11)); + + assert_eq!("boo", prefs.pref_string); + assert_eq!(-25, prefs.pref_i64); + assert_eq!(-1.9, prefs.pref_f64); + assert_eq!(false, prefs.pref_bool); + assert_eq!(10, prefs.group.nested.nested_i64); + assert_eq!(11, prefs.group.nested.renamed); +} + +#[test] +fn test_static_struct() { + let prefs: gen::TestPrefs = serde_json::from_str(DEF_JSON_STR).unwrap(); + assert_eq!("hello", prefs.pref_string); + assert_eq!(23, prefs.pref_i64); + assert_eq!(1.5, prefs.pref_f64); + assert_eq!(true, prefs.pref_bool); + assert_eq!(333, prefs.group.nested.nested_i64); + assert_eq!(42, prefs.group.nested.renamed); +} + +#[test] +fn test_set_pref() { + let prefs = Preferences::new(gen::TestPrefs::default(), &gen::TEST_PREF_ACCESSORS); + assert_eq!(Some(0), prefs.get("group.nested.nested_i64").as_i64()); + let result = prefs.set("group.nested.nested_i64", 1); + assert_eq!(true, result.is_ok()); + assert_eq!(Some(1), prefs.get("group.nested.nested_i64").as_i64()); + assert_eq!(1, prefs.values().read().unwrap().group.nested.nested_i64); +} + +#[test] +fn test_set_unknown_pref_is_err() -> Result<(), Box> { + let prefs = Preferences::new(gen::TestPrefs::default(), &gen::TEST_PREF_ACCESSORS); + let result = prefs.set("unknown_pref", 1); + assert_eq!(true, result.is_err()); + Ok(()) +} + +#[test] +fn test_reset_pref() -> Result<(), Box> { + let mut def_prefs = gen::TestPrefs::default(); + def_prefs.group.nested.nested_i64 = 999; + let prefs = Preferences::new(def_prefs, &gen::TEST_PREF_ACCESSORS); + assert_eq!(Some(999), prefs.get("group.nested.nested_i64").as_i64()); + + prefs.set("group.nested.nested_i64", 1)?; + assert_eq!(Some(1), prefs.get("group.nested.nested_i64").as_i64()); + + prefs.reset("group.nested.nested_i64")?; + assert_eq!(Some(999), prefs.get("group.nested.nested_i64").as_i64()); + assert_eq!(999, prefs.values().read().unwrap().group.nested.nested_i64); + Ok(()) +} + +#[test] +fn test_default_values() -> Result<(), Box> { + let def_prefs: gen::TestPrefs = serde_json::from_str(DEF_JSON_STR)?; + let prefs = Preferences::new(def_prefs, &gen::TEST_PREF_ACCESSORS); + assert_eq!(Some(0), prefs.get("default_value").as_i64()); + assert_eq!(Some(555), prefs.get("computed_default_value").as_i64()); + Ok(()) +} + +#[test] +fn test_override_default_values() -> Result<(), Box> { + let def_prefs: gen::TestPrefs = serde_json::from_str(WITHOUT_DEFAULTS_JSON_STR)?; + let prefs = Preferences::new(def_prefs, &gen::TEST_PREF_ACCESSORS); + assert_eq!(Some(-1), prefs.get("default_value").as_i64()); + assert_eq!(Some(-1), prefs.get("computed_default_value").as_i64()); + Ok(()) +} + +#[test] +fn test_update_reset_default_values() -> Result<(), Box> { + let def_prefs: gen::TestPrefs = serde_json::from_str(DEF_JSON_STR)?; + let prefs = Preferences::new(def_prefs, &gen::TEST_PREF_ACCESSORS); + + prefs.set("default_value", 99)?; + prefs.set("computed_default_value", 199)?; + assert_eq!(Some(99), prefs.get("default_value").as_i64()); + assert_eq!(Some(199), prefs.get("computed_default_value").as_i64()); + + prefs.reset("default_value")?; + prefs.reset("computed_default_value")?; + assert_eq!(Some(0), prefs.get("default_value").as_i64()); + assert_eq!(Some(555), prefs.get("computed_default_value").as_i64()); + Ok(()) +} + +#[test] +fn test_update_reset_overridden_default_values() -> Result<(), Box> { + let def_prefs: gen::TestPrefs = serde_json::from_str(WITHOUT_DEFAULTS_JSON_STR)?; + let prefs = Preferences::new(def_prefs, &gen::TEST_PREF_ACCESSORS); + prefs.set("default_value", 99)?; + prefs.set("computed_default_value", 199)?; + assert_eq!(Some(99), prefs.get("default_value").as_i64()); + assert_eq!(Some(199), prefs.get("computed_default_value").as_i64()); + + prefs.reset("default_value")?; + prefs.reset("computed_default_value")?; + assert_eq!(Some(-1), prefs.get("default_value").as_i64()); + assert_eq!(Some(-1), prefs.get("computed_default_value").as_i64()); + Ok(()) +} + +#[test] +fn test_user_prefs_override_and_reset() -> Result<(), Box> { + let mut def_prefs = gen::TestPrefs::default(); + def_prefs.group.nested.nested_i64 = 999; + let prefs = Preferences::new(def_prefs, &gen::TEST_PREF_ACCESSORS); + + prefs.set("group.nested.nested_i64", 45)?; + assert_eq!(Some(45), prefs.get("group.nested.nested_i64").as_i64()); + + prefs.reset("group.nested.nested_i64")?; + assert_eq!(Some(999), prefs.get("group.nested.nested_i64").as_i64()); + Ok(()) +} + +#[test] +fn test_reset_all() -> Result<(), Box> { + let def_prefs: gen::TestPrefs = serde_json::from_str(DEF_JSON_STR)?; + let prefs = Preferences::new(def_prefs, &gen::TEST_PREF_ACCESSORS); + prefs.set_all(read_prefs_map(USER_JSON_STR)?)?; + + let values = prefs.values(); + assert_eq!("bye", values.read().unwrap().pref_string); + assert_eq!(-1, values.read().unwrap().pref_i64); + assert_eq!(-1.0, values.read().unwrap().pref_f64); + assert_eq!(false, values.read().unwrap().pref_bool); + assert_eq!(-1, values.read().unwrap().group.nested.nested_i64); + assert_eq!(-1, values.read().unwrap().group.nested.renamed); + + prefs.reset_all(); + + let values = prefs.values(); + assert_eq!("hello", values.read().unwrap().pref_string); + assert_eq!(23, values.read().unwrap().pref_i64); + assert_eq!(1.5, values.read().unwrap().pref_f64); + assert_eq!(true, values.read().unwrap().pref_bool); + assert_eq!(333, values.read().unwrap().group.nested.nested_i64); + assert_eq!(42, values.read().unwrap().group.nested.renamed); + Ok(()) +} + +#[test] +fn test_set_all_from_map() -> Result<(), Box> { + let def_prefs: gen::TestPrefs = serde_json::from_str(DEF_JSON_STR)?; + let prefs = Preferences::new(def_prefs, &gen::TEST_PREF_ACCESSORS); + prefs.set_all(read_prefs_map(USER_JSON_STR)?)?; + + let mut overrides = HashMap::new(); + overrides.insert(String::from("pref_string"), PrefValue::from("new value")); + overrides.insert( + String::from("group.nested.nested_i64"), + PrefValue::from(1001), ); - assert_eq!(*PREFS.get("extra.stuff"), PrefValue::Boolean(false)); + overrides.insert(String::from("a.renamed.pref"), PrefValue::from(47)); + + let result = prefs.set_all(overrides.into_iter()); + assert_eq!(true, result.is_ok()); + + let values = prefs.values(); + assert_eq!("new value", values.read().unwrap().pref_string); + assert_eq!(1001, values.read().unwrap().group.nested.nested_i64); + assert_eq!(47, values.read().unwrap().group.nested.renamed); + Ok(()) +} + +#[test] +fn test_set_all_error_on_unknown_field() -> Result<(), Box> { + let def_prefs: gen::TestPrefs = serde_json::from_str(DEF_JSON_STR)?; + let prefs = Preferences::new(def_prefs, &gen::TEST_PREF_ACCESSORS); + + let mut overrides = HashMap::new(); + overrides.insert(String::from("doesnt_exist"), PrefValue::from(1001)); + + let result = prefs.set_all(overrides.into_iter()); + assert_eq!(true, result.is_err()); + Ok(()) } #[cfg(not(target_os = "android"))] @@ -86,3 +272,63 @@ fn test_default_config_dir_create_read_write() { fs::remove_file(&json_path).unwrap(); } + +static DEF_JSON_STR: &'static str = r#"{ + "pref_string": "hello", + "pref_i64": 23, + "pref_f64": 1.5, + "pref_bool": true, + "group.nested.nested_i64": 333, + "a.renamed.pref": 42 +}"#; + +static USER_JSON_STR: &'static str = r#"{ + "pref_string": "bye", + "pref_i64": -1, + "pref_f64": -1.0, + "pref_bool": false, + "group.nested.nested_i64": -1, + "a.renamed.pref": -1 +}"#; + +static WITHOUT_DEFAULTS_JSON_STR: &'static str = r#"{ + "pref_string": "bye", + "pref_i64": -1, + "pref_f64": -1.0, + "pref_bool": false, + "group.nested.nested_i64": -1, + "a.renamed.pref": -1, + "computed_default_value": -1, + "default_value": -1 +}"#; + +mod gen { + use servo_config::pref_util::{Accessor, PrefValue}; + use servo_config_plugins::build_structs; + + fn compute_default() -> i64 { + 555 + } + + build_structs! { + accessor_type = Accessor::, + gen_accessors = TEST_PREF_ACCESSORS, + gen_types = TestPrefs { + pref_string: String, + pref_i64: i64, + pref_f64: f64, + pref_bool: bool, + #[serde(default)] + default_value: i64, + #[serde(default = "compute_default")] + computed_default_value: i64, + group: { + nested: { + nested_i64: i64, + #[serde(rename = "a.renamed.pref")] + renamed: i64, + } + } + } + } +} diff --git a/components/config_plugins/Cargo.toml b/components/config_plugins/Cargo.toml new file mode 100644 index 000000000000..db94f48beec0 --- /dev/null +++ b/components/config_plugins/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "servo_config_plugins" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +edition = "2018" +publish = false + +[lib] +name = "servo_config_plugins" +proc-macro = true +path = "lib.rs" + +[dependencies] +syn = "0.15" +quote = "0.6" +proc-macro2 = "0.4" +itertools = "0.8" + diff --git a/components/config_plugins/lib.rs b/components/config_plugins/lib.rs new file mode 100644 index 000000000000..b93542d737bd --- /dev/null +++ b/components/config_plugins/lib.rs @@ -0,0 +1,219 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#![feature(proc_macro_diagnostic)] + +extern crate proc_macro; + +use itertools::Itertools; +use proc_macro2::{Span, TokenStream}; +use quote::*; +use std::collections::{hash_map, HashMap}; +use std::{fmt::Write, iter}; +use syn::{ + parse::Result, parse_macro_input, spanned::Spanned, Attribute, Ident, Lit, LitStr, Meta, + MetaList, MetaNameValue, NestedMeta, Path, +}; + +mod parse; +use parse::*; + +#[proc_macro] +pub fn build_structs(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input: MacroInput = parse_macro_input!(tokens); + let out = Build::new(&input) + .build(&input.type_def) + .unwrap_or_else(|e| { + proc_macro::Diagnostic::spanned( + e.span().unwrap(), + proc_macro::Level::Error, + format!("{}", e), + ) + .emit(); + TokenStream::new() + }); + out.into() +} + +struct Build { + root_type_name: Ident, + gen_accessors: Ident, + accessor_type: Path, + output: TokenStream, + path_stack: Vec, + path_map: HashMap>, +} + +impl Build { + fn new(input: &MacroInput) -> Self { + Build { + root_type_name: input.type_def.type_name.clone(), + gen_accessors: input.gen_accessors.clone(), + accessor_type: input.accessor_type.clone(), + output: TokenStream::new(), + path_stack: Vec::new(), + path_map: HashMap::new(), + } + } + + fn build(mut self, type_def: &RootTypeDef) -> Result { + self.walk(&type_def.type_def)?; + self.build_accessors(); + Ok(self.output) + } + + fn walk(&mut self, type_def: &NewTypeDef) -> Result<()> { + self.define_pref_struct(type_def)?; + + for field in type_def.fields.iter() { + self.path_stack.push(field.name.clone()); + + if let FieldType::NewTypeDef(new_def) = &field.field_type { + self.walk(&new_def)?; + } else { + let pref_name = + self.pref_name(field, &self.path_stack[..self.path_stack.len() - 1]); + if let hash_map::Entry::Vacant(slot) = self.path_map.entry(pref_name) { + slot.insert(self.path_stack.clone()); + } else { + return Err(err(&field.name, "duplicate preference name")); + } + } + + self.path_stack.pop(); + } + Ok(()) + } + + fn define_pref_struct(&mut self, type_def: &NewTypeDef) -> Result<()> { + let struct_name = self.path_to_name(self.path_stack.iter()); + let field_defs = type_def + .fields + .iter() + .map(|field| self.field_to_tokens(field, &self.path_stack)) + .collect::>>()?; + self.output.extend(quote! { + #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] + pub struct #struct_name { + #(#field_defs), * + } + }); + Ok(()) + } + + fn build_accessors(&mut self) { + let accessor_type = &self.accessor_type; + let values = self.path_map.iter().map(|(key, path)| { + quote! { + map.insert(String::from(#key), + #accessor_type::new( + |prefs| prefs #(.#path)*.clone().into(), + |prefs, value| prefs #(.#path)* = value.into() + ) + ); + } + }); + + let gen_accessors = &self.gen_accessors; + let num_prefs = self.path_map.len(); + + self.output.extend(quote! { + lazy_static::lazy_static! { + pub static ref #gen_accessors: std::collections::HashMap = { + let mut map = std::collections::HashMap::with_capacity(#num_prefs); + #(#values)* + map + }; + } + }); + } + + fn pref_name(&self, field: &Field, path_stack: &[Ident]) -> String { + field + .get_field_name_mapping() + .map(|pref_attr| pref_attr.value()) + .unwrap_or_else(|| { + path_stack + .iter() + .chain(iter::once(&field.name)) + .map(Ident::to_string) + .intersperse(String::from(".")) + .collect() + }) + } + + fn field_to_tokens(&self, field: &Field, path_stack: &[Ident]) -> Result { + let name = &field.name; + Ok(match &field.field_type { + FieldType::NewTypeDef(_) => { + let type_name = self.path_to_name(path_stack.iter().chain(iter::once(name))); + quote! { + #[serde(flatten)] + pub #name: #type_name + } + }, + FieldType::Existing(type_name) => { + let pref_name = self.pref_name(field, &path_stack); + let attributes = field.get_attributes(&pref_name); + quote! { + #attributes + pub #name: #type_name + } + }, + }) + } + + fn path_to_name<'p, P: Iterator + 'p>(&self, path: P) -> Ident { + let mut name = format!("{}", self.root_type_name); + for part in path { + name.write_fmt(format_args!("__{}", part)).unwrap(); + } + Ident::new(&name, Span::call_site()) + } +} + +impl Field { + fn get_attributes(&self, pref_name: &str) -> TokenStream { + let mut tokens = TokenStream::new(); + for attr in self + .attributes + .iter() + .filter(|attr| attr_to_pref_name(attr).is_none()) + { + attr.to_tokens(&mut tokens); + } + tokens.extend(quote! { + #[serde(rename = #pref_name)] + }); + tokens + } + + fn get_field_name_mapping(&self) -> Option { + self.attributes.iter().filter_map(attr_to_pref_name).next() + } +} + +fn attr_to_pref_name(attr: &Attribute) -> Option { + attr.parse_meta().ok().and_then(|meta| { + if let Meta::List(MetaList { ident, nested, .. }) = meta { + if ident.to_string() == "serde" { + if let Some(NestedMeta::Meta(Meta::NameValue(MetaNameValue { + ref ident, + lit: Lit::Str(val), + .. + }))) = nested.iter().next() + { + if ident.to_string() == "rename" { + return Some(val.clone()); + } + } + } + } + None + }) +} + +fn err(s: S, msg: &str) -> syn::Error { + syn::Error::new(s.span(), msg) +} diff --git a/components/config_plugins/parse.rs b/components/config_plugins/parse.rs new file mode 100644 index 000000000000..715b60f50e90 --- /dev/null +++ b/components/config_plugins/parse.rs @@ -0,0 +1,149 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use proc_macro2::Span; +use syn::parse::{Parse, ParseStream, Result}; +use syn::{braced, punctuated::Punctuated, token, Attribute, Ident, Path, Token, Type}; + +#[allow(non_camel_case_types)] +mod kw { + syn::custom_keyword!(accessor_type); + syn::custom_keyword!(gen_accessors); + syn::custom_keyword!(gen_types); +} + +pub struct MacroInput { + pub type_def: RootTypeDef, + pub gen_accessors: Ident, + pub accessor_type: Path, +} + +enum MacroArg { + GenAccessors(ArgInner), + AccessorType(ArgInner), + Types(ArgInner), +} + +struct ArgInner { + _field_kw: K, + _equals: Token![=], + value: V, +} + +pub struct Field { + pub attributes: Vec, + pub name: Ident, + _colon: Token![:], + pub field_type: FieldType, +} + +pub enum FieldType { + Existing(Type), + NewTypeDef(NewTypeDef), +} + +pub struct NewTypeDef { + _braces: token::Brace, + pub fields: Punctuated, +} + +pub struct RootTypeDef { + pub type_name: Ident, + pub type_def: NewTypeDef, +} + +impl Parse for MacroInput { + fn parse(input: ParseStream) -> Result { + let fields: Punctuated = input.parse_terminated(MacroArg::parse)?; + let mut gen_accessors = None; + let mut type_def = None; + let mut accessor_type = None; + for arg in fields.into_iter() { + match arg { + MacroArg::GenAccessors(ArgInner { value, .. }) => gen_accessors = Some(value), + MacroArg::AccessorType(ArgInner { value, .. }) => accessor_type = Some(value), + MacroArg::Types(ArgInner { value, .. }) => type_def = Some(value), + } + } + + fn missing_attr(att_name: &str) -> syn::Error { + syn::Error::new( + Span::call_site(), + format!("Expected `{}` attribute", att_name), + ) + } + + Ok(MacroInput { + type_def: type_def.ok_or_else(|| missing_attr("gen_types"))?, + gen_accessors: gen_accessors.ok_or_else(|| missing_attr("gen_accessors"))?, + accessor_type: accessor_type.ok_or_else(|| missing_attr("accessor_type"))?, + }) + } +} + +impl Parse for MacroArg { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(kw::gen_types) { + Ok(MacroArg::Types(input.parse()?)) + } else if lookahead.peek(kw::gen_accessors) { + Ok(MacroArg::GenAccessors(input.parse()?)) + } else if lookahead.peek(kw::accessor_type) { + Ok(MacroArg::AccessorType(input.parse()?)) + } else { + Err(lookahead.error()) + } + } +} + +impl Parse for ArgInner { + fn parse(input: ParseStream) -> Result { + Ok(ArgInner { + _field_kw: input.parse()?, + _equals: input.parse()?, + value: input.parse()?, + }) + } +} + +impl Parse for Field { + fn parse(input: ParseStream) -> Result { + Ok(Field { + attributes: input.call(Attribute::parse_outer)?, + name: input.parse()?, + _colon: input.parse()?, + field_type: input.parse()?, + }) + } +} + +impl Parse for RootTypeDef { + fn parse(input: ParseStream) -> Result { + Ok(RootTypeDef { + type_name: input.parse()?, + type_def: input.parse()?, + }) + } +} + +impl Parse for NewTypeDef { + fn parse(input: ParseStream) -> Result { + let content; + #[allow(clippy::eval_order_dependence)] + Ok(NewTypeDef { + _braces: braced!(content in input), + fields: content.parse_terminated(Field::parse)?, + }) + } +} + +impl Parse for FieldType { + fn parse(input: ParseStream) -> Result { + if input.peek(token::Brace) { + Ok(FieldType::NewTypeDef(input.parse()?)) + } else { + Ok(FieldType::Existing(input.parse()?)) + } + } +} diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index e41205799127..9cd1008a1770 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -150,8 +150,7 @@ use script_traits::{IFrameSizeMsg, WindowSizeData, WindowSizeType}; use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory}; use script_traits::{SWManagerMsg, ScopeThings, UpdatePipelineIdReason, WebDriverCommandMsg}; use serde::{Deserialize, Serialize}; -use servo_config::opts; -use servo_config::prefs::PREFS; +use servo_config::{opts, pref}; use servo_rand::{random, Rng, SeedableRng, ServoRng}; use servo_remutex::ReentrantMutex; use servo_url::{Host, ImmutableOrigin, ServoUrl}; @@ -3506,10 +3505,7 @@ where let pipelines_to_evict = { let session_history = self.get_joint_session_history(top_level_browsing_context_id); - let history_length = PREFS - .get("session-history.max-length") - .as_u64() - .unwrap_or(20) as usize; + let history_length = pref!(session_history.max_length) as usize; // The past is stored with older entries at the front. // We reverse the iter so that newer entries are at the front and then diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index be369c0c08eb..71d3f0aea62a 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -31,7 +31,7 @@ use script_traits::{LayoutControlMsg, LayoutMsg, LoadData}; use script_traits::{NewLayoutInfo, SWManagerMsg, SWManagerSenders}; use script_traits::{ScriptThreadFactory, TimerSchedulerMsg, WindowSizeData}; use servo_config::opts::{self, Opts}; -use servo_config::prefs::{Pref, PREFS}; +use servo_config::{prefs, prefs::PrefValue}; use servo_url::ServoUrl; use std::collections::{HashMap, HashSet}; #[cfg(not(windows))] @@ -286,7 +286,7 @@ impl Pipeline { load_data: state.load_data.clone(), script_port: script_port, opts: (*opts::get()).clone(), - prefs: PREFS.cloned(), + prefs: prefs::pref_map().iter().collect(), pipeline_port: pipeline_port, pipeline_namespace_id: state.pipeline_namespace_id, layout_content_process_shutdown_chan: layout_content_process_shutdown_chan, @@ -482,7 +482,7 @@ pub struct UnprivilegedPipelineContent { load_data: LoadData, script_port: IpcReceiver, opts: Opts, - prefs: HashMap, + prefs: HashMap, pipeline_port: IpcReceiver, pipeline_namespace_id: PipelineNamespaceId, layout_content_process_shutdown_chan: IpcSender<()>, @@ -681,7 +681,7 @@ impl UnprivilegedPipelineContent { self.opts.clone() } - pub fn prefs(&self) -> HashMap { + pub fn prefs(&self) -> HashMap { self.prefs.clone() } diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index ecc4153ab71b..db48aa483aa8 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -88,7 +88,7 @@ use selectors::Element; use servo_arc::Arc as ServoArc; use servo_atoms::Atom; use servo_config::opts; -use servo_config::prefs::PREFS; +use servo_config::pref; use servo_geometry::MaxRect; use servo_url::ServoUrl; use std::borrow::ToOwned; @@ -536,11 +536,7 @@ impl LayoutThread { element_inner_text_response: String::new(), })), webrender_image_cache: Arc::new(RwLock::new(FnvHashMap::default())), - timer: if PREFS - .get("layout.animations.test.enabled") - .as_boolean() - .unwrap_or(false) - { + timer: if pref!(layout.animations.test.enabled) { Timer::test_mode() } else { Timer::new() diff --git a/components/net/filemanager_thread.rs b/components/net/filemanager_thread.rs index d10b149bc7c3..2052b6e0fed7 100644 --- a/components/net/filemanager_thread.rs +++ b/components/net/filemanager_thread.rs @@ -18,7 +18,6 @@ use net_traits::filemanager_thread::{ use net_traits::http_percent_encode; use net_traits::response::{Response, ResponseBody}; use servo_arc::Arc as ServoArc; -use servo_config::prefs::PREFS; use std::collections::HashMap; use std::fs::File; use std::io::{BufRead, BufReader, Read, Seek, SeekFrom}; @@ -311,7 +310,7 @@ impl FileManagerStore { // Check if the select_files preference is enabled // to ensure process-level security against compromised script; // Then try applying opt_test_path directly for testing convenience - let opt_s = if select_files_pref_enabled() { + let opt_s = if pref!(dom.testing.html_input_element.select_files.enabled) { opt_test_path } else { self.query_files_from_embedder(patterns, false, embedder_proxy) @@ -342,7 +341,7 @@ impl FileManagerStore { // Check if the select_files preference is enabled // to ensure process-level security against compromised script; // Then try applying opt_test_paths directly for testing convenience - let opt_v = if select_files_pref_enabled() { + let opt_v = if pref!(dom.testing.html_input_element.select_files.enabled) { opt_test_paths } else { self.query_files_from_embedder(patterns, true, embedder_proxy) @@ -740,13 +739,6 @@ impl FileManagerStore { } } -fn select_files_pref_enabled() -> bool { - PREFS - .get("dom.testing.htmlinputelement.select_files.enabled") - .as_boolean() - .unwrap_or(false) -} - fn read_file_in_chunks( sender: &IpcSender>, file: &mut File, diff --git a/components/net/http_cache.rs b/components/net/http_cache.rs index 988d7666b8c0..9e430ceea3e2 100644 --- a/components/net/http_cache.rs +++ b/components/net/http_cache.rs @@ -22,7 +22,6 @@ use net_traits::request::Request; use net_traits::response::{HttpsState, Response, ResponseBody}; use net_traits::{FetchMetadata, Metadata, ResourceFetchTiming}; use servo_arc::Arc; -use servo_config::prefs::PREFS; use servo_url::ServoUrl; use std::collections::HashMap; use std::ops::Bound; @@ -766,11 +765,7 @@ impl HttpCache { /// Storing Responses in Caches. /// pub fn store(&mut self, request: &Request, response: &Response) { - if PREFS - .get("network.http-cache.disabled") - .as_boolean() - .unwrap_or(false) - { + if pref!(network.http_cache.disabled) { return; } if request.method != Method::GET { diff --git a/components/net/lib.rs b/components/net/lib.rs index fb3c6fff78b5..08b46844a485 100644 --- a/components/net/lib.rs +++ b/components/net/lib.rs @@ -16,6 +16,8 @@ extern crate matches; extern crate profile_traits; #[macro_use] extern crate serde; +#[macro_use] +extern crate servo_config; pub mod connector; pub mod cookie; diff --git a/components/net/tests/filemanager_thread.rs b/components/net/tests/filemanager_thread.rs index 5fd01eb8d437..baf38a80392f 100644 --- a/components/net/tests/filemanager_thread.rs +++ b/components/net/tests/filemanager_thread.rs @@ -10,7 +10,7 @@ use net_traits::blob_url_store::BlobURLStoreError; use net_traits::filemanager_thread::{ FileManagerThreadError, FileManagerThreadMsg, ReadFileProgress, }; -use servo_config::prefs::{PrefValue, PREFS}; +use servo_config::set_pref; use std::fs::File; use std::io::Read; use std::path::PathBuf; @@ -18,10 +18,7 @@ use std::path::PathBuf; #[test] fn test_filemanager() { let filemanager = FileManager::new(create_embedder_proxy()); - PREFS.set( - "dom.testing.htmlinputelement.select_files.enabled", - PrefValue::Boolean(true), - ); + set_pref!(dom.testing.html_input_element.select_files.enabled, true); // Try to open a dummy file "components/net/tests/test.jpeg" in tree let mut handler = File::open("tests/test.jpeg").expect("test.jpeg is stolen"); diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index df156fb0eb7b..ea21989178b8 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -2586,7 +2586,7 @@ def definition_body(self): pref = iface.getExtendedAttribute("Pref") if pref: assert isinstance(pref, list) and len(pref) == 1 - conditions.append('PREFS.get("%s").as_boolean().unwrap_or(false)' % pref[0]) + conditions.append('prefs::pref_map().get("%s").as_bool().unwrap_or(false)' % pref[0]) func = iface.getExtendedAttribute("Func") if func: @@ -5977,7 +5977,8 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'crate::dom::globalscope::GlobalScope', 'crate::mem::malloc_size_of_including_raw_self', 'libc', - 'servo_config::prefs::PREFS', + 'servo_config::pref', + 'servo_config::prefs', 'std::borrow::ToOwned', 'std::cmp', 'std::mem', diff --git a/components/script/dom/bindings/guard.rs b/components/script/dom/bindings/guard.rs index 3daca82bd901..5ab9971afb7b 100644 --- a/components/script/dom/bindings/guard.rs +++ b/components/script/dom/bindings/guard.rs @@ -6,7 +6,7 @@ use js::jsapi::JSContext; use js::rust::HandleObject; -use servo_config::prefs::PREFS; +use servo_config::prefs; /// A container with a condition. pub struct Guard { @@ -48,7 +48,7 @@ pub enum Condition { impl Condition { unsafe fn is_satisfied(&self, cx: *mut JSContext, obj: HandleObject) -> bool { match *self { - Condition::Pref(name) => PREFS.get(name).as_boolean().unwrap_or(false), + Condition::Pref(name) => prefs::pref_map().get(name).as_bool().unwrap_or(false), Condition::Func(f) => f(cx, obj), Condition::Satisfied => true, } diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs index ae8308434067..461e37a28def 100644 --- a/components/script/dom/create.rs +++ b/components/script/dom/create.rs @@ -81,7 +81,7 @@ use crate::dom::svgsvgelement::SVGSVGElement; use crate::script_thread::ScriptThread; use html5ever::{LocalName, Prefix, QualName}; use js::jsapi::JSAutoCompartment; -use servo_config::prefs::PREFS; +use servo_config::pref; fn create_svg_element( name: QualName, @@ -101,7 +101,7 @@ fn create_svg_element( }) ); - if !PREFS.get("dom.svg.enabled").as_boolean().unwrap_or(false) { + if !pref!(dom.svg.enabled) { return Element::new(name.local, name.ns, prefix, document); } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index b589d4e5b79f..012aaecbe4f3 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -137,7 +137,7 @@ use script_traits::{AnimationState, DocumentActivity, MouseButton, MouseEventTyp use script_traits::{MsDuration, ScriptMsg, TouchEventType, TouchId, UntrustedNodeAddress}; use servo_arc::Arc; use servo_atoms::Atom; -use servo_config::prefs::PREFS; +use servo_config::pref; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use std::borrow::ToOwned; use std::cell::{Cell, Ref, RefMut}; @@ -1090,16 +1090,9 @@ impl Document { let opt = self.last_click_info.borrow_mut().take(); if let Some((last_time, last_pos)) = opt { - let DBL_CLICK_TIMEOUT = Duration::from_millis( - PREFS - .get("dom.document.dblclick_timeout") - .as_u64() - .unwrap_or(300), - ); - let DBL_CLICK_DIST_THRESHOLD = PREFS - .get("dom.document.dblclick_dist") - .as_u64() - .unwrap_or(1); + let DBL_CLICK_TIMEOUT = + Duration::from_millis(pref!(dom.document.dblclick_timeout) as u64); + let DBL_CLICK_DIST_THRESHOLD = pref!(dom.document.dblclick_dist) as u64; // Calculate distance between this click and the previous click. let line = click_pos - last_pos; @@ -2423,11 +2416,7 @@ impl Document { local_name: &LocalName, is: Option<&LocalName>, ) -> Option> { - if !PREFS - .get("dom.customelements.enabled") - .as_boolean() - .unwrap_or(false) - { + if !pref!(dom.custom_elements.enabled) { return None; } @@ -3165,11 +3154,7 @@ impl Document { error = true; } - if PREFS - .get("dom.fullscreen.test") - .as_boolean() - .unwrap_or(false) - { + if pref!(dom.fullscreen.test) { // For reftests we just take over the current window, // and don't try to really enter fullscreen. info!("Tests don't really enter fullscreen."); diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index c9ccdd9ad56d..ecf3a36c28ef 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -42,7 +42,7 @@ use js::jsapi::JSContext; use js::rust::HandleValue; use profile_traits::ipc; use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource}; -use servo_config::prefs::PREFS; +use servo_config::pref; use std::cell::Ref; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; @@ -232,7 +232,7 @@ impl HTMLCanvasElement { cx: *mut JSContext, options: HandleValue, ) -> Option> { - if !PREFS.is_webgl2_enabled() { + if !pref!(dom.webgl2.enabled) { return None; } if let Some(ctx) = self.context() { diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 703ce9438a79..079bbd9d4bd8 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -64,7 +64,7 @@ use net_traits::request::{CredentialsMode, Destination, RequestInit}; use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, Metadata}; use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType}; use script_layout_interface::HTMLMediaData; -use servo_config::prefs::PREFS; +use servo_config::pref; use servo_media::player::frame::{Frame, FrameRenderer}; use servo_media::player::{PlaybackState, Player, PlayerError, PlayerEvent, StreamType}; use servo_media::ServoMedia; @@ -1121,13 +1121,12 @@ impl HTMLMediaElement { .unwrap() .render_poster_frame(image); self.upcast::().dirty(NodeDamage::OtherNodeDamage); - if let Some(testing_on) = PREFS.get("media.testing.enabled").as_boolean() { - if !testing_on { - return; - } + if pref!(media.testing.enabled) { let window = window_from_node(self); let task_source = window.task_manager().media_element_task_source(); task_source.queue_simple_event(self.upcast(), atom!("postershown"), &window); + } else { + return; } } } diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs index 26c35ed9a3e0..a8e829c3d1ca 100644 --- a/components/script/dom/htmlmetaelement.rs +++ b/components/script/dom/htmlmetaelement.rs @@ -21,7 +21,7 @@ use dom_struct::dom_struct; use html5ever::{LocalName, Prefix}; use parking_lot::RwLock; use servo_arc::Arc; -use servo_config::prefs::PREFS; +use servo_config::pref; use std::sync::atomic::AtomicBool; use style::attr::AttrValue; use style::media_queries::MediaList; @@ -98,11 +98,7 @@ impl HTMLMetaElement { } fn apply_viewport(&self) { - if !PREFS - .get("layout.viewport.enabled") - .as_boolean() - .unwrap_or(false) - { + if !pref!(layout.viewport.enabled) { return; } let element = self.upcast::(); diff --git a/components/script/dom/mouseevent.rs b/components/script/dom/mouseevent.rs index 695ed4c3d4cf..6ae1c2efab9b 100644 --- a/components/script/dom/mouseevent.rs +++ b/components/script/dom/mouseevent.rs @@ -16,7 +16,7 @@ use crate::dom::uievent::UIEvent; use crate::dom::window::Window; use dom_struct::dom_struct; use euclid::Point2D; -use servo_config::prefs::PREFS; +use servo_config::pref; use std::cell::Cell; use std::default::Default; @@ -194,11 +194,7 @@ impl MouseEventMethods for MouseEvent { // This returns the same result as current gecko. // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which fn Which(&self) -> i32 { - if PREFS - .get("dom.mouseevent.which.enabled") - .as_boolean() - .unwrap_or(false) - { + if pref!(dom.mouse_event.which.enabled) { (self.button.get() + 1) as i32 } else { 0 diff --git a/components/script/dom/paintworkletglobalscope.rs b/components/script/dom/paintworkletglobalscope.rs index 9e8e91496bef..fe119eefbf3b 100644 --- a/components/script/dom/paintworkletglobalscope.rs +++ b/components/script/dom/paintworkletglobalscope.rs @@ -50,7 +50,7 @@ use profile_traits::ipc; use script_traits::Painter; use script_traits::{DrawAPaintImageResult, PaintWorkletError}; use servo_atoms::Atom; -use servo_config::prefs::PREFS; +use servo_config::pref; use servo_url::ServoUrl; use std::cell::Cell; use std::collections::hash_map::Entry; @@ -439,10 +439,7 @@ impl PaintWorkletGlobalScope { .expect("Locking a painter.") .schedule_a_worklet_task(WorkletTask::Paint(task)); - let timeout = PREFS - .get("dom.worklet.timeout_ms") - .as_u64() - .unwrap_or(10u64); + let timeout = pref!(dom.worklet.timeout_ms) as u64; receiver .recv_timeout(Duration::from_millis(timeout)) diff --git a/components/script/dom/permissions.rs b/components/script/dom/permissions.rs index aa8ea5e27db0..3732c60d1912 100644 --- a/components/script/dom/permissions.rs +++ b/components/script/dom/permissions.rs @@ -22,7 +22,7 @@ use js::jsapi::{JSContext, JSObject}; use js::jsval::{ObjectValue, UndefinedValue}; #[cfg(target_os = "linux")] use servo_config::opts; -use servo_config::prefs::PREFS; +use servo_config::pref; use std::rc::Rc; #[cfg(target_os = "linux")] use tinyfiledialogs::{self, MessageBoxIcon, YesNo}; @@ -307,11 +307,7 @@ pub fn get_descriptor_permission_state( let state = if allowed_in_nonsecure_contexts(&permission_name) { PermissionState::Prompt } else { - if PREFS - .get("dom.permissions.testing.allowed_in_nonsecure_contexts") - .as_boolean() - .unwrap_or(false) - { + if pref!(dom.permissions.testing.allowed_in_nonsecure_contexts) { PermissionState::Granted } else { settings diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index 0c8ca193228d..7c0842d7b65f 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -35,7 +35,7 @@ use net_traits::{load_whole_resource, CustomResponseMediator, IpcSend}; use script_traits::{ ScopeThings, ServiceWorkerMsg, TimerEvent, WorkerGlobalScopeInit, WorkerScriptLoadOrigin, }; -use servo_config::prefs::PREFS; +use servo_config::pref; use servo_rand::random; use servo_url::ServoUrl; use std::thread; @@ -336,10 +336,7 @@ impl ServiceWorkerGlobalScope { thread::Builder::new() .name("SWTimeoutThread".to_owned()) .spawn(move || { - let sw_lifetime_timeout = PREFS - .get("dom.serviceworker.timeout_seconds") - .as_u64() - .unwrap(); + let sw_lifetime_timeout = pref!(dom.serviceworker.timeout_seconds) as u64; thread::sleep(Duration::new(sw_lifetime_timeout, 0)); let _ = timer_chan.send(()); }) diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index c4d0f6279fa0..3c0d321910ce 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -52,7 +52,7 @@ use profile_traits::time::{ profile, ProfilerCategory, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType, }; use script_traits::DocumentActivity; -use servo_config::prefs::PREFS; +use servo_config::pref; use servo_url::ServoUrl; use std::borrow::Cow; use std::cell::Cell; @@ -135,11 +135,7 @@ impl ServoParser { } pub fn parse_html_document(document: &Document, input: DOMString, url: ServoUrl) { - let parser = if PREFS - .get("dom.servoparser.async_html_tokenizer.enabled") - .as_boolean() - .unwrap() - { + let parser = if pref!(dom.servoparser.async_html_tokenizer.enabled) { ServoParser::new( document, Tokenizer::AsyncHtml(self::async_html::Tokenizer::new(document, url, None)), diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs index 14b607d790d5..505e51325a9c 100644 --- a/components/script/dom/testbinding.rs +++ b/components/script/dom/testbinding.rs @@ -57,7 +57,7 @@ use js::rust::CustomAutoRooterGuard; use js::rust::{HandleObject, HandleValue}; use js::typedarray; use script_traits::MsDuration; -use servo_config::prefs::PREFS; +use servo_config::prefs; use std::borrow::ToOwned; use std::ptr; use std::ptr::NonNull; @@ -887,12 +887,15 @@ impl TestBindingMethods for TestBinding { #[allow(unsafe_code)] unsafe fn PassVariadicObject(&self, _: *mut JSContext, _: Vec<*mut JSObject>) {} fn BooleanMozPreference(&self, pref_name: DOMString) -> bool { - PREFS.get(pref_name.as_ref()).as_boolean().unwrap_or(false) + prefs::pref_map() + .get(pref_name.as_ref()) + .as_bool() + .unwrap_or(false) } fn StringMozPreference(&self, pref_name: DOMString) -> DOMString { - PREFS + prefs::pref_map() .get(pref_name.as_ref()) - .as_string() + .as_str() .map(|s| DOMString::from(s)) .unwrap_or_else(|| DOMString::new()) } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 022f78a4644f..c0966ab8cc7d 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -71,7 +71,7 @@ use net_traits::image_cache::ImageResponse; use pixels::{self, PixelFormat}; use script_layout_interface::HTMLCanvasDataSource; use serde::{Deserialize, Serialize}; -use servo_config::prefs::PREFS; +use servo_config::pref; use std::cell::Cell; use std::cmp; use std::ptr::{self, NonNull}; @@ -173,10 +173,7 @@ impl WebGLRenderingContext { size: Size2D, attrs: GLContextAttributes, ) -> Result { - if let Some(true) = PREFS - .get("webgl.testing.context_creation_error") - .as_boolean() - { + if pref!(webgl.testing.context_creation_error) { return Err("WebGL context creation error forced by pref `webgl.testing.context_creation_error`".into()); } diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index 2ee27ab38bbd..6cad5d4238e8 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -49,7 +49,7 @@ use malloc_size_of::MallocSizeOfOps; use msg::constellation_msg::PipelineId; use profile_traits::mem::{Report, ReportKind, ReportsChan}; use servo_config::opts; -use servo_config::prefs::PREFS; +use servo_config::pref; use std::cell::Cell; use std::fmt; use std::io::{stdout, Write}; @@ -375,204 +375,151 @@ unsafe fn new_rt_and_cx_with_parent(parent: Option) -> Runtime { // Enable or disable the JITs. let cx_opts = &mut *ContextOptionsRef(cx); - if let Some(val) = PREFS.get("js.baseline.enabled").as_boolean() { - cx_opts.set_baseline_(val); - } - if let Some(val) = PREFS.get("js.ion.enabled").as_boolean() { - cx_opts.set_ion_(val); - } - if let Some(val) = PREFS.get("js.asmjs.enabled").as_boolean() { - cx_opts.set_asmJS_(val); - } - if let Some(val) = PREFS.get("js.wasm.enabled").as_boolean() { - cx_opts.set_wasm_(val); - if val { - // If WASM is enabled without setting the buildIdOp, - // initializing a module will report an out of memory error. - // https://dxr.mozilla.org/mozilla-central/source/js/src/wasm/WasmTypes.cpp#458 - SetBuildIdOp(cx, Some(servo_build_id)); - } - } - if let Some(val) = PREFS.get("js.wasm.baseline.enabled").as_boolean() { - cx_opts.set_wasmBaseline_(val); - } - if let Some(val) = PREFS.get("js.wasm.ion.enabled").as_boolean() { - cx_opts.set_wasmIon_(val); - } - if let Some(val) = PREFS.get("js.strict.enabled").as_boolean() { - cx_opts.set_extraWarnings_(val); - } + cx_opts.set_baseline_(pref!(js.baseline.enabled)); + cx_opts.set_ion_(pref!(js.ion.enabled)); + cx_opts.set_asmJS_(pref!(js.asmjs.enabled)); + let wasm_enabled = pref!(js.wasm.enabled); + cx_opts.set_wasm_(wasm_enabled); + if wasm_enabled { + // If WASM is enabled without setting the buildIdOp, + // initializing a module will report an out of memory error. + // https://dxr.mozilla.org/mozilla-central/source/js/src/wasm/WasmTypes.cpp#458 + SetBuildIdOp(cx, Some(servo_build_id)); + } + cx_opts.set_wasmBaseline_(pref!(js.wasm.baseline.enabled)); + cx_opts.set_wasmIon_(pref!(js.wasm.ion.enabled)); + cx_opts.set_extraWarnings_(pref!(js.strict.enabled)); // TODO: handle js.strict.debug.enabled // TODO: handle js.throw_on_asmjs_validation_failure (needs new Spidermonkey) - if let Some(val) = PREFS.get("js.native_regexp.enabled").as_boolean() { - cx_opts.set_nativeRegExp_(val); - } - if let Some(val) = PREFS.get("js.parallel_parsing.enabled").as_boolean() { - JS_SetParallelParsingEnabled(cx, val); - } - if let Some(val) = PREFS.get("js.offthread_compilation_enabled").as_boolean() { - JS_SetOffthreadIonCompilationEnabled(cx, val); - } - if let Some(val) = PREFS - .get("js.baseline.unsafe_eager_compilation.enabled") - .as_boolean() - { - let trigger: i32 = if val { 0 } else { -1 }; - JS_SetGlobalJitCompilerOption( - cx, - JSJitCompilerOption::JSJITCOMPILER_BASELINE_WARMUP_TRIGGER, - trigger as u32, - ); - } - if let Some(val) = PREFS - .get("js.ion.unsafe_eager_compilation.enabled") - .as_boolean() - { - let trigger: i64 = if val { 0 } else { -1 }; - JS_SetGlobalJitCompilerOption( - cx, - JSJitCompilerOption::JSJITCOMPILER_ION_WARMUP_TRIGGER, - trigger as u32, - ); - } + cx_opts.set_nativeRegExp_(pref!(js.native_regex.enabled)); + JS_SetParallelParsingEnabled(cx, pref!(js.parallel_parsing.enabled)); + JS_SetOffthreadIonCompilationEnabled(cx, pref!(js.offthread_compilation.enabled)); + JS_SetGlobalJitCompilerOption( + cx, + JSJitCompilerOption::JSJITCOMPILER_BASELINE_WARMUP_TRIGGER, + if pref!(js.baseline.unsafe_eager_compilation.enabled) { + 0 + } else { + u32::max_value() + }, + ); + JS_SetGlobalJitCompilerOption( + cx, + JSJitCompilerOption::JSJITCOMPILER_ION_WARMUP_TRIGGER, + if pref!(js.ion.unsafe_eager_compilation.enabled) { + 0 + } else { + u32::max_value() + }, + ); // TODO: handle js.discard_system_source.enabled // TODO: handle js.asyncstack.enabled (needs new Spidermonkey) // TODO: handle js.throw_on_debugee_would_run (needs new Spidermonkey) // TODO: handle js.dump_stack_on_debugee_would_run (needs new Spidermonkey) - if let Some(val) = PREFS.get("js.werror.enabled").as_boolean() { - cx_opts.set_werror_(val); - } + cx_opts.set_werror_(pref!(js.werror.enabled)); // TODO: handle js.shared_memory.enabled - if let Some(val) = PREFS.get("js.mem.high_water_mark").as_i64() { - JS_SetGCParameter( - cx, - JSGCParamKey::JSGC_MAX_MALLOC_BYTES, - val as u32 * 1024 * 1024, - ); - } - if let Some(val) = PREFS.get("js.mem.max").as_i64() { - let max = if val <= 0 || val >= 0x1000 { - -1 - } else { - val * 1024 * 1024 - }; - JS_SetGCParameter(cx, JSGCParamKey::JSGC_MAX_BYTES, max as u32); - } + JS_SetGCParameter( + cx, + JSGCParamKey::JSGC_MAX_MALLOC_BYTES, + (pref!(js.mem.high_water_mark) * 1024 * 1024) as u32, + ); + JS_SetGCParameter( + cx, + JSGCParamKey::JSGC_MAX_BYTES, + in_range(pref!(js.mem.max), 1, 0x100) + .map(|val| (val * 1024 * 1024) as u32) + .unwrap_or(u32::max_value()), + ); // NOTE: This is disabled above, so enabling it here will do nothing for now. - if let Some(val) = PREFS.get("js.mem.gc.incremental.enabled").as_boolean() { - let compartment = if let Some(val) = PREFS.get("js.mem.gc.per_zone.enabled").as_boolean() { - val - } else { - false - }; - let mode = if val { - JSGCMode::JSGC_MODE_INCREMENTAL - } else if compartment { - JSGCMode::JSGC_MODE_ZONE - } else { - JSGCMode::JSGC_MODE_GLOBAL - }; - JS_SetGCParameter(cx, JSGCParamKey::JSGC_MODE, mode as u32); - } - if let Some(val) = PREFS.get("js.mem.gc.incremental.slice_ms").as_i64() { - if val >= 0 && val < 100000 { - JS_SetGCParameter(cx, JSGCParamKey::JSGC_SLICE_TIME_BUDGET, val as u32); - } - } - if let Some(val) = PREFS.get("js.mem.gc.compacting.enabled").as_boolean() { - JS_SetGCParameter(cx, JSGCParamKey::JSGC_COMPACTING_ENABLED, val as u32); - } - if let Some(val) = PREFS.get("js.mem.gc.high_frequency_time_limit_ms").as_i64() { - if val >= 0 && val < 10000 { - JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_TIME_LIMIT, val as u32); - } - } - if let Some(val) = PREFS - .get("js.mem.gc.dynamic_mark_slice.enabled") - .as_boolean() - { - JS_SetGCParameter(cx, JSGCParamKey::JSGC_DYNAMIC_MARK_SLICE, val as u32); - } - if let Some(val) = PREFS - .get("js.mem.gc.dynamic_heap_growth.enabled") - .as_boolean() - { - JS_SetGCParameter(cx, JSGCParamKey::JSGC_DYNAMIC_HEAP_GROWTH, val as u32); + let js_gc_mode = if pref!(js.mem.gc.incremental.enabled) { + JSGCMode::JSGC_MODE_INCREMENTAL + } else if pref!(js.mem.gc.per_zone.enabled) { + JSGCMode::JSGC_MODE_ZONE + } else { + JSGCMode::JSGC_MODE_GLOBAL + }; + JS_SetGCParameter(cx, JSGCParamKey::JSGC_MODE, js_gc_mode as u32); + if let Some(val) = in_range(pref!(js.mem.gc.incremental.slice_ms), 0, 100_000) { + JS_SetGCParameter(cx, JSGCParamKey::JSGC_SLICE_TIME_BUDGET, val as u32); + } + JS_SetGCParameter( + cx, + JSGCParamKey::JSGC_COMPACTING_ENABLED, + pref!(js.mem.gc.compacting.enabled) as u32, + ); + + if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_time_limit_ms), 0, 10_000) { + JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_TIME_LIMIT, val as u32); } - if let Some(val) = PREFS.get("js.mem.gc.low_frequency_heap_growth").as_i64() { - if val >= 0 && val < 10000 { - JS_SetGCParameter(cx, JSGCParamKey::JSGC_LOW_FREQUENCY_HEAP_GROWTH, val as u32); - } + JS_SetGCParameter( + cx, + JSGCParamKey::JSGC_DYNAMIC_MARK_SLICE, + pref!(js.mem.gc.dynamic_mark_slice.enabled) as u32, + ); + JS_SetGCParameter( + cx, + JSGCParamKey::JSGC_DYNAMIC_HEAP_GROWTH, + pref!(js.mem.gc.dynamic_heap_growth.enabled) as u32, + ); + if let Some(val) = in_range(pref!(js.mem.gc.low_frequency_heap_growth), 0, 10_000) { + JS_SetGCParameter(cx, JSGCParamKey::JSGC_LOW_FREQUENCY_HEAP_GROWTH, val as u32); } - if let Some(val) = PREFS - .get("js.mem.gc.high_frequency_heap_growth_min") - .as_i64() - { - if val >= 0 && val < 10000 { - JS_SetGCParameter( - cx, - JSGCParamKey::JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN, - val as u32, - ); - } + if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_heap_growth_min), 0, 10_000) { + JS_SetGCParameter( + cx, + JSGCParamKey::JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN, + val as u32, + ); } - if let Some(val) = PREFS - .get("js.mem.gc.high_frequency_heap_growth_max") - .as_i64() - { - if val >= 0 && val < 10000 { - JS_SetGCParameter( - cx, - JSGCParamKey::JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX, - val as u32, - ); - } + if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_heap_growth_max), 0, 10_000) { + JS_SetGCParameter( + cx, + JSGCParamKey::JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX, + val as u32, + ); } - if let Some(val) = PREFS.get("js.mem.gc.high_frequency_low_limit_mb").as_i64() { - if val >= 0 && val < 10000 { - JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_LOW_LIMIT, val as u32); - } + if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_low_limit_mb), 0, 10_000) { + JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_LOW_LIMIT, val as u32); } - if let Some(val) = PREFS.get("js.mem.gc.high_frequency_high_limit_mb").as_i64() { - if val >= 0 && val < 10000 { - JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_HIGH_LIMIT, val as u32); - } + if let Some(val) = in_range(pref!(js.mem.gc.high_frequency_high_limit_mb), 0, 10_000) { + JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_HIGH_LIMIT, val as u32); } - if let Some(val) = PREFS.get("js.mem.gc.allocation_threshold_factor").as_i64() { - if val >= 0 && val < 10000 { - JS_SetGCParameter( - cx, - JSGCParamKey::JSGC_ALLOCATION_THRESHOLD_FACTOR, - val as u32, - ); - } + if let Some(val) = in_range(pref!(js.mem.gc.allocation_threshold_factor), 0, 10_000) { + JS_SetGCParameter( + cx, + JSGCParamKey::JSGC_ALLOCATION_THRESHOLD_FACTOR, + val as u32, + ); } - if let Some(val) = PREFS - .get("js.mem.gc.allocation_threshold_avoid_interrupt_factor") - .as_i64() - { - if val >= 0 && val < 10000 { - JS_SetGCParameter( - cx, - JSGCParamKey::JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT, - val as u32, - ); - } + if let Some(val) = in_range( + pref!(js.mem.gc.allocation_threshold_avoid_interrupt_factor), + 0, + 10_000, + ) { + JS_SetGCParameter( + cx, + JSGCParamKey::JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT, + val as u32, + ); } - if let Some(val) = PREFS.get("js.mem.gc.empty_chunk_count_min").as_i64() { - if val >= 0 && val < 10000 { - JS_SetGCParameter(cx, JSGCParamKey::JSGC_MIN_EMPTY_CHUNK_COUNT, val as u32); - } + if let Some(val) = in_range(pref!(js.mem.gc.empty_chunk_count_min), 0, 10_000) { + JS_SetGCParameter(cx, JSGCParamKey::JSGC_MIN_EMPTY_CHUNK_COUNT, val as u32); } - if let Some(val) = PREFS.get("js.mem.gc.empty_chunk_count_max").as_i64() { - if val >= 0 && val < 10000 { - JS_SetGCParameter(cx, JSGCParamKey::JSGC_MAX_EMPTY_CHUNK_COUNT, val as u32); - } + if let Some(val) = in_range(pref!(js.mem.gc.empty_chunk_count_max), 0, 10_000) { + JS_SetGCParameter(cx, JSGCParamKey::JSGC_MAX_EMPTY_CHUNK_COUNT, val as u32); } Runtime(runtime) } +fn in_range(val: T, min: T, max: T) -> Option { + if val < min || val >= max { + None + } else { + Some(val) + } +} + #[allow(unsafe_code)] unsafe extern "C" fn get_size(obj: *mut JSObject) -> usize { match get_dom_class(obj) { @@ -737,11 +684,11 @@ unsafe extern "C" fn servo_build_id(build_id: *mut BuildIdCharVector) -> bool { unsafe fn set_gc_zeal_options(cx: *mut JSContext) { use js::jsapi::{JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ}; - let level = match PREFS.get("js.mem.gc.zeal.level").as_i64() { + let level = match pref!(js.mem.gc.zeal.level) { Some(level @ 0...14) => level as u8, _ => return, }; - let frequency = match PREFS.get("js.mem.gc.zeal.frequency").as_i64() { + let frequency = match pref!(js.mem.gc.zeal.frequency) { Some(frequency) if frequency >= 0 => frequency as u32, _ => JS_DEFAULT_ZEAL_FREQ, }; diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs index f76ba776be98..2499c7a84948 100644 --- a/components/script/serviceworker_manager.rs +++ b/components/script/serviceworker_manager.rs @@ -17,7 +17,7 @@ use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use net_traits::{CoreResourceMsg, CustomResponseMediator}; use script_traits::{DOMMessage, SWManagerMsg, SWManagerSenders, ScopeThings, ServiceWorkerMsg}; -use servo_config::prefs::PREFS; +use servo_config::pref; use servo_url::ServoUrl; use std::collections::HashMap; use std::thread; @@ -206,8 +206,5 @@ impl ServiceWorkerManager { } pub fn serviceworker_enabled() -> bool { - PREFS - .get("dom.serviceworker.enabled") - .as_boolean() - .unwrap_or(false) + pref!(dom.serviceworker.enabled) } diff --git a/components/script/timers.rs b/components/script/timers.rs index c83fbff96497..45c809394a19 100644 --- a/components/script/timers.rs +++ b/components/script/timers.rs @@ -20,7 +20,7 @@ use js::rust::HandleValue; use script_traits::{precise_time_ms, MsDuration}; use script_traits::{TimerEvent, TimerEventId, TimerEventRequest}; use script_traits::{TimerSchedulerMsg, TimerSource}; -use servo_config::prefs::PREFS; +use servo_config::pref; use std::cell::Cell; use std::cmp::{self, Ord, Ordering}; use std::collections::HashMap; @@ -227,10 +227,7 @@ impl OneshotTimers { } pub fn slow_down(&self) { - let duration = PREFS - .get("js.timers.minimum_duration") - .as_u64() - .unwrap_or(1000); + let duration = pref!(js.timers.minimum_duration) as u64; self.js_timers.set_min_duration(MsDuration::new(duration)); } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index cc3a7c849c21..bdf56372dfff 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -103,7 +103,7 @@ use profile_traits::mem; use profile_traits::time; use script_traits::{ConstellationMsg, SWManagerSenders, ScriptToConstellationChan}; use servo_config::opts; -use servo_config::prefs::PREFS; +use servo_config::{pref, prefs}; use std::borrow::Cow; use std::cmp::max; use std::path::PathBuf; @@ -259,7 +259,7 @@ where script::init(); let mut webvr_heartbeats = Vec::new(); - let webvr_services = if PREFS.is_webvr_enabled() { + let webvr_services = if pref!(dom.webvr.enabled) { let mut services = VRServiceManager::new(); services.register_defaults(); window.register_vr_services(&mut services, &mut webvr_heartbeats); @@ -712,7 +712,9 @@ pub fn run_content_process(token: String) { let unprivileged_content = unprivileged_content_receiver.recv().unwrap(); opts::set_options(unprivileged_content.opts()); - PREFS.extend(unprivileged_content.prefs()); + prefs::pref_map() + .set_all(unprivileged_content.prefs()) + .expect("Failed to set preferences"); set_logger(unprivileged_content.script_to_constellation_chan().clone()); // Enter the sandbox if necessary. diff --git a/components/style/global_style_data.rs b/components/style/global_style_data.rs index 7928db13d81c..8f852dccb459 100644 --- a/components/style/global_style_data.rs +++ b/components/style/global_style_data.rs @@ -11,6 +11,7 @@ use crate::parallel::STYLE_THREAD_STACK_SIZE_KB; use crate::shared_lock::SharedRwLock; use crate::thread_state; use rayon; +use servo_config::pref; use std::env; /// Global style data @@ -69,8 +70,7 @@ lazy_static! { // We always set this pref on startup, before layout or script // have had a chance of accessing (and thus creating) the // thread-pool. - use servo_config::prefs::PREFS; - PREFS.get("layout.threads").as_u64().unwrap() as usize + pref!(layout.threads) as usize } #[cfg(feature = "gecko")] _ => { diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 0153d6fe336e..7e538c4a662e 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -33,7 +33,7 @@ use crate::parser::ParserContext; use crate::properties::longhands::system_font::SystemFont; use crate::selector_parser::PseudoElement; use selectors::parser::SelectorParseErrorKind; -#[cfg(feature = "servo")] use servo_config::prefs::PREFS; +#[cfg(feature = "servo")] use servo_config::prefs; use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; use crate::stylesheets::{CssRuleType, Origin, UrlExtraData}; @@ -514,7 +514,7 @@ impl NonCustomPropertyId { Some(pref) => pref, }; - PREFS.get(pref).as_boolean().unwrap_or(false) + prefs::pref_map().get(pref).as_bool().unwrap_or(false) % else: unsafe { structs::nsCSSProps_gPropertyEnabled[self.0] } % endif diff --git a/components/style/stylesheets/viewport_rule.rs b/components/style/stylesheets/viewport_rule.rs index 8325216519da..3d7b29f19bf5 100644 --- a/components/style/stylesheets/viewport_rule.rs +++ b/components/style/stylesheets/viewport_rule.rs @@ -27,6 +27,7 @@ use cssparser::CowRcStr; use cssparser::{parse_important, AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use euclid::TypedSize2D; use selectors::parser::SelectorParseErrorKind; +use servo_config::pref; use std::borrow::Cow; use std::cell::RefCell; use std::fmt::{self, Write}; @@ -38,11 +39,7 @@ use style_traits::{CssWriter, ParseError, PinchZoomFactor, StyleParseErrorKind, /// Whether parsing and processing of `@viewport` rules is enabled. #[cfg(feature = "servo")] pub fn enabled() -> bool { - use servo_config::prefs::PREFS; - PREFS - .get("layout.viewport.enabled") - .as_boolean() - .unwrap_or(false) + pref!(layout.viewport.enabled) } /// Whether parsing and processing of `@viewport` rules is enabled. diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs index 7adf57bca9fe..bf838715a4ca 100644 --- a/components/webdriver_server/lib.rs +++ b/components/webdriver_server/lib.rs @@ -29,7 +29,7 @@ use script_traits::{ConstellationMsg, LoadData, WebDriverCommandMsg}; use serde::de::{Deserialize, Deserializer, MapAccess, Visitor}; use serde::ser::{Serialize, Serializer}; use serde_json::{self, Value}; -use servo_config::prefs::{PrefValue, PREFS}; +use servo_config::{prefs, prefs::PrefValue}; use servo_url::ServoUrl; use std::borrow::ToOwned; use std::collections::BTreeMap; @@ -218,9 +218,10 @@ impl Serialize for WebDriverPrefValue { S: Serializer, { match self.0 { - PrefValue::Boolean(b) => serializer.serialize_bool(b), - PrefValue::String(ref s) => serializer.serialize_str(&s), - PrefValue::Number(f) => serializer.serialize_f64(f), + PrefValue::Bool(b) => serializer.serialize_bool(b), + PrefValue::Str(ref s) => serializer.serialize_str(&s), + PrefValue::Float(f) => serializer.serialize_f64(f), + PrefValue::Int(i) => serializer.serialize_i64(i), PrefValue::Missing => serializer.serialize_unit(), } } @@ -244,35 +245,35 @@ impl<'de> Deserialize<'de> for WebDriverPrefValue { where E: ::serde::de::Error, { - Ok(WebDriverPrefValue(PrefValue::Number(value))) + Ok(WebDriverPrefValue(PrefValue::Float(value))) } fn visit_i64(self, value: i64) -> Result where E: ::serde::de::Error, { - Ok(WebDriverPrefValue(PrefValue::Number(value as f64))) + Ok(WebDriverPrefValue(PrefValue::Int(value))) } fn visit_u64(self, value: u64) -> Result where E: ::serde::de::Error, { - Ok(WebDriverPrefValue(PrefValue::Number(value as f64))) + Ok(WebDriverPrefValue(PrefValue::Int(value as i64))) } fn visit_str(self, value: &str) -> Result where E: ::serde::de::Error, { - Ok(WebDriverPrefValue(PrefValue::String(value.to_owned()))) + Ok(WebDriverPrefValue(PrefValue::Str(String::from(value)))) } fn visit_bool(self, value: bool) -> Result where E: ::serde::de::Error, { - Ok(WebDriverPrefValue(PrefValue::Boolean(value))) + Ok(WebDriverPrefValue(PrefValue::Bool(value))) } } @@ -1146,7 +1147,12 @@ impl Handler { let prefs = parameters .prefs .iter() - .map(|item| (item.clone(), serde_json::to_value(PREFS.get(item)).unwrap())) + .map(|item| { + ( + item.clone(), + serde_json::to_value(prefs::pref_map().get(item)).unwrap(), + ) + }) .collect::>(); Ok(WebDriverResponse::Generic(ValueResponse( @@ -1159,7 +1165,9 @@ impl Handler { parameters: &SetPrefsParameters, ) -> WebDriverResult { for &(ref key, ref value) in parameters.prefs.iter() { - PREFS.set(key, value.0.clone()); + prefs::pref_map() + .set(key, value.0.clone()) + .expect("Failed to set preference"); } Ok(WebDriverResponse::Void) } @@ -1169,7 +1177,7 @@ impl Handler { parameters: &GetPrefsParameters, ) -> WebDriverResult { let prefs = if parameters.prefs.len() == 0 { - PREFS.reset_all(); + prefs::pref_map().reset_all(); BTreeMap::new() } else { parameters @@ -1178,7 +1186,10 @@ impl Handler { .map(|item| { ( item.clone(), - serde_json::to_value(PREFS.reset(item)).unwrap(), + serde_json::to_value( + prefs::pref_map().reset(item).unwrap_or(PrefValue::Missing), + ) + .unwrap(), ) }) .collect::>() diff --git a/components/webvr/webvr_thread.rs b/components/webvr/webvr_thread.rs index 59593c927d35..e7803509c82d 100644 --- a/components/webvr/webvr_thread.rs +++ b/components/webvr/webvr_thread.rs @@ -11,7 +11,7 @@ use ipc_channel::ipc::{IpcReceiver, IpcSender}; use msg::constellation_msg::PipelineId; use rust_webvr::VRServiceManager; use script_traits::ConstellationMsg; -use servo_config::prefs::PREFS; +use servo_config::pref; use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::{thread, time}; @@ -308,10 +308,7 @@ impl WebVRThread { let (sender, receiver) = ipc::channel().unwrap(); // Defines the polling interval time in ms for VR Events such as VRDisplay connected, disconnected, etc. - let polling_interval: u64 = PREFS - .get("dom.webvr.event_polling_interval") - .as_u64() - .unwrap_or(500); + let polling_interval = pref!(dom.webvr.event_polling_interval) as u64; thread::Builder::new() .name("WebVRPollEvents".into()) diff --git a/docs/COMMAND_LINE_ARGS.md b/docs/COMMAND_LINE_ARGS.md index 628013e51f20..a13d541a2188 100644 --- a/docs/COMMAND_LINE_ARGS.md +++ b/docs/COMMAND_LINE_ARGS.md @@ -13,9 +13,9 @@ Only arguments that need more explanation will be documented here. ## Enable Experimental Features Use `--pref` to enable experimental features like experimental DOM API, JavaScript API and CSS properties. -e.g. To enable `flex` and `flex-direction` css properties: +e.g. To enable Web VR and Bluetooth features: ``` -./mach run -d -- --pref layout.flex.enabled --pref layout.flex-direction.enabled ... +./mach run -d -- --pref dom.webvr.enabled --pref dom.bluetooth.enabled ... ``` You can find all the available preferences at [resources/prefs.json](https://dxr.mozilla.org/servo/source/resources/prefs.json). diff --git a/ports/libsimpleservo/api/src/lib.rs b/ports/libsimpleservo/api/src/lib.rs index 8622f832af7f..99d2866b9ad6 100644 --- a/ports/libsimpleservo/api/src/lib.rs +++ b/ports/libsimpleservo/api/src/lib.rs @@ -19,7 +19,7 @@ use servo::keyboard_types::{Key, KeyState, KeyboardEvent}; use servo::msg::constellation_msg::TraversalDirection; use servo::script_traits::{TouchEventType, TouchId}; use servo::servo_config::opts; -use servo::servo_config::prefs::{PrefValue, PREFS}; +use servo::servo_config::{pref, set_pref}; use servo::servo_url::ServoUrl; use servo::webvr::{VRExternalShmemPtr, VRMainThreadHeartbeat, VRServiceManager}; use servo::{self, gl, webrender_api, BrowserId, Servo}; @@ -125,17 +125,16 @@ pub fn init( // opts::from_cmdline_args expects the first argument to be the binary name. args.insert(0, "servo".to_string()); - let pref = PrefValue::Boolean(init_opts.enable_subpixel_text_antialiasing); - PREFS.set("gfx.subpixel-text-antialiasing.enabled", pref); + set_pref!( + gfx.subpixel_text_antialiasing.enabled, + init_opts.enable_subpixel_text_antialiasing + ); opts::from_cmdline_args(&args); } let embedder_url = init_opts.url.as_ref().and_then(|s| ServoUrl::parse(s).ok()); let cmdline_url = opts::get().url.clone(); - let pref_url = PREFS - .get("shell.homepage") - .as_string() - .and_then(|s| ServoUrl::parse(s).ok()); + let pref_url = ServoUrl::parse(&pref!(shell.homepage)).ok(); let blank_url = ServoUrl::parse("about:blank").ok(); let url = embedder_url diff --git a/ports/servo/browser.rs b/ports/servo/browser.rs index 784173054017..804445369b46 100644 --- a/ports/servo/browser.rs +++ b/ports/servo/browser.rs @@ -13,7 +13,7 @@ use servo::msg::constellation_msg::TraversalDirection; use servo::net_traits::pub_domains::is_reg_domain; use servo::script_traits::TouchEventType; use servo::servo_config::opts; -use servo::servo_config::prefs::PREFS; +use servo::servo_config::pref; use servo::servo_url::ServoUrl; use servo::webrender_api::ScrollLocation; use std::mem; @@ -461,12 +461,7 @@ fn sanitize_url(request: &str) -> Option { } }) .or_else(|| { - PREFS - .get("shell.searchpage") - .as_string() - .and_then(|s: &str| { - let url = s.replace("%s", request); - ServoUrl::parse(&url).ok() - }) + let url = pref!(shell.searchpage).replace("%s", request); + ServoUrl::parse(&url).ok() }) } diff --git a/ports/servo/glutin_app/window.rs b/ports/servo/glutin_app/window.rs index ab6d35e2f5a5..bd56a5f0fae5 100644 --- a/ports/servo/glutin_app/window.rs +++ b/ports/servo/glutin_app/window.rs @@ -13,8 +13,7 @@ use servo::compositing::windowing::{AnimationState, MouseWindowEvent, WindowEven use servo::compositing::windowing::{EmbedderCoordinates, WindowMethods}; use servo::embedder_traits::{Cursor, EventLoopWaker}; use servo::script_traits::TouchEventType; -use servo::servo_config::opts; -use servo::servo_config::prefs::PREFS; +use servo::servo_config::{opts, pref}; use servo::servo_geometry::DeviceIndependentPixel; use servo::style_traits::DevicePixel; use servo::webrender_api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, ScrollLocation}; @@ -792,7 +791,7 @@ impl WindowMethods for Window { services: &mut VRServiceManager, heartbeats: &mut Vec> ) { - if PREFS.get("dom.webvr.test").as_boolean().unwrap_or(false) { + if pref!(dom.webvr.test) { warn!("Creating test VR display"); // TODO: support dom.webvr.test in headless environments if let WindowKind::Window(_, ref events_loop) = self.kind { diff --git a/ports/servo/non_android_main.rs b/ports/servo/non_android_main.rs index 7dd8a5008f0c..767d27a646c9 100644 --- a/ports/servo/non_android_main.rs +++ b/ports/servo/non_android_main.rs @@ -18,7 +18,7 @@ use servo::{Servo, BrowserId}; use servo::compositing::windowing::WindowEvent; use servo::config::opts::{self, ArgumentParsingResult, parse_url_or_filename}; use servo::config::servo_version; -use servo::servo_config::prefs::PREFS; +use servo::servo_config::pref; use servo::servo_url::ServoUrl; use std::env; use std::panic; @@ -126,14 +126,14 @@ pub fn main() { let mut browser = browser::Browser::new(window.clone()); - // If the url is not provided, we fallback to the homepage in PREFS, + // If the url is not provided, we fallback to the homepage in prefs, // or a blank page in case the homepage is not set either. let cwd = env::current_dir().unwrap(); let cmdline_url = opts::get().url.clone(); - let pref_url = PREFS - .get("shell.homepage") - .as_string() - .and_then(|str| parse_url_or_filename(&cwd, str).ok()); + let pref_url = { + let homepage_url = pref!(shell.homepage); + parse_url_or_filename(&cwd, &homepage_url).ok() + }; let blank_url = ServoUrl::parse("about:blank").ok(); let target_url = cmdline_url.or(pref_url).or(blank_url).unwrap(); diff --git a/resources/prefs.json b/resources/prefs.json index 7adcf75c0ea2..85ae67919ad7 100644 --- a/resources/prefs.json +++ b/resources/prefs.json @@ -4,23 +4,32 @@ "dom.canvas-text.enabled": true, "dom.compositionevent.enabled": false, "dom.customelements.enabled": true, + "dom.document.dblclick_dist": 1, + "dom.document.dblclick_timeout": 300, "dom.forcetouch.enabled": false, + "dom.fullscreen.test": false, "dom.gamepad.enabled": false, - "dom.microdata.testing.enabled": true, + "dom.microdata.enabled": false, + "dom.microdata.testing.enabled": false, "dom.mouseevent.which.enabled": false, "dom.mutation_observer.enabled": true, "dom.offscreen_canvas.enabled": false, "dom.permissions.enabled": false, "dom.permissions.testing.allowed_in_nonsecure_contexts": false, + "dom.serviceworker.enabled": false, "dom.serviceworker.timeout_seconds": 60, "dom.servoparser.async_html_tokenizer.enabled": false, + "dom.svg.enabled": false, "dom.testable_crash.enabled": false, "dom.testbinding.enabled": false, + "dom.testing.htmlinputelement.select_files.enabled": false, "dom.webgl.dom_to_texture.enabled": false, "dom.webgl2.enabled": false, "dom.webrtc.enabled": false, "dom.webvr.enabled": false, "dom.webvr.event_polling_interval": 500, + "dom.webvr.test": false, + "dom.worklet.timeout_ms": 10, "gfx.subpixel-text-antialiasing.enabled": true, "js.asmjs.enabled": true, "js.asyncstack.enabled": false, @@ -31,6 +40,8 @@ "js.ion.enabled": true, "js.ion.offthread_compilation.enabled": true, "js.ion.unsafe_eager_compilation.enabled": false, + "js.mem.gc.allocation_threshold_avoid_interrupt_factor": 1, + "js.mem.gc.allocation_threshold_factor": 1, "js.mem.gc.allocation_threshold_mb": 30, "js.mem.gc.compacting.enabled": true, "js.mem.gc.decommit_threshold_mb": 32, @@ -47,25 +58,30 @@ "js.mem.gc.incremental.slice_ms": 10, "js.mem.gc.low_frequency_heap_growth": 150, "js.mem.gc.per_compartment.enabled": true, + "js.mem.gc.per_zone.enabled": false, "js.mem.gc.zeal.frequency": 100, "js.mem.gc.zeal.level": 0, "js.mem.high_water_mark": 128, "js.mem.max": -1, - "js.native_regexp.enabled": true, + "js.native_regex.enabled": true, + "js.offthread_compilation.enabled": true, "js.parallel_parsing.enabled": true, "js.shared_memory.enabled": true, "js.strict.debug.enabled": false, "js.strict.enabled": false, "js.throw_on_asmjs_validation_failure.enabled": false, "js.throw_on_debuggee_would_run.enabled": false, + "js.timers.minimum_duration": 1000, "js.wasm.baseline.enabled": true, "js.wasm.enabled": true, "js.wasm.ion.enabled": true, "js.werror.enabled": false, "layout.animations.test.enabled": false, "layout.columns.enabled": false, + "layout.threads": 3, "layout.viewport.enabled": false, "layout.writing-mode.enabled": false, + "media.testing.enabled": false, "network.http-cache.disabled": false, "network.mime.sniff": false, "session-history.max-length": 20, diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs index 68bbff46f9fc..fe8a7b842697 100644 --- a/tests/unit/style/stylesheets.rs +++ b/tests/unit/style/stylesheets.rs @@ -9,7 +9,7 @@ use selectors::attr::*; use selectors::parser::*; use servo_arc::Arc; use servo_atoms::Atom; -use servo_config::prefs::{PrefValue, PREFS}; +use servo_config::set_pref; use servo_url::ServoUrl; use std::borrow::ToOwned; use std::cell::RefCell; @@ -339,7 +339,7 @@ impl ParseErrorReporter for TestingErrorReporter { #[test] fn test_report_error_stylesheet() { - PREFS.set("layout.viewport.enabled", PrefValue::Boolean(true)); + set_pref!(layout.viewport.enabled, true); let css = r" div { background-color: red; diff --git a/tests/unit/style/viewport.rs b/tests/unit/style/viewport.rs index b4043038b836..23e6c05dc257 100644 --- a/tests/unit/style/viewport.rs +++ b/tests/unit/style/viewport.rs @@ -6,7 +6,7 @@ use cssparser::{Parser, ParserInput}; use euclid::TypedScale; use euclid::TypedSize2D; use servo_arc::Arc; -use servo_config::prefs::{PrefValue, PREFS}; +use servo_config::set_pref; use servo_url::ServoUrl; use style::context::QuirksMode; use style::media_queries::{Device, MediaList, MediaType}; @@ -45,7 +45,7 @@ fn test_viewport_rule(css: &str, device: &Device, callback: F) where F: Fn(&Vec, &str), { - PREFS.set("layout.viewport.enabled", PrefValue::Boolean(true)); + set_pref!(layout.viewport.enabled, true); let stylesheet = stylesheet!(css, Author); let mut rule_count = 0; stylesheet.effective_viewport_rules(&device, &stylesheet.shared_lock.read(), |rule| { @@ -445,7 +445,7 @@ fn cascading_within_viewport_rule() { #[test] fn multiple_stylesheets_cascading() { - PREFS.set("layout.viewport.enabled", PrefValue::Boolean(true)); + set_pref!(layout.viewport.enabled, true); let device = Device::new( MediaType::screen(), TypedSize2D::new(800., 600.), diff --git a/tests/wpt/metadata/css/css-flexbox/__dir__.ini b/tests/wpt/metadata/css/css-flexbox/__dir__.ini index da05958fc274..383f96c4de97 100644 --- a/tests/wpt/metadata/css/css-flexbox/__dir__.ini +++ b/tests/wpt/metadata/css/css-flexbox/__dir__.ini @@ -1,11 +1 @@ -prefs: ["layout.flex.enabled:true", - "layout.flex-flow.enabled:true", - "layout.flex-direction.enabled:true", - "layout.flex-wrap.enabled:true", - "layout.flex-grow.enabled:true", - "layout.flex-shrink.enabled:true", - "layout.justify-content.enabled:true", - "layout.align-items.enabled:true", - "layout.align-self.enabled:true", - "layout.align-content.enabled:true", - "layout.columns.enabled:true"] +prefs: ["layout.columns.enabled:true"] diff --git a/tests/wpt/mozilla/meta/css/__dir__.ini b/tests/wpt/mozilla/meta/css/__dir__.ini deleted file mode 100644 index dfd23294ccca..000000000000 --- a/tests/wpt/mozilla/meta/css/__dir__.ini +++ /dev/null @@ -1,2 +0,0 @@ -prefs: ["layout.flex.enabled:true", - "layout.flex-direction.enabled:true"]