Skip to main content

style/properties/
shorthands.rs

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