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