mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
Add parsing of CSS Custom Properties.
This commit is contained in:
parent
b7c88dd547
commit
09e60beb78
3 changed files with 175 additions and 10 deletions
125
components/style/custom_properties.rs
Normal file
125
components/style/custom_properties.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 cssparser::{Parser, Token};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use string_cache::Atom;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Value {
|
||||
/// In CSS syntax
|
||||
pub value: String,
|
||||
|
||||
/// Custom property names in var() functions
|
||||
pub references: HashSet<Atom>,
|
||||
}
|
||||
|
||||
/// Names (atoms) do not include the `--` prefix.
|
||||
pub type Map = HashMap<Atom, Value>;
|
||||
|
||||
pub fn parse(input: &mut Parser) -> Result<Value, ()> {
|
||||
let start = input.position();
|
||||
let mut references = HashSet::new();
|
||||
try!(parse_declaration_value(input, &mut references));
|
||||
Ok(Value {
|
||||
value: input.slice_from(start).to_owned(),
|
||||
references: references,
|
||||
})
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value
|
||||
fn parse_declaration_value(input: &mut Parser, references: &mut HashSet<Atom>) -> Result<(), ()> {
|
||||
while let Ok(token) = input.next() {
|
||||
match token {
|
||||
Token::BadUrl |
|
||||
Token::BadString |
|
||||
Token::CloseParenthesis |
|
||||
Token::CloseSquareBracket |
|
||||
Token::CloseCurlyBracket |
|
||||
|
||||
Token::Semicolon |
|
||||
Token::Delim('!') => {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
Token::Function(ref name) if name == "var" => {
|
||||
try!(parse_var_function(input, references));
|
||||
}
|
||||
|
||||
Token::Function(_) |
|
||||
Token::ParenthesisBlock |
|
||||
Token::CurlyBracketBlock |
|
||||
Token::SquareBracketBlock => {
|
||||
try!(parse_declaration_value_block(input, references))
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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>)
|
||||
-> Result<(), ()> {
|
||||
while let Ok(token) = input.next() {
|
||||
match token {
|
||||
Token::BadUrl |
|
||||
Token::BadString |
|
||||
Token::CloseParenthesis |
|
||||
Token::CloseSquareBracket |
|
||||
Token::CloseCurlyBracket => {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
Token::Function(ref name) if name == "var" => {
|
||||
try!(parse_var_function(input, references));
|
||||
}
|
||||
|
||||
Token::Function(_) |
|
||||
Token::ParenthesisBlock |
|
||||
Token::CurlyBracketBlock |
|
||||
Token::SquareBracketBlock => {
|
||||
try!(parse_declaration_value_block(input, references))
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 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>)
|
||||
-> Result<(), ()> {
|
||||
// https://drafts.csswg.org/css-variables/#typedef-custom-property-name
|
||||
let name = try!(input.expect_ident());
|
||||
let name = if name.starts_with("--") {
|
||||
&name[2..]
|
||||
} else {
|
||||
return Err(())
|
||||
};
|
||||
if input.expect_comma().is_ok() {
|
||||
try!(parse_declaration_value(input, references));
|
||||
}
|
||||
references.insert(Atom::from_slice(name));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cascade(custom_properties: &mut Option<Map>, inherited_custom_properties: &Option<Arc<Map>>,
|
||||
name: &Atom, value: &Value) {
|
||||
let map = match *custom_properties {
|
||||
Some(ref mut map) => map,
|
||||
None => {
|
||||
*custom_properties = Some(match *inherited_custom_properties {
|
||||
Some(ref arc) => (**arc).clone(),
|
||||
None => HashMap::new(),
|
||||
});
|
||||
custom_properties.as_mut().unwrap()
|
||||
}
|
||||
};
|
||||
map.entry(name.clone()).or_insert(value.clone());
|
||||
}
|
|
@ -43,6 +43,7 @@ extern crate num;
|
|||
extern crate util;
|
||||
|
||||
|
||||
mod custom_properties;
|
||||
pub mod stylesheets;
|
||||
pub mod parser;
|
||||
pub mod selector_matching;
|
||||
|
|
|
@ -22,6 +22,7 @@ use util::logical_geometry::{LogicalMargin, PhysicalSide, WritingMode};
|
|||
use euclid::SideOffsets2D;
|
||||
use euclid::size::Size2D;
|
||||
use fnv::FnvHasher;
|
||||
use string_cache::Atom;
|
||||
|
||||
use computed_values;
|
||||
use parser::{ParserContext, log_css_error};
|
||||
|
@ -5610,6 +5611,7 @@ fn deduplicate_property_declarations(declarations: Vec<PropertyDeclaration>)
|
|||
-> Vec<PropertyDeclaration> {
|
||||
let mut deduplicated = vec![];
|
||||
let mut seen = PropertyBitField::new();
|
||||
let mut seen_custom = Vec::new();
|
||||
for declaration in declarations.into_iter().rev() {
|
||||
match declaration {
|
||||
% for property in LONGHANDS:
|
||||
|
@ -5624,6 +5626,12 @@ fn deduplicate_property_declarations(declarations: Vec<PropertyDeclaration>)
|
|||
% endif
|
||||
},
|
||||
% endfor
|
||||
PropertyDeclaration::Custom(ref name, _) => {
|
||||
if seen_custom.contains(name) {
|
||||
continue
|
||||
}
|
||||
seen_custom.push(name.clone())
|
||||
}
|
||||
}
|
||||
deduplicated.push(declaration)
|
||||
}
|
||||
|
@ -5675,6 +5683,8 @@ pub enum PropertyDeclaration {
|
|||
% for property in LONGHANDS:
|
||||
${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
|
||||
% endfor
|
||||
/// Name (atom) does not include the `--` prefix.
|
||||
Custom(Atom, ::custom_properties::Value),
|
||||
}
|
||||
|
||||
|
||||
|
@ -5725,6 +5735,15 @@ impl PropertyDeclaration {
|
|||
|
||||
pub fn parse(name: &str, context: &ParserContext, input: &mut Parser,
|
||||
result_list: &mut Vec<PropertyDeclaration>) -> PropertyDeclarationParseResult {
|
||||
if name.starts_with("--") {
|
||||
if let Ok(value) = ::custom_properties::parse(input) {
|
||||
let name = Atom::from_slice(&name[2..]);
|
||||
result_list.push(PropertyDeclaration::Custom(name, value));
|
||||
return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration;
|
||||
} else {
|
||||
return PropertyDeclarationParseResult::InvalidValue;
|
||||
}
|
||||
}
|
||||
match_ignore_ascii_case! { name,
|
||||
% for property in LONGHANDS:
|
||||
% if property.derived_from is None:
|
||||
|
@ -5832,6 +5851,7 @@ pub struct ComputedValues {
|
|||
% for style_struct in STYLE_STRUCTS:
|
||||
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
|
||||
% endfor
|
||||
custom_properties: Option<Arc<::custom_properties::Map>>,
|
||||
shareable: bool,
|
||||
pub writing_mode: WritingMode,
|
||||
pub root_font_size: Au,
|
||||
|
@ -6050,6 +6070,7 @@ lazy_static! {
|
|||
% endif
|
||||
}),
|
||||
% endfor
|
||||
custom_properties: None,
|
||||
shareable: true,
|
||||
writing_mode: WritingMode::empty(),
|
||||
root_font_size: longhands::font_size::get_initial_value(),
|
||||
|
@ -6073,6 +6094,7 @@ fn cascade_with_cached_declarations(
|
|||
let mut style_${style_struct.ident} = cached_style.${style_struct.ident}.clone();
|
||||
% endif
|
||||
% endfor
|
||||
let mut custom_properties = None;
|
||||
|
||||
let mut seen = PropertyBitField::new();
|
||||
// Declaration blocks are stored in increasing precedence order,
|
||||
|
@ -6134,6 +6156,10 @@ fn cascade_with_cached_declarations(
|
|||
% endif
|
||||
% endfor
|
||||
% endfor
|
||||
PropertyDeclaration::Custom(ref name, ref value) => {
|
||||
::custom_properties::cascade(
|
||||
&mut custom_properties, &parent_style.custom_properties, name, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6148,6 +6174,8 @@ fn cascade_with_cached_declarations(
|
|||
% for style_struct in STYLE_STRUCTS:
|
||||
${style_struct.ident}: style_${style_struct.ident},
|
||||
% endfor
|
||||
custom_properties: custom_properties
|
||||
.map(Arc::new).or_else(|| parent_style.custom_properties.clone()),
|
||||
shareable: shareable,
|
||||
root_font_size: parent_style.root_font_size,
|
||||
}
|
||||
|
@ -6210,7 +6238,6 @@ pub fn cascade(viewport_size: Size2D<Au>,
|
|||
Some(parent_style) => (false, parent_style),
|
||||
None => (true, initial_values),
|
||||
};
|
||||
|
||||
let mut context = {
|
||||
let inherited_font_style = inherited_style.get_font();
|
||||
computed::Context {
|
||||
|
@ -6348,12 +6375,14 @@ pub fn cascade(viewport_size: Size2D<Au>,
|
|||
% endif
|
||||
.${style_struct.ident}.clone(),
|
||||
% endfor
|
||||
custom_properties: None,
|
||||
shareable: false,
|
||||
writing_mode: WritingMode::empty(),
|
||||
root_font_size: context.root_font_size,
|
||||
};
|
||||
let mut cacheable = true;
|
||||
let mut seen = PropertyBitField::new();
|
||||
let mut custom_properties = None;
|
||||
// Declaration blocks are stored in increasing precedence order, we want them in decreasing
|
||||
// order here.
|
||||
//
|
||||
|
@ -6364,15 +6393,23 @@ pub fn cascade(viewport_size: Size2D<Au>,
|
|||
for sub_list in applicable_declarations.iter().rev() {
|
||||
// Declarations are already stored in reverse order.
|
||||
for declaration in sub_list.declarations.iter() {
|
||||
let discriminant = unsafe {
|
||||
intrinsics::discriminant_value(declaration) as usize
|
||||
};
|
||||
(cascade_property[discriminant].unwrap())(declaration,
|
||||
&mut style,
|
||||
inherited_style,
|
||||
&context,
|
||||
&mut seen,
|
||||
&mut cacheable);
|
||||
match *declaration {
|
||||
PropertyDeclaration::Custom(ref name, ref value) => {
|
||||
::custom_properties::cascade(
|
||||
&mut custom_properties, &inherited_style.custom_properties, name, value)
|
||||
}
|
||||
_ => {
|
||||
let discriminant = unsafe {
|
||||
intrinsics::discriminant_value(declaration) as usize
|
||||
};
|
||||
(cascade_property[discriminant].unwrap())(declaration,
|
||||
&mut style,
|
||||
inherited_style,
|
||||
&context,
|
||||
&mut seen,
|
||||
&mut cacheable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -6414,6 +6451,8 @@ pub fn cascade(viewport_size: Size2D<Au>,
|
|||
% for style_struct in STYLE_STRUCTS:
|
||||
${style_struct.ident}: style.${style_struct.ident},
|
||||
% endfor
|
||||
custom_properties: custom_properties
|
||||
.map(Arc::new).or_else(|| inherited_style.custom_properties.clone()),
|
||||
shareable: shareable,
|
||||
root_font_size: context.root_font_size,
|
||||
}, cacheable)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue