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_language_override, font_size, font_size_adjust, font_variant_alternates,
2559        font_variant_emoji,
2560    };
2561    use crate::properties::longhands::{
2562        font_feature_settings, font_kerning, font_optical_sizing, font_stretch, font_style,
2563        font_variant_caps, 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            #[cfg(feature = "gecko")]
2666            font_language_override: font_language_override::get_initial_specified_value(),
2667            #[cfg(feature = "gecko")]
2668            font_size_adjust: font_size_adjust::get_initial_specified_value(),
2669            #[cfg(feature = "gecko")]
2670            font_variant_alternates: font_variant_alternates::get_initial_specified_value(),
2671            font_variant_east_asian: font_variant_east_asian::get_initial_specified_value(),
2672            #[cfg(feature = "gecko")]
2673            font_variant_emoji: font_variant_emoji::get_initial_specified_value(),
2674            font_variant_ligatures: font_variant_ligatures::get_initial_specified_value(),
2675            font_variant_numeric: font_variant_numeric::get_initial_specified_value(),
2676            font_variant_position: font_variant_position::get_initial_specified_value(),
2677            font_feature_settings: font_feature_settings::get_initial_specified_value(),
2678        })
2679    }
2680
2681    #[cfg(feature = "gecko")]
2682    enum CheckSystemResult {
2683        AllSystem(SystemFont),
2684        SomeSystem,
2685        None,
2686    }
2687
2688    impl<'a> ToCss for LonghandsToSerialize<'a> {
2689        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2690        where
2691            W: fmt::Write,
2692        {
2693            #[cfg(feature = "gecko")]
2694            match self.check_system() {
2695                CheckSystemResult::AllSystem(sys) => return sys.to_css(dest),
2696                CheckSystemResult::SomeSystem => return Ok(()),
2697                CheckSystemResult::None => {},
2698            }
2699
2700            if let Some(v) = self.font_optical_sizing {
2701                if v != &font_optical_sizing::get_initial_specified_value() {
2702                    return Ok(());
2703                }
2704            }
2705            if let Some(v) = self.font_variation_settings {
2706                if v != &font_variation_settings::get_initial_specified_value() {
2707                    return Ok(());
2708                }
2709            }
2710            #[cfg(feature = "gecko")]
2711            if let Some(v) = self.font_variant_emoji {
2712                if v != &font_variant_emoji::get_initial_specified_value() {
2713                    return Ok(());
2714                }
2715            }
2716
2717            if self.font_kerning != &font_kerning::get_initial_specified_value() {
2718                return Ok(());
2719            }
2720            #[cfg(feature = "gecko")]
2721            if self.font_language_override != &font_language_override::get_initial_specified_value()
2722            {
2723                return Ok(());
2724            }
2725            #[cfg(feature = "gecko")]
2726            if self.font_size_adjust != &font_size_adjust::get_initial_specified_value() {
2727                return Ok(());
2728            }
2729            #[cfg(feature = "gecko")]
2730            if self.font_variant_alternates
2731                != &font_variant_alternates::get_initial_specified_value()
2732            {
2733                return Ok(());
2734            }
2735            if self.font_variant_east_asian
2736                != &font_variant_east_asian::get_initial_specified_value()
2737            {
2738                return Ok(());
2739            }
2740            if self.font_variant_ligatures != &font_variant_ligatures::get_initial_specified_value()
2741            {
2742                return Ok(());
2743            }
2744            if self.font_variant_numeric != &font_variant_numeric::get_initial_specified_value() {
2745                return Ok(());
2746            }
2747            if self.font_variant_position != &font_variant_position::get_initial_specified_value() {
2748                return Ok(());
2749            }
2750            if self.font_feature_settings != &font_feature_settings::get_initial_specified_value() {
2751                return Ok(());
2752            }
2753
2754            let font_stretch = match self.font_stretch {
2755                FontStretch::Keyword(kw) => *kw,
2756                FontStretch::Stretch(percentage) => {
2757                    let computed = match percentage.compute() {
2758                        Some(v) => v,
2759                        None => return Ok(()),
2760                    };
2761                    match FontStretchKeyword::from_percentage(computed.0) {
2762                        Some(kw) => kw,
2763                        None => return Ok(()),
2764                    }
2765                },
2766                FontStretch::System(..) => return Ok(()),
2767            };
2768
2769            if self.font_variant_caps != &font_variant_caps::get_initial_specified_value()
2770                && *self.font_variant_caps != font_variant_caps::SpecifiedValue::SmallCaps
2771            {
2772                return Ok(());
2773            }
2774
2775            if self.font_style != &font_style::get_initial_specified_value() {
2776                self.font_style.to_css(dest)?;
2777                dest.write_char(' ')?;
2778            }
2779            if self.font_variant_caps != &font_variant_caps::get_initial_specified_value() {
2780                self.font_variant_caps.to_css(dest)?;
2781                dest.write_char(' ')?;
2782            }
2783
2784            if self.font_weight != &FontWeight::normal()
2785                && self.font_weight != &FontWeight::from_gecko_keyword(400)
2786            {
2787                self.font_weight.to_css(dest)?;
2788                dest.write_char(' ')?;
2789            }
2790
2791            if font_stretch != FontStretchKeyword::Normal {
2792                font_stretch.to_css(dest)?;
2793                dest.write_char(' ')?;
2794            }
2795
2796            self.font_size.to_css(dest)?;
2797
2798            if *self.line_height != LineHeight::normal() {
2799                dest.write_str(" / ")?;
2800                self.line_height.to_css(dest)?;
2801            }
2802
2803            dest.write_char(' ')?;
2804            self.font_family.to_css(dest)?;
2805
2806            Ok(())
2807        }
2808    }
2809
2810    impl<'a> LonghandsToSerialize<'a> {
2811        #[cfg(feature = "gecko")]
2812        fn check_system(&self) -> CheckSystemResult {
2813            let mut sys = None;
2814            let mut all = true;
2815
2816            macro_rules! check {
2817                ($v:expr) => {
2818                    match $v.get_system() {
2819                        Some(s) => {
2820                            debug_assert!(sys.is_none() || s == sys.unwrap());
2821                            sys = Some(s);
2822                        },
2823                        None => {
2824                            all = false;
2825                        },
2826                    }
2827                };
2828                ($e:expr, $($es:expr),+) => { check!($e); check!($($es),*); };
2829            }
2830
2831            check!(
2832                self.font_family,
2833                self.font_size,
2834                self.font_style,
2835                self.font_stretch,
2836                self.font_weight
2837            );
2838
2839            if self.line_height != &LineHeight::normal() {
2840                all = false
2841            }
2842            if all {
2843                CheckSystemResult::AllSystem(sys.unwrap())
2844            } else if sys.is_some() {
2845                CheckSystemResult::SomeSystem
2846            } else {
2847                CheckSystemResult::None
2848            }
2849        }
2850    }
2851
2852    impl SpecifiedValueInfo for Longhands {
2853        const SUPPORTED_TYPES: u8 = FontStyle::SUPPORTED_TYPES
2854            | FontWeight::SUPPORTED_TYPES
2855            | FontStretch::SUPPORTED_TYPES
2856            | font_variant_caps::SpecifiedValue::SUPPORTED_TYPES
2857            | FontSize::SUPPORTED_TYPES
2858            | FontFamily::SUPPORTED_TYPES;
2859
2860        fn collect_completion_keywords(f: KeywordsCollectFn) {
2861            FontStyle::collect_completion_keywords(f);
2862            FontWeight::collect_completion_keywords(f);
2863            FontStretch::collect_completion_keywords(f);
2864            font_variant_caps::SpecifiedValue::collect_completion_keywords(f);
2865            FontSize::collect_completion_keywords(f);
2866            FontFamily::collect_completion_keywords(f);
2867
2868            #[cfg(feature = "gecko")]
2869            SystemFont::collect_completion_keywords(f);
2870        }
2871    }
2872}
2873
2874pub mod font_variant {
2875    pub use crate::properties::generated::shorthands::font_variant::*;
2876
2877    use super::*;
2878    #[cfg(feature = "gecko")]
2879    use crate::properties::longhands::{font_variant_alternates, font_variant_emoji};
2880    use crate::properties::longhands::{
2881        font_variant_caps, font_variant_east_asian, font_variant_ligatures, font_variant_numeric,
2882        font_variant_position,
2883    };
2884    use crate::values::specified::FontVariantLigatures;
2885
2886    pub fn parse_value<'i, 't>(
2887        context: &ParserContext,
2888        input: &mut Parser<'i, 't>,
2889    ) -> Result<Longhands, ParseError<'i>> {
2890        let mut ligatures = None;
2891        let mut caps = None;
2892        #[cfg(feature = "gecko")]
2893        let mut alternates = None;
2894        let mut numeric = None;
2895        let mut east_asian = None;
2896        let mut position = None;
2897        #[cfg(feature = "gecko")]
2898        let mut emoji = None;
2899
2900        if input
2901            .try_parse(|input| input.expect_ident_matching("normal"))
2902            .is_ok()
2903        {
2904        } else if input
2905            .try_parse(|input| input.expect_ident_matching("none"))
2906            .is_ok()
2907        {
2908            ligatures = Some(FontVariantLigatures::NONE);
2909        } else {
2910            let mut parsed = 0;
2911            loop {
2912                parsed += 1;
2913                if input
2914                    .try_parse(|input| input.expect_ident_matching("normal"))
2915                    .is_ok()
2916                    || input
2917                        .try_parse(|input| input.expect_ident_matching("none"))
2918                        .is_ok()
2919                {
2920                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2921                }
2922                try_parse_one!(context, input, ligatures, font_variant_ligatures::parse);
2923                try_parse_one!(context, input, caps, font_variant_caps::parse);
2924                #[cfg(feature = "gecko")]
2925                try_parse_one!(context, input, alternates, font_variant_alternates::parse);
2926                try_parse_one!(context, input, numeric, font_variant_numeric::parse);
2927                try_parse_one!(context, input, east_asian, font_variant_east_asian::parse);
2928                try_parse_one!(context, input, position, font_variant_position::parse);
2929                #[cfg(feature = "gecko")]
2930                try_parse_one!(context, input, emoji, font_variant_emoji::parse);
2931                parsed -= 1;
2932                break;
2933            }
2934
2935            if parsed == 0 {
2936                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2937            }
2938        }
2939
2940        #[cfg(feature = "gecko")]
2941        return Ok(expanded! {
2942            font_variant_ligatures: unwrap_or_initial!(font_variant_ligatures, ligatures),
2943            font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),
2944            font_variant_alternates: unwrap_or_initial!(font_variant_alternates, alternates),
2945            font_variant_numeric: unwrap_or_initial!(font_variant_numeric, numeric),
2946            font_variant_east_asian: unwrap_or_initial!(font_variant_east_asian, east_asian),
2947            font_variant_position: unwrap_or_initial!(font_variant_position, position),
2948            font_variant_emoji: unwrap_or_initial!(font_variant_emoji, emoji),
2949        });
2950        #[cfg(feature = "servo")]
2951        return Ok(expanded! {
2952            font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),
2953            font_variant_east_asian: unwrap_or_initial!(font_variant_east_asian, east_asian),
2954            font_variant_ligatures: unwrap_or_initial!(font_variant_ligatures, ligatures),
2955            font_variant_numeric: unwrap_or_initial!(font_variant_numeric, numeric),
2956            font_variant_position: unwrap_or_initial!(font_variant_position, position),
2957        });
2958    }
2959
2960    impl<'a> ToCss for LonghandsToSerialize<'a> {
2961        #[allow(unused_assignments)]
2962        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2963        where
2964            W: fmt::Write,
2965        {
2966            let has_none_ligatures = self.font_variant_ligatures == &FontVariantLigatures::NONE;
2967
2968            #[cfg(feature = "gecko")]
2969            const TOTAL_SUBPROPS: usize = 7;
2970            #[cfg(feature = "servo")]
2971            const TOTAL_SUBPROPS: usize = 5;
2972            let mut nb_normals = 0;
2973            macro_rules! count_normal {
2974                ($e: expr, $p: ident) => {
2975                    if *$e == $p::get_initial_specified_value() {
2976                        nb_normals += 1;
2977                    }
2978                };
2979                ($v: ident) => {
2980                    count_normal!(self.$v, $v);
2981                };
2982            }
2983            count_normal!(font_variant_ligatures);
2984            count_normal!(font_variant_caps);
2985            #[cfg(feature = "gecko")]
2986            count_normal!(font_variant_alternates);
2987            count_normal!(font_variant_numeric);
2988            count_normal!(font_variant_east_asian);
2989            count_normal!(font_variant_position);
2990            #[cfg(feature = "gecko")]
2991            if let Some(value) = self.font_variant_emoji {
2992                if value == &font_variant_emoji::get_initial_specified_value() {
2993                    nb_normals += 1;
2994                }
2995            } else {
2996                nb_normals += 1;
2997            }
2998
2999            if nb_normals == TOTAL_SUBPROPS {
3000                return dest.write_str("normal");
3001            }
3002            if has_none_ligatures {
3003                if nb_normals == TOTAL_SUBPROPS - 1 {
3004                    dest.write_str("none")?;
3005                }
3006                return Ok(());
3007            }
3008
3009            let mut writer = SequenceWriter::new(dest, " ");
3010            macro_rules! write {
3011                ($e: expr, $p: ident) => {
3012                    if *$e != $p::get_initial_specified_value() {
3013                        writer.item($e)?;
3014                    }
3015                };
3016                ($v: ident) => {
3017                    write!(self.$v, $v);
3018                };
3019            }
3020
3021            write!(font_variant_ligatures);
3022            write!(font_variant_caps);
3023            #[cfg(feature = "gecko")]
3024            write!(font_variant_alternates);
3025            write!(font_variant_numeric);
3026            write!(font_variant_east_asian);
3027            write!(font_variant_position);
3028            #[cfg(feature = "gecko")]
3029            if let Some(v) = self.font_variant_emoji {
3030                write!(v, font_variant_emoji);
3031            }
3032            Ok(())
3033        }
3034    }
3035}
3036
3037#[cfg(feature = "gecko")]
3038pub mod font_synthesis {
3039    pub use crate::properties::generated::shorthands::font_synthesis::*;
3040
3041    use super::*;
3042    use crate::values::specified::{FontSynthesis, FontSynthesisStyle};
3043
3044    pub fn parse_value<'i, 't>(
3045        _context: &ParserContext,
3046        input: &mut Parser<'i, 't>,
3047    ) -> Result<Longhands, ParseError<'i>> {
3048        let mut weight = FontSynthesis::None;
3049        let mut style = FontSynthesisStyle::None;
3050        let mut small_caps = FontSynthesis::None;
3051        let mut position = FontSynthesis::None;
3052
3053        if !input
3054            .try_parse(|input| input.expect_ident_matching("none"))
3055            .is_ok()
3056        {
3057            let mut has_custom_value = false;
3058            while !input.is_exhausted() {
3059                try_match_ident_ignore_ascii_case! { input,
3060                    "weight" if weight == FontSynthesis::None => {
3061                        has_custom_value = true;
3062                        weight = FontSynthesis::Auto;
3063                        continue;
3064                    },
3065                    "style" if style == FontSynthesisStyle::None => {
3066                        has_custom_value = true;
3067                        style = FontSynthesisStyle::Auto;
3068                        continue;
3069                    },
3070                    "small-caps" if small_caps == FontSynthesis::None => {
3071                        has_custom_value = true;
3072                        small_caps = FontSynthesis::Auto;
3073                        continue;
3074                    },
3075                    "position" if position == FontSynthesis::None => {
3076                        has_custom_value = true;
3077                        position = FontSynthesis::Auto;
3078                        continue;
3079                    },
3080                    "oblique-only" if style == FontSynthesisStyle::None => {
3081                        has_custom_value = true;
3082                        style = FontSynthesisStyle::ObliqueOnly;
3083                        continue;
3084                    },
3085                }
3086            }
3087            if !has_custom_value {
3088                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3089            }
3090        }
3091
3092        Ok(expanded! {
3093            font_synthesis_weight: weight,
3094            font_synthesis_style: style,
3095            font_synthesis_small_caps: small_caps,
3096            font_synthesis_position: position,
3097        })
3098    }
3099
3100    impl<'a> ToCss for LonghandsToSerialize<'a> {
3101        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3102        where
3103            W: fmt::Write,
3104        {
3105            let mut writer = SequenceWriter::new(dest, " ");
3106            if self.font_synthesis_weight == &FontSynthesis::Auto {
3107                writer.raw_item("weight")?;
3108            }
3109            if self.font_synthesis_style != &FontSynthesisStyle::None {
3110                if self.font_synthesis_style == &FontSynthesisStyle::Auto {
3111                    writer.raw_item("style")?;
3112                } else {
3113                    writer.raw_item("oblique-only")?;
3114                }
3115            }
3116            if self.font_synthesis_small_caps == &FontSynthesis::Auto {
3117                writer.raw_item("small-caps")?;
3118            }
3119            if self.font_synthesis_position == &FontSynthesis::Auto {
3120                writer.raw_item("position")?;
3121            }
3122            if !writer.has_written() {
3123                writer.raw_item("none")?;
3124            }
3125            Ok(())
3126        }
3127    }
3128
3129    // The shorthand takes the sub-property names of the longhands, and not the
3130    // 'auto' keyword like they do, so we can't automatically derive this.
3131    impl SpecifiedValueInfo for Longhands {
3132        fn collect_completion_keywords(f: KeywordsCollectFn) {
3133            f(&[
3134                "none",
3135                "oblique-only",
3136                "small-caps",
3137                "position",
3138                "style",
3139                "weight",
3140            ]);
3141        }
3142    }
3143}
3144
3145#[cfg(feature = "gecko")]
3146pub mod text_box {
3147    pub use crate::properties::generated::shorthands::text_box::*;
3148
3149    use super::*;
3150    use crate::values::specified::{TextBoxEdge, TextBoxTrim};
3151
3152    pub fn parse_value<'i>(
3153        context: &ParserContext,
3154        input: &mut Parser<'i, '_>,
3155    ) -> Result<Longhands, ParseError<'i>> {
3156        let mut trim = None;
3157        let mut edge = None;
3158
3159        if input
3160            .try_parse(|input| input.expect_ident_matching("normal"))
3161            .is_ok()
3162        {
3163            return Ok(Longhands {
3164                text_box_trim: TextBoxTrim::NONE,
3165                text_box_edge: TextBoxEdge::Auto,
3166            });
3167        }
3168
3169        loop {
3170            try_parse_one!(context, input, trim, TextBoxTrim::parse);
3171            try_parse_one!(context, input, edge, TextBoxEdge::parse);
3172            break;
3173        }
3174
3175        if trim.is_none() && edge.is_none() {
3176            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3177        }
3178
3179        // From https://drafts.csswg.org/css-inline-3/#text-box-shorthand:
3180        // > Omitting the 'text-box-trim' value sets it to 'trim-both'
3181        // > (not the initial value), while omitting the 'text-box-edge'
3182        // > value sets it to auto (the initial value).
3183        Ok(Longhands {
3184            text_box_trim: trim.unwrap_or(TextBoxTrim::TRIM_BOTH),
3185            text_box_edge: edge.unwrap_or(TextBoxEdge::Auto),
3186        })
3187    }
3188
3189    impl<'a> ToCss for LonghandsToSerialize<'a> {
3190        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3191        where
3192            W: fmt::Write,
3193        {
3194            if *self.text_box_trim == TextBoxTrim::NONE && *self.text_box_edge == TextBoxEdge::Auto
3195            {
3196                return dest.write_str("normal");
3197            }
3198
3199            let mut writer = SequenceWriter::new(dest, " ");
3200            if *self.text_box_trim != specified::TextBoxTrim::TRIM_BOTH {
3201                writer.item(self.text_box_trim)?;
3202            }
3203            if *self.text_box_edge != specified::TextBoxEdge::Auto {
3204                writer.item(self.text_box_edge)?;
3205            }
3206            if !writer.has_written() {
3207                self.text_box_trim.to_css(dest)?;
3208            }
3209            Ok(())
3210        }
3211    }
3212}
3213
3214#[cfg(feature = "gecko")]
3215pub mod text_emphasis {
3216    pub use crate::properties::generated::shorthands::text_emphasis::*;
3217
3218    use super::*;
3219    use crate::properties::longhands::{text_emphasis_color, text_emphasis_style};
3220
3221    pub fn parse_value<'i, 't>(
3222        context: &ParserContext,
3223        input: &mut Parser<'i, 't>,
3224    ) -> Result<Longhands, ParseError<'i>> {
3225        let mut color = None;
3226        let mut style = None;
3227        let mut parsed = 0;
3228        loop {
3229            parsed += 1;
3230            try_parse_one!(context, input, color, text_emphasis_color::parse);
3231            try_parse_one!(context, input, style, text_emphasis_style::parse);
3232            parsed -= 1;
3233            break;
3234        }
3235        if parsed == 0 {
3236            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3237        }
3238        Ok(expanded! {
3239            text_emphasis_color: unwrap_or_initial!(text_emphasis_color, color),
3240            text_emphasis_style: unwrap_or_initial!(text_emphasis_style, style),
3241        })
3242    }
3243}
3244
3245pub mod text_decoration {
3246    pub use crate::properties::generated::shorthands::text_decoration::*;
3247
3248    use super::*;
3249    #[cfg(feature = "gecko")]
3250    use crate::properties::longhands::text_decoration_thickness;
3251    use crate::properties::longhands::{
3252        text_decoration_color, text_decoration_line, text_decoration_style,
3253    };
3254
3255    pub fn parse_value<'i, 't>(
3256        context: &ParserContext,
3257        input: &mut Parser<'i, 't>,
3258    ) -> Result<Longhands, ParseError<'i>> {
3259        let mut line = None;
3260        let mut style = None;
3261        let mut color = None;
3262        #[cfg(feature = "gecko")]
3263        let mut thickness = None;
3264
3265        let mut parsed = 0;
3266        loop {
3267            parsed += 1;
3268            try_parse_one!(context, input, line, text_decoration_line::parse);
3269            try_parse_one!(context, input, style, text_decoration_style::parse);
3270            try_parse_one!(context, input, color, text_decoration_color::parse);
3271            #[cfg(feature = "gecko")]
3272            try_parse_one!(context, input, thickness, text_decoration_thickness::parse);
3273            parsed -= 1;
3274            break;
3275        }
3276
3277        if parsed == 0 {
3278            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3279        }
3280
3281        #[cfg(feature = "gecko")]
3282        return Ok(expanded! {
3283            text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
3284            text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
3285            text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
3286            text_decoration_thickness: unwrap_or_initial!(text_decoration_thickness, thickness),
3287        });
3288        #[cfg(feature = "servo")]
3289        return Ok(expanded! {
3290            text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
3291            text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
3292            text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
3293        });
3294    }
3295
3296    impl<'a> ToCss for LonghandsToSerialize<'a> {
3297        #[allow(unused)]
3298        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3299        where
3300            W: fmt::Write,
3301        {
3302            use crate::values::specified::Color;
3303            use crate::values::specified::TextDecorationLine;
3304
3305            let is_solid_style =
3306                *self.text_decoration_style == text_decoration_style::SpecifiedValue::Solid;
3307            let is_current_color = *self.text_decoration_color == Color::CurrentColor;
3308            #[cfg(feature = "gecko")]
3309            let is_auto_thickness = self.text_decoration_thickness.is_auto();
3310            #[cfg(feature = "servo")]
3311            let is_auto_thickness = true;
3312            let is_none = *self.text_decoration_line == TextDecorationLine::none();
3313
3314            let mut writer = SequenceWriter::new(dest, " ");
3315            if (is_solid_style && is_current_color && is_auto_thickness) || !is_none {
3316                writer.item(self.text_decoration_line)?;
3317            }
3318            #[cfg(feature = "gecko")]
3319            if !is_auto_thickness {
3320                writer.item(self.text_decoration_thickness)?;
3321            }
3322            if !is_solid_style {
3323                writer.item(self.text_decoration_style)?;
3324            }
3325            if !is_current_color {
3326                writer.item(self.text_decoration_color)?;
3327            }
3328            Ok(())
3329        }
3330    }
3331}
3332
3333pub mod animation {
3334    pub use crate::properties::generated::shorthands::animation::*;
3335
3336    use super::*;
3337    use crate::properties::longhands::{
3338        animation_delay, animation_direction, animation_duration, animation_fill_mode,
3339        animation_iteration_count, animation_name, animation_play_state, animation_range_end,
3340        animation_range_start, animation_timeline, animation_timing_function,
3341    };
3342
3343    pub fn parse_value<'i, 't>(
3344        context: &ParserContext,
3345        input: &mut Parser<'i, 't>,
3346    ) -> Result<Longhands, ParseError<'i>> {
3347        struct SingleAnimation {
3348            animation_name: animation_name::SingleSpecifiedValue,
3349            animation_duration: animation_duration::SingleSpecifiedValue,
3350            animation_timing_function: animation_timing_function::SingleSpecifiedValue,
3351            animation_delay: animation_delay::SingleSpecifiedValue,
3352            animation_iteration_count: animation_iteration_count::SingleSpecifiedValue,
3353            animation_direction: animation_direction::SingleSpecifiedValue,
3354            animation_fill_mode: animation_fill_mode::SingleSpecifiedValue,
3355            animation_play_state: animation_play_state::SingleSpecifiedValue,
3356        }
3357
3358        fn parse_one_animation<'i, 't>(
3359            context: &ParserContext,
3360            input: &mut Parser<'i, 't>,
3361        ) -> Result<SingleAnimation, ParseError<'i>> {
3362            let mut name = None;
3363            let mut duration = None;
3364            let mut timing_function = None;
3365            let mut delay = None;
3366            let mut iteration_count = None;
3367            let mut direction = None;
3368            let mut fill_mode = None;
3369            let mut play_state = None;
3370
3371            let mut parsed = 0;
3372            loop {
3373                parsed += 1;
3374                try_parse_one!(
3375                    context,
3376                    input,
3377                    duration,
3378                    animation_duration::single_value::parse
3379                );
3380                try_parse_one!(
3381                    context,
3382                    input,
3383                    timing_function,
3384                    animation_timing_function::single_value::parse
3385                );
3386                try_parse_one!(context, input, delay, animation_delay::single_value::parse);
3387                try_parse_one!(
3388                    context,
3389                    input,
3390                    iteration_count,
3391                    animation_iteration_count::single_value::parse
3392                );
3393                try_parse_one!(
3394                    context,
3395                    input,
3396                    direction,
3397                    animation_direction::single_value::parse
3398                );
3399                try_parse_one!(
3400                    context,
3401                    input,
3402                    fill_mode,
3403                    animation_fill_mode::single_value::parse
3404                );
3405                try_parse_one!(
3406                    context,
3407                    input,
3408                    play_state,
3409                    animation_play_state::single_value::parse
3410                );
3411                try_parse_one!(context, input, name, animation_name::single_value::parse);
3412                parsed -= 1;
3413                break;
3414            }
3415
3416            if parsed == 0 {
3417                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3418            }
3419            Ok(SingleAnimation {
3420                animation_name: name
3421                    .unwrap_or_else(animation_name::single_value::get_initial_specified_value),
3422                animation_duration: duration
3423                    .unwrap_or_else(animation_duration::single_value::get_initial_specified_value),
3424                animation_timing_function: timing_function.unwrap_or_else(
3425                    animation_timing_function::single_value::get_initial_specified_value,
3426                ),
3427                animation_delay: delay
3428                    .unwrap_or_else(animation_delay::single_value::get_initial_specified_value),
3429                animation_iteration_count: iteration_count.unwrap_or_else(
3430                    animation_iteration_count::single_value::get_initial_specified_value,
3431                ),
3432                animation_direction: direction
3433                    .unwrap_or_else(animation_direction::single_value::get_initial_specified_value),
3434                animation_fill_mode: fill_mode
3435                    .unwrap_or_else(animation_fill_mode::single_value::get_initial_specified_value),
3436                animation_play_state: play_state.unwrap_or_else(
3437                    animation_play_state::single_value::get_initial_specified_value,
3438                ),
3439            })
3440        }
3441
3442        let mut names = vec![];
3443        let mut durations = vec![];
3444        let mut timing_functions = vec![];
3445        let mut delays = vec![];
3446        let mut iteration_counts = vec![];
3447        let mut directions = vec![];
3448        let mut fill_modes = vec![];
3449        let mut play_states = vec![];
3450
3451        let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?;
3452        for result in results.into_iter() {
3453            names.push(result.animation_name);
3454            durations.push(result.animation_duration);
3455            timing_functions.push(result.animation_timing_function);
3456            delays.push(result.animation_delay);
3457            iteration_counts.push(result.animation_iteration_count);
3458            directions.push(result.animation_direction);
3459            fill_modes.push(result.animation_fill_mode);
3460            play_states.push(result.animation_play_state);
3461        }
3462
3463        Ok(expanded! {
3464            animation_name: animation_name::SpecifiedValue(names.into()),
3465            animation_duration: animation_duration::SpecifiedValue(durations.into()),
3466            animation_timing_function: animation_timing_function::SpecifiedValue(timing_functions.into()),
3467            animation_delay: animation_delay::SpecifiedValue(delays.into()),
3468            animation_iteration_count: animation_iteration_count::SpecifiedValue(iteration_counts.into()),
3469            animation_direction: animation_direction::SpecifiedValue(directions.into()),
3470            animation_fill_mode: animation_fill_mode::SpecifiedValue(fill_modes.into()),
3471            animation_play_state: animation_play_state::SpecifiedValue(play_states.into()),
3472            animation_timeline: animation_timeline::SpecifiedValue(
3473                vec![animation_timeline::single_value::get_initial_specified_value()].into()
3474            ),
3475            // The animation-range properties are reset-only sub-properties of the animation
3476            // shorthand.
3477            // https://drafts.csswg.org/scroll-animations-1/#named-range-animation-declaration
3478            animation_range_start: animation_range_start::SpecifiedValue(
3479                vec![animation_range_start::single_value::get_initial_specified_value()].into()
3480            ),
3481            animation_range_end: animation_range_end::SpecifiedValue(
3482                vec![animation_range_end::single_value::get_initial_specified_value()].into()
3483            ),
3484        })
3485    }
3486
3487    impl<'a> ToCss for LonghandsToSerialize<'a> {
3488        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3489        where
3490            W: fmt::Write,
3491        {
3492            use crate::values::specified::easing::TimingFunction;
3493            use crate::values::specified::{
3494                AnimationDirection, AnimationFillMode, AnimationPlayState,
3495            };
3496            use crate::Zero;
3497            use style_traits::values::SequenceWriter;
3498
3499            let len = self.animation_name.0.len();
3500            if len == 0 {
3501                return Ok(());
3502            }
3503
3504            if len != self.animation_duration.0.len() {
3505                return Ok(());
3506            }
3507            if len != self.animation_timing_function.0.len() {
3508                return Ok(());
3509            }
3510            if len != self.animation_delay.0.len() {
3511                return Ok(());
3512            }
3513            if len != self.animation_iteration_count.0.len() {
3514                return Ok(());
3515            }
3516            if len != self.animation_direction.0.len() {
3517                return Ok(());
3518            }
3519            if len != self.animation_fill_mode.0.len() {
3520                return Ok(());
3521            }
3522            if len != self.animation_play_state.0.len() {
3523                return Ok(());
3524            }
3525
3526            // We don't serialize animation-timeline, animation-range-start and animation-range-end
3527            // if any of them are not the initial value.
3528            if self
3529                .animation_timeline
3530                .map_or(false, |v| v.0.len() != 1 || !v.0[0].is_auto())
3531            {
3532                return Ok(());
3533            }
3534            if self
3535                .animation_range_start
3536                .map_or(false, |v| v.0.len() != 1 || !v.0[0].0.is_normal())
3537            {
3538                return Ok(());
3539            }
3540            if self
3541                .animation_range_end
3542                .map_or(false, |v| v.0.len() != 1 || !v.0[0].0.is_normal())
3543            {
3544                return Ok(());
3545            }
3546
3547            for i in 0..len {
3548                if i != 0 {
3549                    dest.write_str(", ")?;
3550                }
3551
3552                let has_duration = !self.animation_duration.0[i].is_auto()
3553                    && !self.animation_duration.0[i].is_zero();
3554                let has_timing_function = !self.animation_timing_function.0[i].is_ease();
3555                let has_delay = !self.animation_delay.0[i].is_zero();
3556                let has_iteration_count = !self.animation_iteration_count.0[i].is_one();
3557                let has_direction =
3558                    !matches!(self.animation_direction.0[i], AnimationDirection::Normal);
3559                let has_fill_mode =
3560                    !matches!(self.animation_fill_mode.0[i], AnimationFillMode::None);
3561                let has_play_state =
3562                    !matches!(self.animation_play_state.0[i], AnimationPlayState::Running);
3563                let animation_name = &self.animation_name.0[i];
3564                let has_name = !animation_name.is_none();
3565
3566                let mut writer = SequenceWriter::new(dest, " ");
3567
3568                if has_duration || has_delay {
3569                    writer.item(&self.animation_duration.0[i])?;
3570                }
3571
3572                if has_timing_function || TimingFunction::match_keywords(animation_name) {
3573                    writer.item(&self.animation_timing_function.0[i])?;
3574                }
3575
3576                if has_delay {
3577                    writer.item(&self.animation_delay.0[i])?;
3578                }
3579                if has_iteration_count {
3580                    writer.item(&self.animation_iteration_count.0[i])?;
3581                }
3582
3583                if has_direction || AnimationDirection::match_keywords(animation_name) {
3584                    writer.item(&self.animation_direction.0[i])?;
3585                }
3586
3587                if has_fill_mode || AnimationFillMode::match_keywords(animation_name) {
3588                    writer.item(&self.animation_fill_mode.0[i])?;
3589                }
3590
3591                if has_play_state || AnimationPlayState::match_keywords(animation_name) {
3592                    writer.item(&self.animation_play_state.0[i])?;
3593                }
3594
3595                if has_name || !writer.has_written() {
3596                    writer.item(animation_name)?;
3597                }
3598            }
3599            Ok(())
3600        }
3601    }
3602}
3603
3604pub mod mask {
3605    pub use crate::properties::generated::shorthands::mask::*;
3606
3607    use super::*;
3608    use crate::parser::Parse;
3609    use crate::properties::longhands::{
3610        mask_clip, mask_composite, mask_mode, mask_origin, mask_position_x, mask_position_y,
3611        mask_repeat,
3612    };
3613    use crate::properties::longhands::{mask_image, mask_size};
3614    use crate::values::specified::{Position, PositionComponent};
3615
3616    impl From<mask_origin::single_value::SpecifiedValue> for mask_clip::single_value::SpecifiedValue {
3617        fn from(
3618            origin: mask_origin::single_value::SpecifiedValue,
3619        ) -> mask_clip::single_value::SpecifiedValue {
3620            match origin {
3621                mask_origin::single_value::SpecifiedValue::ContentBox => {
3622                    mask_clip::single_value::SpecifiedValue::ContentBox
3623                },
3624                mask_origin::single_value::SpecifiedValue::PaddingBox => {
3625                    mask_clip::single_value::SpecifiedValue::PaddingBox
3626                },
3627                mask_origin::single_value::SpecifiedValue::BorderBox => {
3628                    mask_clip::single_value::SpecifiedValue::BorderBox
3629                },
3630                #[cfg(feature = "gecko")]
3631                mask_origin::single_value::SpecifiedValue::FillBox => {
3632                    mask_clip::single_value::SpecifiedValue::FillBox
3633                },
3634                #[cfg(feature = "gecko")]
3635                mask_origin::single_value::SpecifiedValue::StrokeBox => {
3636                    mask_clip::single_value::SpecifiedValue::StrokeBox
3637                },
3638                #[cfg(feature = "gecko")]
3639                mask_origin::single_value::SpecifiedValue::ViewBox => {
3640                    mask_clip::single_value::SpecifiedValue::ViewBox
3641                },
3642            }
3643        }
3644    }
3645
3646    pub fn parse_value<'i, 't>(
3647        context: &ParserContext,
3648        input: &mut Parser<'i, 't>,
3649    ) -> Result<Longhands, ParseError<'i>> {
3650        let mut mask_image = Vec::with_capacity(1);
3651        let mut mask_mode = Vec::with_capacity(1);
3652        let mut mask_position_x = Vec::with_capacity(1);
3653        let mut mask_position_y = Vec::with_capacity(1);
3654        let mut mask_size = Vec::with_capacity(1);
3655        let mut mask_repeat = Vec::with_capacity(1);
3656        let mut mask_origin = Vec::with_capacity(1);
3657        let mut mask_clip = Vec::with_capacity(1);
3658        let mut mask_composite = Vec::with_capacity(1);
3659
3660        input.parse_comma_separated(|input| {
3661            let mut image = None;
3662            let mut mode = None;
3663            let mut position = None;
3664            let mut size = None;
3665            let mut repeat = None;
3666            let mut origin = None;
3667            let mut clip = None;
3668            let mut composite = None;
3669            let mut parsed = 0;
3670            loop {
3671                parsed += 1;
3672
3673                try_parse_one!(context, input, image, mask_image::single_value::parse);
3674                if position.is_none() {
3675                    if let Ok(value) = input.try_parse(|input| Position::parse(context, input)) {
3676                        position = Some(value);
3677                        size = input
3678                            .try_parse(|input| {
3679                                input.expect_delim('/')?;
3680                                mask_size::single_value::parse(context, input)
3681                            })
3682                            .ok();
3683
3684                        continue;
3685                    }
3686                }
3687                try_parse_one!(context, input, repeat, mask_repeat::single_value::parse);
3688                try_parse_one!(context, input, origin, mask_origin::single_value::parse);
3689                try_parse_one!(context, input, clip, mask_clip::single_value::parse);
3690                try_parse_one!(
3691                    context,
3692                    input,
3693                    composite,
3694                    mask_composite::single_value::parse
3695                );
3696                try_parse_one!(context, input, mode, mask_mode::single_value::parse);
3697
3698                parsed -= 1;
3699                break;
3700            }
3701            if clip.is_none() {
3702                if let Some(origin) = origin {
3703                    clip = Some(mask_clip::single_value::SpecifiedValue::from(origin));
3704                }
3705            }
3706            if parsed == 0 {
3707                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3708            }
3709            if let Some(position) = position {
3710                mask_position_x.push(position.horizontal);
3711                mask_position_y.push(position.vertical);
3712            } else {
3713                mask_position_x.push(PositionComponent::zero());
3714                mask_position_y.push(PositionComponent::zero());
3715            }
3716            if let Some(m_image) = image {
3717                mask_image.push(m_image);
3718            } else {
3719                mask_image.push(mask_image::single_value::get_initial_specified_value());
3720            }
3721            if let Some(m_mode) = mode {
3722                mask_mode.push(m_mode);
3723            } else {
3724                mask_mode.push(mask_mode::single_value::get_initial_specified_value());
3725            }
3726            if let Some(m_size) = size {
3727                mask_size.push(m_size);
3728            } else {
3729                mask_size.push(mask_size::single_value::get_initial_specified_value());
3730            }
3731            if let Some(m_repeat) = repeat {
3732                mask_repeat.push(m_repeat);
3733            } else {
3734                mask_repeat.push(mask_repeat::single_value::get_initial_specified_value());
3735            }
3736            if let Some(m_origin) = origin {
3737                mask_origin.push(m_origin);
3738            } else {
3739                mask_origin.push(mask_origin::single_value::get_initial_specified_value());
3740            }
3741            if let Some(m_clip) = clip {
3742                mask_clip.push(m_clip);
3743            } else {
3744                mask_clip.push(mask_clip::single_value::get_initial_specified_value());
3745            }
3746            if let Some(m_composite) = composite {
3747                mask_composite.push(m_composite);
3748            } else {
3749                mask_composite.push(mask_composite::single_value::get_initial_specified_value());
3750            }
3751            Ok(())
3752        })?;
3753
3754        Ok(expanded! {
3755           mask_image: mask_image::SpecifiedValue(mask_image.into()),
3756           mask_mode: mask_mode::SpecifiedValue(mask_mode.into()),
3757           mask_position_x: mask_position_x::SpecifiedValue(mask_position_x.into()),
3758           mask_position_y: mask_position_y::SpecifiedValue(mask_position_y.into()),
3759           mask_size: mask_size::SpecifiedValue(mask_size.into()),
3760           mask_repeat: mask_repeat::SpecifiedValue(mask_repeat.into()),
3761           mask_origin: mask_origin::SpecifiedValue(mask_origin.into()),
3762           mask_clip: mask_clip::SpecifiedValue(mask_clip.into()),
3763           mask_composite: mask_composite::SpecifiedValue(mask_composite.into()),
3764        })
3765    }
3766
3767    impl<'a> ToCss for LonghandsToSerialize<'a> {
3768        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3769        where
3770            W: fmt::Write,
3771        {
3772            use crate::properties::longhands::mask_clip::single_value::computed_value::T as Clip;
3773            use crate::properties::longhands::mask_origin::single_value::computed_value::T as Origin;
3774            use style_traits::values::SequenceWriter;
3775
3776            let len = self.mask_image.0.len();
3777            if len == 0 {
3778                return Ok(());
3779            }
3780            if self.mask_mode.0.len() != len {
3781                return Ok(());
3782            }
3783            if self.mask_position_x.0.len() != len {
3784                return Ok(());
3785            }
3786            if self.mask_position_y.0.len() != len {
3787                return Ok(());
3788            }
3789            if self.mask_size.0.len() != len {
3790                return Ok(());
3791            }
3792            if self.mask_repeat.0.len() != len {
3793                return Ok(());
3794            }
3795            if self.mask_origin.0.len() != len {
3796                return Ok(());
3797            }
3798            if self.mask_clip.0.len() != len {
3799                return Ok(());
3800            }
3801            if self.mask_composite.0.len() != len {
3802                return Ok(());
3803            }
3804
3805            for i in 0..len {
3806                if i > 0 {
3807                    dest.write_str(", ")?;
3808                }
3809
3810                let image = &self.mask_image.0[i];
3811                let mode = &self.mask_mode.0[i];
3812                let position_x = &self.mask_position_x.0[i];
3813                let position_y = &self.mask_position_y.0[i];
3814                let size = &self.mask_size.0[i];
3815                let repeat = &self.mask_repeat.0[i];
3816                let origin = &self.mask_origin.0[i];
3817                let clip = &self.mask_clip.0[i];
3818                let composite = &self.mask_composite.0[i];
3819
3820                let mut has_other = false;
3821                let has_image = *image != mask_image::single_value::get_initial_specified_value();
3822                has_other |= has_image;
3823                let has_mode = *mode != mask_mode::single_value::get_initial_specified_value();
3824                has_other |= has_mode;
3825                let has_size = *size != mask_size::single_value::get_initial_specified_value();
3826                has_other |= has_size;
3827                let has_repeat =
3828                    *repeat != mask_repeat::single_value::get_initial_specified_value();
3829                has_other |= has_repeat;
3830                let has_composite =
3831                    *composite != mask_composite::single_value::get_initial_specified_value();
3832                has_other |= has_composite;
3833                let has_position = *position_x != PositionComponent::zero()
3834                    || *position_y != PositionComponent::zero();
3835                let has_origin = *origin != Origin::BorderBox;
3836                let has_clip = *clip != Clip::BorderBox;
3837
3838                if !has_other && !has_position && !has_origin && !has_clip {
3839                    return image.to_css(dest);
3840                }
3841
3842                let mut writer = SequenceWriter::new(dest, " ");
3843                if has_image {
3844                    writer.item(image)?;
3845                }
3846                if has_position || has_size {
3847                    writer.write_item(|dest| {
3848                        Position {
3849                            horizontal: position_x.clone(),
3850                            vertical: position_y.clone(),
3851                        }
3852                        .to_css(dest)?;
3853                        if has_size {
3854                            dest.write_str(" / ")?;
3855                            size.to_css(dest)?;
3856                        }
3857                        Ok(())
3858                    })?;
3859                }
3860
3861                if has_repeat {
3862                    writer.item(repeat)?;
3863                }
3864
3865                #[cfg(feature = "gecko")]
3866                {
3867                    if has_origin || (has_clip && *clip != Clip::NoClip) {
3868                        writer.item(origin)?;
3869                    }
3870                }
3871
3872                #[cfg(feature = "servo")]
3873                {
3874                    if has_origin || has_clip {
3875                        writer.item(origin)?;
3876                    }
3877                }
3878
3879                if has_clip && *clip != From::from(*origin) {
3880                    writer.item(clip)?;
3881                }
3882
3883                if has_composite {
3884                    writer.item(composite)?;
3885                }
3886
3887                if has_mode {
3888                    writer.item(mode)?;
3889                }
3890            }
3891
3892            Ok(())
3893        }
3894    }
3895}
3896
3897pub mod mask_position {
3898    pub use crate::properties::generated::shorthands::mask_position::*;
3899
3900    use super::*;
3901    use crate::properties::longhands::{mask_position_x, mask_position_y};
3902    use crate::values::specified::Position;
3903
3904    pub fn parse_value<'i, 't>(
3905        context: &ParserContext,
3906        input: &mut Parser<'i, 't>,
3907    ) -> Result<Longhands, ParseError<'i>> {
3908        // Vec grows from 0 to 4 by default on first push().  So allocate with capacity 1, so in
3909        // the common case of only one item we don't way overallocate, then shrink.  Note that we
3910        // always push at least one item if parsing succeeds.
3911        let mut position_x = Vec::with_capacity(1);
3912        let mut position_y = Vec::with_capacity(1);
3913        input.parse_comma_separated(|input| {
3914            let value = Position::parse(context, input)?;
3915            position_x.push(value.horizontal);
3916            position_y.push(value.vertical);
3917            Ok(())
3918        })?;
3919
3920        if position_x.is_empty() {
3921            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
3922        }
3923
3924        Ok(expanded! {
3925            mask_position_x: mask_position_x::SpecifiedValue(position_x.into()),
3926            mask_position_y: mask_position_y::SpecifiedValue(position_y.into()),
3927        })
3928    }
3929
3930    impl<'a> ToCss for LonghandsToSerialize<'a> {
3931        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
3932        where
3933            W: fmt::Write,
3934        {
3935            let len = self.mask_position_x.0.len();
3936            if len == 0 || self.mask_position_y.0.len() != len {
3937                return Ok(());
3938            }
3939
3940            for i in 0..len {
3941                Position {
3942                    horizontal: self.mask_position_x.0[i].clone(),
3943                    vertical: self.mask_position_y.0[i].clone(),
3944                }
3945                .to_css(dest)?;
3946
3947                if i < len - 1 {
3948                    dest.write_str(", ")?;
3949                }
3950            }
3951
3952            Ok(())
3953        }
3954    }
3955}
3956
3957pub mod grid_template {
3958    pub use crate::properties::generated::shorthands::grid_template::*;
3959
3960    use super::*;
3961    use crate::parser::Parse;
3962    use crate::values::generics::grid::{concat_serialize_idents, TrackListValue};
3963    use crate::values::generics::grid::{TrackList, TrackSize};
3964    use crate::values::specified::grid::parse_line_names;
3965    use crate::values::specified::position::{
3966        GridTemplateAreas, TemplateAreasArc, TemplateAreasParser,
3967    };
3968    use crate::values::specified::{GenericGridTemplateComponent, GridTemplateComponent};
3969    use servo_arc::Arc;
3970
3971    pub fn parse_grid_template<'i, 't>(
3972        context: &ParserContext,
3973        input: &mut Parser<'i, 't>,
3974    ) -> Result<
3975        (
3976            GridTemplateComponent,
3977            GridTemplateComponent,
3978            GridTemplateAreas,
3979        ),
3980        ParseError<'i>,
3981    > {
3982        if let Ok(x) = input.try_parse(|i| {
3983            if i.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
3984                if !i.is_exhausted() {
3985                    return Err(());
3986                }
3987                return Ok((
3988                    GenericGridTemplateComponent::None,
3989                    GenericGridTemplateComponent::None,
3990                    GridTemplateAreas::None,
3991                ));
3992            }
3993            Err(())
3994        }) {
3995            return Ok(x);
3996        }
3997
3998        let first_line_names = input.try_parse(parse_line_names).unwrap_or_default();
3999        let mut areas_parser = TemplateAreasParser::default();
4000        if areas_parser.try_parse_string(input).is_ok() {
4001            let mut values = vec![];
4002            let mut line_names = vec![];
4003            line_names.push(first_line_names);
4004            loop {
4005                let size = input
4006                    .try_parse(|i| TrackSize::parse(context, i))
4007                    .unwrap_or_default();
4008                values.push(TrackListValue::TrackSize(size));
4009                let mut names = input.try_parse(parse_line_names).unwrap_or_default();
4010                let more_names = input.try_parse(parse_line_names);
4011
4012                match areas_parser.try_parse_string(input) {
4013                    Ok(()) => {
4014                        if let Ok(v) = more_names {
4015                            let mut names_vec = names.into_vec();
4016                            names_vec.extend(v.into_iter());
4017                            names = names_vec.into();
4018                        }
4019                        line_names.push(names);
4020                    },
4021                    Err(e) => {
4022                        if more_names.is_ok() {
4023                            return Err(e);
4024                        }
4025                        line_names.push(names);
4026                        break;
4027                    },
4028                };
4029            }
4030
4031            if line_names.len() == values.len() {
4032                line_names.push(Default::default());
4033            }
4034
4035            let template_areas = areas_parser
4036                .finish()
4037                .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
4038            let template_rows = TrackList {
4039                values: values.into(),
4040                line_names: line_names.into(),
4041                auto_repeat_index: std::usize::MAX,
4042            };
4043
4044            let template_cols = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
4045                let value = GridTemplateComponent::parse_without_none(context, input)?;
4046                if let GenericGridTemplateComponent::TrackList(ref list) = value {
4047                    if !list.is_explicit() {
4048                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
4049                    }
4050                }
4051
4052                value
4053            } else {
4054                GridTemplateComponent::default()
4055            };
4056
4057            Ok((
4058                GenericGridTemplateComponent::TrackList(Box::new(template_rows)),
4059                template_cols,
4060                GridTemplateAreas::Areas(TemplateAreasArc(Arc::new(template_areas))),
4061            ))
4062        } else {
4063            let mut template_rows = GridTemplateComponent::parse(context, input)?;
4064            if let GenericGridTemplateComponent::TrackList(ref mut list) = template_rows {
4065                if list.line_names[0].is_empty() {
4066                    list.line_names[0] = first_line_names;
4067                } else {
4068                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
4069                }
4070            }
4071
4072            input.expect_delim('/')?;
4073            Ok((
4074                template_rows,
4075                GridTemplateComponent::parse(context, input)?,
4076                GridTemplateAreas::None,
4077            ))
4078        }
4079    }
4080
4081    #[inline]
4082    pub fn parse_value<'i, 't>(
4083        context: &ParserContext,
4084        input: &mut Parser<'i, 't>,
4085    ) -> Result<Longhands, ParseError<'i>> {
4086        let (rows, columns, areas) = parse_grid_template(context, input)?;
4087        Ok(expanded! {
4088            grid_template_rows: rows,
4089            grid_template_columns: columns,
4090            grid_template_areas: areas,
4091        })
4092    }
4093
4094    pub fn serialize_grid_template<W>(
4095        template_rows: &GridTemplateComponent,
4096        template_columns: &GridTemplateComponent,
4097        template_areas: &GridTemplateAreas,
4098        dest: &mut CssWriter<W>,
4099    ) -> fmt::Result
4100    where
4101        W: fmt::Write,
4102    {
4103        match *template_areas {
4104            GridTemplateAreas::None => {
4105                if template_rows.is_initial() && template_columns.is_initial() {
4106                    return GridTemplateComponent::default().to_css(dest);
4107                }
4108                template_rows.to_css(dest)?;
4109                dest.write_str(" / ")?;
4110                template_columns.to_css(dest)
4111            },
4112            GridTemplateAreas::Areas(ref areas) => {
4113                if areas.0.strings.len() != template_rows.track_list_len() {
4114                    return Ok(());
4115                }
4116
4117                let track_list = match *template_rows {
4118                    GenericGridTemplateComponent::TrackList(ref list) => {
4119                        if !list.is_explicit() {
4120                            return Ok(());
4121                        }
4122                        list
4123                    },
4124                    _ => return Ok(()),
4125                };
4126
4127                match *template_columns {
4128                    GenericGridTemplateComponent::TrackList(ref list) => {
4129                        if !list.is_explicit() {
4130                            return Ok(());
4131                        }
4132                    },
4133                    GenericGridTemplateComponent::Subgrid(_) => {
4134                        return Ok(());
4135                    },
4136                    _ => {},
4137                }
4138
4139                let mut names_iter = track_list.line_names.iter();
4140                for (((i, string), names), value) in areas
4141                    .0
4142                    .strings
4143                    .iter()
4144                    .enumerate()
4145                    .zip(&mut names_iter)
4146                    .zip(track_list.values.iter())
4147                {
4148                    if i > 0 {
4149                        dest.write_char(' ')?;
4150                    }
4151
4152                    if !names.is_empty() {
4153                        concat_serialize_idents("[", "] ", names, " ", dest)?;
4154                    }
4155
4156                    string.to_css(dest)?;
4157
4158                    if !value.is_initial() {
4159                        dest.write_char(' ')?;
4160                        value.to_css(dest)?;
4161                    }
4162                }
4163
4164                if let Some(names) = names_iter.next() {
4165                    concat_serialize_idents(" [", "]", names, " ", dest)?;
4166                }
4167
4168                if let GenericGridTemplateComponent::TrackList(ref list) = *template_columns {
4169                    dest.write_str(" / ")?;
4170                    list.to_css(dest)?;
4171                }
4172
4173                Ok(())
4174            },
4175        }
4176    }
4177
4178    impl<'a> ToCss for LonghandsToSerialize<'a> {
4179        #[inline]
4180        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
4181        where
4182            W: fmt::Write,
4183        {
4184            serialize_grid_template(
4185                self.grid_template_rows,
4186                self.grid_template_columns,
4187                self.grid_template_areas,
4188                dest,
4189            )
4190        }
4191    }
4192}
4193
4194pub mod grid {
4195    pub use crate::properties::generated::shorthands::grid::*;
4196
4197    use super::*;
4198    use crate::parser::Parse;
4199    use crate::properties::longhands::{grid_auto_columns, grid_auto_flow, grid_auto_rows};
4200    use crate::values::generics::grid::GridTemplateComponent;
4201    use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
4202    use crate::values::specified::{GenericGridTemplateComponent, ImplicitGridTracks};
4203
4204    pub fn parse_value<'i, 't>(
4205        context: &ParserContext,
4206        input: &mut Parser<'i, 't>,
4207    ) -> Result<Longhands, ParseError<'i>> {
4208        let mut temp_rows = GridTemplateComponent::default();
4209        let mut temp_cols = GridTemplateComponent::default();
4210        let mut temp_areas = GridTemplateAreas::None;
4211        let mut auto_rows = ImplicitGridTracks::default();
4212        let mut auto_cols = ImplicitGridTracks::default();
4213        let mut flow = grid_auto_flow::get_initial_value();
4214
4215        fn parse_auto_flow<'i, 't>(
4216            input: &mut Parser<'i, 't>,
4217            is_row: bool,
4218        ) -> Result<GridAutoFlow, ParseError<'i>> {
4219            let mut track = None;
4220            let mut dense = GridAutoFlow::empty();
4221
4222            for _ in 0..2 {
4223                if input
4224                    .try_parse(|i| i.expect_ident_matching("auto-flow"))
4225                    .is_ok()
4226                {
4227                    track = if is_row {
4228                        Some(GridAutoFlow::ROW)
4229                    } else {
4230                        Some(GridAutoFlow::COLUMN)
4231                    };
4232                } else if input
4233                    .try_parse(|i| i.expect_ident_matching("dense"))
4234                    .is_ok()
4235                {
4236                    dense = GridAutoFlow::DENSE
4237                } else {
4238                    break;
4239                }
4240            }
4241
4242            if track.is_some() {
4243                Ok(track.unwrap() | dense)
4244            } else {
4245                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
4246            }
4247        }
4248
4249        if let Ok((rows, cols, areas)) =
4250            input.try_parse(|i| super::grid_template::parse_grid_template(context, i))
4251        {
4252            temp_rows = rows;
4253            temp_cols = cols;
4254            temp_areas = areas;
4255        } else if let Ok(rows) = input.try_parse(|i| GridTemplateComponent::parse(context, i)) {
4256            temp_rows = rows;
4257            input.expect_delim('/')?;
4258            flow = parse_auto_flow(input, false)?;
4259            auto_cols = input
4260                .try_parse(|i| grid_auto_columns::parse(context, i))
4261                .unwrap_or_default();
4262        } else {
4263            flow = parse_auto_flow(input, true)?;
4264            auto_rows = input
4265                .try_parse(|i| grid_auto_rows::parse(context, i))
4266                .unwrap_or_default();
4267            input.expect_delim('/')?;
4268            temp_cols = GridTemplateComponent::parse(context, input)?;
4269        }
4270
4271        Ok(expanded! {
4272            grid_template_rows: temp_rows,
4273            grid_template_columns: temp_cols,
4274            grid_template_areas: temp_areas,
4275            grid_auto_rows: auto_rows,
4276            grid_auto_columns: auto_cols,
4277            grid_auto_flow: flow,
4278        })
4279    }
4280
4281    impl<'a> LonghandsToSerialize<'a> {
4282        fn is_grid_template(&self) -> bool {
4283            self.grid_auto_rows.is_initial()
4284                && self.grid_auto_columns.is_initial()
4285                && *self.grid_auto_flow == grid_auto_flow::get_initial_value()
4286        }
4287    }
4288
4289    impl<'a> ToCss for LonghandsToSerialize<'a> {
4290        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
4291        where
4292            W: fmt::Write,
4293        {
4294            if self.is_grid_template() {
4295                return super::grid_template::serialize_grid_template(
4296                    self.grid_template_rows,
4297                    self.grid_template_columns,
4298                    self.grid_template_areas,
4299                    dest,
4300                );
4301            }
4302
4303            if *self.grid_template_areas != GridTemplateAreas::None {
4304                return Ok(());
4305            }
4306
4307            if self.grid_auto_flow.contains(GridAutoFlow::COLUMN) {
4308                if !self.grid_auto_rows.is_initial() || !self.grid_template_columns.is_initial() {
4309                    return Ok(());
4310                }
4311
4312                if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_rows
4313                {
4314                    if !list.is_explicit() {
4315                        return Ok(());
4316                    }
4317                }
4318
4319                self.grid_template_rows.to_css(dest)?;
4320                dest.write_str(" / auto-flow")?;
4321                if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
4322                    dest.write_str(" dense")?;
4323                }
4324
4325                if !self.grid_auto_columns.is_initial() {
4326                    dest.write_char(' ')?;
4327                    self.grid_auto_columns.to_css(dest)?;
4328                }
4329
4330                return Ok(());
4331            }
4332
4333            if !self.grid_auto_columns.is_initial() || !self.grid_template_rows.is_initial() {
4334                return Ok(());
4335            }
4336
4337            if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_columns {
4338                if !list.is_explicit() {
4339                    return Ok(());
4340                }
4341            }
4342
4343            dest.write_str("auto-flow")?;
4344            if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
4345                dest.write_str(" dense")?;
4346            }
4347
4348            if !self.grid_auto_rows.is_initial() {
4349                dest.write_char(' ')?;
4350                self.grid_auto_rows.to_css(dest)?;
4351            }
4352
4353            dest.write_str(" / ")?;
4354            self.grid_template_columns.to_css(dest)?;
4355            Ok(())
4356        }
4357    }
4358}