Skip to main content

style/values/specified/
source_size_list.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//! https://html.spec.whatwg.org/multipage/#source-size-list
6
7use crate::device::Device;
8use crate::dom::AttributeTracker;
9use crate::parser::{Parse, ParserContext};
10use crate::queries::{FeatureType, QueryCondition};
11use crate::stylesheets::CustomMediaEvaluator;
12use crate::values::computed::{self, ToComputedValue};
13use crate::values::specified::length::LengthUnit;
14use crate::values::specified::{Length, NoCalcLength};
15use app_units::Au;
16use cssparser::{Delimiter, Parser, Token};
17use selectors::context::QuirksMode;
18use style_traits::ParseError;
19
20/// A value for a `<source-size>`:
21///
22/// https://html.spec.whatwg.org/multipage/#source-size
23#[derive(Clone, Debug)]
24pub struct SourceSize {
25    condition: QueryCondition,
26    value: Length,
27}
28
29impl Parse for SourceSize {
30    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
31        let condition = QueryCondition::parse(context, input, FeatureType::Media)?;
32        let value = Length::parse_non_negative(context, input)?;
33        Ok(Self { condition, value })
34    }
35}
36
37/// A value for a `<source-size-list>`:
38///
39/// https://html.spec.whatwg.org/multipage/#source-size-list
40#[derive(Clone, Debug)]
41pub struct SourceSizeList {
42    source_sizes: Vec<SourceSize>,
43    value: Option<Length>,
44}
45
46impl SourceSizeList {
47    /// Create an empty `SourceSizeList`, which can be used as a fall-back.
48    pub fn empty() -> Self {
49        Self {
50            source_sizes: vec![],
51            value: None,
52        }
53    }
54
55    /// Evaluate this <source-size-list> to get the final viewport length.
56    pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> Au {
57        computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
58            let matching_source_size = self.source_sizes.iter().find(|source_size| {
59                source_size
60                    .condition
61                    .matches(
62                        context,
63                        &mut CustomMediaEvaluator::none(),
64                        &mut AttributeTracker::new_dummy(),
65                    )
66                    .to_bool(/* unknown = */ false)
67            });
68
69            match matching_source_size {
70                Some(source_size) => source_size.value.to_computed_value(context),
71                None => match self.value {
72                    Some(ref v) => v.to_computed_value(context),
73                    None => Length::new(NoCalcLength::new(LengthUnit::Vw, 100.))
74                        .to_computed_value(context),
75                },
76            }
77        })
78        .into()
79    }
80}
81
82enum SourceSizeOrLength {
83    SourceSize(SourceSize),
84    Length(Length),
85}
86
87impl Parse for SourceSizeOrLength {
88    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
89        if let Ok(size) = input.try_parse(|input| SourceSize::parse(context, input)) {
90            return Ok(SourceSizeOrLength::SourceSize(size));
91        }
92
93        let length = Length::parse_non_negative(context, input)?;
94        Ok(SourceSizeOrLength::Length(length))
95    }
96}
97
98impl SourceSizeList {
99    /// NOTE(emilio): This doesn't match the grammar in the spec, see:
100    ///
101    /// https://html.spec.whatwg.org/multipage/#parsing-a-sizes-attribute
102    pub fn parse(context: &ParserContext, input: &mut Parser) -> Self {
103        let mut source_sizes = vec![];
104
105        loop {
106            let result = input.parse_until_before(Delimiter::Comma, |input| {
107                SourceSizeOrLength::parse(context, input)
108            });
109
110            match result {
111                Ok(SourceSizeOrLength::Length(value)) => {
112                    return Self {
113                        source_sizes,
114                        value: Some(value),
115                    };
116                },
117                Ok(SourceSizeOrLength::SourceSize(source_size)) => {
118                    source_sizes.push(source_size);
119                },
120                Err(..) => {},
121            }
122
123            match input.next() {
124                Ok(&Token::Comma) => {},
125                Err(..) => break,
126                _ => unreachable!(),
127            }
128        }
129
130        SourceSizeList {
131            source_sizes,
132            value: None,
133        }
134    }
135}