Skip to main content

style/properties/
shorthands.rs

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