mirror of
https://github.com/servo/servo.git
synced 2025-09-19 11:20:09 +01:00
Add preference observer API for runtime webxr preference changes (#38649)
Adds a global preference observer that is notified whenever any preference value is updated. This is used to support runtime configuration of WebXR automated testing, which is a prerequisite for running multiple WPT tests in a single browser session. Testing: Ran `./mach test-wpt /webxr --product=servodriver` Fixes: #38647 --------- Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
parent
fc3feceee5
commit
f19b2f6e84
4 changed files with 63 additions and 6 deletions
|
@ -34,6 +34,12 @@ fn servo_preferences_derive(input: synstructure::Structure) -> TokenStream {
|
||||||
set_match_cases.extend(quote!(stringify!(#name) => self.#name = value.try_into().unwrap(),))
|
set_match_cases.extend(quote!(stringify!(#name) => self.#name = value.try_into().unwrap(),))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut comparisons = quote!();
|
||||||
|
for field in named_fields.named.iter() {
|
||||||
|
let name = field.ident.as_ref().unwrap();
|
||||||
|
comparisons.extend(quote!(if self.#name != other.#name { changes.push((stringify!(#name), self.#name.clone().into(),)) }))
|
||||||
|
}
|
||||||
|
|
||||||
let structure_name = &ast.ident;
|
let structure_name = &ast.ident;
|
||||||
quote! {
|
quote! {
|
||||||
impl #structure_name {
|
impl #structure_name {
|
||||||
|
@ -50,6 +56,12 @@ fn servo_preferences_derive(input: synstructure::Structure) -> TokenStream {
|
||||||
_ => { panic!("Unknown preference: {:?}", name); }
|
_ => { panic!("Unknown preference: {:?}", name); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn diff(&self, other: &Self) -> Vec<(&'static str, PrefValue)> {
|
||||||
|
let mut changes = vec![];
|
||||||
|
#comparisons
|
||||||
|
changes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,22 @@ pub use crate::pref_util::PrefValue;
|
||||||
|
|
||||||
static PREFERENCES: RwLock<Preferences> = RwLock::new(Preferences::const_default());
|
static PREFERENCES: RwLock<Preferences> = RwLock::new(Preferences::const_default());
|
||||||
|
|
||||||
|
pub trait Observer: Send + Sync {
|
||||||
|
fn prefs_changed(&self, _changes: Vec<(&'static str, PrefValue)>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
static OBSERVER: RwLock<Option<Box<dyn Observer>>> = RwLock::new(None);
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Get the current set of global preferences for Servo.
|
/// Get the current set of global preferences for Servo.
|
||||||
pub fn get() -> RwLockReadGuard<'static, Preferences> {
|
pub fn get() -> RwLockReadGuard<'static, Preferences> {
|
||||||
PREFERENCES.read().unwrap()
|
PREFERENCES.read().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_observer(observer: Box<dyn Observer>) {
|
||||||
|
*OBSERVER.write().unwrap() = Some(observer);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set(preferences: Preferences) {
|
pub fn set(preferences: Preferences) {
|
||||||
// Map between Stylo preference names and Servo preference names as the This should be
|
// Map between Stylo preference names and Servo preference names as the This should be
|
||||||
// kept in sync with components/script/dom/bindings/codegen/run.py which generates the
|
// kept in sync with components/script/dom/bindings/codegen/run.py which generates the
|
||||||
|
@ -39,7 +49,13 @@ pub fn set(preferences: Preferences) {
|
||||||
preferences.layout_container_queries_enabled,
|
preferences.layout_container_queries_enabled,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let changed = preferences.diff(&PREFERENCES.read().unwrap());
|
||||||
|
|
||||||
*PREFERENCES.write().unwrap() = preferences;
|
*PREFERENCES.write().unwrap() = preferences;
|
||||||
|
|
||||||
|
if let Some(observer) = OBSERVER.read().unwrap().as_deref() {
|
||||||
|
observer.prefs_changed(changed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A convenience macro for accessing a preference value using its static path.
|
/// A convenience macro for accessing a preference value using its static path.
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
|
@ -19,8 +20,15 @@ use webxr_api::{
|
||||||
|
|
||||||
use crate::{SurfmanGL, SurfmanLayerManager};
|
use crate::{SurfmanGL, SurfmanLayerManager};
|
||||||
|
|
||||||
#[derive(Default)]
|
pub struct HeadlessMockDiscovery {
|
||||||
pub struct HeadlessMockDiscovery {}
|
enabled: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeadlessMockDiscovery {
|
||||||
|
pub fn new(enabled: Arc<AtomicBool>) -> Self {
|
||||||
|
Self { enabled }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct HeadlessDiscovery {
|
struct HeadlessDiscovery {
|
||||||
data: Arc<Mutex<HeadlessDeviceData>>,
|
data: Arc<Mutex<HeadlessDeviceData>>,
|
||||||
|
@ -76,6 +84,10 @@ impl MockDiscoveryAPI<SurfmanGL> for HeadlessMockDiscovery {
|
||||||
init: MockDeviceInit,
|
init: MockDeviceInit,
|
||||||
receiver: WebXrReceiver<MockDeviceMsg>,
|
receiver: WebXrReceiver<MockDeviceMsg>,
|
||||||
) -> Result<Box<dyn DiscoveryAPI<SurfmanGL>>, Error> {
|
) -> Result<Box<dyn DiscoveryAPI<SurfmanGL>>, Error> {
|
||||||
|
if !self.enabled.load(Ordering::Relaxed) {
|
||||||
|
return Err(Error::NoMatchingDevice);
|
||||||
|
}
|
||||||
|
|
||||||
let viewer_origin = init.viewer_origin;
|
let viewer_origin = init.viewer_origin;
|
||||||
let floor_transform = init.floor_origin.map(|f| f.inverse());
|
let floor_transform = init.floor_origin.map(|f| f.inverse());
|
||||||
let views = init.views.clone();
|
let views = init.views.clone();
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use servo::config::pref;
|
use servo::config::pref;
|
||||||
use servo::config::prefs::Preferences;
|
use servo::config::prefs::{self, Preferences};
|
||||||
use servo::webxr::WebXrRegistry;
|
use servo::webxr::WebXrRegistry;
|
||||||
use servo::webxr::glwindow::GlWindowDiscovery;
|
use servo::webxr::glwindow::GlWindowDiscovery;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
@ -60,14 +62,29 @@ impl XrDiscoveryWebXrRegistry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct XrPrefObserver(Arc<AtomicBool>);
|
||||||
|
|
||||||
|
impl prefs::Observer for XrPrefObserver {
|
||||||
|
fn prefs_changed(&self, changes: Vec<(&'static str, prefs::PrefValue)>) {
|
||||||
|
if let Some((_, value)) = changes.iter().find(|(name, _)| *name == "dom_webxr_test") {
|
||||||
|
let prefs::PrefValue::Bool(value) = value else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.0.store(*value, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "webxr")]
|
#[cfg(feature = "webxr")]
|
||||||
impl WebXrRegistry for XrDiscoveryWebXrRegistry {
|
impl WebXrRegistry for XrDiscoveryWebXrRegistry {
|
||||||
fn register(&self, xr: &mut servo::webxr::MainThreadRegistry) {
|
fn register(&self, xr: &mut servo::webxr::MainThreadRegistry) {
|
||||||
use servo::webxr::headless::HeadlessMockDiscovery;
|
use servo::webxr::headless::HeadlessMockDiscovery;
|
||||||
|
|
||||||
if pref!(dom_webxr_test) {
|
let mock_enabled = Arc::new(AtomicBool::new(pref!(dom_webxr_test)));
|
||||||
xr.register_mock(HeadlessMockDiscovery::default());
|
xr.register_mock(HeadlessMockDiscovery::new(mock_enabled.clone()));
|
||||||
} else if let Some(xr_discovery) = self.xr_discovery.take() {
|
prefs::set_observer(Box::new(XrPrefObserver(mock_enabled)));
|
||||||
|
|
||||||
|
if let Some(xr_discovery) = self.xr_discovery.take() {
|
||||||
match xr_discovery {
|
match xr_discovery {
|
||||||
XrDiscovery::GlWindow(discovery) => xr.register(discovery),
|
XrDiscovery::GlWindow(discovery) => xr.register(discovery),
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue