Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pref api #26793

Merged
merged 3 commits into from Jun 10, 2020
Merged

Pref api #26793

Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -5,7 +5,6 @@
//! Configuration options for a single run of the servo application. Created
//! from command line arguments.

use crate::prefs::{self, PrefValue};
use euclid::Size2D;
use getopts::{Matches, Options};
use servo_geometry::DeviceIndependentPixel;
@@ -640,18 +639,6 @@ pub fn from_cmdline_args(mut opts: Options, args: &[String]) -> ArgumentParsingR
"Run as a content process and connect to the given pipe",
"servo-ipc-channel.abcdefg",
);
opts.optmulti(
"",
"pref",
"A preference to set to enable",
"dom.bluetooth.enabled",
);
opts.optmulti(
"",
"pref",
"A preference to set to enable",
"dom.webgpu.enabled",
);

This comment has been minimized.

Copy link
@paulrouget

paulrouget Jun 5, 2020

Author Contributor

This is moved under the winit port. The embedder is in charge of parsing the command line and feeding the pref to servo.

opts.optflag("b", "no-native-titlebar", "Do not use native titlebar");
opts.optflag("w", "webrender", "Use webrender backend");
opts.optopt("G", "graphics", "Select graphics backend (gl or es2)", "gl");
@@ -926,16 +913,6 @@ pub fn from_cmdline_args(mut opts: Options, args: &[String]) -> ArgumentParsingR

set_options(opts);

// These must happen after setting the default options, since the prefs rely on
// on the resource path.
// Note that command line preferences have the highest precedence

prefs::add_user_prefs();

for pref in opt_match.opt_strs("pref").iter() {
parse_pref_from_command_line(pref);
}

if let Some(layout_threads) = layout_threads {
set_pref!(layout.threads, layout_threads as i64);
}
@@ -965,31 +942,6 @@ pub fn get() -> RwLockReadGuard<'static, Opts> {
OPTIONS.read().unwrap()
}

pub fn parse_pref_from_command_line(pref: &str) {
let split: Vec<&str> = pref.splitn(2, '=').collect();
let pref_name = split[0];
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::<i64>().ok() {
PrefValue::Int(int)
} else if let Some(float) = string.parse::<f64>().ok() {
PrefValue::Float(float)
} else {
PrefValue::from(string)
}
},
}
}

pub fn parse_url_or_filename(cwd: &Path, input: &str) -> Result<ServoUrl, ()> {
match ServoUrl::parse(input) {
Ok(url) => Ok(url),
@@ -220,6 +220,17 @@ impl<'m, P: Clone> Preferences<'m, P> {
}
}

/// Has the preference been modified from its original value?
pub fn is_default(&self, key: &str) -> Result<bool, PrefError> {
if let Some(accessor) = self.accessors.get(key) {
let user = (accessor.getter)(&self.default_prefs);
let default = (accessor.getter)(&self.user_prefs.read().unwrap());
Ok(default == user)
} else {
Err(PrefError::NoSuchPref(String::from(key)))
}
}

/// Creates an iterator over all keys and values
pub fn iter<'a>(&'a self) -> impl Iterator<Item = (String, PrefValue)> + 'a {
let prefs = self.user_prefs.read().unwrap();
@@ -2,15 +2,10 @@
* 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 crate::basedir::default_config_dir;
use crate::opts;
use embedder_traits::resources::{self, Resource};
use serde_json::{self, Value};
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::fs::File;
use std::io::{stderr, Read, Write};
use std::path::PathBuf;

use crate::pref_util::Preferences;
pub use crate::pref_util::{PrefError, PrefValue};
@@ -58,42 +53,12 @@ pub fn pref_map() -> &'static Preferences<'static, Prefs> {
&PREFS
}

pub(crate) fn add_user_prefs() {
if let Some(path) = user_prefs_path() {
init_user_prefs(path);
pub fn add_user_prefs(prefs: HashMap<String, PrefValue>) {
if let Err(error) = PREFS.set_all(prefs.into_iter()) {
panic!("Error setting preference: {:?}", error);
}
}

fn user_prefs_path() -> Option<PathBuf> {
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: PathBuf) {
if let Ok(mut file) = File::open(&path) {
let mut txt = String::new();
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 user prefs from {:?}", path)
}
.expect("failed printing to stderr");
}

pub fn read_prefs_map(txt: &str) -> Result<HashMap<String, PrefValue>, PrefError> {
let prefs: HashMap<String, Value> =
serde_json::from_str(txt).map_err(|e| PrefError::JsonParseErr(e))?;
@@ -2,11 +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/. */

#[macro_use]
extern crate servo_config;

use servo_config::opts::{parse_pref_from_command_line, parse_url_or_filename};
use servo_config::{prefs, prefs::PrefValue};
use servo_config::opts::parse_url_or_filename;
use std::path::Path;

#[cfg(not(target_os = "windows"))]
@@ -88,54 +84,3 @@ fn test_argument_parsing_special() {
assert_eq!(url.query(), None);
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::<String>().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("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);
}
@@ -7,6 +7,7 @@ extern crate log;

pub mod gl_glue;

pub use servo::config::prefs::{add_user_prefs, PrefValue};
pub use servo::embedder_traits::{
ContextMenuResult, MediaSessionPlaybackState, PermissionPrompt, PermissionRequest, PromptResult,
};
@@ -19,6 +20,7 @@ use servo::compositing::windowing::{
AnimationState, EmbedderCoordinates, EmbedderMethods, MouseWindowEvent, WindowEvent,
WindowMethods,
};
use servo::config::prefs::pref_map;
use servo::embedder_traits::resources::{self, Resource, ResourceReaderMethods};
use servo::embedder_traits::{
EmbedderMsg, EmbedderProxy, MediaSessionEvent, PromptDefinition, PromptOrigin,
@@ -36,6 +38,7 @@ use servo::webrender_surfman::WebrenderSurfman;
use servo::{self, gl, BrowserId, Servo};
use servo_media::player::context as MediaPlayerContext;
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem;
use std::os::raw::c_void;
use std::path::PathBuf;
@@ -55,14 +58,14 @@ pub use servo::embedder_traits::EventLoopWaker;

pub struct InitOptions {
pub args: Vec<String>,
pub url: Option<String>,
pub coordinates: Coordinates,
pub density: f32,
pub xr_discovery: Option<webxr::Discovery>,
pub enable_subpixel_text_antialiasing: bool,
pub gl_context_pointer: Option<*const c_void>,
pub native_display_pointer: Option<*const c_void>,
pub native_widget: *mut c_void,
pub prefs: Option<HashMap<String, PrefValue>>,
}

This comment has been minimized.

Copy link
@paulrouget

paulrouget Jun 5, 2020

Author Contributor

Now that we can pass preferences to the embedder, there's not need to also pass the URL, and the embedder can just set homepage pref.


#[derive(Clone, Debug)]
@@ -173,6 +176,44 @@ pub fn is_uri_valid(url: &str) -> bool {
ServoUrl::parse(url).is_ok()
}

/// Retrieve a snapshot of the current preferences
pub fn get_prefs() -> HashMap<String, (PrefValue, bool)> {
pref_map()
.iter()
.map(|(key, value)| {
let is_default = pref_map().is_default(&key).unwrap();
(key, (value, is_default))
})
.collect()
}

/// Retrieve a preference.
pub fn get_pref(key: &str) -> (PrefValue, bool) {
if let Ok(is_default) = pref_map().is_default(&key) {
(pref_map().get(key), is_default)
} else {
(PrefValue::Missing, false)
}
}

/// Restore a preference to its default value.
pub fn reset_pref(key: &str) -> bool {
pref_map().reset(key).is_ok()
}

/// Restore all the preferences to their default values.
pub fn reset_all_prefs() {
pref_map().reset_all();
}

/// Change the value of a preference.
pub fn set_pref(key: &str, val: PrefValue) -> Result<(), &'static str> {
pref_map()
.set(key, val)
.map(|_| ())
.map_err(|_| "Pref set failed")
}

/// Initialize Servo. At that point, we need a valid GL context.
/// In the future, this will be done in multiple steps.
pub fn init(
@@ -195,16 +236,14 @@ pub fn init(
opts::from_cmdline_args(Options::new(), &args);
}

let embedder_url = init_opts.url.as_ref().and_then(|s| ServoUrl::parse(s).ok());
let cmdline_url = opts::get().url.clone();
if let Some(prefs) = init_opts.prefs {
add_user_prefs(prefs);
}

let pref_url = ServoUrl::parse(&pref!(shell.homepage)).ok();
let blank_url = ServoUrl::parse("about:blank").ok();

let url = embedder_url
.or(cmdline_url)
.or(pref_url)
.or(blank_url)
.unwrap();
let url = pref_url.or(blank_url).unwrap();

gl.clear_color(1.0, 1.0, 1.0, 1.0);
gl.clear(gl::COLOR_BUFFER_BIT);
@@ -8,6 +8,8 @@ extern crate lazy_static;
#[macro_use]
extern crate log;

mod prefs;

#[cfg(target_os = "windows")]
mod vslogger;

@@ -187,7 +189,7 @@ fn do_redirect_stdout_stderr(handler: LogHandlerFn) -> Result<(), ()> {

fn call<T, F>(f: F) -> T
where
F: Fn(&mut ServoGlue) -> Result<T, &'static str>,
F: FnOnce(&mut ServoGlue) -> Result<T, &'static str>,
{
match SERVO.with(|s| match s.borrow_mut().as_mut() {
Some(ref mut s) => (f)(s),
@@ -235,14 +237,14 @@ pub struct CHostCallbacks {
#[repr(C)]
pub struct CInitOptions {
pub args: *const c_char,
pub url: *const c_char,
pub width: i32,
pub height: i32,
pub density: f32,
pub enable_subpixel_text_antialiasing: bool,
pub vslogger_mod_list: *const *const c_char,
pub vslogger_mod_size: u32,
pub native_widget: *mut c_void,
pub prefs: *const prefs::CPrefList,
}

#[repr(C)]
@@ -427,15 +429,18 @@ unsafe fn init(
warn!("Error redirecting stdout/stderr: {}", reason);
}

let url = CStr::from_ptr(opts.url);
let url = url.to_str().map(|s| s.to_string()).ok();

let coordinates = Coordinates::new(0, 0, opts.width, opts.height, opts.width, opts.height);

let prefs = if opts.prefs.is_null() {
None
} else {
Some((*opts.prefs).convert())
};

let opts = InitOptions {
args,
url,
coordinates,
prefs,
density: opts.density,
xr_discovery: None,
enable_subpixel_text_antialiasing: opts.enable_subpixel_text_antialiasing,
@@ -532,6 +537,9 @@ pub extern "C" fn resize(width: i32, height: i32) {
pub extern "C" fn perform_updates() {
catch_any_panic(|| {
debug!("perform_updates");
// We might have allocated some memory to respond to a potential
// request, from the embedder, for a copy of Servo's preferences.
prefs::free_prefs();

This comment has been minimized.

Copy link
@paulrouget

paulrouget Jun 5, 2020

Author Contributor

Free the C-compatible structures we allocated to build the preferences list.

This comment has been minimized.

Copy link
@Manishearth

Manishearth Jun 5, 2020

Member

Add a comment?

call(|s| s.perform_updates());
});
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.