mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Substitute var() in custom properties at computed value time.
This commit is contained in:
parent
7bcf9f0c9a
commit
1c1a9379a3
2 changed files with 113 additions and 34 deletions
|
@ -2,23 +2,25 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use cssparser::{Parser, Token};
|
use cssparser::{Parser, Token, SourcePosition};
|
||||||
use properties::DeclaredValue;
|
use properties::DeclaredValue;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use string_cache::Atom;
|
use string_cache::Atom;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct Value {
|
pub struct Value {
|
||||||
/// In CSS syntax
|
/// In CSS syntax
|
||||||
pub value: String,
|
value: String,
|
||||||
|
|
||||||
/// Custom property names in var() functions. Do not include the `--` prefix.
|
/// Custom property names in var() functions. Do not include the `--` prefix.
|
||||||
pub references: HashSet<Atom>,
|
references: HashSet<Atom>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Names (atoms) do not include the `--` prefix.
|
pub struct BorrowedValue<'a> {
|
||||||
pub type Map = HashMap<Atom, Value>;
|
value: &'a str,
|
||||||
|
references: Option<&'a HashSet<Atom>>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse(input: &mut Parser) -> Result<Value, ()> {
|
pub fn parse(input: &mut Parser) -> Result<Value, ()> {
|
||||||
let start = input.position();
|
let start = input.position();
|
||||||
|
@ -120,51 +122,53 @@ fn parse_var_function<'i, 't>(input: &mut Parser<'i, 't>, references: &mut HashS
|
||||||
|
|
||||||
/// 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<Map>,
|
pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Atom, BorrowedValue<'a>>>,
|
||||||
inherited_custom_properties: &Option<Arc<Map>>,
|
inherited_custom_properties: &'a Option<Arc<HashMap<Atom, String>>>,
|
||||||
seen: &mut HashSet<&'a Atom>,
|
seen: &mut HashSet<&'a Atom>,
|
||||||
name: &'a Atom,
|
name: &'a Atom,
|
||||||
value: &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 {
|
||||||
let map = match *custom_properties {
|
let map = match *custom_properties {
|
||||||
Some(ref mut map) => map,
|
Some(ref mut map) => map,
|
||||||
None => {
|
None => {
|
||||||
*custom_properties = Some(match *inherited_custom_properties {
|
*custom_properties = Some(match *inherited_custom_properties {
|
||||||
Some(ref arc) => (**arc).clone(),
|
Some(ref inherited) => inherited.iter().map(|(key, value)| {
|
||||||
|
(key, BorrowedValue { value: &value, references: None })
|
||||||
|
}).collect(),
|
||||||
None => HashMap::new(),
|
None => HashMap::new(),
|
||||||
});
|
});
|
||||||
custom_properties.as_mut().unwrap()
|
custom_properties.as_mut().unwrap()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match *value {
|
match *value {
|
||||||
DeclaredValue::SpecifiedValue(ref value) => {
|
DeclaredValue::Value(ref value) => {
|
||||||
map.insert(name.clone(), value.clone());
|
map.insert(name, BorrowedValue {
|
||||||
|
value: &value.value,
|
||||||
|
references: Some(&value.references),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
DeclaredValue::Initial => {
|
DeclaredValue::Initial => {
|
||||||
map.remove(name);
|
map.remove(&name);
|
||||||
}
|
}
|
||||||
DeclaredValue::Inherit => {} // The inherited value is what we already have.
|
DeclaredValue::Inherit => {} // The inherited value is what we already have.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If any custom property declarations where found for this element (`custom_properties.is_some()`)
|
pub fn finish_cascade(custom_properties: Option<HashMap<&Atom, BorrowedValue>>,
|
||||||
/// remove cycles and move the map into an `Arc`.
|
inherited_custom_properties: &Option<Arc<HashMap<Atom, String>>>)
|
||||||
/// Otherwise, default to the inherited map.
|
-> Option<Arc<HashMap<Atom, String>>> {
|
||||||
pub fn finish_cascade(custom_properties: Option<Map>,
|
|
||||||
inherited_custom_properties: &Option<Arc<Map>>)
|
|
||||||
-> Option<Arc<Map>> {
|
|
||||||
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(map))
|
Some(Arc::new(substitute_all(map)))
|
||||||
} else {
|
} else {
|
||||||
inherited_custom_properties.clone()
|
inherited_custom_properties.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-variables/#cycles
|
/// https://drafts.csswg.org/css-variables/#cycles
|
||||||
fn remove_cycles(map: &mut Map) {
|
fn remove_cycles(map: &mut HashMap<&Atom, BorrowedValue>) {
|
||||||
let mut to_remove = HashSet::new();
|
let mut to_remove = HashSet::new();
|
||||||
{
|
{
|
||||||
let mut visited = HashSet::new();
|
let mut visited = HashSet::new();
|
||||||
|
@ -172,25 +176,30 @@ fn remove_cycles(map: &mut Map) {
|
||||||
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: &'a Map, name: &'a Atom, stack: &mut Vec<&'a Atom>,
|
fn walk<'a>(map: &HashMap<&'a Atom, BorrowedValue<'a>>,
|
||||||
visited: &mut HashSet<&'a Atom>, to_remove: &mut HashSet<Atom>) {
|
name: &'a Atom,
|
||||||
|
stack: &mut Vec<&'a Atom>,
|
||||||
|
visited: &mut HashSet<&'a Atom>,
|
||||||
|
to_remove: &mut HashSet<Atom>) {
|
||||||
let was_not_already_present = visited.insert(name);
|
let was_not_already_present = visited.insert(name);
|
||||||
if !was_not_already_present {
|
if !was_not_already_present {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let Some(value) = map.get(name) {
|
if let Some(value) = map.get(name) {
|
||||||
stack.push(name);
|
if let Some(references) = value.references {
|
||||||
for next in &value.references {
|
stack.push(name);
|
||||||
if let Some(position) = stack.position_elem(&next) {
|
for next in references {
|
||||||
// Found a cycle
|
if let Some(position) = stack.position_elem(&next) {
|
||||||
for in_cycle in &stack[position..] {
|
// Found a cycle
|
||||||
to_remove.insert((**in_cycle).clone());
|
for in_cycle in &stack[position..] {
|
||||||
|
to_remove.insert((**in_cycle).clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
walk(map, next, stack, visited, to_remove);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
walk(map, next, stack, visited, to_remove);
|
|
||||||
}
|
}
|
||||||
|
stack.pop();
|
||||||
}
|
}
|
||||||
stack.pop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,3 +208,72 @@ fn remove_cycles(map: &mut Map) {
|
||||||
map.remove(name);
|
map.remove(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn substitute_all(custom_properties: HashMap<&Atom, BorrowedValue>) -> HashMap<Atom, String> {
|
||||||
|
custom_properties.iter().filter_map(|(&name, value)| {
|
||||||
|
let mut substituted = String::new();
|
||||||
|
if substitute_one(value, &mut substituted, &custom_properties).is_ok() {
|
||||||
|
Some((name.clone(), substituted))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn substitute_one(value: &BorrowedValue,
|
||||||
|
substituted: &mut String,
|
||||||
|
custom_properties: &HashMap<&Atom, BorrowedValue>)
|
||||||
|
-> Result<(), ()> {
|
||||||
|
if let Some(references) = value.references {
|
||||||
|
if !references.is_empty() {
|
||||||
|
let mut input = Parser::new(&value.value);
|
||||||
|
let mut start = input.position();
|
||||||
|
try!(substitute_block(&mut input, &mut start, substituted, &custom_properties));
|
||||||
|
substituted.push_str(input.slice_from(start));
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
substituted.push_str(value.value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn substitute_block(input: &mut Parser,
|
||||||
|
start: &mut SourcePosition,
|
||||||
|
substituted: &mut String,
|
||||||
|
custom_properties: &HashMap<&Atom, BorrowedValue>)
|
||||||
|
-> Result<(), ()> {
|
||||||
|
while let Ok(token) = input.next() {
|
||||||
|
match token {
|
||||||
|
Token::Function(ref name) if name == "var" => {
|
||||||
|
substituted.push_str(input.slice_from(*start));
|
||||||
|
try!(input.parse_nested_block(|input| {
|
||||||
|
let name = input.expect_ident().unwrap();
|
||||||
|
debug_assert!(name.starts_with("--"));
|
||||||
|
let name = Atom::from_slice(&name[2..]);
|
||||||
|
|
||||||
|
if let Some(value) = custom_properties.get(&name) {
|
||||||
|
return substitute_one(value, substituted, custom_properties)
|
||||||
|
}
|
||||||
|
try!(input.expect_comma());
|
||||||
|
let mut start = input.position();
|
||||||
|
try!(substitute_block(input, &mut start, substituted, custom_properties));
|
||||||
|
substituted.push_str(input.slice_from(start));
|
||||||
|
Ok(())
|
||||||
|
}));
|
||||||
|
*start = input.position();
|
||||||
|
}
|
||||||
|
|
||||||
|
Token::Function(_) |
|
||||||
|
Token::ParenthesisBlock |
|
||||||
|
Token::CurlyBracketBlock |
|
||||||
|
Token::SquareBracketBlock => {
|
||||||
|
try!(input.parse_nested_block(|input| {
|
||||||
|
substitute_block(input, start, substituted, custom_properties)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::collections::HashSet;
|
use std::collections::{HashSet, HashMap};
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
@ -5857,7 +5857,8 @@ 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
|
||||||
custom_properties: Option<Arc<::custom_properties::Map>>,
|
/// Names (atoms) do not include the `--` prefix.
|
||||||
|
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,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue