mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Substitute var() in longhand property declarations.
This commit is contained in:
parent
b2ee828520
commit
b4fdb5d803
6 changed files with 254 additions and 157 deletions
|
@ -24,18 +24,19 @@ pub struct BorrowedValue<'a> {
|
|||
|
||||
pub fn parse(input: &mut Parser) -> Result<Value, ()> {
|
||||
let start = input.position();
|
||||
let mut references = HashSet::new();
|
||||
let mut references = Some(HashSet::new());
|
||||
// FIXME: don’t consume a top-level `!` as that would prevent parsing `!important`.
|
||||
// Maybe using Parser::parse_until_before?
|
||||
try!(parse_declaration_value(input, &mut references));
|
||||
Ok(Value {
|
||||
value: input.slice_from(start).to_owned(),
|
||||
references: references,
|
||||
references: references.unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value
|
||||
fn parse_declaration_value(input: &mut Parser, references: &mut HashSet<Atom>) -> Result<(), ()> {
|
||||
pub fn parse_declaration_value(input: &mut Parser, references: &mut Option<HashSet<Atom>>)
|
||||
-> Result<(), ()> {
|
||||
if input.is_exhausted() {
|
||||
// Need at least one token
|
||||
return Err(())
|
||||
|
@ -76,7 +77,7 @@ fn parse_declaration_value(input: &mut Parser, references: &mut HashSet<Atom>) -
|
|||
|
||||
/// Like parse_declaration_value,
|
||||
/// but accept `!` and `;` since they are only invalid at the top level
|
||||
fn parse_declaration_value_block(input: &mut Parser, references: &mut HashSet<Atom>)
|
||||
fn parse_declaration_value_block(input: &mut Parser, references: &mut Option<HashSet<Atom>>)
|
||||
-> Result<(), ()> {
|
||||
while let Ok(token) = input.next() {
|
||||
match token {
|
||||
|
@ -110,7 +111,7 @@ fn parse_declaration_value_block(input: &mut Parser, references: &mut HashSet<At
|
|||
}
|
||||
|
||||
// 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 HashSet<Atom>)
|
||||
fn parse_var_function<'i, 't>(input: &mut Parser<'i, 't>, references: &mut Option<HashSet<Atom>>)
|
||||
-> Result<(), ()> {
|
||||
// https://drafts.csswg.org/css-variables/#typedef-custom-property-name
|
||||
let name = try!(input.expect_ident());
|
||||
|
@ -122,7 +123,9 @@ fn parse_var_function<'i, 't>(input: &mut Parser<'i, 't>, references: &mut HashS
|
|||
if input.expect_comma().is_ok() {
|
||||
try!(parse_declaration_value(input, references));
|
||||
}
|
||||
references.insert(Atom::from_slice(name));
|
||||
if let Some(ref mut refs) = *references {
|
||||
refs.insert(Atom::from_slice(name));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -153,7 +156,8 @@ pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Atom, BorrowedValu
|
|||
value: &value.value,
|
||||
references: Some(&value.references),
|
||||
});
|
||||
}
|
||||
},
|
||||
DeclaredValue::WithVariables { .. } => unreachable!(),
|
||||
DeclaredValue::Initial => {
|
||||
map.remove(&name);
|
||||
}
|
||||
|
@ -188,8 +192,8 @@ fn remove_cycles(map: &mut HashMap<&Atom, BorrowedValue>) {
|
|||
stack: &mut Vec<&'a Atom>,
|
||||
visited: &mut HashSet<&'a Atom>,
|
||||
to_remove: &mut HashSet<Atom>) {
|
||||
let was_not_already_present = visited.insert(name);
|
||||
if !was_not_already_present {
|
||||
let already_visited_before = !visited.insert(name);
|
||||
if already_visited_before {
|
||||
return
|
||||
}
|
||||
if let Some(value) = map.get(name) {
|
||||
|
@ -216,6 +220,7 @@ fn remove_cycles(map: &mut HashMap<&Atom, BorrowedValue>) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Replace `var()` functions for all custom properties.
|
||||
fn substitute_all(custom_properties: HashMap<&Atom, BorrowedValue>,
|
||||
inherited: &Option<Arc<HashMap<Atom, String>>>)
|
||||
-> HashMap<Atom, String> {
|
||||
|
@ -230,6 +235,9 @@ fn substitute_all(custom_properties: HashMap<&Atom, BorrowedValue>,
|
|||
substituted_map
|
||||
}
|
||||
|
||||
/// Replace `var()` functions for one custom property.
|
||||
/// Also recursively record results for other custom properties referenced by `var()` functions.
|
||||
/// Return `Err(())` for invalid at computed time.
|
||||
fn substitute_one(name: &Atom,
|
||||
value: &BorrowedValue,
|
||||
custom_properties: &HashMap<&Atom, BorrowedValue>,
|
||||
|
@ -252,10 +260,14 @@ fn substitute_one(name: &Atom,
|
|||
let mut substituted = String::new();
|
||||
let mut input = Parser::new(&value.value);
|
||||
let mut start = input.position();
|
||||
if substitute_block(
|
||||
custom_properties, inherited, &mut input, &mut start, &mut substituted,
|
||||
substituted_map, invalid,
|
||||
).is_ok() {
|
||||
if substitute_block(&mut input, &mut start, &mut substituted, &mut |name, substituted| {
|
||||
if let Some(value) = custom_properties.get(name) {
|
||||
substitute_one(name, value, custom_properties, inherited,
|
||||
Some(substituted), substituted_map, invalid)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}).is_ok() {
|
||||
substituted.push_str(input.slice_from(start));
|
||||
substituted
|
||||
} else {
|
||||
|
@ -279,27 +291,33 @@ fn substitute_one(name: &Atom,
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn substitute_block(custom_properties: &HashMap<&Atom, BorrowedValue>,
|
||||
inherited: &Option<Arc<HashMap<Atom, String>>>,
|
||||
input: &mut Parser,
|
||||
start: &mut SourcePosition,
|
||||
substituted: &mut String,
|
||||
substituted_map: &mut HashMap<Atom, String>,
|
||||
invalid: &mut HashSet<Atom>)
|
||||
-> Result<(), ()> {
|
||||
while let Ok(token) = input.next() {
|
||||
/// Replace `var()` functions in an arbitrary bit of input.
|
||||
///
|
||||
/// The `substitute_one` callback is called for each `var()` function in `input`.
|
||||
/// If the variable has its initial value,
|
||||
/// the callback should return `Err(())` and leave `substituted` unchanged.
|
||||
/// Otherwise, it should push the value of the variable (with its own `var()` functions replaced)
|
||||
/// to `substituted` and return `Ok(())`.
|
||||
///
|
||||
/// Return `Err(())` if `input` is invalid at computed-value time.
|
||||
fn substitute_block<F>(input: &mut Parser,
|
||||
start: &mut SourcePosition,
|
||||
substituted: &mut String,
|
||||
substitute_one: &mut F)
|
||||
-> Result<(), ()>
|
||||
where F: FnMut(&Atom, &mut String) -> Result<(), ()> {
|
||||
loop {
|
||||
let input_slice = input.slice_from(*start);
|
||||
let token = if let Ok(token) = input.next() { token } else { break };
|
||||
match token {
|
||||
Token::Function(ref name) if name == "var" => {
|
||||
substituted.push_str(input.slice_from(*start));
|
||||
substituted.push_str(input_slice);
|
||||
try!(input.parse_nested_block(|input| {
|
||||
let name = input.expect_ident().unwrap();
|
||||
debug_assert!(name.starts_with("--"));
|
||||
debug_assert!(name.starts_with("--")); // Ensured by parse_var_function()
|
||||
let name = Atom::from_slice(&name[2..]);
|
||||
|
||||
if let Some(value) = custom_properties.get(&name) {
|
||||
try!(substitute_one(
|
||||
&name, value, custom_properties, inherited,
|
||||
Some(substituted), substituted_map, invalid));
|
||||
if substitute_one(&name, substituted).is_ok() {
|
||||
// Skip over the fallback, as `parse_nested_block` would return `Err`
|
||||
// if we don’t consume all of `input`.
|
||||
// FIXME: Add a specialized method to cssparser to do this with less work.
|
||||
|
@ -307,9 +325,7 @@ fn substitute_block(custom_properties: &HashMap<&Atom, BorrowedValue>,
|
|||
} else {
|
||||
try!(input.expect_comma());
|
||||
let mut start = input.position();
|
||||
try!(substitute_block(
|
||||
custom_properties, inherited, input,
|
||||
&mut start, substituted, substituted_map, invalid));
|
||||
try!(substitute_block(input, &mut start, substituted, substitute_one));
|
||||
substituted.push_str(input.slice_from(start));
|
||||
}
|
||||
Ok(())
|
||||
|
@ -321,9 +337,9 @@ fn substitute_block(custom_properties: &HashMap<&Atom, BorrowedValue>,
|
|||
Token::ParenthesisBlock |
|
||||
Token::CurlyBracketBlock |
|
||||
Token::SquareBracketBlock => {
|
||||
try!(input.parse_nested_block(|input| substitute_block(
|
||||
custom_properties, inherited, input,
|
||||
start, substituted, substituted_map, invalid)));
|
||||
try!(input.parse_nested_block(|input| {
|
||||
substitute_block(input, start, substituted, substitute_one)
|
||||
}));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
|
@ -337,3 +353,29 @@ fn substitute_block(custom_properties: &HashMap<&Atom, BorrowedValue>,
|
|||
// ```
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Replace `var()` functions for a non-custom property.
|
||||
/// Return `Err(())` for invalid at computed time.
|
||||
pub fn substitute(input: &str, custom_properties: &Option<Arc<HashMap<Atom, String>>>)
|
||||
-> Result<String, ()> {
|
||||
let empty_map;
|
||||
let custom_properties = if let &Some(ref arc) = custom_properties {
|
||||
&**arc
|
||||
} else {
|
||||
empty_map = HashMap::new();
|
||||
&empty_map
|
||||
};
|
||||
let mut substituted = String::new();
|
||||
let mut input = Parser::new(input);
|
||||
let mut start = input.position();
|
||||
try!(substitute_block(&mut input, &mut start, &mut substituted, &mut |name, substituted| {
|
||||
if let Some(value) = custom_properties.get(name) {
|
||||
substituted.push_str(value);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}));
|
||||
substituted.push_str(input.slice_from(start));
|
||||
Ok(substituted)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue