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