tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

serialization.rs (33174B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 use properties::{parse, parse_input};
      6 use style::computed_values::display::T as Display;
      7 use style::properties::declaration_block::PropertyDeclarationBlock;
      8 use style::properties::parse_property_declaration_list;
      9 use style::properties::{Importance, PropertyDeclaration};
     10 use style::values::specified::url::SpecifiedUrl;
     11 use style::values::specified::NoCalcLength;
     12 use style::values::specified::{BorderSideWidth, BorderStyle, Color};
     13 use style::values::specified::{Length, LengthPercentage, LengthPercentageOrAuto};
     14 use style::values::RGBA;
     15 use style_traits::ToCss;
     16 use stylesheets::block_from;
     17 
     18 trait ToCssString {
     19    fn to_css_string(&self) -> String;
     20 }
     21 
     22 impl ToCssString for PropertyDeclarationBlock {
     23    fn to_css_string(&self) -> String {
     24        let mut css = String::new();
     25        self.to_css(&mut css).unwrap();
     26        css
     27    }
     28 }
     29 
     30 #[test]
     31 fn property_declaration_block_should_serialize_correctly() {
     32    use style::properties::longhands::overflow_x::SpecifiedValue as OverflowValue;
     33 
     34    let declarations = vec![
     35        (
     36            PropertyDeclaration::Width(LengthPercentageOrAuto::Length(NoCalcLength::from_px(
     37                70f32,
     38            ))),
     39            Importance::Normal,
     40        ),
     41        (
     42            PropertyDeclaration::MinHeight(LengthPercentage::Length(NoCalcLength::from_px(20f32))),
     43            Importance::Normal,
     44        ),
     45        (
     46            PropertyDeclaration::Height(LengthPercentageOrAuto::Length(NoCalcLength::from_px(
     47                20f32,
     48            ))),
     49            Importance::Important,
     50        ),
     51        (
     52            PropertyDeclaration::Display(Display::InlineBlock),
     53            Importance::Normal,
     54        ),
     55        (
     56            PropertyDeclaration::OverflowX(OverflowValue::Auto),
     57            Importance::Normal,
     58        ),
     59        (
     60            PropertyDeclaration::OverflowY(OverflowValue::Auto),
     61            Importance::Normal,
     62        ),
     63    ];
     64 
     65    let block = block_from(declarations);
     66 
     67    let css_string = block.to_css_string();
     68 
     69    assert_eq!(
     70        css_string,
     71        "width: 70px; min-height: 20px; height: 20px !important; display: inline-block; overflow: auto;"
     72    );
     73 }
     74 
     75 mod shorthand_serialization {
     76    pub use super::*;
     77 
     78    pub fn shorthand_properties_to_string(properties: Vec<PropertyDeclaration>) -> String {
     79        let block = block_from(properties.into_iter().map(|d| (d, Importance::Normal)));
     80 
     81        block.to_css_string()
     82    }
     83 
     84    mod four_sides_shorthands {
     85        pub use super::*;
     86 
     87        // we can use margin as a base to test out the different combinations
     88        // but afterwards, we only need to to one test per "four sides shorthand"
     89        #[test]
     90        fn all_equal_properties_should_serialize_to_one_value() {
     91            let mut properties = Vec::new();
     92 
     93            let px_70 = LengthPercentageOrAuto::Length(NoCalcLength::from_px(70f32));
     94            properties.push(PropertyDeclaration::MarginTop(px_70.clone()));
     95            properties.push(PropertyDeclaration::MarginRight(px_70.clone()));
     96            properties.push(PropertyDeclaration::MarginBottom(px_70.clone()));
     97            properties.push(PropertyDeclaration::MarginLeft(px_70));
     98 
     99            let serialization = shorthand_properties_to_string(properties);
    100            assert_eq!(serialization, "margin: 70px;");
    101        }
    102 
    103        #[test]
    104        fn equal_vertical_and_equal_horizontal_properties_should_serialize_to_two_value() {
    105            let mut properties = Vec::new();
    106 
    107            let vertical_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(10f32));
    108            let horizontal_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(5f32));
    109 
    110            properties.push(PropertyDeclaration::MarginTop(vertical_px.clone()));
    111            properties.push(PropertyDeclaration::MarginRight(horizontal_px.clone()));
    112            properties.push(PropertyDeclaration::MarginBottom(vertical_px));
    113            properties.push(PropertyDeclaration::MarginLeft(horizontal_px));
    114 
    115            let serialization = shorthand_properties_to_string(properties);
    116            assert_eq!(serialization, "margin: 10px 5px;");
    117        }
    118 
    119        #[test]
    120        fn different_vertical_and_equal_horizontal_properties_should_serialize_to_three_values() {
    121            let mut properties = Vec::new();
    122 
    123            let top_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(8f32));
    124            let bottom_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(10f32));
    125            let horizontal_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(5f32));
    126 
    127            properties.push(PropertyDeclaration::MarginTop(top_px));
    128            properties.push(PropertyDeclaration::MarginRight(horizontal_px.clone()));
    129            properties.push(PropertyDeclaration::MarginBottom(bottom_px));
    130            properties.push(PropertyDeclaration::MarginLeft(horizontal_px));
    131 
    132            let serialization = shorthand_properties_to_string(properties);
    133            assert_eq!(serialization, "margin: 8px 5px 10px;");
    134        }
    135 
    136        #[test]
    137        fn different_properties_should_serialize_to_four_values() {
    138            let mut properties = Vec::new();
    139 
    140            let top_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(8f32));
    141            let right_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(12f32));
    142            let bottom_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(10f32));
    143            let left_px = LengthPercentageOrAuto::Length(NoCalcLength::from_px(14f32));
    144 
    145            properties.push(PropertyDeclaration::MarginTop(top_px));
    146            properties.push(PropertyDeclaration::MarginRight(right_px));
    147            properties.push(PropertyDeclaration::MarginBottom(bottom_px));
    148            properties.push(PropertyDeclaration::MarginLeft(left_px));
    149 
    150            let serialization = shorthand_properties_to_string(properties);
    151            assert_eq!(serialization, "margin: 8px 12px 10px 14px;");
    152        }
    153 
    154        #[test]
    155        fn different_longhands_should_serialize_to_long_form() {
    156            let mut properties = Vec::new();
    157 
    158            let solid = BorderStyle::Solid;
    159 
    160            properties.push(PropertyDeclaration::BorderTopStyle(solid.clone()));
    161            properties.push(PropertyDeclaration::BorderRightStyle(solid.clone()));
    162            properties.push(PropertyDeclaration::BorderBottomStyle(solid.clone()));
    163            properties.push(PropertyDeclaration::BorderLeftStyle(solid.clone()));
    164 
    165            let px_30 = BorderSideWidth::Length(Length::from_px(30f32));
    166            let px_10 = BorderSideWidth::Length(Length::from_px(10f32));
    167 
    168            properties.push(PropertyDeclaration::BorderTopWidth(px_30.clone()));
    169            properties.push(PropertyDeclaration::BorderRightWidth(px_30.clone()));
    170            properties.push(PropertyDeclaration::BorderBottomWidth(px_30.clone()));
    171            properties.push(PropertyDeclaration::BorderLeftWidth(px_10.clone()));
    172 
    173            let blue = Color::rgba(RGBA::new(0, 0, 255, 255));
    174 
    175            properties.push(PropertyDeclaration::BorderTopColor(blue.clone()));
    176            properties.push(PropertyDeclaration::BorderRightColor(blue.clone()));
    177            properties.push(PropertyDeclaration::BorderBottomColor(blue.clone()));
    178            properties.push(PropertyDeclaration::BorderLeftColor(blue.clone()));
    179 
    180            let serialization = shorthand_properties_to_string(properties);
    181            assert_eq!(serialization,
    182          "border-style: solid; border-width: 30px 30px 30px 10px; border-color: rgb(0, 0, 255);");
    183        }
    184 
    185        #[test]
    186        fn same_longhands_should_serialize_correctly() {
    187            let mut properties = Vec::new();
    188 
    189            let solid = BorderStyle::Solid;
    190 
    191            properties.push(PropertyDeclaration::BorderTopStyle(solid.clone()));
    192            properties.push(PropertyDeclaration::BorderRightStyle(solid.clone()));
    193            properties.push(PropertyDeclaration::BorderBottomStyle(solid.clone()));
    194            properties.push(PropertyDeclaration::BorderLeftStyle(solid.clone()));
    195 
    196            let px_30 = BorderSideWidth::Length(Length::from_px(30f32));
    197 
    198            properties.push(PropertyDeclaration::BorderTopWidth(px_30.clone()));
    199            properties.push(PropertyDeclaration::BorderRightWidth(px_30.clone()));
    200            properties.push(PropertyDeclaration::BorderBottomWidth(px_30.clone()));
    201            properties.push(PropertyDeclaration::BorderLeftWidth(px_30.clone()));
    202 
    203            let blue = Color::rgba(RGBA::new(0, 0, 255, 255));
    204 
    205            properties.push(PropertyDeclaration::BorderTopColor(blue.clone()));
    206            properties.push(PropertyDeclaration::BorderRightColor(blue.clone()));
    207            properties.push(PropertyDeclaration::BorderBottomColor(blue.clone()));
    208            properties.push(PropertyDeclaration::BorderLeftColor(blue.clone()));
    209 
    210            let serialization = shorthand_properties_to_string(properties);
    211            assert_eq!(
    212                serialization,
    213                "border-style: solid; border-width: 30px; border-color: rgb(0, 0, 255);"
    214            );
    215        }
    216 
    217        #[test]
    218        fn padding_should_serialize_correctly() {
    219            use style::values::specified::NonNegativeLengthPercentage;
    220 
    221            let mut properties = Vec::new();
    222 
    223            let px_10: NonNegativeLengthPercentage = NoCalcLength::from_px(10f32).into();
    224            let px_15: NonNegativeLengthPercentage = NoCalcLength::from_px(15f32).into();
    225            properties.push(PropertyDeclaration::PaddingTop(px_10.clone()));
    226            properties.push(PropertyDeclaration::PaddingRight(px_15.clone()));
    227            properties.push(PropertyDeclaration::PaddingBottom(px_10));
    228            properties.push(PropertyDeclaration::PaddingLeft(px_15));
    229 
    230            let serialization = shorthand_properties_to_string(properties);
    231            assert_eq!(serialization, "padding: 10px 15px;");
    232        }
    233 
    234        #[test]
    235        fn border_width_should_serialize_correctly() {
    236            let mut properties = Vec::new();
    237 
    238            let top_px = BorderSideWidth::Length(Length::from_px(10f32));
    239            let bottom_px = BorderSideWidth::Length(Length::from_px(10f32));
    240 
    241            let right_px = BorderSideWidth::Length(Length::from_px(15f32));
    242            let left_px = BorderSideWidth::Length(Length::from_px(15f32));
    243 
    244            properties.push(PropertyDeclaration::BorderTopWidth(top_px));
    245            properties.push(PropertyDeclaration::BorderRightWidth(right_px));
    246            properties.push(PropertyDeclaration::BorderBottomWidth(bottom_px));
    247            properties.push(PropertyDeclaration::BorderLeftWidth(left_px));
    248 
    249            let serialization = shorthand_properties_to_string(properties);
    250            assert_eq!(serialization, "border-width: 10px 15px;");
    251        }
    252 
    253        #[test]
    254        fn border_width_with_keywords_should_serialize_correctly() {
    255            let mut properties = Vec::new();
    256 
    257            let top_px = BorderSideWidth::Thin;
    258            let right_px = BorderSideWidth::Medium;
    259            let bottom_px = BorderSideWidth::Thick;
    260            let left_px = BorderSideWidth::Length(Length::from_px(15f32));
    261 
    262            properties.push(PropertyDeclaration::BorderTopWidth(top_px));
    263            properties.push(PropertyDeclaration::BorderRightWidth(right_px));
    264            properties.push(PropertyDeclaration::BorderBottomWidth(bottom_px));
    265            properties.push(PropertyDeclaration::BorderLeftWidth(left_px));
    266 
    267            let serialization = shorthand_properties_to_string(properties);
    268            assert_eq!(serialization, "border-width: thin medium thick 15px;");
    269        }
    270 
    271        #[test]
    272        fn border_color_should_serialize_correctly() {
    273            let mut properties = Vec::new();
    274 
    275            let red = Color::rgba(RGBA::new(255, 0, 0, 255));
    276            let blue = Color::rgba(RGBA::new(0, 0, 255, 255));
    277 
    278            properties.push(PropertyDeclaration::BorderTopColor(blue.clone()));
    279            properties.push(PropertyDeclaration::BorderRightColor(red.clone()));
    280            properties.push(PropertyDeclaration::BorderBottomColor(blue));
    281            properties.push(PropertyDeclaration::BorderLeftColor(red));
    282 
    283            let serialization = shorthand_properties_to_string(properties);
    284 
    285            // TODO: Make the rgb test show border-color as blue red instead of below tuples
    286            assert_eq!(
    287                serialization,
    288                "border-color: rgb(0, 0, 255) rgb(255, 0, 0);"
    289            );
    290        }
    291 
    292        #[test]
    293        fn border_style_should_serialize_correctly() {
    294            let mut properties = Vec::new();
    295 
    296            let solid = BorderStyle::Solid;
    297            let dotted = BorderStyle::Dotted;
    298            properties.push(PropertyDeclaration::BorderTopStyle(solid.clone()));
    299            properties.push(PropertyDeclaration::BorderRightStyle(dotted.clone()));
    300            properties.push(PropertyDeclaration::BorderBottomStyle(solid));
    301            properties.push(PropertyDeclaration::BorderLeftStyle(dotted));
    302 
    303            let serialization = shorthand_properties_to_string(properties);
    304            assert_eq!(serialization, "border-style: solid dotted;");
    305        }
    306 
    307        use style::values::specified::{BorderCornerRadius, Percentage};
    308 
    309        #[test]
    310        fn border_radius_should_serialize_correctly() {
    311            let mut properties = Vec::new();
    312            properties.push(PropertyDeclaration::BorderTopLeftRadius(Box::new(
    313                BorderCornerRadius::new(Percentage::new(0.01).into(), Percentage::new(0.05).into()),
    314            )));
    315            properties.push(PropertyDeclaration::BorderTopRightRadius(Box::new(
    316                BorderCornerRadius::new(Percentage::new(0.02).into(), Percentage::new(0.06).into()),
    317            )));
    318            properties.push(PropertyDeclaration::BorderBottomRightRadius(Box::new(
    319                BorderCornerRadius::new(Percentage::new(0.03).into(), Percentage::new(0.07).into()),
    320            )));
    321            properties.push(PropertyDeclaration::BorderBottomLeftRadius(Box::new(
    322                BorderCornerRadius::new(Percentage::new(0.04).into(), Percentage::new(0.08).into()),
    323            )));
    324 
    325            let serialization = shorthand_properties_to_string(properties);
    326            assert_eq!(serialization, "border-radius: 1% 2% 3% 4% / 5% 6% 7% 8%;");
    327        }
    328    }
    329 
    330    mod border_shorthands {
    331        use super::*;
    332 
    333        #[test]
    334        fn border_top_and_color() {
    335            let mut properties = Vec::new();
    336            properties.push(PropertyDeclaration::BorderTopWidth(
    337                BorderSideWidth::Length(Length::from_px(1.)),
    338            ));
    339            properties.push(PropertyDeclaration::BorderTopStyle(BorderStyle::Solid));
    340            let c = Color::Numeric {
    341                parsed: RGBA::new(255, 0, 0, 255),
    342                authored: Some("green".to_string().into_boxed_str()),
    343            };
    344            properties.push(PropertyDeclaration::BorderTopColor(c));
    345            let c = Color::Numeric {
    346                parsed: RGBA::new(0, 255, 0, 255),
    347                authored: Some("red".to_string().into_boxed_str()),
    348            };
    349            properties.push(PropertyDeclaration::BorderTopColor(c.clone()));
    350            properties.push(PropertyDeclaration::BorderBottomColor(c.clone()));
    351            properties.push(PropertyDeclaration::BorderLeftColor(c.clone()));
    352            properties.push(PropertyDeclaration::BorderRightColor(c.clone()));
    353 
    354            let serialization = shorthand_properties_to_string(properties);
    355            assert_eq!(
    356                serialization,
    357                "border-top: 1px solid red; border-color: red;"
    358            );
    359        }
    360 
    361        #[test]
    362        fn border_color_and_top() {
    363            let mut properties = Vec::new();
    364            let c = Color::Numeric {
    365                parsed: RGBA::new(0, 255, 0, 255),
    366                authored: Some("red".to_string().into_boxed_str()),
    367            };
    368            properties.push(PropertyDeclaration::BorderTopColor(c.clone()));
    369            properties.push(PropertyDeclaration::BorderBottomColor(c.clone()));
    370            properties.push(PropertyDeclaration::BorderLeftColor(c.clone()));
    371            properties.push(PropertyDeclaration::BorderRightColor(c.clone()));
    372 
    373            properties.push(PropertyDeclaration::BorderTopWidth(
    374                BorderSideWidth::Length(Length::from_px(1.)),
    375            ));
    376            properties.push(PropertyDeclaration::BorderTopStyle(BorderStyle::Solid));
    377            let c = Color::Numeric {
    378                parsed: RGBA::new(255, 0, 0, 255),
    379                authored: Some("green".to_string().into_boxed_str()),
    380            };
    381            properties.push(PropertyDeclaration::BorderTopColor(c));
    382 
    383            let serialization = shorthand_properties_to_string(properties);
    384            assert_eq!(
    385                serialization,
    386                "border-color: green red red; border-top: 1px solid green;"
    387            );
    388        }
    389 
    390        // we can use border-top as a base to test out the different combinations
    391        // but afterwards, we only need to to one test per "directional border shorthand"
    392 
    393        #[test]
    394        fn directional_border_should_show_all_properties_when_values_are_set() {
    395            let mut properties = Vec::new();
    396 
    397            let width = BorderSideWidth::Length(Length::from_px(4f32));
    398            let style = BorderStyle::Solid;
    399            let color = RGBA::new(255, 0, 0, 255).into();
    400 
    401            properties.push(PropertyDeclaration::BorderTopWidth(width));
    402            properties.push(PropertyDeclaration::BorderTopStyle(style));
    403            properties.push(PropertyDeclaration::BorderTopColor(color));
    404 
    405            let serialization = shorthand_properties_to_string(properties);
    406            assert_eq!(serialization, "border-top: 4px solid rgb(255, 0, 0);");
    407        }
    408 
    409        fn get_border_property_values() -> (BorderSideWidth, BorderStyle, Color) {
    410            (
    411                BorderSideWidth::Length(Length::from_px(4f32)),
    412                BorderStyle::Solid,
    413                Color::currentcolor(),
    414            )
    415        }
    416 
    417        #[test]
    418        fn border_top_should_serialize_correctly() {
    419            let mut properties = Vec::new();
    420            let (width, style, color) = get_border_property_values();
    421            properties.push(PropertyDeclaration::BorderTopWidth(width));
    422            properties.push(PropertyDeclaration::BorderTopStyle(style));
    423            properties.push(PropertyDeclaration::BorderTopColor(color));
    424 
    425            let serialization = shorthand_properties_to_string(properties);
    426            assert_eq!(serialization, "border-top: 4px solid;");
    427        }
    428 
    429        #[test]
    430        fn border_right_should_serialize_correctly() {
    431            let mut properties = Vec::new();
    432            let (width, style, color) = get_border_property_values();
    433            properties.push(PropertyDeclaration::BorderRightWidth(width));
    434            properties.push(PropertyDeclaration::BorderRightStyle(style));
    435            properties.push(PropertyDeclaration::BorderRightColor(color));
    436 
    437            let serialization = shorthand_properties_to_string(properties);
    438            assert_eq!(serialization, "border-right: 4px solid;");
    439        }
    440 
    441        #[test]
    442        fn border_bottom_should_serialize_correctly() {
    443            let mut properties = Vec::new();
    444            let (width, style, color) = get_border_property_values();
    445            properties.push(PropertyDeclaration::BorderBottomWidth(width));
    446            properties.push(PropertyDeclaration::BorderBottomStyle(style));
    447            properties.push(PropertyDeclaration::BorderBottomColor(color));
    448 
    449            let serialization = shorthand_properties_to_string(properties);
    450            assert_eq!(serialization, "border-bottom: 4px solid;");
    451        }
    452 
    453        #[test]
    454        fn border_left_should_serialize_correctly() {
    455            let mut properties = Vec::new();
    456            let (width, style, color) = get_border_property_values();
    457            properties.push(PropertyDeclaration::BorderLeftWidth(width));
    458            properties.push(PropertyDeclaration::BorderLeftStyle(style));
    459            properties.push(PropertyDeclaration::BorderLeftColor(color));
    460 
    461            let serialization = shorthand_properties_to_string(properties);
    462            assert_eq!(serialization, "border-left: 4px solid;");
    463        }
    464 
    465        #[test]
    466        fn border_should_serialize_correctly() {
    467            // According to https://drafts.csswg.org/css-backgrounds-3/#the-border-shorthands,
    468            // the ‘border’ shorthand resets ‘border-image’ to its initial value. To verify the
    469            // serialization of 'border' shorthand, we need to set 'border-image' as well.
    470            let block_text = "\
    471                border-top: 4px solid; \
    472                border-right: 4px solid; \
    473                border-bottom: 4px solid; \
    474                border-left: 4px solid; \
    475                border-image: none;";
    476 
    477            let block =
    478                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    479 
    480            let serialization = block.to_css_string();
    481 
    482            assert_eq!(serialization, "border: 4px solid;");
    483        }
    484    }
    485 
    486    mod background {
    487        use super::*;
    488 
    489        #[test]
    490        fn background_should_serialize_all_available_properties_when_specified() {
    491            let block_text = "\
    492                background-color: rgb(255, 0, 0); \
    493                background-image: url(\"http://servo/test.png\"); \
    494                background-repeat: repeat-x; \
    495                background-attachment: scroll; \
    496                background-size: 70px 50px; \
    497                background-position-x: 7px; \
    498                background-position-y: bottom 4px; \
    499                background-origin: border-box; \
    500                background-clip: padding-box;";
    501 
    502            let block =
    503                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    504 
    505            let serialization = block.to_css_string();
    506 
    507            assert_eq!(
    508                serialization,
    509                "background: rgb(255, 0, 0) url(\"http://servo/test.png\") repeat-x \
    510                scroll left 7px bottom 4px / 70px 50px border-box padding-box;"
    511            );
    512        }
    513 
    514        #[test]
    515        fn background_should_combine_origin_and_clip_properties_when_equal() {
    516            let block_text = "\
    517                background-color: rgb(255, 0, 0); \
    518                background-image: url(\"http://servo/test.png\"); \
    519                background-repeat: repeat-x; \
    520                background-attachment: scroll; \
    521                background-size: 70px 50px; \
    522                background-position-x: 7px; \
    523                background-position-y: 4px; \
    524                background-origin: padding-box; \
    525                background-clip: padding-box;";
    526 
    527            let block =
    528                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    529 
    530            let serialization = block.to_css_string();
    531 
    532            assert_eq!(
    533                serialization,
    534                "background: rgb(255, 0, 0) url(\"http://servo/test.png\") repeat-x \
    535                scroll 7px 4px / 70px 50px padding-box;"
    536            );
    537        }
    538 
    539        #[test]
    540        fn serialize_multiple_backgrounds() {
    541            let block_text = "\
    542                background-color: rgb(0, 0, 255); \
    543                background-image: url(\"http://servo/test.png\"), none; \
    544                background-repeat: repeat-x, repeat-y; \
    545                background-attachment: scroll, scroll; \
    546                background-size: 70px 50px, 20px 30px; \
    547                background-position-x: 7px, 70px; \
    548                background-position-y: 4px, 40px; \
    549                background-origin: border-box, padding-box; \
    550                background-clip: padding-box, padding-box;";
    551 
    552            let block =
    553                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    554 
    555            let serialization = block.to_css_string();
    556 
    557            assert_eq!(
    558                serialization, "background: \
    559                url(\"http://servo/test.png\") repeat-x scroll 7px 4px / 70px 50px border-box padding-box, \
    560                rgb(0, 0, 255) none repeat-y scroll 70px 40px / 20px 30px padding-box;"
    561            );
    562        }
    563 
    564        #[test]
    565        fn serialize_multiple_backgrounds_unequal_property_lists() {
    566            // When the lengths of property values are different, the shorthand serialization
    567            // should not be used. Previously the implementation cycled values if the lists were
    568            // uneven. This is incorrect, in that we should serialize to a shorthand only when the
    569            // lists have the same length (this affects background, transition and animation).
    570            // https://github.com/servo/servo/issues/15398 )
    571            // With background, the color is one exception as it should only appear once for
    572            // multiple backgrounds.
    573            // Below background-origin only has one value.
    574            let block_text = "\
    575                background-color: rgb(0, 0, 255); \
    576                background-image: url(\"http://servo/test.png\"), none; \
    577                background-repeat: repeat-x, repeat-y; \
    578                background-attachment: scroll, scroll; \
    579                background-size: 70px 50px, 20px 30px; \
    580                background-position: 7px 4px, 5px 6px; \
    581                background-origin: border-box; \
    582                background-clip: padding-box, padding-box;";
    583 
    584            let block =
    585                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    586 
    587            let serialization = block.to_css_string();
    588 
    589            assert_eq!(serialization, block_text);
    590        }
    591 
    592        #[test]
    593        fn background_position_should_be_a_valid_form_its_longhands() {
    594            // If there is any longhand consisted of both keyword and position,
    595            // the shorthand result should be the 4-value format.
    596            let block_text = "\
    597                background-position-x: 30px;\
    598                background-position-y: bottom 20px;";
    599            let block =
    600                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    601            let serialization = block.to_css_string();
    602            assert_eq!(serialization, "background-position: left 30px bottom 20px;");
    603 
    604            // If there is no longhand consisted of both keyword and position,
    605            // the shorthand result should be the 2-value format.
    606            let block_text = "\
    607                background-position-x: center;\
    608                background-position-y: 20px;";
    609            let block =
    610                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    611            let serialization = block.to_css_string();
    612            assert_eq!(serialization, "background-position: center 20px;");
    613        }
    614    }
    615 
    616    mod quotes {
    617        pub use super::*;
    618 
    619        #[test]
    620        fn should_serialize_none_correctly() {
    621            use style::properties::longhands::quotes;
    622 
    623            assert_roundtrip_with_context!(quotes::parse, "none");
    624        }
    625    }
    626 
    627    mod animation {
    628        pub use super::*;
    629 
    630        #[test]
    631        fn serialize_single_animation() {
    632            let block_text = "\
    633                animation-name: bounce;\
    634                animation-duration: 1s;\
    635                animation-timing-function: ease-in;\
    636                animation-delay: 0s;\
    637                animation-direction: normal;\
    638                animation-fill-mode: forwards;\
    639                animation-iteration-count: infinite;\
    640                animation-play-state: paused;";
    641 
    642            let block =
    643                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    644 
    645            let serialization = block.to_css_string();
    646 
    647            assert_eq!(
    648                serialization,
    649                "animation: 1s ease-in 0s infinite normal forwards paused bounce;"
    650            )
    651        }
    652 
    653        #[test]
    654        fn serialize_multiple_animations() {
    655            let block_text = "\
    656                animation-name: bounce, roll;\
    657                animation-duration: 1s, 0.2s;\
    658                animation-timing-function: ease-in, linear;\
    659                animation-delay: 0s, 1s;\
    660                animation-direction: normal, reverse;\
    661                animation-fill-mode: forwards, backwards;\
    662                animation-iteration-count: infinite, 2;\
    663                animation-play-state: paused, running;";
    664 
    665            let block =
    666                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    667 
    668            let serialization = block.to_css_string();
    669 
    670            assert_eq!(
    671                serialization,
    672                "animation: 1s ease-in 0s infinite normal forwards paused bounce, \
    673                                   0.2s linear 1s 2 reverse backwards running roll;"
    674            );
    675        }
    676 
    677        #[test]
    678        fn serialize_multiple_animations_unequal_property_lists() {
    679            // When the lengths of property values are different, the shorthand serialization
    680            // should not be used. Previously the implementation cycled values if the lists were
    681            // uneven. This is incorrect, in that we should serialize to a shorthand only when the
    682            // lists have the same length (this affects background, transition and animation).
    683            // https://github.com/servo/servo/issues/15398 )
    684            let block_text = "\
    685                animation-name: bounce, roll, flip, jump; \
    686                animation-duration: 1s, 0.2s; \
    687                animation-timing-function: ease-in, linear; \
    688                animation-delay: 0s, 1s, 0.5s; \
    689                animation-direction: normal; \
    690                animation-fill-mode: forwards, backwards; \
    691                animation-iteration-count: infinite, 2; \
    692                animation-play-state: paused, running;";
    693 
    694            let block =
    695                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    696 
    697            let serialization = block.to_css_string();
    698 
    699            assert_eq!(serialization, block_text);
    700        }
    701 
    702        #[test]
    703        fn serialize_multiple_without_all_properties_returns_longhand() {
    704            // timing function and direction are missing, so no shorthand is returned.
    705            let block_text = "animation-name: bounce, roll; \
    706                              animation-duration: 1s, 0.2s; \
    707                              animation-delay: 0s, 1s; \
    708                              animation-fill-mode: forwards, backwards; \
    709                              animation-iteration-count: infinite, 2; \
    710                              animation-play-state: paused, running;";
    711 
    712            let block =
    713                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    714 
    715            let serialization = block.to_css_string();
    716 
    717            assert_eq!(serialization, block_text);
    718        }
    719    }
    720 
    721    mod keywords {
    722        pub use super::*;
    723        #[test]
    724        fn css_wide_keywords_should_be_parsed() {
    725            let block_text = "--a:inherit;";
    726            let block =
    727                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    728 
    729            let serialization = block.to_css_string();
    730            assert_eq!(serialization, "--a: inherit;");
    731        }
    732 
    733        #[test]
    734        fn non_keyword_custom_property_should_be_unparsed() {
    735            let block_text = "--main-color: #06c;";
    736            let block =
    737                parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
    738 
    739            let serialization = block.to_css_string();
    740            assert_eq!(serialization, block_text);
    741        }
    742    }
    743 
    744    mod effects {
    745        pub use super::*;
    746        pub use style::properties::longhands::box_shadow::SpecifiedValue as BoxShadowList;
    747        pub use style::values::specified::effects::{BoxShadow, SimpleShadow};
    748 
    749        #[test]
    750        fn box_shadow_should_serialize_correctly() {
    751            use style::values::specified::length::NonNegativeLength;
    752 
    753            let mut properties = Vec::new();
    754            let shadow_val = BoxShadow {
    755                base: SimpleShadow {
    756                    color: None,
    757                    horizontal: Length::from_px(1f32),
    758                    vertical: Length::from_px(2f32),
    759                    blur: Some(NonNegativeLength::from_px(3f32)),
    760                },
    761                spread: Some(Length::from_px(4f32)),
    762                inset: false,
    763            };
    764            let shadow_decl = BoxShadowList(vec![shadow_val]);
    765            properties.push(PropertyDeclaration::BoxShadow(shadow_decl));
    766            let shadow_css = "box-shadow: 1px 2px 3px 4px;";
    767            let shadow =
    768                parse(|c, i| Ok(parse_property_declaration_list(c, i)), shadow_css).unwrap();
    769 
    770            assert_eq!(shadow.to_css_string(), shadow_css);
    771        }
    772    }
    773 }