style/device/
mod.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Media-query device and expression representation.
6
7use crate::custom_properties::CssEnvironment;
8#[cfg(feature = "servo")]
9use crate::derives::*;
10use crate::properties::ComputedValues;
11use crate::values::computed::font::QueryFontMetricsFlags;
12use crate::values::computed::Length;
13use parking_lot::RwLock;
14use servo_arc::Arc;
15use std::mem;
16use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
17
18#[cfg(feature = "gecko")]
19use crate::device::gecko::ExtraDeviceData;
20#[cfg(feature = "servo")]
21use crate::device::servo::ExtraDeviceData;
22
23#[cfg(feature = "gecko")]
24pub mod gecko;
25#[cfg(feature = "servo")]
26pub mod servo;
27
28/// A device is a structure that represents the current media a given document
29/// is displayed in.
30///
31/// This is the struct against which media queries are evaluated, has a default
32/// values computed, and contains all the viewport rule state.
33///
34/// This structure also contains atomics used for computing root font-relative
35/// units. These atomics use relaxed ordering, since when computing the style
36/// of the root element, there can't be any other style being computed at the
37/// same time (given we need the style of the parent to compute everything else).
38///
39/// In Gecko, it wraps a pres context.
40#[cfg_attr(feature = "servo", derive(Debug, MallocSizeOf))]
41pub struct Device {
42    /// The default computed values for this Device.
43    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc is shared")]
44    default_values: Arc<ComputedValues>,
45    /// Current computed style of the root element, used for calculations of
46    /// root font-relative units.
47    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
48    root_style: RwLock<Arc<ComputedValues>>,
49    /// Font size of the root element, used for rem units in other elements.
50    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")]
51    root_font_size: AtomicU32,
52    /// Line height of the root element, used for rlh units in other elements.
53    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")]
54    root_line_height: AtomicU32,
55    /// X-height of the root element, used for rex units in other elements.
56    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")]
57    root_font_metrics_ex: AtomicU32,
58    /// Cap-height of the root element, used for rcap units in other elements.
59    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")]
60    root_font_metrics_cap: AtomicU32,
61    /// Advance measure (ch) of the root element, used for rch units in other elements.
62    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")]
63    root_font_metrics_ch: AtomicU32,
64    /// Ideographic advance measure of the root element, used for ric units in other elements.
65    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")]
66    root_font_metrics_ic: AtomicU32,
67    /// Whether any styles computed in the document relied on the root font-size
68    /// by using rem units.
69    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")]
70    used_root_font_size: AtomicBool,
71    /// Whether any styles computed in the document relied on the root line-height
72    /// by using rlh units.
73    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")]
74    used_root_line_height: AtomicBool,
75    /// Whether any styles computed in the document relied on the root font metrics
76    /// by using rcap, rch, rex, or ric units. This is a lock instead of an atomic
77    /// in order to prevent concurrent writes to the root font metric values.
78    #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")]
79    used_root_font_metrics: RwLock<bool>,
80    /// Whether any styles computed in the document relied on font metrics.
81    used_font_metrics: AtomicBool,
82    /// Whether any styles computed in the document relied on the viewport size
83    /// by using vw/vh/vmin/vmax units.
84    used_viewport_size: AtomicBool,
85    /// Whether any styles computed in the document relied on the viewport size
86    /// by using dvw/dvh/dvmin/dvmax units.
87    used_dynamic_viewport_size: AtomicBool,
88    /// The CssEnvironment object responsible of getting CSS environment
89    /// variables.
90    environment: CssEnvironment,
91
92    /// Extra Gecko-specific or Servo-specific data.
93    extra: ExtraDeviceData,
94}
95
96impl Device {
97    /// Get the relevant environment to resolve `env()` functions.
98    #[inline]
99    pub fn environment(&self) -> &CssEnvironment {
100        &self.environment
101    }
102
103    /// Returns the default computed values as a reference, in order to match
104    /// Servo.
105    pub fn default_computed_values(&self) -> &ComputedValues {
106        &self.default_values
107    }
108
109    /// Returns the default computed values as an `Arc`.
110    pub fn default_computed_values_arc(&self) -> &Arc<ComputedValues> {
111        &self.default_values
112    }
113
114    /// Store a pointer to the root element's computed style, for use in
115    /// calculation of root font-relative metrics.
116    pub fn set_root_style(&self, style: &Arc<ComputedValues>) {
117        *self.root_style.write() = style.clone();
118    }
119
120    /// Get the font size of the root element (for rem)
121    pub fn root_font_size(&self) -> Length {
122        self.used_root_font_size.store(true, Ordering::Relaxed);
123        Length::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed)))
124    }
125
126    /// Set the font size of the root element (for rem), in zoom-independent CSS pixels.
127    pub fn set_root_font_size(&self, size: f32) {
128        self.root_font_size.store(size.to_bits(), Ordering::Relaxed)
129    }
130
131    /// Get the line height of the root element (for rlh)
132    pub fn root_line_height(&self) -> Length {
133        self.used_root_line_height.store(true, Ordering::Relaxed);
134        Length::new(f32::from_bits(
135            self.root_line_height.load(Ordering::Relaxed),
136        ))
137    }
138
139    /// Set the line height of the root element (for rlh), in zoom-independent CSS pixels.
140    pub fn set_root_line_height(&self, size: f32) {
141        self.root_line_height
142            .store(size.to_bits(), Ordering::Relaxed);
143    }
144
145    /// Get the x-height of the root element (for rex)
146    pub fn root_font_metrics_ex(&self) -> Length {
147        self.ensure_root_font_metrics_updated();
148        Length::new(f32::from_bits(
149            self.root_font_metrics_ex.load(Ordering::Relaxed),
150        ))
151    }
152
153    /// Set the x-height of the root element (for rex), in zoom-independent CSS pixels.
154    pub fn set_root_font_metrics_ex(&self, size: f32) -> bool {
155        let size = size.to_bits();
156        let previous = self.root_font_metrics_ex.swap(size, Ordering::Relaxed);
157        previous != size
158    }
159
160    /// Get the cap-height of the root element (for rcap)
161    pub fn root_font_metrics_cap(&self) -> Length {
162        self.ensure_root_font_metrics_updated();
163        Length::new(f32::from_bits(
164            self.root_font_metrics_cap.load(Ordering::Relaxed),
165        ))
166    }
167
168    /// Set the cap-height of the root element (for rcap), in zoom-independent CSS pixels.
169    pub fn set_root_font_metrics_cap(&self, size: f32) -> bool {
170        let size = size.to_bits();
171        let previous = self.root_font_metrics_cap.swap(size, Ordering::Relaxed);
172        previous != size
173    }
174
175    /// Get the advance measure of the root element (for rch)
176    pub fn root_font_metrics_ch(&self) -> Length {
177        self.ensure_root_font_metrics_updated();
178        Length::new(f32::from_bits(
179            self.root_font_metrics_ch.load(Ordering::Relaxed),
180        ))
181    }
182
183    /// Set the advance measure of the root element (for rch), in zoom-independent CSS pixels.
184    pub fn set_root_font_metrics_ch(&self, size: f32) -> bool {
185        let size = size.to_bits();
186        let previous = self.root_font_metrics_ch.swap(size, Ordering::Relaxed);
187        previous != size
188    }
189
190    /// Get the ideographic advance measure of the root element (for ric)
191    pub fn root_font_metrics_ic(&self) -> Length {
192        self.ensure_root_font_metrics_updated();
193        Length::new(f32::from_bits(
194            self.root_font_metrics_ic.load(Ordering::Relaxed),
195        ))
196    }
197
198    /// Set the ideographic advance measure of the root element (for ric), in zoom-independent CSS pixels.
199    pub fn set_root_font_metrics_ic(&self, size: f32) -> bool {
200        let size = size.to_bits();
201        let previous = self.root_font_metrics_ic.swap(size, Ordering::Relaxed);
202        previous != size
203    }
204
205    fn ensure_root_font_metrics_updated(&self) {
206        let mut guard = self.used_root_font_metrics.write();
207        let previously_computed = mem::replace(&mut *guard, true);
208        if !previously_computed {
209            self.update_root_font_metrics();
210        }
211    }
212
213    /// Compute the root element's font metrics, and returns a bool indicating whether
214    /// the font metrics have changed since the previous restyle.
215    pub fn update_root_font_metrics(&self) -> bool {
216        let root_style = self.root_style.read();
217        let root_effective_zoom = (*root_style).effective_zoom;
218        let root_font_size = (*root_style).get_font().clone_font_size().computed_size();
219
220        let root_font_metrics = self.query_font_metrics(
221            (*root_style).writing_mode.is_upright(),
222            &(*root_style).get_font(),
223            root_font_size,
224            QueryFontMetricsFlags::USE_USER_FONT_SET
225                | QueryFontMetricsFlags::NEEDS_CH
226                | QueryFontMetricsFlags::NEEDS_IC,
227            /* track_usage = */ false,
228        );
229
230        let mut root_font_metrics_changed = false;
231        root_font_metrics_changed |= self.set_root_font_metrics_ex(
232            root_effective_zoom.unzoom(root_font_metrics.x_height_or_default(root_font_size).px()),
233        );
234        root_font_metrics_changed |= self.set_root_font_metrics_ch(
235            root_effective_zoom.unzoom(
236                root_font_metrics
237                    .zero_advance_measure_or_default(
238                        root_font_size,
239                        (*root_style).writing_mode.is_upright(),
240                    )
241                    .px(),
242            ),
243        );
244        root_font_metrics_changed |= self.set_root_font_metrics_cap(
245            root_effective_zoom.unzoom(root_font_metrics.cap_height_or_default().px()),
246        );
247        root_font_metrics_changed |= self.set_root_font_metrics_ic(
248            root_effective_zoom.unzoom(root_font_metrics.ic_width_or_default(root_font_size).px()),
249        );
250
251        root_font_metrics_changed
252    }
253
254    /// Returns whether we ever looked up the root font size of the Device.
255    pub fn used_root_font_size(&self) -> bool {
256        self.used_root_font_size.load(Ordering::Relaxed)
257    }
258
259    /// Returns whether we ever looked up the root line-height of the device.
260    pub fn used_root_line_height(&self) -> bool {
261        self.used_root_line_height.load(Ordering::Relaxed)
262    }
263
264    /// Returns whether we ever looked up the root font metrics of the device.
265    pub fn used_root_font_metrics(&self) -> bool {
266        *self.used_root_font_metrics.read()
267    }
268
269    /// Returns whether we ever looked up the viewport size of the Device.
270    pub fn used_viewport_size(&self) -> bool {
271        self.used_viewport_size.load(Ordering::Relaxed)
272    }
273
274    /// Returns whether we ever looked up the dynamic viewport size of the Device.
275    pub fn used_dynamic_viewport_size(&self) -> bool {
276        self.used_dynamic_viewport_size.load(Ordering::Relaxed)
277    }
278
279    /// Returns whether font metrics have been queried.
280    pub fn used_font_metrics(&self) -> bool {
281        self.used_font_metrics.load(Ordering::Relaxed)
282    }
283}