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