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