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