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