1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* 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 servo::embedder_traits;
use getopts::{Matches, Options};
use servo::config::opts::{self, ArgumentParsingResult};
use servo::config::prefs::{self, PrefValue};
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use servo::servo_config::basedir;

pub fn register_user_prefs(opts_matches: &Matches) {
    // Read user's prefs.json and then parse --pref command line args.

    let user_prefs_path = opts::get()
        .config_dir
        .clone()
        .or_else(|| basedir::default_config_dir())
        .map(|path| path.join("prefs.json"))
        .filter(|path| path.exists());

    let mut userprefs = if let Some(path) = user_prefs_path {
        let mut file = File::open(&path).expect("Error opening user prefs");
        let mut txt = String::new();
        file.read_to_string(&mut txt).expect("Can't read user prefs file");
        prefs::read_prefs_map(&txt).expect("Can't parse user prefs file")
    } else {
        HashMap::new()
    };

    let argprefs: HashMap<String, PrefValue> = opts_matches.opt_strs("pref").iter().map(|pref| {
        let split: Vec<&str> = pref.splitn(2, '=').collect();
        let pref_name = split[0];
        let pref_value = match split.get(1).cloned() {
            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)
                }
            },
        };
        (pref_name.to_string(), pref_value)
    }).collect();

    // --pref overrides user prefs.json
    userprefs.extend(argprefs);

    prefs::add_user_prefs(userprefs);
}

// Use for test
#[allow(dead_code)]
fn test_parse_pref(arg: &str) {
    embedder_traits::resources::set_for_tests();
    let mut opts = Options::new();
    opts.optmulti("", "pref", "", "");
    let args = vec![
        "servo".to_string(),
        "--pref".to_string(),
        arg.to_string()
    ];
    let matches = match opts::from_cmdline_args(opts, &args) {
        ArgumentParsingResult::ContentProcess(m, _) => m,
        ArgumentParsingResult::ChromeProcess(m) => m,
    };
    register_user_prefs(&matches);
}

#[test]
fn test_parse_pref_from_command_line() {
    use servo::servo_config::pref;
    // Test with boolean values.
    test_parse_pref("dom.bluetooth.enabled=true");
    assert_eq!(
        prefs::pref_map().get("dom.bluetooth.enabled"),
        PrefValue::Bool(true)
    );
    assert_eq!(pref!(dom.bluetooth.enabled), true);

    test_parse_pref("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
    test_parse_pref("layout.threads=42");
    assert_eq!(pref!(layout.threads), 42);

    // Test string.
    test_parse_pref("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();
    test_parse_pref("dom.bluetooth.enabled");
    assert_eq!(pref!(dom.bluetooth.enabled), true);
}


#[test]
fn test_invalid_prefs_from_command_line_panics() {
    let err_msg = std::panic::catch_unwind(|| {
        test_parse_pref("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"
    );
}