mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
657 lines
23 KiB
Rust
657 lines
23 KiB
Rust
/* 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 std::borrow::ToOwned;
|
||
use std::collections::HashMap;
|
||
use std::convert::{TryFrom, TryInto};
|
||
use std::sync::LazyLock;
|
||
|
||
use embedder_traits::resources::{self, Resource};
|
||
use gen::Prefs;
|
||
use log::{error, warn};
|
||
use serde_json::{self, Value};
|
||
|
||
use crate::pref_util::Preferences;
|
||
pub use crate::pref_util::{PrefError, PrefValue};
|
||
|
||
static PREFS: LazyLock<Preferences<'static, Prefs>> = LazyLock::new(|| {
|
||
let def_prefs: Prefs = serde_json::from_str(&resources::read_string(Resource::Preferences))
|
||
.unwrap_or_else(|_| {
|
||
error!("Preference json file is invalid. Setting Preference to default values");
|
||
Prefs::default()
|
||
});
|
||
let result = Preferences::new(def_prefs, &gen::PREF_ACCESSORS);
|
||
for (key, value) in result.iter() {
|
||
set_stylo_pref_ref(&key, &value);
|
||
}
|
||
result
|
||
});
|
||
|
||
/// 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()
|
||
}};
|
||
}
|
||
|
||
/// 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 value = $value;
|
||
$crate::prefs::set_stylo_pref(stringify!($($segment).+), value);
|
||
let values = $crate::prefs::pref_map().values();
|
||
let mut lock = values.write().unwrap();
|
||
lock$ (.$segment)+ = 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 add_user_prefs(prefs: HashMap<String, PrefValue>) {
|
||
for (key, value) in prefs.iter() {
|
||
set_stylo_pref_ref(key, value);
|
||
}
|
||
if let Err(error) = PREFS.set_all(prefs) {
|
||
panic!("Error setting preference: {:?}", error);
|
||
}
|
||
}
|
||
|
||
pub fn set_stylo_pref(key: &str, value: impl Into<PrefValue>) {
|
||
set_stylo_pref_ref(key, &value.into());
|
||
}
|
||
|
||
fn set_stylo_pref_ref(key: &str, value: &PrefValue) {
|
||
match value.try_into() {
|
||
Ok(StyloPrefValue::Bool(value)) => style_config::set_bool(key, value),
|
||
Ok(StyloPrefValue::Int(value)) => style_config::set_i32(key, value),
|
||
Err(TryFromPrefValueError::IntegerOverflow(value)) => {
|
||
// TODO: logging doesn’t actually work this early, so we should
|
||
// split PrefValue into i32 and i64 variants.
|
||
warn!("Pref value too big for Stylo: {} ({})", key, value);
|
||
},
|
||
Err(TryFromPrefValueError::UnmappedType) => {
|
||
// Most of Servo’s prefs will hit this. When adding a new pref type
|
||
// in Stylo, update TryFrom<&PrefValue> for StyloPrefValue as well.
|
||
},
|
||
}
|
||
}
|
||
|
||
enum StyloPrefValue {
|
||
Bool(bool),
|
||
Int(i32),
|
||
}
|
||
|
||
enum TryFromPrefValueError {
|
||
IntegerOverflow(i64),
|
||
UnmappedType,
|
||
}
|
||
|
||
impl TryFrom<&PrefValue> for StyloPrefValue {
|
||
type Error = TryFromPrefValueError;
|
||
|
||
fn try_from(value: &PrefValue) -> Result<Self, Self::Error> {
|
||
match *value {
|
||
PrefValue::Int(value) => {
|
||
if let Ok(value) = value.try_into() {
|
||
Ok(Self::Int(value))
|
||
} else {
|
||
Err(TryFromPrefValueError::IntegerOverflow(value))
|
||
}
|
||
},
|
||
PrefValue::Bool(value) => Ok(Self::Bool(value)),
|
||
_ => Err(TryFromPrefValueError::UnmappedType),
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn read_prefs_map(txt: &str) -> Result<HashMap<String, PrefValue>, PrefError> {
|
||
let prefs: HashMap<String, Value> =
|
||
serde_json::from_str(txt).map_err(PrefError::JsonParseErr)?;
|
||
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()),
|
||
Value::Array(v) => {
|
||
let mut array = v.iter().map(PrefValue::from_json_value);
|
||
if array.all(|v| v.is_some()) {
|
||
PrefValue::Array(array.flatten().collect())
|
||
} else {
|
||
return Err(PrefError::InvalidValue(format!(
|
||
"Invalid value: {}",
|
||
pref_value
|
||
)));
|
||
}
|
||
},
|
||
_ => {
|
||
return Err(PrefError::InvalidValue(format!(
|
||
"Invalid value: {}",
|
||
pref_value
|
||
)));
|
||
},
|
||
};
|
||
(k.to_owned(), v)
|
||
})
|
||
})
|
||
.collect()
|
||
}
|
||
|
||
mod gen {
|
||
use serde::{Deserialize, Serialize};
|
||
use servo_config_plugins::build_structs;
|
||
|
||
// 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
|
||
}
|
||
|
||
fn default_font_size() -> i64 {
|
||
16
|
||
}
|
||
|
||
fn default_monospace_font_size() -> i64 {
|
||
13
|
||
}
|
||
|
||
build_structs! {
|
||
// type of the accessors
|
||
accessor_type = crate::pref_util::Accessor::<Prefs, crate::pref_util::PrefValue>,
|
||
// name of the constant, which will hold a HashMap of preference accessors
|
||
gen_accessors = PREF_ACCESSORS,
|
||
// tree of structs to generate
|
||
gen_types = Prefs {
|
||
fonts: {
|
||
#[serde(default)]
|
||
default: String,
|
||
#[serde(default)]
|
||
serif: String,
|
||
#[serde(default)]
|
||
#[serde(rename = "fonts.sans-serif")]
|
||
sans_serif: String,
|
||
#[serde(default)]
|
||
monospace: String,
|
||
#[serde(default = "default_font_size")]
|
||
#[serde(rename = "fonts.default-size")]
|
||
default_size: i64,
|
||
#[serde(default = "default_monospace_font_size")]
|
||
#[serde(rename = "fonts.default-monospace-size")]
|
||
default_monospace_size: i64,
|
||
},
|
||
/// Allows customizing the different threadpools used by servo
|
||
threadpools: {
|
||
/// Number of workers per threadpool, if we fail to detect how much
|
||
/// parallelism is available at runtime.
|
||
fallback_worker_num: i64,
|
||
image_cache_workers: {
|
||
/// Maximum number of workers for the Image Cache thread pool
|
||
max: i64,
|
||
},
|
||
async_runtime_workers: {
|
||
/// Maximum number of workers for the Networking async runtime thread pool
|
||
max: i64
|
||
},
|
||
resource_workers: {
|
||
/// Maximum number of workers for the Core Resource Manager
|
||
max: i64,
|
||
},
|
||
webrender_workers: {
|
||
/// Maximum number of workers for webrender
|
||
max: i64,
|
||
},
|
||
},
|
||
css: {
|
||
animations: {
|
||
testing: {
|
||
#[serde(default)]
|
||
enabled: bool,
|
||
},
|
||
},
|
||
},
|
||
devtools: {
|
||
server: {
|
||
enabled: bool,
|
||
port: i64,
|
||
},
|
||
},
|
||
dom: {
|
||
webgpu: {
|
||
/// Enable WebGPU APIs.
|
||
enabled: bool,
|
||
/// List of comma-separated backends to be used by wgpu
|
||
wgpu_backend: String,
|
||
},
|
||
bluetooth: {
|
||
enabled: bool,
|
||
testing: {
|
||
enabled: bool,
|
||
}
|
||
},
|
||
abort_controller: {
|
||
/// Whether to expose the AbortControll/AbortSignal DOM interfaces.
|
||
enabled: bool,
|
||
},
|
||
allow_scripts_to_close_windows: bool,
|
||
canvas_capture: {
|
||
enabled: bool,
|
||
},
|
||
canvas_text: {
|
||
enabled: bool,
|
||
},
|
||
composition_event: {
|
||
#[serde(rename = "dom.compositionevent.enabled")]
|
||
enabled: bool,
|
||
},
|
||
crypto: {
|
||
subtle: {
|
||
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,
|
||
},
|
||
imagebitmap: {
|
||
enabled: bool,
|
||
},
|
||
intersection_observer: {
|
||
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,
|
||
}
|
||
},
|
||
resize_observer: {
|
||
enabled: bool,
|
||
},
|
||
script: {
|
||
asynch: bool,
|
||
},
|
||
serviceworker: {
|
||
enabled: bool,
|
||
timeout_seconds: i64,
|
||
},
|
||
servo_helpers: {
|
||
enabled: bool,
|
||
},
|
||
servoparser: {
|
||
async_html_tokenizer: {
|
||
enabled: bool,
|
||
}
|
||
},
|
||
shadowdom: {
|
||
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,
|
||
},
|
||
webgl2: {
|
||
/// Enable WebGL2 APIs.
|
||
enabled: bool,
|
||
},
|
||
webrtc: {
|
||
transceiver: {
|
||
enabled: bool,
|
||
},
|
||
#[serde(default)]
|
||
enabled: bool,
|
||
},
|
||
webvtt: {
|
||
enabled: bool,
|
||
},
|
||
webxr: {
|
||
#[serde(default)]
|
||
enabled: bool,
|
||
#[serde(default)]
|
||
test: bool,
|
||
first_person_observer_view: bool,
|
||
glwindow: {
|
||
#[serde(default)]
|
||
enabled: bool,
|
||
#[serde(rename = "dom.webxr.glwindow.left-right")]
|
||
left_right: bool,
|
||
#[serde(rename = "dom.webxr.glwindow.red-cyan")]
|
||
red_cyan: bool,
|
||
spherical: bool,
|
||
cubemap: bool,
|
||
},
|
||
hands: {
|
||
#[serde(default)]
|
||
enabled: bool,
|
||
},
|
||
layers: {
|
||
enabled: bool,
|
||
},
|
||
openxr: {
|
||
enabled: bool,
|
||
},
|
||
sessionavailable: bool,
|
||
#[serde(rename = "dom.webxr.unsafe-assume-user-intent")]
|
||
unsafe_assume_user_intent: bool,
|
||
},
|
||
worklet: {
|
||
blockingsleep: {
|
||
#[serde(default)]
|
||
enabled: bool,
|
||
},
|
||
#[serde(default)]
|
||
enabled: bool,
|
||
testing: {
|
||
#[serde(default)]
|
||
enabled: bool,
|
||
},
|
||
timeout_ms: i64,
|
||
},
|
||
xpath: {
|
||
enabled: bool,
|
||
}
|
||
},
|
||
gfx: {
|
||
subpixel_text_antialiasing: {
|
||
#[serde(rename = "gfx.subpixel-text-antialiasing.enabled")]
|
||
enabled: bool,
|
||
},
|
||
texture_swizzling: {
|
||
#[serde(rename = "gfx.texture-swizzling.enabled")]
|
||
enabled: bool,
|
||
},
|
||
},
|
||
js: {
|
||
asmjs: {
|
||
enabled: bool,
|
||
},
|
||
asyncstack: {
|
||
enabled: bool,
|
||
},
|
||
baseline_interpreter: {
|
||
enabled: bool,
|
||
},
|
||
/// Whether to disable the jit within SpiderMonkey
|
||
disable_jit: bool,
|
||
baseline_jit: {
|
||
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_zone: {
|
||
enabled: bool,
|
||
},
|
||
zeal: {
|
||
frequency: i64,
|
||
level: i64,
|
||
},
|
||
},
|
||
max: i64,
|
||
},
|
||
native_regex: {
|
||
enabled: bool,
|
||
},
|
||
offthread_compilation: {
|
||
enabled: bool,
|
||
},
|
||
parallel_parsing: {
|
||
enabled: bool,
|
||
},
|
||
shared_memory: {
|
||
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,
|
||
},
|
||
css: {
|
||
transition_behavior: {
|
||
#[serde(rename = "layout.css.transition-behavior.enabled")]
|
||
enabled: bool,
|
||
}
|
||
},
|
||
flexbox: {
|
||
enabled: bool,
|
||
},
|
||
grid: {
|
||
enabled: bool,
|
||
},
|
||
legacy_layout: bool,
|
||
#[serde(default = "default_layout_threads")]
|
||
threads: i64,
|
||
writing_mode: {
|
||
#[serde(rename = "layout.writing-mode.enabled")]
|
||
enabled: bool,
|
||
}
|
||
},
|
||
media: {
|
||
glvideo: {
|
||
/// Enable hardware acceleration for video playback.
|
||
enabled: bool,
|
||
},
|
||
testing: {
|
||
/// Enable a non-standard event handler for verifying behavior of media elements during tests.
|
||
enabled: bool,
|
||
}
|
||
},
|
||
network: {
|
||
enforce_tls: {
|
||
enabled: bool,
|
||
localhost: bool,
|
||
onion: bool,
|
||
},
|
||
http_cache: {
|
||
#[serde(rename = "network.http-cache.disabled")]
|
||
disabled: bool,
|
||
},
|
||
local_directory_listing: {
|
||
enabled: bool,
|
||
},
|
||
mime: {
|
||
sniff: bool,
|
||
},
|
||
tls: {
|
||
/// Ignore `std::io::Error` with `ErrorKind::UnexpectedEof` received when a TLS connection
|
||
/// is closed without a close_notify.
|
||
///
|
||
/// Used for tests because WPT server doesn't properly close the TLS connection.
|
||
// TODO: remove this when WPT server is updated to use a proper TLS implementation.
|
||
ignore_unexpected_eof: bool,
|
||
},
|
||
},
|
||
session_history: {
|
||
#[serde(rename = "session-history.max-length")]
|
||
max_length: i64,
|
||
},
|
||
shell: {
|
||
background_color: {
|
||
/// The background color of shell's viewport. This will be used by OpenGL's `glClearColor`.
|
||
#[serde(rename = "shell.background-color.rgba")]
|
||
rgba: [f64; 4],
|
||
},
|
||
crash_reporter: {
|
||
enabled: bool,
|
||
},
|
||
/// URL string of the homepage.
|
||
homepage: String,
|
||
#[serde(rename = "shell.native-orientation")]
|
||
native_orientation: String,
|
||
native_titlebar: {
|
||
/// Enable native window's titlebar and decorations.
|
||
#[serde(rename = "shell.native-titlebar.enabled")]
|
||
enabled: bool,
|
||
},
|
||
/// URL string of the search engine page (for example <https://google.com> or and <https://duckduckgo.com>.
|
||
searchpage: String,
|
||
},
|
||
webgl: {
|
||
testing: {
|
||
context_creation_error: bool,
|
||
}
|
||
},
|
||
}
|
||
}
|
||
}
|