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::values::computed::{self, ToComputedValue};
11use crate::values::specified::{Length, NoCalcLength, ViewportPercentageLength};
12use app_units::Au;
13use cssparser::{Delimiter, Parser, Token};
14use selectors::context::QuirksMode;
15use style_traits::ParseError;
16
17/// A value for a `<source-size>`:
18///
19/// https://html.spec.whatwg.org/multipage/#source-size
20#[derive(Debug)]
21pub struct SourceSize {
22    condition: QueryCondition,
23    value: Length,
24}
25
26impl Parse for SourceSize {
27    fn parse<'i, 't>(
28        context: &ParserContext,
29        input: &mut Parser<'i, 't>,
30    ) -> Result<Self, ParseError<'i>> {
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(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(context)
62                    .to_bool(/* unknown = */ false)
63            });
64
65            match matching_source_size {
66                Some(source_size) => source_size.value.to_computed_value(context),
67                None => match self.value {
68                    Some(ref v) => v.to_computed_value(context),
69                    None => Length::NoCalc(NoCalcLength::ViewportPercentage(
70                        ViewportPercentageLength::Vw(100.),
71                    ))
72                    .to_computed_value(context),
73                },
74            }
75        })
76        .into()
77    }
78}
79
80enum SourceSizeOrLength {
81    SourceSize(SourceSize),
82    Length(Length),
83}
84
85impl Parse for SourceSizeOrLength {
86    fn parse<'i, 't>(
87        context: &ParserContext,
88        input: &mut Parser<'i, 't>,
89    ) -> Result<Self, ParseError<'i>> {
90        if let Ok(size) = input.try_parse(|input| SourceSize::parse(context, input)) {
91            return Ok(SourceSizeOrLength::SourceSize(size));
92        }
93
94        let length = Length::parse_non_negative(context, input)?;
95        Ok(SourceSizeOrLength::Length(length))
96    }
97}
98
99impl SourceSizeList {
100    /// NOTE(emilio): This doesn't match the grammar in the spec, see:
101    ///
102    /// https://html.spec.whatwg.org/multipage/#parsing-a-sizes-attribute
103    pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Self {
104        let mut source_sizes = vec![];
105
106        loop {
107            let result = input.parse_until_before(Delimiter::Comma, |input| {
108                SourceSizeOrLength::parse(context, input)
109            });
110
111            match result {
112                Ok(SourceSizeOrLength::Length(value)) => {
113                    return Self {
114                        source_sizes,
115                        value: Some(value),
116                    };
117                },
118                Ok(SourceSizeOrLength::SourceSize(source_size)) => {
119                    source_sizes.push(source_size);
120                },
121                Err(..) => {},
122            }
123
124            match input.next() {
125                Ok(&Token::Comma) => {},
126                Err(..) => break,
127                _ => unreachable!(),
128            }
129        }
130
131        SourceSizeList {
132            source_sizes,
133            value: None,
134        }
135    }
136}