summaryrefslogtreecommitdiffstats
path: root/servo/tests/unit/style/properties/serialization.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/tests/unit/style/properties/serialization.rs')
-rw-r--r--servo/tests/unit/style/properties/serialization.rs731
1 files changed, 731 insertions, 0 deletions
diff --git a/servo/tests/unit/style/properties/serialization.rs b/servo/tests/unit/style/properties/serialization.rs
new file mode 100644
index 0000000000..fe1d158153
--- /dev/null
+++ b/servo/tests/unit/style/properties/serialization.rs
@@ -0,0 +1,731 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use properties::{parse, parse_input};
+use style::computed_values::display::T as Display;
+use style::properties::{PropertyDeclaration, Importance};
+use style::properties::declaration_block::PropertyDeclarationBlock;
+use style::properties::parse_property_declaration_list;
+use style::values::RGBA;
+use style::values::specified::{BorderStyle, BorderSideWidth, Color};
+use style::values::specified::{Length, LengthPercentage, LengthPercentageOrAuto};
+use style::values::specified::NoCalcLength;
+use style::values::specified::url::SpecifiedUrl;
+use style_traits::ToCss;
+use stylesheets::block_from;
+
+trait ToCssString {
+ fn to_css_string(&self) -> String;
+}
+
+impl ToCssString for PropertyDeclarationBlock {
+ fn to_css_string(&self) -> String {
+ let mut css = String::new();
+ self.to_css(&mut css).unwrap();
+ css
+ }
+}
+
+#[test]
+fn property_declaration_block_should_serialize_correctly() {
+ use style::properties::longhands::overflow_x::SpecifiedValue as OverflowValue;
+
+ let declarations = vec![
+ (PropertyDeclaration::Width(
+ LengthPercentageOrAuto::Length(NoCalcLength::from_px(70f32))),
+ Importance::Normal),
+
+ (PropertyDeclaration::MinHeight(
+ LengthPercentage::Length(NoCalcLength::from_px(20f32))),
+ Importance::Normal),
+
+ (PropertyDeclaration::Height(
+ LengthPercentageOrAuto::Length(NoCalcLength::from_px(20f32))),
+ Importance::Important),
+
+ (PropertyDeclaration::Display(Display::InlineBlock),
+ Importance::Normal),
+
+ (PropertyDeclaration::OverflowX(
+ OverflowValue::Auto),
+ Importance::Normal),
+
+ (PropertyDeclaration::OverflowY(
+ OverflowValue::Auto),
+ Importance::Normal),
+ ];
+
+ let block = block_from(declarations);
+
+ let css_string = block.to_css_string();
+
+ assert_eq!(
+ css_string,
+ "width: 70px; min-height: 20px; height: 20px !important; display: inline-block; overflow: auto;"
+ );
+}
+
+mod shorthand_serialization {
+ pub use super::*;
+
+ pub fn shorthand_properties_to_string(properties: Vec<PropertyDeclaration>) -> String {
+ let block = block_from(properties.into_iter().map(|d| (d, Importance::Normal)));
+
+ block.to_css_string()
+ }
+
+ mod four_sides_shorthands {
+ pub use super::*;
+
+ // we can use margin as a base to test out the different combinations
+ // but afterwards, we only need to to one test per "four sides shorthand"
+ #[test]
+ fn all_equal_properties_should_serialize_to_one_value() {
+ let mut properties = Vec::new();
+
+ let px_70 = LengthPercentageOrAuto::Length(NoCalcLength::from_px(70f32));
+ properties.push(PropertyDeclaration::MarginTop(px_70.clone()));
+ properties.push(PropertyDeclaration::MarginRight(px_70.clone()));
+ properties.push(PropertyDeclaration::MarginBottom(px_70.clone()));
+ properties.push(PropertyDeclaration::MarginLeft(px_70));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "margin: 70px;");
+ }
+
+ #[test]
+ fn equal_vertical_and_equal_horizontal_properties_should_serialize_to_two_value() {
+ let mut properties = Vec::new();
+
+ let vertical_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(10f32));
+ let horizontal_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(5f32));
+
+ properties.push(PropertyDeclaration::MarginTop(vertical_px.clone()));
+ properties.push(PropertyDeclaration::MarginRight(horizontal_px.clone()));
+ properties.push(PropertyDeclaration::MarginBottom(vertical_px));
+ properties.push(PropertyDeclaration::MarginLeft(horizontal_px));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "margin: 10px 5px;");
+ }
+
+ #[test]
+ fn different_vertical_and_equal_horizontal_properties_should_serialize_to_three_values() {
+ let mut properties = Vec::new();
+
+ let top_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(8f32));
+ let bottom_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(10f32));
+ let horizontal_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(5f32));
+
+ properties.push(PropertyDeclaration::MarginTop(top_px));
+ properties.push(PropertyDeclaration::MarginRight(horizontal_px.clone()));
+ properties.push(PropertyDeclaration::MarginBottom(bottom_px));
+ properties.push(PropertyDeclaration::MarginLeft(horizontal_px));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "margin: 8px 5px 10px;");
+ }
+
+ #[test]
+ fn different_properties_should_serialize_to_four_values() {
+ let mut properties = Vec::new();
+
+ let top_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(8f32));
+ let right_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(12f32));
+ let bottom_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(10f32));
+ let left_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(14f32));
+
+ properties.push(PropertyDeclaration::MarginTop(top_px));
+ properties.push(PropertyDeclaration::MarginRight(right_px));
+ properties.push(PropertyDeclaration::MarginBottom(bottom_px));
+ properties.push(PropertyDeclaration::MarginLeft(left_px));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "margin: 8px 12px 10px 14px;");
+ }
+
+ #[test]
+ fn different_longhands_should_serialize_to_long_form() {
+ let mut properties = Vec::new();
+
+ let solid = BorderStyle::Solid;
+
+ properties.push(PropertyDeclaration::BorderTopStyle(solid.clone()));
+ properties.push(PropertyDeclaration::BorderRightStyle(solid.clone()));
+ properties.push(PropertyDeclaration::BorderBottomStyle(solid.clone()));
+ properties.push(PropertyDeclaration::BorderLeftStyle(solid.clone()));
+
+ let px_30 = BorderSideWidth::Length(Length::from_px(30f32));
+ let px_10 = BorderSideWidth::Length(Length::from_px(10f32));
+
+ properties.push(PropertyDeclaration::BorderTopWidth(px_30.clone()));
+ properties.push(PropertyDeclaration::BorderRightWidth(px_30.clone()));
+ properties.push(PropertyDeclaration::BorderBottomWidth(px_30.clone()));
+ properties.push(PropertyDeclaration::BorderLeftWidth(px_10.clone()));
+
+ let blue = Color::rgba(RGBA::new(0, 0, 255, 255));
+
+ properties.push(PropertyDeclaration::BorderTopColor(blue.clone()));
+ properties.push(PropertyDeclaration::BorderRightColor(blue.clone()));
+ properties.push(PropertyDeclaration::BorderBottomColor(blue.clone()));
+ properties.push(PropertyDeclaration::BorderLeftColor(blue.clone()));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization,
+ "border-style: solid; border-width: 30px 30px 30px 10px; border-color: rgb(0, 0, 255);");
+ }
+
+ #[test]
+ fn same_longhands_should_serialize_correctly() {
+ let mut properties = Vec::new();
+
+ let solid = BorderStyle::Solid;
+
+ properties.push(PropertyDeclaration::BorderTopStyle(solid.clone()));
+ properties.push(PropertyDeclaration::BorderRightStyle(solid.clone()));
+ properties.push(PropertyDeclaration::BorderBottomStyle(solid.clone()));
+ properties.push(PropertyDeclaration::BorderLeftStyle(solid.clone()));
+
+ let px_30 = BorderSideWidth::Length(Length::from_px(30f32));
+
+ properties.push(PropertyDeclaration::BorderTopWidth(px_30.clone()));
+ properties.push(PropertyDeclaration::BorderRightWidth(px_30.clone()));
+ properties.push(PropertyDeclaration::BorderBottomWidth(px_30.clone()));
+ properties.push(PropertyDeclaration::BorderLeftWidth(px_30.clone()));
+
+ let blue = Color::rgba(RGBA::new(0, 0, 255, 255));
+
+ properties.push(PropertyDeclaration::BorderTopColor(blue.clone()));
+ properties.push(PropertyDeclaration::BorderRightColor(blue.clone()));
+ properties.push(PropertyDeclaration::BorderBottomColor(blue.clone()));
+ properties.push(PropertyDeclaration::BorderLeftColor(blue.clone()));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "border-style: solid; border-width: 30px; border-color: rgb(0, 0, 255);");
+ }
+
+ #[test]
+ fn padding_should_serialize_correctly() {
+ use style::values::specified::NonNegativeLengthPercentage;
+
+ let mut properties = Vec::new();
+
+ let px_10: NonNegativeLengthPercentage = NoCalcLength::from_px(10f32).into();
+ let px_15: NonNegativeLengthPercentage = NoCalcLength::from_px(15f32).into();
+ properties.push(PropertyDeclaration::PaddingTop(px_10.clone()));
+ properties.push(PropertyDeclaration::PaddingRight(px_15.clone()));
+ properties.push(PropertyDeclaration::PaddingBottom(px_10));
+ properties.push(PropertyDeclaration::PaddingLeft(px_15));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "padding: 10px 15px;");
+ }
+
+ #[test]
+ fn border_width_should_serialize_correctly() {
+ let mut properties = Vec::new();
+
+ let top_px = BorderSideWidth::Length(Length::from_px(10f32));
+ let bottom_px = BorderSideWidth::Length(Length::from_px(10f32));
+
+ let right_px = BorderSideWidth::Length(Length::from_px(15f32));
+ let left_px = BorderSideWidth::Length(Length::from_px(15f32));
+
+ properties.push(PropertyDeclaration::BorderTopWidth(top_px));
+ properties.push(PropertyDeclaration::BorderRightWidth(right_px));
+ properties.push(PropertyDeclaration::BorderBottomWidth(bottom_px));
+ properties.push(PropertyDeclaration::BorderLeftWidth(left_px));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "border-width: 10px 15px;");
+ }
+
+ #[test]
+ fn border_width_with_keywords_should_serialize_correctly() {
+ let mut properties = Vec::new();
+
+ let top_px = BorderSideWidth::Thin;
+ let right_px = BorderSideWidth::Medium;
+ let bottom_px = BorderSideWidth::Thick;
+ let left_px = BorderSideWidth::Length(Length::from_px(15f32));
+
+ properties.push(PropertyDeclaration::BorderTopWidth(top_px));
+ properties.push(PropertyDeclaration::BorderRightWidth(right_px));
+ properties.push(PropertyDeclaration::BorderBottomWidth(bottom_px));
+ properties.push(PropertyDeclaration::BorderLeftWidth(left_px));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "border-width: thin medium thick 15px;");
+ }
+
+ #[test]
+ fn border_color_should_serialize_correctly() {
+ let mut properties = Vec::new();
+
+ let red = Color::rgba(RGBA::new(255, 0, 0, 255));
+ let blue = Color::rgba(RGBA::new(0, 0, 255, 255));
+
+ properties.push(PropertyDeclaration::BorderTopColor(blue.clone()));
+ properties.push(PropertyDeclaration::BorderRightColor(red.clone()));
+ properties.push(PropertyDeclaration::BorderBottomColor(blue));
+ properties.push(PropertyDeclaration::BorderLeftColor(red));
+
+ let serialization = shorthand_properties_to_string(properties);
+
+ // TODO: Make the rgb test show border-color as blue red instead of below tuples
+ assert_eq!(serialization, "border-color: rgb(0, 0, 255) rgb(255, 0, 0);");
+ }
+
+ #[test]
+ fn border_style_should_serialize_correctly() {
+ let mut properties = Vec::new();
+
+ let solid = BorderStyle::Solid;
+ let dotted = BorderStyle::Dotted;
+ properties.push(PropertyDeclaration::BorderTopStyle(solid.clone()));
+ properties.push(PropertyDeclaration::BorderRightStyle(dotted.clone()));
+ properties.push(PropertyDeclaration::BorderBottomStyle(solid));
+ properties.push(PropertyDeclaration::BorderLeftStyle(dotted));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "border-style: solid dotted;");
+ }
+
+ use style::values::specified::{BorderCornerRadius, Percentage};
+
+ #[test]
+ fn border_radius_should_serialize_correctly() {
+ let mut properties = Vec::new();
+ properties.push(PropertyDeclaration::BorderTopLeftRadius(Box::new(BorderCornerRadius::new(
+ Percentage::new(0.01).into(), Percentage::new(0.05).into()
+ ))));
+ properties.push(PropertyDeclaration::BorderTopRightRadius(Box::new(BorderCornerRadius::new(
+ Percentage::new(0.02).into(), Percentage::new(0.06).into()
+ ))));
+ properties.push(PropertyDeclaration::BorderBottomRightRadius(Box::new(BorderCornerRadius::new(
+ Percentage::new(0.03).into(), Percentage::new(0.07).into()
+ ))));
+ properties.push(PropertyDeclaration::BorderBottomLeftRadius(Box::new(BorderCornerRadius::new(
+ Percentage::new(0.04).into(), Percentage::new(0.08).into()
+ ))));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "border-radius: 1% 2% 3% 4% / 5% 6% 7% 8%;");
+ }
+ }
+
+
+ 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"
+
+ #[test]
+ fn directional_border_should_show_all_properties_when_values_are_set() {
+ let mut properties = Vec::new();
+
+ let width = BorderSideWidth::Length(Length::from_px(4f32));
+ let style = BorderStyle::Solid;
+ let color = RGBA::new(255, 0, 0, 255).into();
+
+ properties.push(PropertyDeclaration::BorderTopWidth(width));
+ properties.push(PropertyDeclaration::BorderTopStyle(style));
+ properties.push(PropertyDeclaration::BorderTopColor(color));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "border-top: 4px solid rgb(255, 0, 0);");
+ }
+
+ fn get_border_property_values() -> (BorderSideWidth, BorderStyle, Color) {
+ (BorderSideWidth::Length(Length::from_px(4f32)),
+ BorderStyle::Solid,
+ Color::currentcolor())
+ }
+
+ #[test]
+ fn border_top_should_serialize_correctly() {
+ let mut properties = Vec::new();
+ let (width, style, color) = get_border_property_values();
+ properties.push(PropertyDeclaration::BorderTopWidth(width));
+ properties.push(PropertyDeclaration::BorderTopStyle(style));
+ properties.push(PropertyDeclaration::BorderTopColor(color));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "border-top: 4px solid;");
+ }
+
+ #[test]
+ fn border_right_should_serialize_correctly() {
+ let mut properties = Vec::new();
+ let (width, style, color) = get_border_property_values();
+ properties.push(PropertyDeclaration::BorderRightWidth(width));
+ properties.push(PropertyDeclaration::BorderRightStyle(style));
+ properties.push(PropertyDeclaration::BorderRightColor(color));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "border-right: 4px solid;");
+ }
+
+ #[test]
+ fn border_bottom_should_serialize_correctly() {
+ let mut properties = Vec::new();
+ let (width, style, color) = get_border_property_values();
+ properties.push(PropertyDeclaration::BorderBottomWidth(width));
+ properties.push(PropertyDeclaration::BorderBottomStyle(style));
+ properties.push(PropertyDeclaration::BorderBottomColor(color));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "border-bottom: 4px solid;");
+ }
+
+ #[test]
+ fn border_left_should_serialize_correctly() {
+ let mut properties = Vec::new();
+ let (width, style, color) = get_border_property_values();
+ properties.push(PropertyDeclaration::BorderLeftWidth(width));
+ properties.push(PropertyDeclaration::BorderLeftStyle(style));
+ properties.push(PropertyDeclaration::BorderLeftColor(color));
+
+ let serialization = shorthand_properties_to_string(properties);
+ assert_eq!(serialization, "border-left: 4px solid;");
+ }
+
+ #[test]
+ fn border_should_serialize_correctly() {
+ // According to https://drafts.csswg.org/css-backgrounds-3/#the-border-shorthands,
+ // the ‘border’ shorthand resets ‘border-image’ to its initial value. To verify the
+ // serialization of 'border' shorthand, we need to set 'border-image' as well.
+ let block_text = "\
+ border-top: 4px solid; \
+ border-right: 4px solid; \
+ border-bottom: 4px solid; \
+ border-left: 4px solid; \
+ border-image: none;";
+
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+
+ let serialization = block.to_css_string();
+
+ assert_eq!(serialization, "border: 4px solid;");
+ }
+ }
+
+ mod background {
+ use super::*;
+
+ #[test]
+ fn background_should_serialize_all_available_properties_when_specified() {
+ let block_text = "\
+ background-color: rgb(255, 0, 0); \
+ background-image: url(\"http://servo/test.png\"); \
+ background-repeat: repeat-x; \
+ background-attachment: scroll; \
+ background-size: 70px 50px; \
+ background-position-x: 7px; \
+ background-position-y: bottom 4px; \
+ background-origin: border-box; \
+ background-clip: padding-box;";
+
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+
+ let serialization = block.to_css_string();
+
+ assert_eq!(
+ serialization,
+ "background: rgb(255, 0, 0) url(\"http://servo/test.png\") repeat-x \
+ scroll left 7px bottom 4px / 70px 50px border-box padding-box;"
+ );
+ }
+
+ #[test]
+ fn background_should_combine_origin_and_clip_properties_when_equal() {
+ let block_text = "\
+ background-color: rgb(255, 0, 0); \
+ background-image: url(\"http://servo/test.png\"); \
+ background-repeat: repeat-x; \
+ background-attachment: scroll; \
+ background-size: 70px 50px; \
+ background-position-x: 7px; \
+ background-position-y: 4px; \
+ background-origin: padding-box; \
+ background-clip: padding-box;";
+
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+
+ let serialization = block.to_css_string();
+
+ assert_eq!(
+ serialization,
+ "background: rgb(255, 0, 0) url(\"http://servo/test.png\") repeat-x \
+ scroll 7px 4px / 70px 50px padding-box;"
+ );
+ }
+
+ #[test]
+ fn serialize_multiple_backgrounds() {
+ let block_text = "\
+ background-color: rgb(0, 0, 255); \
+ background-image: url(\"http://servo/test.png\"), none; \
+ background-repeat: repeat-x, repeat-y; \
+ background-attachment: scroll, scroll; \
+ background-size: 70px 50px, 20px 30px; \
+ background-position-x: 7px, 70px; \
+ background-position-y: 4px, 40px; \
+ background-origin: border-box, padding-box; \
+ background-clip: padding-box, padding-box;";
+
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+
+ let serialization = block.to_css_string();
+
+ assert_eq!(
+ serialization, "background: \
+ url(\"http://servo/test.png\") repeat-x scroll 7px 4px / 70px 50px border-box padding-box, \
+ rgb(0, 0, 255) none repeat-y scroll 70px 40px / 20px 30px padding-box;"
+ );
+ }
+
+ #[test]
+ fn serialize_multiple_backgrounds_unequal_property_lists() {
+ // When the lengths of property values are different, the shorthand serialization
+ // should not be used. Previously the implementation cycled values if the lists were
+ // uneven. This is incorrect, in that we should serialize to a shorthand only when the
+ // lists have the same length (this affects background, transition and animation).
+ // https://github.com/servo/servo/issues/15398 )
+ // With background, the color is one exception as it should only appear once for
+ // multiple backgrounds.
+ // Below background-origin only has one value.
+ let block_text = "\
+ background-color: rgb(0, 0, 255); \
+ background-image: url(\"http://servo/test.png\"), none; \
+ background-repeat: repeat-x, repeat-y; \
+ background-attachment: scroll, scroll; \
+ background-size: 70px 50px, 20px 30px; \
+ background-position: 7px 4px, 5px 6px; \
+ background-origin: border-box; \
+ background-clip: padding-box, padding-box;";
+
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+
+ let serialization = block.to_css_string();
+
+ assert_eq!(serialization, block_text);
+ }
+
+ #[test]
+ fn background_position_should_be_a_valid_form_its_longhands() {
+ // If there is any longhand consisted of both keyword and position,
+ // the shorthand result should be the 4-value format.
+ let block_text = "\
+ background-position-x: 30px;\
+ background-position-y: bottom 20px;";
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+ let serialization = block.to_css_string();
+ assert_eq!(serialization, "background-position: left 30px bottom 20px;");
+
+ // If there is no longhand consisted of both keyword and position,
+ // the shorthand result should be the 2-value format.
+ let block_text = "\
+ background-position-x: center;\
+ background-position-y: 20px;";
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+ let serialization = block.to_css_string();
+ assert_eq!(serialization, "background-position: center 20px;");
+ }
+ }
+
+ mod quotes {
+ pub use super::*;
+
+ #[test]
+ fn should_serialize_none_correctly() {
+ use style::properties::longhands::quotes;
+
+ assert_roundtrip_with_context!(quotes::parse, "none");
+ }
+ }
+
+ mod animation {
+ pub use super::*;
+
+ #[test]
+ fn serialize_single_animation() {
+ let block_text = "\
+ animation-name: bounce;\
+ animation-duration: 1s;\
+ animation-timing-function: ease-in;\
+ animation-delay: 0s;\
+ animation-direction: normal;\
+ animation-fill-mode: forwards;\
+ animation-iteration-count: infinite;\
+ animation-play-state: paused;";
+
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+
+ let serialization = block.to_css_string();
+
+ assert_eq!(serialization, "animation: 1s ease-in 0s infinite normal forwards paused bounce;")
+ }
+
+ #[test]
+ fn serialize_multiple_animations() {
+ let block_text = "\
+ animation-name: bounce, roll;\
+ animation-duration: 1s, 0.2s;\
+ animation-timing-function: ease-in, linear;\
+ animation-delay: 0s, 1s;\
+ animation-direction: normal, reverse;\
+ animation-fill-mode: forwards, backwards;\
+ animation-iteration-count: infinite, 2;\
+ animation-play-state: paused, running;";
+
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+
+ let serialization = block.to_css_string();
+
+ assert_eq!(serialization,
+ "animation: 1s ease-in 0s infinite normal forwards paused bounce, \
+ 0.2s linear 1s 2 reverse backwards running roll;");
+ }
+
+ #[test]
+ fn serialize_multiple_animations_unequal_property_lists() {
+ // When the lengths of property values are different, the shorthand serialization
+ // should not be used. Previously the implementation cycled values if the lists were
+ // uneven. This is incorrect, in that we should serialize to a shorthand only when the
+ // lists have the same length (this affects background, transition and animation).
+ // https://github.com/servo/servo/issues/15398 )
+ let block_text = "\
+ animation-name: bounce, roll, flip, jump; \
+ animation-duration: 1s, 0.2s; \
+ animation-timing-function: ease-in, linear; \
+ animation-delay: 0s, 1s, 0.5s; \
+ animation-direction: normal; \
+ animation-fill-mode: forwards, backwards; \
+ animation-iteration-count: infinite, 2; \
+ animation-play-state: paused, running;";
+
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+
+ let serialization = block.to_css_string();
+
+ assert_eq!(serialization, block_text);
+ }
+
+ #[test]
+ fn serialize_multiple_without_all_properties_returns_longhand() {
+ // timing function and direction are missing, so no shorthand is returned.
+ let block_text = "animation-name: bounce, roll; \
+ animation-duration: 1s, 0.2s; \
+ animation-delay: 0s, 1s; \
+ animation-fill-mode: forwards, backwards; \
+ animation-iteration-count: infinite, 2; \
+ animation-play-state: paused, running;";
+
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+
+ let serialization = block.to_css_string();
+
+ assert_eq!(serialization, block_text);
+ }
+ }
+
+ mod keywords {
+ pub use super::*;
+ #[test]
+ fn css_wide_keywords_should_be_parsed() {
+ let block_text = "--a:inherit;";
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+
+ let serialization = block.to_css_string();
+ assert_eq!(serialization, "--a: inherit;");
+ }
+
+ #[test]
+ fn non_keyword_custom_property_should_be_unparsed() {
+ let block_text = "--main-color: #06c;";
+ let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
+
+ let serialization = block.to_css_string();
+ assert_eq!(serialization, block_text);
+ }
+ }
+
+ mod effects {
+ pub use super::*;
+ pub use style::properties::longhands::box_shadow::SpecifiedValue as BoxShadowList;
+ pub use style::values::specified::effects::{BoxShadow, SimpleShadow};
+
+ #[test]
+ fn box_shadow_should_serialize_correctly() {
+ use style::values::specified::length::NonNegativeLength;
+
+ let mut properties = Vec::new();
+ let shadow_val = BoxShadow {
+ base: SimpleShadow {
+ color: None,
+ horizontal: Length::from_px(1f32),
+ vertical: Length::from_px(2f32),
+ blur: Some(NonNegativeLength::from_px(3f32)),
+ },
+ spread: Some(Length::from_px(4f32)),
+ inset: false,
+ };
+ let shadow_decl = BoxShadowList(vec![shadow_val]);
+ properties.push(PropertyDeclaration::BoxShadow(shadow_decl));
+ let shadow_css = "box-shadow: 1px 2px 3px 4px;";
+ let shadow = parse(|c, i| Ok(parse_property_declaration_list(c, i)), shadow_css).unwrap();
+
+ assert_eq!(shadow.to_css_string(), shadow_css);
+ }
+ }
+}