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