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().is_some_and(|c| {
124 c.cumulative_flags()
125 .contains(FeatureFlags::VIEWPORT_DEPENDENT)
126 })
127 }
128
129 pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
133 let (qualifier, explicit_media_type) = input
134 .try_parse(|input| -> Result<_, ()> {
135 let qualifier = input.try_parse(Qualifier::parse).ok();
136 let ident = input.expect_ident().map_err(|_| ())?;
137 let media_type = MediaQueryType::parse(ident)?;
138 Ok((qualifier, Some(media_type)))
139 })
140 .unwrap_or_default();
141
142 let condition = if explicit_media_type.is_none() {
143 Some(QueryCondition::parse(context, input, FeatureType::Media)?)
144 } else if input.try_parse(|i| i.expect_ident_matching("and")).is_ok() {
145 Some(QueryCondition::parse_disallow_or(
146 context,
147 input,
148 FeatureType::Media,
149 )?)
150 } else {
151 None
152 };
153
154 let media_type = explicit_media_type.unwrap_or(MediaQueryType::All);
155 Ok(Self {
156 qualifier,
157 media_type,
158 condition,
159 })
160 }
161}
162
163#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
165pub enum MediaQueryType {
166 All,
168 Concrete(MediaType),
170}
171
172impl MediaQueryType {
173 fn parse(ident: &str) -> Result<Self, ()> {
174 match_ignore_ascii_case! { ident,
175 "all" => return Ok(MediaQueryType::All),
176 _ => (),
177 };
178
179 MediaType::parse(ident).map(MediaQueryType::Concrete)
181 }
182
183 pub fn matches(&self, other: MediaType) -> bool {
185 match *self {
186 MediaQueryType::All => true,
187 MediaQueryType::Concrete(ref known_type) => *known_type == other,
188 }
189 }
190}