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