Skip to main content

style/servo/
media_features.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's media feature list and evaluator.
6
7use crate::derives::*;
8use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription};
9use crate::queries::values::{Orientation, PrefersColorScheme};
10use crate::values::computed::{CSSPixelLength, Context, Ratio, Resolution};
11use std::fmt::Debug;
12
13/// https://drafts.csswg.org/mediaqueries-4/#width
14fn eval_width(context: &Context) -> CSSPixelLength {
15    CSSPixelLength::new(context.device().au_viewport_size().width.to_f32_px())
16}
17
18/// https://drafts.csswg.org/mediaqueries-4/#height
19fn eval_height(context: &Context) -> CSSPixelLength {
20    CSSPixelLength::new(context.device().au_viewport_size().height.to_f32_px())
21}
22
23/// https://drafts.csswg.org/mediaqueries-4/#device-width
24fn eval_device_width(context: &Context) -> CSSPixelLength {
25    let device = context.device();
26    let scaled = device.device_size() / device.device_pixel_ratio();
27    CSSPixelLength::new(scaled.width)
28}
29
30/// https://drafts.csswg.org/mediaqueries-4/#device-height
31fn eval_device_height(context: &Context) -> CSSPixelLength {
32    let device = context.device();
33    let scaled = device.device_size() / device.device_pixel_ratio();
34    CSSPixelLength::new(scaled.height)
35}
36
37/// https://drafts.csswg.org/mediaqueries-4/#orientation
38fn eval_orientation(context: &Context, value: Option<Orientation>) -> bool {
39    Orientation::eval(context.device().au_viewport_size(), value)
40}
41
42#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
43#[repr(u8)]
44enum Scan {
45    Progressive,
46    Interlace,
47}
48
49/// https://drafts.csswg.org/mediaqueries-4/#scan
50fn eval_scan(_: &Context, _: Option<Scan>) -> bool {
51    // Since we doesn't support the 'tv' media type, the 'scan' feature never
52    // matches.
53    false
54}
55
56/// https://drafts.csswg.org/mediaqueries-4/#resolution
57fn eval_resolution(context: &Context) -> Resolution {
58    Resolution::from_dppx(context.device().device_pixel_ratio().0)
59}
60
61/// https://compat.spec.whatwg.org/#css-media-queries-webkit-device-pixel-ratio
62fn eval_device_pixel_ratio(context: &Context) -> f32 {
63    eval_resolution(context).dppx()
64}
65
66fn eval_prefers_color_scheme(context: &Context, query_value: Option<PrefersColorScheme>) -> bool {
67    match query_value {
68        Some(v) => context.device().color_scheme() == v,
69        None => true,
70    }
71}
72
73bitflags! {
74    /// https://drafts.csswg.org/mediaqueries-4/#mf-interaction
75    #[derive(Debug, Clone, Copy)]
76    pub struct PointerCapabilities: u8 {
77        /// The input mechanism includes a pointing device of limited accuracy, such as a finger on a touchscreen.
78        const COARSE = 0b001;
79        /// The input mechanism includes an accurate pointing device, such as a mouse.
80        const FINE = 0b010;
81        /// The input mechanism can conveniently hover over elements.
82        const HOVER = 0b100;
83    }
84}
85
86impl Default for PointerCapabilities {
87    #[cfg(any(target_os = "ios", target_os = "android", target_env = "ohos"))]
88    fn default() -> Self {
89        PointerCapabilities::COARSE
90    }
91    #[cfg(not(any(target_os = "ios", target_os = "android", target_env = "ohos")))]
92    fn default() -> Self {
93        PointerCapabilities::FINE | PointerCapabilities::HOVER
94    }
95}
96
97#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
98#[repr(u8)]
99enum Pointer {
100    None,
101    Coarse,
102    Fine,
103}
104
105fn eval_pointer_capabilities(
106    query_value: Option<Pointer>,
107    pointer_capabilities: PointerCapabilities,
108) -> bool {
109    match query_value {
110        None => !pointer_capabilities.is_empty(),
111        Some(Pointer::None) => pointer_capabilities.is_empty(),
112        Some(Pointer::Coarse) => pointer_capabilities.intersects(PointerCapabilities::COARSE),
113        Some(Pointer::Fine) => pointer_capabilities.intersects(PointerCapabilities::FINE),
114    }
115}
116
117/// https://drafts.csswg.org/mediaqueries-4/#pointer
118fn eval_pointer(context: &Context, query_value: Option<Pointer>) -> bool {
119    eval_pointer_capabilities(query_value, context.device().primary_pointer_capabilities())
120}
121
122/// https://drafts.csswg.org/mediaqueries-4/#descdef-media-any-pointer
123fn eval_any_pointer(context: &Context, query_value: Option<Pointer>) -> bool {
124    eval_pointer_capabilities(query_value, context.device().all_pointer_capabilities())
125}
126
127#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
128#[repr(u8)]
129enum Hover {
130    None,
131    Hover,
132}
133
134fn eval_hover_capabilities(
135    query_value: Option<Hover>,
136    pointer_capabilities: PointerCapabilities,
137) -> bool {
138    let can_hover = pointer_capabilities.intersects(PointerCapabilities::HOVER);
139    match query_value {
140        Some(Hover::None) => !can_hover,
141        Some(Hover::Hover) => can_hover,
142        None => return can_hover,
143    }
144}
145
146/// https://drafts.csswg.org/mediaqueries-4/#hover
147fn eval_hover(context: &Context, query_value: Option<Hover>) -> bool {
148    eval_hover_capabilities(query_value, context.device().primary_pointer_capabilities())
149}
150
151/// https://drafts.csswg.org/mediaqueries-4/#descdef-media-any-hover
152fn eval_any_hover(context: &Context, query_value: Option<Hover>) -> bool {
153    eval_hover_capabilities(query_value, context.device().all_pointer_capabilities())
154}
155
156/// <https://drafts.csswg.org/mediaqueries-4/#aspect-ratio>
157fn eval_aspect_ratio(context: &Context) -> Ratio {
158    let size = context.device().au_viewport_size();
159    Ratio::new(size.width.0 as f32, size.height.0 as f32)
160}
161
162/// A list with all the media features that Servo supports.
163pub static MEDIA_FEATURES: [QueryFeatureDescription; 15] = [
164    feature!(
165        atom!("width"),
166        AllowsRanges::Yes,
167        Evaluator::Length(eval_width),
168        FeatureFlags::VIEWPORT_DEPENDENT,
169    ),
170    feature!(
171        atom!("height"),
172        AllowsRanges::Yes,
173        Evaluator::Length(eval_height),
174        FeatureFlags::VIEWPORT_DEPENDENT,
175    ),
176    feature!(
177        atom!("orientation"),
178        AllowsRanges::No,
179        keyword_evaluator!(eval_orientation, Orientation),
180        FeatureFlags::VIEWPORT_DEPENDENT,
181    ),
182    feature!(
183        atom!("pointer"),
184        AllowsRanges::No,
185        keyword_evaluator!(eval_pointer, Pointer),
186        FeatureFlags::empty(),
187    ),
188    feature!(
189        atom!("any-pointer"),
190        AllowsRanges::No,
191        keyword_evaluator!(eval_any_pointer, Pointer),
192        FeatureFlags::empty(),
193    ),
194    feature!(
195        atom!("hover"),
196        AllowsRanges::No,
197        keyword_evaluator!(eval_hover, Hover),
198        FeatureFlags::empty(),
199    ),
200    feature!(
201        atom!("any-hover"),
202        AllowsRanges::No,
203        keyword_evaluator!(eval_any_hover, Hover),
204        FeatureFlags::empty(),
205    ),
206    feature!(
207        atom!("aspect-ratio"),
208        AllowsRanges::Yes,
209        Evaluator::NumberRatio(eval_aspect_ratio),
210        FeatureFlags::VIEWPORT_DEPENDENT,
211    ),
212    feature!(
213        atom!("device-width"),
214        AllowsRanges::Yes,
215        Evaluator::Length(eval_device_width),
216        FeatureFlags::empty(),
217    ),
218    feature!(
219        atom!("device-height"),
220        AllowsRanges::Yes,
221        Evaluator::Length(eval_device_height),
222        FeatureFlags::empty(),
223    ),
224    feature!(
225        atom!("scan"),
226        AllowsRanges::No,
227        keyword_evaluator!(eval_scan, Scan),
228        FeatureFlags::empty(),
229    ),
230    feature!(
231        atom!("resolution"),
232        AllowsRanges::Yes,
233        Evaluator::Resolution(eval_resolution),
234        FeatureFlags::empty(),
235    ),
236    feature!(
237        atom!("device-pixel-ratio"),
238        AllowsRanges::Yes,
239        Evaluator::Float(eval_device_pixel_ratio),
240        FeatureFlags::WEBKIT_PREFIX,
241    ),
242    feature!(
243        atom!("-moz-device-pixel-ratio"),
244        AllowsRanges::Yes,
245        Evaluator::Float(eval_device_pixel_ratio),
246        FeatureFlags::empty(),
247    ),
248    feature!(
249        atom!("prefers-color-scheme"),
250        AllowsRanges::No,
251        keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme),
252        FeatureFlags::empty(),
253    ),
254];