Skip to main content

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