aboutsummaryrefslogtreecommitdiffstats
path: root/components/style/media_queries.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/style/media_queries.rs')
-rw-r--r--components/style/media_queries.rs131
1 files changed, 131 insertions, 0 deletions
diff --git a/components/style/media_queries.rs b/components/style/media_queries.rs
new file mode 100644
index 00000000000..2c7b6b4b08f
--- /dev/null
+++ b/components/style/media_queries.rs
@@ -0,0 +1,131 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::ascii::StrAsciiExt;
+use cssparser::parse_rule_list;
+use cssparser::ast::*;
+
+use errors::{ErrorLoggerIterator, log_css_error};
+use stylesheets::{CSSRule, CSSMediaRule, parse_style_rule, parse_nested_at_rule};
+use namespaces::NamespaceMap;
+use url::Url;
+
+
+pub struct MediaRule {
+ pub media_queries: MediaQueryList,
+ pub rules: Vec<CSSRule>,
+}
+
+
+pub struct MediaQueryList {
+ // "not all" is omitted from the list.
+ // An empty list never matches.
+ media_queries: Vec<MediaQuery>
+}
+
+// For now, this is a "Level 2 MQ", ie. a media type.
+pub struct MediaQuery {
+ media_type: MediaQueryType,
+ // TODO: Level 3 MQ expressions
+}
+
+
+pub enum MediaQueryType {
+ All, // Always true
+ MediaType(MediaType),
+}
+
+#[deriving(PartialEq)]
+pub enum MediaType {
+ Screen,
+ Print,
+}
+
+pub struct Device {
+ pub media_type: MediaType,
+ // TODO: Level 3 MQ data: viewport size, etc.
+}
+
+
+pub fn parse_media_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>,
+ namespaces: &NamespaceMap, base_url: &Url) {
+ let media_queries = parse_media_query_list(rule.prelude.as_slice());
+ let block = match rule.block {
+ Some(block) => block,
+ None => {
+ log_css_error(rule.location, "Invalid @media rule");
+ return
+ }
+ };
+ let mut rules = vec!();
+ for rule in ErrorLoggerIterator(parse_rule_list(block.move_iter())) {
+ match rule {
+ QualifiedRule(rule) => parse_style_rule(rule, &mut rules, namespaces, base_url),
+ AtRule(rule) => parse_nested_at_rule(
+ rule.name.as_slice().to_ascii_lower().as_slice(), rule, &mut rules, namespaces, base_url),
+ }
+ }
+ parent_rules.push(CSSMediaRule(MediaRule {
+ media_queries: media_queries,
+ rules: rules,
+ }))
+}
+
+
+pub fn parse_media_query_list(input: &[ComponentValue]) -> MediaQueryList {
+ let iter = &mut input.skip_whitespace();
+ let mut next = iter.next();
+ if next.is_none() {
+ return MediaQueryList{ media_queries: vec!(MediaQuery{media_type: All}) }
+ }
+ let mut queries = vec!();
+ loop {
+ let mq = match next {
+ Some(&Ident(ref value)) => {
+ match value.as_slice().to_ascii_lower().as_slice() {
+ "screen" => Some(MediaQuery{ media_type: MediaType(Screen) }),
+ "print" => Some(MediaQuery{ media_type: MediaType(Print) }),
+ "all" => Some(MediaQuery{ media_type: All }),
+ _ => None
+ }
+ },
+ _ => None
+ };
+ match iter.next() {
+ None => {
+ for mq in mq.move_iter() {
+ queries.push(mq);
+ }
+ return MediaQueryList{ media_queries: queries }
+ },
+ Some(&Comma) => {
+ for mq in mq.move_iter() {
+ queries.push(mq);
+ }
+ },
+ // Ingnore this comma-separated part
+ _ => loop {
+ match iter.next() {
+ Some(&Comma) => break,
+ None => return MediaQueryList{ media_queries: queries },
+ _ => (),
+ }
+ },
+ }
+ next = iter.next();
+ }
+}
+
+
+impl MediaQueryList {
+ pub fn evaluate(&self, device: &Device) -> bool {
+ self.media_queries.iter().any(|mq| {
+ match mq.media_type {
+ MediaType(media_type) => media_type == device.media_type,
+ All => true,
+ }
+ // TODO: match Level 3 expressions
+ })
+ }
+}