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-query device and expression representation.
6 
7 use crate::context::QuirksMode;
8 use crate::custom_properties::CssEnvironment;
9 use crate::media_queries::media_feature::{AllowsRanges, ParsingRequirements};
10 use crate::media_queries::media_feature::{Evaluator, MediaFeatureDescription};
11 use crate::media_queries::media_feature_expression::RangeOrOperator;
12 use crate::media_queries::MediaType;
13 use crate::properties::ComputedValues;
14 use crate::values::computed::CSSPixelLength;
15 use crate::values::specified::font::FONT_MEDIUM_PX;
16 use crate::values::KeyframesName;
17 use app_units::Au;
18 use cssparser::RGBA;
19 use euclid::default::Size2D as UntypedSize2D;
20 use euclid::{Scale, SideOffsets2D, Size2D};
21 use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
22 use style_traits::viewport::ViewportConstraints;
23 use style_traits::{CSSPixel, DevicePixel};
24 
25 /// A device is a structure that represents the current media a given document
26 /// is displayed in.
27 ///
28 /// This is the struct against which media queries are evaluated.
29 #[derive(Debug, MallocSizeOf)]
30 pub struct Device {
31     /// The current media type used by de device.
32     media_type: MediaType,
33     /// The current viewport size, in CSS pixels.
34     viewport_size: Size2D<f32, CSSPixel>,
35     /// The current device pixel ratio, from CSS pixels to device pixels.
36     device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
37     /// The current quirks mode.
38     #[ignore_malloc_size_of = "Pure stack type"]
39     quirks_mode: QuirksMode,
40 
41     /// The font size of the root element
42     /// This is set when computing the style of the root
43     /// element, and used for rem units in other elements
44     ///
45     /// When computing the style of the root element, there can't be any
46     /// other style being computed at the same time, given we need the style of
47     /// the parent to compute everything else. So it is correct to just use
48     /// a relaxed atomic here.
49     #[ignore_malloc_size_of = "Pure stack type"]
50     root_font_size: AtomicU32,
51     /// Whether any styles computed in the document relied on the root font-size
52     /// by using rem units.
53     #[ignore_malloc_size_of = "Pure stack type"]
54     used_root_font_size: AtomicBool,
55     /// Whether any styles computed in the document relied on the viewport size.
56     #[ignore_malloc_size_of = "Pure stack type"]
57     used_viewport_units: AtomicBool,
58     /// The CssEnvironment object responsible of getting CSS environment
59     /// variables.
60     environment: CssEnvironment,
61 }
62 
63 impl Device {
64     /// Trivially construct a new `Device`.
new( media_type: MediaType, quirks_mode: QuirksMode, viewport_size: Size2D<f32, CSSPixel>, device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>, ) -> Device65     pub fn new(
66         media_type: MediaType,
67         quirks_mode: QuirksMode,
68         viewport_size: Size2D<f32, CSSPixel>,
69         device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
70     ) -> Device {
71         Device {
72             media_type,
73             viewport_size,
74             device_pixel_ratio,
75             quirks_mode,
76             // FIXME(bz): Seems dubious?
77             root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),
78             used_root_font_size: AtomicBool::new(false),
79             used_viewport_units: AtomicBool::new(false),
80             environment: CssEnvironment,
81         }
82     }
83 
84     /// Get the relevant environment to resolve `env()` functions.
85     #[inline]
environment(&self) -> &CssEnvironment86     pub fn environment(&self) -> &CssEnvironment {
87         &self.environment
88     }
89 
90     /// Return the default computed values for this device.
default_computed_values(&self) -> &ComputedValues91     pub fn default_computed_values(&self) -> &ComputedValues {
92         // FIXME(bz): This isn't really right, but it's no more wrong
93         // than what we used to do.  See
94         // https://github.com/servo/servo/issues/14773 for fixing it properly.
95         ComputedValues::initial_values()
96     }
97 
98     /// Get the font size of the root element (for rem)
root_font_size(&self) -> CSSPixelLength99     pub fn root_font_size(&self) -> CSSPixelLength {
100         self.used_root_font_size.store(true, Ordering::Relaxed);
101         CSSPixelLength::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed)))
102     }
103 
104     /// Set the font size of the root element (for rem)
set_root_font_size(&self, size: CSSPixelLength)105     pub fn set_root_font_size(&self, size: CSSPixelLength) {
106         self.root_font_size
107             .store(size.px().to_bits(), Ordering::Relaxed)
108     }
109 
110     /// Get the quirks mode of the current device.
quirks_mode(&self) -> QuirksMode111     pub fn quirks_mode(&self) -> QuirksMode {
112         self.quirks_mode
113     }
114 
115     /// Sets the body text color for the "inherit color from body" quirk.
116     ///
117     /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
set_body_text_color(&self, _color: RGBA)118     pub fn set_body_text_color(&self, _color: RGBA) {
119         // Servo doesn't implement this quirk (yet)
120     }
121 
122     /// Whether a given animation name may be referenced from style.
animation_name_may_be_referenced(&self, _: &KeyframesName) -> bool123     pub fn animation_name_may_be_referenced(&self, _: &KeyframesName) -> bool {
124         // Assume it is, since we don't have any good way to prove it's not.
125         true
126     }
127 
128     /// Returns whether we ever looked up the root font size of the Device.
used_root_font_size(&self) -> bool129     pub fn used_root_font_size(&self) -> bool {
130         self.used_root_font_size.load(Ordering::Relaxed)
131     }
132 
133     /// Returns the viewport size of the current device in app units, needed,
134     /// among other things, to resolve viewport units.
135     #[inline]
au_viewport_size(&self) -> UntypedSize2D<Au>136     pub fn au_viewport_size(&self) -> UntypedSize2D<Au> {
137         Size2D::new(
138             Au::from_f32_px(self.viewport_size.width),
139             Au::from_f32_px(self.viewport_size.height),
140         )
141     }
142 
143     /// Like the above, but records that we've used viewport units.
au_viewport_size_for_viewport_unit_resolution(&self) -> UntypedSize2D<Au>144     pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> UntypedSize2D<Au> {
145         self.used_viewport_units.store(true, Ordering::Relaxed);
146         self.au_viewport_size()
147     }
148 
149     /// Whether viewport units were used since the last device change.
used_viewport_units(&self) -> bool150     pub fn used_viewport_units(&self) -> bool {
151         self.used_viewport_units.load(Ordering::Relaxed)
152     }
153 
154     /// Returns the device pixel ratio.
device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel>155     pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
156         self.device_pixel_ratio
157     }
158 
159     /// Take into account a viewport rule taken from the stylesheets.
account_for_viewport_rule(&mut self, constraints: &ViewportConstraints)160     pub fn account_for_viewport_rule(&mut self, constraints: &ViewportConstraints) {
161         self.viewport_size = constraints.size;
162     }
163 
164     /// Return the media type of the current device.
media_type(&self) -> MediaType165     pub fn media_type(&self) -> MediaType {
166         self.media_type.clone()
167     }
168 
169     /// Returns whether document colors are enabled.
use_document_colors(&self) -> bool170     pub fn use_document_colors(&self) -> bool {
171         true
172     }
173 
174     /// Returns the default background color.
default_background_color(&self) -> RGBA175     pub fn default_background_color(&self) -> RGBA {
176         RGBA::new(255, 255, 255, 255)
177     }
178 
179     /// Returns the default color color.
default_color(&self) -> RGBA180     pub fn default_color(&self) -> RGBA {
181         RGBA::new(0, 0, 0, 255)
182     }
183 
184     /// Returns safe area insets
safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel>185     pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
186         SideOffsets2D::zero()
187     }
188 }
189 
190 /// https://drafts.csswg.org/mediaqueries-4/#width
eval_width( device: &Device, value: Option<CSSPixelLength>, range_or_operator: Option<RangeOrOperator>, ) -> bool191 fn eval_width(
192     device: &Device,
193     value: Option<CSSPixelLength>,
194     range_or_operator: Option<RangeOrOperator>,
195 ) -> bool {
196     RangeOrOperator::evaluate(
197         range_or_operator,
198         value.map(Au::from),
199         device.au_viewport_size().width,
200     )
201 }
202 
203 #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
204 #[repr(u8)]
205 enum Scan {
206     Progressive,
207     Interlace,
208 }
209 
210 /// https://drafts.csswg.org/mediaqueries-4/#scan
eval_scan(_: &Device, _: Option<Scan>) -> bool211 fn eval_scan(_: &Device, _: Option<Scan>) -> bool {
212     // Since we doesn't support the 'tv' media type, the 'scan' feature never
213     // matches.
214     false
215 }
216 
217 lazy_static! {
218     /// A list with all the media features that Servo supports.
219     pub static ref MEDIA_FEATURES: [MediaFeatureDescription; 2] = [
220         feature!(
221             atom!("width"),
222             AllowsRanges::Yes,
223             Evaluator::Length(eval_width),
224             ParsingRequirements::empty(),
225         ),
226         feature!(
227             atom!("scan"),
228             AllowsRanges::No,
229             keyword_evaluator!(eval_scan, Scan),
230             ParsingRequirements::empty(),
231         ),
232     ];
233 }
234