/* 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/. */ //! Media-query device and expression representation. use crate::color::AbsoluteColor; use crate::custom_properties::CssEnvironment; #[cfg(feature = "servo")] use crate::derives::*; use crate::properties::ComputedValues; use crate::values::computed::font::QueryFontMetricsFlags; use crate::values::computed::Length; use parking_lot::RwLock; use servo_arc::Arc; use std::mem; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; #[cfg(feature = "gecko")] use crate::device::gecko::ExtraDeviceData; #[cfg(feature = "servo")] use crate::device::servo::ExtraDeviceData; #[cfg(feature = "gecko")] pub mod gecko; #[cfg(feature = "servo")] pub mod servo; /// 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, has a default /// values computed, and contains all the viewport rule state. /// /// This structure also contains atomics used for computing root font-relative /// units. These atomics use relaxed ordering, since 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). /// /// In Gecko, it wraps a pres context. #[cfg_attr(feature = "servo", derive(Debug, MallocSizeOf))] pub struct Device { /// The default computed values for this Device. #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc is shared")] default_values: Arc, /// Current computed style of the root element, used for calculations of /// root font-relative units. #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] root_style: RwLock>, /// Font size of the root element, used for rem units in other elements. root_font_size: AtomicU32, /// Line height of the root element, used for rlh units in other elements. root_line_height: AtomicU32, /// X-height of the root element, used for rex units in other elements. root_font_metrics_ex: AtomicU32, /// Cap-height of the root element, used for rcap units in other elements. root_font_metrics_cap: AtomicU32, /// Advance measure (ch) of the root element, used for rch units in other elements. root_font_metrics_ch: AtomicU32, /// Ideographic advance measure of the root element, used for ric units in other elements. root_font_metrics_ic: AtomicU32, /// Whether any styles computed in the document relied on the root font-size /// by using rem units. used_root_font_size: AtomicBool, /// Whether any styles computed in the document relied on the root line-height /// by using rlh units. used_root_line_height: AtomicBool, /// Whether any styles computed in the document relied on the root font metrics /// by using rcap, rch, rex, or ric units. This is a lock instead of an atomic /// in order to prevent concurrent writes to the root font metric values. #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")] used_root_font_metrics: RwLock, /// Whether any styles computed in the document relied on font metrics. used_font_metrics: AtomicBool, /// Whether any styles computed in the document relied on the viewport size /// by using vw/vh/vmin/vmax units. used_viewport_size: AtomicBool, /// Whether any styles computed in the document relied on the viewport size /// by using dvw/dvh/dvmin/dvmax units. used_dynamic_viewport_size: AtomicBool, /// The CssEnvironment object responsible of getting CSS environment /// variables. environment: CssEnvironment, /// The body text color, stored as an `nscolor`, used for the "tables /// inherit from body" quirk. /// /// body_text_color: AtomicU32, /// Extra Gecko-specific or Servo-specific data. extra: ExtraDeviceData, } impl Device { /// Get the relevant environment to resolve `env()` functions. #[inline] pub fn environment(&self) -> &CssEnvironment { &self.environment } /// Returns the default computed values as a reference, in order to match /// Servo. pub fn default_computed_values(&self) -> &ComputedValues { &self.default_values } /// Returns the default computed values as an `Arc`. pub fn default_computed_values_arc(&self) -> &Arc { &self.default_values } /// Store a pointer to the root element's computed style, for use in /// calculation of root font-relative metrics. pub fn set_root_style(&self, style: &Arc) { *self.root_style.write() = style.clone(); } /// Get the font size of the root element (for rem) pub fn root_font_size(&self) -> Length { self.used_root_font_size.store(true, Ordering::Relaxed); Length::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed))) } /// Set the font size of the root element (for rem), in zoom-independent CSS pixels. pub fn set_root_font_size(&self, size: f32) { self.root_font_size.store(size.to_bits(), Ordering::Relaxed) } /// Get the line height of the root element (for rlh) pub fn root_line_height(&self) -> Length { self.used_root_line_height.store(true, Ordering::Relaxed); Length::new(f32::from_bits( self.root_line_height.load(Ordering::Relaxed), )) } /// Set the line height of the root element (for rlh), in zoom-independent CSS pixels. pub fn set_root_line_height(&self, size: f32) { self.root_line_height .store(size.to_bits(), Ordering::Relaxed); } /// Get the x-height of the root element (for rex) pub fn root_font_metrics_ex(&self) -> Length { self.ensure_root_font_metrics_updated(); Length::new(f32::from_bits( self.root_font_metrics_ex.load(Ordering::Relaxed), )) } /// Set the x-height of the root element (for rex), in zoom-independent CSS pixels. pub fn set_root_font_metrics_ex(&self, size: f32) -> bool { let size = size.to_bits(); let previous = self.root_font_metrics_ex.swap(size, Ordering::Relaxed); previous != size } /// Get the cap-height of the root element (for rcap) pub fn root_font_metrics_cap(&self) -> Length { self.ensure_root_font_metrics_updated(); Length::new(f32::from_bits( self.root_font_metrics_cap.load(Ordering::Relaxed), )) } /// Set the cap-height of the root element (for rcap), in zoom-independent CSS pixels. pub fn set_root_font_metrics_cap(&self, size: f32) -> bool { let size = size.to_bits(); let previous = self.root_font_metrics_cap.swap(size, Ordering::Relaxed); previous != size } /// Get the advance measure of the root element (for rch) pub fn root_font_metrics_ch(&self) -> Length { self.ensure_root_font_metrics_updated(); Length::new(f32::from_bits( self.root_font_metrics_ch.load(Ordering::Relaxed), )) } /// Set the advance measure of the root element (for rch), in zoom-independent CSS pixels. pub fn set_root_font_metrics_ch(&self, size: f32) -> bool { let size = size.to_bits(); let previous = self.root_font_metrics_ch.swap(size, Ordering::Relaxed); previous != size } /// Get the ideographic advance measure of the root element (for ric) pub fn root_font_metrics_ic(&self) -> Length { self.ensure_root_font_metrics_updated(); Length::new(f32::from_bits( self.root_font_metrics_ic.load(Ordering::Relaxed), )) } /// Set the ideographic advance measure of the root element (for ric), in zoom-independent CSS pixels. pub fn set_root_font_metrics_ic(&self, size: f32) -> bool { let size = size.to_bits(); let previous = self.root_font_metrics_ic.swap(size, Ordering::Relaxed); previous != size } fn ensure_root_font_metrics_updated(&self) { let mut guard = self.used_root_font_metrics.write(); let previously_computed = mem::replace(&mut *guard, true); if !previously_computed { self.update_root_font_metrics(); } } /// Compute the root element's font metrics, and returns a bool indicating whether /// the font metrics have changed since the previous restyle. pub fn update_root_font_metrics(&self) -> bool { let root_style = self.root_style.read(); let root_effective_zoom = (*root_style).effective_zoom; let root_font_size = (*root_style).get_font().clone_font_size().computed_size(); let root_font_metrics = self.query_font_metrics( (*root_style).writing_mode.is_upright(), &(*root_style).get_font(), root_font_size, QueryFontMetricsFlags::USE_USER_FONT_SET | QueryFontMetricsFlags::NEEDS_CH | QueryFontMetricsFlags::NEEDS_IC, /* track_usage = */ false, ); let mut root_font_metrics_changed = false; root_font_metrics_changed |= self.set_root_font_metrics_ex( root_effective_zoom.unzoom(root_font_metrics.x_height_or_default(root_font_size).px()), ); root_font_metrics_changed |= self.set_root_font_metrics_ch( root_effective_zoom.unzoom( root_font_metrics .zero_advance_measure_or_default( root_font_size, (*root_style).writing_mode.is_upright(), ) .px(), ), ); root_font_metrics_changed |= self.set_root_font_metrics_cap( root_effective_zoom.unzoom(root_font_metrics.cap_height_or_default().px()), ); root_font_metrics_changed |= self.set_root_font_metrics_ic( root_effective_zoom.unzoom(root_font_metrics.ic_width_or_default(root_font_size).px()), ); root_font_metrics_changed } /// 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 whether we ever looked up the root line-height of the device. pub fn used_root_line_height(&self) -> bool { self.used_root_line_height.load(Ordering::Relaxed) } /// Returns whether we ever looked up the root font metrics of the device. pub fn used_root_font_metrics(&self) -> bool { *self.used_root_font_metrics.read() } /// Returns whether we ever looked up the viewport size of the Device. pub fn used_viewport_size(&self) -> bool { self.used_viewport_size.load(Ordering::Relaxed) } /// Returns whether we ever looked up the dynamic viewport size of the Device. pub fn used_dynamic_viewport_size(&self) -> bool { self.used_dynamic_viewport_size.load(Ordering::Relaxed) } /// Returns whether font metrics have been queried. pub fn used_font_metrics(&self) -> bool { self.used_font_metrics.load(Ordering::Relaxed) } /// Returns the body text color. pub fn body_text_color(&self) -> AbsoluteColor { AbsoluteColor::from_nscolor(self.body_text_color.load(Ordering::Relaxed)) } /// Sets the body text color for the "inherit color from body" quirk. /// /// pub fn set_body_text_color(&self, color: AbsoluteColor) { self.body_text_color .store(color.to_nscolor(), Ordering::Relaxed) } }