diff options
author | Alan Jeffrey <ajeffrey@mozilla.com> | 2016-12-14 10:37:58 -0600 |
---|---|---|
committer | Alan Jeffrey <ajeffrey@mozilla.com> | 2016-12-14 18:04:37 -0600 |
commit | 9be4fd56ce5467ce6d0e48c6efda9eb26f40eb7a (patch) | |
tree | 93d46ce512414914439920505f41467ffbc17e95 /components/config | |
parent | 01b6ad55bd435bc4f58e5eab2e8adb7e4febb50a (diff) | |
download | servo-9be4fd56ce5467ce6d0e48c6efda9eb26f40eb7a.tar.gz servo-9be4fd56ce5467ce6d0e48c6efda9eb26f40eb7a.zip |
Removed util.
Diffstat (limited to 'components/config')
-rw-r--r-- | components/config/Cargo.toml | 35 | ||||
-rw-r--r-- | components/config/basedir.rs | 72 | ||||
-rw-r--r-- | components/config/lib.rs | 39 | ||||
-rw-r--r-- | components/config/opts.rs | 958 | ||||
-rw-r--r-- | components/config/prefs.rs | 262 | ||||
-rw-r--r-- | components/config/resource_files.rs | 67 |
6 files changed, 1433 insertions, 0 deletions
diff --git a/components/config/Cargo.toml b/components/config/Cargo.toml new file mode 100644 index 00000000000..1ef60b64382 --- /dev/null +++ b/components/config/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "servo_config" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +publish = false + +[lib] +name = "servo_config" +path = "lib.rs" + +[features] +# servo as opposed to geckolib +servo = ["serde", "serde_derive", "url/heap_size", "url/serde", "plugins"] + +[dependencies] +bitflags = "0.7" +euclid = "0.10.1" +getopts = "0.2.11" +lazy_static = "0.2" +log = "0.3.5" +num_cpus = "1.1.0" +rustc-serialize = "0.3" +serde = {version = "0.8", optional = true} +serde_derive = {version = "0.8", optional = true} +servo_geometry = {path = "../geometry"} +servo_url = {path = "../url"} +plugins = {path = "../plugins", optional = true} +url = "1.2" + +[dev-dependencies] +env_logger = "0.3" + +[target.'cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))'.dependencies] +xdg = "2.0" diff --git a/components/config/basedir.rs b/components/config/basedir.rs new file mode 100644 index 00000000000..5c7896a388b --- /dev/null +++ b/components/config/basedir.rs @@ -0,0 +1,72 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +//! Contains routines for retrieving default config directories. +//! For linux based platforms, it uses the XDG base directory spec but provides +//! similar abstractions for non-linux platforms. + +#[cfg(any(target_os = "macos", target_os = "windows"))] +use std::env; +use std::path::PathBuf; +#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))] +use xdg; + +#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))] +pub fn default_config_dir() -> Option<PathBuf> { + let xdg_dirs = xdg::BaseDirectories::with_profile("servo", "default").unwrap(); + let config_dir = xdg_dirs.get_config_home(); + Some(config_dir) +} + +#[cfg(target_os = "android")] +pub fn default_config_dir() -> Option<PathBuf> { + Some(PathBuf::from("/sdcard/servo")) +} + +#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))] +pub fn default_data_dir() -> Option<PathBuf> { + let xdg_dirs = xdg::BaseDirectories::with_profile("servo", "default").unwrap(); + let data_dir = xdg_dirs.get_data_home(); + Some(data_dir) +} + +#[cfg(target_os = "android")] +pub fn default_data_dir() -> Option<PathBuf> { + Some(PathBuf::from("/sdcard/servo")) +} + +#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))] +pub fn default_cache_dir() -> Option<PathBuf> { + let xdg_dirs = xdg::BaseDirectories::with_profile("servo", "default").unwrap(); + let cache_dir = xdg_dirs.get_cache_home(); + Some(cache_dir) +} + +#[cfg(target_os = "android")] +pub fn default_cache_dir() -> Option<PathBuf> { + Some(PathBuf::from("/sdcard/servo")) +} + +#[cfg(target_os = "macos")] +pub fn default_config_dir() -> Option<PathBuf> { + let mut config_dir = env::home_dir().unwrap(); + config_dir.push("Library"); + config_dir.push("Application Support"); + config_dir.push("Servo"); + Some(config_dir) +} + +#[cfg(target_os = "windows")] +pub fn default_config_dir() -> Option<PathBuf> { + let mut config_dir = match env::var("APPDATA") { + Ok(appdata_path) => PathBuf::from(appdata_path), + Err(_) => { let mut dir = env::home_dir().unwrap(); + dir.push("Appdata"); + dir.push("Roaming"); + dir + } + }; + config_dir.push("Servo"); + Some(config_dir) +} diff --git a/components/config/lib.rs b/components/config/lib.rs new file mode 100644 index 00000000000..e4df206f47a --- /dev/null +++ b/components/config/lib.rs @@ -0,0 +1,39 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +#![cfg_attr(feature = "servo", feature(plugin))] +#![cfg_attr(feature = "servo", feature(proc_macro))] +#![cfg_attr(feature = "servo", plugin(plugins))] + +#![deny(unsafe_code)] + +#[allow(unused_extern_crates)] #[macro_use] extern crate bitflags; +extern crate core; +extern crate euclid; +extern crate getopts; +#[allow(unused_extern_crates)] #[macro_use] extern crate lazy_static; +#[macro_use] extern crate log; +extern crate num_cpus; +extern crate rustc_serialize; +#[cfg(feature = "servo")] extern crate serde; +#[cfg(feature = "servo")] #[macro_use] extern crate serde_derive; +extern crate servo_geometry; +extern crate servo_url; +extern crate url; +#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))] +extern crate xdg; + +pub mod basedir; +#[allow(unsafe_code)] pub mod opts; +pub mod prefs; +pub mod resource_files; + +pub fn servo_version() -> String { + let cargo_version = env!("CARGO_PKG_VERSION"); + let git_info = option_env!("GIT_INFO"); + match git_info { + Some(info) => format!("Servo {}{}", cargo_version, info), + None => format!("Servo {}", cargo_version), + } +} diff --git a/components/config/opts.rs b/components/config/opts.rs new file mode 100644 index 00000000000..a59cd56f0d7 --- /dev/null +++ b/components/config/opts.rs @@ -0,0 +1,958 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +//! Configuration options for a single run of the servo application. Created +//! from command line arguments. + +use euclid::size::TypedSize2D; +use getopts::Options; +use num_cpus; +use prefs::{self, PrefValue, PREFS}; +use resource_files::set_resources_path; +use servo_geometry::ScreenPx; +use servo_url::ServoUrl; +use std::borrow::Cow; +use std::cmp; +use std::default::Default; +use std::env; +use std::fs::{self, File}; +use std::io::{self, Read, Write}; +use std::path::{Path, PathBuf}; +use std::process; +use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; +use url::{self, Url}; + + +/// Global flags for Servo, currently set on the command line. +#[derive(Clone)] +#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +pub struct Opts { + pub is_running_problem_test: bool, + + /// The initial URL to load. + pub url: Option<ServoUrl>, + + /// How many threads to use for CPU painting (`-t`). + /// + /// Note that painting is sequentialized when using GPU painting. + pub paint_threads: usize, + + /// The maximum size of each tile in pixels (`-s`). + pub tile_size: usize, + + /// The ratio of device pixels per px at the default scale. If unspecified, will use the + /// platform default setting. + pub device_pixels_per_px: Option<f32>, + + /// `None` to disable the time profiler or `Some` with an interval in seconds to enable it and + /// cause it to produce output on that interval (`-p`). + pub time_profiling: Option<OutputOptions>, + + /// When the profiler is enabled, this is an optional path to dump a self-contained HTML file + /// visualizing the traces as a timeline. + pub time_profiler_trace_path: Option<String>, + + /// `None` to disable the memory profiler or `Some` with an interval in seconds to enable it + /// and cause it to produce output on that interval (`-m`). + pub mem_profiler_period: Option<f64>, + + pub nonincremental_layout: bool, + + /// Where to load userscripts from, if any. An empty string will load from + /// the resources/user-agent-js directory, and if the option isn't passed userscripts + /// won't be loaded + pub userscripts: Option<String>, + + pub user_stylesheets: Vec<(Vec<u8>, ServoUrl)>, + + pub output_file: Option<String>, + + /// Replace unpaires surrogates in DOM strings with U+FFFD. + /// See https://github.com/servo/servo/issues/6564 + pub replace_surrogates: bool, + + /// Log GC passes and their durations. + pub gc_profile: bool, + + /// Load web fonts synchronously to avoid non-deterministic network-driven reflows. + pub load_webfonts_synchronously: bool, + + pub headless: bool, + pub hard_fail: bool, + + /// True if we should bubble intrinsic widths sequentially (`-b`). If this is true, then + /// intrinsic widths are computed as a separate pass instead of during flow construction. You + /// may wish to turn this flag on in order to benchmark style recalculation against other + /// browser engines. + pub bubble_inline_sizes_separately: bool, + + /// True if we should show borders on all layers and tiles for + /// debugging purposes (`--show-debug-borders`). + pub show_debug_borders: bool, + + /// True if we should show borders on all fragments for debugging purposes + /// (`--show-debug-fragment-borders`). + pub show_debug_fragment_borders: bool, + + /// True if we should paint tiles with overlays based on which thread painted them. + pub show_debug_parallel_paint: bool, + + /// True if we should paint borders around flows based on which thread painted them. + pub show_debug_parallel_layout: bool, + + /// True if we should paint tiles a random color whenever they're repainted. Useful for + /// debugging invalidation. + pub paint_flashing: bool, + + /// If set with --disable-text-aa, disable antialiasing on fonts. This is primarily useful for reftests + /// where pixel perfect results are required when using fonts such as the Ahem + /// font for layout tests. + pub enable_text_antialiasing: bool, + + /// If set with --enable-subpixel, use subpixel antialiasing for glyphs. In the future + /// this will likely become the default, but for now it's opt-in while we work + /// out any bugs and improve the implementation. + pub enable_subpixel_text_antialiasing: bool, + + /// If set with --disable-canvas-aa, disable antialiasing on the HTML canvas element. + /// Like --disable-text-aa, this is useful for reftests where pixel perfect results are required. + pub enable_canvas_antialiasing: bool, + + /// True if each step of layout is traced to an external JSON file + /// for debugging purposes. Settings this implies sequential layout + /// and paint. + pub trace_layout: bool, + + /// Periodically print out on which events script threads spend their processing time. + pub profile_script_events: bool, + + /// Enable all heartbeats for profiling. + pub profile_heartbeats: bool, + + /// `None` to disable debugger or `Some` with a port number to start a server to listen to + /// remote Firefox debugger connections. + pub debugger_port: Option<u16>, + + /// `None` to disable devtools or `Some` with a port number to start a server to listen to + /// remote Firefox devtools connections. + pub devtools_port: Option<u16>, + + /// `None` to disable WebDriver or `Some` with a port number to start a server to listen to + /// remote WebDriver commands. + pub webdriver_port: Option<u16>, + + /// The initial requested size of the window. + pub initial_window_size: TypedSize2D<u32, ScreenPx>, + + /// An optional string allowing the user agent to be set for testing. + pub user_agent: Cow<'static, str>, + + /// Whether we're running in multiprocess mode. + pub multiprocess: bool, + + /// Whether we're running inside the sandbox. + pub sandbox: bool, + + /// Probability of randomly closing a pipeline, + /// used for testing the hardening of the constellation. + pub random_pipeline_closure_probability: Option<f32>, + + /// The seed for the RNG used to randomly close pipelines, + /// used for testing the hardening of the constellation. + pub random_pipeline_closure_seed: Option<usize>, + + /// Dumps the DOM after restyle. + pub dump_style_tree: bool, + + /// Dumps the rule tree. + pub dump_rule_tree: bool, + + /// Dumps the flow tree after a layout. + pub dump_flow_tree: bool, + + /// Dumps the display list after a layout. + pub dump_display_list: bool, + + /// Dumps the display list in JSON form after a layout. + pub dump_display_list_json: bool, + + /// Dumps the layer tree when it changes. + pub dump_layer_tree: bool, + + /// Emits notifications when there is a relayout. + pub relayout_event: bool, + + /// Whether Style Sharing Cache is used + pub disable_share_style_cache: bool, + + /// Whether to show in stdout style sharing cache stats after a restyle. + pub style_sharing_stats: bool, + + /// Translate mouse input into touch events. + pub convert_mouse_to_touch: bool, + + /// True to exit after the page load (`-x`). + pub exit_after_load: bool, + + /// Do not use native titlebar + pub no_native_titlebar: bool, + + /// Enable vsync in the compositor + pub enable_vsync: bool, + + /// True to show webrender profiling stats on screen. + pub webrender_stats: bool, + + /// True to show webrender debug on screen. + pub webrender_debug: bool, + + /// True if webrender recording should be enabled. + pub webrender_record: bool, + + /// True to compile all webrender shaders at init time. This is mostly + /// useful when modifying the shaders, to ensure they all compile + /// after each change is made. + pub precache_shaders: bool, + + /// True if WebRender should use multisample antialiasing. + pub use_msaa: bool, + + /// Directory for a default config directory + pub config_dir: Option<PathBuf>, + + // don't skip any backtraces on panic + pub full_backtraces: bool, + + /// True to use OS native signposting facilities. This makes profiling events (script activity, + /// reflow, compositing, etc.) appear in Instruments.app on macOS. + pub signpost: bool, + + /// Print the version and exit. + pub is_printing_version: bool, +} + +fn print_usage(app: &str, opts: &Options) { + let message = format!("Usage: {} [ options ... ] [URL]\n\twhere options include", app); + println!("{}", opts.usage(&message)); +} + + +/// Debug options for Servo, currently set on the command line with -Z +#[derive(Default)] +pub struct DebugOptions { + /// List all the debug options. + pub help: bool, + + /// Bubble intrinsic widths separately like other engines. + pub bubble_widths: bool, + + /// Disable antialiasing of rendered text. + pub disable_text_aa: bool, + + /// Enable subpixel antialiasing of rendered text. + pub enable_subpixel_aa: bool, + + /// Disable antialiasing of rendered text on the HTML canvas element. + pub disable_canvas_aa: bool, + + /// Print the DOM after each restyle. + pub dump_style_tree: bool, + + /// Dumps the rule tree. + pub dump_rule_tree: bool, + + /// Print the flow tree after each layout. + pub dump_flow_tree: bool, + + /// Print the display list after each layout. + pub dump_display_list: bool, + + /// Print the display list in JSON form. + pub dump_display_list_json: bool, + + /// Print the layer tree whenever it changes. + pub dump_layer_tree: bool, + + /// Print notifications when there is a relayout. + pub relayout_event: bool, + + /// Profile which events script threads spend their time on. + pub profile_script_events: bool, + + /// Enable all heartbeats for profiling. + pub profile_heartbeats: bool, + + /// Paint borders along layer and tile boundaries. + pub show_compositor_borders: bool, + + /// Paint borders along fragment boundaries. + pub show_fragment_borders: bool, + + /// Overlay tiles with colors showing which thread painted them. + pub show_parallel_paint: bool, + + /// Mark which thread laid each flow out with colors. + pub show_parallel_layout: bool, + + /// Overlay repainted areas with a random color. + pub paint_flashing: bool, + + /// Write layout trace to an external file for debugging. + pub trace_layout: bool, + + /// Disable the style sharing cache. + pub disable_share_style_cache: bool, + + /// Whether to show in stdout style sharing cache stats after a restyle. + pub style_sharing_stats: bool, + + /// Translate mouse input into touch events. + pub convert_mouse_to_touch: bool, + + /// Replace unpaires surrogates in DOM strings with U+FFFD. + /// See https://github.com/servo/servo/issues/6564 + pub replace_surrogates: bool, + + /// Log GC passes and their durations. + pub gc_profile: bool, + + /// Load web fonts synchronously to avoid non-deterministic network-driven reflows. + pub load_webfonts_synchronously: bool, + + /// Disable vsync in the compositor + pub disable_vsync: bool, + + /// Show webrender profiling stats on screen. + pub webrender_stats: bool, + + /// Show webrender debug on screen. + pub webrender_debug: bool, + + /// Enable webrender recording. + pub webrender_record: bool, + + /// Use multisample antialiasing in WebRender. + pub use_msaa: bool, + + // don't skip any backtraces on panic + pub full_backtraces: bool, + + /// True to compile all webrender shaders at init time. This is mostly + /// useful when modifying the shaders, to ensure they all compile + /// after each change is made. + pub precache_shaders: bool, + + /// True to use OS native signposting facilities. This makes profiling events (script activity, + /// reflow, compositing, etc.) appear in Instruments.app on macOS. + pub signpost: bool, +} + + +impl DebugOptions { + pub fn extend(&mut self, debug_string: String) -> Result<(), String> { + for option in debug_string.split(',') { + match option { + "help" => self.help = true, + "bubble-widths" => self.bubble_widths = true, + "disable-text-aa" => self.disable_text_aa = true, + "enable-subpixel-aa" => self.enable_subpixel_aa = true, + "disable-canvas-aa" => self.disable_text_aa = true, + "dump-style-tree" => self.dump_style_tree = true, + "dump-rule-tree" => self.dump_rule_tree = true, + "dump-flow-tree" => self.dump_flow_tree = true, + "dump-display-list" => self.dump_display_list = true, + "dump-display-list-json" => self.dump_display_list_json = true, + "dump-layer-tree" => self.dump_layer_tree = true, + "relayout-event" => self.relayout_event = true, + "profile-script-events" => self.profile_script_events = true, + "profile-heartbeats" => self.profile_heartbeats = true, + "show-compositor-borders" => self.show_compositor_borders = true, + "show-fragment-borders" => self.show_fragment_borders = true, + "show-parallel-paint" => self.show_parallel_paint = true, + "show-parallel-layout" => self.show_parallel_layout = true, + "paint-flashing" => self.paint_flashing = true, + "trace-layout" => self.trace_layout = true, + "disable-share-style-cache" => self.disable_share_style_cache = true, + "style-sharing-stats" => self.style_sharing_stats = true, + "convert-mouse-to-touch" => self.convert_mouse_to_touch = true, + "replace-surrogates" => self.replace_surrogates = true, + "gc-profile" => self.gc_profile = true, + "load-webfonts-synchronously" => self.load_webfonts_synchronously = true, + "disable-vsync" => self.disable_vsync = true, + "wr-stats" => self.webrender_stats = true, + "wr-debug" => self.webrender_debug = true, + "wr-record" => self.webrender_record = true, + "msaa" => self.use_msaa = true, + "full-backtraces" => self.full_backtraces = true, + "precache-shaders" => self.precache_shaders = true, + "signpost" => self.signpost = true, + "" => {}, + _ => return Err(String::from(option)), + }; + }; + Ok(()) + } +} + + +fn print_debug_usage(app: &str) -> ! { + fn print_option(name: &str, description: &str) { + println!("\t{:<35} {}", name, description); + } + + println!("Usage: {} debug option,[options,...]\n\twhere options include\n\nOptions:", app); + + print_option("bubble-widths", "Bubble intrinsic widths separately like other engines."); + print_option("disable-text-aa", "Disable antialiasing of rendered text."); + print_option("disable-canvas-aa", "Disable antialiasing on the HTML canvas element."); + print_option("dump-style-tree", "Print the DOM with computed styles after each restyle."); + print_option("dump-flow-tree", "Print the flow tree after each layout."); + print_option("dump-display-list", "Print the display list after each layout."); + print_option("dump-display-list-json", "Print the display list in JSON form."); + print_option("dump-layer-tree", "Print the layer tree whenever it changes."); + print_option("relayout-event", "Print notifications when there is a relayout."); + print_option("profile-script-events", "Enable profiling of script-related events."); + print_option("profile-heartbeats", "Enable heartbeats for all thread categories."); + print_option("show-compositor-borders", "Paint borders along layer and tile boundaries."); + print_option("show-fragment-borders", "Paint borders along fragment boundaries."); + print_option("show-parallel-paint", "Overlay tiles with colors showing which thread painted them."); + print_option("show-parallel-layout", "Mark which thread laid each flow out with colors."); + print_option("paint-flashing", "Overlay repainted areas with a random color."); + print_option("trace-layout", "Write layout trace to an external file for debugging."); + print_option("disable-share-style-cache", + "Disable the style sharing cache."); + print_option("parallel-display-list-building", "Build display lists in parallel."); + print_option("convert-mouse-to-touch", "Send touch events instead of mouse events"); + print_option("replace-surrogates", "Replace unpaires surrogates in DOM strings with U+FFFD. \ + See https://github.com/servo/servo/issues/6564"); + print_option("gc-profile", "Log GC passes and their durations."); + print_option("load-webfonts-synchronously", + "Load web fonts synchronously to avoid non-deterministic network-driven reflows"); + print_option("disable-vsync", + "Disable vsync mode in the compositor to allow profiling at more than monitor refresh rate"); + print_option("wr-stats", "Show WebRender profiler on screen."); + print_option("msaa", "Use multisample antialiasing in WebRender."); + print_option("full-backtraces", "Print full backtraces for all errors"); + print_option("wr-debug", "Display webrender tile borders. Must be used with -w option."); + print_option("precache-shaders", "Compile all shaders during init. Must be used with -w option."); + print_option("signpost", "Emit native OS signposts for profile events (currently macOS only)"); + + println!(""); + + process::exit(0) +} + +#[derive(Clone)] +#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +pub enum OutputOptions { + FileName(String), + Stdout(f64) +} + +fn args_fail(msg: &str) -> ! { + writeln!(io::stderr(), "{}", msg).unwrap(); + process::exit(1) +} + +static MULTIPROCESS: AtomicBool = ATOMIC_BOOL_INIT; + +#[inline] +pub fn multiprocess() -> bool { + MULTIPROCESS.load(Ordering::Relaxed) +} + +enum UserAgent { + Desktop, + Android, +} + +fn default_user_agent_string(agent: UserAgent) -> &'static str { + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] + const DESKTOP_UA_STRING: &'static str = + "Mozilla/5.0 (X11; Linux x86_64; rv:37.0) Servo/1.0 Firefox/37.0"; + #[cfg(all(target_os = "linux", not(target_arch = "x86_64")))] + const DESKTOP_UA_STRING: &'static str = + "Mozilla/5.0 (X11; Linux i686; rv:37.0) Servo/1.0 Firefox/37.0"; + + #[cfg(all(target_os = "windows", target_arch = "x86_64"))] + const DESKTOP_UA_STRING: &'static str = + "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:37.0) Servo/1.0 Firefox/37.0"; + #[cfg(all(target_os = "windows", not(target_arch = "x86_64")))] + const DESKTOP_UA_STRING: &'static str = + "Mozilla/5.0 (Windows NT 6.1; rv:37.0) Servo/1.0 Firefox/37.0"; + + #[cfg(not(any(target_os = "linux", target_os = "windows")))] + // Neither Linux nor Windows, so maybe OS X, and if not then OS X is an okay fallback. + const DESKTOP_UA_STRING: &'static str = + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:37.0) Servo/1.0 Firefox/37.0"; + + + match agent { + UserAgent::Desktop => { + DESKTOP_UA_STRING + } + UserAgent::Android => { + "Mozilla/5.0 (Android; Mobile; rv:37.0) Servo/1.0 Firefox/37.0" + } + } +} + +#[cfg(target_os = "android")] +const DEFAULT_USER_AGENT: UserAgent = UserAgent::Android; + +#[cfg(not(target_os = "android"))] +const DEFAULT_USER_AGENT: UserAgent = UserAgent::Desktop; + +pub fn default_opts() -> Opts { + Opts { + is_running_problem_test: false, + url: Some(ServoUrl::parse("about:blank").unwrap()), + paint_threads: 1, + tile_size: 512, + device_pixels_per_px: None, + time_profiling: None, + time_profiler_trace_path: None, + mem_profiler_period: None, + nonincremental_layout: false, + userscripts: None, + user_stylesheets: Vec::new(), + output_file: None, + replace_surrogates: false, + gc_profile: false, + load_webfonts_synchronously: false, + headless: true, + hard_fail: true, + bubble_inline_sizes_separately: false, + show_debug_borders: false, + show_debug_fragment_borders: false, + show_debug_parallel_paint: false, + show_debug_parallel_layout: false, + paint_flashing: false, + enable_text_antialiasing: false, + enable_subpixel_text_antialiasing: false, + enable_canvas_antialiasing: false, + trace_layout: false, + debugger_port: None, + devtools_port: None, + webdriver_port: None, + initial_window_size: TypedSize2D::new(1024, 740), + user_agent: default_user_agent_string(DEFAULT_USER_AGENT).into(), + multiprocess: false, + random_pipeline_closure_probability: None, + random_pipeline_closure_seed: None, + sandbox: false, + dump_style_tree: false, + dump_rule_tree: false, + dump_flow_tree: false, + dump_display_list: false, + dump_display_list_json: false, + dump_layer_tree: false, + relayout_event: false, + profile_script_events: false, + profile_heartbeats: false, + disable_share_style_cache: false, + style_sharing_stats: false, + convert_mouse_to_touch: false, + exit_after_load: false, + no_native_titlebar: false, + enable_vsync: true, + webrender_stats: false, + use_msaa: false, + config_dir: None, + full_backtraces: false, + is_printing_version: false, + webrender_debug: false, + webrender_record: false, + precache_shaders: false, + signpost: false, + } +} + +pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult { + let (app_name, args) = args.split_first().unwrap(); + + let mut opts = Options::new(); + opts.optflag("c", "cpu", "CPU painting"); + opts.optflag("g", "gpu", "GPU painting"); + opts.optopt("o", "output", "Output file", "output.png"); + opts.optopt("s", "size", "Size of tiles", "512"); + opts.optopt("", "device-pixel-ratio", "Device pixels per px", ""); + opts.optopt("t", "threads", "Number of paint threads", "1"); + opts.optflagopt("p", "profile", "Time profiler flag and either a TSV output filename \ + OR an interval for output to Stdout (blank for Stdout with interval of 5s)", "10 \ + OR time.tsv"); + opts.optflagopt("", "profiler-trace-path", + "Path to dump a self-contained HTML timeline of profiler traces", + ""); + opts.optflagopt("m", "memory-profile", "Memory profiler flag and output interval", "10"); + opts.optflag("x", "exit", "Exit after load flag"); + opts.optopt("y", "layout-threads", "Number of threads to use for layout", "1"); + opts.optflag("i", "nonincremental-layout", "Enable to turn off incremental layout."); + opts.optflagopt("", "userscripts", + "Uses userscripts in resources/user-agent-js, or a specified full path", ""); + opts.optmulti("", "user-stylesheet", + "A user stylesheet to be added to every document", "file.css"); + opts.optflag("z", "headless", "Headless mode"); + opts.optflag("f", "hard-fail", "Exit on thread failure instead of displaying about:failure"); + opts.optflag("F", "soft-fail", "Display about:failure on thread failure instead of exiting"); + opts.optflagopt("", "remote-debugging-port", "Start remote debugger server on port", "2794"); + opts.optflagopt("", "devtools", "Start remote devtools server on port", "6000"); + opts.optflagopt("", "webdriver", "Start remote WebDriver server on port", "7000"); + opts.optopt("", "resolution", "Set window resolution.", "1024x740"); + opts.optopt("u", + "user-agent", + "Set custom user agent string (or android / desktop for platform default)", + "NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)"); + opts.optflag("M", "multiprocess", "Run in multiprocess mode"); + opts.optflag("S", "sandbox", "Run in a sandbox if multiprocess"); + opts.optopt("", + "random-pipeline-closure-probability", + "Probability of randomly closing a pipeline (for testing constellation hardening).", + "0.0"); + opts.optopt("", "random-pipeline-closure-seed", "A fixed seed for repeatbility of random pipeline closure.", ""); + opts.optmulti("Z", "debug", + "A comma-separated string of debug options. Pass help to show available options.", ""); + opts.optflag("h", "help", "Print this message"); + opts.optopt("", "resources-path", "Path to find static resources", "/home/servo/resources"); + opts.optopt("", "content-process" , "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.mozbrowser.enabled"); + 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"); + opts.optopt("", "config-dir", + "config directory following xdg spec on linux platform", ""); + opts.optflag("v", "version", "Display servo version information"); + + let opt_match = match opts.parse(args) { + Ok(m) => m, + Err(f) => args_fail(&f.to_string()), + }; + + set_resources_path(opt_match.opt_str("resources-path")); + + if opt_match.opt_present("h") || opt_match.opt_present("help") { + print_usage(app_name, &opts); + process::exit(0); + }; + + // If this is the content process, we'll receive the real options over IPC. So just fill in + // some dummy options for now. + if let Some(content_process) = opt_match.opt_str("content-process") { + MULTIPROCESS.store(true, Ordering::SeqCst); + return ArgumentParsingResult::ContentProcess(content_process); + } + + let mut debug_options = DebugOptions::default(); + + for debug_string in opt_match.opt_strs("Z") { + if let Err(e) = debug_options.extend(debug_string) { + args_fail(&format!("error: unrecognized debug option: {}", e)); + } + } + + if debug_options.help { + print_debug_usage(app_name) + } + + let cwd = env::current_dir().unwrap(); + let homepage_pref = PREFS.get("shell.homepage"); + let url_opt = if !opt_match.free.is_empty() { + Some(&opt_match.free[0][..]) + } else { + homepage_pref.as_string() + }; + let is_running_problem_test = + url_opt + .as_ref() + .map_or(false, |url| + url.starts_with("http://web-platform.test:8000/2dcontext/drawing-images-to-the-canvas/") || + url.starts_with("http://web-platform.test:8000/_mozilla/mozilla/canvas/") || + url.starts_with("http://web-platform.test:8000/_mozilla/css/canvas_over_area.html")); + + let url = match url_opt { + Some(url_string) => { + parse_url_or_filename(&cwd, url_string) + .unwrap_or_else(|()| args_fail("URL parsing failed")) + }, + None => { + print_usage(app_name, &opts); + args_fail("servo asks that you provide a URL") + } + }; + + let tile_size: usize = match opt_match.opt_str("s") { + Some(tile_size_str) => tile_size_str.parse() + .unwrap_or_else(|err| args_fail(&format!("Error parsing option: -s ({})", err))), + None => 512, + }; + + let device_pixels_per_px = opt_match.opt_str("device-pixel-ratio").map(|dppx_str| + dppx_str.parse() + .unwrap_or_else(|err| args_fail(&format!("Error parsing option: --device-pixel-ratio ({})", err))) + ); + + let mut paint_threads: usize = match opt_match.opt_str("t") { + Some(paint_threads_str) => paint_threads_str.parse() + .unwrap_or_else(|err| args_fail(&format!("Error parsing option: -t ({})", err))), + None => cmp::max(num_cpus::get() * 3 / 4, 1), + }; + + // If only the flag is present, default to a 5 second period for both profilers + let time_profiling = if opt_match.opt_present("p") { + match opt_match.opt_str("p") { + Some(argument) => match argument.parse::<f64>() { + Ok(interval) => Some(OutputOptions::Stdout(interval)) , + Err(_) => Some(OutputOptions::FileName(argument)), + }, + None => Some(OutputOptions::Stdout(5.0 as f64)), + } + } else { + // if the p option doesn't exist: + None + }; + + if let Some(ref time_profiler_trace_path) = opt_match.opt_str("profiler-trace-path") { + let mut path = PathBuf::from(time_profiler_trace_path); + path.pop(); + if let Err(why) = fs::create_dir_all(&path) { + error!("Couldn't create/open {:?}: {:?}", + Path::new(time_profiler_trace_path).to_string_lossy(), why); + } + } + + let mem_profiler_period = opt_match.opt_default("m", "5").map(|period| { + period.parse().unwrap_or_else(|err| args_fail(&format!("Error parsing option: -m ({})", err))) + }); + + let mut layout_threads: Option<usize> = opt_match.opt_str("y") + .map(|layout_threads_str| { + layout_threads_str.parse() + .unwrap_or_else(|err| args_fail(&format!("Error parsing option: -y ({})", err))) + }); + + let nonincremental_layout = opt_match.opt_present("i"); + + let random_pipeline_closure_probability = opt_match.opt_str("random-pipeline-closure-probability").map(|prob| + prob.parse().unwrap_or_else(|err| { + args_fail(&format!("Error parsing option: --random-pipeline-closure-probability ({})", err)) + }) + ); + + let random_pipeline_closure_seed = opt_match.opt_str("random-pipeline-closure-seed").map(|seed| + seed.parse().unwrap_or_else(|err| { + args_fail(&format!("Error parsing option: --random-pipeline-closure-seed ({})", err)) + }) + ); + + let mut bubble_inline_sizes_separately = debug_options.bubble_widths; + if debug_options.trace_layout { + paint_threads = 1; + layout_threads = Some(1); + bubble_inline_sizes_separately = true; + } + + let debugger_port = opt_match.opt_default("remote-debugging-port", "2794").map(|port| { + port.parse() + .unwrap_or_else(|err| args_fail(&format!("Error parsing option: --remote-debugging-port ({})", err))) + }); + + let devtools_port = opt_match.opt_default("devtools", "6000").map(|port| { + port.parse().unwrap_or_else(|err| args_fail(&format!("Error parsing option: --devtools ({})", err))) + }); + + let webdriver_port = opt_match.opt_default("webdriver", "7000").map(|port| { + port.parse().unwrap_or_else(|err| args_fail(&format!("Error parsing option: --webdriver ({})", err))) + }); + + let initial_window_size = match opt_match.opt_str("resolution") { + Some(res_string) => { + let res: Vec<u32> = res_string.split('x').map(|r| { + r.parse().unwrap_or_else(|err| args_fail(&format!("Error parsing option: --resolution ({})", err))) + }).collect(); + TypedSize2D::new(res[0], res[1]) + } + None => { + TypedSize2D::new(1024, 740) + } + }; + + if opt_match.opt_present("M") { + MULTIPROCESS.store(true, Ordering::SeqCst) + } + + let user_agent = match opt_match.opt_str("u") { + Some(ref ua) if ua == "android" => default_user_agent_string(UserAgent::Android).into(), + Some(ref ua) if ua == "desktop" => default_user_agent_string(UserAgent::Desktop).into(), + Some(ua) => ua.into(), + None => default_user_agent_string(DEFAULT_USER_AGENT).into(), + }; + + let user_stylesheets = opt_match.opt_strs("user-stylesheet").iter().map(|filename| { + let path = cwd.join(filename); + let url = ServoUrl::from_url(Url::from_file_path(&path).unwrap()); + let mut contents = Vec::new(); + File::open(path) + .unwrap_or_else(|err| args_fail(&format!("Couldn't open {}: {}", filename, err))) + .read_to_end(&mut contents) + .unwrap_or_else(|err| args_fail(&format!("Couldn't read {}: {}", filename, err))); + (contents, url) + }).collect(); + + let do_not_use_native_titlebar = + opt_match.opt_present("b") || + !PREFS.get("shell.native-titlebar.enabled").as_boolean().unwrap(); + + let is_printing_version = opt_match.opt_present("v") || opt_match.opt_present("version"); + + let opts = Opts { + is_running_problem_test: is_running_problem_test, + url: Some(url), + paint_threads: paint_threads, + tile_size: tile_size, + device_pixels_per_px: device_pixels_per_px, + time_profiling: time_profiling, + time_profiler_trace_path: opt_match.opt_str("profiler-trace-path"), + mem_profiler_period: mem_profiler_period, + nonincremental_layout: nonincremental_layout, + userscripts: opt_match.opt_default("userscripts", ""), + user_stylesheets: user_stylesheets, + output_file: opt_match.opt_str("o"), + replace_surrogates: debug_options.replace_surrogates, + gc_profile: debug_options.gc_profile, + load_webfonts_synchronously: debug_options.load_webfonts_synchronously, + headless: opt_match.opt_present("z"), + hard_fail: opt_match.opt_present("f") && !opt_match.opt_present("F"), + bubble_inline_sizes_separately: bubble_inline_sizes_separately, + profile_script_events: debug_options.profile_script_events, + profile_heartbeats: debug_options.profile_heartbeats, + trace_layout: debug_options.trace_layout, + debugger_port: debugger_port, + devtools_port: devtools_port, + webdriver_port: webdriver_port, + initial_window_size: initial_window_size, + user_agent: user_agent, + multiprocess: opt_match.opt_present("M"), + sandbox: opt_match.opt_present("S"), + random_pipeline_closure_probability: random_pipeline_closure_probability, + random_pipeline_closure_seed: random_pipeline_closure_seed, + show_debug_borders: debug_options.show_compositor_borders, + show_debug_fragment_borders: debug_options.show_fragment_borders, + show_debug_parallel_paint: debug_options.show_parallel_paint, + show_debug_parallel_layout: debug_options.show_parallel_layout, + paint_flashing: debug_options.paint_flashing, + enable_text_antialiasing: !debug_options.disable_text_aa, + enable_subpixel_text_antialiasing: debug_options.enable_subpixel_aa, + enable_canvas_antialiasing: !debug_options.disable_canvas_aa, + dump_style_tree: debug_options.dump_style_tree, + dump_rule_tree: debug_options.dump_rule_tree, + dump_flow_tree: debug_options.dump_flow_tree, + dump_display_list: debug_options.dump_display_list, + dump_display_list_json: debug_options.dump_display_list_json, + dump_layer_tree: debug_options.dump_layer_tree, + relayout_event: debug_options.relayout_event, + disable_share_style_cache: debug_options.disable_share_style_cache, + style_sharing_stats: debug_options.style_sharing_stats, + convert_mouse_to_touch: debug_options.convert_mouse_to_touch, + exit_after_load: opt_match.opt_present("x"), + no_native_titlebar: do_not_use_native_titlebar, + enable_vsync: !debug_options.disable_vsync, + webrender_stats: debug_options.webrender_stats, + use_msaa: debug_options.use_msaa, + config_dir: opt_match.opt_str("config-dir").map(Into::into), + full_backtraces: debug_options.full_backtraces, + is_printing_version: is_printing_version, + webrender_debug: debug_options.webrender_debug, + webrender_record: debug_options.webrender_record, + precache_shaders: debug_options.precache_shaders, + signpost: debug_options.signpost, + }; + + set_defaults(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() { + 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)), + _ => PREFS.set(pref_name, PrefValue::String(value.unwrap().to_string())) + }; + } + + 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::<f64>().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)); + } + + ArgumentParsingResult::ChromeProcess +} + +pub enum ArgumentParsingResult { + ChromeProcess, + ContentProcess(String), +} + +// Make Opts available globally. This saves having to clone and pass +// opts everywhere it is used, which gets particularly cumbersome +// when passing through the DOM structures. +static mut DEFAULT_OPTIONS: *mut Opts = 0 as *mut Opts; +const INVALID_OPTIONS: *mut Opts = 0x01 as *mut Opts; + +lazy_static! { + static ref OPTIONS: Opts = { + unsafe { + let initial = if !DEFAULT_OPTIONS.is_null() { + let opts = Box::from_raw(DEFAULT_OPTIONS); + *opts + } else { + default_opts() + }; + DEFAULT_OPTIONS = INVALID_OPTIONS; + initial + } + }; +} + +pub fn set_defaults(opts: Opts) { + unsafe { + assert!(DEFAULT_OPTIONS.is_null()); + assert!(DEFAULT_OPTIONS != INVALID_OPTIONS); + let box_opts = Box::new(opts); + DEFAULT_OPTIONS = Box::into_raw(box_opts); + } +} + +#[inline] +pub fn get() -> &'static Opts { + &OPTIONS +} + +pub fn parse_url_or_filename(cwd: &Path, input: &str) -> Result<ServoUrl, ()> { + match ServoUrl::parse(input) { + Ok(url) => Ok(url), + Err(url::ParseError::RelativeUrlWithoutBase) => { + Url::from_file_path(&*cwd.join(input)).map(ServoUrl::from_url) + } + Err(_) => Err(()), + } +} + +impl Opts { + pub fn should_use_osmesa(&self) -> bool { + self.headless + } +} diff --git a/components/config/prefs.rs b/components/config/prefs.rs new file mode 100644 index 00000000000..c0ad11bfe0b --- /dev/null +++ b/components/config/prefs.rs @@ -0,0 +1,262 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use basedir::default_config_dir; +use opts; +use resource_files::resources_dir_path; +use rustc_serialize::json::{Json, ToJson}; +use std::borrow::ToOwned; +use std::collections::HashMap; +use std::fs::File; +use std::io::{Read, Write, stderr}; +use std::path::PathBuf; +use std::sync::{Arc, RwLock}; + +lazy_static! { + pub static ref PREFS: Preferences = { + let prefs = read_prefs().ok().unwrap_or_else(HashMap::new); + Preferences(Arc::new(RwLock::new(prefs))) + }; +} + +#[derive(PartialEq, Clone, Debug)] +#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +pub enum PrefValue { + Boolean(bool), + String(String), + Number(f64), + 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), + Json::F64(x) => PrefValue::Number(x), + Json::I64(x) => PrefValue::Number(x as f64), + Json::U64(x) => PrefValue::Number(x as f64), + _ => 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 + } + } + + pub fn as_i64(&self) -> Option<i64> { + match *self { + PrefValue::Number(x) => Some(x as i64), + _ => None, + } + } + + pub fn as_u64(&self) -> Option<u64> { + match *self { + PrefValue::Number(x) => Some(x as u64), + _ => 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::Number(x) => { + Json::F64(x) + }, + PrefValue::Missing => Json::Null + } + } +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +pub 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 { + Pref::NoDefault(ref mut pref_value) => { + *pref_value = Arc::new(value) + }, + 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() + } +} + +pub fn read_prefs_from_file<T>(mut file: T) + -> Result<HashMap<String, Pref>, ()> where T: Read { + let json = try!(Json::from_reader(&mut file).or_else(|e| { + println!("Ignoring invalid JSON in preferences: {:?}.", e); + Err(()) + })); + + let mut prefs = HashMap::new(); + if let Json::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), + } + } + } + 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 => { + let mut path = default_config_dir().unwrap(); + if path.join("prefs.json").exists() { + init_user_prefs(&mut path); + } + } + } +} + +fn init_user_prefs(path: &mut PathBuf) { + path.push("prefs.json"); + if let Ok(file) = File::open(path) { + if let Ok(prefs) = read_prefs_from_file(file) { + PREFS.extend(prefs); + } + } else { + writeln!(&mut stderr(), "Error opening prefs.json from config directory") + .expect("failed printing to stderr"); + } +} + +fn read_prefs() -> Result<HashMap<String, Pref>, ()> { + let mut path = try!(resources_dir_path().map_err(|_| ())); + path.push("prefs.json"); + + let file = try!(File::open(path).or_else(|e| { + writeln!(&mut stderr(), "Error opening preferences: {:?}.", e) + .expect("failed printing to stderr"); + Err(()) + })); + + read_prefs_from_file(file) +} + +pub struct Preferences(Arc<RwLock<HashMap<String, Pref>>>); + +impl Preferences { + pub fn get(&self, name: &str) -> Arc<PrefValue> { + self.0.read().unwrap().get(name).map_or(Arc::new(PrefValue::Missing), |x| x.value().clone()) + } + + pub fn cloned(&self) -> HashMap<String, Pref> { + self.0.read().unwrap().clone() + } + + pub fn is_mozbrowser_enabled(&self) -> bool { + self.get("dom.mozbrowser.enabled").as_boolean().unwrap_or(false) + } + + 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)); + } + + pub fn reset(&self, name: &str) -> Arc<PrefValue> { + 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 + } + + pub fn reset_all(&self) { + let names = { + self.0.read().unwrap().keys().cloned().collect::<Vec<String>>() + }; + for name in names.iter() { + self.reset(name); + } + } + + pub fn extend(&self, extension: HashMap<String, Pref>) { + self.0.write().unwrap().extend(extension); + } +} diff --git a/components/config/resource_files.rs b/components/config/resource_files.rs new file mode 100644 index 00000000000..4a155ecf51e --- /dev/null +++ b/components/config/resource_files.rs @@ -0,0 +1,67 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +#[cfg(not(target_os = "android"))] +use std::env; +use std::fs::File; +use std::io::{self, Read}; +use std::path::{Path, PathBuf}; +use std::sync::{Arc, Mutex}; + +lazy_static! { + static ref CMD_RESOURCE_DIR: Arc<Mutex<Option<String>>> = { + Arc::new(Mutex::new(None)) + }; +} + +pub fn set_resources_path(path: Option<String>) { + let mut dir = CMD_RESOURCE_DIR.lock().unwrap(); + *dir = path; +} + +#[cfg(target_os = "android")] +pub fn resources_dir_path() -> io::Result<PathBuf> { + Ok(PathBuf::from("/sdcard/servo/")) +} + +#[cfg(not(target_os = "android"))] +pub fn resources_dir_path() -> io::Result<PathBuf> { + let mut dir = CMD_RESOURCE_DIR.lock().unwrap(); + + if let Some(ref path) = *dir { + return Ok(PathBuf::from(path)); + } + + // FIXME: Find a way to not rely on the executable being + // under `<servo source>[/$target_triple]/target/debug` + // or `<servo source>[/$target_triple]/target/release`. + let mut path = try!(env::current_exe()); + // Follow symlink + path = try!(path.canonicalize()); + + while path.pop() { + path.push("resources"); + if path.is_dir() { + break; + } + path.pop(); + // Check for Resources on mac when using a case sensitive filesystem. + path.push("Resources"); + if path.is_dir() { + break; + } + path.pop(); + } + *dir = Some(path.to_str().unwrap().to_owned()); + Ok(path) +} + +pub fn read_resource_file<P: AsRef<Path>>(relative_path: P) -> io::Result<Vec<u8>> { + let mut path = try!(resources_dir_path()); + path.push(relative_path); + let mut file = try!(File::open(&path)); + let mut data = Vec::new(); + try!(file.read_to_end(&mut data)); + Ok(data) +} |