1use crate::color::AbsoluteColor;
8use crate::context::QuirksMode;
9use crate::custom_properties::CssEnvironment;
10use crate::derives::*;
11use crate::font_metrics::FontMetrics;
12use crate::logical_geometry::WritingMode;
13use crate::media_queries::MediaType;
14use crate::properties::style_structs::Font;
15use crate::properties::ComputedValues;
16use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription};
17use crate::queries::values::PrefersColorScheme;
18use crate::values::computed::font::GenericFontFamily;
19use crate::values::computed::{
20 CSSPixelLength, Context, Length, LineHeight, NonNegativeLength, Resolution,
21};
22use crate::values::specified::color::{ColorSchemeFlags, ForcedColors, SystemColor};
23use crate::values::specified::font::{
24 QueryFontMetricsFlags, FONT_MEDIUM_CAP_PX, FONT_MEDIUM_CH_PX, FONT_MEDIUM_EX_PX,
25 FONT_MEDIUM_IC_PX, FONT_MEDIUM_LINE_HEIGHT_PX, FONT_MEDIUM_PX,
26};
27use crate::values::specified::ViewportVariant;
28use crate::values::KeyframesName;
29use app_units::{Au, AU_PER_PX};
30use euclid::default::Size2D as UntypedSize2D;
31use euclid::{Scale, SideOffsets2D, Size2D};
32use mime::Mime;
33use parking_lot::RwLock;
34use servo_arc::Arc;
35use std::fmt::Debug;
36use std::mem;
37use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
38use style_traits::{CSSPixel, DevicePixel};
39
40pub trait FontMetricsProvider: Debug + Sync {
43 fn query_font_metrics(
45 &self,
46 vertical: bool,
47 font: &Font,
48 base_size: CSSPixelLength,
49 flags: QueryFontMetricsFlags,
50 ) -> FontMetrics;
51 fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length;
53}
54
55#[derive(Debug, MallocSizeOf)]
66pub struct Device {
67 media_type: MediaType,
69 viewport_size: Size2D<f32, CSSPixel>,
71 device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
73 #[ignore_malloc_size_of = "Pure stack type"]
75 quirks_mode: QuirksMode,
76
77 #[ignore_malloc_size_of = "Arc"]
80 root_style: RwLock<Arc<ComputedValues>>,
81 #[ignore_malloc_size_of = "Pure stack type"]
83 root_font_size: AtomicU32,
84 #[ignore_malloc_size_of = "Pure stack type"]
86 root_line_height: AtomicU32,
87 #[ignore_malloc_size_of = "Pure stack type"]
89 root_font_metrics_ex: AtomicU32,
90 #[ignore_malloc_size_of = "Pure stack type"]
92 root_font_metrics_cap: AtomicU32,
93 #[ignore_malloc_size_of = "Pure stack type"]
95 root_font_metrics_ch: AtomicU32,
96 #[ignore_malloc_size_of = "Pure stack type"]
98 root_font_metrics_ic: AtomicU32,
99 #[ignore_malloc_size_of = "Pure stack type"]
102 used_root_font_size: AtomicBool,
103 #[ignore_malloc_size_of = "Pure stack type"]
106 used_root_line_height: AtomicBool,
107 #[ignore_malloc_size_of = "Pure stack type"]
111 used_root_font_metrics: RwLock<bool>,
112 used_font_metrics: AtomicBool,
114 #[ignore_malloc_size_of = "Pure stack type"]
116 used_viewport_units: AtomicBool,
117 #[ignore_malloc_size_of = "Pure stack type"]
119 prefers_color_scheme: PrefersColorScheme,
120 environment: CssEnvironment,
123 #[ignore_malloc_size_of = "Owned by embedder"]
125 font_metrics_provider: Box<dyn FontMetricsProvider>,
126 #[ignore_malloc_size_of = "Arc is shared"]
128 default_computed_values: Arc<ComputedValues>,
129}
130
131impl Device {
132 pub fn new(
134 media_type: MediaType,
135 quirks_mode: QuirksMode,
136 viewport_size: Size2D<f32, CSSPixel>,
137 device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
138 font_metrics_provider: Box<dyn FontMetricsProvider>,
139 default_computed_values: Arc<ComputedValues>,
140 prefers_color_scheme: PrefersColorScheme,
141 ) -> Device {
142 let default_values =
143 ComputedValues::initial_values_with_font_override(Font::initial_values());
144 let root_style = RwLock::new(Arc::clone(&default_values));
145 Device {
146 media_type,
147 viewport_size,
148 device_pixel_ratio,
149 quirks_mode,
150 root_style,
151 root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),
152 root_line_height: AtomicU32::new(FONT_MEDIUM_LINE_HEIGHT_PX.to_bits()),
153 root_font_metrics_ex: AtomicU32::new(FONT_MEDIUM_EX_PX.to_bits()),
154 root_font_metrics_cap: AtomicU32::new(FONT_MEDIUM_CAP_PX.to_bits()),
155 root_font_metrics_ch: AtomicU32::new(FONT_MEDIUM_CH_PX.to_bits()),
156 root_font_metrics_ic: AtomicU32::new(FONT_MEDIUM_IC_PX.to_bits()),
157 used_root_font_size: AtomicBool::new(false),
158 used_root_line_height: AtomicBool::new(false),
159 used_root_font_metrics: RwLock::new(false),
160 used_font_metrics: AtomicBool::new(false),
161 used_viewport_units: AtomicBool::new(false),
162 prefers_color_scheme,
163 environment: CssEnvironment,
164 font_metrics_provider,
165 default_computed_values,
166 }
167 }
168
169 #[inline]
171 pub fn environment(&self) -> &CssEnvironment {
172 &self.environment
173 }
174
175 pub fn default_computed_values(&self) -> &ComputedValues {
177 &self.default_computed_values
178 }
179
180 pub fn set_root_style(&self, style: &Arc<ComputedValues>) {
183 *self.root_style.write() = style.clone();
184 }
185
186 pub fn root_font_size(&self) -> CSSPixelLength {
188 self.used_root_font_size.store(true, Ordering::Relaxed);
189 CSSPixelLength::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed)))
190 }
191
192 pub fn set_root_font_size(&self, size: f32) {
194 self.root_font_size.store(size.to_bits(), Ordering::Relaxed)
195 }
196
197 pub fn root_line_height(&self) -> CSSPixelLength {
199 self.used_root_line_height.store(true, Ordering::Relaxed);
200 CSSPixelLength::new(f32::from_bits(
201 self.root_line_height.load(Ordering::Relaxed),
202 ))
203 }
204
205 pub fn set_root_line_height(&self, size: f32) {
207 self.root_line_height
208 .store(size.to_bits(), Ordering::Relaxed);
209 }
210
211 pub fn root_font_metrics_ex(&self) -> Length {
213 self.ensure_root_font_metrics_updated();
214 Length::new(f32::from_bits(
215 self.root_font_metrics_ex.load(Ordering::Relaxed),
216 ))
217 }
218
219 pub fn set_root_font_metrics_ex(&self, size: f32) -> bool {
221 let size = size.to_bits();
222 let previous = self.root_font_metrics_ex.swap(size, Ordering::Relaxed);
223 previous != size
224 }
225
226 pub fn root_font_metrics_cap(&self) -> Length {
228 self.ensure_root_font_metrics_updated();
229 Length::new(f32::from_bits(
230 self.root_font_metrics_cap.load(Ordering::Relaxed),
231 ))
232 }
233
234 pub fn set_root_font_metrics_cap(&self, size: f32) -> bool {
236 let size = size.to_bits();
237 let previous = self.root_font_metrics_cap.swap(size, Ordering::Relaxed);
238 previous != size
239 }
240
241 pub fn root_font_metrics_ch(&self) -> Length {
243 self.ensure_root_font_metrics_updated();
244 Length::new(f32::from_bits(
245 self.root_font_metrics_ch.load(Ordering::Relaxed),
246 ))
247 }
248
249 pub fn set_root_font_metrics_ch(&self, size: f32) -> bool {
251 let size = size.to_bits();
252 let previous = self.root_font_metrics_ch.swap(size, Ordering::Relaxed);
253 previous != size
254 }
255
256 pub fn root_font_metrics_ic(&self) -> Length {
258 self.ensure_root_font_metrics_updated();
259 Length::new(f32::from_bits(
260 self.root_font_metrics_ic.load(Ordering::Relaxed),
261 ))
262 }
263
264 pub fn set_root_font_metrics_ic(&self, size: f32) -> bool {
266 let size = size.to_bits();
267 let previous = self.root_font_metrics_ic.swap(size, Ordering::Relaxed);
268 previous != size
269 }
270
271 pub fn calc_line_height(
275 &self,
276 font: &crate::properties::style_structs::Font,
277 _writing_mode: WritingMode,
278 _element: Option<()>,
279 ) -> NonNegativeLength {
280 (match font.line_height {
281 LineHeight::Normal => CSSPixelLength::new(0.),
283 LineHeight::Number(number) => font.font_size.computed_size() * number.0,
284 LineHeight::Length(length) => length.0,
285 })
286 .into()
287 }
288
289 pub fn quirks_mode(&self) -> QuirksMode {
291 self.quirks_mode
292 }
293
294 pub fn set_body_text_color(&self, _color: AbsoluteColor) {
298 }
300
301 pub fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length {
303 self.font_metrics_provider.base_size_for_generic(generic)
304 }
305
306 pub fn animation_name_may_be_referenced(&self, _: &KeyframesName) -> bool {
308 true
310 }
311
312 pub fn used_root_font_size(&self) -> bool {
314 self.used_root_font_size.load(Ordering::Relaxed)
315 }
316
317 pub fn used_root_line_height(&self) -> bool {
319 self.used_root_line_height.load(Ordering::Relaxed)
320 }
321
322 pub fn used_root_font_metrics(&self) -> bool {
324 *self.used_root_font_metrics.read()
325 }
326
327 pub fn used_font_metrics(&self) -> bool {
329 self.used_font_metrics.load(Ordering::Relaxed)
330 }
331
332 pub fn viewport_size(&self) -> Size2D<f32, CSSPixel> {
334 self.viewport_size
335 }
336
337 pub fn set_viewport_size(&mut self, viewport_size: Size2D<f32, CSSPixel>) {
343 self.viewport_size = viewport_size;
344 }
345
346 #[inline]
349 pub fn au_viewport_size(&self) -> UntypedSize2D<Au> {
350 Size2D::new(
351 Au::from_f32_px(self.viewport_size.width),
352 Au::from_f32_px(self.viewport_size.height),
353 )
354 }
355
356 pub fn au_viewport_size_for_viewport_unit_resolution(
358 &self,
359 _: ViewportVariant,
360 ) -> UntypedSize2D<Au> {
361 self.used_viewport_units.store(true, Ordering::Relaxed);
362 self.au_viewport_size()
365 }
366
367 pub fn used_viewport_units(&self) -> bool {
369 self.used_viewport_units.load(Ordering::Relaxed)
370 }
371
372 pub fn app_units_per_device_pixel(&self) -> i32 {
374 (AU_PER_PX as f32 / self.device_pixel_ratio.0) as i32
375 }
376
377 pub fn device_pixel_ratio_ignoring_full_zoom(&self) -> Scale<f32, CSSPixel, DevicePixel> {
379 self.device_pixel_ratio
380 }
381
382 pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
384 self.device_pixel_ratio
385 }
386
387 pub fn set_device_pixel_ratio(
393 &mut self,
394 device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
395 ) {
396 self.device_pixel_ratio = device_pixel_ratio;
397 }
398
399 pub fn scrollbar_inline_size(&self) -> CSSPixelLength {
401 CSSPixelLength::new(0.0)
403 }
404
405 pub fn query_font_metrics(
407 &self,
408 vertical: bool,
409 font: &Font,
410 base_size: CSSPixelLength,
411 flags: QueryFontMetricsFlags,
412 track_usage: bool,
413 ) -> FontMetrics {
414 if track_usage {
415 self.used_font_metrics.store(true, Ordering::Relaxed);
416 }
417 self.font_metrics_provider
418 .query_font_metrics(vertical, font, base_size, flags)
419 }
420
421 fn ensure_root_font_metrics_updated(&self) {
422 let mut guard = self.used_root_font_metrics.write();
423 let previously_computed = mem::replace(&mut *guard, true);
424 if !previously_computed {
425 self.update_root_font_metrics();
426 }
427 }
428
429 pub fn update_root_font_metrics(&self) -> bool {
432 let root_style = self.root_style.read();
433 let root_effective_zoom = (*root_style).effective_zoom;
434 let root_font_size = (*root_style).get_font().clone_font_size().computed_size();
435
436 let root_font_metrics = self.query_font_metrics(
437 (*root_style).writing_mode.is_upright(),
438 &(*root_style).get_font(),
439 root_font_size,
440 QueryFontMetricsFlags::USE_USER_FONT_SET
441 | QueryFontMetricsFlags::NEEDS_CH
442 | QueryFontMetricsFlags::NEEDS_IC,
443 false,
444 );
445
446 let mut root_font_metrics_changed = false;
447 root_font_metrics_changed |= self.set_root_font_metrics_ex(
448 root_effective_zoom.unzoom(root_font_metrics.x_height_or_default(root_font_size).px()),
449 );
450 root_font_metrics_changed |= self.set_root_font_metrics_ch(
451 root_effective_zoom.unzoom(
452 root_font_metrics
453 .zero_advance_measure_or_default(
454 root_font_size,
455 (*root_style).writing_mode.is_upright(),
456 )
457 .px(),
458 ),
459 );
460 root_font_metrics_changed |= self.set_root_font_metrics_cap(
461 root_effective_zoom.unzoom(root_font_metrics.cap_height_or_default().px()),
462 );
463 root_font_metrics_changed |= self.set_root_font_metrics_ic(
464 root_effective_zoom.unzoom(root_font_metrics.ic_width_or_default(root_font_size).px()),
465 );
466
467 root_font_metrics_changed
468 }
469
470 pub fn media_type(&self) -> MediaType {
472 self.media_type.clone()
473 }
474
475 pub fn forced_colors(&self) -> ForcedColors {
477 ForcedColors::None
478 }
479
480 pub fn default_background_color(&self) -> AbsoluteColor {
482 AbsoluteColor::WHITE
483 }
484
485 pub fn default_color(&self) -> AbsoluteColor {
487 AbsoluteColor::BLACK
488 }
489
490 pub fn set_color_scheme(&mut self, new_color_scheme: PrefersColorScheme) {
496 self.prefers_color_scheme = new_color_scheme;
497 }
498
499 pub fn color_scheme(&self) -> PrefersColorScheme {
501 self.prefers_color_scheme
502 }
503
504 pub(crate) fn is_dark_color_scheme(&self, _: ColorSchemeFlags) -> bool {
505 false
506 }
507
508 pub(crate) fn system_color(
509 &self,
510 system_color: SystemColor,
511 color_scheme_flags: ColorSchemeFlags,
512 ) -> AbsoluteColor {
513 fn srgb(r: u8, g: u8, b: u8) -> AbsoluteColor {
514 AbsoluteColor::srgb_legacy(r, g, b, 1f32)
515 }
516
517 if self.is_dark_color_scheme(color_scheme_flags) {
520 match system_color {
522 SystemColor::Accentcolor => srgb(10, 132, 255),
523 SystemColor::Accentcolortext => srgb(255, 255, 255),
524 SystemColor::Activetext => srgb(255, 0, 0),
525 SystemColor::Linktext => srgb(158, 158, 255),
526 SystemColor::Visitedtext => srgb(208, 173, 240),
527 SystemColor::Buttonborder
528 | SystemColor::Activeborder
530 | SystemColor::Inactiveborder
531 | SystemColor::Threeddarkshadow
532 | SystemColor::Threedshadow
533 | SystemColor::Windowframe => srgb(255, 255, 255),
534 SystemColor::Buttonface
535 | SystemColor::Buttonhighlight
537 | SystemColor::Buttonshadow
538 | SystemColor::Threedface
539 | SystemColor::Threedhighlight
540 | SystemColor::Threedlightshadow => srgb(107, 107, 107),
541 SystemColor::Buttontext => srgb(245, 245, 245),
542 SystemColor::Canvas
543 | SystemColor::Activecaption
545 | SystemColor::Appworkspace
546 | SystemColor::Background
547 | SystemColor::Inactivecaption
548 | SystemColor::Infobackground
549 | SystemColor::Menu
550 | SystemColor::Scrollbar
551 | SystemColor::Window => srgb(30, 30, 30),
552 SystemColor::Canvastext
553 | SystemColor::Captiontext
555 | SystemColor::Infotext
556 | SystemColor::Menutext
557 | SystemColor::Windowtext => srgb(232, 232, 232),
558 SystemColor::Field => srgb(45, 45, 45),
559 SystemColor::Fieldtext => srgb(240, 240, 240),
560 SystemColor::Graytext
561 | SystemColor::Inactivecaptiontext => srgb(155, 155, 155),
563 SystemColor::Highlight => srgb(38, 79, 120),
564 SystemColor::Highlighttext => srgb(255, 255, 255),
565 SystemColor::Mark => srgb(102, 92, 0),
566 SystemColor::Marktext => srgb(255, 255, 255),
567 SystemColor::Selecteditem => srgb(153, 200, 255),
568 SystemColor::Selecteditemtext => srgb(59, 59, 59),
569 }
570 } else {
571 match system_color {
572 SystemColor::Accentcolor => srgb(0, 102, 204),
573 SystemColor::Accentcolortext => srgb(255, 255, 255),
574 SystemColor::Activetext => srgb(238, 0, 0),
575 SystemColor::Linktext => srgb(0, 0, 238),
576 SystemColor::Visitedtext => srgb(85, 26, 139),
577 SystemColor::Buttonborder
578 | SystemColor::Activeborder
580 | SystemColor::Inactiveborder
581 | SystemColor::Threeddarkshadow
582 | SystemColor::Threedshadow
583 | SystemColor::Windowframe => srgb(169, 169, 169),
584 SystemColor::Buttonface
585 | SystemColor::Buttonhighlight
587 | SystemColor::Buttonshadow
588 | SystemColor::Threedface
589 | SystemColor::Threedhighlight
590 | SystemColor::Threedlightshadow => srgb(220, 220, 220),
591 SystemColor::Buttontext => srgb(0, 0, 0),
592 SystemColor::Canvas
593 | SystemColor::Activecaption
595 | SystemColor::Appworkspace
596 | SystemColor::Background
597 | SystemColor::Inactivecaption
598 | SystemColor::Infobackground
599 | SystemColor::Menu
600 | SystemColor::Scrollbar
601 | SystemColor::Window => srgb(255, 255, 255),
602 SystemColor::Canvastext
603 | SystemColor::Captiontext
605 | SystemColor::Infotext
606 | SystemColor::Menutext
607 | SystemColor::Windowtext => srgb(0, 0, 0),
608 SystemColor::Field => srgb(255, 255, 255),
609 SystemColor::Fieldtext => srgb(0, 0, 0),
610 SystemColor::Graytext
611 | SystemColor::Inactivecaptiontext => srgb(109, 109, 109),
613 SystemColor::Highlight => srgb(0, 65, 198),
614 SystemColor::Highlighttext => srgb(0, 0, 0),
615 SystemColor::Mark => srgb(255, 235, 59),
616 SystemColor::Marktext => srgb(0, 0, 0),
617 SystemColor::Selecteditem => srgb(0, 102, 204),
618 SystemColor::Selecteditemtext => srgb(255, 255, 255),
619 }
620 }
621 }
622
623 pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
625 SideOffsets2D::zero()
626 }
627
628 pub fn is_supported_mime_type(&self, mime_type: &str) -> bool {
630 match mime_type.parse::<Mime>() {
631 Ok(m) => {
632 m == mime::IMAGE_BMP
635 || m == mime::IMAGE_GIF
636 || m == mime::IMAGE_PNG
637 || m == mime::IMAGE_JPEG
638 || m == "image/x-icon"
639 || m == "image/webp"
640 },
641 _ => false,
642 }
643 }
644
645 #[inline]
647 pub fn chrome_rules_enabled_for_document(&self) -> bool {
648 false
649 }
650}
651
652fn eval_width(context: &Context) -> CSSPixelLength {
654 CSSPixelLength::new(context.device().au_viewport_size().width.to_f32_px())
655}
656
657#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
658#[repr(u8)]
659enum Scan {
660 Progressive,
661 Interlace,
662}
663
664fn eval_scan(_: &Context, _: Option<Scan>) -> bool {
666 false
669}
670
671fn eval_resolution(context: &Context) -> Resolution {
673 Resolution::from_dppx(context.device().device_pixel_ratio.0)
674}
675
676fn eval_device_pixel_ratio(context: &Context) -> f32 {
678 eval_resolution(context).dppx()
679}
680
681fn eval_prefers_color_scheme(context: &Context, query_value: Option<PrefersColorScheme>) -> bool {
682 match query_value {
683 Some(v) => context.device().prefers_color_scheme == v,
684 None => true,
685 }
686}
687
688pub static MEDIA_FEATURES: [QueryFeatureDescription; 6] = [
690 feature!(
691 atom!("width"),
692 AllowsRanges::Yes,
693 Evaluator::Length(eval_width),
694 FeatureFlags::empty(),
695 ),
696 feature!(
697 atom!("scan"),
698 AllowsRanges::No,
699 keyword_evaluator!(eval_scan, Scan),
700 FeatureFlags::empty(),
701 ),
702 feature!(
703 atom!("resolution"),
704 AllowsRanges::Yes,
705 Evaluator::Resolution(eval_resolution),
706 FeatureFlags::empty(),
707 ),
708 feature!(
709 atom!("device-pixel-ratio"),
710 AllowsRanges::Yes,
711 Evaluator::Float(eval_device_pixel_ratio),
712 FeatureFlags::WEBKIT_PREFIX,
713 ),
714 feature!(
715 atom!("-moz-device-pixel-ratio"),
716 AllowsRanges::Yes,
717 Evaluator::Float(eval_device_pixel_ratio),
718 FeatureFlags::empty(),
719 ),
720 feature!(
721 atom!("prefers-color-scheme"),
722 AllowsRanges::No,
723 keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme),
724 FeatureFlags::empty(),
725 ),
726];