style/media_queries/
media_query.rs1use crate::parser::ParserContext;
10use crate::queries::{FeatureFlags, FeatureType, QueryCondition};
11use crate::str::string_as_ascii_lowercase;
12use crate::values::CustomIdent;
13use crate::Atom;
14use cssparser::Parser;
15use std::fmt::{self, Write};
16use style_traits::{CssWriter, ParseError, ToCss};
17
18#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
20pub enum Qualifier {
21 Only,
24 Not,
27}
28
29#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
31pub struct MediaType(pub CustomIdent);
32
33impl MediaType {
34 pub fn screen() -> Self {
36 MediaType(CustomIdent(atom!("screen")))
37 }
38
39 pub fn print() -> Self {
41 MediaType(CustomIdent(atom!("print")))
42 }
43
44 fn parse(name: &str) -> Result<Self, ()> {
45 match_ignore_ascii_case! { name,
52 "not" | "or" | "and" | "only" | "layer" => Err(()),
53 _ => Ok(MediaType(CustomIdent(Atom::from(string_as_ascii_lowercase(name))))),
54 }
55 }
56}
57
58#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
62pub struct MediaQuery {
63 pub qualifier: Option<Qualifier>,
65 pub media_type: MediaQueryType,
67 pub condition: Option<QueryCondition>,
70}
71
72impl ToCss for MediaQuery {
73 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
74 where
75 W: Write,
76 {
77 if let Some(qual) = self.qualifier {
78 qual.to_css(dest)?;
79 dest.write_char(' ')?;
80 }
81
82 match self.media_type {
83 MediaQueryType::All => {
84 if self.qualifier.is_some() || self.condition.is_none() {
90 dest.write_str("all")?;
91 }
92 },
93 MediaQueryType::Concrete(MediaType(ref desc)) => desc.to_css(dest)?,
94 }
95
96 let condition = match self.condition {
97 Some(ref c) => c,
98 None => return Ok(()),
99 };
100
101 if self.media_type != MediaQueryType::All || self.qualifier.is_some() {
102 dest.write_str(" and ")?;
103 }
104
105 condition.to_css(dest)
106 }
107}
108
109impl MediaQuery {
110 pub fn never_matching() -> Self {
113 Self {
114 qualifier: Some(Qualifier::Not),
115 media_type: MediaQueryType::All,
116 condition: None,
117 }
118 }
119
120 pub fn is_viewport_dependent(&self) -> bool {
122 self.condition.as_ref().map_or(false, |c| {
123 return c
124 .cumulative_flags()
125 .contains(FeatureFlags::VIEWPORT_DEPENDENT);
126 })
127 }
128
129 pub fn parse<'i, 't>(
133 context: &ParserContext,
134 input: &mut Parser<'i, 't>,
135 ) -> Result<Self, ParseError<'i>> {
136 let (qualifier, explicit_media_type) = input
137 .try_parse(|input| -> Result<_, ()> {
138 let qualifier = input.try_parse(Qualifier::parse).ok();
139 let ident = input.expect_ident().map_err(|_| ())?;
140 let media_type = MediaQueryType::parse(&ident)?;
141 Ok((qualifier, Some(media_type)))
142 })
143 .unwrap_or_default();
144
145 let condition = if explicit_media_type.is_none() {
146 Some(QueryCondition::parse(context, input, FeatureType::Media)?)
147 } else if input.try_parse(|i| i.expect_ident_matching("and")).is_ok() {
148 Some(QueryCondition::parse_disallow_or(
149 context,
150 input,
151 FeatureType::Media,
152 )?)
153 } else {
154 None
155 };
156
157 let media_type = explicit_media_type.unwrap_or(MediaQueryType::All);
158 Ok(Self {
159 qualifier,
160 media_type,
161 condition,
162 })
163 }
164}
165
166#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
168pub enum MediaQueryType {
169 All,
171 Concrete(MediaType),
173}
174
175impl MediaQueryType {
176 fn parse(ident: &str) -> Result<Self, ()> {
177 match_ignore_ascii_case! { ident,
178 "all" => return Ok(MediaQueryType::All),
179 _ => (),
180 };
181
182 MediaType::parse(ident).map(MediaQueryType::Concrete)
184 }
185
186 pub fn matches(&self, other: MediaType) -> bool {
188 match *self {
189 MediaQueryType::All => true,
190 MediaQueryType::Concrete(ref known_type) => *known_type == other,
191 }
192 }
193}