mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Add @media and media type parsing (no Media Queries yet.)
This commit is contained in:
parent
9b22acf2f3
commit
5758c31df7
3 changed files with 148 additions and 11 deletions
125
media_queries.rs
Normal file
125
media_queries.rs
Normal file
|
@ -0,0 +1,125 @@
|
|||
/* 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::to_ascii_lower;
|
||||
use cssparser::*;
|
||||
use errors::{ErrorLoggerIterator, log_css_error};
|
||||
use stylesheets::{CSSRule, CSSMediaRule, parse_style_rule, parse_nested_at_rule};
|
||||
use namespaces::NamespaceMap;
|
||||
|
||||
|
||||
pub struct MediaRule {
|
||||
media_queries: MediaQueryList,
|
||||
rules: ~[CSSRule],
|
||||
}
|
||||
|
||||
|
||||
pub struct MediaQueryList {
|
||||
// "not all" is omitted from the list.
|
||||
// An empty list never matches.
|
||||
media_queries: ~[MediaQuery]
|
||||
}
|
||||
|
||||
// For now, this is a "Level 2 MQ", ie. a media type.
|
||||
struct MediaQuery {
|
||||
media_type: MediaQueryType,
|
||||
// TODO: Level 3 MQ expressions
|
||||
}
|
||||
|
||||
|
||||
enum MediaQueryType {
|
||||
All, // Always true
|
||||
MediaType(MediaType),
|
||||
}
|
||||
|
||||
#[deriving(Eq)]
|
||||
pub enum MediaType {
|
||||
Screen,
|
||||
Print,
|
||||
}
|
||||
|
||||
pub struct Device {
|
||||
media_type: MediaType,
|
||||
// TODO: Level 3 MQ data: viewport size, etc.
|
||||
}
|
||||
|
||||
|
||||
pub fn parse_media_rule(rule: AtRule, parent_rules: &mut ~[CSSRule],
|
||||
namespaces: &NamespaceMap) {
|
||||
let media_queries = parse_media_query_list(rule.prelude);
|
||||
let block = match rule.block {
|
||||
Some(block) => block,
|
||||
None => {
|
||||
log_css_error(rule.location, "Invalid @media rule");
|
||||
return
|
||||
}
|
||||
};
|
||||
let mut rules = ~[];
|
||||
for rule in ErrorLoggerIterator(parse_rule_list(block.consume_iter())) {
|
||||
match rule {
|
||||
QualifiedRule(rule) => parse_style_rule(rule, &mut rules, namespaces),
|
||||
AtRule(rule) => parse_nested_at_rule(
|
||||
to_ascii_lower(rule.name), rule, &mut rules, namespaces),
|
||||
}
|
||||
}
|
||||
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: ~[MediaQuery{media_type: All}] }
|
||||
}
|
||||
let mut queries = ~[];
|
||||
loop {
|
||||
let mq = match next {
|
||||
Some(&Ident(ref value)) => {
|
||||
let media_type: &str = to_ascii_lower(value.as_slice());
|
||||
match media_type {
|
||||
"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 => {
|
||||
mq.map_move(|mq| queries.push(mq));
|
||||
return MediaQueryList{ media_queries: queries }
|
||||
},
|
||||
Some(&Comma) => {
|
||||
mq.map_move(|mq| 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 {
|
||||
do 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,3 +13,4 @@ pub mod errors;
|
|||
pub mod selectors;
|
||||
pub mod properties;
|
||||
pub mod namespaces;
|
||||
pub mod media_queries;
|
||||
|
|
|
@ -9,6 +9,7 @@ use selectors;
|
|||
use properties;
|
||||
use errors::{ErrorLoggerIterator, log_css_error};
|
||||
use namespaces::{NamespaceMap, parse_namespace_rule};
|
||||
use media_queries::{MediaRule, parse_media_rule};
|
||||
|
||||
|
||||
pub struct Stylesheet {
|
||||
|
@ -19,7 +20,7 @@ pub struct Stylesheet {
|
|||
|
||||
pub enum CSSRule {
|
||||
CSSStyleRule(StyleRule),
|
||||
// CSSMediaRule(MediaRule),
|
||||
CSSMediaRule(MediaRule),
|
||||
}
|
||||
|
||||
|
||||
|
@ -42,9 +43,13 @@ fn parse_stylesheet(css: &str) -> Stylesheet {
|
|||
for rule in ErrorLoggerIterator(parse_stylesheet_rules(tokenize(css))) {
|
||||
let next_state; // Unitialized to force each branch to set it.
|
||||
match rule {
|
||||
QualifiedRule(rule) => {
|
||||
next_state = STATE_BODY;
|
||||
parse_style_rule(rule, &mut rules, &namespaces)
|
||||
},
|
||||
AtRule(rule) => {
|
||||
let name: &str = to_ascii_lower(rule.name);
|
||||
match name {
|
||||
let lower_name: &str = to_ascii_lower(rule.name);
|
||||
match lower_name {
|
||||
"charset" => {
|
||||
if state > STATE_CHARSET {
|
||||
log_css_error(rule.location, "@charset must be the first rule")
|
||||
|
@ -76,14 +81,10 @@ fn parse_stylesheet(css: &str) -> Stylesheet {
|
|||
},
|
||||
_ => {
|
||||
next_state = STATE_BODY;
|
||||
log_css_error(rule.location, fmt!("Unsupported at-rule: @%s", name))
|
||||
parse_nested_at_rule(lower_name, rule, &mut rules, &namespaces)
|
||||
},
|
||||
}
|
||||
},
|
||||
QualifiedRule(rule) => {
|
||||
next_state = STATE_BODY;
|
||||
parse_style_rule(rule, &mut rules, &namespaces)
|
||||
},
|
||||
}
|
||||
state = next_state;
|
||||
}
|
||||
|
@ -91,14 +92,24 @@ fn parse_stylesheet(css: &str) -> Stylesheet {
|
|||
}
|
||||
|
||||
|
||||
fn parse_style_rule(rule: QualifiedRule, rule_list: &mut ~[CSSRule],
|
||||
namespaces: &NamespaceMap) {
|
||||
pub fn parse_style_rule(rule: QualifiedRule, parent_rules: &mut ~[CSSRule],
|
||||
namespaces: &NamespaceMap) {
|
||||
let QualifiedRule{location: location, prelude: prelude, block: block} = rule;
|
||||
match selectors::parse_selector_list(prelude, namespaces) {
|
||||
Some(selectors) => rule_list.push(CSSStyleRule(StyleRule{
|
||||
Some(selectors) => parent_rules.push(CSSStyleRule(StyleRule{
|
||||
selectors: selectors,
|
||||
declarations: properties::parse_property_declaration_list(block)
|
||||
})),
|
||||
None => log_css_error(location, "Unsupported CSS selector."),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// lower_name is passed explicitly to avoid computing it twice.
|
||||
pub fn parse_nested_at_rule(lower_name: &str, rule: AtRule,
|
||||
parent_rules: &mut ~[CSSRule], namespaces: &NamespaceMap) {
|
||||
match lower_name {
|
||||
"media" => parse_media_rule(rule, parent_rules, namespaces),
|
||||
_ => log_css_error(rule.location, fmt!("Unsupported at-rule: @%s", lower_name))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue