Enable resetable and String prefs.

This allows both boolean and string-type preferences. It
also implements a system where prefs that are read from a
configuration file can be reset back to their initial value,
which is useful in a number of cases e.g. when running tests
to ensure that each test starts with the same values for
the prefs.
This commit is contained in:
James Graham 2015-09-17 00:49:05 +01:00
parent d811f2d1ba
commit 79e548905e
10 changed files with 174 additions and 33 deletions

View file

@ -905,7 +905,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
containing_pipeline_id: PipelineId, containing_pipeline_id: PipelineId,
subpage_id: SubpageId, subpage_id: SubpageId,
event: MozBrowserEvent) { event: MozBrowserEvent) {
assert!(prefs::get_pref("dom.mozbrowser.enabled").unwrap_or(false)); assert!(prefs::get_pref("dom.mozbrowser.enabled").as_boolean().unwrap_or(false));
// Find the script channel for the given parent pipeline, // Find the script channel for the given parent pipeline,
// and pass the event to that script task. // and pass the event to that script task.
@ -1387,7 +1387,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserlocationchange // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserlocationchange
fn trigger_mozbrowserlocationchange(&self, pipeline_id: PipelineId) { fn trigger_mozbrowserlocationchange(&self, pipeline_id: PipelineId) {
if prefs::get_pref("dom.mozbrowser.enabled").unwrap_or(false) { if prefs::get_pref("dom.mozbrowser.enabled").as_boolean().unwrap_or(false) {
// Work around borrow checker // Work around borrow checker
let event_info = { let event_info = {
let pipeline = self.pipeline(pipeline_id); let pipeline = self.pipeline(pipeline_id);

View file

@ -289,7 +289,7 @@ impl Pipeline {
pub fn trigger_mozbrowser_event(&self, pub fn trigger_mozbrowser_event(&self,
subpage_id: SubpageId, subpage_id: SubpageId,
event: MozBrowserEvent) { event: MozBrowserEvent) {
assert!(prefs::get_pref("dom.mozbrowser.enabled").unwrap_or(false)); assert!(prefs::get_pref("dom.mozbrowser.enabled").as_boolean().unwrap_or(false));
let event = ConstellationControlMsg::MozBrowserEvent(self.id, let event = ConstellationControlMsg::MozBrowserEvent(self.id,
subpage_id, subpage_id,

View file

@ -40,7 +40,7 @@ use util::str::DOMString;
use util::str::{self, LengthOrPercentageOrAuto}; use util::str::{self, LengthOrPercentageOrAuto};
pub fn mozbrowser_enabled() -> bool { pub fn mozbrowser_enabled() -> bool {
prefs::get_pref("dom.mozbrowser.enabled").unwrap_or(false) prefs::get_pref("dom.mozbrowser.enabled").as_boolean().unwrap_or(false)
} }
#[derive(HeapSizeOf)] #[derive(HeapSizeOf)]

View file

@ -171,7 +171,7 @@ impl MouseEventMethods for MouseEvent {
// This returns the same result as current gecko. // This returns the same result as current gecko.
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which
fn Which(&self) -> i32 { fn Which(&self) -> i32 {
if prefs::get_pref("dom.mouseevent.which.enabled").unwrap_or(false) { if prefs::get_pref("dom.mouseevent.which.enabled").as_boolean().unwrap_or(false) {
(self.button.get() + 1) as i32 (self.button.get() + 1) as i32
} else { } else {
0 0

View file

@ -477,7 +477,8 @@ pub mod longhands {
% for value in values[:-1]: % for value in values[:-1]:
"${value}" => { "${value}" => {
% if value in experimental_values: % if value in experimental_values:
if !::util::prefs::get_pref("layout.${value}.enabled").unwrap_or(false) { if !::util::prefs::get_pref("layout.${value}.enabled")
.as_boolean().unwrap_or(false) {
return Err(()) return Err(())
} }
% endif % endif
@ -487,7 +488,8 @@ pub mod longhands {
% for value in values[-1:]: % for value in values[-1:]:
"${value}" => { "${value}" => {
% if value in experimental_values: % if value in experimental_values:
if !::util::prefs::get_pref("layout.${value}.enabled".unwrap_or(false) { if !::util::prefs::get_pref("layout.${value}.enabled")
.as_boolean().unwrap_or(false) {
return Err(()) return Err(())
} }
% endif % endif
@ -5945,7 +5947,8 @@ impl PropertyDeclaration {
% if property.derived_from is None: % if property.derived_from is None:
"${property.name}" => { "${property.name}" => {
% if property.experimental: % if property.experimental:
if !::util::prefs::get_pref("${property.experimental}").unwrap_or(false) { if !::util::prefs::get_pref("${property.experimental}")
.as_boolean().unwrap_or(false) {
return PropertyDeclarationParseResult::ExperimentalProperty return PropertyDeclarationParseResult::ExperimentalProperty
} }
% endif % endif
@ -5964,7 +5967,8 @@ impl PropertyDeclaration {
% for shorthand in SHORTHANDS: % for shorthand in SHORTHANDS:
"${shorthand.name}" => { "${shorthand.name}" => {
% if shorthand.experimental: % if shorthand.experimental:
if !::util::prefs::get_pref("${shorthand.experimental}").unwrap_or(false) { if !::util::prefs::get_pref("${shorthand.experimental}")
.as_boolean().unwrap_or(false) {
return PropertyDeclarationParseResult::ExperimentalProperty return PropertyDeclarationParseResult::ExperimentalProperty
} }
% endif % endif

View file

@ -426,7 +426,7 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace)) Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace))
}, },
"viewport" => { "viewport" => {
if ::util::prefs::get_pref("layout.viewport.enabled").unwrap_or(false) { if ::util::prefs::get_pref("layout.viewport.enabled").as_boolean().unwrap_or(false) {
Ok(AtRuleType::WithBlock(AtRulePrelude::Viewport)) Ok(AtRuleType::WithBlock(AtRulePrelude::Viewport))
} else { } else {
Err(()) Err(())

View file

@ -9,7 +9,7 @@ use euclid::size::{Size2D, TypedSize2D};
use geometry::ScreenPx; use geometry::ScreenPx;
use getopts::Options; use getopts::Options;
use num_cpus; use num_cpus;
use prefs; use prefs::{self, PrefValue};
use std::cmp; use std::cmp;
use std::default::Default; use std::default::Default;
use std::env; use std::env;
@ -614,7 +614,7 @@ pub fn from_cmdline_args(args: &[String]) {
// This must happen after setting the default options, since the prefs rely on // This must happen after setting the default options, since the prefs rely on
// on the resource path. // on the resource path.
for pref in opt_match.opt_strs("pref").iter() { for pref in opt_match.opt_strs("pref").iter() {
prefs::set_pref(pref, true); prefs::set_pref(pref, PrefValue::Boolean(true));
} }
} }

View file

@ -3,19 +3,122 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use resource_files::resources_dir_path; use resource_files::resources_dir_path;
use rustc_serialize::json::Json; use rustc_serialize::json::{Json, ToJson};
use std::borrow::ToOwned;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
lazy_static! { lazy_static! {
static ref PREFS: Arc<Mutex<HashMap<String, bool>>> = { static ref PREFS: Arc<Mutex<HashMap<String, Pref>>> = {
let prefs = read_prefs().unwrap_or(HashMap::new()); let prefs = read_prefs().unwrap_or(HashMap::new());
Arc::new(Mutex::new(prefs)) Arc::new(Mutex::new(prefs))
}; };
} }
fn read_prefs() -> Result<HashMap<String, bool>, ()> { #[derive(PartialEq, Clone, Debug)]
pub enum PrefValue {
Boolean(bool),
String(String),
Missing
}
impl PrefValue {
pub fn from_json(data: Json) -> Result<PrefValue, ()> {
let value = match data {
Json::Boolean(x) => PrefValue::Boolean(x),
Json::String(x) => PrefValue::String(x),
_ => return Err(())
};
Ok(value)
}
pub fn as_boolean(&self) -> Option<bool> {
match self {
&PrefValue::Boolean(value) => {
Some(value)
},
_ => None
}
}
pub fn as_string(&self) -> Option<&str> {
match *self {
PrefValue::String(ref value) => {
Some(&value)
},
_ => None
}
}
}
impl ToJson for PrefValue {
fn to_json(&self) -> Json {
match *self {
PrefValue::Boolean(x) => {
Json::Boolean(x)
},
PrefValue::String(ref x) => {
Json::String(x.clone())
}
PrefValue::Missing => Json::Null
}
}
}
enum Pref {
NoDefault(Arc<PrefValue>),
WithDefault(Arc<PrefValue>, Option<Arc<PrefValue>>)
}
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: Json) -> Result<Pref, ()> {
let value = try!(PrefValue::from_json(data));
Ok(Pref::new_default(value))
}
pub fn value(&self) -> &Arc<PrefValue> {
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 {
&mut Pref::NoDefault(ref mut pref_value) => {
*pref_value = Arc::new(value)
},
&mut Pref::WithDefault(_, ref mut override_value) => {
*override_value = Some(Arc::new(value))
}
}
}
}
impl ToJson for Pref {
fn to_json(&self) -> Json {
self.value().to_json()
}
}
fn read_prefs() -> Result<HashMap<String, Pref>, ()> {
let mut path = resources_dir_path(); let mut path = resources_dir_path();
path.push("prefs.json"); path.push("prefs.json");
@ -29,22 +132,53 @@ fn read_prefs() -> Result<HashMap<String, bool>, ()> {
})); }));
let mut prefs = HashMap::new(); let mut prefs = HashMap::new();
if let Some(obj) = json.as_object() { if let Json::Object(obj) = json {
for (name, value) in obj.iter() { for (name, value) in obj.into_iter() {
if let Some(bool_value) = value.as_boolean() { match Pref::from_json(value) {
prefs.insert(name.clone(), bool_value); Ok(x) => {
} else { prefs.insert(name, x);
println!("Ignoring non-boolean preference value for {:?}", name); },
Err(_) => println!("Ignoring non-boolean/string preference value for {:?}", name)
} }
} }
} }
Ok(prefs) Ok(prefs)
} }
pub fn get_pref(name: &str) -> Option<bool> { pub fn get_pref(name: &str) -> Arc<PrefValue> {
PREFS.lock().unwrap().get(name).cloned() PREFS.lock().unwrap().get(name).map(|x| x.value().clone()).unwrap_or(Arc::new(PrefValue::Missing))
} }
pub fn set_pref(name: &str, value: bool) { pub fn set_pref(name: &str, value: PrefValue) {
let _ = PREFS.lock().unwrap().insert(name.to_owned(), value); let mut prefs = PREFS.lock().unwrap();
if let Some(pref) = prefs.get_mut(name) {
pref.set(value);
return;
}
prefs.insert(name.to_owned(), Pref::new(value));
}
pub fn reset_pref(name: &str) -> Arc<PrefValue> {
let mut prefs = PREFS.lock().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
}
pub fn reset_all_prefs() {
let names = {
PREFS.lock().unwrap().keys().map(|x| x.clone()).collect::<Vec<String>>()
};
for name in names.iter() {
reset_pref(name);
}
} }

View file

@ -35,7 +35,7 @@ use std::collections::BTreeMap;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::thread::{self, sleep_ms}; use std::thread::{self, sleep_ms};
use url::Url; use url::Url;
use util::prefs::{get_pref, set_pref}; use util::prefs::{get_pref, reset_all_prefs, reset_pref, set_pref, PrefValue};
use util::task::spawn_named; use util::task::spawn_named;
use uuid::Uuid; use uuid::Uuid;
use webdriver::command::{GetParameters, JavascriptCommandParameters, LocatorParameters}; use webdriver::command::{GetParameters, JavascriptCommandParameters, LocatorParameters};
@ -150,7 +150,7 @@ impl ToJson for GetPrefsParameters {
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
struct SetPrefsParameters { struct SetPrefsParameters {
prefs: Vec<(String, bool)> prefs: Vec<(String, PrefValue)>
} }
impl Parameters for SetPrefsParameters { impl Parameters for SetPrefsParameters {
@ -166,9 +166,9 @@ impl Parameters for SetPrefsParameters {
"prefs was not an array"))); "prefs was not an array")));
let mut params = Vec::with_capacity(items.len()); let mut params = Vec::with_capacity(items.len());
for (name, val) in items.iter() { for (name, val) in items.iter() {
let value = try!(val.as_boolean().ok_or( let value = try!(PrefValue::from_json(val).or(
WebDriverError::new(ErrorStatus::InvalidArgument, Err(WebDriverError::new(ErrorStatus::InvalidArgument,
"Pref is not a bool"))); "Pref is not a boolean or string"))));
let key = name.to_owned(); let key = name.to_owned();
params.push((key, value)); params.push((key, value));
} }
@ -633,13 +633,14 @@ impl Handler {
.iter() .iter()
.map(|item| (item.clone(), get_pref(item).to_json())) .map(|item| (item.clone(), get_pref(item).to_json()))
.collect::<BTreeMap<_, _>>(); .collect::<BTreeMap<_, _>>();
Ok(WebDriverResponse::Generic(ValueResponse::new(prefs.to_json()))) Ok(WebDriverResponse::Generic(ValueResponse::new(prefs.to_json())))
} }
fn handle_set_prefs(&self, fn handle_set_prefs(&self,
parameters: &SetPrefsParameters) -> WebDriverResult<WebDriverResponse> { parameters: &SetPrefsParameters) -> WebDriverResult<WebDriverResponse> {
for &(ref key, ref value) in parameters.prefs.iter() { for &(ref key, ref value) in parameters.prefs.iter() {
set_pref(key, *value); set_pref(key, value.clone());
} }
Ok(WebDriverResponse::Void) Ok(WebDriverResponse::Void)
} }

View file

@ -26,7 +26,8 @@ fn test_viewport_rule<F>(css: &str,
callback: F) callback: F)
where F: Fn(&Vec<ViewportDescriptorDeclaration>, &str) where F: Fn(&Vec<ViewportDescriptorDeclaration>, &str)
{ {
::util::prefs::set_pref("layout.viewport.enabled", true); ::util::prefs::set_pref("layout.viewport.enabled",
::util::prefs::PrefValue::Boolean(true));
let stylesheet = stylesheet!(css, Author); let stylesheet = stylesheet!(css, Author);
let mut rule_count = 0; let mut rule_count = 0;
@ -173,7 +174,8 @@ fn cascading_within_viewport_rule() {
#[test] #[test]
fn multiple_stylesheets_cascading() { fn multiple_stylesheets_cascading() {
::util::prefs::set_pref("layout.viewport.enabled", true); ::util::prefs::set_pref("layout.viewport.enabled",
::util::prefs::PrefValue::Boolean(true));
let device = Device::new(MediaType::Screen, Size2D::typed(800., 600.)); let device = Device::new(MediaType::Screen, Size2D::typed(800., 600.));
let stylesheets = vec![ let stylesheets = vec![