From feaf6f4c3fbee9cafb3fdf9981c3ab639a56195b Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 11 Sep 2015 17:44:53 +0200 Subject: [PATCH] Initial support for custom properties in CSSStyleDeclaration --- components/layout/layout_task.rs | 2 +- components/script/dom/element.rs | 7 +- components/style/custom_properties.rs | 23 +++- components/style/properties.mako.rs | 82 +++++++++--- tests/wpt/mozilla/meta/MANIFEST.json | 12 ++ ...t_variable_serialization_computed.html.ini | 56 ++++++++ ..._variable_serialization_specified.html.ini | 116 +++++++++++++++++ .../test_variable_serialization_computed.html | 79 ++++++++++++ ...test_variable_serialization_specified.html | 121 ++++++++++++++++++ 9 files changed, 471 insertions(+), 27 deletions(-) create mode 100644 tests/wpt/mozilla/meta/css/test_variable_serialization_computed.html.ini create mode 100644 tests/wpt/mozilla/meta/css/test_variable_serialization_specified.html.ini create mode 100644 tests/wpt/mozilla/tests/css/test_variable_serialization_computed.html create mode 100644 tests/wpt/mozilla/tests/css/test_variable_serialization_specified.html 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/style/custom_properties.rs b/components/style/custom_properties.rs index 851d830abc1..4a6f5076f56 100644 --- a/components/style/custom_properties.rs +++ b/components/style/custom_properties.rs @@ -2,10 +2,11 @@ * 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, Delimiter, TokenSerializationType}; +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; @@ -14,9 +15,9 @@ use util::mem::HeapSizeOf; pub type Name = Atom; // https://drafts.csswg.org/css-variables/#typedef-custom-property-name -pub fn parse_name(s: &str) -> Result { +pub fn parse_name(s: &str) -> Result<&str, ()> { if s.starts_with("--") { - Ok(Atom::from_slice(&s[2..])) + Ok(&s[2..]) } else { Err(()) } @@ -47,6 +48,18 @@ pub struct ComputedValue { last_token_type: TokenSerializationType, } +impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dest.write_str(&self.css) + } +} + +impl ToCss for ComputedValue { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dest.write_str(&self.css) + } +} + pub type ComputedValuesMap = HashMap; impl ComputedValue { @@ -157,7 +170,7 @@ 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(()) } @@ -381,7 +394,7 @@ fn substitute_block(input: &mut Parser, 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 let Ok(last) = substitute_one(&name, partial_computed_value) { last_token_type = last; diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index c8e52592c36..374b9b875d0 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -5,7 +5,6 @@ // This file is a Mako template: http://www.makotemplates.org/ use std::ascii::AsciiExt; -use std::borrow::ToOwned; use std::collections::HashSet; use std::default::Default; use std::fmt; @@ -23,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}; @@ -5815,17 +5815,17 @@ pub enum DeclaredValue { // depending on whether the property is inherited. } -impl DeclaredValue { - pub fn specified_value(&self) -> String { +impl ToCss for DeclaredValue { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self { - DeclaredValue::Value(ref inner) => inner.to_css_string(), + DeclaredValue::Value(ref inner) => inner.to_css(dest), DeclaredValue::WithVariables { ref css, from_shorthand: Shorthand::None, .. } => { - css.clone() + dest.write_str(css) } // https://drafts.csswg.org/css-variables/#variables-in-shorthands - DeclaredValue::WithVariables { .. } => String::new(), - DeclaredValue::Initial => "initial".to_owned(), - DeclaredValue::Inherit => "inherit".to_owned(), + DeclaredValue::WithVariables { .. } => Ok(()), + DeclaredValue::Initial => dest.write_str("initial"), + DeclaredValue::Inherit => dest.write_str("inherit"), } } } @@ -5847,15 +5847,52 @@ pub enum PropertyDeclarationParseResult { ValidOrIgnoredDeclaration, } +#[derive(Eq, PartialEq, Clone)] +pub enum PropertyDeclarationName { + Longhand(&'static str), + Custom(::custom_properties::Name), + Internal +} + +impl PartialEq 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, } } @@ -5864,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()), } } @@ -5880,6 +5918,9 @@ impl PropertyDeclaration { } % endif % endfor + PropertyDeclaration::Custom(ref declaration_name, _) => { + ::custom_properties::parse_name(name) == Ok(&**declaration_name) + } _ => false, } } @@ -5896,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, @@ -6154,14 +6195,19 @@ impl ComputedValues { } % endfor - pub fn computed_value_to_string(&self, name: &str) -> Option { + pub fn computed_value_to_string(&self, name: &str) -> Result { 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()) + } } } } @@ -6777,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/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index aa587fcf670..3cd5550686c 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -287,6 +287,18 @@ "url": "/_mozilla/css/test_variable_legal_values.html" } ], + "css/test_variable_serialization_computed.html": [ + { + "path": "css/test_variable_serialization_computed.html", + "url": "/_mozilla/css/test_variable_serialization_computed.html" + } + ], + "css/test_variable_serialization_specified.html": [ + { + "path": "css/test_variable_serialization_specified.html", + "url": "/_mozilla/css/test_variable_serialization_specified.html" + } + ], "mozilla/DOMParser.html": [ { "path": "mozilla/DOMParser.html", diff --git a/tests/wpt/mozilla/meta/css/test_variable_serialization_computed.html.ini b/tests/wpt/mozilla/meta/css/test_variable_serialization_computed.html.ini new file mode 100644 index 00000000000..1e88bbd62e9 --- /dev/null +++ b/tests/wpt/mozilla/meta/css/test_variable_serialization_computed.html.ini @@ -0,0 +1,56 @@ +[test_variable_serialization_computed.html] + type: testharness + [subtest #20 with `--a: var(--b)var(--c); --b:orange; --c:red;`] + expected: FAIL + + [subtest #21 with `--a: var(--b)var(--c,red); --b:orange;`] + expected: FAIL + + [subtest #22 with `--a: var(--b,orange)var(--c); --c:red;`] + expected: FAIL + + [subtest #23 with `counter-reset: var(--a)red; --a:orange;`] + expected: FAIL + + [subtest #24 with `--a: var(--b)var(--c); --c:[c\]; --b:('ab`] + expected: FAIL + + [subtest #25 with `--a: '`] + expected: FAIL + + [subtest #26 with `--a: '\\`] + expected: FAIL + + [subtest #27 with `--a: \\`] + expected: FAIL + + [subtest #28 with `--a: "`] + expected: FAIL + + [subtest #29 with `--a: "\\`] + expected: FAIL + + [subtest #30 with `--a: /* abc `] + expected: FAIL + + [subtest #31 with `--a: /* abc *`] + expected: FAIL + + [subtest #32 with `--a: url(http://example.org/`] + expected: FAIL + + [subtest #33 with `--a: url(http://example.org/\\`] + expected: FAIL + + [subtest #34 with `--a: url('http://example.org/`] + expected: FAIL + + [subtest #35 with `--a: url('http://example.org/\\`] + expected: FAIL + + [subtest #36 with `--a: url("http://example.org/`] + expected: FAIL + + [subtest #37 with `--a: url("http://example.org/\\`] + expected: FAIL + diff --git a/tests/wpt/mozilla/meta/css/test_variable_serialization_specified.html.ini b/tests/wpt/mozilla/meta/css/test_variable_serialization_specified.html.ini new file mode 100644 index 00000000000..b4b7a38b3f5 --- /dev/null +++ b/tests/wpt/mozilla/meta/css/test_variable_serialization_specified.html.ini @@ -0,0 +1,116 @@ +[test_variable_serialization_specified.html] + type: testharness + [`var(--a)` is unchanged by specified value serialization] + expected: FAIL + + [`var(--a) ` is unchanged by specified value serialization] + expected: FAIL + + [`var( --a ) ` is unchanged by specified value serialization] + expected: FAIL + + [`var(--a, )` is unchanged by specified value serialization] + expected: FAIL + + [`var(--a,/**/a)` is unchanged by specified value serialization] + expected: FAIL + + [`1px var(--a)` is unchanged by specified value serialization] + expected: FAIL + + [`var(--a) 1px` is unchanged by specified value serialization] + expected: FAIL + + [`something 3px url(whereever) calc(var(--a) + 1px)` is unchanged by specified value serialization] + expected: FAIL + + [`var(--a)var(--b)` is unchanged by specified value serialization] + expected: FAIL + + [`var(--a, var(--b, var(--c, black)))` is unchanged by specified value serialization] + expected: FAIL + + [`var(--a) var(--a)` is unchanged by specified value serialization] + expected: FAIL + + [`{ [ var(--a) \] }` is unchanged by specified value serialization] + expected: FAIL + + [`[;\] var(--a)` is unchanged by specified value serialization] + expected: FAIL + + [`var(--a,(;))` is unchanged by specified value serialization] + expected: FAIL + + [`VAR(--a)` is unchanged by specified value serialization] + expected: FAIL + + [`var(--0)` is unchanged by specified value serialization] + expected: FAIL + + [`var(--\\30)` is unchanged by specified value serialization] + expected: FAIL + + [`var(--\\d800)` is unchanged by specified value serialization] + expected: FAIL + + [`var(--\\ffffff)` is unchanged by specified value serialization] + expected: FAIL + + [`var(--a` becomes `var(--a)` in specified value serialization] + expected: FAIL + + [`var(--a , ` becomes `var(--a , )` in specified value serialization] + expected: FAIL + + [`var(--a, ` becomes `var(--a, )` in specified value serialization] + expected: FAIL + + [`var(--a, var(--b` becomes `var(--a, var(--b))` in specified value serialization] + expected: FAIL + + [`var(--a /* unclosed comment` becomes `var(--a /* unclosed comment*/)` in specified value serialization] + expected: FAIL + + [`var(--a /* unclosed comment *` becomes `var(--a /* unclosed comment */)` in specified value serialization] + expected: FAIL + + [`[{(((var(--a` becomes `[{(((var(--a))))}\]` in specified value serialization] + expected: FAIL + + [`var(--a, "unclosed string` becomes `var(--a, "unclosed string")` in specified value serialization] + expected: FAIL + + [`var(--a, 'unclosed string` becomes `var(--a, 'unclosed string')` in specified value serialization] + expected: FAIL + + [`var(--a) "unclosed string\\` becomes `var(--a) "unclosed string"` in specified value serialization] + expected: FAIL + + [`var(--a) 'unclosed string\\` becomes `var(--a) 'unclosed string'` in specified value serialization] + expected: FAIL + + [`var(--a) \\` becomes `var(--a) \\�` in specified value serialization] + expected: FAIL + + [`var(--a) url(unclosedurl` becomes `var(--a) url(unclosedurl)` in specified value serialization] + expected: FAIL + + [`var(--a) url('unclosedurl` becomes `var(--a) url('unclosedurl')` in specified value serialization] + expected: FAIL + + [`var(--a) url("unclosedurl` becomes `var(--a) url("unclosedurl")` in specified value serialization] + expected: FAIL + + [`var(--a) url(unclosedurl\\` becomes `var(--a) url(unclosedurl\\�)` in specified value serialization] + expected: FAIL + + [`var(--a) url('unclosedurl\\` becomes `var(--a) url('unclosedurl')` in specified value serialization] + expected: FAIL + + [`var(--a) url("unclosedurl\\` becomes `var(--a) url("unclosedurl")` in specified value serialization] + expected: FAIL + diff --git a/tests/wpt/mozilla/tests/css/test_variable_serialization_computed.html b/tests/wpt/mozilla/tests/css/test_variable_serialization_computed.html new file mode 100644 index 00000000000..a7f0b63d1c4 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/test_variable_serialization_computed.html @@ -0,0 +1,79 @@ + +Test serialization of computed CSS variable values + + + + + +
+ +
+ + diff --git a/tests/wpt/mozilla/tests/css/test_variable_serialization_specified.html b/tests/wpt/mozilla/tests/css/test_variable_serialization_specified.html new file mode 100644 index 00000000000..cbb9e01e3fb --- /dev/null +++ b/tests/wpt/mozilla/tests/css/test_variable_serialization_specified.html @@ -0,0 +1,121 @@ + +Test serialization of specified CSS variable values + + + + + +
+ +