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