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 }