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