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