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