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::AnimationRangeValue;
1882
1883                    if !start.0.name.is_none() {
1884                        // If `<animation-range-start>` includes a timeline range name,
1885                        // `animation-range-end` is set to that same timeline range name and 100%.
1886                        AnimationRangeEnd(AnimationRangeValue::timeline_range(
1887                            start.0.name,
1888                            LengthPercentage::hundred_percent(),
1889                        ))
1890                    } else {
1891                        // Otherwise, any omitted longhand is set to its initial value.
1892                        animation_range_end::single_value::get_initial_specified_value()
1893                    }
1894                });
1895
1896            starts.push(start);
1897            ends.push(end);
1898            Ok(())
1899        })?;
1900
1901        Ok(expanded! {
1902            animation_range_start: animation_range_start::SpecifiedValue(starts.into()),
1903            animation_range_end: animation_range_end::SpecifiedValue(ends.into()),
1904        })
1905    }
1906
1907    impl<'a> ToCss for LonghandsToSerialize<'a> {
1908        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1909        where
1910            W: fmt::Write,
1911        {
1912            use crate::values::generics::Optional;
1913
1914            let starts = &self.animation_range_start.0;
1915            let ends = &self.animation_range_end.0;
1916
1917            if starts.len() != ends.len() {
1918                return Ok(());
1919            }
1920
1921            for (i, (start, end)) in std::iter::zip(starts.iter(), ends.iter()).enumerate() {
1922                if i != 0 {
1923                    dest.write_str(", ")?;
1924                }
1925
1926                start.to_css(dest)?;
1927
1928                let can_omit = if start.0.name == end.0.name {
1929                    match end.0.lp {
1930                        // If both have the same range name, or they don't have range name, we omit
1931                        // the end value if it is 100%.
1932                        Optional::Some(ref p) => p == &LengthPercentage::hundred_percent(),
1933                        // The end value is `normal`.
1934                        Optional::None => end.0.name.is_none(),
1935                    }
1936                } else {
1937                    false
1938                };
1939                if !can_omit {
1940                    dest.write_char(' ')?;
1941                    end.to_css(dest)?;
1942                }
1943            }
1944            Ok(())
1945        }
1946    }
1947}
1948
1949pub mod transition {
1950    pub use crate::properties::generated::shorthands::transition::*;
1951
1952    use super::*;
1953    use crate::properties::longhands::{
1954        transition_behavior, transition_delay, transition_duration, transition_property,
1955        transition_timing_function,
1956    };
1957    use crate::values::specified::TransitionProperty;
1958
1959    pub fn parse_value<'i, 't>(
1960        context: &ParserContext,
1961        input: &mut Parser<'i, 't>,
1962    ) -> Result<Longhands, ParseError<'i>> {
1963        struct SingleTransition {
1964            transition_property: transition_property::SingleSpecifiedValue,
1965            transition_duration: transition_duration::SingleSpecifiedValue,
1966            transition_timing_function: transition_timing_function::SingleSpecifiedValue,
1967            transition_delay: transition_delay::SingleSpecifiedValue,
1968            transition_behavior: transition_behavior::SingleSpecifiedValue,
1969        }
1970
1971        fn parse_one_transition<'i, 't>(
1972            context: &ParserContext,
1973            input: &mut Parser<'i, 't>,
1974            first: bool,
1975        ) -> Result<SingleTransition, ParseError<'i>> {
1976            let mut property = None;
1977            let mut duration = None;
1978            let mut timing_function = None;
1979            let mut delay = None;
1980            let mut behavior = None;
1981
1982            let mut parsed = 0;
1983            loop {
1984                parsed += 1;
1985
1986                try_parse_one!(
1987                    context,
1988                    input,
1989                    duration,
1990                    transition_duration::single_value::parse
1991                );
1992                try_parse_one!(
1993                    context,
1994                    input,
1995                    timing_function,
1996                    transition_timing_function::single_value::parse
1997                );
1998                try_parse_one!(context, input, delay, transition_delay::single_value::parse);
1999                try_parse_one!(
2000                    context,
2001                    input,
2002                    behavior,
2003                    transition_behavior::single_value::parse
2004                );
2005                if property.is_none() {
2006                    if let Ok(value) = input.try_parse(|i| TransitionProperty::parse(context, i)) {
2007                        property = Some(value);
2008                        continue;
2009                    }
2010
2011                    if first && input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
2012                        property = Some(TransitionProperty::none());
2013                        continue;
2014                    }
2015                }
2016
2017                parsed -= 1;
2018                break;
2019            }
2020
2021            if parsed != 0 {
2022                Ok(SingleTransition {
2023                    transition_property: property.unwrap_or_else(
2024                        transition_property::single_value::get_initial_specified_value,
2025                    ),
2026                    transition_duration: duration.unwrap_or_else(
2027                        transition_duration::single_value::get_initial_specified_value,
2028                    ),
2029                    transition_timing_function: timing_function.unwrap_or_else(
2030                        transition_timing_function::single_value::get_initial_specified_value,
2031                    ),
2032                    transition_delay: delay.unwrap_or_else(
2033                        transition_delay::single_value::get_initial_specified_value,
2034                    ),
2035                    transition_behavior: behavior.unwrap_or_else(
2036                        transition_behavior::single_value::get_initial_specified_value,
2037                    ),
2038                })
2039            } else {
2040                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
2041            }
2042        }
2043
2044        let mut first = true;
2045        let mut has_transition_property_none = false;
2046        let results = input.parse_comma_separated(|i| {
2047            if has_transition_property_none {
2048                return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2049            }
2050            let transition = parse_one_transition(context, i, first)?;
2051            first = false;
2052            has_transition_property_none = transition.transition_property.is_none();
2053            Ok(transition)
2054        })?;
2055
2056        let len = results.len();
2057        let mut property = Vec::with_capacity(len);
2058        let mut duration = Vec::with_capacity(len);
2059        let mut timing_function = Vec::with_capacity(len);
2060        let mut delay = Vec::with_capacity(len);
2061        let mut behavior = Vec::with_capacity(len);
2062        for result in results {
2063            property.push(result.transition_property);
2064            duration.push(result.transition_duration);
2065            timing_function.push(result.transition_timing_function);
2066            delay.push(result.transition_delay);
2067            behavior.push(result.transition_behavior);
2068        }
2069
2070        Ok(Longhands {
2071            transition_property: transition_property::SpecifiedValue(property.into()),
2072            transition_duration: transition_duration::SpecifiedValue(duration.into()),
2073            transition_timing_function: transition_timing_function::SpecifiedValue(
2074                timing_function.into(),
2075            ),
2076            transition_delay: transition_delay::SpecifiedValue(delay.into()),
2077            transition_behavior: transition_behavior::SpecifiedValue(behavior.into()),
2078        })
2079    }
2080
2081    impl<'a> ToCss for LonghandsToSerialize<'a> {
2082        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2083        where
2084            W: fmt::Write,
2085        {
2086            use crate::Zero;
2087            use style_traits::values::SequenceWriter;
2088
2089            let len = self.transition_property.0.len();
2090            debug_assert_ne!(
2091                len, 0,
2092                "We should always have at least one transition-property, even if none"
2093            );
2094            if self.transition_duration.0.len() != len {
2095                return Ok(());
2096            }
2097            if self.transition_delay.0.len() != len {
2098                return Ok(());
2099            }
2100            if self.transition_timing_function.0.len() != len {
2101                return Ok(());
2102            }
2103            if self.transition_behavior.0.len() != len {
2104                return Ok(());
2105            }
2106            for i in 0..len {
2107                if i != 0 {
2108                    dest.write_str(", ")?;
2109                }
2110
2111                let has_duration = !self.transition_duration.0[i].is_zero();
2112                let has_timing = !self.transition_timing_function.0[i].is_ease();
2113                let has_delay = !self.transition_delay.0[i].is_zero();
2114                let has_behavior = !self.transition_behavior.0[i].is_normal();
2115                let has_any = has_duration || has_timing || has_delay || has_behavior;
2116
2117                let mut writer = SequenceWriter::new(dest, " ");
2118                if !self.transition_property.0[i].is_all() || !has_any {
2119                    writer.item(&self.transition_property.0[i])?;
2120                }
2121                if has_duration || has_delay {
2122                    writer.item(&self.transition_duration.0[i])?;
2123                }
2124                if has_timing {
2125                    writer.item(&self.transition_timing_function.0[i])?;
2126                }
2127                if has_delay {
2128                    writer.item(&self.transition_delay.0[i])?;
2129                }
2130                if has_behavior {
2131                    writer.item(&self.transition_behavior.0[i])?;
2132                }
2133            }
2134            Ok(())
2135        }
2136    }
2137}
2138
2139pub mod outline {
2140    pub use crate::properties::generated::shorthands::outline::*;
2141
2142    use super::*;
2143    use crate::properties::longhands::{outline_color, outline_style, outline_width};
2144
2145    pub fn parse_value<'i, 't>(
2146        context: &ParserContext,
2147        input: &mut Parser<'i, 't>,
2148    ) -> Result<Longhands, ParseError<'i>> {
2149        let _unused = context;
2150        let mut color = None;
2151        let mut style = None;
2152        let mut width = None;
2153        let mut parsed = 0;
2154        loop {
2155            parsed += 1;
2156            try_parse_one!(context, input, color, specified::Color::parse);
2157            try_parse_one!(context, input, style, outline_style::parse);
2158            try_parse_one!(context, input, width, outline_width::parse);
2159            parsed -= 1;
2160            break;
2161        }
2162        if parsed == 0 {
2163            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2164        }
2165        Ok(expanded! {
2166            outline_color: unwrap_or_initial!(outline_color, color),
2167            outline_style: unwrap_or_initial!(outline_style, style),
2168            outline_width: unwrap_or_initial!(outline_width, width),
2169        })
2170    }
2171
2172    impl<'a> ToCss for LonghandsToSerialize<'a> {
2173        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2174        where
2175            W: fmt::Write,
2176        {
2177            let mut writer = SequenceWriter::new(dest, " ");
2178            if *self.outline_color != outline_color::get_initial_specified_value() {
2179                writer.item(self.outline_color)?;
2180            }
2181            if *self.outline_style != outline_style::get_initial_specified_value() {
2182                writer.item(self.outline_style)?;
2183            }
2184            if *self.outline_width != outline_width::get_initial_specified_value() {
2185                writer.item(self.outline_width)?;
2186            }
2187            if !writer.has_written() {
2188                self.outline_style.to_css(dest)?;
2189            }
2190            Ok(())
2191        }
2192    }
2193}
2194
2195pub mod background_position {
2196    pub use crate::properties::generated::shorthands::background_position::*;
2197
2198    use super::*;
2199    use crate::properties::longhands::{background_position_x, background_position_y};
2200    use crate::values::specified::position::Position;
2201    use crate::values::specified::AllowQuirks;
2202
2203    pub fn parse_value<'i, 't>(
2204        context: &ParserContext,
2205        input: &mut Parser<'i, 't>,
2206    ) -> Result<Longhands, ParseError<'i>> {
2207        let mut position_x = Vec::with_capacity(1);
2208        let mut position_y = Vec::with_capacity(1);
2209        let mut any = false;
2210
2211        input.parse_comma_separated(|input| {
2212            let value = Position::parse_three_value_quirky(context, input, AllowQuirks::Yes)?;
2213            position_x.push(value.horizontal);
2214            position_y.push(value.vertical);
2215            any = true;
2216            Ok(())
2217        })?;
2218        if !any {
2219            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2220        }
2221
2222        Ok(expanded! {
2223            background_position_x: background_position_x::SpecifiedValue(position_x.into()),
2224            background_position_y: background_position_y::SpecifiedValue(position_y.into()),
2225        })
2226    }
2227
2228    impl<'a> ToCss for LonghandsToSerialize<'a> {
2229        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2230        where
2231            W: fmt::Write,
2232        {
2233            let len = self.background_position_x.0.len();
2234            if len == 0 || len != self.background_position_y.0.len() {
2235                return Ok(());
2236            }
2237            for i in 0..len {
2238                Position {
2239                    horizontal: self.background_position_x.0[i].clone(),
2240                    vertical: self.background_position_y.0[i].clone(),
2241                }
2242                .to_css(dest)?;
2243
2244                if i < len - 1 {
2245                    dest.write_str(", ")?;
2246                }
2247            }
2248            Ok(())
2249        }
2250    }
2251}
2252
2253pub mod background {
2254    pub use crate::properties::generated::shorthands::background::*;
2255
2256    use super::*;
2257    use crate::properties::longhands::background_clip;
2258    use crate::properties::longhands::background_clip::single_value::computed_value::T as Clip;
2259    use crate::properties::longhands::background_origin::single_value::computed_value::T as Origin;
2260    use crate::properties::longhands::{
2261        background_attachment, background_color, background_image, background_origin,
2262        background_size,
2263    };
2264    use crate::properties::longhands::{
2265        background_position_x, background_position_y, background_repeat,
2266    };
2267    use crate::values::specified::{AllowQuirks, Color, Position, PositionComponent};
2268
2269    impl From<background_origin::single_value::SpecifiedValue>
2270        for background_clip::single_value::SpecifiedValue
2271    {
2272        fn from(
2273            origin: background_origin::single_value::SpecifiedValue,
2274        ) -> background_clip::single_value::SpecifiedValue {
2275            match origin {
2276                background_origin::single_value::SpecifiedValue::ContentBox => {
2277                    background_clip::single_value::SpecifiedValue::ContentBox
2278                },
2279                background_origin::single_value::SpecifiedValue::PaddingBox => {
2280                    background_clip::single_value::SpecifiedValue::PaddingBox
2281                },
2282                background_origin::single_value::SpecifiedValue::BorderBox => {
2283                    background_clip::single_value::SpecifiedValue::BorderBox
2284                },
2285            }
2286        }
2287    }
2288
2289    pub fn parse_value<'i, 't>(
2290        context: &ParserContext,
2291        input: &mut Parser<'i, 't>,
2292    ) -> Result<Longhands, ParseError<'i>> {
2293        let mut background_color = None;
2294
2295        let mut background_image = Vec::with_capacity(1);
2296        let mut background_position_x = Vec::with_capacity(1);
2297        let mut background_position_y = Vec::with_capacity(1);
2298        let mut background_repeat = Vec::with_capacity(1);
2299        let mut background_size = Vec::with_capacity(1);
2300        let mut background_attachment = Vec::with_capacity(1);
2301        let mut background_origin = Vec::with_capacity(1);
2302        let mut background_clip = Vec::with_capacity(1);
2303        input.parse_comma_separated(|input| {
2304            if background_color.is_some() {
2305                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2306            }
2307
2308            let mut image = None;
2309            let mut position = None;
2310            let mut repeat = None;
2311            let mut size = None;
2312            let mut attachment = None;
2313            let mut origin = None;
2314            let mut clip = None;
2315            let mut parsed = 0;
2316            loop {
2317                parsed += 1;
2318                try_parse_one!(context, input, background_color, Color::parse);
2319                if position.is_none() {
2320                    if let Ok(value) = input.try_parse(|input| {
2321                        Position::parse_three_value_quirky(context, input, AllowQuirks::No)
2322                    }) {
2323                        position = Some(value);
2324
2325                        size = input
2326                            .try_parse(|input| {
2327                                input.expect_delim('/')?;
2328                                background_size::single_value::parse(context, input)
2329                            })
2330                            .ok();
2331
2332                        continue;
2333                    }
2334                }
2335                try_parse_one!(context, input, image, background_image::single_value::parse);
2336                try_parse_one!(
2337                    context,
2338                    input,
2339                    repeat,
2340                    background_repeat::single_value::parse
2341                );
2342                try_parse_one!(
2343                    context,
2344                    input,
2345                    attachment,
2346                    background_attachment::single_value::parse
2347                );
2348                try_parse_one!(
2349                    context,
2350                    input,
2351                    origin,
2352                    background_origin::single_value::parse
2353                );
2354                try_parse_one!(context, input, clip, background_clip::single_value::parse);
2355                parsed -= 1;
2356                break;
2357            }
2358            if parsed == 0 {
2359                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2360            }
2361            if clip.is_none() {
2362                if let Some(origin) = origin {
2363                    clip = Some(background_clip::single_value::SpecifiedValue::from(origin));
2364                }
2365            }
2366            if let Some(position) = position {
2367                background_position_x.push(position.horizontal);
2368                background_position_y.push(position.vertical);
2369            } else {
2370                background_position_x.push(PositionComponent::zero());
2371                background_position_y.push(PositionComponent::zero());
2372            }
2373            if let Some(bg_image) = image {
2374                background_image.push(bg_image);
2375            } else {
2376                background_image
2377                    .push(background_image::single_value::get_initial_specified_value());
2378            }
2379            if let Some(bg_repeat) = repeat {
2380                background_repeat.push(bg_repeat);
2381            } else {
2382                background_repeat
2383                    .push(background_repeat::single_value::get_initial_specified_value());
2384            }
2385            if let Some(bg_size) = size {
2386                background_size.push(bg_size);
2387            } else {
2388                background_size.push(background_size::single_value::get_initial_specified_value());
2389            }
2390            if let Some(bg_attachment) = attachment {
2391                background_attachment.push(bg_attachment);
2392            } else {
2393                background_attachment
2394                    .push(background_attachment::single_value::get_initial_specified_value());
2395            }
2396            if let Some(bg_origin) = origin {
2397                background_origin.push(bg_origin);
2398            } else {
2399                background_origin
2400                    .push(background_origin::single_value::get_initial_specified_value());
2401            }
2402            if let Some(bg_clip) = clip {
2403                background_clip.push(bg_clip);
2404            } else {
2405                background_clip.push(background_clip::single_value::get_initial_specified_value());
2406            }
2407            Ok(())
2408        })?;
2409
2410        Ok(expanded! {
2411            background_color: background_color.unwrap_or(Color::transparent()),
2412            background_image: background_image::SpecifiedValue(background_image.into()),
2413            background_position_x: background_position_x::SpecifiedValue(background_position_x.into()),
2414            background_position_y: background_position_y::SpecifiedValue(background_position_y.into()),
2415            background_repeat: background_repeat::SpecifiedValue(background_repeat.into()),
2416            background_size: background_size::SpecifiedValue(background_size.into()),
2417            background_attachment: background_attachment::SpecifiedValue(background_attachment.into()),
2418            background_origin: background_origin::SpecifiedValue(background_origin.into()),
2419            background_clip: background_clip::SpecifiedValue(background_clip.into()),
2420        })
2421    }
2422
2423    impl<'a> ToCss for LonghandsToSerialize<'a> {
2424        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2425        where
2426            W: fmt::Write,
2427        {
2428            let len = self.background_image.0.len();
2429            if len == 0 {
2430                return Ok(());
2431            }
2432            if len != self.background_image.0.len() {
2433                return Ok(());
2434            }
2435            if len != self.background_position_x.0.len() {
2436                return Ok(());
2437            }
2438            if len != self.background_position_y.0.len() {
2439                return Ok(());
2440            }
2441            if len != self.background_size.0.len() {
2442                return Ok(());
2443            }
2444            if len != self.background_repeat.0.len() {
2445                return Ok(());
2446            }
2447            if len != self.background_origin.0.len() {
2448                return Ok(());
2449            }
2450            if len != self.background_clip.0.len() {
2451                return Ok(());
2452            }
2453            if len != self.background_attachment.0.len() {
2454                return Ok(());
2455            }
2456
2457            for i in 0..len {
2458                let image = &self.background_image.0[i];
2459                let position_x = &self.background_position_x.0[i];
2460                let position_y = &self.background_position_y.0[i];
2461                let repeat = &self.background_repeat.0[i];
2462                let size = &self.background_size.0[i];
2463                let attachment = &self.background_attachment.0[i];
2464                let origin = &self.background_origin.0[i];
2465                let clip = &self.background_clip.0[i];
2466
2467                if i != 0 {
2468                    dest.write_str(", ")?;
2469                }
2470
2471                let mut writer = SequenceWriter::new(dest, " ");
2472                if *image != background_image::single_value::get_initial_specified_value() {
2473                    writer.item(image)?;
2474                }
2475
2476                if *position_x != PositionComponent::zero()
2477                    || *position_y != PositionComponent::zero()
2478                    || *size != background_size::single_value::get_initial_specified_value()
2479                {
2480                    writer.write_item(|dest| {
2481                        Position {
2482                            horizontal: position_x.clone(),
2483                            vertical: position_y.clone(),
2484                        }
2485                        .to_css(dest)?;
2486                        if *size != background_size::single_value::get_initial_specified_value() {
2487                            dest.write_str(" / ")?;
2488                            size.to_css(dest)?;
2489                        }
2490                        Ok(())
2491                    })?;
2492                }
2493                if *repeat != background_repeat::single_value::get_initial_specified_value() {
2494                    writer.item(repeat)?;
2495                }
2496                if *attachment != background_attachment::single_value::get_initial_specified_value()
2497                {
2498                    writer.item(attachment)?;
2499                }
2500
2501                if *origin != Origin::PaddingBox || *clip != Clip::BorderBox {
2502                    writer.item(origin)?;
2503                    if *clip != From::from(*origin) {
2504                        writer.item(clip)?;
2505                    }
2506                }
2507
2508                if i == len - 1 {
2509                    if *self.background_color != background_color::get_initial_specified_value() {
2510                        writer.item(self.background_color)?;
2511                    }
2512                }
2513
2514                if !writer.has_written() {
2515                    image.to_css(dest)?;
2516                }
2517            }
2518
2519            Ok(())
2520        }
2521    }
2522}
2523
2524pub mod font {
2525    pub use crate::properties::generated::shorthands::font::*;
2526
2527    use super::*;
2528    #[cfg(feature = "gecko")]
2529    use crate::properties::longhands::{
2530        font_family, font_feature_settings, font_kerning, font_language_override, font_size,
2531        font_size_adjust, font_variant_alternates, font_variant_east_asian, font_variant_emoji,
2532        font_variant_ligatures, font_variant_numeric, font_variant_position,
2533    };
2534    use crate::properties::longhands::{
2535        font_optical_sizing, font_stretch, font_style, font_variant_caps, font_variation_settings,
2536        font_weight,
2537    };
2538    #[cfg(feature = "gecko")]
2539    use crate::values::specified::font::SystemFont;
2540    use crate::values::specified::font::{
2541        FontFamily, FontSize, FontStretch, FontStretchKeyword, FontStyle, FontWeight, LineHeight,
2542    };
2543
2544    pub fn parse_value<'i, 't>(
2545        context: &ParserContext,
2546        input: &mut Parser<'i, 't>,
2547    ) -> Result<Longhands, ParseError<'i>> {
2548        let mut nb_normals = 0;
2549        let mut style = None;
2550        let mut variant_caps = None;
2551        let mut weight = None;
2552        let mut stretch = None;
2553        #[cfg(feature = "gecko")]
2554        if let Ok(sys) = input.try_parse(|i| SystemFont::parse(context, i)) {
2555            return Ok(Longhands {
2556                font_family: font_family::SpecifiedValue::system_font(sys),
2557                font_size: font_size::SpecifiedValue::system_font(sys),
2558                font_style: font_style::SpecifiedValue::system_font(sys),
2559                font_stretch: font_stretch::SpecifiedValue::system_font(sys),
2560                font_weight: font_weight::SpecifiedValue::system_font(sys),
2561                line_height: LineHeight::normal(),
2562                font_kerning: font_kerning::get_initial_specified_value(),
2563                font_language_override: font_language_override::get_initial_specified_value(),
2564                font_size_adjust: font_size_adjust::get_initial_specified_value(),
2565                font_variant_alternates: font_variant_alternates::get_initial_specified_value(),
2566                font_variant_east_asian: font_variant_east_asian::get_initial_specified_value(),
2567                font_variant_emoji: font_variant_emoji::get_initial_specified_value(),
2568                font_variant_ligatures: font_variant_ligatures::get_initial_specified_value(),
2569                font_variant_numeric: font_variant_numeric::get_initial_specified_value(),
2570                font_variant_position: font_variant_position::get_initial_specified_value(),
2571                font_feature_settings: font_feature_settings::get_initial_specified_value(),
2572                font_optical_sizing: font_optical_sizing::get_initial_specified_value(),
2573                font_variant_caps: font_variant_caps::get_initial_specified_value(),
2574                font_variation_settings: font_variation_settings::get_initial_specified_value(),
2575            });
2576        }
2577
2578        let size;
2579        loop {
2580            if input
2581                .try_parse(|input| input.expect_ident_matching("normal"))
2582                .is_ok()
2583            {
2584                nb_normals += 1;
2585                continue;
2586            }
2587            try_parse_one!(context, input, style, font_style::parse);
2588            try_parse_one!(context, input, weight, font_weight::parse);
2589            if variant_caps.is_none() {
2590                if input
2591                    .try_parse(|input| input.expect_ident_matching("small-caps"))
2592                    .is_ok()
2593                {
2594                    variant_caps = Some(font_variant_caps::SpecifiedValue::SmallCaps);
2595                    continue;
2596                }
2597            }
2598            try_parse_one!(input, stretch, FontStretchKeyword::parse);
2599            size = FontSize::parse(context, input)?;
2600            break;
2601        }
2602
2603        let line_height = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
2604            Some(LineHeight::parse(context, input)?)
2605        } else {
2606            None
2607        };
2608
2609        #[inline]
2610        fn count<T>(opt: &Option<T>) -> u8 {
2611            if opt.is_some() {
2612                1
2613            } else {
2614                0
2615            }
2616        }
2617
2618        if (count(&style) + count(&weight) + count(&variant_caps) + count(&stretch) + nb_normals)
2619            > 4
2620        {
2621            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2622        }
2623
2624        let family = FontFamily::parse(context, input)?;
2625        let stretch = stretch.map(FontStretch::Keyword);
2626        Ok(Longhands {
2627            font_style: unwrap_or_initial!(font_style, style),
2628            font_weight: unwrap_or_initial!(font_weight, weight),
2629            font_stretch: unwrap_or_initial!(font_stretch, stretch),
2630            font_variant_caps: unwrap_or_initial!(font_variant_caps, variant_caps),
2631            font_size: size,
2632            line_height: line_height.unwrap_or(LineHeight::normal()),
2633            font_family: family,
2634            font_optical_sizing: font_optical_sizing::get_initial_specified_value(),
2635            font_variation_settings: font_variation_settings::get_initial_specified_value(),
2636            #[cfg(feature = "gecko")]
2637            font_kerning: font_kerning::get_initial_specified_value(),
2638            #[cfg(feature = "gecko")]
2639            font_language_override: font_language_override::get_initial_specified_value(),
2640            #[cfg(feature = "gecko")]
2641            font_size_adjust: font_size_adjust::get_initial_specified_value(),
2642            #[cfg(feature = "gecko")]
2643            font_variant_alternates: font_variant_alternates::get_initial_specified_value(),
2644            #[cfg(feature = "gecko")]
2645            font_variant_east_asian: font_variant_east_asian::get_initial_specified_value(),
2646            #[cfg(feature = "gecko")]
2647            font_variant_emoji: font_variant_emoji::get_initial_specified_value(),
2648            #[cfg(feature = "gecko")]
2649            font_variant_ligatures: font_variant_ligatures::get_initial_specified_value(),
2650            #[cfg(feature = "gecko")]
2651            font_variant_numeric: font_variant_numeric::get_initial_specified_value(),
2652            #[cfg(feature = "gecko")]
2653            font_variant_position: font_variant_position::get_initial_specified_value(),
2654            #[cfg(feature = "gecko")]
2655            font_feature_settings: font_feature_settings::get_initial_specified_value(),
2656        })
2657    }
2658
2659    #[cfg(feature = "gecko")]
2660    enum CheckSystemResult {
2661        AllSystem(SystemFont),
2662        SomeSystem,
2663        None,
2664    }
2665
2666    impl<'a> ToCss for LonghandsToSerialize<'a> {
2667        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2668        where
2669            W: fmt::Write,
2670        {
2671            #[cfg(feature = "gecko")]
2672            match self.check_system() {
2673                CheckSystemResult::AllSystem(sys) => return sys.to_css(dest),
2674                CheckSystemResult::SomeSystem => return Ok(()),
2675                CheckSystemResult::None => {},
2676            }
2677
2678            if let Some(v) = self.font_optical_sizing {
2679                if v != &font_optical_sizing::get_initial_specified_value() {
2680                    return Ok(());
2681                }
2682            }
2683            if let Some(v) = self.font_variation_settings {
2684                if v != &font_variation_settings::get_initial_specified_value() {
2685                    return Ok(());
2686                }
2687            }
2688            #[cfg(feature = "gecko")]
2689            if let Some(v) = self.font_variant_emoji {
2690                if v != &font_variant_emoji::get_initial_specified_value() {
2691                    return Ok(());
2692                }
2693            }
2694
2695            #[cfg(feature = "gecko")]
2696            if self.font_kerning != &font_kerning::get_initial_specified_value() {
2697                return Ok(());
2698            }
2699            #[cfg(feature = "gecko")]
2700            if self.font_language_override != &font_language_override::get_initial_specified_value()
2701            {
2702                return Ok(());
2703            }
2704            #[cfg(feature = "gecko")]
2705            if self.font_size_adjust != &font_size_adjust::get_initial_specified_value() {
2706                return Ok(());
2707            }
2708            #[cfg(feature = "gecko")]
2709            if self.font_variant_alternates
2710                != &font_variant_alternates::get_initial_specified_value()
2711            {
2712                return Ok(());
2713            }
2714            #[cfg(feature = "gecko")]
2715            if self.font_variant_east_asian
2716                != &font_variant_east_asian::get_initial_specified_value()
2717            {
2718                return Ok(());
2719            }
2720            #[cfg(feature = "gecko")]
2721            if self.font_variant_ligatures != &font_variant_ligatures::get_initial_specified_value()
2722            {
2723                return Ok(());
2724            }
2725            #[cfg(feature = "gecko")]
2726            if self.font_variant_numeric != &font_variant_numeric::get_initial_specified_value() {
2727                return Ok(());
2728            }
2729            #[cfg(feature = "gecko")]
2730            if self.font_variant_position != &font_variant_position::get_initial_specified_value() {
2731                return Ok(());
2732            }
2733            #[cfg(feature = "gecko")]
2734            if self.font_feature_settings != &font_feature_settings::get_initial_specified_value() {
2735                return Ok(());
2736            }
2737
2738            let font_stretch = match *self.font_stretch {
2739                FontStretch::Keyword(kw) => kw,
2740                FontStretch::Stretch(percentage) => {
2741                    match FontStretchKeyword::from_percentage(percentage.0.get()) {
2742                        Some(kw) => kw,
2743                        None => return Ok(()),
2744                    }
2745                },
2746                FontStretch::System(..) => return Ok(()),
2747            };
2748
2749            if self.font_variant_caps != &font_variant_caps::get_initial_specified_value()
2750                && *self.font_variant_caps != font_variant_caps::SpecifiedValue::SmallCaps
2751            {
2752                return Ok(());
2753            }
2754
2755            if self.font_style != &font_style::get_initial_specified_value() {
2756                self.font_style.to_css(dest)?;
2757                dest.write_char(' ')?;
2758            }
2759            if self.font_variant_caps != &font_variant_caps::get_initial_specified_value() {
2760                self.font_variant_caps.to_css(dest)?;
2761                dest.write_char(' ')?;
2762            }
2763
2764            if self.font_weight != &FontWeight::normal()
2765                && self.font_weight != &FontWeight::from_gecko_keyword(400)
2766            {
2767                self.font_weight.to_css(dest)?;
2768                dest.write_char(' ')?;
2769            }
2770
2771            if font_stretch != FontStretchKeyword::Normal {
2772                font_stretch.to_css(dest)?;
2773                dest.write_char(' ')?;
2774            }
2775
2776            self.font_size.to_css(dest)?;
2777
2778            if *self.line_height != LineHeight::normal() {
2779                dest.write_str(" / ")?;
2780                self.line_height.to_css(dest)?;
2781            }
2782
2783            dest.write_char(' ')?;
2784            self.font_family.to_css(dest)?;
2785
2786            Ok(())
2787        }
2788    }
2789
2790    impl<'a> LonghandsToSerialize<'a> {
2791        #[cfg(feature = "gecko")]
2792        fn check_system(&self) -> CheckSystemResult {
2793            let mut sys = None;
2794            let mut all = true;
2795
2796            macro_rules! check {
2797                ($v:expr) => {
2798                    match $v.get_system() {
2799                        Some(s) => {
2800                            debug_assert!(sys.is_none() || s == sys.unwrap());
2801                            sys = Some(s);
2802                        },
2803                        None => {
2804                            all = false;
2805                        },
2806                    }
2807                };
2808                ($e:expr, $($es:expr),+) => { check!($e); check!($($es),*); };
2809            }
2810
2811            check!(
2812                self.font_family,
2813                self.font_size,
2814                self.font_style,
2815                self.font_stretch,
2816                self.font_weight
2817            );
2818
2819            if self.line_height != &LineHeight::normal() {
2820                all = false
2821            }
2822            if all {
2823                CheckSystemResult::AllSystem(sys.unwrap())
2824            } else if sys.is_some() {
2825                CheckSystemResult::SomeSystem
2826            } else {
2827                CheckSystemResult::None
2828            }
2829        }
2830    }
2831
2832    impl SpecifiedValueInfo for Longhands {
2833        const SUPPORTED_TYPES: u8 = FontStyle::SUPPORTED_TYPES
2834            | FontWeight::SUPPORTED_TYPES
2835            | FontStretch::SUPPORTED_TYPES
2836            | font_variant_caps::SpecifiedValue::SUPPORTED_TYPES
2837            | FontSize::SUPPORTED_TYPES
2838            | FontFamily::SUPPORTED_TYPES;
2839
2840        fn collect_completion_keywords(f: KeywordsCollectFn) {
2841            FontStyle::collect_completion_keywords(f);
2842            FontWeight::collect_completion_keywords(f);
2843            FontStretch::collect_completion_keywords(f);
2844            font_variant_caps::SpecifiedValue::collect_completion_keywords(f);
2845            FontSize::collect_completion_keywords(f);
2846            FontFamily::collect_completion_keywords(f);
2847
2848            #[cfg(feature = "gecko")]
2849            SystemFont::collect_completion_keywords(f);
2850        }
2851    }
2852}
2853
2854pub mod font_variant {
2855    pub use crate::properties::generated::shorthands::font_variant::*;
2856
2857    use super::*;
2858    use crate::properties::longhands::font_variant_caps;
2859    #[cfg(feature = "gecko")]
2860    use crate::properties::longhands::{
2861        font_variant_alternates, font_variant_east_asian, font_variant_emoji,
2862        font_variant_ligatures, font_variant_numeric, font_variant_position,
2863    };
2864    #[allow(unused_imports)]
2865    use crate::values::specified::FontVariantLigatures;
2866
2867    pub fn parse_value<'i, 't>(
2868        context: &ParserContext,
2869        input: &mut Parser<'i, 't>,
2870    ) -> Result<Longhands, ParseError<'i>> {
2871        #[cfg(feature = "gecko")]
2872        let mut ligatures = None;
2873        let mut caps = None;
2874        #[cfg(feature = "gecko")]
2875        let mut alternates = None;
2876        #[cfg(feature = "gecko")]
2877        let mut numeric = None;
2878        #[cfg(feature = "gecko")]
2879        let mut east_asian = None;
2880        #[cfg(feature = "gecko")]
2881        let mut position = None;
2882        #[cfg(feature = "gecko")]
2883        let mut emoji = None;
2884
2885        if input
2886            .try_parse(|input| input.expect_ident_matching("normal"))
2887            .is_ok()
2888        {
2889        } else if input
2890            .try_parse(|input| input.expect_ident_matching("none"))
2891            .is_ok()
2892        {
2893            #[cfg(feature = "gecko")]
2894            {
2895                ligatures = Some(FontVariantLigatures::NONE);
2896            }
2897        } else {
2898            let mut parsed = 0;
2899            loop {
2900                parsed += 1;
2901                if input
2902                    .try_parse(|input| input.expect_ident_matching("normal"))
2903                    .is_ok()
2904                    || input
2905                        .try_parse(|input| input.expect_ident_matching("none"))
2906                        .is_ok()
2907                {
2908                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2909                }
2910                #[cfg(feature = "gecko")]
2911                try_parse_one!(context, input, ligatures, font_variant_ligatures::parse);
2912                try_parse_one!(context, input, caps, font_variant_caps::parse);
2913                #[cfg(feature = "gecko")]
2914                try_parse_one!(context, input, alternates, font_variant_alternates::parse);
2915                #[cfg(feature = "gecko")]
2916                try_parse_one!(context, input, numeric, font_variant_numeric::parse);
2917                #[cfg(feature = "gecko")]
2918                try_parse_one!(context, input, east_asian, font_variant_east_asian::parse);
2919                #[cfg(feature = "gecko")]
2920                try_parse_one!(context, input, position, font_variant_position::parse);
2921                #[cfg(feature = "gecko")]
2922                try_parse_one!(context, input, emoji, font_variant_emoji::parse);
2923                parsed -= 1;
2924                break;
2925            }
2926
2927            if parsed == 0 {
2928                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2929            }
2930        }
2931
2932        #[cfg(feature = "gecko")]
2933        return Ok(expanded! {
2934            font_variant_ligatures: unwrap_or_initial!(font_variant_ligatures, ligatures),
2935            font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),
2936            font_variant_alternates: unwrap_or_initial!(font_variant_alternates, alternates),
2937            font_variant_numeric: unwrap_or_initial!(font_variant_numeric, numeric),
2938            font_variant_east_asian: unwrap_or_initial!(font_variant_east_asian, east_asian),
2939            font_variant_position: unwrap_or_initial!(font_variant_position, position),
2940            font_variant_emoji: unwrap_or_initial!(font_variant_emoji, emoji),
2941        });
2942        #[cfg(feature = "servo")]
2943        return Ok(expanded! {
2944            font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),
2945        });
2946    }
2947
2948    impl<'a> ToCss for LonghandsToSerialize<'a> {
2949        #[allow(unused_assignments)]
2950        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2951        where
2952            W: fmt::Write,
2953        {
2954            #[cfg(feature = "gecko")]
2955            let has_none_ligatures = self.font_variant_ligatures == &FontVariantLigatures::NONE;
2956            #[cfg(feature = "servo")]
2957            let has_none_ligatures = false;
2958
2959            #[cfg(feature = "gecko")]
2960            const TOTAL_SUBPROPS: usize = 7;
2961            #[cfg(feature = "servo")]
2962            const TOTAL_SUBPROPS: usize = 1;
2963            let mut nb_normals = 0;
2964            macro_rules! count_normal {
2965                ($e: expr, $p: ident) => {
2966                    if *$e == $p::get_initial_specified_value() {
2967                        nb_normals += 1;
2968                    }
2969                };
2970                ($v: ident) => {
2971                    count_normal!(self.$v, $v);
2972                };
2973            }
2974            #[cfg(feature = "gecko")]
2975            count_normal!(font_variant_ligatures);
2976            count_normal!(font_variant_caps);
2977            #[cfg(feature = "gecko")]
2978            count_normal!(font_variant_alternates);
2979            #[cfg(feature = "gecko")]
2980            count_normal!(font_variant_numeric);
2981            #[cfg(feature = "gecko")]
2982            count_normal!(font_variant_east_asian);
2983            #[cfg(feature = "gecko")]
2984            count_normal!(font_variant_position);
2985            #[cfg(feature = "gecko")]
2986            if let Some(value) = self.font_variant_emoji {
2987                if value == &font_variant_emoji::get_initial_specified_value() {
2988                    nb_normals += 1;
2989                }
2990            } else {
2991                nb_normals += 1;
2992            }
2993
2994            if nb_normals == TOTAL_SUBPROPS {
2995                return dest.write_str("normal");
2996            }
2997            if has_none_ligatures {
2998                if nb_normals == TOTAL_SUBPROPS - 1 {
2999                    dest.write_str("none")?;
3000                }
3001                return Ok(());
3002            }
3003
3004            let mut writer = SequenceWriter::new(dest, " ");
3005            macro_rules! write {
3006                ($e: expr, $p: ident) => {
3007                    if *$e != $p::get_initial_specified_value() {
3008                        writer.item($e)?;
3009                    }
3010                };
3011                ($v: ident) => {
3012                    write!(self.$v, $v);
3013                };
3014            }
3015
3016            #[cfg(feature = "gecko")]
3017            write!(font_variant_ligatures);
3018            write!(font_variant_caps);
3019            #[cfg(feature = "gecko")]
3020            write!(font_variant_alternates);
3021            #[cfg(feature = "gecko")]
3022            write!(font_variant_numeric);
3023            #[cfg(feature = "gecko")]
3024            write!(font_variant_east_asian);
3025            #[cfg(feature = "gecko")]
3026            write!(font_variant_position);
3027            #[cfg(feature = "gecko")]
3028            if let Some(v) = self.font_variant_emoji {
3029                write!(v, font_variant_emoji);
3030            }
3031            Ok(())
3032        }
3033    }
3034}
3035
3036#[cfg(feature = "gecko")]
3037pub mod font_synthesis {
3038    pub use crate::properties::generated::shorthands::font_synthesis::*;
3039
3040    use super::*;
3041    use crate::values::specified::{FontSynthesis, FontSynthesisStyle};
3042
3043    pub fn parse_value<'i, 't>(
3044        _context: &ParserContext,
3045        input: &mut Parser<'i, 't>,
3046    ) -> Result<Longhands, ParseError<'i>> {
3047        let mut weight = FontSynthesis::None;
3048        let mut style = FontSynthesisStyle::None;
3049        let mut small_caps = FontSynthesis::None;
3050        let mut position = FontSynthesis::None;
3051
3052        if !input
3053            .try_parse(|input| input.expect_ident_matching("none"))
3054            .is_ok()
3055        {
3056            let mut has_custom_value = false;
3057            while !input.is_exhausted() {
3058                try_match_ident_ignore_ascii_case! { input,
3059                    "weight" if weight == FontSynthesis::None => {
3060                        has_custom_value = true;
3061                        weight = FontSynthesis::Auto;
3062                        continue;
3063                    },
3064                    "style" if style == FontSynthesisStyle::None => {
3065                        has_custom_value = true;
3066                        style = FontSynthesisStyle::Auto;
3067                        continue;
3068                    },
3069                    "small-caps" if small_caps == FontSynthesis::None => {
3070                        has_custom_value = true;
3071                        small_caps = FontSynthesis::Auto;
3072                        continue;
3073                    },
3074                    "position" if position == FontSynthesis::None => {
3075                        has_custom_value = true;
3076                        position = FontSynthesis::Auto;
3077                        continue;
3078                    },
3079                    "oblique-only" if style == FontSynthesisStyle::None => {
3080                        has_custom_value = true;
3081                        style = FontSynthesisStyle::ObliqueOnly;
3082                        continue;
3083                    },
3084                }
3085            }
3086            if !has_custom_value {
3087                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3088            }
3089        }
3090
3091        Ok(expanded! {
3092            font_synthesis_weight: weight,
3093            font_synthesis_style: style,
3094            font_synthesis_small_caps: small_caps,
3095            font_synthesis_position: position,
3096        })
3097    }
3098
3099    impl<'a> ToCss for LonghandsToSerialize<'a> {
3100        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3101        where
3102            W: fmt::Write,
3103        {
3104            let mut writer = SequenceWriter::new(dest, " ");
3105            if self.font_synthesis_weight == &FontSynthesis::Auto {
3106                writer.raw_item("weight")?;
3107            }
3108            if self.font_synthesis_style != &FontSynthesisStyle::None {
3109                if self.font_synthesis_style == &FontSynthesisStyle::Auto {
3110                    writer.raw_item("style")?;
3111                } else {
3112                    writer.raw_item("oblique-only")?;
3113                }
3114            }
3115            if self.font_synthesis_small_caps == &FontSynthesis::Auto {
3116                writer.raw_item("small-caps")?;
3117            }
3118            if self.font_synthesis_position == &FontSynthesis::Auto {
3119                writer.raw_item("position")?;
3120            }
3121            if !writer.has_written() {
3122                writer.raw_item("none")?;
3123            }
3124            Ok(())
3125        }
3126    }
3127
3128    // The shorthand takes the sub-property names of the longhands, and not the
3129    // 'auto' keyword like they do, so we can't automatically derive this.
3130    impl SpecifiedValueInfo for Longhands {
3131        fn collect_completion_keywords(f: KeywordsCollectFn) {
3132            f(&[
3133                "none",
3134                "oblique-only",
3135                "small-caps",
3136                "position",
3137                "style",
3138                "weight",
3139            ]);
3140        }
3141    }
3142}
3143
3144#[cfg(feature = "gecko")]
3145pub mod text_box {
3146    pub use crate::properties::generated::shorthands::text_box::*;
3147
3148    use super::*;
3149    use crate::values::specified::{TextBoxEdge, TextBoxTrim};
3150
3151    pub fn parse_value<'i>(
3152        context: &ParserContext,
3153        input: &mut Parser<'i, '_>,
3154    ) -> Result<Longhands, ParseError<'i>> {
3155        let mut trim = None;
3156        let mut edge = None;
3157
3158        if input
3159            .try_parse(|input| input.expect_ident_matching("normal"))
3160            .is_ok()
3161        {
3162            return Ok(Longhands {
3163                text_box_trim: TextBoxTrim::NONE,
3164                text_box_edge: TextBoxEdge::Auto,
3165            });
3166        }
3167
3168        loop {
3169            try_parse_one!(context, input, trim, TextBoxTrim::parse);
3170            try_parse_one!(context, input, edge, TextBoxEdge::parse);
3171            break;
3172        }
3173
3174        if trim.is_none() && edge.is_none() {
3175            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3176        }
3177
3178        // From https://drafts.csswg.org/css-inline-3/#text-box-shorthand:
3179        // > Omitting the 'text-box-trim' value sets it to 'trim-both'
3180        // > (not the initial value), while omitting the 'text-box-edge'
3181        // > value sets it to auto (the initial value).
3182        Ok(Longhands {
3183            text_box_trim: trim.unwrap_or(TextBoxTrim::TRIM_BOTH),
3184            text_box_edge: edge.unwrap_or(TextBoxEdge::Auto),
3185        })
3186    }
3187
3188    impl<'a> ToCss for LonghandsToSerialize<'a> {
3189        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3190        where
3191            W: fmt::Write,
3192        {
3193            if *self.text_box_trim == TextBoxTrim::NONE && *self.text_box_edge == TextBoxEdge::Auto
3194            {
3195                return dest.write_str("normal");
3196            }
3197
3198            let mut writer = SequenceWriter::new(dest, " ");
3199            if *self.text_box_trim != specified::TextBoxTrim::TRIM_BOTH {
3200                writer.item(self.text_box_trim)?;
3201            }
3202            if *self.text_box_edge != specified::TextBoxEdge::Auto {
3203                writer.item(self.text_box_edge)?;
3204            }
3205            if !writer.has_written() {
3206                self.text_box_trim.to_css(dest)?;
3207            }
3208            Ok(())
3209        }
3210    }
3211}
3212
3213#[cfg(feature = "gecko")]
3214pub mod text_emphasis {
3215    pub use crate::properties::generated::shorthands::text_emphasis::*;
3216
3217    use super::*;
3218    use crate::properties::longhands::{text_emphasis_color, text_emphasis_style};
3219
3220    pub fn parse_value<'i, 't>(
3221        context: &ParserContext,
3222        input: &mut Parser<'i, 't>,
3223    ) -> Result<Longhands, ParseError<'i>> {
3224        let mut color = None;
3225        let mut style = None;
3226        let mut parsed = 0;
3227        loop {
3228            parsed += 1;
3229            try_parse_one!(context, input, color, text_emphasis_color::parse);
3230            try_parse_one!(context, input, style, text_emphasis_style::parse);
3231            parsed -= 1;
3232            break;
3233        }
3234        if parsed == 0 {
3235            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3236        }
3237        Ok(expanded! {
3238            text_emphasis_color: unwrap_or_initial!(text_emphasis_color, color),
3239            text_emphasis_style: unwrap_or_initial!(text_emphasis_style, style),
3240        })
3241    }
3242}
3243
3244pub mod text_decoration {
3245    pub use crate::properties::generated::shorthands::text_decoration::*;
3246
3247    use super::*;
3248    #[cfg(feature = "gecko")]
3249    use crate::properties::longhands::text_decoration_thickness;
3250    use crate::properties::longhands::{
3251        text_decoration_color, text_decoration_line, text_decoration_style,
3252    };
3253
3254    pub fn parse_value<'i, 't>(
3255        context: &ParserContext,
3256        input: &mut Parser<'i, 't>,
3257    ) -> Result<Longhands, ParseError<'i>> {
3258        let mut line = None;
3259        let mut style = None;
3260        let mut color = None;
3261        #[cfg(feature = "gecko")]
3262        let mut thickness = None;
3263
3264        let mut parsed = 0;
3265        loop {
3266            parsed += 1;
3267            try_parse_one!(context, input, line, text_decoration_line::parse);
3268            try_parse_one!(context, input, style, text_decoration_style::parse);
3269            try_parse_one!(context, input, color, text_decoration_color::parse);
3270            #[cfg(feature = "gecko")]
3271            try_parse_one!(context, input, thickness, text_decoration_thickness::parse);
3272            parsed -= 1;
3273            break;
3274        }
3275
3276        if parsed == 0 {
3277            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3278        }
3279
3280        #[cfg(feature = "gecko")]
3281        return Ok(expanded! {
3282            text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
3283            text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
3284            text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
3285            text_decoration_thickness: unwrap_or_initial!(text_decoration_thickness, thickness),
3286        });
3287        #[cfg(feature = "servo")]
3288        return Ok(expanded! {
3289            text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
3290            text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
3291            text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
3292        });
3293    }
3294
3295    impl<'a> ToCss for LonghandsToSerialize<'a> {
3296        #[allow(unused)]
3297        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3298        where
3299            W: fmt::Write,
3300        {
3301            use crate::values::specified::Color;
3302            use crate::values::specified::TextDecorationLine;
3303
3304            let is_solid_style =
3305                *self.text_decoration_style == text_decoration_style::SpecifiedValue::Solid;
3306            let is_current_color = *self.text_decoration_color == Color::CurrentColor;
3307            #[cfg(feature = "gecko")]
3308            let is_auto_thickness = self.text_decoration_thickness.is_auto();
3309            #[cfg(feature = "servo")]
3310            let is_auto_thickness = true;
3311            let is_none = *self.text_decoration_line == TextDecorationLine::none();
3312
3313            let mut writer = SequenceWriter::new(dest, " ");
3314            if (is_solid_style && is_current_color && is_auto_thickness) || !is_none {
3315                writer.item(self.text_decoration_line)?;
3316            }
3317            #[cfg(feature = "gecko")]
3318            if !is_auto_thickness {
3319                writer.item(self.text_decoration_thickness)?;
3320            }
3321            if !is_solid_style {
3322                writer.item(self.text_decoration_style)?;
3323            }
3324            if !is_current_color {
3325                writer.item(self.text_decoration_color)?;
3326            }
3327            Ok(())
3328        }
3329    }
3330}
3331
3332pub mod animation {
3333    pub use crate::properties::generated::shorthands::animation::*;
3334
3335    use super::*;
3336    use crate::properties::longhands::{
3337        animation_delay, animation_direction, animation_duration, animation_fill_mode,
3338        animation_iteration_count, animation_name, animation_play_state, animation_timeline,
3339        animation_timing_function,
3340    };
3341
3342    pub fn parse_value<'i, 't>(
3343        context: &ParserContext,
3344        input: &mut Parser<'i, 't>,
3345    ) -> Result<Longhands, ParseError<'i>> {
3346        struct SingleAnimation {
3347            animation_name: animation_name::SingleSpecifiedValue,
3348            animation_duration: animation_duration::SingleSpecifiedValue,
3349            animation_timing_function: animation_timing_function::SingleSpecifiedValue,
3350            animation_delay: animation_delay::SingleSpecifiedValue,
3351            animation_iteration_count: animation_iteration_count::SingleSpecifiedValue,
3352            animation_direction: animation_direction::SingleSpecifiedValue,
3353            animation_fill_mode: animation_fill_mode::SingleSpecifiedValue,
3354            animation_play_state: animation_play_state::SingleSpecifiedValue,
3355        }
3356
3357        fn parse_one_animation<'i, 't>(
3358            context: &ParserContext,
3359            input: &mut Parser<'i, 't>,
3360        ) -> Result<SingleAnimation, ParseError<'i>> {
3361            let mut name = None;
3362            let mut duration = None;
3363            let mut timing_function = None;
3364            let mut delay = None;
3365            let mut iteration_count = None;
3366            let mut direction = None;
3367            let mut fill_mode = None;
3368            let mut play_state = None;
3369
3370            let mut parsed = 0;
3371            loop {
3372                parsed += 1;
3373                try_parse_one!(
3374                    context,
3375                    input,
3376                    duration,
3377                    animation_duration::single_value::parse
3378                );
3379                try_parse_one!(
3380                    context,
3381                    input,
3382                    timing_function,
3383                    animation_timing_function::single_value::parse
3384                );
3385                try_parse_one!(context, input, delay, animation_delay::single_value::parse);
3386                try_parse_one!(
3387                    context,
3388                    input,
3389                    iteration_count,
3390                    animation_iteration_count::single_value::parse
3391                );
3392                try_parse_one!(
3393                    context,
3394                    input,
3395                    direction,
3396                    animation_direction::single_value::parse
3397                );
3398                try_parse_one!(
3399                    context,
3400                    input,
3401                    fill_mode,
3402                    animation_fill_mode::single_value::parse
3403                );
3404                try_parse_one!(
3405                    context,
3406                    input,
3407                    play_state,
3408                    animation_play_state::single_value::parse
3409                );
3410                try_parse_one!(context, input, name, animation_name::single_value::parse);
3411                parsed -= 1;
3412                break;
3413            }
3414
3415            if parsed == 0 {
3416                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3417            }
3418            Ok(SingleAnimation {
3419                animation_name: name
3420                    .unwrap_or_else(animation_name::single_value::get_initial_specified_value),
3421                animation_duration: duration
3422                    .unwrap_or_else(animation_duration::single_value::get_initial_specified_value),
3423                animation_timing_function: timing_function.unwrap_or_else(
3424                    animation_timing_function::single_value::get_initial_specified_value,
3425                ),
3426                animation_delay: delay
3427                    .unwrap_or_else(animation_delay::single_value::get_initial_specified_value),
3428                animation_iteration_count: iteration_count.unwrap_or_else(
3429                    animation_iteration_count::single_value::get_initial_specified_value,
3430                ),
3431                animation_direction: direction
3432                    .unwrap_or_else(animation_direction::single_value::get_initial_specified_value),
3433                animation_fill_mode: fill_mode
3434                    .unwrap_or_else(animation_fill_mode::single_value::get_initial_specified_value),
3435                animation_play_state: play_state.unwrap_or_else(
3436                    animation_play_state::single_value::get_initial_specified_value,
3437                ),
3438            })
3439        }
3440
3441        let mut names = vec![];
3442        let mut durations = vec![];
3443        let mut timing_functions = vec![];
3444        let mut delays = vec![];
3445        let mut iteration_counts = vec![];
3446        let mut directions = vec![];
3447        let mut fill_modes = vec![];
3448        let mut play_states = vec![];
3449
3450        let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?;
3451        for result in results.into_iter() {
3452            names.push(result.animation_name);
3453            durations.push(result.animation_duration);
3454            timing_functions.push(result.animation_timing_function);
3455            delays.push(result.animation_delay);
3456            iteration_counts.push(result.animation_iteration_count);
3457            directions.push(result.animation_direction);
3458            fill_modes.push(result.animation_fill_mode);
3459            play_states.push(result.animation_play_state);
3460        }
3461
3462        Ok(expanded! {
3463            animation_name: animation_name::SpecifiedValue(names.into()),
3464            animation_duration: animation_duration::SpecifiedValue(durations.into()),
3465            animation_timing_function: animation_timing_function::SpecifiedValue(timing_functions.into()),
3466            animation_delay: animation_delay::SpecifiedValue(delays.into()),
3467            animation_iteration_count: animation_iteration_count::SpecifiedValue(iteration_counts.into()),
3468            animation_direction: animation_direction::SpecifiedValue(directions.into()),
3469            animation_fill_mode: animation_fill_mode::SpecifiedValue(fill_modes.into()),
3470            animation_play_state: animation_play_state::SpecifiedValue(play_states.into()),
3471            animation_timeline: animation_timeline::SpecifiedValue(
3472                vec![animation_timeline::single_value::get_initial_specified_value()].into()
3473            ),
3474        })
3475    }
3476
3477    impl<'a> ToCss for LonghandsToSerialize<'a> {
3478        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3479        where
3480            W: fmt::Write,
3481        {
3482            use crate::values::specified::easing::TimingFunction;
3483            use crate::values::specified::{
3484                AnimationDirection, AnimationFillMode, AnimationPlayState,
3485            };
3486            use crate::Zero;
3487            use style_traits::values::SequenceWriter;
3488
3489            let len = self.animation_name.0.len();
3490            if len == 0 {
3491                return Ok(());
3492            }
3493
3494            if len != self.animation_duration.0.len() {
3495                return Ok(());
3496            }
3497            if len != self.animation_timing_function.0.len() {
3498                return Ok(());
3499            }
3500            if len != self.animation_delay.0.len() {
3501                return Ok(());
3502            }
3503            if len != self.animation_iteration_count.0.len() {
3504                return Ok(());
3505            }
3506            if len != self.animation_direction.0.len() {
3507                return Ok(());
3508            }
3509            if len != self.animation_fill_mode.0.len() {
3510                return Ok(());
3511            }
3512            if len != self.animation_play_state.0.len() {
3513                return Ok(());
3514            }
3515
3516            if self
3517                .animation_timeline
3518                .map_or(false, |v| v.0.len() != 1 || !v.0[0].is_auto())
3519            {
3520                return Ok(());
3521            }
3522
3523            for i in 0..len {
3524                if i != 0 {
3525                    dest.write_str(", ")?;
3526                }
3527
3528                let has_duration = !self.animation_duration.0[i].is_auto()
3529                    && !self.animation_duration.0[i].is_zero();
3530                let has_timing_function = !self.animation_timing_function.0[i].is_ease();
3531                let has_delay = !self.animation_delay.0[i].is_zero();
3532                let has_iteration_count = !self.animation_iteration_count.0[i].is_one();
3533                let has_direction =
3534                    !matches!(self.animation_direction.0[i], AnimationDirection::Normal);
3535                let has_fill_mode =
3536                    !matches!(self.animation_fill_mode.0[i], AnimationFillMode::None);
3537                let has_play_state =
3538                    !matches!(self.animation_play_state.0[i], AnimationPlayState::Running);
3539                let animation_name = &self.animation_name.0[i];
3540                let has_name = !animation_name.is_none();
3541
3542                let mut writer = SequenceWriter::new(dest, " ");
3543
3544                if has_duration || has_delay {
3545                    writer.item(&self.animation_duration.0[i])?;
3546                }
3547
3548                if has_timing_function || TimingFunction::match_keywords(animation_name) {
3549                    writer.item(&self.animation_timing_function.0[i])?;
3550                }
3551
3552                if has_delay {
3553                    writer.item(&self.animation_delay.0[i])?;
3554                }
3555                if has_iteration_count {
3556                    writer.item(&self.animation_iteration_count.0[i])?;
3557                }
3558
3559                if has_direction || AnimationDirection::match_keywords(animation_name) {
3560                    writer.item(&self.animation_direction.0[i])?;
3561                }
3562
3563                if has_fill_mode || AnimationFillMode::match_keywords(animation_name) {
3564                    writer.item(&self.animation_fill_mode.0[i])?;
3565                }
3566
3567                if has_play_state || AnimationPlayState::match_keywords(animation_name) {
3568                    writer.item(&self.animation_play_state.0[i])?;
3569                }
3570
3571                if has_name || !writer.has_written() {
3572                    writer.item(animation_name)?;
3573                }
3574            }
3575            Ok(())
3576        }
3577    }
3578}
3579
3580#[cfg(feature = "gecko")]
3581pub mod mask {
3582    pub use crate::properties::generated::shorthands::mask::*;
3583
3584    use super::*;
3585    use crate::parser::Parse;
3586    use crate::properties::longhands::{
3587        mask_clip, mask_composite, mask_mode, mask_origin, mask_position_x, mask_position_y,
3588        mask_repeat,
3589    };
3590    use crate::properties::longhands::{mask_image, mask_size};
3591    use crate::values::specified::{Position, PositionComponent};
3592
3593    impl From<mask_origin::single_value::SpecifiedValue> for mask_clip::single_value::SpecifiedValue {
3594        fn from(
3595            origin: mask_origin::single_value::SpecifiedValue,
3596        ) -> mask_clip::single_value::SpecifiedValue {
3597            match origin {
3598                mask_origin::single_value::SpecifiedValue::ContentBox => {
3599                    mask_clip::single_value::SpecifiedValue::ContentBox
3600                },
3601                mask_origin::single_value::SpecifiedValue::PaddingBox => {
3602                    mask_clip::single_value::SpecifiedValue::PaddingBox
3603                },
3604                mask_origin::single_value::SpecifiedValue::BorderBox => {
3605                    mask_clip::single_value::SpecifiedValue::BorderBox
3606                },
3607                mask_origin::single_value::SpecifiedValue::FillBox => {
3608                    mask_clip::single_value::SpecifiedValue::FillBox
3609                },
3610                mask_origin::single_value::SpecifiedValue::StrokeBox => {
3611                    mask_clip::single_value::SpecifiedValue::StrokeBox
3612                },
3613                mask_origin::single_value::SpecifiedValue::ViewBox => {
3614                    mask_clip::single_value::SpecifiedValue::ViewBox
3615                },
3616            }
3617        }
3618    }
3619
3620    pub fn parse_value<'i, 't>(
3621        context: &ParserContext,
3622        input: &mut Parser<'i, 't>,
3623    ) -> Result<Longhands, ParseError<'i>> {
3624        let mut mask_image = Vec::with_capacity(1);
3625        let mut mask_mode = Vec::with_capacity(1);
3626        let mut mask_position_x = Vec::with_capacity(1);
3627        let mut mask_position_y = Vec::with_capacity(1);
3628        let mut mask_size = Vec::with_capacity(1);
3629        let mut mask_repeat = Vec::with_capacity(1);
3630        let mut mask_origin = Vec::with_capacity(1);
3631        let mut mask_clip = Vec::with_capacity(1);
3632        let mut mask_composite = Vec::with_capacity(1);
3633
3634        input.parse_comma_separated(|input| {
3635            let mut image = None;
3636            let mut mode = None;
3637            let mut position = None;
3638            let mut size = None;
3639            let mut repeat = None;
3640            let mut origin = None;
3641            let mut clip = None;
3642            let mut composite = None;
3643            let mut parsed = 0;
3644            loop {
3645                parsed += 1;
3646
3647                try_parse_one!(context, input, image, mask_image::single_value::parse);
3648                if position.is_none() {
3649                    if let Ok(value) = input.try_parse(|input| Position::parse(context, input)) {
3650                        position = Some(value);
3651                        size = input
3652                            .try_parse(|input| {
3653                                input.expect_delim('/')?;
3654                                mask_size::single_value::parse(context, input)
3655                            })
3656                            .ok();
3657
3658                        continue;
3659                    }
3660                }
3661                try_parse_one!(context, input, repeat, mask_repeat::single_value::parse);
3662                try_parse_one!(context, input, origin, mask_origin::single_value::parse);
3663                try_parse_one!(context, input, clip, mask_clip::single_value::parse);
3664                try_parse_one!(
3665                    context,
3666                    input,
3667                    composite,
3668                    mask_composite::single_value::parse
3669                );
3670                try_parse_one!(context, input, mode, mask_mode::single_value::parse);
3671
3672                parsed -= 1;
3673                break;
3674            }
3675            if clip.is_none() {
3676                if let Some(origin) = origin {
3677                    clip = Some(mask_clip::single_value::SpecifiedValue::from(origin));
3678                }
3679            }
3680            if parsed == 0 {
3681                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3682            }
3683            if let Some(position) = position {
3684                mask_position_x.push(position.horizontal);
3685                mask_position_y.push(position.vertical);
3686            } else {
3687                mask_position_x.push(PositionComponent::zero());
3688                mask_position_y.push(PositionComponent::zero());
3689            }
3690            if let Some(m_image) = image {
3691                mask_image.push(m_image);
3692            } else {
3693                mask_image.push(mask_image::single_value::get_initial_specified_value());
3694            }
3695            if let Some(m_mode) = mode {
3696                mask_mode.push(m_mode);
3697            } else {
3698                mask_mode.push(mask_mode::single_value::get_initial_specified_value());
3699            }
3700            if let Some(m_size) = size {
3701                mask_size.push(m_size);
3702            } else {
3703                mask_size.push(mask_size::single_value::get_initial_specified_value());
3704            }
3705            if let Some(m_repeat) = repeat {
3706                mask_repeat.push(m_repeat);
3707            } else {
3708                mask_repeat.push(mask_repeat::single_value::get_initial_specified_value());
3709            }
3710            if let Some(m_origin) = origin {
3711                mask_origin.push(m_origin);
3712            } else {
3713                mask_origin.push(mask_origin::single_value::get_initial_specified_value());
3714            }
3715            if let Some(m_clip) = clip {
3716                mask_clip.push(m_clip);
3717            } else {
3718                mask_clip.push(mask_clip::single_value::get_initial_specified_value());
3719            }
3720            if let Some(m_composite) = composite {
3721                mask_composite.push(m_composite);
3722            } else {
3723                mask_composite.push(mask_composite::single_value::get_initial_specified_value());
3724            }
3725            Ok(())
3726        })?;
3727
3728        Ok(expanded! {
3729           mask_image: mask_image::SpecifiedValue(mask_image.into()),
3730           mask_mode: mask_mode::SpecifiedValue(mask_mode.into()),
3731           mask_position_x: mask_position_x::SpecifiedValue(mask_position_x.into()),
3732           mask_position_y: mask_position_y::SpecifiedValue(mask_position_y.into()),
3733           mask_size: mask_size::SpecifiedValue(mask_size.into()),
3734           mask_repeat: mask_repeat::SpecifiedValue(mask_repeat.into()),
3735           mask_origin: mask_origin::SpecifiedValue(mask_origin.into()),
3736           mask_clip: mask_clip::SpecifiedValue(mask_clip.into()),
3737           mask_composite: mask_composite::SpecifiedValue(mask_composite.into()),
3738        })
3739    }
3740
3741    impl<'a> ToCss for LonghandsToSerialize<'a> {
3742        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3743        where
3744            W: fmt::Write,
3745        {
3746            use crate::properties::longhands::mask_clip::single_value::computed_value::T as Clip;
3747            use crate::properties::longhands::mask_origin::single_value::computed_value::T as Origin;
3748            use style_traits::values::SequenceWriter;
3749
3750            let len = self.mask_image.0.len();
3751            if len == 0 {
3752                return Ok(());
3753            }
3754            if self.mask_mode.0.len() != len {
3755                return Ok(());
3756            }
3757            if self.mask_position_x.0.len() != len {
3758                return Ok(());
3759            }
3760            if self.mask_position_y.0.len() != len {
3761                return Ok(());
3762            }
3763            if self.mask_size.0.len() != len {
3764                return Ok(());
3765            }
3766            if self.mask_repeat.0.len() != len {
3767                return Ok(());
3768            }
3769            if self.mask_origin.0.len() != len {
3770                return Ok(());
3771            }
3772            if self.mask_clip.0.len() != len {
3773                return Ok(());
3774            }
3775            if self.mask_composite.0.len() != len {
3776                return Ok(());
3777            }
3778
3779            for i in 0..len {
3780                if i > 0 {
3781                    dest.write_str(", ")?;
3782                }
3783
3784                let image = &self.mask_image.0[i];
3785                let mode = &self.mask_mode.0[i];
3786                let position_x = &self.mask_position_x.0[i];
3787                let position_y = &self.mask_position_y.0[i];
3788                let size = &self.mask_size.0[i];
3789                let repeat = &self.mask_repeat.0[i];
3790                let origin = &self.mask_origin.0[i];
3791                let clip = &self.mask_clip.0[i];
3792                let composite = &self.mask_composite.0[i];
3793
3794                let mut has_other = false;
3795                let has_image = *image != mask_image::single_value::get_initial_specified_value();
3796                has_other |= has_image;
3797                let has_mode = *mode != mask_mode::single_value::get_initial_specified_value();
3798                has_other |= has_mode;
3799                let has_size = *size != mask_size::single_value::get_initial_specified_value();
3800                has_other |= has_size;
3801                let has_repeat =
3802                    *repeat != mask_repeat::single_value::get_initial_specified_value();
3803                has_other |= has_repeat;
3804                let has_composite =
3805                    *composite != mask_composite::single_value::get_initial_specified_value();
3806                has_other |= has_composite;
3807                let has_position = *position_x != PositionComponent::zero()
3808                    || *position_y != PositionComponent::zero();
3809                let has_origin = *origin != Origin::BorderBox;
3810                let has_clip = *clip != Clip::BorderBox;
3811
3812                if !has_other && !has_position && !has_origin && !has_clip {
3813                    return image.to_css(dest);
3814                }
3815
3816                let mut writer = SequenceWriter::new(dest, " ");
3817                if has_image {
3818                    writer.item(image)?;
3819                }
3820                if has_position || has_size {
3821                    writer.write_item(|dest| {
3822                        Position {
3823                            horizontal: position_x.clone(),
3824                            vertical: position_y.clone(),
3825                        }
3826                        .to_css(dest)?;
3827                        if has_size {
3828                            dest.write_str(" / ")?;
3829                            size.to_css(dest)?;
3830                        }
3831                        Ok(())
3832                    })?;
3833                }
3834
3835                if has_repeat {
3836                    writer.item(repeat)?;
3837                }
3838
3839                if has_origin || (has_clip && *clip != Clip::NoClip) {
3840                    writer.item(origin)?;
3841                }
3842
3843                if has_clip && *clip != From::from(*origin) {
3844                    writer.item(clip)?;
3845                }
3846
3847                if has_composite {
3848                    writer.item(composite)?;
3849                }
3850
3851                if has_mode {
3852                    writer.item(mode)?;
3853                }
3854            }
3855
3856            Ok(())
3857        }
3858    }
3859}
3860
3861#[cfg(feature = "gecko")]
3862pub mod mask_position {
3863    pub use crate::properties::generated::shorthands::mask_position::*;
3864
3865    use super::*;
3866    use crate::properties::longhands::{mask_position_x, mask_position_y};
3867    use crate::values::specified::Position;
3868
3869    pub fn parse_value<'i, 't>(
3870        context: &ParserContext,
3871        input: &mut Parser<'i, 't>,
3872    ) -> Result<Longhands, ParseError<'i>> {
3873        // Vec grows from 0 to 4 by default on first push().  So allocate with capacity 1, so in
3874        // the common case of only one item we don't way overallocate, then shrink.  Note that we
3875        // always push at least one item if parsing succeeds.
3876        let mut position_x = Vec::with_capacity(1);
3877        let mut position_y = Vec::with_capacity(1);
3878        input.parse_comma_separated(|input| {
3879            let value = Position::parse(context, input)?;
3880            position_x.push(value.horizontal);
3881            position_y.push(value.vertical);
3882            Ok(())
3883        })?;
3884
3885        if position_x.is_empty() {
3886            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3887        }
3888
3889        Ok(expanded! {
3890            mask_position_x: mask_position_x::SpecifiedValue(position_x.into()),
3891            mask_position_y: mask_position_y::SpecifiedValue(position_y.into()),
3892        })
3893    }
3894
3895    impl<'a> ToCss for LonghandsToSerialize<'a> {
3896        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3897        where
3898            W: fmt::Write,
3899        {
3900            let len = self.mask_position_x.0.len();
3901            if len == 0 || self.mask_position_y.0.len() != len {
3902                return Ok(());
3903            }
3904
3905            for i in 0..len {
3906                Position {
3907                    horizontal: self.mask_position_x.0[i].clone(),
3908                    vertical: self.mask_position_y.0[i].clone(),
3909                }
3910                .to_css(dest)?;
3911
3912                if i < len - 1 {
3913                    dest.write_str(", ")?;
3914                }
3915            }
3916
3917            Ok(())
3918        }
3919    }
3920}
3921
3922pub mod grid_template {
3923    pub use crate::properties::generated::shorthands::grid_template::*;
3924
3925    use super::*;
3926    use crate::parser::Parse;
3927    use crate::values::generics::grid::{concat_serialize_idents, TrackListValue};
3928    use crate::values::generics::grid::{TrackList, TrackSize};
3929    use crate::values::specified::grid::parse_line_names;
3930    use crate::values::specified::position::{
3931        GridTemplateAreas, TemplateAreasArc, TemplateAreasParser,
3932    };
3933    use crate::values::specified::{GenericGridTemplateComponent, GridTemplateComponent};
3934    use servo_arc::Arc;
3935
3936    pub fn parse_grid_template<'i, 't>(
3937        context: &ParserContext,
3938        input: &mut Parser<'i, 't>,
3939    ) -> Result<
3940        (
3941            GridTemplateComponent,
3942            GridTemplateComponent,
3943            GridTemplateAreas,
3944        ),
3945        ParseError<'i>,
3946    > {
3947        if let Ok(x) = input.try_parse(|i| {
3948            if i.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
3949                if !i.is_exhausted() {
3950                    return Err(());
3951                }
3952                return Ok((
3953                    GenericGridTemplateComponent::None,
3954                    GenericGridTemplateComponent::None,
3955                    GridTemplateAreas::None,
3956                ));
3957            }
3958            Err(())
3959        }) {
3960            return Ok(x);
3961        }
3962
3963        let first_line_names = input.try_parse(parse_line_names).unwrap_or_default();
3964        let mut areas_parser = TemplateAreasParser::default();
3965        if areas_parser.try_parse_string(input).is_ok() {
3966            let mut values = vec![];
3967            let mut line_names = vec![];
3968            line_names.push(first_line_names);
3969            loop {
3970                let size = input
3971                    .try_parse(|i| TrackSize::parse(context, i))
3972                    .unwrap_or_default();
3973                values.push(TrackListValue::TrackSize(size));
3974                let mut names = input.try_parse(parse_line_names).unwrap_or_default();
3975                let more_names = input.try_parse(parse_line_names);
3976
3977                match areas_parser.try_parse_string(input) {
3978                    Ok(()) => {
3979                        if let Ok(v) = more_names {
3980                            let mut names_vec = names.into_vec();
3981                            names_vec.extend(v.into_iter());
3982                            names = names_vec.into();
3983                        }
3984                        line_names.push(names);
3985                    },
3986                    Err(e) => {
3987                        if more_names.is_ok() {
3988                            return Err(e);
3989                        }
3990                        line_names.push(names);
3991                        break;
3992                    },
3993                };
3994            }
3995
3996            if line_names.len() == values.len() {
3997                line_names.push(Default::default());
3998            }
3999
4000            let template_areas = areas_parser
4001                .finish()
4002                .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
4003            let template_rows = TrackList {
4004                values: values.into(),
4005                line_names: line_names.into(),
4006                auto_repeat_index: std::usize::MAX,
4007            };
4008
4009            let template_cols = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
4010                let value = GridTemplateComponent::parse_without_none(context, input)?;
4011                if let GenericGridTemplateComponent::TrackList(ref list) = value {
4012                    if !list.is_explicit() {
4013                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
4014                    }
4015                }
4016
4017                value
4018            } else {
4019                GridTemplateComponent::default()
4020            };
4021
4022            Ok((
4023                GenericGridTemplateComponent::TrackList(Box::new(template_rows)),
4024                template_cols,
4025                GridTemplateAreas::Areas(TemplateAreasArc(Arc::new(template_areas))),
4026            ))
4027        } else {
4028            let mut template_rows = GridTemplateComponent::parse(context, input)?;
4029            if let GenericGridTemplateComponent::TrackList(ref mut list) = template_rows {
4030                if list.line_names[0].is_empty() {
4031                    list.line_names[0] = first_line_names;
4032                } else {
4033                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
4034                }
4035            }
4036
4037            input.expect_delim('/')?;
4038            Ok((
4039                template_rows,
4040                GridTemplateComponent::parse(context, input)?,
4041                GridTemplateAreas::None,
4042            ))
4043        }
4044    }
4045
4046    #[inline]
4047    pub fn parse_value<'i, 't>(
4048        context: &ParserContext,
4049        input: &mut Parser<'i, 't>,
4050    ) -> Result<Longhands, ParseError<'i>> {
4051        let (rows, columns, areas) = parse_grid_template(context, input)?;
4052        Ok(expanded! {
4053            grid_template_rows: rows,
4054            grid_template_columns: columns,
4055            grid_template_areas: areas,
4056        })
4057    }
4058
4059    pub fn serialize_grid_template<W>(
4060        template_rows: &GridTemplateComponent,
4061        template_columns: &GridTemplateComponent,
4062        template_areas: &GridTemplateAreas,
4063        dest: &mut CssWriter<W>,
4064    ) -> fmt::Result
4065    where
4066        W: fmt::Write,
4067    {
4068        match *template_areas {
4069            GridTemplateAreas::None => {
4070                if template_rows.is_initial() && template_columns.is_initial() {
4071                    return GridTemplateComponent::default().to_css(dest);
4072                }
4073                template_rows.to_css(dest)?;
4074                dest.write_str(" / ")?;
4075                template_columns.to_css(dest)
4076            },
4077            GridTemplateAreas::Areas(ref areas) => {
4078                if areas.0.strings.len() != template_rows.track_list_len() {
4079                    return Ok(());
4080                }
4081
4082                let track_list = match *template_rows {
4083                    GenericGridTemplateComponent::TrackList(ref list) => {
4084                        if !list.is_explicit() {
4085                            return Ok(());
4086                        }
4087                        list
4088                    },
4089                    _ => return Ok(()),
4090                };
4091
4092                match *template_columns {
4093                    GenericGridTemplateComponent::TrackList(ref list) => {
4094                        if !list.is_explicit() {
4095                            return Ok(());
4096                        }
4097                    },
4098                    GenericGridTemplateComponent::Subgrid(_) => {
4099                        return Ok(());
4100                    },
4101                    _ => {},
4102                }
4103
4104                let mut names_iter = track_list.line_names.iter();
4105                for (((i, string), names), value) in areas
4106                    .0
4107                    .strings
4108                    .iter()
4109                    .enumerate()
4110                    .zip(&mut names_iter)
4111                    .zip(track_list.values.iter())
4112                {
4113                    if i > 0 {
4114                        dest.write_char(' ')?;
4115                    }
4116
4117                    if !names.is_empty() {
4118                        concat_serialize_idents("[", "] ", names, " ", dest)?;
4119                    }
4120
4121                    string.to_css(dest)?;
4122
4123                    if !value.is_initial() {
4124                        dest.write_char(' ')?;
4125                        value.to_css(dest)?;
4126                    }
4127                }
4128
4129                if let Some(names) = names_iter.next() {
4130                    concat_serialize_idents(" [", "]", names, " ", dest)?;
4131                }
4132
4133                if let GenericGridTemplateComponent::TrackList(ref list) = *template_columns {
4134                    dest.write_str(" / ")?;
4135                    list.to_css(dest)?;
4136                }
4137
4138                Ok(())
4139            },
4140        }
4141    }
4142
4143    impl<'a> ToCss for LonghandsToSerialize<'a> {
4144        #[inline]
4145        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
4146        where
4147            W: fmt::Write,
4148        {
4149            serialize_grid_template(
4150                self.grid_template_rows,
4151                self.grid_template_columns,
4152                self.grid_template_areas,
4153                dest,
4154            )
4155        }
4156    }
4157}
4158
4159pub mod grid {
4160    pub use crate::properties::generated::shorthands::grid::*;
4161
4162    use super::*;
4163    use crate::parser::Parse;
4164    use crate::properties::longhands::{grid_auto_columns, grid_auto_flow, grid_auto_rows};
4165    use crate::values::generics::grid::GridTemplateComponent;
4166    use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
4167    use crate::values::specified::{GenericGridTemplateComponent, ImplicitGridTracks};
4168
4169    pub fn parse_value<'i, 't>(
4170        context: &ParserContext,
4171        input: &mut Parser<'i, 't>,
4172    ) -> Result<Longhands, ParseError<'i>> {
4173        let mut temp_rows = GridTemplateComponent::default();
4174        let mut temp_cols = GridTemplateComponent::default();
4175        let mut temp_areas = GridTemplateAreas::None;
4176        let mut auto_rows = ImplicitGridTracks::default();
4177        let mut auto_cols = ImplicitGridTracks::default();
4178        let mut flow = grid_auto_flow::get_initial_value();
4179
4180        fn parse_auto_flow<'i, 't>(
4181            input: &mut Parser<'i, 't>,
4182            is_row: bool,
4183        ) -> Result<GridAutoFlow, ParseError<'i>> {
4184            let mut track = None;
4185            let mut dense = GridAutoFlow::empty();
4186
4187            for _ in 0..2 {
4188                if input
4189                    .try_parse(|i| i.expect_ident_matching("auto-flow"))
4190                    .is_ok()
4191                {
4192                    track = if is_row {
4193                        Some(GridAutoFlow::ROW)
4194                    } else {
4195                        Some(GridAutoFlow::COLUMN)
4196                    };
4197                } else if input
4198                    .try_parse(|i| i.expect_ident_matching("dense"))
4199                    .is_ok()
4200                {
4201                    dense = GridAutoFlow::DENSE
4202                } else {
4203                    break;
4204                }
4205            }
4206
4207            if track.is_some() {
4208                Ok(track.unwrap() | dense)
4209            } else {
4210                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
4211            }
4212        }
4213
4214        if let Ok((rows, cols, areas)) =
4215            input.try_parse(|i| super::grid_template::parse_grid_template(context, i))
4216        {
4217            temp_rows = rows;
4218            temp_cols = cols;
4219            temp_areas = areas;
4220        } else if let Ok(rows) = input.try_parse(|i| GridTemplateComponent::parse(context, i)) {
4221            temp_rows = rows;
4222            input.expect_delim('/')?;
4223            flow = parse_auto_flow(input, false)?;
4224            auto_cols = input
4225                .try_parse(|i| grid_auto_columns::parse(context, i))
4226                .unwrap_or_default();
4227        } else {
4228            flow = parse_auto_flow(input, true)?;
4229            auto_rows = input
4230                .try_parse(|i| grid_auto_rows::parse(context, i))
4231                .unwrap_or_default();
4232            input.expect_delim('/')?;
4233            temp_cols = GridTemplateComponent::parse(context, input)?;
4234        }
4235
4236        Ok(expanded! {
4237            grid_template_rows: temp_rows,
4238            grid_template_columns: temp_cols,
4239            grid_template_areas: temp_areas,
4240            grid_auto_rows: auto_rows,
4241            grid_auto_columns: auto_cols,
4242            grid_auto_flow: flow,
4243        })
4244    }
4245
4246    impl<'a> LonghandsToSerialize<'a> {
4247        fn is_grid_template(&self) -> bool {
4248            self.grid_auto_rows.is_initial()
4249                && self.grid_auto_columns.is_initial()
4250                && *self.grid_auto_flow == grid_auto_flow::get_initial_value()
4251        }
4252    }
4253
4254    impl<'a> ToCss for LonghandsToSerialize<'a> {
4255        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
4256        where
4257            W: fmt::Write,
4258        {
4259            if self.is_grid_template() {
4260                return super::grid_template::serialize_grid_template(
4261                    self.grid_template_rows,
4262                    self.grid_template_columns,
4263                    self.grid_template_areas,
4264                    dest,
4265                );
4266            }
4267
4268            if *self.grid_template_areas != GridTemplateAreas::None {
4269                return Ok(());
4270            }
4271
4272            if self.grid_auto_flow.contains(GridAutoFlow::COLUMN) {
4273                if !self.grid_auto_rows.is_initial() || !self.grid_template_columns.is_initial() {
4274                    return Ok(());
4275                }
4276
4277                if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_rows
4278                {
4279                    if !list.is_explicit() {
4280                        return Ok(());
4281                    }
4282                }
4283
4284                self.grid_template_rows.to_css(dest)?;
4285                dest.write_str(" / auto-flow")?;
4286                if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
4287                    dest.write_str(" dense")?;
4288                }
4289
4290                if !self.grid_auto_columns.is_initial() {
4291                    dest.write_char(' ')?;
4292                    self.grid_auto_columns.to_css(dest)?;
4293                }
4294
4295                return Ok(());
4296            }
4297
4298            if !self.grid_auto_columns.is_initial() || !self.grid_template_rows.is_initial() {
4299                return Ok(());
4300            }
4301
4302            if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_columns {
4303                if !list.is_explicit() {
4304                    return Ok(());
4305                }
4306            }
4307
4308            dest.write_str("auto-flow")?;
4309            if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
4310                dest.write_str(" dense")?;
4311            }
4312
4313            if !self.grid_auto_rows.is_initial() {
4314                dest.write_char(' ')?;
4315                self.grid_auto_rows.to_css(dest)?;
4316            }
4317
4318            dest.write_str(" / ")?;
4319            self.grid_template_columns.to_css(dest)?;
4320            Ok(())
4321        }
4322    }
4323}