mirror of
https://github.com/servo/servo.git
synced 2025-08-09 15:35:34 +01:00
Parse @namespace rules.
This commit is contained in:
parent
10827f160b
commit
9512d13cbb
2 changed files with 106 additions and 17 deletions
18
selectors.rs
18
selectors.rs
|
@ -1,4 +1,5 @@
|
|||
use cssparser::*;
|
||||
use stylesheets::NamespaceMap;
|
||||
|
||||
|
||||
pub struct Selector {
|
||||
|
@ -63,9 +64,10 @@ pub struct AttrSelector {
|
|||
}
|
||||
|
||||
|
||||
pub fn parse_selector_list(input: &[ComponentValue]) -> Option<~[Selector]> {
|
||||
pub fn parse_selector_list(input: &[ComponentValue], namespaces: &NamespaceMap)
|
||||
-> Option<~[Selector]> {
|
||||
let len = input.len();
|
||||
let (first, pos) = match parse_selector(input, 0) {
|
||||
let (first, pos) = match parse_selector(input, 0, namespaces) {
|
||||
None => return None,
|
||||
Some(result) => result
|
||||
};
|
||||
|
@ -77,7 +79,7 @@ pub fn parse_selector_list(input: &[ComponentValue]) -> Option<~[Selector]> {
|
|||
if pos >= len { break } // EOF
|
||||
if input[pos] != Comma { return None }
|
||||
pos = skip_whitespace(input, pos);
|
||||
match parse_selector(input, pos) {
|
||||
match parse_selector(input, pos, namespaces) {
|
||||
None => return None,
|
||||
Some((selector, next_pos)) => {
|
||||
results.push(selector);
|
||||
|
@ -89,9 +91,10 @@ pub fn parse_selector_list(input: &[ComponentValue]) -> Option<~[Selector]> {
|
|||
}
|
||||
|
||||
|
||||
fn parse_selector(input: &[ComponentValue], pos: uint) -> Option<(Selector, uint)> {
|
||||
fn parse_selector(input: &[ComponentValue], pos: uint, namespaces: &NamespaceMap)
|
||||
-> Option<(Selector, uint)> {
|
||||
let len = input.len();
|
||||
let (first, pos) = match parse_simple_selectors(input, pos) {
|
||||
let (first, pos) = match parse_simple_selectors(input, pos, namespaces) {
|
||||
None => return None,
|
||||
Some(result) => result
|
||||
};
|
||||
|
@ -112,7 +115,7 @@ fn parse_selector(input: &[ComponentValue], pos: uint) -> Option<(Selector, uint
|
|||
}
|
||||
};
|
||||
pos = skip_whitespace(input, pos);
|
||||
match parse_simple_selectors(input, pos) {
|
||||
match parse_simple_selectors(input, pos, namespaces) {
|
||||
None => return None,
|
||||
Some((simple_selectors, next_pos)) => {
|
||||
compound = CompoundSelector {
|
||||
|
@ -132,10 +135,11 @@ fn parse_selector(input: &[ComponentValue], pos: uint) -> Option<(Selector, uint
|
|||
}
|
||||
|
||||
|
||||
fn parse_simple_selectors(input: &[ComponentValue], pos: uint)
|
||||
fn parse_simple_selectors(input: &[ComponentValue], pos: uint, namespaces: &NamespaceMap)
|
||||
-> Option<(~[SimpleSelector], uint)> {
|
||||
let _ = input;
|
||||
let _ = pos;
|
||||
let _ = namespaces;
|
||||
None // TODO
|
||||
}
|
||||
|
||||
|
|
105
stylesheets.rs
105
stylesheets.rs
|
@ -1,50 +1,135 @@
|
|||
use std::iterator::Iterator;
|
||||
use std::hashmap::HashMap;
|
||||
use std::ascii::to_ascii_lower;
|
||||
use cssparser::*;
|
||||
use selectors;
|
||||
use properties;
|
||||
|
||||
|
||||
struct Stylesheet {
|
||||
pub struct Stylesheet {
|
||||
style_rules: ~[StyleRule],
|
||||
namespaces: NamespaceMap,
|
||||
}
|
||||
|
||||
|
||||
struct StyleRule {
|
||||
pub struct StyleRule {
|
||||
selectors: ~[selectors::Selector],
|
||||
declarations: ~[properties::PropertyDeclaration],
|
||||
}
|
||||
|
||||
pub struct NamespaceMap {
|
||||
default: Option<~str>, // Optional URL
|
||||
prefix_map: HashMap<~str, ~str>, // prefix -> URL
|
||||
}
|
||||
|
||||
|
||||
fn parse_stylesheet(css: &str) -> Stylesheet {
|
||||
static STATE_CHARSET: uint = 1;
|
||||
static STATE_IMPORTS: uint = 2;
|
||||
static STATE_NAMESPACES: uint = 3;
|
||||
static STATE_BODY: uint = 4;
|
||||
let mut state: uint = STATE_CHARSET;
|
||||
|
||||
let mut rules = ~[];
|
||||
let mut namespaces = NamespaceMap { default: None, prefix_map: HashMap::new() };
|
||||
|
||||
for rule in ErrorLogger(parse_stylesheet_rules(tokenize(css))) {
|
||||
match rule {
|
||||
AtRule(rule) => {
|
||||
log_css_error(fmt!("Unsupported at-rule: @%s", rule.name))
|
||||
let name = to_ascii_lower(rule.name);
|
||||
if "charset" == name {
|
||||
if state > STATE_CHARSET { log_css_error(rule.location,
|
||||
"@charset must be the first rule") }
|
||||
// Valid @charset rules are just ignored
|
||||
loop;
|
||||
}
|
||||
if "import" == name {
|
||||
if state > STATE_IMPORTS { log_css_error(
|
||||
rule.location, "@import must be before any rule but @charset") }
|
||||
else {
|
||||
state = STATE_IMPORTS;
|
||||
log_css_error(rule.location, "@import is not supported yet") // TODO
|
||||
}
|
||||
loop;
|
||||
}
|
||||
if "namespace" == name {
|
||||
if state > STATE_NAMESPACES { log_css_error(rule.location,
|
||||
"@namespace must be before any rule but @charset and @import") }
|
||||
else {
|
||||
state = STATE_NAMESPACES;
|
||||
let location = rule.location;
|
||||
if !parse_namespace_rule(rule, &mut namespaces) {
|
||||
log_css_error(location, "Invalid @namespace rule")
|
||||
}
|
||||
}
|
||||
loop;
|
||||
}
|
||||
state = STATE_BODY;
|
||||
log_css_error(rule.location, fmt!("Unsupported at-rule: @%s", name))
|
||||
},
|
||||
QualifiedRule(rule) => {
|
||||
match selectors::parse_selector_list(rule.prelude) {
|
||||
state = STATE_BODY;
|
||||
match selectors::parse_selector_list(rule.prelude, &namespaces) {
|
||||
Some(selectors) => rules.push(StyleRule{
|
||||
selectors: selectors,
|
||||
declarations: properties::parse_property_declaration_list(rule.block)
|
||||
}),
|
||||
None => log_css_error("Unsupported CSS selector."),
|
||||
None => log_css_error(rule.location, "Unsupported CSS selector."),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Stylesheet{ style_rules: rules }
|
||||
Stylesheet{ style_rules: rules, namespaces: namespaces }
|
||||
}
|
||||
|
||||
|
||||
fn parse_namespace_rule(rule: AtRule, namespaces: &mut NamespaceMap) -> bool {
|
||||
if rule.block.is_some() { return false }
|
||||
let location = rule.location;
|
||||
let mut prefix: Option<~str> = None;
|
||||
let mut url: Option<~str> = None;
|
||||
let mut iter = rule.prelude.consume_skip_whitespace();
|
||||
for component_value in iter {
|
||||
match component_value {
|
||||
Ident(value) => {
|
||||
if prefix.is_some() { return false }
|
||||
prefix = Some(value);
|
||||
},
|
||||
URL(value) | String(value) => {
|
||||
if url.is_some() { return false }
|
||||
url = Some(value);
|
||||
break
|
||||
},
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
if iter.next().is_some() { return false }
|
||||
match (prefix, url) {
|
||||
(Some(prefix), Some(url)) => {
|
||||
if namespaces.prefix_map.swap(prefix, url).is_some() {
|
||||
log_css_error(location, "Duplicate @namespace rule");
|
||||
}
|
||||
},
|
||||
(None, Some(url)) => {
|
||||
if namespaces.default.is_some() {
|
||||
log_css_error(location, "Duplicate @namespace rule");
|
||||
}
|
||||
namespaces.default = Some(url);
|
||||
},
|
||||
_ => return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
struct ErrorLogger<I>(I);
|
||||
|
||||
impl<T, I: Iterator<Result<T, ErrorReason>>> Iterator<T> for ErrorLogger<I> {
|
||||
impl<T, I: Iterator<Result<T, SyntaxError>>> Iterator<T> for ErrorLogger<I> {
|
||||
fn next(&mut self) -> Option<T> {
|
||||
for result in **self {
|
||||
match result {
|
||||
Ok(v) => return Some(v),
|
||||
Err(e) => log_css_error(fmt!("%?", e))
|
||||
Err(error) => log_css_error(error.location, fmt!("%?", error.reason))
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -52,7 +137,7 @@ impl<T, I: Iterator<Result<T, ErrorReason>>> Iterator<T> for ErrorLogger<I> {
|
|||
}
|
||||
|
||||
|
||||
fn log_css_error(message: &str) {
|
||||
fn log_css_error(location: SourceLocation, message: &str) {
|
||||
// TODO eventually this will got into a "web console" or something.
|
||||
info!(message)
|
||||
info!("%u:%u %s", location.line, location.column, message)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue