diff --git a/style/custom_properties.rs b/style/custom_properties.rs index 8ac9e51dd5..810670d7b3 100644 --- a/style/custom_properties.rs +++ b/style/custom_properties.rs @@ -227,6 +227,18 @@ impl CssEnvironment { return None; } + #[cfg(feature = "servo")] + if *name == Atom::from("preferred-text-scale") { + return Some(VariableValue::from_token( + Token::Number { + has_sign: false, + value: device.preferred_text_scale(), + int_value: None, + }, + url_data, + )); + } + if let Some(var) = ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name) { return Some((var.evaluator)(device, url_data)); } diff --git a/style/device/servo.rs b/style/device/servo.rs index 10dba0298d..0c734d5d80 100644 --- a/style/device/servo.rs +++ b/style/device/servo.rs @@ -84,6 +84,8 @@ impl Default for ServoMediaFeaturePreferences { pub(super) struct ExtraDeviceData { /// The current media type used by de device. media_type: MediaType, + /// User preferred text scale exposed through CSS env(). + preferred_text_scale: f32, /// The current viewport size, in CSS pixels. viewport_size: Size2D, /// The current screen size, in device pixels. @@ -148,6 +150,7 @@ impl Device { body_text_color: AtomicU32::new(AbsoluteColor::BLACK.to_nscolor()), extra: ExtraDeviceData { media_type, + preferred_text_scale: 1.0, viewport_size, device_size, device_pixel_ratio, @@ -162,6 +165,21 @@ impl Device { } } + /// User preferred text scale exposed by `env(preferred-text-scale)`. + pub fn preferred_text_scale(&self) -> f32 { + self.extra.preferred_text_scale + } + + /// Set the CSS environment text scale. The embedder must invalidate styles + /// that depend on environment variables when this value changes. + pub fn set_preferred_text_scale(&mut self, scale: f32) { + assert!( + scale.is_finite() && scale > 0.0, + "preferred text scale must be finite and positive" + ); + self.extra.preferred_text_scale = scale; + } + /// Returns the computed line-height for the font in a given computed values instance. /// /// If you pass down an element, then the used line-height is returned. @@ -571,6 +589,70 @@ fn used_color_scheme_is_dark( mod tests { use super::*; + #[derive(Debug)] + struct TestFontMetrics; + + impl FontMetricsProvider for TestFontMetrics { + fn query_font_metrics( + &self, + _vertical: bool, + _font: &Font, + _base_size: CSSPixelLength, + _flags: QueryFontMetricsFlags, + ) -> FontMetrics { + FontMetrics::default() + } + + fn base_size_for_generic(&self, _generic: GenericFontFamily) -> Length { + Length::new(16.0) + } + } + + #[test] + fn preferred_text_scale_environment_tracks_device_and_rejects_invalid_values() { + let mut device = Device::new( + MediaType::screen(), + QuirksMode::NoQuirks, + euclid::size2(100.0, 100.0), + euclid::size2(100.0, 100.0), + Scale::new(1.0), + Box::new(TestFontMetrics), + ComputedValues::initial_values_with_font_override(Font::initial_values()), + PrefersColorScheme::Light, + PointerCapabilities::default(), + PointerCapabilities::default(), + ); + let url_data = crate::stylesheets::UrlExtraData::from( + url::Url::parse("https://example.test/").unwrap(), + ); + let text_scale = crate::Atom::from("preferred-text-scale"); + let value = |device: &Device| { + device + .environment() + .get(&text_scale, device, &url_data) + .unwrap() + .css + .parse::() + .unwrap() + }; + assert_eq!(value(&device), 1.0); + device.set_preferred_text_scale(1.5); + assert_eq!(value(&device), 1.5); + device.set_preferred_text_scale(2.0); + assert_eq!(value(&device), 2.0); + assert!(device + .environment() + .get(&crate::Atom::from("unknown"), &device, &url_data) + .is_none()); + for invalid in [0.0, -1.0, f32::NAN, f32::INFINITY, f32::NEG_INFINITY] { + assert!(std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + device.set_preferred_text_scale(invalid); + })) + .is_err()); + assert_eq!(value(&device), 2.0); + } + } + #[test] fn used_color_scheme_honors_supported_schemes_and_user_preference() { assert!(!used_color_scheme_is_dark(