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