From 191201de6c007051c1cfe28eeab66de4ad3ee7ce Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 1 Sep 2017 15:04:27 -0700 Subject: [PATCH 1/2] Serialize shorthands in preferred order. --- components/style/properties/properties.mako.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index b66a1a44ec3..33d8444a832 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -476,15 +476,28 @@ impl LonghandId { // algorithms and what not, I guess. <% longhand_to_shorthand_map = {} + num_sub_properties = {} for shorthand in data.shorthands: + num_sub_properties[shorthand.camel_case] = len(shorthand.sub_properties) for sub_property in shorthand.sub_properties: if sub_property.ident not in longhand_to_shorthand_map: longhand_to_shorthand_map[sub_property.ident] = [] longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case) + def preferred_order(x, y): + # Since we want properties in order from most subproperties to least, + # reverse the arguments to cmp from the expected order. + result = cmp(num_sub_properties.get(y, 0), num_sub_properties.get(x, 0)) + if result: + return result + # Fall back to lexicographic comparison. + return cmp(x, y) + + # Sort the lists of shorthand properties according to preferred order: + # https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order for shorthand_list in longhand_to_shorthand_map.itervalues(): - shorthand_list.sort() + shorthand_list.sort(cmp=preferred_order) %> // based on lookup results for each longhand, create result arrays From 5b83beacebc8f8512898d1a52fe50d8c235ce9f7 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 1 Sep 2017 15:06:08 -0700 Subject: [PATCH 2/2] Consider serializing shorthands based on previously-serialized longhands. --- .../style/properties/declaration_block.rs | 15 +++--- tests/unit/style/properties/serialization.rs | 47 +++++++++++++++++++ 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs index c458bad1717..a87b87fe9db 100644 --- a/components/style/properties/declaration_block.rs +++ b/components/style/properties/declaration_block.rs @@ -657,10 +657,6 @@ impl ToCss for PropertyDeclarationBlock { if is_system_font { for (longhand, importance) in self.declaration_importance_iter() { - if already_serialized.contains(longhand.id()) { - continue; - } - if longhand.get_system().is_some() || longhand.is_default_line_height() { current_longhands.push(longhand); if found_system.is_none() { @@ -673,10 +669,6 @@ impl ToCss for PropertyDeclarationBlock { } } else { for (longhand, importance) in self.declaration_importance_iter() { - if already_serialized.contains(longhand.id()) { - continue; - } - if longhand.id().is_longhand_of(shorthand) { current_longhands.push(longhand); if importance.important() { @@ -771,6 +763,13 @@ impl ToCss for PropertyDeclarationBlock { // Substep 9 already_serialized.insert(current_longhand.id()); } + + // FIXME(https://github.com/w3c/csswg-drafts/issues/1774) + // The specification does not include an instruction to abort + // the shorthand loop at this point, but doing so both matches + // Gecko and makes sense since shorthands are checked in + // preferred order. + break; } } diff --git a/tests/unit/style/properties/serialization.rs b/tests/unit/style/properties/serialization.rs index 751db0eab32..3216293ab75 100644 --- a/tests/unit/style/properties/serialization.rs +++ b/tests/unit/style/properties/serialization.rs @@ -341,6 +341,53 @@ mod shorthand_serialization { mod border_shorthands { use super::*; + #[test] + fn border_top_and_color() { + let mut properties = Vec::new(); + properties.push(PropertyDeclaration::BorderTopWidth(BorderSideWidth::Length(Length::from_px(1.)))); + properties.push(PropertyDeclaration::BorderTopStyle(BorderStyle::solid)); + let c = Color::Numeric { + parsed: RGBA::new(255, 0, 0, 255), + authored: Some("green".to_string().into_boxed_str()) + }; + properties.push(PropertyDeclaration::BorderTopColor(c)); + let c = Color::Numeric { + parsed: RGBA::new(0, 255, 0, 255), + authored: Some("red".to_string().into_boxed_str()) + }; + properties.push(PropertyDeclaration::BorderTopColor(c.clone())); + properties.push(PropertyDeclaration::BorderBottomColor(c.clone())); + properties.push(PropertyDeclaration::BorderLeftColor(c.clone())); + properties.push(PropertyDeclaration::BorderRightColor(c.clone())); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "border-top: 1px solid red; border-color: red;"); + } + + #[test] + fn border_color_and_top() { + let mut properties = Vec::new(); + let c = Color::Numeric { + parsed: RGBA::new(0, 255, 0, 255), + authored: Some("red".to_string().into_boxed_str()) + }; + properties.push(PropertyDeclaration::BorderTopColor(c.clone())); + properties.push(PropertyDeclaration::BorderBottomColor(c.clone())); + properties.push(PropertyDeclaration::BorderLeftColor(c.clone())); + properties.push(PropertyDeclaration::BorderRightColor(c.clone())); + + properties.push(PropertyDeclaration::BorderTopWidth(BorderSideWidth::Length(Length::from_px(1.)))); + properties.push(PropertyDeclaration::BorderTopStyle(BorderStyle::solid)); + let c = Color::Numeric { + parsed: RGBA::new(255, 0, 0, 255), + authored: Some("green".to_string().into_boxed_str()) + }; + properties.push(PropertyDeclaration::BorderTopColor(c)); + + let serialization = shorthand_properties_to_string(properties); + assert_eq!(serialization, "border-color: green red red; border-top: 1px solid green;"); + } + // we can use border-top as a base to test out the different combinations // but afterwards, we only need to to one test per "directional border shorthand"