style/properties/
shorthands.rs

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