Auto merge of #22923 - peterjoel:issue_8539_prefs_refactor, r=jdm

#8539 Config preferences backend restructure

<!-- Please describe your changes on the following line: -->

A procedural macro for generating static structures for use as the backend for config preferences, as well a mapping from string names to accessors.

Preferences can be accessed and updated via a map-like interface with `String` keys, and now also via a convenience macro: `get_pref!(path.to.pref)`. Various `serde`-compatible field attributes are also supported, including renaming the string keys. This could be useful when changing the backend structure without breaking existing usages.

I have added the choice to use `i64` as well as `f64` for storing numbers. As it turns out, none of the existing preferences used non-integer values. Setting a floating point value from a command-line argument requires a decimal point in order to parse correctly.

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #8539

<!-- Either: -->
- [X] There are tests for these changes OR
- [ ] These changes do not require tests because ___

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

-----

I have a few outstanding problems or questions:

1. I am unable to get rid of this warning:
    ```
    warning: unnecessary path disambiguator
       --> components/config/prefs.rs:130:51
        |
    130 |         accessor_type = crate::pref_util::Accessor::<Prefs, crate::pref_util::PrefValue>,
        |                                                   ^^ try removing `::`
    ```
    See: https://stackoverflow.com/questions/54710797/how-to-disable-unnecessary-path-disambiguator-warning
2. Several of the preferences in use were not represented in `prefs.json`. Some of these may be in error, but it is hard to tell. For example `js.offthread_compilation.enabled` vs `js.ion.offthread_compilation.enabled` could be different preferences or could be intended to have the same value.
3. Previously, several pieces of code provided default values when accessing a preference that may not be present. For example:
    ```Rust
    let DBL_CLICK_TIMEOUT = Duration::from_millis(
        PREFS
            .get("dom.document.dblclick_timeout")
            .as_u64()
            .unwrap_or(300),
    );
    ```
    Fallback values don't really make sense now and I've added these defaults to `prefs.json`. Does this sound right?
4. I have kept `PrefValue::Missing`, though it doesn't seem very useful any more. An error might be more appropriate now for an incorrect preference key. I've kept this partly because [`webdriver_server` uses it](https://github.com/servo/servo/blob/master/components/webdriver_server/lib.rs#L224).

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/22923)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-03-20 14:33:37 -04:00 committed by GitHub
commit 5ec725488f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 1748 additions and 680 deletions

View file

@ -2586,7 +2586,7 @@ class CGConstructorEnabled(CGAbstractMethod):
pref = iface.getExtendedAttribute("Pref")
if pref:
assert isinstance(pref, list) and len(pref) == 1
conditions.append('PREFS.get("%s").as_boolean().unwrap_or(false)' % pref[0])
conditions.append('prefs::pref_map().get("%s").as_bool().unwrap_or(false)' % pref[0])
func = iface.getExtendedAttribute("Func")
if func:
@ -5977,7 +5977,8 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'crate::dom::globalscope::GlobalScope',
'crate::mem::malloc_size_of_including_raw_self',
'libc',
'servo_config::prefs::PREFS',
'servo_config::pref',
'servo_config::prefs',
'std::borrow::ToOwned',
'std::cmp',
'std::mem',

View file

@ -6,7 +6,7 @@
use js::jsapi::JSContext;
use js::rust::HandleObject;
use servo_config::prefs::PREFS;
use servo_config::prefs;
/// A container with a condition.
pub struct Guard<T: Clone + Copy> {
@ -48,7 +48,7 @@ pub enum Condition {
impl Condition {
unsafe fn is_satisfied(&self, cx: *mut JSContext, obj: HandleObject) -> bool {
match *self {
Condition::Pref(name) => PREFS.get(name).as_boolean().unwrap_or(false),
Condition::Pref(name) => prefs::pref_map().get(name).as_bool().unwrap_or(false),
Condition::Func(f) => f(cx, obj),
Condition::Satisfied => true,
}

View file

@ -81,7 +81,7 @@ use crate::dom::svgsvgelement::SVGSVGElement;
use crate::script_thread::ScriptThread;
use html5ever::{LocalName, Prefix, QualName};
use js::jsapi::JSAutoCompartment;
use servo_config::prefs::PREFS;
use servo_config::pref;
fn create_svg_element(
name: QualName,
@ -101,7 +101,7 @@ fn create_svg_element(
})
);
if !PREFS.get("dom.svg.enabled").as_boolean().unwrap_or(false) {
if !pref!(dom.svg.enabled) {
return Element::new(name.local, name.ns, prefix, document);
}

View file

@ -137,7 +137,7 @@ use script_traits::{AnimationState, DocumentActivity, MouseButton, MouseEventTyp
use script_traits::{MsDuration, ScriptMsg, TouchEventType, TouchId, UntrustedNodeAddress};
use servo_arc::Arc;
use servo_atoms::Atom;
use servo_config::prefs::PREFS;
use servo_config::pref;
use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
use std::borrow::ToOwned;
use std::cell::{Cell, Ref, RefMut};
@ -1090,16 +1090,9 @@ impl Document {
let opt = self.last_click_info.borrow_mut().take();
if let Some((last_time, last_pos)) = opt {
let DBL_CLICK_TIMEOUT = Duration::from_millis(
PREFS
.get("dom.document.dblclick_timeout")
.as_u64()
.unwrap_or(300),
);
let DBL_CLICK_DIST_THRESHOLD = PREFS
.get("dom.document.dblclick_dist")
.as_u64()
.unwrap_or(1);
let DBL_CLICK_TIMEOUT =
Duration::from_millis(pref!(dom.document.dblclick_timeout) as u64);
let DBL_CLICK_DIST_THRESHOLD = pref!(dom.document.dblclick_dist) as u64;
// Calculate distance between this click and the previous click.
let line = click_pos - last_pos;
@ -2423,11 +2416,7 @@ impl Document {
local_name: &LocalName,
is: Option<&LocalName>,
) -> Option<Rc<CustomElementDefinition>> {
if !PREFS
.get("dom.customelements.enabled")
.as_boolean()
.unwrap_or(false)
{
if !pref!(dom.custom_elements.enabled) {
return None;
}
@ -3165,11 +3154,7 @@ impl Document {
error = true;
}
if PREFS
.get("dom.fullscreen.test")
.as_boolean()
.unwrap_or(false)
{
if pref!(dom.fullscreen.test) {
// For reftests we just take over the current window,
// and don't try to really enter fullscreen.
info!("Tests don't really enter fullscreen.");

View file

@ -42,7 +42,7 @@ use js::jsapi::JSContext;
use js::rust::HandleValue;
use profile_traits::ipc;
use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource};
use servo_config::prefs::PREFS;
use servo_config::pref;
use std::cell::Ref;
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
@ -232,7 +232,7 @@ impl HTMLCanvasElement {
cx: *mut JSContext,
options: HandleValue,
) -> Option<DomRoot<WebGL2RenderingContext>> {
if !PREFS.is_webgl2_enabled() {
if !pref!(dom.webgl2.enabled) {
return None;
}
if let Some(ctx) = self.context() {

View file

@ -64,7 +64,7 @@ use net_traits::request::{CredentialsMode, Destination, RequestInit};
use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, Metadata};
use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType};
use script_layout_interface::HTMLMediaData;
use servo_config::prefs::PREFS;
use servo_config::pref;
use servo_media::player::frame::{Frame, FrameRenderer};
use servo_media::player::{PlaybackState, Player, PlayerError, PlayerEvent, StreamType};
use servo_media::ServoMedia;
@ -1121,13 +1121,12 @@ impl HTMLMediaElement {
.unwrap()
.render_poster_frame(image);
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
if let Some(testing_on) = PREFS.get("media.testing.enabled").as_boolean() {
if !testing_on {
return;
}
if pref!(media.testing.enabled) {
let window = window_from_node(self);
let task_source = window.task_manager().media_element_task_source();
task_source.queue_simple_event(self.upcast(), atom!("postershown"), &window);
} else {
return;
}
}
}

View file

@ -21,7 +21,7 @@ use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
use parking_lot::RwLock;
use servo_arc::Arc;
use servo_config::prefs::PREFS;
use servo_config::pref;
use std::sync::atomic::AtomicBool;
use style::attr::AttrValue;
use style::media_queries::MediaList;
@ -98,11 +98,7 @@ impl HTMLMetaElement {
}
fn apply_viewport(&self) {
if !PREFS
.get("layout.viewport.enabled")
.as_boolean()
.unwrap_or(false)
{
if !pref!(layout.viewport.enabled) {
return;
}
let element = self.upcast::<Element>();

View file

@ -16,7 +16,7 @@ use crate::dom::uievent::UIEvent;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use euclid::Point2D;
use servo_config::prefs::PREFS;
use servo_config::pref;
use std::cell::Cell;
use std::default::Default;
@ -194,11 +194,7 @@ impl MouseEventMethods for MouseEvent {
// This returns the same result as current gecko.
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which
fn Which(&self) -> i32 {
if PREFS
.get("dom.mouseevent.which.enabled")
.as_boolean()
.unwrap_or(false)
{
if pref!(dom.mouse_event.which.enabled) {
(self.button.get() + 1) as i32
} else {
0

View file

@ -50,7 +50,7 @@ use profile_traits::ipc;
use script_traits::Painter;
use script_traits::{DrawAPaintImageResult, PaintWorkletError};
use servo_atoms::Atom;
use servo_config::prefs::PREFS;
use servo_config::pref;
use servo_url::ServoUrl;
use std::cell::Cell;
use std::collections::hash_map::Entry;
@ -439,10 +439,7 @@ impl PaintWorkletGlobalScope {
.expect("Locking a painter.")
.schedule_a_worklet_task(WorkletTask::Paint(task));
let timeout = PREFS
.get("dom.worklet.timeout_ms")
.as_u64()
.unwrap_or(10u64);
let timeout = pref!(dom.worklet.timeout_ms) as u64;
receiver
.recv_timeout(Duration::from_millis(timeout))

View file

@ -22,7 +22,7 @@ use js::jsapi::{JSContext, JSObject};
use js::jsval::{ObjectValue, UndefinedValue};
#[cfg(target_os = "linux")]
use servo_config::opts;
use servo_config::prefs::PREFS;
use servo_config::pref;
use std::rc::Rc;
#[cfg(target_os = "linux")]
use tinyfiledialogs::{self, MessageBoxIcon, YesNo};
@ -307,11 +307,7 @@ pub fn get_descriptor_permission_state(
let state = if allowed_in_nonsecure_contexts(&permission_name) {
PermissionState::Prompt
} else {
if PREFS
.get("dom.permissions.testing.allowed_in_nonsecure_contexts")
.as_boolean()
.unwrap_or(false)
{
if pref!(dom.permissions.testing.allowed_in_nonsecure_contexts) {
PermissionState::Granted
} else {
settings

View file

@ -35,7 +35,7 @@ use net_traits::{load_whole_resource, CustomResponseMediator, IpcSend};
use script_traits::{
ScopeThings, ServiceWorkerMsg, TimerEvent, WorkerGlobalScopeInit, WorkerScriptLoadOrigin,
};
use servo_config::prefs::PREFS;
use servo_config::pref;
use servo_rand::random;
use servo_url::ServoUrl;
use std::thread;
@ -336,10 +336,7 @@ impl ServiceWorkerGlobalScope {
thread::Builder::new()
.name("SWTimeoutThread".to_owned())
.spawn(move || {
let sw_lifetime_timeout = PREFS
.get("dom.serviceworker.timeout_seconds")
.as_u64()
.unwrap();
let sw_lifetime_timeout = pref!(dom.serviceworker.timeout_seconds) as u64;
thread::sleep(Duration::new(sw_lifetime_timeout, 0));
let _ = timer_chan.send(());
})

View file

@ -52,7 +52,7 @@ use profile_traits::time::{
profile, ProfilerCategory, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType,
};
use script_traits::DocumentActivity;
use servo_config::prefs::PREFS;
use servo_config::pref;
use servo_url::ServoUrl;
use std::borrow::Cow;
use std::cell::Cell;
@ -135,11 +135,7 @@ impl ServoParser {
}
pub fn parse_html_document(document: &Document, input: DOMString, url: ServoUrl) {
let parser = if PREFS
.get("dom.servoparser.async_html_tokenizer.enabled")
.as_boolean()
.unwrap()
{
let parser = if pref!(dom.servoparser.async_html_tokenizer.enabled) {
ServoParser::new(
document,
Tokenizer::AsyncHtml(self::async_html::Tokenizer::new(document, url, None)),

View file

@ -57,7 +57,7 @@ use js::rust::CustomAutoRooterGuard;
use js::rust::{HandleObject, HandleValue};
use js::typedarray;
use script_traits::MsDuration;
use servo_config::prefs::PREFS;
use servo_config::prefs;
use std::borrow::ToOwned;
use std::ptr;
use std::ptr::NonNull;
@ -887,12 +887,15 @@ impl TestBindingMethods for TestBinding {
#[allow(unsafe_code)]
unsafe fn PassVariadicObject(&self, _: *mut JSContext, _: Vec<*mut JSObject>) {}
fn BooleanMozPreference(&self, pref_name: DOMString) -> bool {
PREFS.get(pref_name.as_ref()).as_boolean().unwrap_or(false)
prefs::pref_map()
.get(pref_name.as_ref())
.as_bool()
.unwrap_or(false)
}
fn StringMozPreference(&self, pref_name: DOMString) -> DOMString {
PREFS
prefs::pref_map()
.get(pref_name.as_ref())
.as_string()
.as_str()
.map(|s| DOMString::from(s))
.unwrap_or_else(|| DOMString::new())
}

View file

@ -71,7 +71,7 @@ use net_traits::image_cache::ImageResponse;
use pixels::{self, PixelFormat};
use script_layout_interface::HTMLCanvasDataSource;
use serde::{Deserialize, Serialize};
use servo_config::prefs::PREFS;
use servo_config::pref;
use std::cell::Cell;
use std::cmp;
use std::ptr::{self, NonNull};
@ -173,10 +173,7 @@ impl WebGLRenderingContext {
size: Size2D<u32>,
attrs: GLContextAttributes,
) -> Result<WebGLRenderingContext, String> {
if let Some(true) = PREFS
.get("webgl.testing.context_creation_error")
.as_boolean()
{
if pref!(webgl.testing.context_creation_error) {
return Err("WebGL context creation error forced by pref `webgl.testing.context_creation_error`".into());
}