Skip to main content

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::ComputedValues;
14use crate::properties::style_structs::Font;
15use crate::queries::values::PrefersColorScheme;
16use crate::servo::media_features::PointerCapabilities;
17use crate::values::KeyframesName;
18use crate::values::computed::font::GenericFontFamily;
19use crate::values::computed::{
20    CSSPixelLength, Length, LineHeight, LinkParameters, NonNegativeLength,
21};
22use crate::values::specified::ViewportVariant;
23use crate::values::specified::color::{ColorSchemeFlags, ForcedColors, SystemColor};
24use crate::values::specified::font::{
25    FONT_MEDIUM_CAP_PX, FONT_MEDIUM_CH_PX, FONT_MEDIUM_EX_PX, FONT_MEDIUM_IC_PX,
26    FONT_MEDIUM_LINE_HEIGHT_PX, FONT_MEDIUM_PX, QueryFontMetricsFlags,
27};
28use app_units::{AU_PER_PX, Au};
29use euclid::default::Size2D as UntypedSize2D;
30use euclid::{Scale, SideOffsets2D, Size2D};
31use malloc_size_of_derive::MallocSizeOf;
32use mime::Mime;
33use parking_lot::RwLock;
34use servo_arc::Arc;
35use std::fmt::Debug;
36use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
37use style_traits::{CSSPixel, DevicePixel};
38
39use crate::device::Device;
40
41/// A trait used to query font metrics in clients of Stylo. This is used by Device to
42/// query font metrics in a way that is specific to the client using Stylo.
43pub trait FontMetricsProvider: Debug + Sync {
44    /// Query the font metrics for the given font and the given base font size.
45    fn query_font_metrics(
46        &self,
47        vertical: bool,
48        font: &Font,
49        base_size: CSSPixelLength,
50        flags: QueryFontMetricsFlags,
51    ) -> FontMetrics;
52    /// Gets the base size given a generic font family.
53    fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length;
54}
55
56#[derive(Debug, MallocSizeOf)]
57pub(super) struct ExtraDeviceData {
58    /// Data which Stylo uses to evaluate media queries
59    media_data: MediaData,
60    /// An implementation of a trait which implements support for querying font metrics.
61    #[ignore_malloc_size_of = "Owned by embedder"]
62    font_metrics_provider: Box<dyn FontMetricsProvider>,
63}
64
65#[derive(Debug, MallocSizeOf, Clone, PartialEq)]
66/// Data which Stylo uses to evaluate media queries
67pub struct MediaData {
68    /// The current media type used by de device.
69    pub media_type: MediaType,
70    /// The current viewport size, in CSS pixels.
71    pub viewport_size: Size2D<f32, CSSPixel>,
72    /// The current screen size, in device pixels.
73    pub device_size: Size2D<f32, DevicePixel>,
74    /// The current device pixel ratio, from CSS pixels to device pixels.
75    pub device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
76    /// The current quirks mode.
77    #[ignore_malloc_size_of = "Pure stack type"]
78    pub quirks_mode: QuirksMode,
79    /// Whether the user prefers light mode or dark mode
80    #[ignore_malloc_size_of = "Pure stack type"]
81    pub prefers_color_scheme: PrefersColorScheme,
82    /// The capabilities of the primary pointer input
83    #[ignore_malloc_size_of = "Pure stack type"]
84    pub primary_pointer_capabilities: PointerCapabilities,
85    /// The union of the capabilities of all pointer inputs
86    #[ignore_malloc_size_of = "Pure stack type"]
87    pub all_pointer_capabilities: PointerCapabilities,
88}
89
90impl Device {
91    /// Trivially construct a new `Device`.
92    pub fn new(
93        default_values: Arc<ComputedValues>,
94        font_metrics_provider: Box<dyn FontMetricsProvider>,
95        media_data: MediaData,
96    ) -> Device {
97        let root_style = RwLock::new(Arc::clone(&default_values));
98        Device {
99            root_style,
100            root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),
101            root_line_height: AtomicU32::new(FONT_MEDIUM_LINE_HEIGHT_PX.to_bits()),
102            root_font_metrics_ex: AtomicU32::new(FONT_MEDIUM_EX_PX.to_bits()),
103            root_font_metrics_cap: AtomicU32::new(FONT_MEDIUM_CAP_PX.to_bits()),
104            root_font_metrics_ch: AtomicU32::new(FONT_MEDIUM_CH_PX.to_bits()),
105            root_font_metrics_ic: AtomicU32::new(FONT_MEDIUM_IC_PX.to_bits()),
106            used_root_font_size: AtomicBool::new(false),
107            used_root_line_height: AtomicBool::new(false),
108            used_root_font_metrics: RwLock::new(false),
109            used_font_metrics: AtomicBool::new(false),
110            used_viewport_size: AtomicBool::new(false),
111            used_dynamic_viewport_size: AtomicBool::new(false),
112            environment: CssEnvironment,
113            default_values,
114            body_text_color: RwLock::new(AbsoluteColor::BLACK),
115            extra: ExtraDeviceData {
116                media_data,
117                font_metrics_provider,
118            },
119        }
120    }
121
122    /// Returns the computed line-height for the font in a given computed values instance.
123    ///
124    /// If you pass down an element, then the used line-height is returned.
125    pub fn calc_line_height(
126        &self,
127        font: &crate::properties::style_structs::Font,
128        _writing_mode: WritingMode,
129        _element: Option<()>,
130    ) -> NonNegativeLength {
131        (match font.line_height {
132            // TODO: compute `normal` from the font metrics
133            LineHeight::Normal => CSSPixelLength::new(0.),
134            LineHeight::Number(number) => font.font_size.computed_size() * number.0,
135            LineHeight::Length(length) => length.0,
136        })
137        .into()
138    }
139
140    /// Get the quirks mode of the current device.
141    pub fn quirks_mode(&self) -> QuirksMode {
142        self.extra.media_data.quirks_mode
143    }
144
145    /// Returns a value that identifies this document, used to vary the random
146    /// base values of `random()` functions between document instances.
147    #[inline]
148    pub fn document_random_seed(&self) -> u64 {
149        // TODO: Implement a document-specific value.
150        0
151    }
152
153    /// Gets the base size given a generic font family.
154    pub fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length {
155        self.extra
156            .font_metrics_provider
157            .base_size_for_generic(generic)
158    }
159
160    /// Whether a given animation name may be referenced from style.
161    pub fn animation_name_may_be_referenced(&self, _: &KeyframesName) -> bool {
162        // Assume it is, since we don't have any good way to prove it's not.
163        true
164    }
165
166    /// Get the [`MediaData`] associated with this [`Device`].
167    #[inline]
168    pub fn media_data(&self) -> &MediaData {
169        &self.extra.media_data
170    }
171
172    /// Mutate the [`MediaData`] on this [`Device`].
173    ///
174    /// Note that this does not update any associated `Stylist`. For this you must call
175    /// `Stylist::media_features_change_changed_style` and
176    /// `Stylist::force_stylesheet_origins_dirty`.
177    pub fn media_data_mut(&mut self) -> &mut MediaData {
178        &mut self.extra.media_data
179    }
180
181    /// Set the [`MediaData`] on this [`Device`].
182    ///
183    /// Note that this does not update any associated `Stylist`. For this you must call
184    /// `Stylist::media_features_change_changed_style` and
185    /// `Stylist::force_stylesheet_origins_dirty`.
186    pub fn set_media_data(&mut self, media_data: MediaData) {
187        self.extra.media_data = media_data;
188    }
189
190    /// Get the viewport size on this [`Device`].
191    pub fn viewport_size(&self) -> Size2D<f32, CSSPixel> {
192        self.extra.media_data.viewport_size
193    }
194
195    /// Set the viewport size on this [`Device`].
196    ///
197    /// Note that this does not update any associated `Stylist`. For this you must call
198    /// `Stylist::media_features_change_changed_style` and
199    /// `Stylist::force_stylesheet_origins_dirty`.
200    pub fn set_viewport_size(&mut self, viewport_size: Size2D<f32, CSSPixel>) {
201        self.extra.media_data.viewport_size = viewport_size;
202    }
203
204    /// Returns the viewport size of the current device in app units, needed,
205    /// among other things, to resolve viewport units.
206    #[inline]
207    pub fn au_viewport_size(&self) -> UntypedSize2D<Au> {
208        Size2D::new(
209            Au::from_f32_px(self.extra.media_data.viewport_size.width),
210            Au::from_f32_px(self.extra.media_data.viewport_size.height),
211        )
212    }
213
214    /// Like the above, but records that we've used viewport units.
215    pub fn au_viewport_size_for_viewport_unit_resolution(
216        &self,
217        _: ViewportVariant,
218    ) -> UntypedSize2D<Au> {
219        self.used_viewport_size.store(true, Ordering::Relaxed);
220        // Servo doesn't have dynamic UA interfaces that affect the viewport,
221        // so we can just ignore the ViewportVariant.
222        self.au_viewport_size()
223    }
224
225    /// Returns the number of app units per device pixel we're using currently.
226    pub fn app_units_per_device_pixel(&self) -> i32 {
227        (AU_PER_PX as f32 / self.extra.media_data.device_pixel_ratio.0) as i32
228    }
229
230    /// Returns the device pixel ratio, ignoring the full zoom factor.
231    pub fn device_pixel_ratio_ignoring_full_zoom(&self) -> Scale<f32, CSSPixel, DevicePixel> {
232        self.extra.media_data.device_pixel_ratio
233    }
234
235    /// Returns the device pixel ratio.
236    pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
237        self.extra.media_data.device_pixel_ratio
238    }
239
240    /// Set a new device pixel ratio on this [`Device`].
241    ///
242    /// Note that this does not update any associated `Stylist`. For this you must call
243    /// `Stylist::media_features_change_changed_style` and
244    /// `Stylist::force_stylesheet_origins_dirty`.
245    pub fn set_device_pixel_ratio(
246        &mut self,
247        device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
248    ) {
249        self.extra.media_data.device_pixel_ratio = device_pixel_ratio;
250    }
251
252    /// Set the device size on this [`Device`] (e.g. the available screen dimensions).
253    ///
254    /// Note that this does not update any associated `Stylist`. For this you must call
255    /// `Stylist::media_features_change_changed_style` and
256    /// `Stylist::force_stylesheet_origins_dirty`.
257    pub fn set_device_size(&mut self, device_size: Size2D<f32, DevicePixel>) {
258        self.extra.media_data.device_size = device_size;
259    }
260
261    /// Returns the screen size of the current device in app units.
262    #[inline]
263    pub fn device_size(&self) -> Size2D<f32, DevicePixel> {
264        self.extra.media_data.device_size
265    }
266
267    /// Gets the size of the scrollbar in CSS pixels.
268    pub fn scrollbar_inline_size(&self) -> CSSPixelLength {
269        // TODO: implement this.
270        CSSPixelLength::new(0.0)
271    }
272
273    /// Queries font metrics using the [`FontMetricsProvider`] interface.
274    pub fn query_font_metrics(
275        &self,
276        vertical: bool,
277        font: &Font,
278        base_size: CSSPixelLength,
279        flags: QueryFontMetricsFlags,
280        track_usage: bool,
281    ) -> FontMetrics {
282        if track_usage {
283            self.used_font_metrics.store(true, Ordering::Relaxed);
284        }
285        self.extra
286            .font_metrics_provider
287            .query_font_metrics(vertical, font, base_size, flags)
288    }
289
290    /// Set the media type on this [`Device`].
291    ///
292    /// Note that this does not update any associated `Stylist`. For this you must call
293    /// `Stylist::media_features_change_changed_style` and
294    /// `Stylist::force_stylesheet_origins_dirty`.
295    pub fn set_media_type(&mut self, media_type: MediaType) {
296        self.extra.media_data.media_type = media_type;
297    }
298
299    /// Return the media type of the current device.
300    pub fn media_type(&self) -> MediaType {
301        self.extra.media_data.media_type.clone()
302    }
303
304    /// Returns whether document colors are enabled.
305    pub fn forced_colors(&self) -> ForcedColors {
306        ForcedColors::None
307    }
308
309    /// Returns the default background color.
310    pub fn default_background_color(&self) -> AbsoluteColor {
311        AbsoluteColor::WHITE
312    }
313
314    /// Returns the default foreground color.
315    pub fn default_color(&self) -> AbsoluteColor {
316        AbsoluteColor::BLACK
317    }
318
319    /// Set the [`PrefersColorScheme`] value on this [`Device`].
320    ///
321    /// Note that this does not update any associated `Stylist`. For this you must call
322    /// `Stylist::media_features_change_changed_style` and
323    /// `Stylist::force_stylesheet_origins_dirty`.
324    pub fn set_color_scheme(&mut self, new_color_scheme: PrefersColorScheme) {
325        self.extra.media_data.prefers_color_scheme = new_color_scheme;
326    }
327
328    /// Returns the color scheme of this [`Device`].
329    pub fn color_scheme(&self) -> PrefersColorScheme {
330        self.extra.media_data.prefers_color_scheme
331    }
332
333    /// Set the [`PointerCapbabilities`] value for the primary pointer on this [`Device`]
334    ///
335    /// Note that this does not update any associated `Stylist`. For this you must call
336    /// `Stylist::media_features_change_changed_style` and
337    /// `Stylist::force_stylesheet_origins_dirty`.
338    pub fn set_primary_pointer_capabilities(&mut self, capabilities: PointerCapabilities) {
339        self.extra.media_data.primary_pointer_capabilities = capabilities;
340    }
341
342    /// Returns the pointer capabilities of this [`Device`].
343    pub fn primary_pointer_capabilities(&self) -> PointerCapabilities {
344        self.extra.media_data.primary_pointer_capabilities
345    }
346
347    /// Set the [`PointerCapbabilities`] value for all pointers on this [`Device`]
348    ///
349    /// Note that this does not update any associated `Stylist`. For this you must call
350    /// `Stylist::media_features_change_changed_style` and
351    /// `Stylist::force_stylesheet_origins_dirty`.
352    pub fn set_all_pointer_capabilities(&mut self, capabilities: PointerCapabilities) {
353        self.extra.media_data.all_pointer_capabilities = capabilities;
354    }
355
356    /// Returns the pointer capabilities of this [`Device`].
357    pub fn all_pointer_capabilities(&self) -> PointerCapabilities {
358        self.extra.media_data.all_pointer_capabilities
359    }
360
361    pub(crate) fn is_dark_color_scheme(&self, color_scheme_flags: ColorSchemeFlags) -> bool {
362        // Inspired by
363        // https://searchfox.org/firefox-main/rev/0a7f146ccac85b8f413264042dcd764028d419ec/widget/nsXPLookAndFeel.cpp#1296
364        let supports_dark_mode = color_scheme_flags.contains(ColorSchemeFlags::DARK);
365        let supports_light_mode = color_scheme_flags.contains(ColorSchemeFlags::LIGHT);
366
367        // If only one is supported, then use dark mode if it was the supported one.
368        if supports_dark_mode != supports_light_mode {
369            return supports_dark_mode;
370        }
371
372        // If either both or none are supported, then use the preferred color scheme
373        // to determine whether the user wants dark mode.
374        return self.color_scheme() == PrefersColorScheme::Dark;
375    }
376
377    pub(crate) fn system_color(
378        &self,
379        system_color: SystemColor,
380        color_scheme_flags: ColorSchemeFlags,
381    ) -> AbsoluteColor {
382        fn srgb(r: u8, g: u8, b: u8) -> AbsoluteColor {
383            AbsoluteColor::srgb_legacy(r, g, b, 1f32)
384        }
385
386        // Refer to spec
387        // <https://www.w3.org/TR/css-color-4/#css-system-colors>
388        if self.is_dark_color_scheme(color_scheme_flags) {
389            match system_color {
390                SystemColor::Accentcolor => srgb(10, 132, 255),
391                SystemColor::Accentcolortext => srgb(255, 255, 255),
392                SystemColor::Activetext => srgb(255, 0, 0),
393                SystemColor::Linktext => srgb(158, 158, 255),
394                SystemColor::Visitedtext => srgb(208, 173, 240),
395                SystemColor::Buttonborder
396                // Deprecated system colors (CSS Color 4) mapped to Buttonborder.
397                | SystemColor::Activeborder
398                | SystemColor::Inactiveborder
399                | SystemColor::Threeddarkshadow
400                | SystemColor::Threedshadow
401                | SystemColor::Windowframe => srgb(255, 255, 255),
402                SystemColor::Buttonface
403                // Deprecated system colors (CSS Color 4) mapped to Buttonface.
404                | SystemColor::Buttonhighlight
405                | SystemColor::Buttonshadow
406                | SystemColor::Threedface
407                | SystemColor::Threedhighlight
408                | SystemColor::Threedlightshadow => srgb(107, 107, 107),
409                SystemColor::Buttontext => srgb(245, 245, 245),
410                SystemColor::Canvas
411                // Deprecated system colors (CSS Color 4) mapped to Canvas.
412                | SystemColor::Activecaption
413                | SystemColor::Appworkspace
414                | SystemColor::Background
415                | SystemColor::Inactivecaption
416                | SystemColor::Infobackground
417                | SystemColor::Menu
418                | SystemColor::Scrollbar
419                | SystemColor::Window => srgb(30, 30, 30),
420                SystemColor::Canvastext
421                // Deprecated system colors (CSS Color 4) mapped to Canvastext.
422                | SystemColor::Captiontext
423                | SystemColor::Infotext
424                | SystemColor::Menutext
425                | SystemColor::Windowtext => srgb(232, 232, 232),
426                SystemColor::Field => srgb(45, 45, 45),
427                SystemColor::Fieldtext => srgb(240, 240, 240),
428                SystemColor::Graytext
429                // Deprecated system colors (CSS Color 4) mapped to Graytext.
430                | SystemColor::Inactivecaptiontext => srgb(155, 155, 155),
431                SystemColor::Highlight => srgb(38, 79, 120),
432                SystemColor::Highlighttext => srgb(255, 255, 255),
433                SystemColor::Mark => srgb(102, 92, 0),
434                SystemColor::Marktext => srgb(255, 255, 255),
435                SystemColor::Selecteditem => srgb(153, 200, 255),
436                SystemColor::Selecteditemtext => srgb(59, 59, 59),
437            }
438        } else {
439            match system_color {
440                SystemColor::Accentcolor => srgb(0, 102, 204),
441                SystemColor::Accentcolortext => srgb(255, 255, 255),
442                SystemColor::Activetext => srgb(238, 0, 0),
443                SystemColor::Linktext => srgb(0, 0, 238),
444                SystemColor::Visitedtext => srgb(85, 26, 139),
445                SystemColor::Buttonborder
446                // Deprecated system colors (CSS Color 4) mapped to Buttonborder.
447                | SystemColor::Activeborder
448                | SystemColor::Inactiveborder
449                | SystemColor::Threeddarkshadow
450                | SystemColor::Threedshadow
451                | SystemColor::Windowframe => srgb(169, 169, 169),
452                SystemColor::Buttonface
453                // Deprecated system colors (CSS Color 4) mapped to Buttonface.
454                | SystemColor::Buttonhighlight
455                | SystemColor::Buttonshadow
456                | SystemColor::Threedface
457                | SystemColor::Threedhighlight
458                | SystemColor::Threedlightshadow => srgb(220, 220, 220),
459                SystemColor::Buttontext => srgb(0, 0, 0),
460                SystemColor::Canvas
461                // Deprecated system colors (CSS Color 4) mapped to Canvas.
462                | SystemColor::Activecaption
463                | SystemColor::Appworkspace
464                | SystemColor::Background
465                | SystemColor::Inactivecaption
466                | SystemColor::Infobackground
467                | SystemColor::Menu
468                | SystemColor::Scrollbar
469                | SystemColor::Window => srgb(255, 255, 255),
470                SystemColor::Canvastext
471                // Deprecated system colors (CSS Color 4) mapped to Canvastext.
472                | SystemColor::Captiontext
473                | SystemColor::Infotext
474                | SystemColor::Menutext
475                | SystemColor::Windowtext => srgb(0, 0, 0),
476                SystemColor::Field => srgb(255, 255, 255),
477                SystemColor::Fieldtext => srgb(0, 0, 0),
478                SystemColor::Graytext
479                // Deprecated system colors (CSS Color 4) mapped to Graytext.
480                | SystemColor::Inactivecaptiontext => srgb(109, 109, 109),
481                SystemColor::Highlight => srgb(0, 65, 198),
482                SystemColor::Highlighttext => srgb(0, 0, 0),
483                SystemColor::Mark => srgb(255, 235, 59),
484                SystemColor::Marktext => srgb(0, 0, 0),
485                SystemColor::Selecteditem => srgb(0, 102, 204),
486                SystemColor::Selecteditemtext => srgb(255, 255, 255),
487            }
488        }
489    }
490
491    /// Returns the current effective text zoom.
492    #[inline]
493    pub(super) fn text_zoom(&self) -> f32 {
494        // (Servo doesn't do text-zoom)
495        1.
496    }
497
498    /// Returns safe area insets
499    pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
500        SideOffsets2D::zero()
501    }
502
503    /// Returns true if the given MIME type is supported
504    pub fn is_supported_mime_type(&self, mime_type: &str) -> bool {
505        match mime_type.parse::<Mime>() {
506            Ok(m) => {
507                // Keep this in sync with 'image_classifer' from
508                // components/net/mime_classifier.rs
509                m == mime::IMAGE_BMP
510                    || m == mime::IMAGE_GIF
511                    || m == mime::IMAGE_PNG
512                    || m == mime::IMAGE_JPEG
513                    || m == "image/x-icon"
514                    || m == "image/webp"
515            },
516            _ => false,
517        }
518    }
519
520    /// Return whether the document is a chrome document.
521    #[inline]
522    pub fn chrome_rules_enabled_for_document(&self) -> bool {
523        false
524    }
525
526    /// Returns the link-parameters that have been set for this document.
527    /// <https://drafts.csswg.org/css-link-params-1/>
528    #[inline]
529    pub fn link_parameters(&self) -> Option<&LinkParameters> {
530        None
531    }
532}