1#![allow(missing_docs)]
7
8use crate::parser::{Parse, ParserContext};
9use crate::values::specified;
10use cssparser::Parser;
11use std::fmt::{self, Write};
12use style_traits::{
13 CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss,
14 values::SequenceWriter,
15};
16
17macro_rules! expanded {
18 ( $( $name: ident: $value: expr ),+ ) => {
19 expanded!( $( $name: $value, )+ )
20 };
21 ( $( $name: ident: $value: expr, )+ ) => {
22 Longhands {
23 $(
24 $name: $crate::properties::MaybeBoxed::maybe_boxed($value),
25 )+
26 }
27 }
28}
29pub(crate) use expanded;
30
31macro_rules! try_parse_one {
32 ($context: expr, $input: expr, $var: ident, $parse_path: path) => {
33 if $var.is_none() {
34 if let Ok(value) = $input.try_parse(|i| $parse_path($context, i)) {
35 $var = Some(value);
36 continue;
37 }
38 }
39 };
40 ($input: expr, $var: ident, $parse_path: path) => {
41 if $var.is_none() {
42 if let Ok(value) = $input.try_parse(|i| $parse_path(i)) {
43 $var = Some(value);
44 continue;
45 }
46 }
47 };
48}
49
50macro_rules! unwrap_or_initial {
51 ($prop: ident) => {
52 unwrap_or_initial!($prop, $prop)
53 };
54 ($prop: ident, $expr: expr) => {
55 $expr.unwrap_or_else(|| $prop::get_initial_specified_value())
56 };
57}
58
59pub fn serialize_directional_border<W>(
61 dest: &mut CssWriter<W>,
62 width: &specified::BorderSideWidth,
63 style: &specified::BorderStyle,
64 color: &specified::Color,
65) -> fmt::Result
66where
67 W: Write,
68{
69 use specified::{BorderSideWidth, BorderStyle, Color};
70 let has_style = *style != BorderStyle::None;
71 let has_color = *color != Color::CurrentColor;
72 let has_width = *width != BorderSideWidth::medium();
73 if !has_style && !has_color && !has_width {
74 return width.to_css(dest);
75 }
76 let mut writer = SequenceWriter::new(dest, " ");
77 if has_width {
78 writer.item(width)?;
79 }
80 if has_style {
81 writer.item(style)?;
82 }
83 if has_color {
84 writer.item(color)?;
85 }
86 Ok(())
87}
88
89pub fn parse_border(
90 context: &ParserContext,
91 input: &mut Parser,
92) -> Result<
93 (
94 specified::BorderSideWidth,
95 specified::BorderStyle,
96 specified::Color,
97 ),
98 ParseError,
99> {
100 use crate::values::specified::{BorderSideWidth, BorderStyle, Color};
101 let mut color = None;
102 let mut style = None;
103 let mut width = None;
104 let mut parsed = 0;
105 loop {
106 parsed += 1;
107 try_parse_one!(context, input, width, BorderSideWidth::parse);
108 try_parse_one!(input, style, BorderStyle::parse);
109 try_parse_one!(context, input, color, Color::parse);
110 parsed -= 1;
111 break;
112 }
113 if parsed == 0 {
114 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
115 }
116 Ok((
117 width.unwrap_or(BorderSideWidth::medium()),
118 style.unwrap_or(BorderStyle::None),
119 color.unwrap_or(Color::CurrentColor),
120 ))
121}
122
123pub mod border_block {
124 use super::*;
125 pub use crate::properties::generated::shorthands::border_block::*;
126
127 pub fn parse_value(
128 context: &ParserContext,
129 input: &mut Parser,
130 ) -> Result<Longhands, ParseError> {
131 let (width, style, color) = super::parse_border(context, input)?;
132 Ok(Longhands {
133 border_block_start_width: width.clone(),
134 border_block_start_style: style,
135 border_block_start_color: color.clone(),
136 border_block_end_width: width,
137 border_block_end_style: style,
138 border_block_end_color: color,
139 })
140 }
141
142 impl<'a> ToCss for LonghandsToSerialize<'a> {
143 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
144 where
145 W: fmt::Write,
146 {
147 super::serialize_directional_border(
149 dest,
150 self.border_block_start_width,
151 self.border_block_start_style,
152 self.border_block_start_color,
153 )
154 }
155 }
156}
157
158pub mod border_inline {
159 use super::*;
160 pub use crate::properties::generated::shorthands::border_inline::*;
161
162 pub fn parse_value(
163 context: &ParserContext,
164 input: &mut Parser,
165 ) -> Result<Longhands, ParseError> {
166 let (width, style, color) = super::parse_border(context, input)?;
167 Ok(Longhands {
168 border_inline_start_width: width.clone(),
169 border_inline_start_style: style,
170 border_inline_start_color: color.clone(),
171 border_inline_end_width: width,
172 border_inline_end_style: style,
173 border_inline_end_color: color,
174 })
175 }
176
177 impl<'a> ToCss for LonghandsToSerialize<'a> {
178 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
179 where
180 W: fmt::Write,
181 {
182 super::serialize_directional_border(
184 dest,
185 self.border_inline_start_width,
186 self.border_inline_start_style,
187 self.border_inline_start_color,
188 )
189 }
190 }
191}
192
193pub mod border_radius {
194 pub use crate::properties::generated::shorthands::border_radius::*;
195
196 use super::*;
197 use crate::values::generics::border::BorderCornerRadius;
198 use crate::values::generics::rect::Rect;
199 use crate::values::specified::BorderRadius;
200
201 pub fn parse_value(
202 context: &ParserContext,
203 input: &mut Parser,
204 ) -> Result<Longhands, ParseError> {
205 let radii = BorderRadius::parse(context, input)?;
206 Ok(expanded! {
207 border_top_left_radius: radii.top_left,
208 border_top_right_radius: radii.top_right,
209 border_bottom_right_radius: radii.bottom_right,
210 border_bottom_left_radius: radii.bottom_left,
211 })
212 }
213
214 impl<'a> ToCss for LonghandsToSerialize<'a> {
215 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
216 where
217 W: fmt::Write,
218 {
219 let LonghandsToSerialize {
220 border_top_left_radius: BorderCornerRadius(tl),
221 border_top_right_radius: BorderCornerRadius(tr),
222 border_bottom_right_radius: BorderCornerRadius(br),
223 border_bottom_left_radius: BorderCornerRadius(bl),
224 } = *self;
225
226 let widths = Rect::new(tl.width(), tr.width(), br.width(), bl.width());
227 let heights = Rect::new(tl.height(), tr.height(), br.height(), bl.height());
228
229 BorderRadius::serialize_rects(widths, heights, dest)
230 }
231 }
232}
233
234pub mod corner_shape {
235 pub use crate::properties::generated::shorthands::corner_shape::*;
236
237 use super::*;
238 use crate::values::generics::rect::Rect;
239 use crate::values::specified::CornerShape;
240
241 pub fn parse_value(
244 context: &ParserContext,
245 input: &mut Parser,
246 ) -> Result<Longhands, ParseError> {
247 let rect = Rect::parse_with(context, input, CornerShape::parse)?;
248 Ok(expanded! {
249 corner_top_left_shape: rect.0,
250 corner_top_right_shape: rect.1,
251 corner_bottom_right_shape: rect.2,
252 corner_bottom_left_shape: rect.3,
253 })
254 }
255
256 impl<'a> ToCss for LonghandsToSerialize<'a> {
257 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
258 where
259 W: fmt::Write,
260 {
261 Rect::new(
262 self.corner_top_left_shape,
263 self.corner_top_right_shape,
264 self.corner_bottom_right_shape,
265 self.corner_bottom_left_shape,
266 )
267 .to_css(dest)
268 }
269 }
270}
271
272pub mod border_image {
273 pub use crate::properties::generated::shorthands::border_image::*;
274
275 use super::*;
276 use crate::properties::longhands::{
277 border_image_outset, border_image_repeat, border_image_slice, border_image_source,
278 border_image_width,
279 };
280
281 pub fn parse_value(
282 context: &ParserContext,
283 input: &mut Parser,
284 ) -> Result<Longhands, ParseError> {
285 let mut outset = border_image_outset::get_initial_specified_value();
286 let mut repeat = border_image_repeat::get_initial_specified_value();
287 let mut slice = border_image_slice::get_initial_specified_value();
288 let mut source = border_image_source::get_initial_specified_value();
289 let mut width = border_image_width::get_initial_specified_value();
290 let mut any = false;
291 let mut parsed_slice = false;
292 let mut parsed_source = false;
293 let mut parsed_repeat = false;
294 loop {
295 if !parsed_slice
296 && let Ok(value) =
297 input.try_parse(|input| border_image_slice::parse(context, input))
298 {
299 parsed_slice = true;
300 any = true;
301 slice = value;
302 let maybe_width_outset: Result<_, ParseError> = input.try_parse(|input| {
304 input.expect_delim('/')?;
305
306 let w = input
308 .try_parse(|input| border_image_width::parse(context, input))
309 .ok();
310
311 let o = input
313 .try_parse(|input| {
314 input.expect_delim('/')?;
315 border_image_outset::parse(context, input)
316 })
317 .ok();
318 if w.is_none() && o.is_none() {
319 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
320 }
321 Ok((w, o))
322 });
323 if let Ok((w, o)) = maybe_width_outset {
324 if let Some(w) = w {
325 width = w;
326 }
327 if let Some(o) = o {
328 outset = o;
329 }
330 }
331 continue;
332 }
333 if !parsed_source
334 && let Ok(value) =
335 input.try_parse(|input| border_image_source::parse(context, input))
336 {
337 source = value;
338 parsed_source = true;
339 any = true;
340 continue;
341 }
342 if !parsed_repeat
343 && let Ok(value) =
344 input.try_parse(|input| border_image_repeat::parse(context, input))
345 {
346 repeat = value;
347 parsed_repeat = true;
348 any = true;
349 continue;
350 }
351 break;
352 }
353 if !any {
354 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
355 }
356 Ok(expanded! {
357 border_image_outset: outset,
358 border_image_repeat: repeat,
359 border_image_slice: slice,
360 border_image_source: source,
361 border_image_width: width,
362 })
363 }
364
365 impl<'a> ToCss for LonghandsToSerialize<'a> {
366 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
367 where
368 W: fmt::Write,
369 {
370 let mut has_any = false;
371 let has_source =
372 *self.border_image_source != border_image_source::get_initial_specified_value();
373 has_any = has_any || has_source;
374 let has_slice =
375 *self.border_image_slice != border_image_slice::get_initial_specified_value();
376 has_any = has_any || has_slice;
377 let has_outset =
378 *self.border_image_outset != border_image_outset::get_initial_specified_value();
379 has_any = has_any || has_outset;
380 let has_width =
381 *self.border_image_width != border_image_width::get_initial_specified_value();
382 has_any = has_any || has_width;
383 let has_repeat =
384 *self.border_image_repeat != border_image_repeat::get_initial_specified_value();
385 has_any = has_any || has_repeat;
386 if has_source || !has_any {
387 self.border_image_source.to_css(dest)?;
388 if !has_any {
389 return Ok(());
390 }
391 }
392 let needs_slice = has_slice || has_width || has_outset;
393 if needs_slice {
394 if has_source {
395 dest.write_char(' ')?;
396 }
397 self.border_image_slice.to_css(dest)?;
398 if has_width || has_outset {
399 dest.write_str(" /")?;
400 if has_width {
401 dest.write_char(' ')?;
402 self.border_image_width.to_css(dest)?;
403 }
404 if has_outset {
405 dest.write_str(" / ")?;
406 self.border_image_outset.to_css(dest)?;
407 }
408 }
409 }
410 if has_repeat {
411 if has_source || needs_slice {
412 dest.write_char(' ')?;
413 }
414 self.border_image_repeat.to_css(dest)?;
415 }
416 Ok(())
417 }
418 }
419}
420
421pub mod border {
422 pub use crate::properties::generated::shorthands::border::*;
423
424 use super::*;
425 pub use crate::properties::generated::shorthands::border_left;
426 use crate::properties::longhands::{
427 border_image_outset, border_image_repeat, border_image_slice, border_image_source,
428 border_image_width,
429 };
430
431 pub fn parse_value(
432 context: &ParserContext,
433 input: &mut Parser,
434 ) -> Result<Longhands, ParseError> {
435 let (width, style, color) = super::parse_border(context, input)?;
436 Ok(expanded! {
437 border_top_width: width.clone(),
438 border_top_style: style,
439 border_top_color: color.clone(),
440 border_right_width: width.clone(),
441 border_right_style: style,
442 border_right_color: color.clone(),
443 border_bottom_width: width.clone(),
444 border_bottom_style: style,
445 border_bottom_color: color.clone(),
446 border_left_width: width.clone(),
447 border_left_style: style,
448 border_left_color: color.clone(),
449
450 border_image_outset: border_image_outset::get_initial_specified_value(),
453 border_image_repeat: border_image_repeat::get_initial_specified_value(),
454 border_image_slice: border_image_slice::get_initial_specified_value(),
455 border_image_source: border_image_source::get_initial_specified_value(),
456 border_image_width: border_image_width::get_initial_specified_value(),
457 })
458 }
459
460 impl<'a> ToCss for LonghandsToSerialize<'a> {
461 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
462 where
463 W: fmt::Write,
464 {
465 use crate::properties::longhands;
466
467 if *self.border_image_outset
470 != longhands::border_image_outset::get_initial_specified_value()
471 {
472 return Ok(());
473 }
474 if *self.border_image_repeat
475 != longhands::border_image_repeat::get_initial_specified_value()
476 {
477 return Ok(());
478 }
479 if *self.border_image_slice
480 != longhands::border_image_slice::get_initial_specified_value()
481 {
482 return Ok(());
483 }
484 if *self.border_image_source
485 != longhands::border_image_source::get_initial_specified_value()
486 {
487 return Ok(());
488 }
489 if *self.border_image_width
490 != longhands::border_image_width::get_initial_specified_value()
491 {
492 return Ok(());
493 }
494
495 let all_equal = {
496 let border_top_width = self.border_top_width;
497 let border_top_style = self.border_top_style;
498 let border_top_color = self.border_top_color;
499 let border_right_width = self.border_right_width;
500 let border_right_style = self.border_right_style;
501 let border_right_color = self.border_right_color;
502 let border_bottom_width = self.border_bottom_width;
503 let border_bottom_style = self.border_bottom_style;
504 let border_bottom_color = self.border_bottom_color;
505 let border_left_width = self.border_left_width;
506 let border_left_style = self.border_left_style;
507 let border_left_color = self.border_left_color;
508
509 border_top_width == border_right_width
510 && border_right_width == border_bottom_width
511 && border_bottom_width == border_left_width
512 && border_top_style == border_right_style
513 && border_right_style == border_bottom_style
514 && border_bottom_style == border_left_style
515 && border_top_color == border_right_color
516 && border_right_color == border_bottom_color
517 && border_bottom_color == border_left_color
518 };
519
520 if !all_equal {
523 return Ok(());
524 }
525 super::serialize_directional_border(
526 dest,
527 self.border_left_width,
528 self.border_left_style,
529 self.border_left_color,
530 )
531 }
532 }
533
534 impl SpecifiedValueInfo for Longhands {
537 const SUPPORTED_TYPES: u8 = border_left::Longhands::SUPPORTED_TYPES;
538
539 fn collect_completion_keywords(f: KeywordsCollectFn) {
540 border_left::Longhands::collect_completion_keywords(f);
541 }
542 }
543}
544
545#[cfg(feature = "gecko")]
546pub mod container {
547 use super::*;
548 pub use crate::properties::generated::shorthands::container::*;
549
550 use crate::values::specified::{ContainerName, ContainerType};
551
552 pub fn parse_value(
553 context: &ParserContext,
554 input: &mut Parser,
555 ) -> Result<Longhands, ParseError> {
556 let container_name = ContainerName::parse(context, input)?;
558 let container_type = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
559 ContainerType::parse(context, input)?
560 } else {
561 ContainerType::NORMAL
562 };
563 Ok(expanded! {
564 container_name: container_name,
565 container_type: container_type,
566 })
567 }
568
569 impl<'a> ToCss for LonghandsToSerialize<'a> {
570 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
571 where
572 W: fmt::Write,
573 {
574 self.container_name.to_css(dest)?;
575 if !self.container_type.is_normal() {
576 dest.write_str(" / ")?;
577 self.container_type.to_css(dest)?;
578 }
579 Ok(())
580 }
581 }
582}
583
584pub mod vertical_align {
585 use super::*;
586 pub use crate::properties::generated::shorthands::vertical_align::*;
587
588 use crate::values::specified::{AlignmentBaseline, BaselineShift, BaselineSource};
589
590 pub fn parse_value(
591 context: &ParserContext,
592 input: &mut Parser,
593 ) -> Result<Longhands, ParseError> {
594 let mut baseline_source = None;
595 let mut alignment_baseline = None;
596 let mut baseline_shift = None;
597 let mut parsed = 0;
598
599 loop {
600 parsed += 1;
601
602 try_parse_one!(input, baseline_source, BaselineSource::parse_non_auto);
603 try_parse_one!(input, alignment_baseline, AlignmentBaseline::parse);
604 try_parse_one!(context, input, baseline_shift, BaselineShift::parse);
605
606 parsed -= 1;
607 break;
608 }
609
610 if parsed == 0 {
611 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
612 }
613
614 Ok(expanded! {
615 baseline_source: baseline_source.unwrap_or(BaselineSource::Auto),
616 alignment_baseline: alignment_baseline.unwrap_or(AlignmentBaseline::Baseline),
617 baseline_shift: baseline_shift.unwrap_or(BaselineShift::zero()),
618 })
619 }
620
621 impl<'a> ToCss for LonghandsToSerialize<'a> {
622 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
623 where
624 W: fmt::Write,
625 {
626 let mut writer = SequenceWriter::new(dest, " ");
627 if *self.baseline_source != BaselineSource::Auto {
628 writer.item(self.baseline_source)?;
629 }
630 if *self.alignment_baseline != AlignmentBaseline::Baseline {
631 writer.item(self.alignment_baseline)?;
632 }
633 if *self.baseline_shift != BaselineShift::zero() {
634 writer.item(self.baseline_shift)?;
635 }
636 if !writer.has_written() {
637 self.alignment_baseline.to_css(dest)?;
638 }
639 Ok(())
640 }
641 }
642}
643
644#[cfg(feature = "gecko")]
645pub mod offset {
646 use super::*;
647 use crate::Zero;
648 pub use crate::properties::generated::shorthands::offset::*;
649 use crate::values::specified::{
650 LengthPercentage, OffsetPath, OffsetPosition, OffsetRotate, PositionOrAuto,
651 };
652
653 pub fn parse_value(
654 context: &ParserContext,
655 input: &mut Parser,
656 ) -> Result<Longhands, ParseError> {
657 let offset_position = input.try_parse(|i| OffsetPosition::parse(context, i)).ok();
658 let offset_path = input.try_parse(|i| OffsetPath::parse(context, i)).ok();
659
660 if offset_position.is_none() && offset_path.is_none() {
663 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
664 }
665
666 let mut offset_distance = None;
667 let mut offset_rotate = None;
668 if offset_path.is_some() {
670 loop {
671 if offset_distance.is_none()
672 && let Ok(value) = input.try_parse(|i| LengthPercentage::parse(context, i))
673 {
674 offset_distance = Some(value);
675 }
676
677 if offset_rotate.is_none()
678 && let Ok(value) = input.try_parse(|i| OffsetRotate::parse(context, i))
679 {
680 offset_rotate = Some(value);
681 continue;
682 }
683 break;
684 }
685 }
686
687 let offset_anchor = input
688 .try_parse(|i| {
689 i.expect_delim('/')?;
690 PositionOrAuto::parse(context, i)
691 })
692 .ok();
693
694 Ok(expanded! {
695 offset_position: offset_position.unwrap_or(OffsetPosition::normal()),
696 offset_path: offset_path.unwrap_or(OffsetPath::none()),
697 offset_distance: offset_distance.unwrap_or(LengthPercentage::zero()),
698 offset_rotate: offset_rotate.unwrap_or(OffsetRotate::auto()),
699 offset_anchor: offset_anchor.unwrap_or(PositionOrAuto::auto()),
700 })
701 }
702
703 impl<'a> ToCss for LonghandsToSerialize<'a> {
704 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
705 where
706 W: fmt::Write,
707 {
708 let must_serialize_path = *self.offset_path != OffsetPath::None
711 || (!self.offset_distance.is_zero() || !self.offset_rotate.is_auto());
712 let position_is_default = matches!(self.offset_position, OffsetPosition::Normal);
713 if !position_is_default || !must_serialize_path {
714 self.offset_position.to_css(dest)?;
715 }
716
717 if must_serialize_path {
718 if !position_is_default {
719 dest.write_char(' ')?;
720 }
721 self.offset_path.to_css(dest)?;
722 }
723
724 if !self.offset_distance.is_zero() {
725 dest.write_char(' ')?;
726 self.offset_distance.to_css(dest)?;
727 }
728
729 if !self.offset_rotate.is_auto() {
730 dest.write_char(' ')?;
731 self.offset_rotate.to_css(dest)?;
732 }
733
734 if *self.offset_anchor != PositionOrAuto::auto() {
735 dest.write_str(" / ")?;
736 self.offset_anchor.to_css(dest)?;
737 }
738 Ok(())
739 }
740 }
741}
742
743pub mod columns {
744 pub use crate::properties::generated::shorthands::columns::*;
745
746 use super::*;
747 use crate::properties::longhands::{column_count, column_width};
748
749 pub fn parse_value(
750 context: &ParserContext,
751 input: &mut Parser,
752 ) -> Result<Longhands, ParseError> {
753 let mut column_count = None;
754 let mut column_width = None;
755 let mut autos = 0;
756
757 loop {
758 if input
759 .try_parse(|input| input.expect_ident_matching("auto"))
760 .is_ok()
761 {
762 autos += 1;
764 continue;
765 }
766
767 if column_count.is_none()
768 && let Ok(value) = input.try_parse(|input| column_count::parse(context, input))
769 {
770 column_count = Some(value);
771 continue;
772 }
773
774 if column_width.is_none()
775 && let Ok(value) = input.try_parse(|input| column_width::parse(context, input))
776 {
777 column_width = Some(value);
778 continue;
779 }
780
781 break;
782 }
783
784 let values = autos + column_count.iter().len() + column_width.iter().len();
785 if values == 0 || values > 2 {
786 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
787 } else {
788 Ok(expanded! {
789 column_count: unwrap_or_initial!(column_count),
790 column_width: unwrap_or_initial!(column_width),
791 })
792 }
793 }
794
795 impl<'a> ToCss for LonghandsToSerialize<'a> {
796 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
797 where
798 W: fmt::Write,
799 {
800 if self.column_width.is_auto() {
801 return self.column_count.to_css(dest);
802 }
803 self.column_width.to_css(dest)?;
804 if !self.column_count.is_auto() {
805 dest.write_char(' ')?;
806 self.column_count.to_css(dest)?;
807 }
808 Ok(())
809 }
810 }
811}
812
813#[cfg(feature = "gecko")]
814pub mod column_rule {
815 pub use crate::properties::generated::shorthands::column_rule::*;
816
817 use super::*;
818 use crate::properties::longhands::column_rule_color;
819 use crate::properties::longhands::{column_rule_style, column_rule_width};
820
821 pub fn parse_value(
822 context: &ParserContext,
823 input: &mut Parser,
824 ) -> Result<Longhands, ParseError> {
825 let mut column_rule_width = None;
826 let mut column_rule_style = None;
827 let mut column_rule_color = None;
828 let mut parsed = 0;
829 loop {
830 parsed += 1;
831 try_parse_one!(context, input, column_rule_width, column_rule_width::parse);
832 try_parse_one!(context, input, column_rule_style, column_rule_style::parse);
833 try_parse_one!(context, input, column_rule_color, column_rule_color::parse);
834 parsed -= 1;
835 break;
836 }
837 if parsed == 0 {
838 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
839 }
840 Ok(expanded! {
841 column_rule_width: unwrap_or_initial!(column_rule_width),
842 column_rule_style: unwrap_or_initial!(column_rule_style),
843 column_rule_color: unwrap_or_initial!(column_rule_color),
844 })
845 }
846}
847
848pub mod text_wrap {
849 pub use crate::properties::generated::shorthands::text_wrap::*;
850
851 use super::*;
852 use crate::properties::longhands::{text_wrap_mode, text_wrap_style};
853
854 pub fn parse_value(
855 context: &ParserContext,
856 input: &mut Parser,
857 ) -> Result<Longhands, ParseError> {
858 let mut mode = None;
859 let mut style = None;
860 let mut parsed = 0;
861 loop {
862 parsed += 1;
863 try_parse_one!(context, input, mode, text_wrap_mode::parse);
864 try_parse_one!(context, input, style, text_wrap_style::parse);
865 parsed -= 1;
866 break;
867 }
868 if parsed == 0 {
869 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
870 }
871 Ok(expanded! {
872 text_wrap_mode: unwrap_or_initial!(text_wrap_mode, mode),
873 text_wrap_style: unwrap_or_initial!(text_wrap_style, style),
874 })
875 }
876
877 impl<'a> ToCss for LonghandsToSerialize<'a> {
878 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
879 where
880 W: fmt::Write,
881 {
882 use text_wrap_mode::computed_value::T as Mode;
883 use text_wrap_style::computed_value::T as Style;
884
885 if matches!(self.text_wrap_style, &Style::Auto) {
886 return self.text_wrap_mode.to_css(dest);
887 }
888
889 if *self.text_wrap_mode != Mode::Wrap {
890 self.text_wrap_mode.to_css(dest)?;
891 dest.write_char(' ')?;
892 }
893
894 self.text_wrap_style.to_css(dest)
895 }
896 }
897}
898
899pub mod white_space {
900 pub use crate::properties::generated::shorthands::white_space::*;
901
902 use super::*;
903 use crate::properties::longhands::{text_wrap_mode, white_space_collapse};
904
905 pub fn parse_value(
906 context: &ParserContext,
907 input: &mut Parser,
908 ) -> Result<Longhands, ParseError> {
909 use text_wrap_mode::computed_value::T as Wrap;
910 use white_space_collapse::computed_value::T as Collapse;
911
912 fn parse_special_shorthands(input: &mut Parser) -> Result<Longhands, ParseError> {
913 let (mode, collapse) = try_match_ident_ignore_ascii_case! { input,
914 "normal" => (Wrap::Wrap, Collapse::Collapse),
915 "pre" => (Wrap::Nowrap, Collapse::Preserve),
916 "pre-wrap" => (Wrap::Wrap, Collapse::Preserve),
917 "pre-line" => (Wrap::Wrap, Collapse::PreserveBreaks),
918 };
919 Ok(expanded! {
920 text_wrap_mode: mode,
921 white_space_collapse: collapse,
922 })
923 }
924
925 if let Ok(result) = input.try_parse(parse_special_shorthands) {
926 return Ok(result);
927 }
928
929 let mut wrap = None;
930 let mut collapse = None;
931 let mut parsed = 0;
932
933 loop {
934 parsed += 1;
935 try_parse_one!(context, input, wrap, text_wrap_mode::parse);
936 try_parse_one!(context, input, collapse, white_space_collapse::parse);
937 parsed -= 1;
938 break;
939 }
940
941 if parsed == 0 {
942 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
943 }
944
945 Ok(expanded! {
946 text_wrap_mode: unwrap_or_initial!(text_wrap_mode, wrap),
947 white_space_collapse: unwrap_or_initial!(white_space_collapse, collapse),
948 })
949 }
950
951 impl<'a> ToCss for LonghandsToSerialize<'a> {
952 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
953 where
954 W: fmt::Write,
955 {
956 use text_wrap_mode::computed_value::T as Wrap;
957 use white_space_collapse::computed_value::T as Collapse;
958
959 match *self.text_wrap_mode {
960 Wrap::Wrap => match *self.white_space_collapse {
961 Collapse::Collapse => return dest.write_str("normal"),
962 Collapse::Preserve => return dest.write_str("pre-wrap"),
963 Collapse::PreserveBreaks => return dest.write_str("pre-line"),
964 _ => (),
965 },
966 Wrap::Nowrap => {
967 if let Collapse::Preserve = *self.white_space_collapse {
968 return dest.write_str("pre");
969 }
970 },
971 }
972
973 let mut has_value = false;
974 if *self.white_space_collapse != Collapse::Collapse {
975 self.white_space_collapse.to_css(dest)?;
976 has_value = true;
977 }
978
979 if *self.text_wrap_mode != Wrap::Wrap {
980 if has_value {
981 dest.write_char(' ')?;
982 }
983 self.text_wrap_mode.to_css(dest)?;
984 }
985
986 Ok(())
987 }
988 }
989
990 impl SpecifiedValueInfo for Longhands {
991 fn collect_completion_keywords(f: KeywordsCollectFn) {
992 text_wrap_mode::SpecifiedValue::collect_completion_keywords(f);
994 white_space_collapse::SpecifiedValue::collect_completion_keywords(f);
995
996 f(&["normal", "pre", "pre-wrap", "pre-line"])
999 }
1000 }
1001}
1002
1003#[cfg(feature = "gecko")]
1004pub mod _webkit_text_stroke {
1005 pub use crate::properties::generated::shorthands::_webkit_text_stroke::*;
1006
1007 use super::*;
1008 use crate::properties::longhands::{_webkit_text_stroke_color, _webkit_text_stroke_width};
1009
1010 pub fn parse_value(
1011 context: &ParserContext,
1012 input: &mut Parser,
1013 ) -> Result<Longhands, ParseError> {
1014 let mut color = None;
1015 let mut width = None;
1016 let mut parsed = 0;
1017 loop {
1018 parsed += 1;
1019 try_parse_one!(context, input, color, _webkit_text_stroke_color::parse);
1020 try_parse_one!(context, input, width, _webkit_text_stroke_width::parse);
1021 parsed -= 1;
1022 break;
1023 }
1024 if parsed == 0 {
1025 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1026 }
1027 Ok(expanded! {
1028 _webkit_text_stroke_color: unwrap_or_initial!(_webkit_text_stroke_color, color),
1029 _webkit_text_stroke_width: unwrap_or_initial!(_webkit_text_stroke_width, width),
1030 })
1031 }
1032}
1033
1034pub mod list_style {
1035 pub use crate::properties::generated::shorthands::list_style::*;
1036
1037 use super::*;
1038 use crate::properties::longhands::{list_style_image, list_style_position, list_style_type};
1039 use crate::values::specified::Image;
1040 use selectors::parser::SelectorParseErrorKind;
1041
1042 pub fn parse_value(
1043 context: &ParserContext,
1044 input: &mut Parser,
1045 ) -> Result<Longhands, ParseError> {
1046 let mut nones = 0u8;
1049 let (mut image, mut position, mut list_style_type) = (None, None, None);
1050 let mut parsed = 0;
1051 loop {
1052 parsed += 1;
1053
1054 if input
1055 .try_parse(|input| input.expect_ident_matching("none"))
1056 .is_ok()
1057 {
1058 nones += 1;
1059 if nones > 2 {
1060 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent));
1061 }
1062 continue;
1063 }
1064
1065 try_parse_one!(context, input, image, list_style_image::parse);
1066 try_parse_one!(context, input, position, list_style_position::parse);
1067 try_parse_one!(context, input, list_style_type, list_style_type::parse);
1071
1072 parsed -= 1;
1073 break;
1074 }
1075
1076 let position = unwrap_or_initial!(list_style_position, position);
1077
1078 if parsed == 0 {
1079 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1080 }
1081
1082 use self::list_style_type::SpecifiedValue as ListStyleType;
1086 match (nones, list_style_type, image) {
1087 (2, None, None) => Ok(expanded! {
1088 list_style_position: position,
1089 list_style_image: Image::None,
1090 list_style_type: ListStyleType::none(),
1091 }),
1092 (1, None, Some(image)) => Ok(expanded! {
1093 list_style_position: position,
1094 list_style_image: image,
1095 list_style_type: ListStyleType::none(),
1096 }),
1097 (1, Some(list_style_type), None) => Ok(expanded! {
1098 list_style_position: position,
1099 list_style_image: Image::None,
1100 list_style_type: list_style_type,
1101 }),
1102 (1, None, None) => Ok(expanded! {
1103 list_style_position: position,
1104 list_style_image: Image::None,
1105 list_style_type: ListStyleType::none(),
1106 }),
1107 (0, list_style_type, image) => Ok(expanded! {
1108 list_style_position: position,
1109 list_style_image: unwrap_or_initial!(list_style_image, image),
1110 list_style_type: unwrap_or_initial!(list_style_type),
1111 }),
1112 _ => Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
1113 }
1114 }
1115
1116 impl<'a> ToCss for LonghandsToSerialize<'a> {
1117 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1118 where
1119 W: fmt::Write,
1120 {
1121 use list_style_image::SpecifiedValue as ListStyleImage;
1122 use list_style_position::SpecifiedValue as ListStylePosition;
1123 use list_style_type::SpecifiedValue as ListStyleType;
1124
1125 let mut writer = SequenceWriter::new(dest, " ");
1126 if *self.list_style_position != ListStylePosition::Outside
1127 || self.list_style_type.is_name(&atom!("outside"))
1128 {
1129 writer.item(self.list_style_position)?;
1130 }
1131 if *self.list_style_image != ListStyleImage::None {
1132 writer.item(self.list_style_image)?;
1133 }
1134 if *self.list_style_type != ListStyleType::disc() {
1135 writer.item(self.list_style_type)?;
1136 }
1137 if !writer.has_written() {
1138 self.list_style_position.to_css(dest)?;
1139 }
1140 Ok(())
1141 }
1142 }
1143}
1144
1145pub mod gap {
1146 pub use crate::properties::generated::shorthands::gap::*;
1147
1148 use super::*;
1149 use crate::properties::longhands::{column_gap, row_gap};
1150
1151 pub fn parse_value(
1152 context: &ParserContext,
1153 input: &mut Parser,
1154 ) -> Result<Longhands, ParseError> {
1155 let r_gap = row_gap::parse(context, input)?;
1156 let c_gap = input
1157 .try_parse(|input| column_gap::parse(context, input))
1158 .unwrap_or(r_gap.clone());
1159
1160 Ok(expanded! {
1161 row_gap: r_gap,
1162 column_gap: c_gap,
1163 })
1164 }
1165
1166 impl<'a> ToCss for LonghandsToSerialize<'a> {
1167 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1168 where
1169 W: fmt::Write,
1170 {
1171 self.row_gap.to_css(dest)?;
1172 if self.row_gap != self.column_gap {
1173 dest.write_char(' ')?;
1174 self.column_gap.to_css(dest)?;
1175 }
1176 Ok(())
1177 }
1178 }
1179}
1180
1181#[cfg(feature = "gecko")]
1182pub mod marker {
1183 pub use crate::properties::generated::shorthands::marker::*;
1184
1185 use super::*;
1186 use crate::values::specified::url::UrlOrNone;
1187
1188 pub fn parse_value(
1189 context: &ParserContext,
1190 input: &mut Parser,
1191 ) -> Result<Longhands, ParseError> {
1192 let url = UrlOrNone::parse(context, input)?;
1193
1194 Ok(expanded! {
1195 marker_start: url.clone(),
1196 marker_mid: url.clone(),
1197 marker_end: url,
1198 })
1199 }
1200
1201 impl<'a> ToCss for LonghandsToSerialize<'a> {
1202 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1203 where
1204 W: fmt::Write,
1205 {
1206 if self.marker_start == self.marker_mid && self.marker_mid == self.marker_end {
1207 self.marker_start.to_css(dest)
1208 } else {
1209 Ok(())
1210 }
1211 }
1212 }
1213}
1214
1215pub mod flex_flow {
1216 pub use crate::properties::generated::shorthands::flex_flow::*;
1217
1218 use super::*;
1219 use crate::properties::longhands::{flex_direction, flex_wrap};
1220
1221 pub fn parse_value(
1222 context: &ParserContext,
1223 input: &mut Parser,
1224 ) -> Result<Longhands, ParseError> {
1225 let mut parsed = 0;
1226 let mut direction = None;
1227 let mut wrap = None;
1228 loop {
1229 parsed += 1;
1230 try_parse_one!(context, input, direction, flex_direction::parse);
1231 try_parse_one!(context, input, wrap, flex_wrap::parse);
1232 parsed -= 1;
1233 break;
1234 }
1235 if parsed == 0 {
1236 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1237 }
1238 Ok(expanded! {
1239 flex_direction: unwrap_or_initial!(flex_direction, direction),
1240 flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
1241 })
1242 }
1243
1244 impl<'a> ToCss for LonghandsToSerialize<'a> {
1245 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1246 where
1247 W: fmt::Write,
1248 {
1249 if *self.flex_direction == flex_direction::get_initial_specified_value()
1250 && *self.flex_wrap != flex_wrap::get_initial_specified_value()
1251 {
1252 return self.flex_wrap.to_css(dest);
1253 }
1254 self.flex_direction.to_css(dest)?;
1255 if *self.flex_wrap != flex_wrap::get_initial_specified_value() {
1256 dest.write_char(' ')?;
1257 self.flex_wrap.to_css(dest)?;
1258 }
1259 Ok(())
1260 }
1261 }
1262}
1263
1264pub mod flex {
1265 pub use crate::properties::generated::shorthands::flex::*;
1266
1267 use super::*;
1268 use crate::properties::longhands::flex_basis::SpecifiedValue as FlexBasis;
1269 use crate::values::specified::NonNegativeNumber;
1270
1271 fn parse_flexibility(
1272 context: &ParserContext,
1273 input: &mut Parser,
1274 ) -> Result<(NonNegativeNumber, Option<NonNegativeNumber>), ParseError> {
1275 let grow = NonNegativeNumber::parse(context, input)?;
1276 let shrink = input
1277 .try_parse(|i| NonNegativeNumber::parse(context, i))
1278 .ok();
1279 Ok((grow, shrink))
1280 }
1281
1282 pub fn parse_value(
1283 context: &ParserContext,
1284 input: &mut Parser,
1285 ) -> Result<Longhands, ParseError> {
1286 let mut grow = None;
1287 let mut shrink = None;
1288 let mut basis = None;
1289
1290 if input
1291 .try_parse(|input| input.expect_ident_matching("none"))
1292 .is_ok()
1293 {
1294 return Ok(expanded! {
1295 flex_grow: NonNegativeNumber::new(0.0),
1296 flex_shrink: NonNegativeNumber::new(0.0),
1297 flex_basis: FlexBasis::auto(),
1298 });
1299 }
1300 loop {
1301 if grow.is_none()
1302 && let Ok((flex_grow, flex_shrink)) =
1303 input.try_parse(|i| parse_flexibility(context, i))
1304 {
1305 grow = Some(flex_grow);
1306 shrink = flex_shrink;
1307 continue;
1308 }
1309 if basis.is_none()
1310 && let Ok(value) = input.try_parse(|input| FlexBasis::parse(context, input))
1311 {
1312 basis = Some(value);
1313 continue;
1314 }
1315 break;
1316 }
1317
1318 if grow.is_none() && basis.is_none() {
1319 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1320 }
1321 Ok(expanded! {
1322 flex_grow: grow.unwrap_or(NonNegativeNumber::new(1.0)),
1323 flex_shrink: shrink.unwrap_or(NonNegativeNumber::new(1.0)),
1324 flex_basis: basis.unwrap_or(FlexBasis::zero_percent()),
1325 })
1326 }
1327}
1328
1329pub mod place_content {
1330 pub use crate::properties::generated::shorthands::place_content::*;
1331
1332 use super::*;
1333 use crate::values::specified::align::ContentDistribution;
1334
1335 pub fn parse_value(
1336 context: &ParserContext,
1337 input: &mut Parser,
1338 ) -> Result<Longhands, ParseError> {
1339 let align_content = ContentDistribution::parse_block(context, input)?;
1340 let justify_content =
1341 input.try_parse(|input| ContentDistribution::parse_inline(context, input));
1342
1343 let justify_content = match justify_content {
1344 Ok(v) => v,
1345 Err(..) => {
1346 if !align_content.is_baseline_position() {
1347 align_content
1348 } else {
1349 ContentDistribution::start()
1350 }
1351 },
1352 };
1353
1354 Ok(expanded! {
1355 align_content: align_content,
1356 justify_content: justify_content,
1357 })
1358 }
1359
1360 impl<'a> ToCss for LonghandsToSerialize<'a> {
1361 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1362 where
1363 W: fmt::Write,
1364 {
1365 self.align_content.to_css(dest)?;
1366 if self.align_content != self.justify_content {
1367 dest.write_char(' ')?;
1368 self.justify_content.to_css(dest)?;
1369 }
1370 Ok(())
1371 }
1372 }
1373}
1374
1375pub mod place_self {
1376 pub use crate::properties::generated::shorthands::place_self::*;
1377
1378 use super::*;
1379 use crate::values::specified::align::SelfAlignment;
1380
1381 pub fn parse_value(
1382 context: &ParserContext,
1383 input: &mut Parser,
1384 ) -> Result<Longhands, ParseError> {
1385 let align = SelfAlignment::parse_block(context, input)?;
1386 let justify = input.try_parse(|input| SelfAlignment::parse_inline(context, input));
1387
1388 let justify = match justify {
1389 Ok(v) => v,
1390 Err(..) => {
1391 debug_assert!(align.is_valid_on_both_axes());
1392 align
1393 },
1394 };
1395
1396 Ok(expanded! {
1397 align_self: align,
1398 justify_self: justify,
1399 })
1400 }
1401
1402 impl<'a> ToCss for LonghandsToSerialize<'a> {
1403 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1404 where
1405 W: fmt::Write,
1406 {
1407 self.align_self.to_css(dest)?;
1408 if self.align_self != self.justify_self {
1409 dest.write_char(' ')?;
1410 self.justify_self.to_css(dest)?;
1411 }
1412 Ok(())
1413 }
1414 }
1415}
1416
1417pub mod place_items {
1418 pub use crate::properties::generated::shorthands::place_items::*;
1419
1420 use super::*;
1421 use crate::values::specified::align::{ItemPlacement, JustifyItems};
1422
1423 pub fn parse_value(
1424 context: &ParserContext,
1425 input: &mut Parser,
1426 ) -> Result<Longhands, ParseError> {
1427 let align = ItemPlacement::parse_block(context, input)?;
1428 let justify = input
1429 .try_parse(|input| ItemPlacement::parse_inline(context, input))
1430 .unwrap_or(align);
1431
1432 Ok(expanded! {
1433 align_items: align,
1434 justify_items: JustifyItems(justify),
1435 })
1436 }
1437
1438 impl<'a> ToCss for LonghandsToSerialize<'a> {
1439 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1440 where
1441 W: fmt::Write,
1442 {
1443 self.align_items.to_css(dest)?;
1444 if self.align_items != &self.justify_items.0 {
1445 dest.write_char(' ')?;
1446 self.justify_items.to_css(dest)?;
1447 }
1448 Ok(())
1449 }
1450 }
1451}
1452
1453pub mod grid_row {
1454 pub use crate::properties::generated::shorthands::grid_row::*;
1455
1456 use super::*;
1457 use crate::Zero;
1458 use crate::values::specified::GridLine;
1459
1460 pub fn parse_value(
1461 context: &ParserContext,
1462 input: &mut Parser,
1463 ) -> Result<Longhands, ParseError> {
1464 let start = input.try_parse(|i| GridLine::parse(context, i))?;
1465 let end = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
1466 GridLine::parse(context, input)?
1467 } else {
1468 let mut line = GridLine::auto();
1469 if start.line_num.is_zero() && !start.is_span {
1470 line.ident = start.ident.clone();
1471 }
1472
1473 line
1474 };
1475
1476 Ok(expanded! {
1477 grid_row_start: start,
1478 grid_row_end: end,
1479 })
1480 }
1481
1482 impl<'a> ToCss for LonghandsToSerialize<'a> {
1483 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1484 where
1485 W: fmt::Write,
1486 {
1487 self.grid_row_start.to_css(dest)?;
1488 if self.grid_row_start.can_omit(self.grid_row_end) {
1489 return Ok(());
1490 }
1491 dest.write_str(" / ")?;
1492 self.grid_row_end.to_css(dest)
1493 }
1494 }
1495}
1496
1497pub mod grid_column {
1498 pub use crate::properties::generated::shorthands::grid_column::*;
1499
1500 use super::*;
1501 use crate::Zero;
1502 use crate::values::specified::GridLine;
1503
1504 pub fn parse_value(
1505 context: &ParserContext,
1506 input: &mut Parser,
1507 ) -> Result<Longhands, ParseError> {
1508 let start = input.try_parse(|i| GridLine::parse(context, i))?;
1509 let end = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
1510 GridLine::parse(context, input)?
1511 } else {
1512 let mut line = GridLine::auto();
1513 if start.line_num.is_zero() && !start.is_span {
1514 line.ident = start.ident.clone();
1515 }
1516
1517 line
1518 };
1519
1520 Ok(expanded! {
1521 grid_column_start: start,
1522 grid_column_end: end,
1523 })
1524 }
1525
1526 impl<'a> ToCss for LonghandsToSerialize<'a> {
1527 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1528 where
1529 W: fmt::Write,
1530 {
1531 self.grid_column_start.to_css(dest)?;
1532 if self.grid_column_start.can_omit(self.grid_column_end) {
1533 return Ok(());
1534 }
1535 dest.write_str(" / ")?;
1536 self.grid_column_end.to_css(dest)
1537 }
1538 }
1539}
1540
1541pub mod grid_area {
1542 pub use crate::properties::generated::shorthands::grid_area::*;
1543
1544 use super::*;
1545 use crate::Zero;
1546 use crate::values::specified::GridLine;
1547
1548 pub fn parse_value(
1549 context: &ParserContext,
1550 input: &mut Parser,
1551 ) -> Result<Longhands, ParseError> {
1552 fn line_with_ident_from(other: &GridLine) -> GridLine {
1553 let mut this = GridLine::auto();
1554 if other.line_num.is_zero() && !other.is_span {
1555 this.ident = other.ident.clone();
1556 }
1557
1558 this
1559 }
1560
1561 let row_start = input.try_parse(|i| GridLine::parse(context, i))?;
1562 let (column_start, row_end, column_end) =
1563 if input.try_parse(|i| i.expect_delim('/')).is_ok() {
1564 let column_start = GridLine::parse(context, input)?;
1565 let (row_end, column_end) = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
1566 let row_end = GridLine::parse(context, input)?;
1567 let column_end = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
1568 GridLine::parse(context, input)?
1569 } else {
1570 line_with_ident_from(&column_start)
1571 };
1572
1573 (row_end, column_end)
1574 } else {
1575 let row_end = line_with_ident_from(&row_start);
1576 let column_end = line_with_ident_from(&column_start);
1577 (row_end, column_end)
1578 };
1579
1580 (column_start, row_end, column_end)
1581 } else {
1582 let line = line_with_ident_from(&row_start);
1583 (line.clone(), line.clone(), line)
1584 };
1585
1586 Ok(expanded! {
1587 grid_row_start: row_start,
1588 grid_row_end: row_end,
1589 grid_column_start: column_start,
1590 grid_column_end: column_end,
1591 })
1592 }
1593
1594 impl<'a> ToCss for LonghandsToSerialize<'a> {
1595 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1596 where
1597 W: fmt::Write,
1598 {
1599 self.grid_row_start.to_css(dest)?;
1600 let mut trailing_values = 3;
1601 if self.grid_column_start.can_omit(self.grid_column_end) {
1602 trailing_values -= 1;
1603 if self.grid_row_start.can_omit(self.grid_row_end) {
1604 trailing_values -= 1;
1605 if self.grid_row_start.can_omit(self.grid_column_start) {
1606 trailing_values -= 1;
1607 }
1608 }
1609 }
1610 let values = [
1611 &self.grid_column_start,
1612 &self.grid_row_end,
1613 &self.grid_column_end,
1614 ];
1615 for value in values.iter().take(trailing_values) {
1616 dest.write_str(" / ")?;
1617 value.to_css(dest)?;
1618 }
1619 Ok(())
1620 }
1621 }
1622}
1623
1624#[cfg(feature = "gecko")]
1625pub mod position_try {
1626 pub use crate::properties::generated::shorthands::position_try::*;
1627
1628 use super::*;
1629 use crate::values::specified::position::{PositionTryFallbacks, PositionTryOrder};
1630
1631 pub fn parse_value(
1632 context: &ParserContext,
1633 input: &mut Parser,
1634 ) -> Result<Longhands, ParseError> {
1635 let order = input.try_parse(PositionTryOrder::parse).ok();
1636 let fallbacks = PositionTryFallbacks::parse(context, input)?;
1637 Ok(expanded! {
1638 position_try_order: order.unwrap_or(PositionTryOrder::normal()),
1639 position_try_fallbacks: fallbacks,
1640 })
1641 }
1642
1643 impl<'a> ToCss for LonghandsToSerialize<'a> {
1644 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1645 where
1646 W: fmt::Write,
1647 {
1648 if !self.position_try_order.is_normal() {
1649 self.position_try_order.to_css(dest)?;
1650 dest.write_char(' ')?;
1651 }
1652 self.position_try_fallbacks.to_css(dest)
1653 }
1654 }
1655}
1656
1657#[cfg(feature = "gecko")]
1658pub mod scroll_timeline {
1659 pub use crate::properties::generated::shorthands::scroll_timeline::*;
1660
1661 use super::*;
1662 use crate::properties::longhands::{scroll_timeline_axis, scroll_timeline_name};
1663
1664 pub fn parse_value(
1665 context: &ParserContext,
1666 input: &mut Parser,
1667 ) -> Result<Longhands, ParseError> {
1668 let mut names = Vec::with_capacity(1);
1669 let mut axes = Vec::with_capacity(1);
1670 input.parse_comma_separated(|input| {
1671 let name = scroll_timeline_name::single_value::parse(context, input)?;
1672 let axis = input.try_parse(|i| scroll_timeline_axis::single_value::parse(context, i));
1673
1674 names.push(name);
1675 axes.push(axis.unwrap_or_default());
1676
1677 Ok(())
1678 })?;
1679
1680 Ok(expanded! {
1681 scroll_timeline_name: scroll_timeline_name::SpecifiedValue(names.into()),
1682 scroll_timeline_axis: scroll_timeline_axis::SpecifiedValue(axes.into()),
1683 })
1684 }
1685
1686 impl<'a> ToCss for LonghandsToSerialize<'a> {
1687 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1688 where
1689 W: fmt::Write,
1690 {
1691 if self.scroll_timeline_name.0.len() != self.scroll_timeline_axis.0.len() {
1692 return Ok(());
1693 }
1694 let mut first = true;
1695 for (name, axis) in std::iter::zip(
1696 self.scroll_timeline_name.0.iter(),
1697 self.scroll_timeline_axis.0.iter(),
1698 ) {
1699 if !first {
1700 dest.write_str(", ")?;
1701 }
1702 name.to_css(dest)?;
1703 if !axis.is_default() {
1704 dest.write_char(' ')?;
1705 axis.to_css(dest)?;
1706 }
1707 first = false;
1708 }
1709 Ok(())
1710 }
1711 }
1712}
1713
1714#[cfg(feature = "gecko")]
1715pub mod view_timeline {
1716 pub use crate::properties::generated::shorthands::view_timeline::*;
1717
1718 use super::*;
1719 use crate::properties::longhands::{
1720 view_timeline_axis, view_timeline_inset, view_timeline_name,
1721 };
1722
1723 pub fn parse_value(
1724 context: &ParserContext,
1725 input: &mut Parser,
1726 ) -> Result<Longhands, ParseError> {
1727 let mut names = Vec::with_capacity(1);
1728 let mut axes = Vec::with_capacity(1);
1729 let mut insets = Vec::with_capacity(1);
1730 input.parse_comma_separated(|input| {
1731 let name = view_timeline_name::single_value::parse(context, input)?;
1732 let mut axis = None;
1733 let mut inset = None;
1734
1735 loop {
1736 try_parse_one!(
1737 context,
1738 input,
1739 axis,
1740 view_timeline_axis::single_value::parse
1741 );
1742 try_parse_one!(
1743 context,
1744 input,
1745 inset,
1746 view_timeline_inset::single_value::parse
1747 );
1748 break;
1749 }
1750
1751 names.push(name);
1752 axes.push(axis.unwrap_or_default());
1753 insets.push(inset.unwrap_or_default());
1754
1755 Ok(())
1756 })?;
1757
1758 Ok(expanded! {
1759 view_timeline_name: view_timeline_name::SpecifiedValue(names.into()),
1760 view_timeline_axis: view_timeline_axis::SpecifiedValue(axes.into()),
1761 view_timeline_inset: view_timeline_inset::SpecifiedValue(insets.into()),
1762 })
1763 }
1764
1765 impl<'a> ToCss for LonghandsToSerialize<'a> {
1766 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1767 where
1768 W: fmt::Write,
1769 {
1770 use itertools::izip;
1771 if self.view_timeline_name.0.len() != self.view_timeline_axis.0.len()
1772 || self.view_timeline_name.0.len() != self.view_timeline_inset.0.len()
1773 {
1774 return Ok(());
1775 }
1776 let mut first = true;
1777 for (name, axis, inset) in izip!(
1778 self.view_timeline_name.0.iter(),
1779 self.view_timeline_axis.0.iter(),
1780 self.view_timeline_inset.0.iter(),
1781 ) {
1782 if !first {
1783 dest.write_str(", ")?;
1784 }
1785 name.to_css(dest)?;
1786 if !axis.is_default() {
1787 dest.write_char(' ')?;
1788 axis.to_css(dest)?;
1789 }
1790 if !inset.is_auto() {
1791 dest.write_char(' ')?;
1792 inset.to_css(dest)?;
1793 }
1794 first = false;
1795 }
1796 Ok(())
1797 }
1798 }
1799}
1800
1801#[cfg(feature = "gecko")]
1802pub mod animation_range {
1803 pub use crate::properties::generated::shorthands::animation_range::*;
1804
1805 use super::*;
1806 use crate::properties::longhands::{animation_range_end, animation_range_start};
1807 use crate::values::specified::LengthPercentage;
1808
1809 pub fn parse_value(
1810 context: &ParserContext,
1811 input: &mut Parser,
1812 ) -> Result<Longhands, ParseError> {
1813 let mut starts = Vec::with_capacity(1);
1814 let mut ends = Vec::with_capacity(1);
1815 input.parse_comma_separated(|input| {
1816 let start = animation_range_start::single_value::parse(context, input)?;
1817 let end = input
1818 .try_parse(|i| animation_range_end::single_value::parse(context, i))
1819 .unwrap_or_else(|_| {
1820 use crate::values::generics::animation::AnimationRangeEnd;
1821 use crate::values::specified::animation::{
1822 AnimationRangeValue, TimelineRangeName,
1823 };
1824
1825 let name = if start.0.name.is_none() {
1829 TimelineRangeName::Normal
1830 } else {
1831 start.0.name
1832 };
1833 AnimationRangeEnd(AnimationRangeValue::new(
1834 name,
1835 LengthPercentage::hundred_percent(),
1836 ))
1837 });
1838
1839 starts.push(start);
1840 ends.push(end);
1841 Ok(())
1842 })?;
1843
1844 Ok(expanded! {
1845 animation_range_start: animation_range_start::SpecifiedValue(starts.into()),
1846 animation_range_end: animation_range_end::SpecifiedValue(ends.into()),
1847 })
1848 }
1849
1850 impl<'a> ToCss for LonghandsToSerialize<'a> {
1851 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1852 where
1853 W: fmt::Write,
1854 {
1855 use crate::values::specified::length::EqualsPercentage;
1856 let starts = &self.animation_range_start.0;
1857 let ends = &self.animation_range_end.0;
1858 if starts.len() != ends.len() {
1859 return Ok(());
1860 }
1861
1862 for (i, (start, end)) in std::iter::zip(starts.iter(), ends.iter()).enumerate() {
1863 if i != 0 {
1864 dest.write_str(", ")?;
1865 }
1866 start.to_css(dest)?;
1867 let can_omit_end = (start.0.name == end.0.name && end.0.lp.equals_percentage(1.0))
1868 || (start.0.name.is_none() && end.0.name.is_normal());
1869 if !can_omit_end {
1870 dest.write_char(' ')?;
1871 end.to_css(dest)?;
1872 }
1873 }
1874 Ok(())
1875 }
1876 }
1877}
1878
1879pub mod transition {
1880 pub use crate::properties::generated::shorthands::transition::*;
1881
1882 use super::*;
1883 use crate::properties::longhands::{
1884 transition_behavior, transition_delay, transition_duration, transition_property,
1885 transition_timing_function,
1886 };
1887 use crate::values::specified::TransitionProperty;
1888
1889 pub fn parse_value(
1890 context: &ParserContext,
1891 input: &mut Parser,
1892 ) -> Result<Longhands, ParseError> {
1893 struct SingleTransition {
1894 transition_property: transition_property::SingleSpecifiedValue,
1895 transition_duration: transition_duration::SingleSpecifiedValue,
1896 transition_timing_function: transition_timing_function::SingleSpecifiedValue,
1897 transition_delay: transition_delay::SingleSpecifiedValue,
1898 transition_behavior: transition_behavior::SingleSpecifiedValue,
1899 }
1900
1901 fn parse_one_transition(
1902 context: &ParserContext,
1903 input: &mut Parser,
1904 first: bool,
1905 ) -> Result<SingleTransition, ParseError> {
1906 let mut property = None;
1907 let mut duration = None;
1908 let mut timing_function = None;
1909 let mut delay = None;
1910 let mut behavior = None;
1911
1912 let mut parsed = 0;
1913 loop {
1914 parsed += 1;
1915
1916 try_parse_one!(
1917 context,
1918 input,
1919 duration,
1920 transition_duration::single_value::parse
1921 );
1922 try_parse_one!(
1923 context,
1924 input,
1925 timing_function,
1926 transition_timing_function::single_value::parse
1927 );
1928 try_parse_one!(context, input, delay, transition_delay::single_value::parse);
1929 try_parse_one!(
1930 context,
1931 input,
1932 behavior,
1933 transition_behavior::single_value::parse
1934 );
1935 if property.is_none() {
1936 if let Ok(value) = input.try_parse(|i| TransitionProperty::parse(context, i)) {
1937 property = Some(value);
1938 continue;
1939 }
1940
1941 if first && input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1942 property = Some(TransitionProperty::none());
1943 continue;
1944 }
1945 }
1946
1947 parsed -= 1;
1948 break;
1949 }
1950
1951 if parsed != 0 {
1952 Ok(SingleTransition {
1953 transition_property: property.unwrap_or_else(
1954 transition_property::single_value::get_initial_specified_value,
1955 ),
1956 transition_duration: duration.unwrap_or_else(
1957 transition_duration::single_value::get_initial_specified_value,
1958 ),
1959 transition_timing_function: timing_function.unwrap_or_else(
1960 transition_timing_function::single_value::get_initial_specified_value,
1961 ),
1962 transition_delay: delay.unwrap_or_else(
1963 transition_delay::single_value::get_initial_specified_value,
1964 ),
1965 transition_behavior: behavior.unwrap_or_else(
1966 transition_behavior::single_value::get_initial_specified_value,
1967 ),
1968 })
1969 } else {
1970 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1971 }
1972 }
1973
1974 let mut first = true;
1975 let mut has_transition_property_none = false;
1976 let results = input.parse_comma_separated(|i| {
1977 if has_transition_property_none {
1978 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1979 }
1980 let transition = parse_one_transition(context, i, first)?;
1981 first = false;
1982 has_transition_property_none = transition.transition_property.is_none();
1983 Ok(transition)
1984 })?;
1985
1986 let len = results.len();
1987 let mut property = Vec::with_capacity(len);
1988 let mut duration = Vec::with_capacity(len);
1989 let mut timing_function = Vec::with_capacity(len);
1990 let mut delay = Vec::with_capacity(len);
1991 let mut behavior = Vec::with_capacity(len);
1992 for result in results {
1993 property.push(result.transition_property);
1994 duration.push(result.transition_duration);
1995 timing_function.push(result.transition_timing_function);
1996 delay.push(result.transition_delay);
1997 behavior.push(result.transition_behavior);
1998 }
1999
2000 Ok(Longhands {
2001 transition_property: transition_property::SpecifiedValue(property.into()),
2002 transition_duration: transition_duration::SpecifiedValue(duration.into()),
2003 transition_timing_function: transition_timing_function::SpecifiedValue(
2004 timing_function.into(),
2005 ),
2006 transition_delay: transition_delay::SpecifiedValue(delay.into()),
2007 transition_behavior: transition_behavior::SpecifiedValue(behavior.into()),
2008 })
2009 }
2010
2011 impl<'a> ToCss for LonghandsToSerialize<'a> {
2012 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2013 where
2014 W: fmt::Write,
2015 {
2016 use crate::Zero;
2017 use style_traits::values::SequenceWriter;
2018
2019 let len = self.transition_property.0.len();
2020 debug_assert_ne!(
2021 len, 0,
2022 "We should always have at least one transition-property, even if none"
2023 );
2024 if self.transition_duration.0.len() != len {
2025 return Ok(());
2026 }
2027 if self.transition_delay.0.len() != len {
2028 return Ok(());
2029 }
2030 if self.transition_timing_function.0.len() != len {
2031 return Ok(());
2032 }
2033 if self.transition_behavior.0.len() != len {
2034 return Ok(());
2035 }
2036 for i in 0..len {
2037 if i != 0 {
2038 dest.write_str(", ")?;
2039 }
2040
2041 let has_duration = !self.transition_duration.0[i].is_zero();
2042 let has_timing = !self.transition_timing_function.0[i].is_ease();
2043 let has_delay = !self.transition_delay.0[i].is_zero();
2044 let has_behavior = !self.transition_behavior.0[i].is_normal();
2045 let has_any = has_duration || has_timing || has_delay || has_behavior;
2046
2047 let mut writer = SequenceWriter::new(dest, " ");
2048 if !self.transition_property.0[i].is_all() || !has_any {
2049 writer.item(&self.transition_property.0[i])?;
2050 }
2051 if has_duration || has_delay {
2052 writer.item(&self.transition_duration.0[i])?;
2053 }
2054 if has_timing {
2055 writer.item(&self.transition_timing_function.0[i])?;
2056 }
2057 if has_delay {
2058 writer.item(&self.transition_delay.0[i])?;
2059 }
2060 if has_behavior {
2061 writer.item(&self.transition_behavior.0[i])?;
2062 }
2063 }
2064 Ok(())
2065 }
2066 }
2067}
2068
2069pub mod outline {
2070 pub use crate::properties::generated::shorthands::outline::*;
2071
2072 use super::*;
2073 use crate::properties::longhands::{outline_color, outline_style, outline_width};
2074
2075 pub fn parse_value(
2076 context: &ParserContext,
2077 input: &mut Parser,
2078 ) -> Result<Longhands, ParseError> {
2079 let _unused = context;
2080 let mut color = None;
2081 let mut style = None;
2082 let mut width = None;
2083 let mut parsed = 0;
2084 loop {
2085 parsed += 1;
2086 try_parse_one!(context, input, color, specified::Color::parse);
2087 try_parse_one!(context, input, style, outline_style::parse);
2088 try_parse_one!(context, input, width, outline_width::parse);
2089 parsed -= 1;
2090 break;
2091 }
2092 if parsed == 0 {
2093 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
2094 }
2095 Ok(expanded! {
2096 outline_color: unwrap_or_initial!(outline_color, color),
2097 outline_style: unwrap_or_initial!(outline_style, style),
2098 outline_width: unwrap_or_initial!(outline_width, width),
2099 })
2100 }
2101
2102 impl<'a> ToCss for LonghandsToSerialize<'a> {
2103 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2104 where
2105 W: fmt::Write,
2106 {
2107 let mut writer = SequenceWriter::new(dest, " ");
2108 if *self.outline_color != outline_color::get_initial_specified_value() {
2109 writer.item(self.outline_color)?;
2110 }
2111 if *self.outline_style != outline_style::get_initial_specified_value() {
2112 writer.item(self.outline_style)?;
2113 }
2114 if *self.outline_width != outline_width::get_initial_specified_value() {
2115 writer.item(self.outline_width)?;
2116 }
2117 if !writer.has_written() {
2118 self.outline_style.to_css(dest)?;
2119 }
2120 Ok(())
2121 }
2122 }
2123}
2124
2125pub mod background_position {
2126 pub use crate::properties::generated::shorthands::background_position::*;
2127
2128 use super::*;
2129 use crate::properties::longhands::{background_position_x, background_position_y};
2130 use crate::values::specified::AllowQuirks;
2131 use crate::values::specified::position::Position;
2132
2133 pub fn parse_value(
2134 context: &ParserContext,
2135 input: &mut Parser,
2136 ) -> Result<Longhands, ParseError> {
2137 let mut position_x = Vec::with_capacity(1);
2138 let mut position_y = Vec::with_capacity(1);
2139 let mut any = false;
2140
2141 input.parse_comma_separated(|input| {
2142 let value = Position::parse_three_value_quirky(context, input, AllowQuirks::Yes)?;
2143 position_x.push(value.horizontal);
2144 position_y.push(value.vertical);
2145 any = true;
2146 Ok(())
2147 })?;
2148 if !any {
2149 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
2150 }
2151
2152 Ok(expanded! {
2153 background_position_x: background_position_x::SpecifiedValue(position_x.into()),
2154 background_position_y: background_position_y::SpecifiedValue(position_y.into()),
2155 })
2156 }
2157
2158 impl<'a> ToCss for LonghandsToSerialize<'a> {
2159 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2160 where
2161 W: fmt::Write,
2162 {
2163 let len = self.background_position_x.0.len();
2164 if len == 0 || len != self.background_position_y.0.len() {
2165 return Ok(());
2166 }
2167 for i in 0..len {
2168 Position {
2169 horizontal: self.background_position_x.0[i].clone(),
2170 vertical: self.background_position_y.0[i].clone(),
2171 }
2172 .to_css(dest)?;
2173
2174 if i < len - 1 {
2175 dest.write_str(", ")?;
2176 }
2177 }
2178 Ok(())
2179 }
2180 }
2181}
2182
2183pub mod background {
2184 pub use crate::properties::generated::shorthands::background::*;
2185
2186 use super::*;
2187 use crate::properties::longhands::background_clip;
2188 use crate::properties::longhands::background_clip::single_value::computed_value::T as Clip;
2189 use crate::properties::longhands::background_origin::single_value::computed_value::T as Origin;
2190 use crate::properties::longhands::{
2191 background_attachment, background_color, background_image, background_origin,
2192 background_size,
2193 };
2194 use crate::properties::longhands::{
2195 background_position_x, background_position_y, background_repeat,
2196 };
2197 use crate::values::specified::{AllowQuirks, Color, Position, PositionComponent};
2198
2199 pub fn parse_value(
2200 context: &ParserContext,
2201 input: &mut Parser,
2202 ) -> Result<Longhands, ParseError> {
2203 let mut background_color = None;
2204
2205 let mut background_image = Vec::with_capacity(1);
2206 let mut background_position_x = Vec::with_capacity(1);
2207 let mut background_position_y = Vec::with_capacity(1);
2208 let mut background_repeat = Vec::with_capacity(1);
2209 let mut background_size = Vec::with_capacity(1);
2210 let mut background_attachment = Vec::with_capacity(1);
2211 let mut background_origin = Vec::with_capacity(1);
2212 let mut background_clip = Vec::with_capacity(1);
2213 input.parse_comma_separated(|input| {
2214 if background_color.is_some() {
2215 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
2216 }
2217
2218 let mut image = None;
2219 let mut position = None;
2220 let mut repeat = None;
2221 let mut size = None;
2222 let mut attachment = None;
2223 let mut origin = None;
2224 let mut clip = None;
2225 let mut parsed = 0;
2226 loop {
2227 parsed += 1;
2228 try_parse_one!(context, input, background_color, Color::parse);
2229 if position.is_none()
2230 && let Ok(value) = input.try_parse(|input| {
2231 Position::parse_three_value_quirky(context, input, AllowQuirks::No)
2232 })
2233 {
2234 position = Some(value);
2235
2236 size = input
2237 .try_parse(|input| {
2238 input.expect_delim('/')?;
2239 background_size::single_value::parse(context, input)
2240 })
2241 .ok();
2242
2243 continue;
2244 }
2245 try_parse_one!(context, input, image, background_image::single_value::parse);
2246 try_parse_one!(
2247 context,
2248 input,
2249 repeat,
2250 background_repeat::single_value::parse
2251 );
2252 try_parse_one!(
2253 context,
2254 input,
2255 attachment,
2256 background_attachment::single_value::parse
2257 );
2258 try_parse_one!(
2259 context,
2260 input,
2261 origin,
2262 background_origin::single_value::parse
2263 );
2264 try_parse_one!(context, input, clip, background_clip::single_value::parse);
2265 parsed -= 1;
2266 break;
2267 }
2268 if parsed == 0 {
2269 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
2270 }
2271 if clip.is_none()
2272 && let Some(origin) = origin
2273 {
2274 clip = Some(background_clip::single_value::SpecifiedValue::from(origin));
2275 }
2276 if let Some(position) = position {
2277 background_position_x.push(position.horizontal);
2278 background_position_y.push(position.vertical);
2279 } else {
2280 background_position_x.push(PositionComponent::zero());
2281 background_position_y.push(PositionComponent::zero());
2282 }
2283 if let Some(bg_image) = image {
2284 background_image.push(bg_image);
2285 } else {
2286 background_image
2287 .push(background_image::single_value::get_initial_specified_value());
2288 }
2289 if let Some(bg_repeat) = repeat {
2290 background_repeat.push(bg_repeat);
2291 } else {
2292 background_repeat
2293 .push(background_repeat::single_value::get_initial_specified_value());
2294 }
2295 if let Some(bg_size) = size {
2296 background_size.push(bg_size);
2297 } else {
2298 background_size.push(background_size::single_value::get_initial_specified_value());
2299 }
2300 if let Some(bg_attachment) = attachment {
2301 background_attachment.push(bg_attachment);
2302 } else {
2303 background_attachment
2304 .push(background_attachment::single_value::get_initial_specified_value());
2305 }
2306 if let Some(bg_origin) = origin {
2307 background_origin.push(bg_origin);
2308 } else {
2309 background_origin
2310 .push(background_origin::single_value::get_initial_specified_value());
2311 }
2312 if let Some(bg_clip) = clip {
2313 background_clip.push(bg_clip);
2314 } else {
2315 background_clip.push(background_clip::single_value::get_initial_specified_value());
2316 }
2317 Ok(())
2318 })?;
2319
2320 Ok(expanded! {
2321 background_color: background_color.unwrap_or(Color::transparent()),
2322 background_image: background_image::SpecifiedValue(background_image.into()),
2323 background_position_x: background_position_x::SpecifiedValue(background_position_x.into()),
2324 background_position_y: background_position_y::SpecifiedValue(background_position_y.into()),
2325 background_repeat: background_repeat::SpecifiedValue(background_repeat.into()),
2326 background_size: background_size::SpecifiedValue(background_size.into()),
2327 background_attachment: background_attachment::SpecifiedValue(background_attachment.into()),
2328 background_origin: background_origin::SpecifiedValue(background_origin.into()),
2329 background_clip: background_clip::SpecifiedValue(background_clip.into()),
2330 })
2331 }
2332
2333 impl<'a> ToCss for LonghandsToSerialize<'a> {
2334 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2335 where
2336 W: fmt::Write,
2337 {
2338 let len = self.background_image.0.len();
2339 if len == 0 {
2340 return Ok(());
2341 }
2342 if len != self.background_image.0.len() {
2343 return Ok(());
2344 }
2345 if len != self.background_position_x.0.len() {
2346 return Ok(());
2347 }
2348 if len != self.background_position_y.0.len() {
2349 return Ok(());
2350 }
2351 if len != self.background_size.0.len() {
2352 return Ok(());
2353 }
2354 if len != self.background_repeat.0.len() {
2355 return Ok(());
2356 }
2357 if len != self.background_origin.0.len() {
2358 return Ok(());
2359 }
2360 if len != self.background_clip.0.len() {
2361 return Ok(());
2362 }
2363 if len != self.background_attachment.0.len() {
2364 return Ok(());
2365 }
2366
2367 for i in 0..len {
2368 let image = &self.background_image.0[i];
2369 let position_x = &self.background_position_x.0[i];
2370 let position_y = &self.background_position_y.0[i];
2371 let repeat = &self.background_repeat.0[i];
2372 let size = &self.background_size.0[i];
2373 let attachment = &self.background_attachment.0[i];
2374 let origin = &self.background_origin.0[i];
2375 let clip = &self.background_clip.0[i];
2376
2377 if i != 0 {
2378 dest.write_str(", ")?;
2379 }
2380
2381 let mut writer = SequenceWriter::new(dest, " ");
2382 if *image != background_image::single_value::get_initial_specified_value() {
2383 writer.item(image)?;
2384 }
2385
2386 if *position_x != PositionComponent::zero()
2387 || *position_y != PositionComponent::zero()
2388 || *size != background_size::single_value::get_initial_specified_value()
2389 {
2390 writer.write_item(|dest| {
2391 Position {
2392 horizontal: position_x.clone(),
2393 vertical: position_y.clone(),
2394 }
2395 .to_css(dest)?;
2396 if *size != background_size::single_value::get_initial_specified_value() {
2397 dest.write_str(" / ")?;
2398 size.to_css(dest)?;
2399 }
2400 Ok(())
2401 })?;
2402 }
2403 if *repeat != background_repeat::single_value::get_initial_specified_value() {
2404 writer.item(repeat)?;
2405 }
2406 if *attachment != background_attachment::single_value::get_initial_specified_value()
2407 {
2408 writer.item(attachment)?;
2409 }
2410
2411 if *origin != Origin::PaddingBox || *clip != Clip::BorderBox {
2412 writer.item(origin)?;
2413 if *clip != From::from(*origin) {
2414 writer.item(clip)?;
2415 }
2416 }
2417
2418 if i == len - 1
2419 && *self.background_color != background_color::get_initial_specified_value()
2420 {
2421 writer.item(self.background_color)?;
2422 }
2423
2424 if !writer.has_written() {
2425 image.to_css(dest)?;
2426 }
2427 }
2428
2429 Ok(())
2430 }
2431 }
2432}
2433
2434pub mod font {
2435 pub use crate::properties::generated::shorthands::font::*;
2436
2437 use super::*;
2438 use crate::properties::longhands::{
2439 font_family, font_feature_settings, font_kerning, font_language_override,
2440 font_optical_sizing, font_size, font_size_adjust, font_style, font_variant_alternates,
2441 font_variant_caps, font_variant_east_asian, font_variant_emoji, font_variant_ligatures,
2442 font_variant_numeric, font_variant_position, font_variation_settings, font_weight,
2443 font_width,
2444 };
2445 use crate::values::specified::font::SystemFont;
2446 use crate::values::specified::font::{
2447 FontFamily, FontSize, FontStyle, FontWeight, FontWidth, FontWidthKeyword, LineHeight,
2448 };
2449
2450 pub fn parse_value(
2451 context: &ParserContext,
2452 input: &mut Parser,
2453 ) -> Result<Longhands, ParseError> {
2454 let mut nb_normals = 0;
2455 let mut style = None;
2456 let mut variant_caps = None;
2457 let mut weight = None;
2458 let mut width = None;
2459 if let Ok(sys) = input.try_parse(|i| SystemFont::parse(context, i)) {
2460 return Ok(Longhands {
2461 font_family: font_family::SpecifiedValue::system_font(sys),
2462 font_size: font_size::SpecifiedValue::system_font(sys),
2463 font_style: font_style::SpecifiedValue::system_font(sys),
2464 font_width: font_width::SpecifiedValue::system_font(sys),
2465 font_weight: font_weight::SpecifiedValue::system_font(sys),
2466 line_height: LineHeight::normal(),
2467 font_kerning: font_kerning::get_initial_specified_value(),
2468 font_language_override: font_language_override::get_initial_specified_value(),
2469 font_size_adjust: font_size_adjust::get_initial_specified_value(),
2470 font_variant_alternates: font_variant_alternates::get_initial_specified_value(),
2471 font_variant_east_asian: font_variant_east_asian::get_initial_specified_value(),
2472 font_variant_emoji: font_variant_emoji::get_initial_specified_value(),
2473 font_variant_ligatures: font_variant_ligatures::get_initial_specified_value(),
2474 font_variant_numeric: font_variant_numeric::get_initial_specified_value(),
2475 font_variant_position: font_variant_position::get_initial_specified_value(),
2476 font_feature_settings: font_feature_settings::get_initial_specified_value(),
2477 font_optical_sizing: font_optical_sizing::get_initial_specified_value(),
2478 font_variant_caps: font_variant_caps::get_initial_specified_value(),
2479 font_variation_settings: font_variation_settings::get_initial_specified_value(),
2480 });
2481 }
2482
2483 let size;
2484 loop {
2485 if input
2486 .try_parse(|input| input.expect_ident_matching("normal"))
2487 .is_ok()
2488 {
2489 nb_normals += 1;
2490 continue;
2491 }
2492 try_parse_one!(context, input, style, font_style::parse);
2493 try_parse_one!(context, input, weight, font_weight::parse);
2494 if variant_caps.is_none()
2495 && input
2496 .try_parse(|input| input.expect_ident_matching("small-caps"))
2497 .is_ok()
2498 {
2499 variant_caps = Some(font_variant_caps::SpecifiedValue::SmallCaps);
2500 continue;
2501 }
2502 try_parse_one!(input, width, FontWidthKeyword::parse);
2503 size = FontSize::parse(context, input)?;
2504 break;
2505 }
2506
2507 let line_height = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
2508 Some(LineHeight::parse(context, input)?)
2509 } else {
2510 None
2511 };
2512
2513 #[inline]
2514 fn count<T>(opt: &Option<T>) -> u8 {
2515 if opt.is_some() { 1 } else { 0 }
2516 }
2517
2518 if (count(&style) + count(&weight) + count(&variant_caps) + count(&width) + nb_normals) > 4
2519 {
2520 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
2521 }
2522
2523 let family = FontFamily::parse(context, input)?;
2524 let width = width.map(FontWidth::Keyword);
2525 Ok(Longhands {
2526 font_style: unwrap_or_initial!(font_style, style),
2527 font_weight: unwrap_or_initial!(font_weight, weight),
2528 font_width: unwrap_or_initial!(font_width, width),
2529 font_variant_caps: unwrap_or_initial!(font_variant_caps, variant_caps),
2530 font_size: size,
2531 line_height: line_height.unwrap_or(LineHeight::normal()),
2532 font_family: family,
2533 font_optical_sizing: font_optical_sizing::get_initial_specified_value(),
2534 font_variation_settings: font_variation_settings::get_initial_specified_value(),
2535 font_kerning: font_kerning::get_initial_specified_value(),
2536 font_language_override: font_language_override::get_initial_specified_value(),
2537 font_size_adjust: font_size_adjust::get_initial_specified_value(),
2538 font_variant_alternates: font_variant_alternates::get_initial_specified_value(),
2539 font_variant_east_asian: font_variant_east_asian::get_initial_specified_value(),
2540 font_variant_emoji: font_variant_emoji::get_initial_specified_value(),
2541 font_variant_ligatures: font_variant_ligatures::get_initial_specified_value(),
2542 font_variant_numeric: font_variant_numeric::get_initial_specified_value(),
2543 font_variant_position: font_variant_position::get_initial_specified_value(),
2544 font_feature_settings: font_feature_settings::get_initial_specified_value(),
2545 })
2546 }
2547
2548 enum CheckSystemResult {
2549 AllSystem(SystemFont),
2550 SomeSystem,
2551 None,
2552 }
2553
2554 impl<'a> ToCss for LonghandsToSerialize<'a> {
2555 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2556 where
2557 W: fmt::Write,
2558 {
2559 match self.check_system() {
2560 CheckSystemResult::AllSystem(sys) => return sys.to_css(dest),
2561 CheckSystemResult::SomeSystem => return Ok(()),
2562 CheckSystemResult::None => {},
2563 }
2564
2565 #[cfg(feature = "gecko")]
2566 if self.font_optical_sizing != &font_optical_sizing::get_initial_specified_value() {
2567 return Ok(());
2568 }
2569 #[cfg(feature = "servo")]
2570 if let Some(v) = self.font_optical_sizing {
2571 if v != &font_optical_sizing::get_initial_specified_value() {
2572 return Ok(());
2573 }
2574 }
2575 #[cfg(feature = "gecko")]
2576 if self.font_variation_settings
2577 != &font_variation_settings::get_initial_specified_value()
2578 {
2579 return Ok(());
2580 }
2581 #[cfg(feature = "servo")]
2582 if let Some(v) = self.font_variation_settings {
2583 if v != &font_variation_settings::get_initial_specified_value() {
2584 return Ok(());
2585 }
2586 }
2587 #[cfg(feature = "gecko")]
2588 if self.font_variant_emoji != &font_variant_emoji::get_initial_specified_value() {
2589 return Ok(());
2590 }
2591 #[cfg(feature = "servo")]
2592 if let Some(v) = self.font_variant_emoji {
2593 if v != &font_variant_emoji::get_initial_specified_value() {
2594 return Ok(());
2595 }
2596 }
2597
2598 if self.font_kerning != &font_kerning::get_initial_specified_value() {
2599 return Ok(());
2600 }
2601 if self.font_language_override != &font_language_override::get_initial_specified_value()
2602 {
2603 return Ok(());
2604 }
2605 #[cfg(feature = "gecko")]
2606 if self.font_size_adjust != &font_size_adjust::get_initial_specified_value() {
2607 return Ok(());
2608 }
2609 #[cfg(feature = "servo")]
2610 if let Some(v) = self.font_size_adjust {
2611 if v != &font_size_adjust::get_initial_specified_value() {
2612 return Ok(());
2613 }
2614 }
2615 if self.font_variant_alternates
2616 != &font_variant_alternates::get_initial_specified_value()
2617 {
2618 return Ok(());
2619 }
2620 if self.font_variant_east_asian
2621 != &font_variant_east_asian::get_initial_specified_value()
2622 {
2623 return Ok(());
2624 }
2625 if self.font_variant_ligatures != &font_variant_ligatures::get_initial_specified_value()
2626 {
2627 return Ok(());
2628 }
2629 if self.font_variant_numeric != &font_variant_numeric::get_initial_specified_value() {
2630 return Ok(());
2631 }
2632 if self.font_variant_position != &font_variant_position::get_initial_specified_value() {
2633 return Ok(());
2634 }
2635 if self.font_feature_settings != &font_feature_settings::get_initial_specified_value() {
2636 return Ok(());
2637 }
2638
2639 let font_width = match self.font_width {
2640 FontWidth::Keyword(kw) => *kw,
2641 FontWidth::Width(percentage) => {
2642 let computed = match percentage.compute() {
2643 Some(v) => v,
2644 None => return Ok(()),
2645 };
2646 match FontWidthKeyword::from_percentage(computed.0) {
2647 Some(kw) => kw,
2648 None => return Ok(()),
2649 }
2650 },
2651 FontWidth::System(..) => return Ok(()),
2652 };
2653
2654 if self.font_variant_caps != &font_variant_caps::get_initial_specified_value()
2655 && *self.font_variant_caps != font_variant_caps::SpecifiedValue::SmallCaps
2656 {
2657 return Ok(());
2658 }
2659
2660 if self.font_style != &font_style::get_initial_specified_value() {
2661 self.font_style.to_css(dest)?;
2662 dest.write_char(' ')?;
2663 }
2664 if self.font_variant_caps != &font_variant_caps::get_initial_specified_value() {
2665 self.font_variant_caps.to_css(dest)?;
2666 dest.write_char(' ')?;
2667 }
2668
2669 if self.font_weight != &FontWeight::normal()
2670 && self.font_weight != &FontWeight::from_gecko_keyword(400)
2671 {
2672 self.font_weight.to_css(dest)?;
2673 dest.write_char(' ')?;
2674 }
2675
2676 if font_width != FontWidthKeyword::Normal {
2677 font_width.to_css(dest)?;
2678 dest.write_char(' ')?;
2679 }
2680
2681 self.font_size.to_css(dest)?;
2682
2683 if *self.line_height != LineHeight::normal() {
2684 dest.write_str(" / ")?;
2685 self.line_height.to_css(dest)?;
2686 }
2687
2688 dest.write_char(' ')?;
2689 self.font_family.to_css(dest)?;
2690
2691 Ok(())
2692 }
2693 }
2694
2695 impl<'a> LonghandsToSerialize<'a> {
2696 fn check_system(&self) -> CheckSystemResult {
2697 let mut sys = None;
2698 let mut all = true;
2699
2700 macro_rules! check {
2701 ($v:expr) => {
2702 match $v.get_system() {
2703 Some(s) => {
2704 debug_assert!(sys.is_none() || s == sys.unwrap());
2705 sys = Some(s);
2706 },
2707 None => {
2708 all = false;
2709 },
2710 }
2711 };
2712 ($e:expr, $($es:expr),+) => { check!($e); check!($($es),*); };
2713 }
2714
2715 check!(
2716 self.font_family,
2717 self.font_size,
2718 self.font_style,
2719 self.font_width,
2720 self.font_weight
2721 );
2722
2723 if self.line_height != &LineHeight::normal() {
2724 all = false
2725 }
2726 if all {
2727 CheckSystemResult::AllSystem(sys.unwrap())
2728 } else if sys.is_some() {
2729 CheckSystemResult::SomeSystem
2730 } else {
2731 CheckSystemResult::None
2732 }
2733 }
2734 }
2735
2736 impl SpecifiedValueInfo for Longhands {
2737 const SUPPORTED_TYPES: u8 = FontStyle::SUPPORTED_TYPES
2738 | FontWeight::SUPPORTED_TYPES
2739 | FontWidth::SUPPORTED_TYPES
2740 | font_variant_caps::SpecifiedValue::SUPPORTED_TYPES
2741 | FontSize::SUPPORTED_TYPES
2742 | FontFamily::SUPPORTED_TYPES;
2743
2744 fn collect_completion_keywords(f: KeywordsCollectFn) {
2745 FontStyle::collect_completion_keywords(f);
2746 FontWeight::collect_completion_keywords(f);
2747 FontWidth::collect_completion_keywords(f);
2748 font_variant_caps::SpecifiedValue::collect_completion_keywords(f);
2749 FontSize::collect_completion_keywords(f);
2750 FontFamily::collect_completion_keywords(f);
2751 SystemFont::collect_completion_keywords(f);
2752 }
2753 }
2754}
2755
2756pub mod font_variant {
2757 pub use crate::properties::generated::shorthands::font_variant::*;
2758
2759 use super::*;
2760 use crate::properties::longhands::{
2761 font_variant_alternates, font_variant_caps, font_variant_east_asian, font_variant_emoji,
2762 font_variant_ligatures, font_variant_numeric, font_variant_position,
2763 };
2764 use crate::properties::{LonghandId, NonCustomPropertyId};
2765 use crate::values::specified::FontVariantLigatures;
2766
2767 pub fn parse_value(
2768 context: &ParserContext,
2769 input: &mut Parser,
2770 ) -> Result<Longhands, ParseError> {
2771 let mut ligatures = None;
2772 let mut caps = None;
2773 let mut alternates = None;
2774 let mut numeric = None;
2775 let mut east_asian = None;
2776 let mut position = None;
2777 let mut emoji = None;
2778 let emoji_enabled = NonCustomPropertyId::from(LonghandId::FontVariantEmoji)
2779 .allowed_in_ignoring_rule_type(context);
2780
2781 if input
2782 .try_parse(|input| input.expect_ident_matching("normal"))
2783 .is_ok()
2784 {
2785 } else if input
2786 .try_parse(|input| input.expect_ident_matching("none"))
2787 .is_ok()
2788 {
2789 ligatures = Some(FontVariantLigatures::NONE);
2790 } else {
2791 let mut parsed = 0;
2792 loop {
2793 parsed += 1;
2794 if input
2795 .try_parse(|input| input.expect_ident_matching("normal"))
2796 .is_ok()
2797 || input
2798 .try_parse(|input| input.expect_ident_matching("none"))
2799 .is_ok()
2800 {
2801 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
2802 }
2803 try_parse_one!(context, input, ligatures, font_variant_ligatures::parse);
2804 try_parse_one!(context, input, caps, font_variant_caps::parse);
2805 try_parse_one!(context, input, alternates, font_variant_alternates::parse);
2806 try_parse_one!(context, input, numeric, font_variant_numeric::parse);
2807 try_parse_one!(context, input, east_asian, font_variant_east_asian::parse);
2808 try_parse_one!(context, input, position, font_variant_position::parse);
2809 if emoji_enabled {
2810 try_parse_one!(context, input, emoji, font_variant_emoji::parse);
2811 }
2812 parsed -= 1;
2813 break;
2814 }
2815
2816 if parsed == 0 {
2817 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
2818 }
2819 }
2820
2821 Ok(expanded! {
2822 font_variant_ligatures: unwrap_or_initial!(font_variant_ligatures, ligatures),
2823 font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),
2824 font_variant_alternates: unwrap_or_initial!(font_variant_alternates, alternates),
2825 font_variant_numeric: unwrap_or_initial!(font_variant_numeric, numeric),
2826 font_variant_east_asian: unwrap_or_initial!(font_variant_east_asian, east_asian),
2827 font_variant_position: unwrap_or_initial!(font_variant_position, position),
2828 font_variant_emoji: unwrap_or_initial!(font_variant_emoji, emoji),
2829 })
2830 }
2831
2832 impl<'a> ToCss for LonghandsToSerialize<'a> {
2833 #[allow(unused_assignments)]
2834 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2835 where
2836 W: fmt::Write,
2837 {
2838 let has_none_ligatures = self.font_variant_ligatures == &FontVariantLigatures::NONE;
2839
2840 #[cfg(feature = "gecko")]
2843 let emoji = Some(self.font_variant_emoji);
2844 #[cfg(feature = "servo")]
2845 let emoji = self.font_variant_emoji;
2846
2847 let total_subprops = 6 + emoji.is_some() as usize;
2848 let mut nb_normals = 0;
2849 macro_rules! count_normal {
2850 ($e: expr, $p: ident) => {
2851 if *$e == $p::get_initial_specified_value() {
2852 nb_normals += 1;
2853 }
2854 };
2855 ($v: ident) => {
2856 count_normal!(self.$v, $v);
2857 };
2858 }
2859 count_normal!(font_variant_ligatures);
2860 count_normal!(font_variant_caps);
2861 count_normal!(font_variant_alternates);
2862 count_normal!(font_variant_numeric);
2863 count_normal!(font_variant_east_asian);
2864 count_normal!(font_variant_position);
2865 if let Some(emoji) = emoji {
2866 count_normal!(emoji, font_variant_emoji);
2867 }
2868
2869 if nb_normals == total_subprops {
2870 return dest.write_str("normal");
2871 }
2872 if has_none_ligatures {
2873 if nb_normals == total_subprops - 1 {
2874 dest.write_str("none")?;
2875 }
2876 return Ok(());
2877 }
2878
2879 let mut writer = SequenceWriter::new(dest, " ");
2880 macro_rules! write {
2881 ($e: expr, $p: ident) => {
2882 if *$e != $p::get_initial_specified_value() {
2883 writer.item($e)?;
2884 }
2885 };
2886 ($v: ident) => {
2887 write!(self.$v, $v);
2888 };
2889 }
2890
2891 write!(font_variant_ligatures);
2892 write!(font_variant_caps);
2893 write!(font_variant_alternates);
2894 write!(font_variant_numeric);
2895 write!(font_variant_east_asian);
2896 write!(font_variant_position);
2897 if let Some(emoji) = emoji {
2898 write!(emoji, font_variant_emoji);
2899 }
2900 Ok(())
2901 }
2902 }
2903}
2904
2905pub mod font_synthesis {
2906 pub use crate::properties::generated::shorthands::font_synthesis::*;
2907
2908 use super::*;
2909 use crate::values::specified::{FontSynthesis, FontSynthesisStyle};
2910
2911 pub fn parse_value(
2912 _context: &ParserContext,
2913 input: &mut Parser,
2914 ) -> Result<Longhands, ParseError> {
2915 let mut weight = FontSynthesis::None;
2916 let mut style = FontSynthesisStyle::None;
2917 let mut small_caps = FontSynthesis::None;
2918 let mut position = FontSynthesis::None;
2919
2920 if input
2921 .try_parse(|input| input.expect_ident_matching("none"))
2922 .is_err()
2923 {
2924 let mut has_custom_value = false;
2925 while !input.is_exhausted() {
2926 try_match_ident_ignore_ascii_case! { input,
2927 "weight" if weight == FontSynthesis::None => {
2928 has_custom_value = true;
2929 weight = FontSynthesis::Auto;
2930 continue;
2931 },
2932 "style" if style == FontSynthesisStyle::None => {
2933 has_custom_value = true;
2934 style = FontSynthesisStyle::Auto;
2935 continue;
2936 },
2937 "small-caps" if small_caps == FontSynthesis::None => {
2938 has_custom_value = true;
2939 small_caps = FontSynthesis::Auto;
2940 continue;
2941 },
2942 "position" if position == FontSynthesis::None => {
2943 has_custom_value = true;
2944 position = FontSynthesis::Auto;
2945 continue;
2946 },
2947 "oblique-only" if style == FontSynthesisStyle::None => {
2948 has_custom_value = true;
2949 style = FontSynthesisStyle::ObliqueOnly;
2950 continue;
2951 },
2952 }
2953 }
2954 if !has_custom_value {
2955 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
2956 }
2957 }
2958
2959 Ok(expanded! {
2960 font_synthesis_weight: weight,
2961 font_synthesis_style: style,
2962 font_synthesis_small_caps: small_caps,
2963 font_synthesis_position: position,
2964 })
2965 }
2966
2967 impl<'a> ToCss for LonghandsToSerialize<'a> {
2968 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2969 where
2970 W: fmt::Write,
2971 {
2972 let mut writer = SequenceWriter::new(dest, " ");
2973 if self.font_synthesis_weight == &FontSynthesis::Auto {
2974 writer.raw_item("weight")?;
2975 }
2976 if self.font_synthesis_style != &FontSynthesisStyle::None {
2977 if self.font_synthesis_style == &FontSynthesisStyle::Auto {
2978 writer.raw_item("style")?;
2979 } else {
2980 writer.raw_item("oblique-only")?;
2981 }
2982 }
2983 if self.font_synthesis_small_caps == &FontSynthesis::Auto {
2984 writer.raw_item("small-caps")?;
2985 }
2986 if self.font_synthesis_position == &FontSynthesis::Auto {
2987 writer.raw_item("position")?;
2988 }
2989 if !writer.has_written() {
2990 writer.raw_item("none")?;
2991 }
2992 Ok(())
2993 }
2994 }
2995
2996 impl SpecifiedValueInfo for Longhands {
2999 fn collect_completion_keywords(f: KeywordsCollectFn) {
3000 f(&[
3001 "none",
3002 "oblique-only",
3003 "small-caps",
3004 "position",
3005 "style",
3006 "weight",
3007 ]);
3008 }
3009 }
3010}
3011
3012pub mod text_box {
3013 pub use crate::properties::generated::shorthands::text_box::*;
3014
3015 use super::*;
3016 use crate::values::specified::{TextBoxEdge, TextBoxTrim};
3017
3018 pub fn parse_value(
3019 context: &ParserContext,
3020 input: &mut Parser,
3021 ) -> Result<Longhands, ParseError> {
3022 let mut trim = None;
3023 let mut edge = None;
3024
3025 if input
3026 .try_parse(|input| input.expect_ident_matching("normal"))
3027 .is_ok()
3028 {
3029 return Ok(Longhands {
3030 text_box_trim: TextBoxTrim::NONE,
3031 text_box_edge: TextBoxEdge::Auto,
3032 });
3033 }
3034
3035 loop {
3036 try_parse_one!(context, input, trim, TextBoxTrim::parse);
3037 try_parse_one!(context, input, edge, TextBoxEdge::parse);
3038 break;
3039 }
3040
3041 if trim.is_none() && edge.is_none() {
3042 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
3043 }
3044
3045 Ok(Longhands {
3050 text_box_trim: trim.unwrap_or(TextBoxTrim::TRIM_BOTH),
3051 text_box_edge: edge.unwrap_or(TextBoxEdge::Auto),
3052 })
3053 }
3054
3055 impl<'a> ToCss for LonghandsToSerialize<'a> {
3056 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3057 where
3058 W: fmt::Write,
3059 {
3060 if *self.text_box_trim == TextBoxTrim::NONE && *self.text_box_edge == TextBoxEdge::Auto
3061 {
3062 return dest.write_str("normal");
3063 }
3064
3065 let mut writer = SequenceWriter::new(dest, " ");
3066 if *self.text_box_trim != specified::TextBoxTrim::TRIM_BOTH {
3067 writer.item(self.text_box_trim)?;
3068 }
3069 if *self.text_box_edge != specified::TextBoxEdge::Auto {
3070 writer.item(self.text_box_edge)?;
3071 }
3072 if !writer.has_written() {
3073 self.text_box_trim.to_css(dest)?;
3074 }
3075 Ok(())
3076 }
3077 }
3078}
3079
3080pub mod text_emphasis {
3081 pub use crate::properties::generated::shorthands::text_emphasis::*;
3082
3083 use super::*;
3084 use crate::properties::longhands::{text_emphasis_color, text_emphasis_style};
3085
3086 pub fn parse_value(
3087 context: &ParserContext,
3088 input: &mut Parser,
3089 ) -> Result<Longhands, ParseError> {
3090 let mut color = None;
3091 let mut style = None;
3092 let mut parsed = 0;
3093 loop {
3094 parsed += 1;
3095 try_parse_one!(context, input, color, text_emphasis_color::parse);
3096 try_parse_one!(context, input, style, text_emphasis_style::parse);
3097 parsed -= 1;
3098 break;
3099 }
3100 if parsed == 0 {
3101 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
3102 }
3103 Ok(expanded! {
3104 text_emphasis_color: unwrap_or_initial!(text_emphasis_color, color),
3105 text_emphasis_style: unwrap_or_initial!(text_emphasis_style, style),
3106 })
3107 }
3108}
3109
3110pub mod text_decoration {
3111 pub use crate::properties::generated::shorthands::text_decoration::*;
3112
3113 use super::*;
3114 use crate::properties::longhands::text_decoration_thickness;
3115 use crate::properties::longhands::{
3116 text_decoration_color, text_decoration_line, text_decoration_style,
3117 };
3118
3119 pub fn parse_value(
3120 context: &ParserContext,
3121 input: &mut Parser,
3122 ) -> Result<Longhands, ParseError> {
3123 let mut line = None;
3124 let mut style = None;
3125 let mut color = None;
3126 let mut thickness = None;
3127
3128 let mut parsed = 0;
3129 loop {
3130 parsed += 1;
3131 try_parse_one!(context, input, line, text_decoration_line::parse);
3132 try_parse_one!(context, input, style, text_decoration_style::parse);
3133 try_parse_one!(context, input, color, text_decoration_color::parse);
3134 try_parse_one!(context, input, thickness, text_decoration_thickness::parse);
3135 parsed -= 1;
3136 break;
3137 }
3138
3139 if parsed == 0 {
3140 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
3141 }
3142
3143 Ok(expanded! {
3144 text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
3145 text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
3146 text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
3147 text_decoration_thickness: unwrap_or_initial!(text_decoration_thickness, thickness),
3148 })
3149 }
3150
3151 impl<'a> ToCss for LonghandsToSerialize<'a> {
3152 #[allow(unused)]
3153 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3154 where
3155 W: fmt::Write,
3156 {
3157 use crate::values::specified::Color;
3158 use crate::values::specified::TextDecorationLine;
3159
3160 let is_solid_style =
3161 *self.text_decoration_style == text_decoration_style::SpecifiedValue::Solid;
3162 let is_current_color = *self.text_decoration_color == Color::CurrentColor;
3163 let is_auto_thickness = self.text_decoration_thickness.is_auto();
3164 let is_none = *self.text_decoration_line == TextDecorationLine::none();
3165
3166 let mut writer = SequenceWriter::new(dest, " ");
3167 if (is_solid_style && is_current_color && is_auto_thickness) || !is_none {
3168 writer.item(self.text_decoration_line)?;
3169 }
3170 if !is_auto_thickness {
3171 writer.item(self.text_decoration_thickness)?;
3172 }
3173 if !is_solid_style {
3174 writer.item(self.text_decoration_style)?;
3175 }
3176 if !is_current_color {
3177 writer.item(self.text_decoration_color)?;
3178 }
3179 Ok(())
3180 }
3181 }
3182}
3183
3184pub mod animation {
3185 pub use crate::properties::generated::shorthands::animation::*;
3186
3187 use super::*;
3188 use crate::properties::longhands::{
3189 animation_delay, animation_direction, animation_duration, animation_fill_mode,
3190 animation_iteration_count, animation_name, animation_play_state, animation_range_end,
3191 animation_range_start, animation_timeline, animation_timing_function,
3192 };
3193
3194 pub fn parse_value(
3195 context: &ParserContext,
3196 input: &mut Parser,
3197 ) -> Result<Longhands, ParseError> {
3198 struct SingleAnimation {
3199 animation_name: animation_name::SingleSpecifiedValue,
3200 animation_duration: animation_duration::SingleSpecifiedValue,
3201 animation_timing_function: animation_timing_function::SingleSpecifiedValue,
3202 animation_delay: animation_delay::SingleSpecifiedValue,
3203 animation_iteration_count: animation_iteration_count::SingleSpecifiedValue,
3204 animation_direction: animation_direction::SingleSpecifiedValue,
3205 animation_fill_mode: animation_fill_mode::SingleSpecifiedValue,
3206 animation_play_state: animation_play_state::SingleSpecifiedValue,
3207 }
3208
3209 fn parse_one_animation(
3210 context: &ParserContext,
3211 input: &mut Parser,
3212 ) -> Result<SingleAnimation, ParseError> {
3213 let mut name = None;
3214 let mut duration = None;
3215 let mut timing_function = None;
3216 let mut delay = None;
3217 let mut iteration_count = None;
3218 let mut direction = None;
3219 let mut fill_mode = None;
3220 let mut play_state = None;
3221
3222 let mut parsed = 0;
3223 loop {
3224 parsed += 1;
3225 try_parse_one!(
3226 context,
3227 input,
3228 duration,
3229 animation_duration::single_value::parse
3230 );
3231 try_parse_one!(
3232 context,
3233 input,
3234 timing_function,
3235 animation_timing_function::single_value::parse
3236 );
3237 try_parse_one!(context, input, delay, animation_delay::single_value::parse);
3238 try_parse_one!(
3239 context,
3240 input,
3241 iteration_count,
3242 animation_iteration_count::single_value::parse
3243 );
3244 try_parse_one!(
3245 context,
3246 input,
3247 direction,
3248 animation_direction::single_value::parse
3249 );
3250 try_parse_one!(
3251 context,
3252 input,
3253 fill_mode,
3254 animation_fill_mode::single_value::parse
3255 );
3256 try_parse_one!(
3257 context,
3258 input,
3259 play_state,
3260 animation_play_state::single_value::parse
3261 );
3262 try_parse_one!(context, input, name, animation_name::single_value::parse);
3263 parsed -= 1;
3264 break;
3265 }
3266
3267 if parsed == 0 {
3268 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
3269 }
3270 Ok(SingleAnimation {
3271 animation_name: name
3272 .unwrap_or_else(animation_name::single_value::get_initial_specified_value),
3273 animation_duration: duration
3274 .unwrap_or_else(animation_duration::single_value::get_initial_specified_value),
3275 animation_timing_function: timing_function.unwrap_or_else(
3276 animation_timing_function::single_value::get_initial_specified_value,
3277 ),
3278 animation_delay: delay
3279 .unwrap_or_else(animation_delay::single_value::get_initial_specified_value),
3280 animation_iteration_count: iteration_count.unwrap_or_else(
3281 animation_iteration_count::single_value::get_initial_specified_value,
3282 ),
3283 animation_direction: direction
3284 .unwrap_or_else(animation_direction::single_value::get_initial_specified_value),
3285 animation_fill_mode: fill_mode
3286 .unwrap_or_else(animation_fill_mode::single_value::get_initial_specified_value),
3287 animation_play_state: play_state.unwrap_or_else(
3288 animation_play_state::single_value::get_initial_specified_value,
3289 ),
3290 })
3291 }
3292
3293 let mut names = vec![];
3294 let mut durations = vec![];
3295 let mut timing_functions = vec![];
3296 let mut delays = vec![];
3297 let mut iteration_counts = vec![];
3298 let mut directions = vec![];
3299 let mut fill_modes = vec![];
3300 let mut play_states = vec![];
3301
3302 let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?;
3303 for result in results.into_iter() {
3304 names.push(result.animation_name);
3305 durations.push(result.animation_duration);
3306 timing_functions.push(result.animation_timing_function);
3307 delays.push(result.animation_delay);
3308 iteration_counts.push(result.animation_iteration_count);
3309 directions.push(result.animation_direction);
3310 fill_modes.push(result.animation_fill_mode);
3311 play_states.push(result.animation_play_state);
3312 }
3313
3314 Ok(expanded! {
3315 animation_name: animation_name::SpecifiedValue(names.into()),
3316 animation_duration: animation_duration::SpecifiedValue(durations.into()),
3317 animation_timing_function: animation_timing_function::SpecifiedValue(timing_functions.into()),
3318 animation_delay: animation_delay::SpecifiedValue(delays.into()),
3319 animation_iteration_count: animation_iteration_count::SpecifiedValue(iteration_counts.into()),
3320 animation_direction: animation_direction::SpecifiedValue(directions.into()),
3321 animation_fill_mode: animation_fill_mode::SpecifiedValue(fill_modes.into()),
3322 animation_play_state: animation_play_state::SpecifiedValue(play_states.into()),
3323 animation_timeline: animation_timeline::SpecifiedValue(
3324 vec![animation_timeline::single_value::get_initial_specified_value()].into()
3325 ),
3326 animation_range_start: animation_range_start::SpecifiedValue(
3330 vec![animation_range_start::single_value::get_initial_specified_value()].into()
3331 ),
3332 animation_range_end: animation_range_end::SpecifiedValue(
3333 vec![animation_range_end::single_value::get_initial_specified_value()].into()
3334 ),
3335 })
3336 }
3337
3338 impl<'a> ToCss for LonghandsToSerialize<'a> {
3339 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3340 where
3341 W: fmt::Write,
3342 {
3343 use crate::Zero;
3344 use crate::values::specified::easing::TimingFunction;
3345 use crate::values::specified::{
3346 AnimationDirection, AnimationFillMode, AnimationPlayState,
3347 };
3348 use style_traits::values::SequenceWriter;
3349
3350 let len = self.animation_name.0.len();
3351 if len == 0 {
3352 return Ok(());
3353 }
3354
3355 if len != self.animation_duration.0.len() {
3356 return Ok(());
3357 }
3358 if len != self.animation_timing_function.0.len() {
3359 return Ok(());
3360 }
3361 if len != self.animation_delay.0.len() {
3362 return Ok(());
3363 }
3364 if len != self.animation_iteration_count.0.len() {
3365 return Ok(());
3366 }
3367 if len != self.animation_direction.0.len() {
3368 return Ok(());
3369 }
3370 if len != self.animation_fill_mode.0.len() {
3371 return Ok(());
3372 }
3373 if len != self.animation_play_state.0.len() {
3374 return Ok(());
3375 }
3376
3377 if self
3380 .animation_timeline
3381 .is_some_and(|v| v.0.len() != 1 || !v.0[0].is_auto())
3382 {
3383 return Ok(());
3384 }
3385 if self
3386 .animation_range_start
3387 .is_some_and(|v| v.0.len() != 1 || !v.0[0].0.is_normal())
3388 {
3389 return Ok(());
3390 }
3391 if self
3392 .animation_range_end
3393 .is_some_and(|v| v.0.len() != 1 || !v.0[0].0.is_normal())
3394 {
3395 return Ok(());
3396 }
3397
3398 for i in 0..len {
3399 if i != 0 {
3400 dest.write_str(", ")?;
3401 }
3402
3403 let has_duration = !self.animation_duration.0[i].is_auto()
3404 && !self.animation_duration.0[i].is_zero();
3405 let has_timing_function = !self.animation_timing_function.0[i].is_ease();
3406 let has_delay = !self.animation_delay.0[i].is_zero();
3407 let has_iteration_count = !self.animation_iteration_count.0[i].is_one();
3408 let has_direction =
3409 !matches!(self.animation_direction.0[i], AnimationDirection::Normal);
3410 let has_fill_mode =
3411 !matches!(self.animation_fill_mode.0[i], AnimationFillMode::None);
3412 let has_play_state =
3413 !matches!(self.animation_play_state.0[i], AnimationPlayState::Running);
3414 let animation_name = &self.animation_name.0[i];
3415 let has_name = !animation_name.is_none();
3416
3417 let mut writer = SequenceWriter::new(dest, " ");
3418
3419 if has_duration || has_delay {
3420 writer.item(&self.animation_duration.0[i])?;
3421 }
3422
3423 if has_timing_function || TimingFunction::match_keywords(animation_name) {
3424 writer.item(&self.animation_timing_function.0[i])?;
3425 }
3426
3427 if has_delay {
3428 writer.item(&self.animation_delay.0[i])?;
3429 }
3430 if has_iteration_count {
3431 writer.item(&self.animation_iteration_count.0[i])?;
3432 }
3433
3434 if has_direction || AnimationDirection::match_keywords(animation_name) {
3435 writer.item(&self.animation_direction.0[i])?;
3436 }
3437
3438 if has_fill_mode || AnimationFillMode::match_keywords(animation_name) {
3439 writer.item(&self.animation_fill_mode.0[i])?;
3440 }
3441
3442 if has_play_state || AnimationPlayState::match_keywords(animation_name) {
3443 writer.item(&self.animation_play_state.0[i])?;
3444 }
3445
3446 if has_name || !writer.has_written() {
3447 writer.item(animation_name)?;
3448 }
3449 }
3450 Ok(())
3451 }
3452 }
3453}
3454
3455pub mod mask {
3456 pub use crate::properties::generated::shorthands::mask::*;
3457
3458 use super::*;
3459 use crate::parser::Parse;
3460 use crate::properties::longhands::{
3461 mask_clip, mask_composite, mask_mode, mask_origin, mask_position_x, mask_position_y,
3462 mask_repeat,
3463 };
3464 use crate::properties::longhands::{mask_image, mask_size};
3465 use crate::values::specified::{Position, PositionComponent};
3466
3467 pub fn parse_value(
3468 context: &ParserContext,
3469 input: &mut Parser,
3470 ) -> Result<Longhands, ParseError> {
3471 let mut mask_image = Vec::with_capacity(1);
3472 let mut mask_mode = Vec::with_capacity(1);
3473 let mut mask_position_x = Vec::with_capacity(1);
3474 let mut mask_position_y = Vec::with_capacity(1);
3475 let mut mask_size = Vec::with_capacity(1);
3476 let mut mask_repeat = Vec::with_capacity(1);
3477 let mut mask_origin = Vec::with_capacity(1);
3478 let mut mask_clip = Vec::with_capacity(1);
3479 let mut mask_composite = Vec::with_capacity(1);
3480
3481 input.parse_comma_separated(|input| {
3482 let mut image = None;
3483 let mut mode = None;
3484 let mut position = None;
3485 let mut size = None;
3486 let mut repeat = None;
3487 let mut origin = None;
3488 let mut clip = None;
3489 let mut composite = None;
3490 let mut parsed = 0;
3491 loop {
3492 parsed += 1;
3493
3494 try_parse_one!(context, input, image, mask_image::single_value::parse);
3495 if position.is_none()
3496 && let Ok(value) = input.try_parse(|input| Position::parse(context, input))
3497 {
3498 position = Some(value);
3499 size = input
3500 .try_parse(|input| {
3501 input.expect_delim('/')?;
3502 mask_size::single_value::parse(context, input)
3503 })
3504 .ok();
3505
3506 continue;
3507 }
3508 try_parse_one!(context, input, repeat, mask_repeat::single_value::parse);
3509 try_parse_one!(context, input, origin, mask_origin::single_value::parse);
3510 try_parse_one!(context, input, clip, mask_clip::single_value::parse);
3511 try_parse_one!(
3512 context,
3513 input,
3514 composite,
3515 mask_composite::single_value::parse
3516 );
3517 try_parse_one!(context, input, mode, mask_mode::single_value::parse);
3518
3519 parsed -= 1;
3520 break;
3521 }
3522 if clip.is_none()
3523 && let Some(origin) = origin
3524 {
3525 clip = Some(mask_clip::single_value::SpecifiedValue::from(origin));
3526 }
3527 if parsed == 0 {
3528 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
3529 }
3530 if let Some(position) = position {
3531 mask_position_x.push(position.horizontal);
3532 mask_position_y.push(position.vertical);
3533 } else {
3534 mask_position_x.push(PositionComponent::zero());
3535 mask_position_y.push(PositionComponent::zero());
3536 }
3537 if let Some(m_image) = image {
3538 mask_image.push(m_image);
3539 } else {
3540 mask_image.push(mask_image::single_value::get_initial_specified_value());
3541 }
3542 if let Some(m_mode) = mode {
3543 mask_mode.push(m_mode);
3544 } else {
3545 mask_mode.push(mask_mode::single_value::get_initial_specified_value());
3546 }
3547 if let Some(m_size) = size {
3548 mask_size.push(m_size);
3549 } else {
3550 mask_size.push(mask_size::single_value::get_initial_specified_value());
3551 }
3552 if let Some(m_repeat) = repeat {
3553 mask_repeat.push(m_repeat);
3554 } else {
3555 mask_repeat.push(mask_repeat::single_value::get_initial_specified_value());
3556 }
3557 if let Some(m_origin) = origin {
3558 mask_origin.push(m_origin);
3559 } else {
3560 mask_origin.push(mask_origin::single_value::get_initial_specified_value());
3561 }
3562 if let Some(m_clip) = clip {
3563 mask_clip.push(m_clip);
3564 } else {
3565 mask_clip.push(mask_clip::single_value::get_initial_specified_value());
3566 }
3567 if let Some(m_composite) = composite {
3568 mask_composite.push(m_composite);
3569 } else {
3570 mask_composite.push(mask_composite::single_value::get_initial_specified_value());
3571 }
3572 Ok(())
3573 })?;
3574
3575 Ok(expanded! {
3576 mask_image: mask_image::SpecifiedValue(mask_image.into()),
3577 mask_mode: mask_mode::SpecifiedValue(mask_mode.into()),
3578 mask_position_x: mask_position_x::SpecifiedValue(mask_position_x.into()),
3579 mask_position_y: mask_position_y::SpecifiedValue(mask_position_y.into()),
3580 mask_size: mask_size::SpecifiedValue(mask_size.into()),
3581 mask_repeat: mask_repeat::SpecifiedValue(mask_repeat.into()),
3582 mask_origin: mask_origin::SpecifiedValue(mask_origin.into()),
3583 mask_clip: mask_clip::SpecifiedValue(mask_clip.into()),
3584 mask_composite: mask_composite::SpecifiedValue(mask_composite.into()),
3585 })
3586 }
3587
3588 impl<'a> ToCss for LonghandsToSerialize<'a> {
3589 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3590 where
3591 W: fmt::Write,
3592 {
3593 use crate::properties::longhands::mask_clip::single_value::computed_value::T as Clip;
3594 use crate::properties::longhands::mask_origin::single_value::computed_value::T as Origin;
3595 use style_traits::values::SequenceWriter;
3596
3597 let len = self.mask_image.0.len();
3598 if len == 0 {
3599 return Ok(());
3600 }
3601 if self.mask_mode.0.len() != len {
3602 return Ok(());
3603 }
3604 if self.mask_position_x.0.len() != len {
3605 return Ok(());
3606 }
3607 if self.mask_position_y.0.len() != len {
3608 return Ok(());
3609 }
3610 if self.mask_size.0.len() != len {
3611 return Ok(());
3612 }
3613 if self.mask_repeat.0.len() != len {
3614 return Ok(());
3615 }
3616 if self.mask_origin.0.len() != len {
3617 return Ok(());
3618 }
3619 if self.mask_clip.0.len() != len {
3620 return Ok(());
3621 }
3622 if self.mask_composite.0.len() != len {
3623 return Ok(());
3624 }
3625
3626 for i in 0..len {
3627 if i > 0 {
3628 dest.write_str(", ")?;
3629 }
3630
3631 let image = &self.mask_image.0[i];
3632 let mode = &self.mask_mode.0[i];
3633 let position_x = &self.mask_position_x.0[i];
3634 let position_y = &self.mask_position_y.0[i];
3635 let size = &self.mask_size.0[i];
3636 let repeat = &self.mask_repeat.0[i];
3637 let origin = &self.mask_origin.0[i];
3638 let clip = &self.mask_clip.0[i];
3639 let composite = &self.mask_composite.0[i];
3640
3641 let mut has_other = false;
3642 let has_image = *image != mask_image::single_value::get_initial_specified_value();
3643 has_other |= has_image;
3644 let has_mode = *mode != mask_mode::single_value::get_initial_specified_value();
3645 has_other |= has_mode;
3646 let has_size = *size != mask_size::single_value::get_initial_specified_value();
3647 has_other |= has_size;
3648 let has_repeat =
3649 *repeat != mask_repeat::single_value::get_initial_specified_value();
3650 has_other |= has_repeat;
3651 let has_composite =
3652 *composite != mask_composite::single_value::get_initial_specified_value();
3653 has_other |= has_composite;
3654 let has_position = *position_x != PositionComponent::zero()
3655 || *position_y != PositionComponent::zero();
3656 let has_origin = *origin != Origin::BorderBox;
3657 let has_clip = *clip != Clip::BorderBox;
3658
3659 if !has_other && !has_position && !has_origin && !has_clip {
3660 return image.to_css(dest);
3661 }
3662
3663 let mut writer = SequenceWriter::new(dest, " ");
3664 if has_image {
3665 writer.item(image)?;
3666 }
3667 if has_position || has_size {
3668 writer.write_item(|dest| {
3669 Position {
3670 horizontal: position_x.clone(),
3671 vertical: position_y.clone(),
3672 }
3673 .to_css(dest)?;
3674 if has_size {
3675 dest.write_str(" / ")?;
3676 size.to_css(dest)?;
3677 }
3678 Ok(())
3679 })?;
3680 }
3681
3682 if has_repeat {
3683 writer.item(repeat)?;
3684 }
3685
3686 #[cfg(feature = "gecko")]
3687 {
3688 if has_origin || (has_clip && *clip != Clip::NoClip) {
3689 writer.item(origin)?;
3690 }
3691 }
3692
3693 #[cfg(feature = "servo")]
3694 {
3695 if has_origin || has_clip {
3696 writer.item(origin)?;
3697 }
3698 }
3699
3700 if has_clip && *clip != From::from(*origin) {
3701 writer.item(clip)?;
3702 }
3703
3704 if has_composite {
3705 writer.item(composite)?;
3706 }
3707
3708 if has_mode {
3709 writer.item(mode)?;
3710 }
3711 }
3712
3713 Ok(())
3714 }
3715 }
3716}
3717
3718pub mod mask_position {
3719 pub use crate::properties::generated::shorthands::mask_position::*;
3720
3721 use super::*;
3722 use crate::properties::longhands::{mask_position_x, mask_position_y};
3723 use crate::values::specified::Position;
3724
3725 pub fn parse_value(
3726 context: &ParserContext,
3727 input: &mut Parser,
3728 ) -> Result<Longhands, ParseError> {
3729 let mut position_x = Vec::with_capacity(1);
3733 let mut position_y = Vec::with_capacity(1);
3734 input.parse_comma_separated(|input| {
3735 let value = Position::parse(context, input)?;
3736 position_x.push(value.horizontal);
3737 position_y.push(value.vertical);
3738 Ok(())
3739 })?;
3740
3741 if position_x.is_empty() {
3742 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
3743 }
3744
3745 Ok(expanded! {
3746 mask_position_x: mask_position_x::SpecifiedValue(position_x.into()),
3747 mask_position_y: mask_position_y::SpecifiedValue(position_y.into()),
3748 })
3749 }
3750
3751 impl<'a> ToCss for LonghandsToSerialize<'a> {
3752 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3753 where
3754 W: fmt::Write,
3755 {
3756 let len = self.mask_position_x.0.len();
3757 if len == 0 || self.mask_position_y.0.len() != len {
3758 return Ok(());
3759 }
3760
3761 for i in 0..len {
3762 Position {
3763 horizontal: self.mask_position_x.0[i].clone(),
3764 vertical: self.mask_position_y.0[i].clone(),
3765 }
3766 .to_css(dest)?;
3767
3768 if i < len - 1 {
3769 dest.write_str(", ")?;
3770 }
3771 }
3772
3773 Ok(())
3774 }
3775 }
3776}
3777
3778pub mod grid_template {
3779 pub use crate::properties::generated::shorthands::grid_template::*;
3780
3781 use super::*;
3782 use crate::parser::Parse;
3783 use crate::values::generics::grid::{TrackList, TrackSize};
3784 use crate::values::generics::grid::{TrackListValue, concat_serialize_idents};
3785 use crate::values::specified::grid::parse_line_names;
3786 use crate::values::specified::position::{
3787 GridTemplateAreas, TemplateAreasArc, TemplateAreasParser,
3788 };
3789 use crate::values::specified::{GenericGridTemplateComponent, GridTemplateComponent};
3790 use servo_arc::Arc;
3791
3792 pub fn parse_grid_template(
3793 context: &ParserContext,
3794 input: &mut Parser,
3795 ) -> Result<
3796 (
3797 GridTemplateComponent,
3798 GridTemplateComponent,
3799 GridTemplateAreas,
3800 ),
3801 ParseError,
3802 > {
3803 if let Ok(x) = input.try_parse(|i| {
3804 if i.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
3805 if !i.is_exhausted() {
3806 return Err(());
3807 }
3808 return Ok((
3809 GenericGridTemplateComponent::None,
3810 GenericGridTemplateComponent::None,
3811 GridTemplateAreas::None,
3812 ));
3813 }
3814 Err(())
3815 }) {
3816 return Ok(x);
3817 }
3818
3819 let first_line_names = input.try_parse(parse_line_names).unwrap_or_default();
3820 let mut areas_parser = TemplateAreasParser::default();
3821 if areas_parser.try_parse_string(input).is_ok() {
3822 let mut values = vec![];
3823 let mut line_names = vec![];
3824 line_names.push(first_line_names);
3825 loop {
3826 let size = input
3827 .try_parse(|i| TrackSize::parse(context, i))
3828 .unwrap_or_default();
3829 values.push(TrackListValue::TrackSize(size));
3830 let mut names = input.try_parse(parse_line_names).unwrap_or_default();
3831 let more_names = input.try_parse(parse_line_names);
3832
3833 match areas_parser.try_parse_string(input) {
3834 Ok(()) => {
3835 if let Ok(v) = more_names {
3836 let mut names_vec = names.into_vec();
3837 names_vec.extend(v);
3838 names = names_vec.into();
3839 }
3840 line_names.push(names);
3841 },
3842 Err(e) => {
3843 if more_names.is_ok() {
3844 return Err(e);
3845 }
3846 line_names.push(names);
3847 break;
3848 },
3849 };
3850 }
3851
3852 if line_names.len() == values.len() {
3853 line_names.push(Default::default());
3854 }
3855
3856 let template_areas = areas_parser
3857 .finish()
3858 .map_err(|()| ParseError::custom(StyleParseErrorKind::UnspecifiedError))?;
3859 let template_rows = TrackList {
3860 values: values.into(),
3861 line_names: line_names.into(),
3862 auto_repeat_index: usize::MAX,
3863 };
3864
3865 let template_cols = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
3866 let value = GridTemplateComponent::parse_without_none(context, input)?;
3867 if let GenericGridTemplateComponent::TrackList(ref list) = value
3868 && !list.is_explicit()
3869 {
3870 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
3871 }
3872
3873 value
3874 } else {
3875 GridTemplateComponent::default()
3876 };
3877
3878 Ok((
3879 GenericGridTemplateComponent::TrackList(Box::new(template_rows)),
3880 template_cols,
3881 GridTemplateAreas::Areas(TemplateAreasArc(Arc::new(template_areas))),
3882 ))
3883 } else {
3884 let mut template_rows = GridTemplateComponent::parse(context, input)?;
3885 if let GenericGridTemplateComponent::TrackList(ref mut list) = template_rows {
3886 if list.line_names[0].is_empty() {
3887 list.line_names[0] = first_line_names;
3888 } else {
3889 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
3890 }
3891 }
3892
3893 input.expect_delim('/')?;
3894 Ok((
3895 template_rows,
3896 GridTemplateComponent::parse(context, input)?,
3897 GridTemplateAreas::None,
3898 ))
3899 }
3900 }
3901
3902 #[inline]
3903 pub fn parse_value(
3904 context: &ParserContext,
3905 input: &mut Parser,
3906 ) -> Result<Longhands, ParseError> {
3907 let (rows, columns, areas) = parse_grid_template(context, input)?;
3908 Ok(expanded! {
3909 grid_template_rows: rows,
3910 grid_template_columns: columns,
3911 grid_template_areas: areas,
3912 })
3913 }
3914
3915 pub fn serialize_grid_template<W>(
3916 template_rows: &GridTemplateComponent,
3917 template_columns: &GridTemplateComponent,
3918 template_areas: &GridTemplateAreas,
3919 dest: &mut CssWriter<W>,
3920 ) -> fmt::Result
3921 where
3922 W: fmt::Write,
3923 {
3924 match *template_areas {
3925 GridTemplateAreas::None => {
3926 if template_rows.is_initial() && template_columns.is_initial() {
3927 return GridTemplateComponent::default().to_css(dest);
3928 }
3929 template_rows.to_css(dest)?;
3930 dest.write_str(" / ")?;
3931 template_columns.to_css(dest)
3932 },
3933 GridTemplateAreas::Areas(ref areas) => {
3934 if areas.0.strings.len() != template_rows.track_list_len() {
3935 return Ok(());
3936 }
3937
3938 let track_list = match *template_rows {
3939 GenericGridTemplateComponent::TrackList(ref list) => {
3940 if !list.is_explicit() {
3941 return Ok(());
3942 }
3943 list
3944 },
3945 _ => return Ok(()),
3946 };
3947
3948 match *template_columns {
3949 GenericGridTemplateComponent::TrackList(ref list) => {
3950 if !list.is_explicit() {
3951 return Ok(());
3952 }
3953 },
3954 GenericGridTemplateComponent::Subgrid(_) => {
3955 return Ok(());
3956 },
3957 _ => {},
3958 }
3959
3960 let mut names_iter = track_list.line_names.iter();
3961 for (((i, string), names), value) in areas
3962 .0
3963 .strings
3964 .iter()
3965 .enumerate()
3966 .zip(&mut names_iter)
3967 .zip(track_list.values.iter())
3968 {
3969 if i > 0 {
3970 dest.write_char(' ')?;
3971 }
3972
3973 if !names.is_empty() {
3974 concat_serialize_idents("[", "] ", names, " ", dest)?;
3975 }
3976
3977 string.to_css(dest)?;
3978
3979 if !value.is_initial() {
3980 dest.write_char(' ')?;
3981 value.to_css(dest)?;
3982 }
3983 }
3984
3985 if let Some(names) = names_iter.next() {
3986 concat_serialize_idents(" [", "]", names, " ", dest)?;
3987 }
3988
3989 if let GenericGridTemplateComponent::TrackList(ref list) = *template_columns {
3990 dest.write_str(" / ")?;
3991 list.to_css(dest)?;
3992 }
3993
3994 Ok(())
3995 },
3996 }
3997 }
3998
3999 impl<'a> ToCss for LonghandsToSerialize<'a> {
4000 #[inline]
4001 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
4002 where
4003 W: fmt::Write,
4004 {
4005 serialize_grid_template(
4006 self.grid_template_rows,
4007 self.grid_template_columns,
4008 self.grid_template_areas,
4009 dest,
4010 )
4011 }
4012 }
4013}
4014
4015pub mod grid {
4016 pub use crate::properties::generated::shorthands::grid::*;
4017
4018 use super::*;
4019 use crate::parser::Parse;
4020 use crate::properties::longhands::{grid_auto_columns, grid_auto_flow, grid_auto_rows};
4021 use crate::values::generics::grid::GridTemplateComponent;
4022 use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
4023 use crate::values::specified::{GenericGridTemplateComponent, ImplicitGridTracks};
4024
4025 pub fn parse_value(
4026 context: &ParserContext,
4027 input: &mut Parser,
4028 ) -> Result<Longhands, ParseError> {
4029 let mut temp_rows = GridTemplateComponent::default();
4030 let mut temp_cols = GridTemplateComponent::default();
4031 let mut temp_areas = GridTemplateAreas::None;
4032 let mut auto_rows = ImplicitGridTracks::default();
4033 let mut auto_cols = ImplicitGridTracks::default();
4034 let mut flow = grid_auto_flow::get_initial_value();
4035
4036 fn parse_auto_flow(input: &mut Parser, is_row: bool) -> Result<GridAutoFlow, ParseError> {
4037 let mut track = None;
4038 let mut dense = GridAutoFlow::empty();
4039
4040 for _ in 0..2 {
4041 if input
4042 .try_parse(|i| i.expect_ident_matching("auto-flow"))
4043 .is_ok()
4044 {
4045 track = if is_row {
4046 Some(GridAutoFlow::ROW)
4047 } else {
4048 Some(GridAutoFlow::COLUMN)
4049 };
4050 } else if input
4051 .try_parse(|i| i.expect_ident_matching("dense"))
4052 .is_ok()
4053 {
4054 dense = GridAutoFlow::DENSE
4055 } else {
4056 break;
4057 }
4058 }
4059
4060 if track.is_some() {
4061 Ok(track.unwrap() | dense)
4062 } else {
4063 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
4064 }
4065 }
4066
4067 if let Ok((rows, cols, areas)) =
4068 input.try_parse(|i| super::grid_template::parse_grid_template(context, i))
4069 {
4070 temp_rows = rows;
4071 temp_cols = cols;
4072 temp_areas = areas;
4073 } else if let Ok(rows) = input.try_parse(|i| GridTemplateComponent::parse(context, i)) {
4074 temp_rows = rows;
4075 input.expect_delim('/')?;
4076 flow = parse_auto_flow(input, false)?;
4077 auto_cols = input
4078 .try_parse(|i| grid_auto_columns::parse(context, i))
4079 .unwrap_or_default();
4080 } else {
4081 flow = parse_auto_flow(input, true)?;
4082 auto_rows = input
4083 .try_parse(|i| grid_auto_rows::parse(context, i))
4084 .unwrap_or_default();
4085 input.expect_delim('/')?;
4086 temp_cols = GridTemplateComponent::parse(context, input)?;
4087 }
4088
4089 Ok(expanded! {
4090 grid_template_rows: temp_rows,
4091 grid_template_columns: temp_cols,
4092 grid_template_areas: temp_areas,
4093 grid_auto_rows: auto_rows,
4094 grid_auto_columns: auto_cols,
4095 grid_auto_flow: flow,
4096 })
4097 }
4098
4099 impl<'a> LonghandsToSerialize<'a> {
4100 fn is_grid_template(&self) -> bool {
4101 self.grid_auto_rows.is_initial()
4102 && self.grid_auto_columns.is_initial()
4103 && *self.grid_auto_flow == grid_auto_flow::get_initial_value()
4104 }
4105 }
4106
4107 impl<'a> ToCss for LonghandsToSerialize<'a> {
4108 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
4109 where
4110 W: fmt::Write,
4111 {
4112 if self.is_grid_template() {
4113 return super::grid_template::serialize_grid_template(
4114 self.grid_template_rows,
4115 self.grid_template_columns,
4116 self.grid_template_areas,
4117 dest,
4118 );
4119 }
4120
4121 if *self.grid_template_areas != GridTemplateAreas::None {
4122 return Ok(());
4123 }
4124
4125 if self.grid_auto_flow.contains(GridAutoFlow::COLUMN) {
4126 if !self.grid_auto_rows.is_initial() || !self.grid_template_columns.is_initial() {
4127 return Ok(());
4128 }
4129
4130 if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_rows
4131 && !list.is_explicit()
4132 {
4133 return Ok(());
4134 }
4135
4136 self.grid_template_rows.to_css(dest)?;
4137 dest.write_str(" / auto-flow")?;
4138 if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
4139 dest.write_str(" dense")?;
4140 }
4141
4142 if !self.grid_auto_columns.is_initial() {
4143 dest.write_char(' ')?;
4144 self.grid_auto_columns.to_css(dest)?;
4145 }
4146
4147 return Ok(());
4148 }
4149
4150 if !self.grid_auto_columns.is_initial() || !self.grid_template_rows.is_initial() {
4151 return Ok(());
4152 }
4153
4154 if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_columns
4155 && !list.is_explicit()
4156 {
4157 return Ok(());
4158 }
4159
4160 dest.write_str("auto-flow")?;
4161 if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
4162 dest.write_str(" dense")?;
4163 }
4164
4165 if !self.grid_auto_rows.is_initial() {
4166 dest.write_char(' ')?;
4167 self.grid_auto_rows.to_css(dest)?;
4168 }
4169
4170 dest.write_str(" / ")?;
4171 self.grid_template_columns.to_css(dest)?;
4172 Ok(())
4173 }
4174 }
4175}