aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/layout/layout_task.rs2
-rw-r--r--components/script/dom/element.rs7
-rw-r--r--components/servo/Cargo.lock20
-rw-r--r--components/style/Cargo.toml2
-rw-r--r--components/style/custom_properties.rs372
-rw-r--r--components/style/lib.rs1
-rw-r--r--components/style/properties.mako.rs257
-rw-r--r--components/util/mem.rs5
8 files changed, 436 insertions, 230 deletions
diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs
index 1a8311e0a78..b6c8a9b4989 100644
--- a/components/layout/layout_task.rs
+++ b/components/layout/layout_task.rs
@@ -979,7 +979,7 @@ impl LayoutTask {
// FIXME: implement used value computation for line-height
property => {
rw_data.resolved_style_response =
- style.computed_value_to_string(property.as_slice());
+ style.computed_value_to_string(property.as_slice()).ok();
}
};
}
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index 337f7799138..2cd65557bee 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -650,7 +650,7 @@ impl Element {
if let &mut Some(ref mut declarations) = &mut *inline_declarations {
let index = declarations.normal
.iter()
- .position(|decl| decl.name() == property);
+ .position(|decl| decl.matches(property));
if let Some(index) = index {
Arc::make_mut(&mut declarations.normal).remove(index);
return;
@@ -658,7 +658,7 @@ impl Element {
let index = declarations.important
.iter()
- .position(|decl| decl.name() == property);
+ .position(|decl| decl.matches(property));
if let Some(index) = index {
Arc::make_mut(&mut declarations.important).remove(index);
return;
@@ -715,7 +715,8 @@ impl Element {
let to = Arc::make_mut(to);
let mut new_from = Vec::new();
for declaration in from.drain(..) {
- if properties.contains(&declaration.name()) {
+ let name = declaration.name();
+ if properties.iter().any(|p| name == **p) {
to.push(declaration)
} else {
new_from.push(declaration)
diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock
index 64b32314511..4207962112d 100644
--- a/components/servo/Cargo.lock
+++ b/components/servo/Cargo.lock
@@ -137,7 +137,7 @@ version = "0.0.1"
dependencies = [
"azure 0.1.0 (git+https://github.com/servo/rust-azure)",
"canvas_traits 0.0.1",
- "cssparser 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx_traits 0.0.1",
"gleam 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -154,7 +154,7 @@ name = "canvas_traits"
version = "0.0.1"
dependencies = [
"azure 0.1.0 (git+https://github.com/servo/rust-azure)",
- "cssparser 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx_traits 0.0.1",
"ipc-channel 0.1.0 (git+https://github.com/pcwalton/ipc-channel)",
@@ -290,7 +290,7 @@ dependencies = [
[[package]]
name = "cssparser"
-version = "0.3.6"
+version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"encoding 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -929,7 +929,7 @@ dependencies = [
"canvas 0.0.1",
"canvas_traits 0.0.1",
"clock_ticks 0.0.6 (git+https://github.com/tomaka/clock_ticks)",
- "cssparser 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1414,7 +1414,7 @@ dependencies = [
"bitflags 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"canvas 0.0.1",
"canvas_traits 0.0.1",
- "cssparser 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"devtools_traits 0.0.1",
"encoding 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1480,7 +1480,7 @@ version = "0.1.0"
source = "git+https://github.com/servo/rust-selectors#572353b3209af040cd3e6261978b09c7f8122844"
dependencies = [
"bitflags 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quickersort 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1622,7 +1622,7 @@ name = "style"
version = "0.0.1"
dependencies = [
"bitflags 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1647,7 +1647,7 @@ dependencies = [
name = "style_tests"
version = "0.0.1"
dependencies = [
- "cssparser 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.1.0 (git+https://github.com/servo/rust-selectors)",
"string_cache 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1662,7 +1662,7 @@ dependencies = [
name = "style_traits"
version = "0.0.1"
dependencies = [
- "cssparser 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1784,7 +1784,7 @@ version = "0.0.1"
dependencies = [
"azure 0.1.0 (git+https://github.com/servo/rust-azure)",
"bitflags 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"html5ever 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml
index c67bf713616..c9a5427e47d 100644
--- a/components/style/Cargo.toml
+++ b/components/style/Cargo.toml
@@ -23,7 +23,7 @@ git = "https://github.com/servo/rust-selectors"
features = ["unstable"]
[dependencies.cssparser]
-version = "0.3.6"
+version = "0.3.9"
features = [ "serde-serialization" ]
[dependencies.url]
diff --git a/components/style/custom_properties.rs b/components/style/custom_properties.rs
index 7a8ce7c2c67..4a6f5076f56 100644
--- a/components/style/custom_properties.rs
+++ b/components/style/custom_properties.rs
@@ -2,96 +2,135 @@
* 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, SourcePosition};
+use cssparser::{Parser, Token, SourcePosition, Delimiter, TokenSerializationType, ToCss};
use properties::DeclaredValue;
+use std::ascii::AsciiExt;
use std::collections::{HashMap, HashSet};
+use std::fmt;
use std::sync::Arc;
use string_cache::Atom;
+use util::mem::HeapSizeOf;
// 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, ()> {
+pub fn parse_name(s: &str) -> Result<&str, ()> {
if s.starts_with("--") {
- Ok(Atom::from_slice(&s[2..]))
+ Ok(&s[2..])
} else {
Err(())
}
}
#[derive(Clone, PartialEq)]
-pub struct Value {
- /// In CSS syntax
- value: String,
+pub struct SpecifiedValue {
+ css: String,
+
+ first_token_type: TokenSerializationType,
+ last_token_type: TokenSerializationType,
/// Custom property names in var() functions.
references: HashSet<Name>,
}
-pub struct BorrowedValue<'a> {
- value: &'a str,
+pub struct BorrowedSpecifiedValue<'a> {
+ css: &'a str,
+ first_token_type: TokenSerializationType,
+ last_token_type: TokenSerializationType,
references: Option<&'a HashSet<Name>>,
}
-pub fn parse(input: &mut Parser) -> Result<Value, ()> {
+#[derive(Clone, HeapSizeOf)]
+pub struct ComputedValue {
+ css: String,
+ first_token_type: TokenSerializationType,
+ last_token_type: TokenSerializationType,
+}
+
+impl ToCss for SpecifiedValue {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ dest.write_str(&self.css)
+ }
+}
+
+impl ToCss for ComputedValue {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ dest.write_str(&self.css)
+ }
+}
+
+pub type ComputedValuesMap = HashMap<Name, ComputedValue>;
+
+impl ComputedValue {
+ fn empty() -> ComputedValue {
+ ComputedValue {
+ css: String::new(),
+ last_token_type: TokenSerializationType::nothing(),
+ first_token_type: TokenSerializationType::nothing(),
+ }
+ }
+
+ fn push(&mut self, css: &str, css_first_token_type: TokenSerializationType,
+ css_last_token_type: TokenSerializationType) {
+ self.first_token_type.set_if_nothing(css_first_token_type);
+ // If self.first_token_type was nothing,
+ // self.last_token_type is also nothing and this will be false:
+ if self.last_token_type.needs_separator_when_before(css_first_token_type) {
+ self.css.push_str("/**/")
+ }
+ self.css.push_str(css);
+ self.last_token_type = css_last_token_type
+ }
+
+ fn push_from(&mut self, position: (SourcePosition, TokenSerializationType),
+ input: &Parser, last_token_type: TokenSerializationType) {
+ self.push(input.slice_from(position.0), position.1, last_token_type)
+ }
+
+ fn push_variable(&mut self, variable: &ComputedValue) {
+ self.push(&variable.css, variable.first_token_type, variable.last_token_type)
+ }
+}
+
+pub fn parse(input: &mut Parser) -> Result<SpecifiedValue, ()> {
let start = input.position();
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(),
+ let (first, last) = try!(parse_declaration_value(input, &mut references));
+ Ok(SpecifiedValue {
+ css: input.slice_from(start).to_owned(),
+ first_token_type: first,
+ last_token_type: last,
references: references.unwrap(),
})
}
/// https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value
pub fn parse_declaration_value(input: &mut Parser, references: &mut Option<HashSet<Name>>)
- -> Result<(), ()> {
- if input.is_exhausted() {
+ -> Result<(TokenSerializationType, TokenSerializationType), ()> {
+ input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
// Need at least one token
- return Err(())
- }
- 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!(input.parse_nested_block(|input| {
- parse_var_function(input, references)
- }));
- }
+ let start_position = input.position();
+ try!(input.next_including_whitespace());
+ input.reset(start_position);
- Token::Function(_) |
- Token::ParenthesisBlock |
- Token::CurlyBracketBlock |
- Token::SquareBracketBlock => {
- try!(input.parse_nested_block(|input| {
- parse_declaration_value_block(input, references)
- }));
- }
-
- _ => {}
- }
- }
- Ok(())
+ parse_declaration_value_block(input, references)
+ })
}
/// 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 Option<HashSet<Name>>)
- -> Result<(), ()> {
- while let Ok(token) = input.next() {
+ -> Result<(TokenSerializationType, TokenSerializationType), ()> {
+ let mut first_token_type = TokenSerializationType::nothing();
+ let mut last_token_type = TokenSerializationType::nothing();
+ while let Ok(token) = input.next_including_whitespace_and_comments() {
+ first_token_type.set_if_nothing(token.serialization_type());
+ // This may be OpenParen when it should be Other (for the closing paren)
+ // but that doesn’t make a difference since OpenParen is only special
+ // when it comes *after* an identifier (it would turn into a function)
+ // but a "last" token will only be concantenated *before* another unrelated token.
+ last_token_type = token.serialization_type();
match token {
Token::BadUrl |
Token::BadString |
@@ -101,7 +140,7 @@ fn parse_declaration_value_block(input: &mut Parser, references: &mut Option<Has
return Err(())
}
- Token::Function(ref name) if name == "var" => {
+ Token::Function(ref name) if name.eq_ignore_ascii_case("var") => {
try!(input.parse_nested_block(|input| {
parse_var_function(input, references)
}));
@@ -119,7 +158,7 @@ fn parse_declaration_value_block(input: &mut Parser, references: &mut Option<Has
_ => {}
}
}
- Ok(())
+ Ok((first_token_type, last_token_type))
}
// If the var function is valid, return Ok((custom_property_name, fallback))
@@ -131,37 +170,44 @@ fn parse_var_function<'i, 't>(input: &mut Parser<'i, 't>, references: &mut Optio
try!(parse_declaration_value(input, references));
}
if let Some(ref mut refs) = *references {
- refs.insert(name);
+ refs.insert(Atom::from_slice(name));
}
Ok(())
}
/// Add one custom property declaration to a map,
/// unless another with the same name was already there.
-pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Name, BorrowedValue<'a>>>,
- inherited: &'a Option<Arc<HashMap<Name, String>>>,
+pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Name, BorrowedSpecifiedValue<'a>>>,
+ inherited: &'a Option<Arc<HashMap<Name, ComputedValue>>>,
seen: &mut HashSet<&'a Name>,
name: &'a Name,
- value: &'a DeclaredValue<Value>) {
+ specified_value: &'a DeclaredValue<SpecifiedValue>) {
let was_not_already_present = seen.insert(name);
if was_not_already_present {
let map = match *custom_properties {
Some(ref mut map) => map,
None => {
*custom_properties = Some(match *inherited {
- Some(ref inherited) => inherited.iter().map(|(key, value)| {
- (key, BorrowedValue { value: &value, references: None })
+ Some(ref inherited) => inherited.iter().map(|(key, inherited_value)| {
+ (key, BorrowedSpecifiedValue {
+ css: &inherited_value.css,
+ first_token_type: inherited_value.first_token_type,
+ last_token_type: inherited_value.last_token_type,
+ references: None
+ })
}).collect(),
None => HashMap::new(),
});
custom_properties.as_mut().unwrap()
}
};
- match *value {
- DeclaredValue::Value(ref value) => {
- map.insert(name, BorrowedValue {
- value: &value.value,
- references: Some(&value.references),
+ match *specified_value {
+ DeclaredValue::Value(ref specified_value) => {
+ map.insert(name, BorrowedSpecifiedValue {
+ css: &specified_value.css,
+ first_token_type: specified_value.first_token_type,
+ last_token_type: specified_value.last_token_type,
+ references: Some(&specified_value.references),
});
},
DeclaredValue::WithVariables { .. } => unreachable!(),
@@ -173,10 +219,10 @@ pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Name, BorrowedValu
}
}
-pub fn finish_cascade(custom_properties: Option<HashMap<&Name, BorrowedValue>>,
- inherited: &Option<Arc<HashMap<Name, String>>>)
- -> Option<Arc<HashMap<Name, String>>> {
- if let Some(mut map) = custom_properties {
+pub fn finish_cascade(specified_values_map: Option<HashMap<&Name, BorrowedSpecifiedValue>>,
+ inherited: &Option<Arc<HashMap<Name, ComputedValue>>>)
+ -> Option<Arc<HashMap<Name, ComputedValue>>> {
+ if let Some(mut map) = specified_values_map {
remove_cycles(&mut map);
Some(Arc::new(substitute_all(map, inherited)))
} else {
@@ -186,7 +232,7 @@ pub fn finish_cascade(custom_properties: Option<HashMap<&Name, BorrowedValue>>,
/// https://drafts.csswg.org/css-variables/#cycles
/// The initial value of a custom property is represented by this property not being in the map.
-fn remove_cycles(map: &mut HashMap<&Name, BorrowedValue>) {
+fn remove_cycles(map: &mut HashMap<&Name, BorrowedSpecifiedValue>) {
let mut to_remove = HashSet::new();
{
let mut visited = HashSet::new();
@@ -194,7 +240,7 @@ fn remove_cycles(map: &mut HashMap<&Name, BorrowedValue>) {
for name in map.keys() {
walk(map, name, &mut stack, &mut visited, &mut to_remove);
- fn walk<'a>(map: &HashMap<&'a Name, BorrowedValue<'a>>,
+ fn walk<'a>(map: &HashMap<&'a Name, BorrowedSpecifiedValue<'a>>,
name: &'a Name,
stack: &mut Vec<&'a Name>,
visited: &mut HashSet<&'a Name>,
@@ -228,114 +274,150 @@ fn remove_cycles(map: &mut HashMap<&Name, BorrowedValue>) {
}
/// Replace `var()` functions for all custom properties.
-fn substitute_all(custom_properties: HashMap<&Name, BorrowedValue>,
- inherited: &Option<Arc<HashMap<Name, String>>>)
- -> HashMap<Name, String> {
- let mut substituted_map = HashMap::new();
+fn substitute_all(specified_values_map: HashMap<&Name, BorrowedSpecifiedValue>,
+ inherited: &Option<Arc<HashMap<Name, ComputedValue>>>)
+ -> HashMap<Name, ComputedValue> {
+ let mut computed_values_map = HashMap::new();
let mut invalid = HashSet::new();
- for (&name, value) in &custom_properties {
- // If this value is invalid at computed-time it won’t be inserted in substituted_map.
+ for (&name, value) in &specified_values_map {
+ // If this value is invalid at computed-time it won’t be inserted in computed_values_map.
// Nothing else to do.
let _ = substitute_one(
- name, value, &custom_properties, inherited, None, &mut substituted_map, &mut invalid);
+ name, value, &specified_values_map, inherited, None,
+ &mut computed_values_map, &mut invalid);
}
- substituted_map
+ computed_values_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.
+/// or `Ok(last_token_type that was pushed to partial_computed_value)` otherwise.
fn substitute_one(name: &Name,
- value: &BorrowedValue,
- custom_properties: &HashMap<&Name, BorrowedValue>,
- inherited: &Option<Arc<HashMap<Name, String>>>,
- substituted: Option<&mut String>,
- substituted_map: &mut HashMap<Name, String>,
+ specified_value: &BorrowedSpecifiedValue,
+ specified_values_map: &HashMap<&Name, BorrowedSpecifiedValue>,
+ inherited: &Option<Arc<HashMap<Name, ComputedValue>>>,
+ partial_computed_value: Option<&mut ComputedValue>,
+ computed_values_map: &mut HashMap<Name, ComputedValue>,
invalid: &mut HashSet<Name>)
- -> Result<(), ()> {
- if let Some(value) = substituted_map.get(name) {
- if let Some(substituted) = substituted {
- substituted.push_str(value)
+ -> Result<TokenSerializationType, ()> {
+ if let Some(computed_value) = computed_values_map.get(name) {
+ if let Some(partial_computed_value) = partial_computed_value {
+ partial_computed_value.push_variable(computed_value)
}
- return Ok(())
+ return Ok(computed_value.last_token_type)
}
if invalid.contains(name) {
return Err(());
}
- let value = if value.references.map(|set| set.is_empty()) == Some(false) {
- let mut substituted = String::new();
- let mut input = Parser::new(&value.value);
- let mut start = input.position();
- 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(())
+ let computed_value = if specified_value.references.map(|set| set.is_empty()) == Some(false) {
+ let mut partial_computed_value = ComputedValue::empty();
+ let mut input = Parser::new(&specified_value.css);
+ let mut position = (input.position(), specified_value.first_token_type);
+ let result = substitute_block(
+ &mut input, &mut position, &mut partial_computed_value,
+ &mut |name, partial_computed_value| {
+ if let Some(other_specified_value) = specified_values_map.get(name) {
+ substitute_one(name, other_specified_value, specified_values_map, inherited,
+ Some(partial_computed_value), computed_values_map, invalid)
+ } else {
+ Err(())
+ }
}
- }).is_ok() {
- substituted.push_str(input.slice_from(start));
- substituted
+ );
+ if let Ok(last_token_type) = result {
+ partial_computed_value.push_from(position, &input, last_token_type);
+ partial_computed_value
} else {
// Invalid at computed-value time. Use the inherited value.
- if let Some(value) = inherited.as_ref().and_then(|i| i.get(name)) {
- value.clone()
+ if let Some(inherited_value) = inherited.as_ref().and_then(|i| i.get(name)) {
+ inherited_value.clone()
} else {
invalid.insert(name.clone());
return Err(())
}
}
} else {
- value.value.to_owned()
+ // The specified value contains no var() reference
+ ComputedValue {
+ css: specified_value.css.to_owned(),
+ first_token_type: specified_value.first_token_type,
+ last_token_type: specified_value.last_token_type,
+ }
};
- if let Some(substituted) = substituted {
- substituted.push_str(&value)
+ if let Some(partial_computed_value) = partial_computed_value {
+ partial_computed_value.push_variable(&computed_value)
}
- substituted_map.insert(name.clone(), value);
- Ok(())
+ let last_token_type = computed_value.last_token_type;
+ computed_values_map.insert(name.clone(), computed_value);
+ Ok(last_token_type)
}
/// 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.
+/// the callback should return `Err(())` and leave `partial_computed_value` unchanged.
/// Otherwise, it should push the value of the variable (with its own `var()` functions replaced)
-/// to `substituted` and return `Ok(())`.
+/// to `partial_computed_value` and return `Ok(last_token_type of what was pushed)`
///
/// Return `Err(())` if `input` is invalid at computed-value time.
+/// or `Ok(last_token_type that was pushed to partial_computed_value)` otherwise.
fn substitute_block<F>(input: &mut Parser,
- start: &mut SourcePosition,
- substituted: &mut String,
+ position: &mut (SourcePosition, TokenSerializationType),
+ partial_computed_value: &mut ComputedValue,
substitute_one: &mut F)
- -> Result<(), ()>
- where F: FnMut(&Name, &mut String) -> Result<(), ()> {
+ -> Result<TokenSerializationType, ()>
+ where F: FnMut(&Name, &mut ComputedValue) -> Result<TokenSerializationType, ()> {
+ let mut last_token_type = TokenSerializationType::nothing();
+ let mut set_position_at_next_iteration = false;
loop {
- let input_slice = input.slice_from(*start);
- let token = if let Ok(token) = input.next() { token } else { break };
+ let before_this_token = input.position();
+ let next = input.next_including_whitespace_and_comments();
+ if set_position_at_next_iteration {
+ *position = (before_this_token, match next {
+ Ok(ref token) => token.serialization_type(),
+ Err(()) => TokenSerializationType::nothing(),
+ });
+ set_position_at_next_iteration = false;
+ }
+ let token = if let Ok(token) = next {
+ token
+ } else {
+ break
+ };
match token {
- Token::Function(ref name) if name == "var" => {
- substituted.push_str(input_slice);
+ Token::Function(ref name) if name.eq_ignore_ascii_case("var") => {
+ partial_computed_value.push(
+ input.slice(position.0..before_this_token), position.1, last_token_type);
try!(input.parse_nested_block(|input| {
// parse_var_function() ensures neither .unwrap() will fail.
let name = input.expect_ident().unwrap();
- let name = parse_name(&name).unwrap();
+ let name = Atom::from_slice(parse_name(&name).unwrap());
- if substitute_one(&name, substituted).is_ok() {
+ if let Ok(last) = substitute_one(&name, partial_computed_value) {
+ last_token_type = last;
// 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.
while let Ok(_) = input.next() {}
} else {
try!(input.expect_comma());
- let mut start = input.position();
- try!(substitute_block(input, &mut start, substituted, substitute_one));
- substituted.push_str(input.slice_from(start));
+ let position = input.position();
+ let first_token_type = input.next_including_whitespace_and_comments()
+ // parse_var_function() ensures that .unwrap() will not fail.
+ .unwrap()
+ .serialization_type();
+ input.reset(position);
+ let mut position = (position, first_token_type);
+ last_token_type = try!(substitute_block(
+ input, &mut position, partial_computed_value, substitute_one));
+ partial_computed_value.push_from(position, input, last_token_type);
}
Ok(())
}));
- *start = input.position();
+ set_position_at_next_iteration = true
}
Token::Function(_) |
@@ -343,11 +425,13 @@ fn substitute_block<F>(input: &mut Parser,
Token::CurlyBracketBlock |
Token::SquareBracketBlock => {
try!(input.parse_nested_block(|input| {
- substitute_block(input, start, substituted, substitute_one)
+ substitute_block(input, position, partial_computed_value, substitute_one)
}));
+ // It’s the same type for CloseCurlyBracket and CloseSquareBracket.
+ last_token_type = Token::CloseParenthesis.serialization_type();
}
- _ => {}
+ _ => last_token_type = token.serialization_type()
}
}
// FIXME: deal with things being implicitly closed at the end of the input. E.g.
@@ -356,31 +440,27 @@ fn substitute_block<F>(input: &mut Parser,
// <p style="background: var(--color) var(--image) top left; --image: url('a.png"></p>
// </div>
// ```
- Ok(())
+ Ok(last_token_type)
}
/// 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<Name, String>>>)
+pub fn substitute(input: &str, first_token_type: TokenSerializationType,
+ computed_values_map: &Option<Arc<HashMap<Name, ComputedValue>>>)
-> 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 substituted = ComputedValue::empty();
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(())
+ let mut position = (input.position(), first_token_type);
+ let last_token_type = try!(substitute_block(
+ &mut input, &mut position, &mut substituted, &mut |name, substituted| {
+ if let Some(value) = computed_values_map.as_ref().and_then(|map| map.get(name)) {
+ substituted.push_variable(value);
+ Ok(value.last_token_type)
+ } else {
+ Err(())
+ }
}
- }));
- substituted.push_str(input.slice_from(start));
- Ok(substituted)
+ ));
+ substituted.push_from(position, &input, last_token_type);
+ Ok(substituted.css)
}
diff --git a/components/style/lib.rs b/components/style/lib.rs
index 60e137a1ab0..425a5eafb06 100644
--- a/components/style/lib.rs
+++ b/components/style/lib.rs
@@ -5,6 +5,7 @@
#![feature(arc_unique)]
#![feature(box_syntax)]
#![feature(box_patterns)]
+#![feature(concat_idents)]
#![feature(core_intrinsics)]
#![feature(custom_attribute)]
#![feature(custom_derive)]
diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs
index 79e1a5e7182..374b9b875d0 100644
--- a/components/style/properties.mako.rs
+++ b/components/style/properties.mako.rs
@@ -5,8 +5,7 @@
// This file is a Mako template: http://www.makotemplates.org/
use std::ascii::AsciiExt;
-use std::borrow::ToOwned;
-use std::collections::{HashSet, HashMap};
+use std::collections::HashSet;
use std::default::Default;
use std::fmt;
use std::fmt::Debug;
@@ -16,7 +15,7 @@ use std::mem;
use std::sync::Arc;
use cssparser::{Parser, Color, RGBA, AtRuleParser, DeclarationParser,
- DeclarationListParser, parse_important, ToCss};
+ DeclarationListParser, parse_important, ToCss, TokenSerializationType};
use url::Url;
use util::geometry::Au;
use util::logical_geometry::{LogicalMargin, PhysicalSide, WritingMode};
@@ -115,6 +114,7 @@ pub mod longhands {
derived_from=derived_from,
custom_cascade=custom_cascade,
experimental=experimental)
+ property.style_struct = THIS_STYLE_STRUCT
THIS_STYLE_STRUCT.longhands.append(property)
LONGHANDS.append(property)
LONGHANDS_BY_NAME[name] = property
@@ -128,7 +128,7 @@ pub mod longhands {
% if derived_from is None:
use cssparser::Parser;
use parser::ParserContext;
- use properties::{CSSWideKeyword, DeclaredValue};
+ use properties::{CSSWideKeyword, DeclaredValue, Shorthand};
% endif
use properties::longhands;
use properties::property_bit_field::PropertyBitField;
@@ -157,7 +157,7 @@ pub mod longhands {
return
}
seen.set_${property.ident}();
- let computed_value = substitute_variables(
+ let computed_value = ::properties::substitute_variables_${property.ident}(
declared_value, &style.custom_properties, |value| match *value {
DeclaredValue::Value(ref specified_value) => {
specified_value.to_computed_value(&context)
@@ -193,29 +193,6 @@ pub mod longhands {
% endif
}
% if derived_from is None:
- pub fn substitute_variables<F, R>(value: &DeclaredValue<SpecifiedValue>,
- custom_properties: &Option<Arc<HashMap<Atom, String>>>,
- f: F)
- -> R
- where F: FnOnce(&DeclaredValue<SpecifiedValue>) -> R {
- if let DeclaredValue::WithVariables { ref css, ref base_url } = *value {
- f(&
- ::custom_properties::substitute(css, custom_properties)
- .and_then(|css| {
- // As of this writing, only the base URL is used for property values:
- let context = ParserContext::new(
- ::stylesheets::Origin::Author, base_url);
- parse_specified(&context, &mut Parser::new(&css))
- })
- .unwrap_or(
- // Invalid at computed-value time.
- DeclaredValue::${"Inherit" if THIS_STYLE_STRUCT.inherited else "Initial"}
- )
- )
- } else {
- f(value)
- }
- }
pub fn parse_declared(context: &ParserContext, input: &mut Parser)
-> Result<DeclaredValue<SpecifiedValue>, ()> {
match input.try(CSSWideKeyword::parse) {
@@ -227,13 +204,19 @@ pub mod longhands {
input.look_for_var_functions();
let start = input.position();
let specified = parse_specified(context, input);
+ if specified.is_err() {
+ while let Ok(_) = input.next() {} // Look for var() after the error.
+ }
let var = input.seen_var_functions();
if specified.is_err() && var {
input.reset(start);
- try!(::custom_properties::parse_declaration_value(input, &mut None));
+ let (first_token_type, _) = try!(
+ ::custom_properties::parse_declaration_value(input, &mut None));
return Ok(DeclaredValue::WithVariables {
css: input.slice_from(start).to_owned(),
+ first_token_type: first_token_type,
base_url: context.base_url.clone(),
+ from_shorthand: Shorthand::None,
})
}
specified
@@ -4881,7 +4864,7 @@ pub mod shorthands {
pub mod ${shorthand.ident} {
use cssparser::Parser;
use parser::ParserContext;
- use properties::longhands;
+ use properties::{longhands, PropertyDeclaration, DeclaredValue, Shorthand};
pub struct Longhands {
% for sub_property in shorthand.sub_properties:
@@ -4890,8 +4873,46 @@ pub mod shorthands {
% endfor
}
+ pub fn parse(context: &ParserContext, input: &mut Parser,
+ declarations: &mut Vec<PropertyDeclaration>)
+ -> Result<(), ()> {
+ input.look_for_var_functions();
+ let start = input.position();
+ let value = parse_value(context, input);
+ let var = input.seen_var_functions();
+ if let Ok(value) = value {
+ % for sub_property in shorthand.sub_properties:
+ declarations.push(PropertyDeclaration::${sub_property.camel_case}(
+ match value.${sub_property.ident} {
+ Some(value) => DeclaredValue::Value(value),
+ None => DeclaredValue::Initial,
+ }
+ ));
+ % endfor
+ Ok(())
+ } else if var {
+ input.reset(start);
+ let (first_token_type, _) = try!(
+ ::custom_properties::parse_declaration_value(input, &mut None));
+ let css = input.slice_from(start);
+ % for sub_property in shorthand.sub_properties:
+ declarations.push(PropertyDeclaration::${sub_property.camel_case}(
+ DeclaredValue::WithVariables {
+ css: css.to_owned(),
+ first_token_type: first_token_type,
+ base_url: context.base_url.clone(),
+ from_shorthand: Shorthand::${shorthand.camel_case},
+ }
+ ));
+ % endfor
+ Ok(())
+ } else {
+ Err(())
+ }
+ }
+
#[allow(unused_variables)]
- pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
+ pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
${caller.body()}
}
}
@@ -5582,6 +5603,55 @@ mod property_bit_field {
}
}
+% for property in LONGHANDS:
+ % if property.derived_from is None:
+ fn substitute_variables_${property.ident}<F, R>(
+ value: &DeclaredValue<longhands::${property.ident}::SpecifiedValue>,
+ custom_properties: &Option<Arc<::custom_properties::ComputedValuesMap>>,
+ f: F)
+ -> R
+ where F: FnOnce(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>) -> R
+ {
+ if let DeclaredValue::WithVariables {
+ ref css, first_token_type, ref base_url, from_shorthand
+ } = *value {
+ f(&
+ ::custom_properties::substitute(css, first_token_type, custom_properties)
+ .and_then(|css| {
+ // As of this writing, only the base URL is used for property values:
+ let context = ParserContext::new(
+ ::stylesheets::Origin::Author, base_url);
+ Parser::new(&css).parse_entirely(|input| {
+ match from_shorthand {
+ Shorthand::None => {
+ longhands::${property.ident}::parse_specified(&context, input)
+ }
+ % for shorthand in SHORTHANDS:
+ % if property in shorthand.sub_properties:
+ Shorthand::${shorthand.camel_case} => {
+ shorthands::${shorthand.ident}::parse_value(&context, input)
+ .map(|result| match result.${property.ident} {
+ Some(value) => DeclaredValue::Value(value),
+ None => DeclaredValue::Initial,
+ })
+ }
+ % endif
+ % endfor
+ _ => unreachable!()
+ }
+ })
+ })
+ .unwrap_or(
+ // Invalid at computed-value time.
+ DeclaredValue::${"Inherit" if property.style_struct.inherited else "Initial"}
+ )
+ )
+ } else {
+ f(value)
+ }
+ }
+ % endif
+% endfor
/// Declarations are stored in reverse order.
/// Overridden declarations are skipped.
@@ -5720,11 +5790,24 @@ impl CSSWideKeyword {
}
}
+#[derive(Clone, Copy, Eq, PartialEq, Debug)]
+pub enum Shorthand {
+ None,
+ % for property in SHORTHANDS:
+ ${property.camel_case},
+ % endfor
+}
+
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum DeclaredValue<T> {
Value(T),
- WithVariables { css: String, base_url: Url },
+ WithVariables {
+ css: String,
+ first_token_type: TokenSerializationType,
+ base_url: Url,
+ from_shorthand: Shorthand
+ },
Initial,
Inherit,
// There is no Unset variant here.
@@ -5732,13 +5815,17 @@ pub enum DeclaredValue<T> {
// depending on whether the property is inherited.
}
-impl<T: ToCss> DeclaredValue<T> {
- pub fn specified_value(&self) -> String {
+impl<T: ToCss> ToCss for DeclaredValue<T> {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
- DeclaredValue::Value(ref inner) => inner.to_css_string(),
- DeclaredValue::WithVariables { ref css, .. } => css.clone(),
- DeclaredValue::Initial => "initial".to_owned(),
- DeclaredValue::Inherit => "inherit".to_owned(),
+ DeclaredValue::Value(ref inner) => inner.to_css(dest),
+ DeclaredValue::WithVariables { ref css, from_shorthand: Shorthand::None, .. } => {
+ dest.write_str(css)
+ }
+ // https://drafts.csswg.org/css-variables/#variables-in-shorthands
+ DeclaredValue::WithVariables { .. } => Ok(()),
+ DeclaredValue::Initial => dest.write_str("initial"),
+ DeclaredValue::Inherit => dest.write_str("inherit"),
}
}
}
@@ -5748,7 +5835,7 @@ pub enum PropertyDeclaration {
% for property in LONGHANDS:
${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
% endfor
- Custom(::custom_properties::Name, DeclaredValue<::custom_properties::Value>),
+ Custom(::custom_properties::Name, DeclaredValue<::custom_properties::SpecifiedValue>),
}
@@ -5760,15 +5847,52 @@ pub enum PropertyDeclarationParseResult {
ValidOrIgnoredDeclaration,
}
+#[derive(Eq, PartialEq, Clone)]
+pub enum PropertyDeclarationName {
+ Longhand(&'static str),
+ Custom(::custom_properties::Name),
+ Internal
+}
+
+impl PartialEq<str> for PropertyDeclarationName {
+ fn eq(&self, other: &str) -> bool {
+ match *self {
+ PropertyDeclarationName::Longhand(n) => n == other,
+ PropertyDeclarationName::Custom(ref n) => {
+ ::custom_properties::parse_name(other) == Ok(&**n)
+ }
+ PropertyDeclarationName::Internal => false,
+ }
+ }
+}
+
+impl fmt::Display for PropertyDeclarationName {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ PropertyDeclarationName::Longhand(n) => f.write_str(n),
+ PropertyDeclarationName::Custom(ref n) => {
+ try!(f.write_str("--"));
+ f.write_str(n)
+ }
+ PropertyDeclarationName::Internal => Ok(()),
+ }
+ }
+}
+
impl PropertyDeclaration {
- pub fn name(&self) -> &'static str {
+ pub fn name(&self) -> PropertyDeclarationName {
match *self {
% for property in LONGHANDS:
% if property.derived_from is None:
- PropertyDeclaration::${property.camel_case}(..) => "${property.name}",
+ PropertyDeclaration::${property.camel_case}(..) => {
+ PropertyDeclarationName::Longhand("${property.name}")
+ }
% endif
% endfor
- _ => "",
+ PropertyDeclaration::Custom(ref name, _) => {
+ PropertyDeclarationName::Custom(name.clone())
+ }
+ _ => PropertyDeclarationName::Internal,
}
}
@@ -5777,10 +5901,11 @@ impl PropertyDeclaration {
% for property in LONGHANDS:
% if property.derived_from is None:
PropertyDeclaration::${property.camel_case}(ref value) =>
- value.specified_value(),
+ value.to_css_string(),
% endif
% endfor
- ref decl => panic!("unsupported property declaration: {:?}", decl.name()),
+ PropertyDeclaration::Custom(_, ref value) => value.to_css_string(),
+ ref decl => panic!("unsupported property declaration: {}", decl.name()),
}
}
@@ -5793,6 +5918,9 @@ impl PropertyDeclaration {
}
% endif
% endfor
+ PropertyDeclaration::Custom(ref declaration_name, _) => {
+ ::custom_properties::parse_name(name) == Ok(&**declaration_name)
+ }
_ => false,
}
}
@@ -5809,7 +5937,7 @@ impl PropertyDeclaration {
Err(()) => return PropertyDeclarationParseResult::InvalidValue,
}
};
- result_list.push(PropertyDeclaration::Custom(name, value));
+ result_list.push(PropertyDeclaration::Custom(Atom::from_slice(name), value));
return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration;
}
match_ignore_ascii_case! { name,
@@ -5865,18 +5993,8 @@ impl PropertyDeclaration {
% endfor
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
},
- Err(()) => match shorthands::${shorthand.ident}::parse(context, input) {
- Ok(result) => {
- % for sub_property in shorthand.sub_properties:
- result_list.push(PropertyDeclaration::${sub_property.camel_case}(
- match result.${sub_property.ident} {
- Some(value) => DeclaredValue::Value(value),
- None => DeclaredValue::Initial,
- }
- ));
- % endfor
- PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
- },
+ Err(()) => match shorthands::${shorthand.ident}::parse(context, input, result_list) {
+ Ok(()) => PropertyDeclarationParseResult::ValidOrIgnoredDeclaration,
Err(()) => PropertyDeclarationParseResult::InvalidValue,
}
}
@@ -5919,7 +6037,7 @@ pub struct ComputedValues {
% for style_struct in STYLE_STRUCTS:
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
- custom_properties: Option<Arc<HashMap<::custom_properties::Name, String>>>,
+ custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
shareable: bool,
pub writing_mode: WritingMode,
pub root_font_size: Au,
@@ -6077,14 +6195,19 @@ impl ComputedValues {
}
% endfor
- pub fn computed_value_to_string(&self, name: &str) -> Option<String> {
+ pub fn computed_value_to_string(&self, name: &str) -> Result<String, ()> {
match name {
% for style_struct in STYLE_STRUCTS:
% for longhand in style_struct.longhands:
- "${longhand.name}" => Some(self.${style_struct.ident}.${longhand.ident}.to_css_string()),
+ "${longhand.name}" => Ok(self.${style_struct.ident}.${longhand.ident}.to_css_string()),
% endfor
% endfor
- _ => None
+ _ => {
+ let name = try!(::custom_properties::parse_name(name));
+ let map = try!(self.custom_properties.as_ref().ok_or(()));
+ let value = try!(map.get(&Atom::from_slice(name)).ok_or(()));
+ Ok(value.to_css_string())
+ }
}
}
}
@@ -6153,7 +6276,7 @@ fn cascade_with_cached_declarations(
shareable: bool,
parent_style: &ComputedValues,
cached_style: &ComputedValues,
- custom_properties: Option<Arc<HashMap<Atom, String>>>,
+ custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
context: &computed::Context)
-> ComputedValues {
% for style_struct in STYLE_STRUCTS:
@@ -6182,7 +6305,7 @@ fn cascade_with_cached_declarations(
}
seen.set_${property.ident}();
let computed_value =
- longhands::${property.ident}::substitute_variables(
+ substitute_variables_${property.ident}(
declared_value, &custom_properties, |value| match *value {
DeclaredValue::Value(ref specified_value)
=> specified_value.to_computed_value(context),
@@ -6354,7 +6477,7 @@ pub fn cascade(viewport_size: Size2D<Au>,
// This assumes that the computed and specified values have the same Rust type.
macro_rules! get_specified(
($style_struct_getter: ident, $property: ident, $declared_value: expr) => {
- longhands::$property::substitute_variables(
+ concat_idents!(substitute_variables_, $property)(
$declared_value, &custom_properties, |value| match *value {
DeclaredValue::Value(specified_value) => specified_value,
DeclaredValue::Initial => longhands::$property::get_initial_value(),
@@ -6374,7 +6497,7 @@ pub fn cascade(viewport_size: Size2D<Au>,
for declaration in sub_list.declarations.iter().rev() {
match *declaration {
PropertyDeclaration::FontSize(ref value) => {
- context.font_size = longhands::font_size::substitute_variables(
+ context.font_size = substitute_variables_font_size(
value, &custom_properties, |value| match *value {
DeclaredValue::Value(ref specified_value) => {
match specified_value.0 {
@@ -6395,7 +6518,7 @@ pub fn cascade(viewport_size: Size2D<Au>,
);
}
PropertyDeclaration::Color(ref value) => {
- context.color = longhands::color::substitute_variables(
+ context.color = substitute_variables_color(
value, &custom_properties, |value| match *value {
DeclaredValue::Value(ref specified_value) => {
specified_value.parsed
@@ -6700,7 +6823,7 @@ pub fn is_supported_property(property: &str) -> bool {
"${property.name}" => true,
% endfor
"${LONGHANDS[-1].name}" => true
- _ => false
+ _ => property.starts_with("--")
}
}
diff --git a/components/util/mem.rs b/components/util/mem.rs
index f336d22be0e..7f83099b06e 100644
--- a/components/util/mem.rs
+++ b/components/util/mem.rs
@@ -15,7 +15,7 @@ use std::sync::Arc;
use azure::azure_hl::Color;
use cssparser::Color as CSSParserColor;
-use cssparser::RGBA;
+use cssparser::{RGBA, TokenSerializationType};
use cursor::Cursor;
use euclid::length::Length;
use euclid::scale_factor::ScaleFactor;
@@ -414,6 +414,7 @@ known_heap_size!(0, Rect<T>, Point2D<T>, Size2D<T>, Matrix2D<T>, SideOffsets2D<T
known_heap_size!(0, Length<T, U>, ScaleFactor<T, U, V>);
known_heap_size!(0, Au, WritingMode, CSSParserColor, Color, RGBA, Cursor, Matrix4, Atom, Namespace);
-known_heap_size!(0, JSVal, PagePx, ViewportPx, DevicePixel, QuirksMode, OsRng, RawStatus, LengthOrPercentageOrAuto);
+known_heap_size!(0, JSVal, PagePx, ViewportPx, DevicePixel, QuirksMode, OsRng, RawStatus);
+known_heap_size!(0, TokenSerializationType, LengthOrPercentageOrAuto);
known_heap_size!(0, PseudoElement, Combinator, str);