/* 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/. */ //! Servo's media-query device and expression representation. use crate::color::AbsoluteColor; use crate::context::QuirksMode; use crate::custom_properties::CssEnvironment; use crate::font_metrics::FontMetrics; use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; use crate::media_queries::MediaType; use crate::properties::ComputedValues; use crate::values::computed::CSSPixelLength; use crate::values::computed::Context; use crate::values::computed::Resolution; use crate::values::specified::font::FONT_MEDIUM_PX; use crate::values::specified::ViewportVariant; use crate::values::KeyframesName; use app_units::{Au, AU_PER_PX}; use euclid::default::Size2D as UntypedSize2D; use euclid::{Scale, SideOffsets2D, Size2D}; use mime::Mime; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use style_traits::viewport::ViewportConstraints; use style_traits::{CSSPixel, DevicePixel}; /// A device is a structure that represents the current media a given document /// is displayed in. /// /// This is the struct against which media queries are evaluated. #[derive(Debug, MallocSizeOf)] pub struct Device { /// The current media type used by de device. media_type: MediaType, /// The current viewport size, in CSS pixels. viewport_size: Size2D, /// The current device pixel ratio, from CSS pixels to device pixels. device_pixel_ratio: Scale, /// The current quirks mode. #[ignore_malloc_size_of = "Pure stack type"] quirks_mode: QuirksMode, /// The font size of the root element /// This is set when computing the style of the root /// element, and used for rem units in other elements /// /// When computing the style of the root element, there can't be any /// other style being computed at the same time, given we need the style of /// the parent to compute everything else. So it is correct to just use /// a relaxed atomic here. #[ignore_malloc_size_of = "Pure stack type"] root_font_size: AtomicU32, /// Whether any styles computed in the document relied on the root font-size /// by using rem units. #[ignore_malloc_size_of = "Pure stack type"] used_root_font_size: AtomicBool, /// Whether any styles computed in the document relied on the viewport size. #[ignore_malloc_size_of = "Pure stack type"] used_viewport_units: AtomicBool, /// The CssEnvironment object responsible of getting CSS environment /// variables. environment: CssEnvironment, } impl Device { /// Trivially construct a new `Device`. pub fn new( media_type: MediaType, quirks_mode: QuirksMode, viewport_size: Size2D, device_pixel_ratio: Scale, ) -> Device { Device { media_type, viewport_size, device_pixel_ratio, quirks_mode, // FIXME(bz): Seems dubious? root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()), used_root_font_size: AtomicBool::new(false), used_viewport_units: AtomicBool::new(false), environment: CssEnvironment, } } /// Get the relevant environment to resolve `env()` functions. #[inline] pub fn environment(&self) -> &CssEnvironment { &self.environment } /// Return the default computed values for this device. pub fn default_computed_values(&self) -> &ComputedValues { // FIXME(bz): This isn't really right, but it's no more wrong // than what we used to do. See // https://github.com/servo/servo/issues/14773 for fixing it properly. ComputedValues::initial_values() } /// Get the font size of the root element (for rem) pub fn root_font_size(&self) -> CSSPixelLength { self.used_root_font_size.store(true, Ordering::Relaxed); CSSPixelLength::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed))) } /// Set the font size of the root element (for rem) pub fn set_root_font_size(&self, size: CSSPixelLength) { self.root_font_size .store(size.px().to_bits(), Ordering::Relaxed) } /// Get the quirks mode of the current device. pub fn quirks_mode(&self) -> QuirksMode { self.quirks_mode } /// Sets the body text color for the "inherit color from body" quirk. /// /// pub fn set_body_text_color(&self, _color: AbsoluteColor) { // Servo doesn't implement this quirk (yet) } /// Whether a given animation name may be referenced from style. pub fn animation_name_may_be_referenced(&self, _: &KeyframesName) -> bool { // Assume it is, since we don't have any good way to prove it's not. true } /// Returns whether we ever looked up the root font size of the Device. pub fn used_root_font_size(&self) -> bool { self.used_root_font_size.load(Ordering::Relaxed) } /// Returns the viewport size of the current device in app units, needed, /// among other things, to resolve viewport units. #[inline] pub fn au_viewport_size(&self) -> UntypedSize2D { Size2D::new( Au::from_f32_px(self.viewport_size.width), Au::from_f32_px(self.viewport_size.height), ) } /// Like the above, but records that we've used viewport units. pub fn au_viewport_size_for_viewport_unit_resolution( &self, _: ViewportVariant, ) -> UntypedSize2D { self.used_viewport_units.store(true, Ordering::Relaxed); // Servo doesn't have dynamic UA interfaces that affect the viewport, // so we can just ignore the ViewportVariant. self.au_viewport_size() } /// Whether viewport units were used since the last device change. pub fn used_viewport_units(&self) -> bool { self.used_viewport_units.load(Ordering::Relaxed) } /// Returns the number of app units per device pixel we're using currently. pub fn app_units_per_device_pixel(&self) -> i32 { (AU_PER_PX as f32 / self.device_pixel_ratio.0) as i32 } /// Returns the device pixel ratio. pub fn device_pixel_ratio(&self) -> Scale { self.device_pixel_ratio } /// Gets the size of the scrollbar in CSS pixels. pub fn scrollbar_inline_size(&self) -> CSSPixelLength { // TODO: implement this. CSSPixelLength::new(0.0) } /// Queries dummy font metrics for Servo. Knows nothing about fonts and does not provide /// any metrics. /// TODO: Servo's font metrics provider will probably not live in this crate, so this will /// have to be replaced with something else (perhaps a trait method on TElement) /// when we get there pub fn query_font_metrics( &self, _vertical: bool, _font: &crate::properties::style_structs::Font, _base_size: CSSPixelLength, _in_media_query: bool, _retrieve_math_scales: bool, ) -> FontMetrics { Default::default() } /// Take into account a viewport rule taken from the stylesheets. pub fn account_for_viewport_rule(&mut self, constraints: &ViewportConstraints) { self.viewport_size = constraints.size; } /// Return the media type of the current device. pub fn media_type(&self) -> MediaType { self.media_type.clone() } /// Returns whether document colors are enabled. pub fn use_document_colors(&self) -> bool { true } /// Returns the default background color. pub fn default_background_color(&self) -> AbsoluteColor { AbsoluteColor::white() } /// Returns the default foreground color. pub fn default_color(&self) -> AbsoluteColor { AbsoluteColor::black() } /// Returns safe area insets pub fn safe_area_insets(&self) -> SideOffsets2D { SideOffsets2D::zero() } /// Returns true if the given MIME type is supported pub fn is_supported_mime_type(&self, mime_type: &str) -> bool { match mime_type.parse::() { Ok(m) => { // Keep this in sync with 'image_classifer' from // components/net/mime_classifier.rs m == mime::IMAGE_BMP || m == mime::IMAGE_GIF || m == mime::IMAGE_PNG || m == mime::IMAGE_JPEG || m == "image/x-icon" || m == "image/webp" } _ => false, } } /// Return whether the document is a chrome document. #[inline] pub fn chrome_rules_enabled_for_document(&self) -> bool { false } } /// https://drafts.csswg.org/mediaqueries-4/#width fn eval_width(context: &Context) -> CSSPixelLength { CSSPixelLength::new(context.device().au_viewport_size().width.to_f32_px()) } #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] #[repr(u8)] enum Scan { Progressive, Interlace, } /// https://drafts.csswg.org/mediaqueries-4/#scan fn eval_scan(_: &Context, _: Option) -> bool { // Since we doesn't support the 'tv' media type, the 'scan' feature never // matches. false } /// https://drafts.csswg.org/mediaqueries-4/#resolution fn eval_resolution(context: &Context) -> Resolution { Resolution::from_dppx(context.device().device_pixel_ratio.0) } /// https://compat.spec.whatwg.org/#css-media-queries-webkit-device-pixel-ratio fn eval_device_pixel_ratio(context: &Context) -> f32 { eval_resolution(context).dppx() } lazy_static! { /// A list with all the media features that Servo supports. pub static ref MEDIA_FEATURES: [QueryFeatureDescription; 5] = [ feature!( atom!("width"), AllowsRanges::Yes, Evaluator::Length(eval_width), FeatureFlags::empty(), ), feature!( atom!("scan"), AllowsRanges::No, keyword_evaluator!(eval_scan, Scan), FeatureFlags::empty(), ), feature!( atom!("resolution"), AllowsRanges::Yes, Evaluator::Resolution(eval_resolution), FeatureFlags::empty(), ), feature!( atom!("device-pixel-ratio"), AllowsRanges::Yes, Evaluator::Float(eval_device_pixel_ratio), FeatureFlags::WEBKIT_PREFIX, ), feature!( atom!("-moz-device-pixel-ratio"), AllowsRanges::Yes, Evaluator::Float(eval_device_pixel_ratio), FeatureFlags::empty(), ), ]; }