Skip to main content

style/properties/
shorthands.rs

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