style/device/
servo.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//! Servo-specific logic for [`Device`].
6
7use crate::color::AbsoluteColor;
8use crate::context::QuirksMode;
9use crate::custom_properties::CssEnvironment;
10use crate::font_metrics::FontMetrics;
11use crate::logical_geometry::WritingMode;
12use crate::media_queries::MediaType;
13use crate::properties::style_structs::Font;
14use crate::properties::ComputedValues;
15use crate::queries::values::PrefersColorScheme;
16use crate::values::computed::font::GenericFontFamily;
17use crate::values::computed::{CSSPixelLength, Length, LineHeight, NonNegativeLength};
18use crate::values::specified::color::{ColorSchemeFlags, ForcedColors, SystemColor};
19use crate::values::specified::font::{
20    QueryFontMetricsFlags, FONT_MEDIUM_CAP_PX, FONT_MEDIUM_CH_PX, FONT_MEDIUM_EX_PX,
21    FONT_MEDIUM_IC_PX, FONT_MEDIUM_LINE_HEIGHT_PX, FONT_MEDIUM_PX,
22};
23use crate::values::specified::ViewportVariant;
24use crate::values::KeyframesName;
25use app_units::{Au, AU_PER_PX};
26use euclid::default::Size2D as UntypedSize2D;
27use euclid::{Scale, SideOffsets2D, Size2D};
28use malloc_size_of_derive::MallocSizeOf;
29use mime::Mime;
30use parking_lot::RwLock;
31use servo_arc::Arc;
32use std::fmt::Debug;
33use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
34use style_traits::{CSSPixel, DevicePixel};
35
36use crate::device::Device;
37
38/// A trait used to query font metrics in clients of Stylo. This is used by Device to
39/// query font metrics in a way that is specific to the client using Stylo.
40pub trait FontMetricsProvider: Debug + Sync {
41    /// Query the font metrics for the given font and the given base font size.
42    fn query_font_metrics(
43        &self,
44        vertical: bool,
45        font: &Font,
46        base_size: CSSPixelLength,
47        flags: QueryFontMetricsFlags,
48    ) -> FontMetrics;
49    /// Gets the base size given a generic font family.
50    fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length;
51}
52
53#[derive(Debug, MallocSizeOf)]
54pub(super) struct ExtraDeviceData {
55    /// The current media type used by de device.
56    media_type: MediaType,
57    /// The current viewport size, in CSS pixels.
58    viewport_size: Size2D<f32, CSSPixel>,
59    /// The current device pixel ratio, from CSS pixels to device pixels.
60    device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
61    /// The current quirks mode.
62    #[ignore_malloc_size_of = "Pure stack type"]
63    quirks_mode: QuirksMode,
64    /// Whether the user prefers light mode or dark mode
65    #[ignore_malloc_size_of = "Pure stack type"]
66    prefers_color_scheme: PrefersColorScheme,
67    /// An implementation of a trait which implements support for querying font metrics.
68    #[ignore_malloc_size_of = "Owned by embedder"]
69    font_metrics_provider: Box<dyn FontMetricsProvider>,
70}
71
72impl Device {
73    /// Trivially construct a new `Device`.
74    pub fn new(
75        media_type: MediaType,
76        quirks_mode: QuirksMode,
77        viewport_size: Size2D<f32, CSSPixel>,
78        device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
79        font_metrics_provider: Box<dyn FontMetricsProvider>,
80        default_values: Arc<ComputedValues>,
81        prefers_color_scheme: PrefersColorScheme,
82    ) -> Device {
83        let root_style = RwLock::new(Arc::clone(&default_values));
84        Device {
85            root_style,
86            root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),
87            root_line_height: AtomicU32::new(FONT_MEDIUM_LINE_HEIGHT_PX.to_bits()),
88            root_font_metrics_ex: AtomicU32::new(FONT_MEDIUM_EX_PX.to_bits()),
89            root_font_metrics_cap: AtomicU32::new(FONT_MEDIUM_CAP_PX.to_bits()),
90            root_font_metrics_ch: AtomicU32::new(FONT_MEDIUM_CH_PX.to_bits()),
91            root_font_metrics_ic: AtomicU32::new(FONT_MEDIUM_IC_PX.to_bits()),
92            used_root_font_size: AtomicBool::new(false),
93            used_root_line_height: AtomicBool::new(false),
94            used_root_font_metrics: RwLock::new(false),
95            used_font_metrics: AtomicBool::new(false),
96            used_viewport_size: AtomicBool::new(false),
97            used_dynamic_viewport_size: AtomicBool::new(false),
98            environment: CssEnvironment,
99            default_values,
100            extra: ExtraDeviceData {
101                media_type,
102                viewport_size,
103                device_pixel_ratio,
104                quirks_mode,
105                prefers_color_scheme,
106                font_metrics_provider,
107            },
108        }
109    }
110
111    /// Returns the computed line-height for the font in a given computed values instance.
112    ///
113    /// If you pass down an element, then the used line-height is returned.
114    pub fn calc_line_height(
115        &self,
116        font: &crate::properties::style_structs::Font,
117        _writing_mode: WritingMode,
118        _element: Option<()>,
119    ) -> NonNegativeLength {
120        (match font.line_height {
121            // TODO: compute `normal` from the font metrics
122            LineHeight::Normal => CSSPixelLength::new(0.),
123            LineHeight::Number(number) => font.font_size.computed_size() * number.0,
124            LineHeight::Length(length) => length.0,
125        })
126        .into()
127    }
128
129    /// Get the quirks mode of the current device.
130    pub fn quirks_mode(&self) -> QuirksMode {
131        self.extra.quirks_mode
132    }
133
134    /// Sets the body text color for the "inherit color from body" quirk.
135    ///
136    /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
137    pub fn set_body_text_color(&self, _color: AbsoluteColor) {
138        // Servo doesn't implement this quirk (yet)
139    }
140
141    /// Gets the base size given a generic font family.
142    pub fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length {
143        self.extra
144            .font_metrics_provider
145            .base_size_for_generic(generic)
146    }
147
148    /// Whether a given animation name may be referenced from style.
149    pub fn animation_name_may_be_referenced(&self, _: &KeyframesName) -> bool {
150        // Assume it is, since we don't have any good way to prove it's not.
151        true
152    }
153
154    /// Get the viewport size on this [`Device`].
155    pub fn viewport_size(&self) -> Size2D<f32, CSSPixel> {
156        self.extra.viewport_size
157    }
158
159    /// Set the viewport size on this [`Device`].
160    ///
161    /// Note that this does not update any associated `Stylist`. For this you must call
162    /// `Stylist::media_features_change_changed_style` and
163    /// `Stylist::force_stylesheet_origins_dirty`.
164    pub fn set_viewport_size(&mut self, viewport_size: Size2D<f32, CSSPixel>) {
165        self.extra.viewport_size = viewport_size;
166    }
167
168    /// Returns the viewport size of the current device in app units, needed,
169    /// among other things, to resolve viewport units.
170    #[inline]
171    pub fn au_viewport_size(&self) -> UntypedSize2D<Au> {
172        Size2D::new(
173            Au::from_f32_px(self.extra.viewport_size.width),
174            Au::from_f32_px(self.extra.viewport_size.height),
175        )
176    }
177
178    /// Like the above, but records that we've used viewport units.
179    pub fn au_viewport_size_for_viewport_unit_resolution(
180        &self,
181        _: ViewportVariant,
182    ) -> UntypedSize2D<Au> {
183        self.used_viewport_size.store(true, Ordering::Relaxed);
184        // Servo doesn't have dynamic UA interfaces that affect the viewport,
185        // so we can just ignore the ViewportVariant.
186        self.au_viewport_size()
187    }
188
189    /// Returns the number of app units per device pixel we're using currently.
190    pub fn app_units_per_device_pixel(&self) -> i32 {
191        (AU_PER_PX as f32 / self.extra.device_pixel_ratio.0) as i32
192    }
193
194    /// Returns the device pixel ratio, ignoring the full zoom factor.
195    pub fn device_pixel_ratio_ignoring_full_zoom(&self) -> Scale<f32, CSSPixel, DevicePixel> {
196        self.extra.device_pixel_ratio
197    }
198
199    /// Returns the device pixel ratio.
200    pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
201        self.extra.device_pixel_ratio
202    }
203
204    /// Set a new device pixel ratio on this [`Device`].
205    ///
206    /// Note that this does not update any associated `Stylist`. For this you must call
207    /// `Stylist::media_features_change_changed_style` and
208    /// `Stylist::force_stylesheet_origins_dirty`.
209    pub fn set_device_pixel_ratio(
210        &mut self,
211        device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
212    ) {
213        self.extra.device_pixel_ratio = device_pixel_ratio;
214    }
215
216    /// Gets the size of the scrollbar in CSS pixels.
217    pub fn scrollbar_inline_size(&self) -> CSSPixelLength {
218        // TODO: implement this.
219        CSSPixelLength::new(0.0)
220    }
221
222    /// Queries font metrics using the [`FontMetricsProvider`] interface.
223    pub fn query_font_metrics(
224        &self,
225        vertical: bool,
226        font: &Font,
227        base_size: CSSPixelLength,
228        flags: QueryFontMetricsFlags,
229        track_usage: bool,
230    ) -> FontMetrics {
231        if track_usage {
232            self.used_font_metrics.store(true, Ordering::Relaxed);
233        }
234        self.extra
235            .font_metrics_provider
236            .query_font_metrics(vertical, font, base_size, flags)
237    }
238
239    /// Return the media type of the current device.
240    pub fn media_type(&self) -> MediaType {
241        self.extra.media_type.clone()
242    }
243
244    /// Returns whether document colors are enabled.
245    pub fn forced_colors(&self) -> ForcedColors {
246        ForcedColors::None
247    }
248
249    /// Returns the default background color.
250    pub fn default_background_color(&self) -> AbsoluteColor {
251        AbsoluteColor::WHITE
252    }
253
254    /// Returns the default foreground color.
255    pub fn default_color(&self) -> AbsoluteColor {
256        AbsoluteColor::BLACK
257    }
258
259    /// Set the [`PrefersColorScheme`] value on this [`Device`].
260    ///
261    /// Note that this does not update any associated `Stylist`. For this you must call
262    /// `Stylist::media_features_change_changed_style` and
263    /// `Stylist::force_stylesheet_origins_dirty`.
264    pub fn set_color_scheme(&mut self, new_color_scheme: PrefersColorScheme) {
265        self.extra.prefers_color_scheme = new_color_scheme;
266    }
267
268    /// Returns the color scheme of this [`Device`].
269    pub fn color_scheme(&self) -> PrefersColorScheme {
270        self.extra.prefers_color_scheme
271    }
272
273    pub(crate) fn is_dark_color_scheme(&self, _: ColorSchemeFlags) -> bool {
274        false
275    }
276
277    pub(crate) fn system_color(
278        &self,
279        system_color: SystemColor,
280        color_scheme_flags: ColorSchemeFlags,
281    ) -> AbsoluteColor {
282        fn srgb(r: u8, g: u8, b: u8) -> AbsoluteColor {
283            AbsoluteColor::srgb_legacy(r, g, b, 1f32)
284        }
285
286        // Refer to spec
287        // <https://www.w3.org/TR/css-color-4/#css-system-colors>
288        if self.is_dark_color_scheme(color_scheme_flags) {
289            // Note: is_dark_color_scheme always returns true, so this code is dead code.
290            match system_color {
291                SystemColor::Accentcolor => srgb(10, 132, 255),
292                SystemColor::Accentcolortext => srgb(255, 255, 255),
293                SystemColor::Activetext => srgb(255, 0, 0),
294                SystemColor::Linktext => srgb(158, 158, 255),
295                SystemColor::Visitedtext => srgb(208, 173, 240),
296                SystemColor::Buttonborder
297                // Deprecated system colors (CSS Color 4) mapped to Buttonborder.
298                | SystemColor::Activeborder
299                | SystemColor::Inactiveborder
300                | SystemColor::Threeddarkshadow
301                | SystemColor::Threedshadow
302                | SystemColor::Windowframe => srgb(255, 255, 255),
303                SystemColor::Buttonface
304                // Deprecated system colors (CSS Color 4) mapped to Buttonface.
305                | SystemColor::Buttonhighlight
306                | SystemColor::Buttonshadow
307                | SystemColor::Threedface
308                | SystemColor::Threedhighlight
309                | SystemColor::Threedlightshadow => srgb(107, 107, 107),
310                SystemColor::Buttontext => srgb(245, 245, 245),
311                SystemColor::Canvas
312                // Deprecated system colors (CSS Color 4) mapped to Canvas.
313                | SystemColor::Activecaption
314                | SystemColor::Appworkspace
315                | SystemColor::Background
316                | SystemColor::Inactivecaption
317                | SystemColor::Infobackground
318                | SystemColor::Menu
319                | SystemColor::Scrollbar
320                | SystemColor::Window => srgb(30, 30, 30),
321                SystemColor::Canvastext
322                // Deprecated system colors (CSS Color 4) mapped to Canvastext.
323                | SystemColor::Captiontext
324                | SystemColor::Infotext
325                | SystemColor::Menutext
326                | SystemColor::Windowtext => srgb(232, 232, 232),
327                SystemColor::Field => srgb(45, 45, 45),
328                SystemColor::Fieldtext => srgb(240, 240, 240),
329                SystemColor::Graytext
330                // Deprecated system colors (CSS Color 4) mapped to Graytext.
331                | SystemColor::Inactivecaptiontext => srgb(155, 155, 155),
332                SystemColor::Highlight => srgb(38, 79, 120),
333                SystemColor::Highlighttext => srgb(255, 255, 255),
334                SystemColor::Mark => srgb(102, 92, 0),
335                SystemColor::Marktext => srgb(255, 255, 255),
336                SystemColor::Selecteditem => srgb(153, 200, 255),
337                SystemColor::Selecteditemtext => srgb(59, 59, 59),
338            }
339        } else {
340            match system_color {
341                SystemColor::Accentcolor => srgb(0, 102, 204),
342                SystemColor::Accentcolortext => srgb(255, 255, 255),
343                SystemColor::Activetext => srgb(238, 0, 0),
344                SystemColor::Linktext => srgb(0, 0, 238),
345                SystemColor::Visitedtext => srgb(85, 26, 139),
346                SystemColor::Buttonborder
347                // Deprecated system colors (CSS Color 4) mapped to Buttonborder.
348                | SystemColor::Activeborder
349                | SystemColor::Inactiveborder
350                | SystemColor::Threeddarkshadow
351                | SystemColor::Threedshadow
352                | SystemColor::Windowframe => srgb(169, 169, 169),
353                SystemColor::Buttonface
354                // Deprecated system colors (CSS Color 4) mapped to Buttonface.
355                | SystemColor::Buttonhighlight
356                | SystemColor::Buttonshadow
357                | SystemColor::Threedface
358                | SystemColor::Threedhighlight
359                | SystemColor::Threedlightshadow => srgb(220, 220, 220),
360                SystemColor::Buttontext => srgb(0, 0, 0),
361                SystemColor::Canvas
362                // Deprecated system colors (CSS Color 4) mapped to Canvas.
363                | SystemColor::Activecaption
364                | SystemColor::Appworkspace
365                | SystemColor::Background
366                | SystemColor::Inactivecaption
367                | SystemColor::Infobackground
368                | SystemColor::Menu
369                | SystemColor::Scrollbar
370                | SystemColor::Window => srgb(255, 255, 255),
371                SystemColor::Canvastext
372                // Deprecated system colors (CSS Color 4) mapped to Canvastext.
373                | SystemColor::Captiontext
374                | SystemColor::Infotext
375                | SystemColor::Menutext
376                | SystemColor::Windowtext => srgb(0, 0, 0),
377                SystemColor::Field => srgb(255, 255, 255),
378                SystemColor::Fieldtext => srgb(0, 0, 0),
379                SystemColor::Graytext
380                // Deprecated system colors (CSS Color 4) mapped to Graytext.
381                | SystemColor::Inactivecaptiontext => srgb(109, 109, 109),
382                SystemColor::Highlight => srgb(0, 65, 198),
383                SystemColor::Highlighttext => srgb(0, 0, 0),
384                SystemColor::Mark => srgb(255, 235, 59),
385                SystemColor::Marktext => srgb(0, 0, 0),
386                SystemColor::Selecteditem => srgb(0, 102, 204),
387                SystemColor::Selecteditemtext => srgb(255, 255, 255),
388            }
389        }
390    }
391
392    /// Returns safe area insets
393    pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
394        SideOffsets2D::zero()
395    }
396
397    /// Returns true if the given MIME type is supported
398    pub fn is_supported_mime_type(&self, mime_type: &str) -> bool {
399        match mime_type.parse::<Mime>() {
400            Ok(m) => {
401                // Keep this in sync with 'image_classifer' from
402                // components/net/mime_classifier.rs
403                m == mime::IMAGE_BMP
404                    || m == mime::IMAGE_GIF
405                    || m == mime::IMAGE_PNG
406                    || m == mime::IMAGE_JPEG
407                    || m == "image/x-icon"
408                    || m == "image/webp"
409            },
410            _ => false,
411        }
412    }
413
414    /// Return whether the document is a chrome document.
415    #[inline]
416    pub fn chrome_rules_enabled_for_document(&self) -> bool {
417        false
418    }
419}