Factor out custom property name parsing.

This commit is contained in:
Simon Sapin 2015-09-03 08:36:03 +02:00
parent b0aedc6f4c
commit a9db4eef14
2 changed files with 48 additions and 44 deletions

View file

@ -8,18 +8,30 @@ use std::collections::{HashMap, HashSet};
use std::sync::Arc; use std::sync::Arc;
use string_cache::Atom; use string_cache::Atom;
// Does not include the `--` prefix
pub type Name = Atom;
// https://drafts.csswg.org/css-variables/#typedef-custom-property-name
pub fn parse_name(s: &str) -> Result<Name, ()> {
if s.starts_with("--") {
Ok(Atom::from_slice(&s[2..]))
} else {
Err(())
}
}
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct Value { pub struct Value {
/// In CSS syntax /// In CSS syntax
value: String, value: String,
/// Custom property names in var() functions. Do not include the `--` prefix. /// Custom property names in var() functions.
references: HashSet<Atom>, references: HashSet<Name>,
} }
pub struct BorrowedValue<'a> { pub struct BorrowedValue<'a> {
value: &'a str, value: &'a str,
references: Option<&'a HashSet<Atom>>, references: Option<&'a HashSet<Name>>,
} }
pub fn parse(input: &mut Parser) -> Result<Value, ()> { pub fn parse(input: &mut Parser) -> Result<Value, ()> {
@ -35,7 +47,7 @@ pub fn parse(input: &mut Parser) -> Result<Value, ()> {
} }
/// https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value /// https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value
pub fn parse_declaration_value(input: &mut Parser, references: &mut Option<HashSet<Atom>>) pub fn parse_declaration_value(input: &mut Parser, references: &mut Option<HashSet<Name>>)
-> Result<(), ()> { -> Result<(), ()> {
if input.is_exhausted() { if input.is_exhausted() {
// Need at least one token // Need at least one token
@ -77,7 +89,7 @@ pub fn parse_declaration_value(input: &mut Parser, references: &mut Option<HashS
/// Like parse_declaration_value, /// Like parse_declaration_value,
/// but accept `!` and `;` since they are only invalid at the top level /// but accept `!` and `;` since they are only invalid at the top level
fn parse_declaration_value_block(input: &mut Parser, references: &mut Option<HashSet<Atom>>) fn parse_declaration_value_block(input: &mut Parser, references: &mut Option<HashSet<Name>>)
-> Result<(), ()> { -> Result<(), ()> {
while let Ok(token) = input.next() { while let Ok(token) = input.next() {
match token { match token {
@ -111,30 +123,25 @@ fn parse_declaration_value_block(input: &mut Parser, references: &mut Option<Has
} }
// If the var function is valid, return Ok((custom_property_name, fallback)) // If the var function is valid, return Ok((custom_property_name, fallback))
fn parse_var_function<'i, 't>(input: &mut Parser<'i, 't>, references: &mut Option<HashSet<Atom>>) fn parse_var_function<'i, 't>(input: &mut Parser<'i, 't>, references: &mut Option<HashSet<Name>>)
-> Result<(), ()> { -> Result<(), ()> {
// https://drafts.csswg.org/css-variables/#typedef-custom-property-name
let name = try!(input.expect_ident()); let name = try!(input.expect_ident());
let name = if name.starts_with("--") { let name = try!(parse_name(&name));
&name[2..]
} else {
return Err(())
};
if input.expect_comma().is_ok() { if input.expect_comma().is_ok() {
try!(parse_declaration_value(input, references)); try!(parse_declaration_value(input, references));
} }
if let Some(ref mut refs) = *references { if let Some(ref mut refs) = *references {
refs.insert(Atom::from_slice(name)); refs.insert(name);
} }
Ok(()) Ok(())
} }
/// Add one custom property declaration to a map, /// Add one custom property declaration to a map,
/// unless another with the same name was already there. /// unless another with the same name was already there.
pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Atom, BorrowedValue<'a>>>, pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Name, BorrowedValue<'a>>>,
inherited: &'a Option<Arc<HashMap<Atom, String>>>, inherited: &'a Option<Arc<HashMap<Name, String>>>,
seen: &mut HashSet<&'a Atom>, seen: &mut HashSet<&'a Name>,
name: &'a Atom, name: &'a Name,
value: &'a DeclaredValue<Value>) { value: &'a DeclaredValue<Value>) {
let was_not_already_present = seen.insert(name); let was_not_already_present = seen.insert(name);
if was_not_already_present { if was_not_already_present {
@ -166,9 +173,9 @@ pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Atom, BorrowedValu
} }
} }
pub fn finish_cascade(custom_properties: Option<HashMap<&Atom, BorrowedValue>>, pub fn finish_cascade(custom_properties: Option<HashMap<&Name, BorrowedValue>>,
inherited: &Option<Arc<HashMap<Atom, String>>>) inherited: &Option<Arc<HashMap<Name, String>>>)
-> Option<Arc<HashMap<Atom, String>>> { -> Option<Arc<HashMap<Name, String>>> {
if let Some(mut map) = custom_properties { if let Some(mut map) = custom_properties {
remove_cycles(&mut map); remove_cycles(&mut map);
Some(Arc::new(substitute_all(map, inherited))) Some(Arc::new(substitute_all(map, inherited)))
@ -179,7 +186,7 @@ pub fn finish_cascade(custom_properties: Option<HashMap<&Atom, BorrowedValue>>,
/// https://drafts.csswg.org/css-variables/#cycles /// https://drafts.csswg.org/css-variables/#cycles
/// The initial value of a custom property is represented by this property not being in the map. /// The initial value of a custom property is represented by this property not being in the map.
fn remove_cycles(map: &mut HashMap<&Atom, BorrowedValue>) { fn remove_cycles(map: &mut HashMap<&Name, BorrowedValue>) {
let mut to_remove = HashSet::new(); let mut to_remove = HashSet::new();
{ {
let mut visited = HashSet::new(); let mut visited = HashSet::new();
@ -187,11 +194,11 @@ fn remove_cycles(map: &mut HashMap<&Atom, BorrowedValue>) {
for name in map.keys() { for name in map.keys() {
walk(map, name, &mut stack, &mut visited, &mut to_remove); walk(map, name, &mut stack, &mut visited, &mut to_remove);
fn walk<'a>(map: &HashMap<&'a Atom, BorrowedValue<'a>>, fn walk<'a>(map: &HashMap<&'a Name, BorrowedValue<'a>>,
name: &'a Atom, name: &'a Name,
stack: &mut Vec<&'a Atom>, stack: &mut Vec<&'a Name>,
visited: &mut HashSet<&'a Atom>, visited: &mut HashSet<&'a Name>,
to_remove: &mut HashSet<Atom>) { to_remove: &mut HashSet<Name>) {
let already_visited_before = !visited.insert(name); let already_visited_before = !visited.insert(name);
if already_visited_before { if already_visited_before {
return return
@ -221,9 +228,9 @@ fn remove_cycles(map: &mut HashMap<&Atom, BorrowedValue>) {
} }
/// Replace `var()` functions for all custom properties. /// Replace `var()` functions for all custom properties.
fn substitute_all(custom_properties: HashMap<&Atom, BorrowedValue>, fn substitute_all(custom_properties: HashMap<&Name, BorrowedValue>,
inherited: &Option<Arc<HashMap<Atom, String>>>) inherited: &Option<Arc<HashMap<Name, String>>>)
-> HashMap<Atom, String> { -> HashMap<Name, String> {
let mut substituted_map = HashMap::new(); let mut substituted_map = HashMap::new();
let mut invalid = HashSet::new(); let mut invalid = HashSet::new();
for (&name, value) in &custom_properties { for (&name, value) in &custom_properties {
@ -238,13 +245,13 @@ fn substitute_all(custom_properties: HashMap<&Atom, BorrowedValue>,
/// Replace `var()` functions for one custom property. /// Replace `var()` functions for one custom property.
/// Also recursively record results for other custom properties referenced by `var()` functions. /// Also recursively record results for other custom properties referenced by `var()` functions.
/// Return `Err(())` for invalid at computed time. /// Return `Err(())` for invalid at computed time.
fn substitute_one(name: &Atom, fn substitute_one(name: &Name,
value: &BorrowedValue, value: &BorrowedValue,
custom_properties: &HashMap<&Atom, BorrowedValue>, custom_properties: &HashMap<&Name, BorrowedValue>,
inherited: &Option<Arc<HashMap<Atom, String>>>, inherited: &Option<Arc<HashMap<Name, String>>>,
substituted: Option<&mut String>, substituted: Option<&mut String>,
substituted_map: &mut HashMap<Atom, String>, substituted_map: &mut HashMap<Name, String>,
invalid: &mut HashSet<Atom>) invalid: &mut HashSet<Name>)
-> Result<(), ()> { -> Result<(), ()> {
if let Some(value) = substituted_map.get(name) { if let Some(value) = substituted_map.get(name) {
if let Some(substituted) = substituted { if let Some(substituted) = substituted {
@ -305,7 +312,7 @@ fn substitute_block<F>(input: &mut Parser,
substituted: &mut String, substituted: &mut String,
substitute_one: &mut F) substitute_one: &mut F)
-> Result<(), ()> -> Result<(), ()>
where F: FnMut(&Atom, &mut String) -> Result<(), ()> { where F: FnMut(&Name, &mut String) -> Result<(), ()> {
loop { loop {
let input_slice = input.slice_from(*start); let input_slice = input.slice_from(*start);
let token = if let Ok(token) = input.next() { token } else { break }; let token = if let Ok(token) = input.next() { token } else { break };
@ -313,9 +320,9 @@ fn substitute_block<F>(input: &mut Parser,
Token::Function(ref name) if name == "var" => { Token::Function(ref name) if name == "var" => {
substituted.push_str(input_slice); substituted.push_str(input_slice);
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
// parse_var_function() ensures neither .unwrap() will fail.
let name = input.expect_ident().unwrap(); let name = input.expect_ident().unwrap();
debug_assert!(name.starts_with("--")); // Ensured by parse_var_function() let name = parse_name(&name).unwrap();
let name = Atom::from_slice(&name[2..]);
if substitute_one(&name, substituted).is_ok() { if substitute_one(&name, substituted).is_ok() {
// Skip over the fallback, as `parse_nested_block` would return `Err` // Skip over the fallback, as `parse_nested_block` would return `Err`
@ -356,7 +363,7 @@ fn substitute_block<F>(input: &mut Parser,
/// Replace `var()` functions for a non-custom property. /// Replace `var()` functions for a non-custom property.
/// Return `Err(())` for invalid at computed time. /// Return `Err(())` for invalid at computed time.
pub fn substitute(input: &str, custom_properties: &Option<Arc<HashMap<Atom, String>>>) pub fn substitute(input: &str, custom_properties: &Option<Arc<HashMap<Name, String>>>)
-> Result<String, ()> { -> Result<String, ()> {
let empty_map; let empty_map;
let custom_properties = if let &Some(ref arc) = custom_properties { let custom_properties = if let &Some(ref arc) = custom_properties {

View file

@ -5727,8 +5727,7 @@ pub enum PropertyDeclaration {
% for property in LONGHANDS: % for property in LONGHANDS:
${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>), ${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
% endfor % endfor
/// Name (atom) does not include the `--` prefix. Custom(::custom_properties::Name, DeclaredValue<::custom_properties::Value>),
Custom(Atom, DeclaredValue<::custom_properties::Value>),
} }
@ -5779,7 +5778,7 @@ impl PropertyDeclaration {
pub fn parse(name: &str, context: &ParserContext, input: &mut Parser, pub fn parse(name: &str, context: &ParserContext, input: &mut Parser,
result_list: &mut Vec<PropertyDeclaration>) -> PropertyDeclarationParseResult { result_list: &mut Vec<PropertyDeclaration>) -> PropertyDeclarationParseResult {
if name.starts_with("--") { if let Ok(name) = ::custom_properties::parse_name(name) {
let value = match input.try(CSSWideKeyword::parse) { let value = match input.try(CSSWideKeyword::parse) {
Ok(CSSWideKeyword::UnsetKeyword) | // Custom properties are alawys inherited Ok(CSSWideKeyword::UnsetKeyword) | // Custom properties are alawys inherited
Ok(CSSWideKeyword::InheritKeyword) => DeclaredValue::Inherit, Ok(CSSWideKeyword::InheritKeyword) => DeclaredValue::Inherit,
@ -5789,7 +5788,6 @@ impl PropertyDeclaration {
Err(()) => return PropertyDeclarationParseResult::InvalidValue, Err(()) => return PropertyDeclarationParseResult::InvalidValue,
} }
}; };
let name = Atom::from_slice(&name[2..]);
result_list.push(PropertyDeclaration::Custom(name, value)); result_list.push(PropertyDeclaration::Custom(name, value));
return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration; return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration;
} }
@ -5900,8 +5898,7 @@ pub struct ComputedValues {
% for style_struct in STYLE_STRUCTS: % for style_struct in STYLE_STRUCTS:
${style_struct.ident}: Arc<style_structs::${style_struct.name}>, ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor % endfor
/// Names (atoms) do not include the `--` prefix. custom_properties: Option<Arc<HashMap<::custom_properties::Name, String>>>,
custom_properties: Option<Arc<HashMap<Atom, String>>>,
shareable: bool, shareable: bool,
pub writing_mode: WritingMode, pub writing_mode: WritingMode,
pub root_font_size: Au, pub root_font_size: Au,